rodAndBarDetection version 1.3.0 : 新的定位盘中心测量功能

This commit is contained in:
jerryzeng 2026-05-23 19:00:02 +08:00
parent 5378038e3b
commit 1ed1e1242d
7 changed files with 1272 additions and 40 deletions

View File

@ -13,6 +13,7 @@
#include <Windows.h>
#include <limits>
#include <cmath>
typedef struct
{
int r;
@ -31,6 +32,34 @@ typedef struct
float b;
} SPointXYZRGB;
// 点乘 dot
double _dotMultiply(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
{
return (a.x * b.x + a.y * b.y + a.z * b.z);
}
// 模长
double _length(const SVzNL3DPoint& a)
{
return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
}
// 计算两个向量夹角(返回 角度)
float _computeVecAngle(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
{
float l = _length(a) * _length(b);
if (l < 1e-6f)
return 0.0f; // 避免除零
float cosTheta = _dotMultiply(a, b) / l;
cosTheta = cosTheta > 1.0 ? 1.0 : cosTheta;
cosTheta = cosTheta < -1.0 ? -1.0 : cosTheta;
float rad = acosf(cosTheta);
float degree = rad * 180.0f / (float)M_PI;
return degree;
}
void wdReadLaserScanPointFromFile_XYZ_vector(const char* fileName, std::vector<std::vector< SVzNL3DPosition>>& scanData)
{
std::ifstream inputFile(fileName);
@ -450,15 +479,15 @@ void _outputRGBDScan_RGBD_centerPose(
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->nPointIdx == 2)
{
rgb = { 135,206,235 };
size = 5;
}
else if (pt3D->nPointIdx == 3)
{
rgb = { 250, 0, 0 };
size = 5;
}
//else if (pt3D->nPointIdx == 1)
//{
// rgb = { 0, 0, 250 };
// size = 3;
//}
else //if (pt3D->nPointIdx == 0)
{
rgb = { 200, 200, 200 };
@ -500,7 +529,7 @@ void _outputRGBDScan_RGBD_centerPose(
//输出法向
size = 1;
double len = 60;
double len = 110;
lineIdx = 0;
{
SVzNL3DPoint pt0 = { poseInfo.center.x, // - len * poseInfo.normalDir.x,
@ -878,7 +907,7 @@ void _outputRGBDScan_RGBD_weldSeam(
sw.close();
}
#define SCREW_TEST_GROUP 7
#define SCREW_TEST_GROUP 10
void screwTest(void)
{
const char* dataPath[SCREW_TEST_GROUP] = {
@ -889,17 +918,22 @@ void screwTest(void)
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置2/", //4
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置2未动螺杆拧进去100mm左右/", //5
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置2向前100mm螺杆拧进去100mm左右/", //6
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云20260621/另一根螺杆/", //7
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云20260621/拍照点1/", //8
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云20260621/拍照点1xyz移动10mm/", //9
};
SVzNLRange fileIdx[SCREW_TEST_GROUP] = {
{1,4},{1,30},{1,11},
{1,20}, {1,20}, {1,5}, {1,21}
{1,20}, {1,20}, {1,5}, {1,21},
{1,10}, {1,10}, {1,10}
};
const char* ver = wd_rodAndBarDetectionVersion();
printf("ver:%s\n", ver);
for (int grp = 3; grp < SCREW_TEST_GROUP; grp++)
for (int grp = 9; grp < SCREW_TEST_GROUP; grp++)
{
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
{
@ -908,6 +942,8 @@ void screwTest(void)
if(0 == grp)
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
else if( (grp>= 7)&&(grp <= 9))
sprintf_s(_scan_file, "%sLaserline_%d.txt", dataPath[grp], fidx);
else
sprintf_s(_scan_file, "%s%d_LaserData_Jl26C299.txt", dataPath[grp], fidx);
@ -962,12 +998,23 @@ void screwTest(void)
&errCode);
long t2 = (long)GetTickCount64();
printf("%s: %d(ms), errCode=%d\n", _scan_file, (int)(t2 - t1), errCode);
//输出测试结果
sprintf_s(_scan_file, "%sresult\\%d_result.txt", dataPath[grp], fidx);
_outputRGBDScan_RGBD(_scan_file, scanLines, screwInfo);
sprintf_s(_scan_file, "%sresult\\%d_screw_info.txt", dataPath[grp], fidx);
_outputChanneltInfo(_scan_file, screwInfo);
char _out_file[256];
sprintf_s(_out_file, "%sresult\\%d_result.txt", dataPath[grp], fidx);
_outputRGBDScan_RGBD(_out_file, scanLines, screwInfo);
sprintf_s(_out_file, "%sresult\\%d_screw_info.txt", dataPath[grp], fidx);
_outputChanneltInfo(_out_file, screwInfo);
if (screwInfo.size() > 0)
{
SVzNL3DPoint zAxis = { 0, 0, 1.0 };
float angle = _computeVecAngle(screwInfo[0].axialDir, zAxis);
printf("%s: %d(ms), errCode=%d, {%f, %f, %f}, 与Z轴夹角=%f \n", _scan_file, (int)(t2 - t1), errCode,
(float)screwInfo[0].center.x, (float)screwInfo[0].center.y, (float)screwInfo[0].center.z, angle);
}
else
printf("%s: %d(ms), errCode=%d\n", _scan_file, (int)(t2 - t1), errCode);
}
}
}
@ -975,7 +1022,7 @@ void screwTest(void)
#define LOCATING_PALTE_TEST_GROUP 1
void locatingPlateTest(void)
{
const char* dataPath[SCREW_TEST_GROUP] = {
const char* dataPath[LOCATING_PALTE_TEST_GROUP] = {
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/定位盘点云/", //0
};
@ -1030,22 +1077,79 @@ void locatingPlateTest(void)
}
}
#define ROD_POSITION_TEST_GROUP 2
void rodPositionTest(void)
#define NEW_LOCATING_PALTE_TEST_GROUP 1
void newLocatingPlateTest(void)
{
const char* dataPath[ROD_POSITION_TEST_GROUP] = {
"F:/ShangGu/项目/冠钦项目/矩森棒材抓取/", //0
"F:/ShangGu/项目/冠钦项目/胶布圆棒抓取/模拟测试数据/", //1
const char* dataPath[NEW_LOCATING_PALTE_TEST_GROUP] = {
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/定位盘点云20260621/", //0
};
SVzNLRange fileIdx[ROD_POSITION_TEST_GROUP] = {
{1,8}, {1,5}
SVzNLRange fileIdx[NEW_LOCATING_PALTE_TEST_GROUP] = {
{2,11},
};
const char* ver = wd_rodAndBarDetectionVersion();
printf("ver:%s\n", ver);
for (int grp = 1; grp < ROD_POSITION_TEST_GROUP; grp++)
for (int grp = 0; grp < NEW_LOCATING_PALTE_TEST_GROUP; grp++)
{
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
{
//fidx =2;
char _scan_file[256];
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
std::vector<std::vector< SVzNL3DPosition>> scanLines;
wdReadLaserScanPointFromFile_XYZ_vector(_scan_file, scanLines);
//转成plyTxt格式
//sprintf_s(_scan_file, "%s%d_ply_Hi229229.txt", dataPath[grp], fidx);
//wdSavePlyTxt(_scan_file, scanLines);
long t1 = (long)GetTickCount64();//统计时间
SSG_cornerParam cornerParam;
cornerParam.cornerTh = 45; //45度角
cornerParam.scale = 5.0; // algoParam.bagParam.bagH / 8; // 15; // algoParam.bagParam.bagH / 8;
cornerParam.minEndingGap = 20; // algoParam.bagParam.bagW / 4;
cornerParam.minEndingGap_z = 5.0;
cornerParam.jumpCornerTh_1 = 15; //水平角度,小于此角度视为水平
cornerParam.jumpCornerTh_2 = 60;
int errCode = 0;
SSX_platePoseInfo centerPose = sx_getLocationPlatePose_new(
scanLines,
cornerParam,
&errCode);
long t2 = (long)GetTickCount64();
printf("%s: %d(ms),errCode=%d\n", _scan_file, (int)(t2 - t1), errCode);
//输出测试结果
sprintf_s(_scan_file, "%sresult\\%d_result.txt", dataPath[grp], fidx);
_outputRGBDScan_RGBD_centerPose(_scan_file, scanLines, centerPose);
sprintf_s(_scan_file, "%sresult\\%d_screw_info.txt", dataPath[grp], fidx);
_outputPlatePiseInfo(_scan_file, centerPose);
}
}
}
#define ROD_POSITION_TEST_GROUP 3
void rodPositionTest(void)
{
const char* dataPath[ROD_POSITION_TEST_GROUP] = {
"F:/ShangGu/项目/冠钦项目/矩森棒材抓取/", //0
"F:/ShangGu/项目/冠钦项目/胶布圆棒抓取/模拟测试数据/", //1
"F:/ShangGu/项目/冠钦项目/胶布圆棒抓取/模拟测试数据2/", //1
};
SVzNLRange fileIdx[ROD_POSITION_TEST_GROUP] = {
{1,8}, {1,5},{1,11}
};
const char* ver = wd_rodAndBarDetectionVersion();
printf("ver:%s\n", ver);
for (int grp = 2; grp < ROD_POSITION_TEST_GROUP; grp++)
{
SSG_planeCalibPara poseCalibPara;
//初始化成单位阵
@ -1069,7 +1173,7 @@ void rodPositionTest(void)
{
//fidx =1;
char _scan_file[256];
if(1 == grp)
if( (1 == grp) ||(2 == grp))
sprintf_s(_scan_file, "%s%d_LaserData_Hi229156.txt", dataPath[grp], fidx);
else
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
@ -1239,21 +1343,35 @@ void rodWeldSeamPosition_test(void)
}
}
typedef enum
{
keSG_测试_配天螺杆定位 = 0,
keSG_测试_配天定位盘定位,
keSG_测试_配天新定位盘定位,
keSG_测试_棒材抓取,
keSG_测试_筑裕钢筋焊缝定位,
} ESG_testMode;
int main()
{
#if 1 //螺杆定位测试
#if 1
screwTest();
#else
locatingPlateTest();
#endif
#else //棒材抓取定位测试
#if 1
rodPositionTest();
#else
rodWeldSeamPosition_test();
#endif
#endif
//ESG_testMode testMode = keSG_测试_配天螺杆定位;
//ESG_testMode testMode = keSG_测试_配天定位盘定位;
ESG_testMode testMode = keSG_测试_配天新定位盘定位;
//ESG_testMode testMode = keSG_测试_棒材抓取;
//ESG_testMode testMode = keSG_测试_筑裕钢筋焊缝定位;
if(keSG_测试_配天螺杆定位 == testMode)
screwTest();
else if(keSG_测试_配天定位盘定位 == testMode)
locatingPlateTest();
else if (keSG_测试_配天新定位盘定位 == testMode)
newLocatingPlateTest();
else if(keSG_测试_棒材抓取 == testMode)
rodPositionTest();
else if(keSG_测试_筑裕钢筋焊缝定位 == testMode)
rodWeldSeamPosition_test();
return 0;
}

View File

@ -14,6 +14,8 @@ SG_APISHARED_EXPORT double vec3_dotMultiply(const SVzNL3DPoint& a, const SVzNL3D
SG_APISHARED_EXPORT double vec3_length(const SVzNL3DPoint& a);
// 归一化(单位向量)
SG_APISHARED_EXPORT SVzNL3DPoint vec3_normalize(const SVzNL3DPoint& a);
// 计算两个向量夹角(返回 角度)
SG_APISHARED_EXPORT float vec3_computeVecAngle(const SVzNL3DPoint& a, const SVzNL3DPoint& b);
/**
* @brief v n theta
@ -246,6 +248,26 @@ SG_APISHARED_EXPORT void wd_searchCornerPeaks(
std::vector< SSG_pntDirAngle>& cornerPeakM
);
//提取backwardAngle极值较早实现函数可以使用此函数进行代码优化
SG_APISHARED_EXPORT void wd_searchBackwardAnglePeaks(
std::vector< SSG_pntDirAngle>& corners,
std::vector< SVzNL3DPosition>& vldPts,
const double minCornerTh,
double cornerMergeScale,
std::vector< SSG_pntDirAngle>& backwardAnglePeakP,
std::vector< SSG_pntDirAngle>& backwardAnglePeakM
);
//提取forwardAngle极值较早实现函数可以使用此函数进行代码优化
SG_APISHARED_EXPORT void wd_searchForwardAnglePeaks(
std::vector< SSG_pntDirAngle>& corners,
std::vector< SVzNL3DPosition>& vldPts,
const double minCornerTh,
double cornerMergeScale,
std::vector< SSG_pntDirAngle>& forwardAnglePeakP,
std::vector< SSG_pntDirAngle>& forwardAnglePeakM
);
/// <summary>
/// 提取激光线上的拐点特征。是在PSM LVTypeFeature, BQ等拐点算法的基础上的版本。
/// nPointIdx被重新定义成Feature类型
@ -262,6 +284,17 @@ SG_APISHARED_EXPORT void wd_getLineCorerFeature(
std::vector<SSG_basicFeature1D>& line_cornerFeatures //拐点
);
/// <summary>
/// 提取激光线上的与XY平面垂直的特征垂直段
/// </summary>
SG_APISHARED_EXPORT void wd_getXYVertialFeature_peakCornerMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double peakChkWin,
const SSG_cornerParam cornerPara,
std::vector<SSG_RUN>& xyVerticalSegs //环
);
/// 提取激光线上的拐点特征。是在PSM LVTypeFeature, BQ等拐点算法的基础上的版本。
/// 使用平均点距进行加速
/// nPointIdx被重新定义成Feature类型
@ -625,6 +658,11 @@ SG_APISHARED_EXPORT double fitCircleByLeastSquare(
const std::vector<SVzNL3DPoint>& pointArray,
SVzNL3DPoint& center,
double& radius);
//圆最小二乘拟合
SG_APISHARED_EXPORT double fitCircleByLeastSquare_2(
const std::vector<SVzNL3DPosition>& pointArray,
SVzNL3DPoint& center,
double& radius);
//抛物线最小二乘拟合 y=ax^2 + bx + c
SG_APISHARED_EXPORT bool leastSquareParabolaFitEigen(

View File

@ -52,6 +52,20 @@ SVzNL3DPoint vec3_normalize(const SVzNL3DPoint& a)
return result;
}
// 计算两个向量夹角(返回 角度)
float vec3_computeVecAngle(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
{
float l = vec3_length(a) * vec3_length(b);
if (l < 1e-6f)
return 0.0f; // 避免除零
float cosTheta = vec3_dotMultiply(a, b) / l;
cosTheta = std::max(std::min(cosTheta, 1.0f), -1.0f); // 防止数值越界
float rad = acosf(cosTheta);
float degree = rad * 180.0f / (float)M_PI;
return degree;
}
/**
* @brief v n theta
* v 使

View File

@ -4672,7 +4672,7 @@ void wd_searchCornerPeaks(
if (i == 275)
int kkk = 1;
SSG_pntDirAngle* curr_data = &corners[i];
if (curr_data->pntIdx < 0)
if ( (curr_data->pntIdx < 0) || (curr_data->type < 0))
{
if (i == i_max - 1) //最后一个
{
@ -4828,6 +4828,366 @@ void wd_searchCornerPeaks(
}
}
//提取backwardAngle极值较早实现函数可以使用此函数进行代码优化
void wd_searchBackwardAnglePeaks(
std::vector< SSG_pntDirAngle>& corners,
std::vector< SVzNL3DPosition>& vldPts,
const double minCornerTh,
double cornerMergeScale,
std::vector< SSG_pntDirAngle>& backwardAnglePeakP,
std::vector< SSG_pntDirAngle>& backwardAnglePeakM
)
{
int cornerSize = (int)corners.size();
std::vector< SSG_pntDirAngle> cornerPk_P;
std::vector< SSG_pntDirAngle> cornerPk_M;
//搜索拐角极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SSG_pntDirAngle* pre_data = NULL;
for (int i = 0, i_max = cornerSize; i < i_max; i++)
{
if (i == 275)
int kkk = 1;
SSG_pntDirAngle* curr_data = &corners[i];
if ((curr_data->pntIdx < 0) || (curr_data->type < 0))
{
if (i == i_max - 1) //最后一个
{
if (1 == _state) //上升
{
if (corners[eEdgePtIdx].backwardAngle > 0)
cornerPk_P.push_back(corners[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
if (corners[eEdgePtIdx].backwardAngle < 0)
cornerPk_M.push_back(corners[eEdgePtIdx]);
}
}
continue;
}
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double backwardAngleDiff = curr_data->backwardAngle - pre_data->backwardAngle;
switch (_state)
{
case 0: //初态
if (backwardAngleDiff < 0) //下降
{
_state = 2;
}
else if (backwardAngleDiff > 0) //上升
{
_state = 1;
}
break;
case 1: //上升
if (backwardAngleDiff < 0) //下降
{
if (pre_data->backwardAngle > 0)
cornerPk_P.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (backwardAngleDiff > 0) // 上升
{
if (pre_data->backwardAngle < 0)
cornerPk_M.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
double square_distTh = cornerMergeScale * cornerMergeScale; //2倍的cornerScale。
for (int i = 0, i_max = (int)cornerPk_P.size(); i < i_max; i++)
{
if (cornerPk_P[i].backwardAngle < minCornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPk_P[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPk_P[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (cornerPk_P[i].backwardAngle < cornerPk_P[j].backwardAngle)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPk_P[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPk_P[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (cornerPk_P[i].backwardAngle < cornerPk_P[j].backwardAngle)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
backwardAnglePeakP.push_back(cornerPk_P[i]);
}
for (int i = 0, i_max = (int)cornerPk_M.size(); i < i_max; i++)
{
if (abs(cornerPk_M[i].backwardAngle) < minCornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPk_M[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPk_M[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (abs(cornerPk_M[i].backwardAngle) < abs(cornerPk_M[j].backwardAngle))
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPk_M[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPk_M[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (abs(cornerPk_M[i].backwardAngle) < abs(cornerPk_M[j].backwardAngle))
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
backwardAnglePeakM.push_back(cornerPk_M[i]);
}
}
//提取forwardAngle极值较早实现函数可以使用此函数进行代码优化
void wd_searchForwardAnglePeaks(
std::vector< SSG_pntDirAngle>& corners,
std::vector< SVzNL3DPosition>& vldPts,
const double minCornerTh,
double cornerMergeScale,
std::vector< SSG_pntDirAngle>& forwardAnglePeakP,
std::vector< SSG_pntDirAngle>& forwardAnglePeakM
)
{
int cornerSize = (int)corners.size();
std::vector< SSG_pntDirAngle> cornerPk_P;
std::vector< SSG_pntDirAngle> cornerPk_M;
//搜索拐角极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SSG_pntDirAngle* pre_data = NULL;
for (int i = 0, i_max = cornerSize; i < i_max; i++)
{
if (i == 275)
int kkk = 1;
SSG_pntDirAngle* curr_data = &corners[i];
if ((curr_data->pntIdx < 0) || (curr_data->type < 0))
{
if (i == i_max - 1) //最后一个
{
if (1 == _state) //上升
{
if (corners[eEdgePtIdx].forwardAngle > 0)
cornerPk_P.push_back(corners[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
if (corners[eEdgePtIdx].forwardAngle < 0)
cornerPk_M.push_back(corners[eEdgePtIdx]);
}
}
continue;
}
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double forwardAngleDiff = curr_data->forwardAngle - pre_data->forwardAngle;
switch (_state)
{
case 0: //初态
if (forwardAngleDiff < 0) //下降
{
_state = 2;
}
else if (forwardAngleDiff > 0) //上升
{
_state = 1;
}
break;
case 1: //上升
if (forwardAngleDiff < 0) //下降
{
if (pre_data->forwardAngle > 0)
cornerPk_P.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (forwardAngleDiff > 0) // 上升
{
if (pre_data->forwardAngle < 0)
cornerPk_M.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
double square_distTh = cornerMergeScale * cornerMergeScale; //2倍的cornerScale。
for (int i = 0, i_max = (int)cornerPk_P.size(); i < i_max; i++)
{
if (cornerPk_P[i].forwardAngle < minCornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPk_P[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPk_P[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (cornerPk_P[i].forwardAngle < cornerPk_P[j].forwardAngle)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPk_P[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPk_P[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (cornerPk_P[i].forwardAngle < cornerPk_P[j].forwardAngle)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
forwardAnglePeakP.push_back(cornerPk_P[i]);
}
for (int i = 0, i_max = (int)cornerPk_M.size(); i < i_max; i++)
{
if (abs(cornerPk_M[i].forwardAngle) < minCornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPk_M[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPk_M[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (abs(cornerPk_M[i].forwardAngle) < abs(cornerPk_M[j].forwardAngle))
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPk_M[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPk_M[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (abs(cornerPk_M[i].forwardAngle) < abs(cornerPk_M[j].forwardAngle))
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
forwardAnglePeakM.push_back(cornerPk_M[i]);
}
}
/// <summary>
/// 提取激光线上的拐点特征。是在PSM LVTypeFeature, BQ等拐点算法的基础上的版本。
/// nPointIdx被重新定义成Feature类型
@ -5019,6 +5379,124 @@ void wd_getLineCorerFeature(
return;
}
/// <summary>
/// 提取激光线上的与XY平面垂直的特征垂直段
/// seg端点z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2搜索同方向的拐角连续段
/// 2搜索Z极值
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
///
/// </summary>
void wd_getXYVertialFeature_peakCornerMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double peakChkWin,
const SSG_cornerParam cornerPara,
std::vector<SSG_RUN>& xyVerticalSegs
)
{
double arcPerPointCornerMinValue = 5; //arc上每个点的转角最小值
double arcTotalCornerMinValue = 30; //整个Arc的转角最小值
//计算前向角和后向角
std::vector< SSG_pntDirAngle> ptDirAngles;
wd_computeDirAngle_wholeLine(lineData, cornerPara, ptDirAngles);
double cornerMergeScale = cornerPara.scale;
//提取backwardAngle极值较早实现函数可以使用此函数进行代码优化
std::vector< SSG_pntDirAngle> backwardAnglePeakP;
std::vector< SSG_pntDirAngle> backwardAnglePeakM;
wd_searchBackwardAnglePeaks(
ptDirAngles,
lineData,
cornerPara.cornerTh,
cornerMergeScale,
backwardAnglePeakP,
backwardAnglePeakM
);
//提取forwardAngle极值较早实现函数可以使用此函数进行代码优化
std::vector< SSG_pntDirAngle> forwardAnglePeakP;
std::vector< SSG_pntDirAngle> forwardAnglePeakM;
wd_searchForwardAnglePeaks(
ptDirAngles,
lineData,
cornerPara.cornerTh,
cornerMergeScale,
forwardAnglePeakP,
forwardAnglePeakM
);
//匹配forwardAnglePeakM和backwardAnglePeakM, forwardAnglePeakM在前backwardAnglePeakM在后
for (int i = 0; i < (int)forwardAnglePeakM.size(); i++)
{
for (int j = 0; j < (int)backwardAnglePeakM.size(); j++)
{
if (backwardAnglePeakM[j].pntIdx > forwardAnglePeakM[i].pntIdx)
{
int pntIdx_1 = forwardAnglePeakM[i].pntIdx;
int pntIdx_2 = backwardAnglePeakM[j].pntIdx;
//计算两者的角度
double diffY = abs(lineData[pntIdx_2].pt3D.y - lineData[pntIdx_1].pt3D.y);
if (diffY < cornerMergeScale)
{
double tanValue = (lineData[pntIdx_2].pt3D.z - lineData[pntIdx_1].pt3D.z) / diffY;
double angle = atan(tanValue) * 180.0 / PI;
if (abs(angle) > cornerPara.cornerTh)
{
SSG_RUN a_seg;
if(lineData[pntIdx_1].pt3D.z < lineData[pntIdx_2].pt3D.z)
a_seg.value = 1;
else
a_seg.value = -1;
a_seg.start = pntIdx_1;
a_seg.len = pntIdx_2 - pntIdx_1 +1;
xyVerticalSegs.push_back(a_seg);
}
}
break;
}
}
}
//匹配forwardAnglePeakP和backwardAnglePeakP, forwardAnglePeakP在前backwardAnglePeakP在后
for (int i = 0; i < (int)forwardAnglePeakP.size(); i++)
{
for (int j = 0; j < (int)backwardAnglePeakP.size(); j++)
{
if (backwardAnglePeakP[j].pntIdx > forwardAnglePeakP[i].pntIdx)
{
int pntIdx_1 = forwardAnglePeakP[i].pntIdx;
int pntIdx_2 = backwardAnglePeakP[j].pntIdx;
//计算两者的角度
double diffY = abs(lineData[pntIdx_2].pt3D.y - lineData[pntIdx_1].pt3D.y);
if (diffY < cornerMergeScale)
{
double tanValue = (lineData[pntIdx_2].pt3D.z - lineData[pntIdx_1].pt3D.z) / diffY;
double angle = atan(tanValue) * 180.0 / PI;
if (abs(angle) > cornerPara.cornerTh)
{
SSG_RUN a_seg;
if (lineData[pntIdx_1].pt3D.z < lineData[pntIdx_2].pt3D.z)
a_seg.value = 1;
else
a_seg.value = -1;
a_seg.start = pntIdx_1;
a_seg.len = pntIdx_2 - pntIdx_1 + 1;
xyVerticalSegs.push_back(a_seg);
}
}
break;
}
}
}
}
/// 提取激光线上的拐点特征。是在PSM LVTypeFeature, BQ等拐点算法的基础上的版本。
/// 使用平均点距进行加速
/// nPointIdx被重新定义成Feature类型

View File

@ -187,6 +187,72 @@ double fitCircleByLeastSquare(
return err;
}
//Ô²×îС¶þ³ËÄâºÏ
double fitCircleByLeastSquare_2(
const std::vector<SVzNL3DPosition>& pointArray,
SVzNL3DPoint& center,
double& radius)
{
int N = pointArray.size();
if (N < 3) {
return std::numeric_limits<double>::max();
}
double sumX = 0.0;
double sumY = 0.0;
double sumX2 = 0.0;
double sumY2 = 0.0;
double sumX3 = 0.0;
double sumY3 = 0.0;
double sumXY = 0.0;
double sumXY2 = 0.0;
double sumX2Y = 0.0;
for (int pId = 0; pId < N; ++pId) {
sumX += pointArray[pId].pt3D.x;
sumY += pointArray[pId].pt3D.y;
double x2 = pointArray[pId].pt3D.x * pointArray[pId].pt3D.x;
double y2 = pointArray[pId].pt3D.y * pointArray[pId].pt3D.y;
sumX2 += x2;
sumY2 += y2;
sumX3 += x2 * pointArray[pId].pt3D.x;
sumY3 += y2 * pointArray[pId].pt3D.y;
sumXY += pointArray[pId].pt3D.x * pointArray[pId].pt3D.y;
sumXY2 += pointArray[pId].pt3D.x * y2;
sumX2Y += x2 * pointArray[pId].pt3D.y;
}
double C, D, E, G, H;
double a, b, c;
C = N * sumX2 - sumX * sumX;
D = N * sumXY - sumX * sumY;
E = N * sumX3 + N * sumXY2 - (sumX2 + sumY2) * sumX;
G = N * sumY2 - sumY * sumY;
H = N * sumX2Y + N * sumY3 - (sumX2 + sumY2) * sumY;
a = (H * D - E * G) / (C * G - D * D);
b = (H * C - E * D) / (D * D - G * C);
c = -(a * sumX + b * sumY + sumX2 + sumY2) / N;
center.x = -a / 2.0;
center.y = -b / 2.0;
radius = sqrt(a * a + b * b - 4 * c) / 2.0;
double err = 0.0;
double e;
double r2 = radius * radius;
for (int pId = 0; pId < N; ++pId) {
e = pow(pointArray[pId].pt3D.x - center.x, 2) + pow(pointArray[pId].pt3D.y - center.y, 2) - r2;
if (e > err) {
err = e;
}
}
return err;
}
#if 0
bool leastSquareParabolaFit(const std::vector<cv::Point2d>& points,
double& a, double& b, double& c,

View File

@ -20,7 +20,8 @@
//version 1.2.9 : 修正螺杆定位算法中轴向拟合计算时方向确定的问题保证投影后的点云与投影前的点云的Z变化趋势一致
//version 1.2.10 : 改进螺杆定位取点算法
//version 1.2.11 : 根据现场数据调整特征生长参数
std::string m_strVersion = "1.2.11";
//version 1.3.0 : 新的定位盘中心测量功能
std::string m_strVersion = "1.3.0";
const char* wd_rodAndBarDetectionVersion(void)
{
return m_strVersion.c_str();
@ -1921,6 +1922,516 @@ SSX_platePoseInfo sx_getLocationPlatePose(
return resultPose;
}
//新的计算定位盘中心点位姿(去除了定位盘,只有定位板)
SSX_platePoseInfo sx_getLocationPlatePose_new(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
int* errCode)
{
*errCode = 0;
SSX_platePoseInfo resultPose;
resultPose.center = { 0, 0, 0 };
resultPose.normalDir = { 0, 0, 0 };
resultPose.holeLT = { 0, 0, 0 };
resultPose.holeRB = { 0, 0, 0 };
resultPose.xDir = { 0, 0, 0 };
resultPose.yDir = { 0, 0, 0 };
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return resultPose;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return resultPose;
}
//产生水平扫描数据
std::vector< std::vector<SVzNL3DPosition>> scanLines_h;
scanLines_h.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
scanLines_h[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
scanLines_h[j][line] = scanLines[line][j];
scanLines_h[j][line].pt3D.x = scanLines[line][j].pt3D.y;
scanLines_h[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
for (int line = 0; line < linePtNum; line++)
{
for (int j = 0, j_max = (int)scanLines_h[line].size(); j < j_max; j++)
scanLines_h[line][j].nPointIdx = j;
}
//算法流程:
//1、检查垂直方向数据并去除
//2、聚类
//3、保留最前面目标
//4、提取孔
//5、拟合
//6、计算中间坐标
std::vector<std::vector<int>> flags;
flags.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
flags[i].resize(linePtNum);
std::fill(flags[i].begin(), flags[i].end(), 0);
}
std::vector<std::vector<SSG_RUN>> zVertivalSegs;
for (int line = 0; line < lineNum; line++)
{
if (line == 700)
int kkk = 1;
std::vector<SSG_RUN> line_verticalSegs;
wd_getXYVertialFeature_peakCornerMethod(
scanLines[line],
line,
cornerPara.scale,
cornerPara,
line_verticalSegs
);
zVertivalSegs.push_back(line_verticalSegs);
for (int i = 0; i < (int)line_verticalSegs.size(); i++)
{
int idx_0 = line_verticalSegs[i].start;
for (int j = 0; j < line_verticalSegs[i].len; j++)
flags[line][idx_0 + j] = 1;
}
}
std::vector<std::vector<SSG_RUN>> zVertivalSegs_h;
for (int line = 0; line < linePtNum; line++)
{
if (line == 1177)
int kkk = 1;
std::vector<SSG_RUN> line_verticalSegs;
wd_getXYVertialFeature_peakCornerMethod(
scanLines_h[line],
line,
cornerPara.scale,
cornerPara,
line_verticalSegs
);
zVertivalSegs_h.push_back(line_verticalSegs);
for (int i = 0; i < (int)line_verticalSegs.size(); i++)
{
int idx_0 = line_verticalSegs[i].start;
for (int j = 0; j < line_verticalSegs[i].len; j++)
flags[idx_0+j][line] = 1;
}
}
//标注
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
}
//将垂直线段去除
std::vector< SVzNL3DPosition> validPoints;
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] > 0)
scanLines[line][j].pt3D.z = 0;
if (scanLines[line][j].pt3D.z > 1e-4)
{
SVzNL3DPosition a_vldPt;
a_vldPt.pt3D = scanLines[line][j].pt3D;
a_vldPt.nPointIdx = (line << 16) | (j & 0xffff);
validPoints.push_back(a_vldPt);
}
}
}
//聚类
//内部参数
double minObjSize_w = 150;
double minObjSize_h = 150;
int clusterCheckWin = 5;
double clusterDist = 5.0;
int distType = 1; //0 - 2d distance; 1- 3d distance
std::vector<std::vector< SVzNL3DPosition>> objClusters; //result
wd_pointClustering_speedUp(
validPoints,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType,
objClusters //result
);
//保留最前面的目标
std::vector<double> objMeanZ;
objMeanZ.resize(objClusters.size());
for (int i = 0; i < (int)objClusters.size(); i++)
{
SSG_ROIRectD a_roi = _getListROI(objClusters[i]);
double w = a_roi.right - a_roi.left;
double h = a_roi.bottom - a_roi.top;
double meanZ = _getListMeanZ(objClusters[i]);
objMeanZ[i] = meanZ;
}
//选出z最小的目标作为顶部轮廓
int objIdx = -1;
double minMeanZ = DBL_MAX;
for (int i = 0; i < (int)objClusters.size(); i++)
{
if (objMeanZ[i] > 1e-4)
{
if (minMeanZ > objMeanZ[i])
{
minMeanZ = objMeanZ[i];
objIdx = i;
}
}
}
if (objIdx < 0)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
std::vector< SVzNL3DPosition>& topCluster = objClusters[objIdx];
//标注
//重新将flags设置为目标的mask
for (int i = 0; i < lineNum; i++)
std::fill(flags[i].begin(), flags[i].end(), -1);
for (int i = 0; i < (int)topCluster.size(); i++)
{
int line = topCluster[i].nPointIdx >> 16;
int ptIdx = topCluster[i].nPointIdx & 0x0000FFFF;
scanLines[line][ptIdx].nPointIdx = 2;
flags[line][ptIdx] = i; //indexing
}
//拟合平面
std::vector<cv::Point3f> Points3ds;
for (int i = 0; i < (int)topCluster.size(); i++)
{
cv::Point3f a_pt = cv::Point3f(topCluster[i].pt3D.x, topCluster[i].pt3D.y, topCluster[i].pt3D.z);
Points3ds.push_back(a_pt);
}
//计算面参数: z = Ax + By + C
//res: [0]=A, [1]= B, [2]=-1.0, [3]=C,
#if 1
std::vector<cv::Point3f> out_inliers;
Plane res = ransacFitPlane(Points3ds, out_inliers);
if (res.C < 0)
{
res.A = -res.A;
res.B = -res.B;
res.C = -res.C;
res.D = -res.D;
}
#else
Plane res = robustFitPlane(Points3ds);
#endif
//计算投影向量
SVzNL3DPoint vec_1 = { res.A, res.B, res.C };
SVzNL3DPoint vec_2 = { 0, 0, 1.0 };
SSG_planeCalibPara poseR = wd_computeRTMatrix(vec_1, vec_2);
//投影
double normDataPlane = sqrt(res.A * res.A + res.B * res.B + res.C * res.C);
std::vector<SVzNL3DPosition> projectPoints3ds;
projectPoints3ds.resize(topCluster.size());
double sum_x = 0, sum_y = 0, sum_z = 0;
int sumZConter = 0;
for (int i = 0; i < (int)topCluster.size(); i++)
{
double distToPlane = abs(res.A * topCluster[i].pt3D.x + res.B * topCluster[i].pt3D.y + res.C * topCluster[i].pt3D.z + res.D) / normDataPlane;
double x = topCluster[i].pt3D.x * poseR.planeCalib[0] + topCluster[i].pt3D.y * poseR.planeCalib[1] + topCluster[i].pt3D.z * poseR.planeCalib[2];
double y = topCluster[i].pt3D.x * poseR.planeCalib[3] + topCluster[i].pt3D.y * poseR.planeCalib[4] + topCluster[i].pt3D.z * poseR.planeCalib[5];
double z = topCluster[i].pt3D.x * poseR.planeCalib[6] + topCluster[i].pt3D.y * poseR.planeCalib[7] + topCluster[i].pt3D.z * poseR.planeCalib[8];
projectPoints3ds[i].nPointIdx = topCluster[i].nPointIdx;
projectPoints3ds[i].pt3D = { x, y, z };
sum_x += x;
sum_y += y;
if (distToPlane < 2.0)
{
sum_z += z;
sumZConter++;
}
}
if(sumZConter == 0)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
double meanZ = sum_z / sumZConter;
//生成孔的边界点
std::vector<std::vector<int>> endingMask;
endingMask.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
endingMask[i].resize(linePtNum);
std::fill(endingMask[i].begin(), endingMask[i].end(), 0);
}
std::vector< SVzNL3DPosition> endingPts;
//垂直
double topZTh = meanZ + 10.0;
for (int line = 0; line < lineNum; line++)
{
int preValid = -1;
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] >= 0)
{
if (preValid == 0)
{
if (endingMask[line][j] == 0)
{
int idx = flags[line][j];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line][j] = 1;
}
}
}
preValid = 1;
}
else
{
if (preValid == 1)
{
if (endingMask[line][j - 1] == 0)
{
int idx = flags[line][j - 1];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line][j - 1] = 1;
}
}
}
preValid = 0;
}
}
}
//水平
for (int j = 0; j < linePtNum; j++)
{
int preValid = -1;
for (int line = 0; line < lineNum; line++)
{
if (flags[line][j] >= 0)
{
if (preValid == 0)
{
if (endingMask[line][j] == 0)
{
int idx = flags[line][j];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line][j] = 1;
}
}
}
preValid = 1;
}
else
{
if (preValid == 1)
{
if (endingMask[line-1][j] == 0)
{
int idx = flags[line-1][j];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line - 1][j] = 1;
}
}
}
preValid = 0;
}
}
}
//标注
for (int i = 0; i < (int)endingPts.size(); i++)
{
int line = endingPts[i].nPointIdx >> 16;
int ptIdx = endingPts[i].nPointIdx & 0x0000FFFF;
scanLines[line][ptIdx].nPointIdx = 3;
}
//孔聚类
clusterCheckWin = 10;
clusterDist = 5.0;
distType = 0; //0 - 2d distance; 1- 3d distance
std::vector<std::vector< SVzNL3DPosition>> holeClusters; //result
wd_pointClustering_speedUp(
endingPts,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType,
holeClusters //result
);
//拟合
std::vector< SVzNL3DPoint> holesCenter;
holesCenter.resize(holeClusters.size());
std::vector<double> holesR;
holesR.resize(holeClusters.size());
//目标圆拟合
//圆拟合
for (int i = 0; i < (int)holeClusters.size(); i++)
{
SVzNL3DPoint a_center;
double a_radius;
double err = fitCircleByLeastSquare_2(holeClusters[i], a_center, a_radius);
holesCenter[i] = a_center;
holesR[i] = a_radius;
}
//计算中心孔的姿态
//目标过滤
//首先挑出最大的孔
const double bigHoleD = 40.0;//大孔直径
const double smallHoleD = 5.0;//大孔直径
std::vector<int> bigHoleIdx;
std::vector<int> smallHoleIdx;
for (int i = 0; i < (int)holeClusters.size(); i++)
{
if ( (holesR[i] > bigHoleD/2) && (holesR[i] < bigHoleD * 1.5))
bigHoleIdx.push_back(i);
else if ( (holesR[i] > smallHoleD/2) && (holesR[i] < smallHoleD * 1.5))
smallHoleIdx.push_back(i);
}
if (bigHoleIdx.size() != 4)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
int holeL_idx = -1, holeR_idx = -1, holeT_idx = -1, holeB_idx = -1;
for (int i = 0; i < (int)bigHoleIdx.size(); i++)
{
int idx = bigHoleIdx[i];
if (holeL_idx < 0)
holeL_idx = idx;
else if (holesCenter[holeL_idx].x > holesCenter[idx].x)
holeL_idx = idx;
if (holeR_idx < 0)
holeR_idx = idx;
else if (holesCenter[holeR_idx].x < holesCenter[idx].x)
holeR_idx = idx;
if (holeT_idx < 0)
holeT_idx = idx;
else if (holesCenter[holeT_idx].y > holesCenter[idx].y)
holeT_idx = idx;
if (holeB_idx < 0)
holeB_idx = idx;
else if (holesCenter[holeB_idx].y < holesCenter[idx].y)
holeB_idx = idx;
}
double ref_x = (holesCenter[holeL_idx].x + holesCenter[holeR_idx].x + holesCenter[holeT_idx].x + holesCenter[holeB_idx].x) / 4.0;
double ref_y = (holesCenter[holeL_idx].y + holesCenter[holeR_idx].y + holesCenter[holeT_idx].y + holesCenter[holeB_idx].y) / 4.0;
//寻找中间孔
int centerHoleIdx = -1;
double minDist = -1;
for (int i = 0; i < (int)smallHoleIdx.size(); i++)
{
int idx = smallHoleIdx[i];
double dist = sqrt(pow(holesCenter[idx].x - ref_x, 2) + pow(holesCenter[idx].y - ref_y, 2));
if (centerHoleIdx < 0)
{
centerHoleIdx = idx;
minDist = dist;
}
else if (minDist > dist)
{
centerHoleIdx = idx;
minDist = dist;
}
}
resultPose.center = { holesCenter[centerHoleIdx].x, holesCenter[centerHoleIdx].y, meanZ};
resultPose.xDir = {0.0, 0.0, -1.0}; // { 1.0, 0.0, 0 };// { 0.0, 0.0, 1.0 };
resultPose.normalDir = { holesCenter[holeR_idx].x - holesCenter[centerHoleIdx].x, holesCenter[holeR_idx].y - holesCenter[centerHoleIdx].y, 0.0 }; // { 1.0, 0.0, 0 };
resultPose.normalDir = vec3_normalize(resultPose.normalDir);
//resultPose.yDir = { 0.0, 1.0, 0 };
//叉乘出y;
//向量叉乘
resultPose.yDir = vec3_cross(resultPose.normalDir, resultPose.xDir);
resultPose.holeLT = resultPose.center;
resultPose.holeRB = resultPose.center;
#if 0
//旋转回去
for (int i = 0; i < lineNum; i++)
{
//行处理
//调平,去除地面
lineDataRT_vector(scanLines[i], poseR.invRMatrix, -1);
}
#endif
double x = resultPose.center.x * poseR.invRMatrix[0] + resultPose.center.y * poseR.invRMatrix[1] + resultPose.center.z * poseR.invRMatrix[2];
double y = resultPose.center.x * poseR.invRMatrix[3] + resultPose.center.y * poseR.invRMatrix[4] + resultPose.center.z * poseR.invRMatrix[5];
double z = resultPose.center.x * poseR.invRMatrix[6] + resultPose.center.y * poseR.invRMatrix[7] + resultPose.center.z * poseR.invRMatrix[8];
resultPose.center = { x, y, z };
x = resultPose.normalDir.x * poseR.invRMatrix[0] + resultPose.normalDir.y * poseR.invRMatrix[1] + resultPose.normalDir.z * poseR.invRMatrix[2];
y = resultPose.normalDir.x * poseR.invRMatrix[3] + resultPose.normalDir.y * poseR.invRMatrix[4] + resultPose.normalDir.z * poseR.invRMatrix[5];
z = resultPose.normalDir.x * poseR.invRMatrix[6] + resultPose.normalDir.y * poseR.invRMatrix[7] + resultPose.normalDir.z * poseR.invRMatrix[8];
resultPose.normalDir = { x, y, z };
x = resultPose.xDir.x * poseR.invRMatrix[0] + resultPose.xDir.y * poseR.invRMatrix[1] + resultPose.xDir.z * poseR.invRMatrix[2];
y = resultPose.xDir.x * poseR.invRMatrix[3] + resultPose.xDir.y * poseR.invRMatrix[4] + resultPose.xDir.z * poseR.invRMatrix[5];
z = resultPose.xDir.x * poseR.invRMatrix[6] + resultPose.xDir.y * poseR.invRMatrix[7] + resultPose.xDir.z * poseR.invRMatrix[8];
resultPose.xDir = { x, y, z };
x = resultPose.yDir.x * poseR.invRMatrix[0] + resultPose.yDir.y * poseR.invRMatrix[1] + resultPose.yDir.z * poseR.invRMatrix[2];
y = resultPose.yDir.x * poseR.invRMatrix[3] + resultPose.yDir.y * poseR.invRMatrix[4] + resultPose.yDir.z * poseR.invRMatrix[5];
z = resultPose.yDir.x * poseR.invRMatrix[6] + resultPose.yDir.y * poseR.invRMatrix[7] + resultPose.yDir.z * poseR.invRMatrix[8];
resultPose.yDir = { x, y, z };
x = resultPose.holeLT.x * poseR.invRMatrix[0] + resultPose.holeLT.y * poseR.invRMatrix[1] + resultPose.holeLT.z * poseR.invRMatrix[2];
y = resultPose.holeLT.x * poseR.invRMatrix[3] + resultPose.holeLT.y * poseR.invRMatrix[4] + resultPose.holeLT.z * poseR.invRMatrix[5];
z = resultPose.holeLT.x * poseR.invRMatrix[6] + resultPose.holeLT.y * poseR.invRMatrix[7] + resultPose.holeLT.z * poseR.invRMatrix[8];
resultPose.holeLT = { x, y, z };
x = resultPose.holeRB.x * poseR.invRMatrix[0] + resultPose.holeRB.y * poseR.invRMatrix[1] + resultPose.holeRB.z * poseR.invRMatrix[2];
y = resultPose.holeRB.x * poseR.invRMatrix[3] + resultPose.holeRB.y * poseR.invRMatrix[4] + resultPose.holeRB.z * poseR.invRMatrix[5];
z = resultPose.holeRB.x * poseR.invRMatrix[6] + resultPose.holeRB.y * poseR.invRMatrix[7] + resultPose.holeRB.z * poseR.invRMatrix[8];
resultPose.holeRB = { x, y, z };
return resultPose;
}
#if 0
void rodAarcFeatueDetection(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
@ -2208,7 +2719,8 @@ void sx_rodPositioning(
pow(startCenter.y - endCenter.y, 2) +
pow(startCenter.z - endCenter.z, 2));
double lenDiff = abs(len - rodParam.len);
if (lenDiff < rodParam.len* 0.15) //validObj
double lenDiff2 = abs(len - 2 * rodParam.len);
if ( (lenDiff < rodParam.len* 0.15) || (lenDiff2 < rodParam.len * 0.15)) //validObj
{
//在XY平面内直线拟合
//为了防止端部影响,跳过端面数据

View File

@ -90,6 +90,12 @@ SG_APISHARED_EXPORT SSX_platePoseInfo sx_getLocationPlatePose(
const SSG_cornerParam cornerPara,
int* errCode);
//新的计算定位盘中心点位姿(去除了定位盘,只有定位板)
SG_APISHARED_EXPORT SSX_platePoseInfo sx_getLocationPlatePose_new(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
int* errCode);
//堵第蚰龰隅弇
SG_APISHARED_EXPORT void sx_rodPositioning(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,