#include #include "SG_baseDataType.h" #include "SG_baseAlgo_Export.h" #include "motorStatorPosition_Export.h" #include #define DEBUG_OUT_IMAGE 1 typedef struct { int objID; cv::Point2f pos; double angle; }SSG_objAngleInfo; typedef struct { bool isSide; double sideDist; }SSG_objSideInfo; typedef struct { int objID; cv::Point2f objPos; SVzNL3DPoint objPos3D; std::vector< SSG_objAngleInfo> neighbours; SSG_objSideInfo sideInfo[4]; //对应L,R,T,P }SSG_statorNeighbourInfo; typedef struct { int ID; int linkNum; int neighborID[6]; }SSG_hexagonNeighbour; typedef struct { cv::RotatedRect fittingPara; SVzNL3DPoint objCenter; }SG_fittingInfo; std::string m_strVersion = "1.0.0"; const char* wd_particleSegVersion(void) { return m_strVersion.c_str(); } //计算一个平面调平参数。 //数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平 //旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数 SSG_planeCalibPara wd_getBaseCalibPara( std::vector< std::vector>& scanLines) { return sg_getPlaneCalibPara2(scanLines); } //相机姿态调平,并去除地面 void wd_lineDataR( std::vector< SVzNL3DPosition>& a_line, const double* camPoseR, double groundH) { lineDataRT_vector(a_line, camPoseR, groundH); } SSG_objSideInfo _getSideX( int cx, int cy, cv::Mat& zSliceData, bool dirLeft) { int edge_x = -1; SSG_objSideInfo sideInfo; sideInfo.isSide = false; sideInfo.sideDist = -1; if (true == dirLeft) { for (int m = cx; m >= 0; m--) { if (zSliceData.at(cy, m) > 0) edge_x = m; } } else { for (int m = cx; m < zSliceData.cols; m++) { if (zSliceData.at(cy, m) > 0) edge_x = m; } } if(edge_x >= 0) { sideInfo.isSide = true; sideInfo.sideDist = edge_x; } return sideInfo; } SSG_objSideInfo _getSideY( int cx, int cy, cv::Mat& zSliceData, bool dirUp) { int edge_y = -1; SSG_objSideInfo sideInfo; sideInfo.isSide = false; sideInfo.sideDist = -1; if (true == dirUp) { for (int m = cy; m >= 0; m--) { if (zSliceData.at(m, cx) > 0) edge_y = m; } } else { for (int m = cy; m < zSliceData.rows; m++) { if (zSliceData.at(m, cx) > 0) edge_y = m; } } if (edge_y >= 0) { sideInfo.isSide = true; sideInfo.sideDist = edge_y; } sideInfo.isSide = true; return sideInfo; } void _getNeighbouringInfo( const SWD_statorParam positionParam, cv::Mat& zSliceData, //用于寻找边界(边框) std::vector& Objects, //目标位置 std::vector< SSG_statorNeighbourInfo>& neighbouringInfo //邻接关系 ) { double searchWin = positionParam.statorOuterD * 2.5; int objNum = Objects.size(); neighbouringInfo.resize(objNum); for (int i = 0; i < objNum; i++) { SG_fittingInfo* a_obj = &Objects[i]; SSG_statorNeighbourInfo* obj_info = &neighbouringInfo[i]; obj_info->objID = i; obj_info->objPos3D = a_obj->objCenter; obj_info->objPos = a_obj->fittingPara.center; bool checkSide[4]; for (int m = 0; m < 4; m++) checkSide[m] = true; for (int j = 0; j < objNum; j++) { if (j != i) { //计算中心点距离 double dist = sqrt(pow(a_obj->fittingPara.center.x - Objects[j].fittingPara.center.x, 2) + pow(a_obj->fittingPara.center.y - Objects[j].fittingPara.center.y, 2)); if (dist < searchWin) { SSG_objAngleInfo a_neighbour; a_neighbour.objID = j; a_neighbour.pos = Objects[j].fittingPara.center; //计算角度 double angle = atan2(Objects[j].fittingPara.center.y - a_obj->fittingPara.center.y, Objects[j].fittingPara.center.x - a_obj->fittingPara.center.x); angle = angle * 180.0 / PI; //转变成角度 if (angle < 0) angle += 360; //转变成0-360度范围 a_neighbour.angle = angle; if ((angle > 345) || (angle < 15)) checkSide[0] = false; else if ((angle > 75) && (angle < 105)) checkSide[1] = false; else if ((angle > 165) && (angle < 195)) checkSide[2] = false; else if ((angle > 255) && (angle < 285)) checkSide[3] = false; obj_info->neighbours.push_back(a_neighbour); } } } //分析neighbour情况,判断是否要检查side情况 int cx = (int)a_obj->fittingPara.center.x; int cy = (int)a_obj->fittingPara.center.y; if (false == checkSide[0]) { obj_info->sideInfo[0].isSide = false; obj_info->sideInfo[0].sideDist = -1; } else { SSG_objSideInfo sideInfo = _getSideX(cx, cy, zSliceData, true); obj_info->sideInfo[0] = sideInfo; } if (false == checkSide[1]) { obj_info->sideInfo[1].isSide = false; obj_info->sideInfo[1].sideDist = -1; } else { SSG_objSideInfo sideInfo = _getSideX(cx, cy, zSliceData, false); obj_info->sideInfo[1] = sideInfo; } if (false == checkSide[2]) { obj_info->sideInfo[2].isSide = false; obj_info->sideInfo[2].sideDist = -1; } else { SSG_objSideInfo sideInfo = _getSideY(cx, cy, zSliceData, true); obj_info->sideInfo[2] = sideInfo; } if (false == checkSide[3]) { obj_info->sideInfo[3].isSide = false; obj_info->sideInfo[3].sideDist = -1; } else { SSG_objSideInfo sideInfo = _getSideY(cx, cy, zSliceData, false); obj_info->sideInfo[3] = sideInfo; } } return; } void _getPtsApart120(cv::Point2f center, double R, double angle, cv::Point2f* testPts) { double theta_0 = angle * PI / 180; double theta_120 = angle + 120; if (theta_120 > 360) theta_120 = theta_120 - 360; theta_120 = theta_120 * PI / 180; double theta_240 = angle + 240; if (theta_240 > 360) theta_240 = theta_240 - 360; theta_240 = theta_240 *PI / 180; testPts[0].x = center.x + R * cos(theta_0); testPts[0].y = center.y - R * sin(theta_0); testPts[1].x = center.x + R * cos(theta_120); testPts[1].y = center.y - R * sin(theta_120); testPts[2].x = center.x + R * cos(theta_240); testPts[2].y = center.y - R * sin(theta_240); } double _getMinDist( SSG_statorNeighbourInfo& neighbouringInfo, cv::Point2f* testPts ) { double min_dist = -1; for (int i = 0; i < 3; i++) { cv::Point2f* a_pt = &testPts[i]; for (int j = 0; j < neighbouringInfo.neighbours.size(); j++) { cv::Point2f* n_pt = &(neighbouringInfo.neighbours[j].pos); double dist = sqrt(pow(a_pt->x - n_pt->x, 2) + pow(a_pt->y - n_pt->y, 2)); if (min_dist < 0) min_dist = dist; else if (min_dist > dist) min_dist = dist; } if (true == neighbouringInfo.sideInfo[0].isSide) { double dist = a_pt->x - neighbouringInfo.sideInfo[0].sideDist;//外边界 dist = dist - 10; //内边界处需要外边界减去壁厚。此处壁厚使用10mm if (dist < 0) dist = 0; if (min_dist < 0) min_dist = dist; else if (min_dist > dist) min_dist = dist; } if (true == neighbouringInfo.sideInfo[1].isSide) { double dist = neighbouringInfo.sideInfo[1].sideDist - a_pt->x;//外边界 dist = dist - 10; //内边界处需要外边界减去壁厚。此处壁厚使用10mm if (dist < 0) dist = 0; if (min_dist < 0) min_dist = dist; else if (min_dist > dist) min_dist = dist; } if (true == neighbouringInfo.sideInfo[2].isSide) { double dist = a_pt->y - neighbouringInfo.sideInfo[2].sideDist;//外边界 dist = dist - 10; //内边界处需要外边界减去壁厚。此处壁厚使用10mm if (dist < 0) dist = 0; if (min_dist < 0) min_dist = dist; else if (min_dist > dist) min_dist = dist; } if (true == neighbouringInfo.sideInfo[3].isSide) { double dist = neighbouringInfo.sideInfo[3].sideDist - a_pt->y;//外边界 dist = dist - 10; //内边界处需要外边界减去壁厚。此处壁厚使用10mm if (dist < 0) dist = 0; if (min_dist < 0) min_dist = dist; else if (min_dist > dist) min_dist = dist; } } return min_dist; } void _computeGripperPose( SSG_statorNeighbourInfo& neighbouringInfo, double gripperR, double* opAngle, double* obstacleDist) { //根据相邻目标数量确定抓取点 //圆周扫描获取距离相邻目标最小距离最大的点 double searchStepping = 0.5; //搜索精度为0.5度 int steps = (int)(360.0 / searchStepping); double max_dist = -1; double max_angle = -1; for (int i = 0; i < steps; i++) { double angle = i * searchStepping; cv::Point2f testPts[3]; _getPtsApart120(neighbouringInfo.objPos, gripperR, angle, testPts); //计算最小距离,相邻目标和边界统一在一起计算 double min_dist = _getMinDist(neighbouringInfo, testPts); //获取最小距离的最大值,对应的角度为操作角度 if (max_dist < 0) { max_dist = min_dist; max_angle = angle; } else if(max_dist < min_dist) { max_dist = min_dist; max_angle = angle; } } *opAngle = max_angle; *obstacleDist = max_dist; return; } bool compareByNeighbourNumber(const SSG_hexagonNeighbour& a, const SSG_hexagonNeighbour& b) { return a.linkNum < b.linkNum; } //圆周扫描定子环 //以scanCenter为扫描中心,圆周扫描从scanR1到scanR2的圆形区域 void circileScan( cv::Mat& scanImg, //扫描对象 int scanCenterX, int scanCenterY, //扫描中心 double scanR1, double scanR2, //扫描区域 std::vector& peakPts //扫描结果 ) { //以1度间隔扫描 double scale = 1.0; for (int i = 0; i < 360; i++) { double angle = (double)i * scale * PI / 180; double sinTheta = sin(angle); double cosTheta = cos(angle); int x0 = scanCenterX + (int)(scanR1 * cosTheta); int y0 = scanCenterY - (int)(scanR1 * sinTheta); int x1 = scanCenterX + (int)(scanR2 * cosTheta); int y1 = scanCenterY - (int)(scanR2 * sinTheta); //Bresenham算法 std::vector pts; drawLine(x0, y0, x1, y1, pts); //搜索峰值 float peakValue = 0; SVzNL2DPoint peakPos = { 0,0 }; for (int m = 0, m_max = (int)pts.size(); m < m_max; m++) { if (peakValue < scanImg.at(pts[m].y, pts[m].x)) { peakValue = scanImg.at(pts[m].y, pts[m].x); peakPos = pts[m]; } } peakPts.push_back(peakPos); } } //对定子外圈抓取的三爪抓手计算旋转位置。 //三爪对称,分别在30,150,270度。根据抓取角度,计算最小旋转角度 double _computeGrasperRotateAngle(double grasperAngle) { double rotateAngle = 0; if ((grasperAngle >= 330) || (grasperAngle < 90)) //丫第一个枝位置30度 { if (grasperAngle < 90) rotateAngle = grasperAngle - 30; else rotateAngle = grasperAngle - 360 - 30; } else if ((grasperAngle >= 90) && (grasperAngle < 210))//丫第二个枝位置150度 rotateAngle = grasperAngle - 150; else rotateAngle = grasperAngle - 270; return rotateAngle; } //计算两个角的间隔(绝对值),结果在0-180范围 double _computeAbsAngleInterval(double angle_0, double angle_1) { double diff = angle_0 < angle_1 ? (angle_1 - angle_0) : (angle_0 - angle_1); if (diff > 180) diff = 360 - diff; return diff; } //计算角度与相邻目标的角度间隔,以其中最小值为结果 double _computeObjAngleInterval(double angle_0, std::vector& linkObjAngles) { if (linkObjAngles.size() == 0) return -1; double minInterval = _computeAbsAngleInterval(angle_0, linkObjAngles[0]); for (int i = 1, i_max = (int)linkObjAngles.size(); i < i_max; i++) { double angleInterval = _computeAbsAngleInterval(angle_0, linkObjAngles[i]); if (minInterval > angleInterval) minInterval = angleInterval; } return minInterval; } //计算目标最佳抓取角度。抓手为三爪设计,间隔120度。 //扫描与相邻目标最大的角度间隔点。此位置为最佳抓取位置 double _scanBestGrasperAngle(std::vector& linkObjAngles, double* angleDistance) { *angleDistance = 0; if (linkObjAngles.size() == 0) return -1; //以0.1度间隔扫描120度范围 double maxAngleDist = -1; double bestAngleIdx = -1; //粗定位,精确到1度 for (int i = 0; i < 120; i++) { double angle_0 = (double)i; double angle_1 = angle_0 + 120; if (angle_1 >= 360) angle_1 = angle_1 - 360; double angle_2 = angle_0 + 240; if (angle_2 >= 360) angle_2 = angle_2 - 360; double angleInterval_0 = _computeObjAngleInterval(angle_0, linkObjAngles); double angleInterval_1 = _computeObjAngleInterval(angle_1, linkObjAngles); double angleInterval_2 = _computeObjAngleInterval(angle_2, linkObjAngles); double angleInterval = angleInterval_0 < angleInterval_1 ? angleInterval_0 : angleInterval_1; angleInterval = angleInterval < angleInterval_2 ? angleInterval : angleInterval_2; if (maxAngleDist < 0) { maxAngleDist = angleInterval; bestAngleIdx = (double)i; } else { if (maxAngleDist < angleInterval) { maxAngleDist = angleInterval; bestAngleIdx = (double)i; } } } if (bestAngleIdx < 0) return -1; //精定位, 精确到0.1度 maxAngleDist = -1; double bestAngle = bestAngleIdx; for (int i = -10; i <= 10; i++) { double angle_0 = (double)i * 0.1 + bestAngleIdx; double angle_1 = angle_0 + 120; if (angle_1 >= 360) angle_1 = angle_1 - 360; double angle_2 = angle_0 + 240; if (angle_2 >= 360) angle_2 = angle_2 - 360; double angleInterval_0 = _computeObjAngleInterval(angle_0, linkObjAngles); double angleInterval_1 = _computeObjAngleInterval(angle_1, linkObjAngles); double angleInterval_2 = _computeObjAngleInterval(angle_2, linkObjAngles); double angleInterval = angleInterval_0 < angleInterval_1 ? angleInterval_0 : angleInterval_1; angleInterval = angleInterval < angleInterval_2 ? angleInterval : angleInterval_2; if (maxAngleDist < 0) { maxAngleDist = angleInterval; bestAngle = angle_0; } else { if (maxAngleDist < angleInterval) { maxAngleDist = angleInterval; bestAngle = angle_0; } } } *angleDistance = maxAngleDist; if (bestAngle < 0) bestAngle = bestAngle + 360; return bestAngle; } bool computeOuterGraspPoint( int objID, double outerR, std::vector< SSG_peakRgnInfo>& objects, std::vector< SSG_hexagonNeighbour>& objLinkings, SWD_statorOuterGrasper& graspPoint, double* angleDistance ) { //R = 1.155outR SSG_hexagonNeighbour& a_link = objLinkings[objID]; std::vector linkIDs; for (int i = 0; i < 6; i++) { if (a_link.neighborID[i] >= 0) { linkIDs.push_back(a_link.neighborID[i]); } } if (linkIDs.size() != a_link.linkNum) return false; //计算抓取位置 if (a_link.linkNum == 0) //默认抓取位置 { graspPoint.objID = objID; graspPoint.grasperAngle = 0; graspPoint.rotateAngle = 0; graspPoint.graspR = 1.2 * outerR; *angleDistance = 120; return true; } else if (a_link.linkNum == 1) //与目标连线方向偏离60度 { int linkID = linkIDs[0]; SSG_peakRgnInfo& obj = objects[objID]; SSG_peakRgnInfo& nObj = objects[linkID]; double dy = nObj.pos2D.y - obj.pos2D.y; double dx = nObj.pos2D.x - obj.pos2D.x; double dist = sqrt(pow(dx, 2) + pow(dy, 2)); double angle = atan2(dy, dx) * 180.0 / PI + 180; //0-360度 double grasperAngle = angle + 60; if (grasperAngle >= 360) grasperAngle = grasperAngle - 360; double rotateAngle = _computeGrasperRotateAngle(grasperAngle); graspPoint.grasperAngle = grasperAngle; graspPoint.rotateAngle = rotateAngle; graspPoint.graspR = 1.2 * outerR; graspPoint.objID = objID; *angleDistance = 60; return true; } else if ( (a_link.linkNum == 2) || (a_link.linkNum == 3)) //目标有两个或3个相邻目标 { std::vector linkObjAngles; for (int i = 0, i_max = (int)linkIDs.size(); i < i_max; i++) { int linkID = linkIDs[i]; SSG_peakRgnInfo& obj = objects[objID]; SSG_peakRgnInfo& nObj = objects[linkID]; double dy = nObj.pos2D.y - obj.pos2D.y; double dx = nObj.pos2D.x - obj.pos2D.x; double dist = sqrt(pow(dx, 2) + pow(dy, 2)); double angle = atan2(dy, dx) * 180.0 / PI + 180; //0-360度 linkObjAngles.push_back(angle); } double grasperAngle = _scanBestGrasperAngle(linkObjAngles, angleDistance); double rotateAngle = _computeGrasperRotateAngle(grasperAngle); graspPoint.grasperAngle = grasperAngle; graspPoint.rotateAngle = rotateAngle; graspPoint.graspR = 1.155 * outerR; graspPoint.objID = objID; return true; } else return false; } //切割 void cloudPointsFilter_zRange(std::vector< std::vector>& scanLines, SVzNLRangeD zRange, std::vector< std::vector>& cutData) { int lineNum = (int)scanLines.size(); cutData.resize(lineNum); for (int line = 0; line < lineNum; line++) { int ptNum = (int)scanLines[line].size(); cutData[line].resize(ptNum); for (int i = 0; i < ptNum; i++) { SVzNL3DPosition a_pt = scanLines[line][i]; if ((a_pt.pt3D.z < zRange.min) || (a_pt.pt3D.z > zRange.max)) a_pt.pt3D.z = 0; cutData[line][i] = a_pt; } } } //对无点区聚类 void genCLusterMask( std::vector< std::vector>& clusterSrcData, bool maskType_null_vldPt, //true: nullPt(z=0); false:valid Point std::vector>& featureInfoMask, std::vector>& feature3DInfo) { int lineNum = (int)clusterSrcData.size(); if (lineNum == 0) return; int linePtNum = (int)clusterSrcData[0].size(); featureInfoMask.resize(lineNum); feature3DInfo.resize(lineNum); for (int i = 0; i < lineNum; i++) { featureInfoMask[i].resize(linePtNum); feature3DInfo[i].resize(linePtNum); } //生成Mask for (int line = 0; line < lineNum; line++) { std::vector& lineData = clusterSrcData[line]; for (int ptIdx = 0; ptIdx < linePtNum; ptIdx++) { if( ((false == maskType_null_vldPt)&&(clusterSrcData[line][ptIdx].pt3D.z > 1e-4))|| ((true == maskType_null_vldPt) && (clusterSrcData[line][ptIdx].pt3D.z < 1e-4))) { SSG_featureClusteringInfo a_mask; memset(&a_mask, 0, sizeof(SSG_featureClusteringInfo)); a_mask.featurType = 1; a_mask.lineIdx = line; a_mask.ptIdx = ptIdx; featureInfoMask[line][ptIdx] = a_mask; feature3DInfo[line][ptIdx] = clusterSrcData[line][ptIdx].pt3D; } } } return; } //聚类:对无效点的聚类实际上是连通域处理 void cloutPointsClustering_nullPt( std::vector< std::vector>& clusterSrcData, std::vector>& featureInfoMask, std::vector>& feature3DInfo, std::vector>& clusters, //只记录位置 std::vector& clustersInfo) { int lineNum = (int)clusterSrcData.size(); if (lineNum == 0) return; int linePtNum = (int)clusterSrcData[0].size(); //采用迭代思想,回归思路进行高效聚类 int clusterID = 1; int clusterCheckWin = 5; for (int y = 0; y < linePtNum; y++) { for (int x = 0; x < lineNum; x++) { SSG_featureClusteringInfo& a_featureInfo = featureInfoMask[x][y]; if ((0 == a_featureInfo.featurType) || (a_featureInfo.clusterID > 0)) //非特征或已经处理 continue; SVzNL3DPoint& a_feature3DValue = feature3DInfo[x][y]; SVzNL3DRangeD a_clusterRoi3D; memset(&a_clusterRoi3D, 0, sizeof(SVzNL3DRangeD)); SVzNLRect clusterRoi2D; clusterRoi2D.left = x; clusterRoi2D.right = x; clusterRoi2D.top = y; clusterRoi2D.bottom = y; SVzNL2DPoint a_seedPos = { x, y }; std::vector< SVzNL2DPoint> a_cluster; a_cluster.push_back(a_seedPos); //使用聚类方法完成8连通连通域分析 wd_gridPointClustering_labelling( featureInfoMask, feature3DInfo, clusterID, //当前Cluster的ID a_cluster, //result clusterRoi2D //roi2D ); clusters.push_back(a_cluster); SWD_clustersInfo a_info; a_info.clusterIdx = clusterID; a_info.ptSize = (int)a_cluster.size(); a_info.roi2D = clusterRoi2D; a_info.roi3D = a_clusterRoi3D; clustersInfo.push_back(a_info); clusterID++; } } } //聚类 void cloutPointsClustering( std::vector< std::vector>& clusterSrcData, std::vector>& featureInfoMask, std::vector>& feature3DInfo, SSG_treeGrowParam growParam, std::vector>& clusters, //只记录位置 std::vector& clustersInfo) { int lineNum = (int)clusterSrcData.size(); if (lineNum == 0) return; int linePtNum = (int)clusterSrcData[0].size(); //采用迭代思想,回归思路进行高效聚类 int clusterID = 1; int clusterCheckWin = 5; for (int y = 0; y < linePtNum; y++) { for (int x = 0; x < lineNum; x++) { SSG_featureClusteringInfo& a_featureInfo = featureInfoMask[x][y]; if ((0 == a_featureInfo.featurType) || (a_featureInfo.clusterID > 0)) //非特征或已经处理 continue; SVzNL3DPoint& a_feature3DValue = feature3DInfo[x][y]; SVzNL3DRangeD a_clusterRoi; a_clusterRoi.xRange.min = a_feature3DValue.x; a_clusterRoi.xRange.max = a_feature3DValue.x; a_clusterRoi.yRange.min = a_feature3DValue.y; a_clusterRoi.yRange.max = a_feature3DValue.y; a_clusterRoi.zRange.min = a_feature3DValue.z; a_clusterRoi.zRange.max = a_feature3DValue.z; SVzNL2DPoint a_seedPos = { x, y }; std::vector< SVzNL2DPoint> a_cluster; a_cluster.push_back(a_seedPos); wd_gridPointClustering( featureInfoMask,//int,记录特征标记和clusterID,附加一个flag feature3DInfo,//double,记录坐标信息 clusterCheckWin, //搜索窗口 growParam,//聚类条件 clusterID, //当前Cluster的ID a_cluster, //result a_clusterRoi ); clusters.push_back(a_cluster); SWD_clustersInfo a_info; a_info.clusterIdx = clusterID; a_info.ptSize = (int)a_cluster.size(); a_info.roi3D = a_clusterRoi; clustersInfo.push_back(a_info); clusterID++; } } } //生成点的Mask, 使用SSG_clusterLabel结构 void genValidPtLabelMask( std::vector< std::vector>& srcData, std::vector>& labelMask, EWD_maskMode maskMode) { int lineNum = (int)srcData.size(); labelMask.resize(lineNum); for(int i = 0; i < lineNum; i ++) { int ptNum = (int)srcData[i].size(); labelMask[i].resize(ptNum); std::fill(labelMask[i].begin(), labelMask[i].end(), SSG_clusterLabel{ 0,0,0 }); for (int j = 0; j < ptNum; j++) { if (KeWD_Mask_ValidPt == maskMode) { if (srcData[i][j].z > 1e-4) labelMask[i][j].validFlag = 1; } else if(KeWD_Mask_NullPt == maskMode) { if (srcData[i][j].z < 1e-4) labelMask[i][j].validFlag = 1; } } } return; } //生成点的Mask, 使用SSG_clusterLabel结构 int countQuantiSize( std::vector< std::vector>& srcData, EWD_maskMode maskMode) { int totalNum = 0; int lineNum = (int)srcData.size(); for (int i = 0; i < lineNum; i++) { int ptNum = (int)srcData[i].size(); for (int j = 0; j < ptNum; j++) { if (KeWD_Mask_ValidPt == maskMode) { if (srcData[i][j].z > 1e-4) totalNum ++; } else if (KeWD_Mask_NullPt == maskMode) { if (srcData[i][j].z < 1e-4) totalNum ++; } } } return totalNum; } //点云聚类 //SSG_intPair成员变量定义: // data_0: flag // data_1: valid point flag // idx: cluster ID void cloutPointsClustering_2( std::vector< std::vector>& clusterSrcData, std::vector>& labelMask, std::vector>& clusters, //只记录位置 std::vector& clustersInfo) { int lineNum = (int)clusterSrcData.size(); if (lineNum == 0) return; int linePtNum = (int)clusterSrcData[0].size(); //采用迭代思想,回归思路进行高效聚类 int clusterID = 1; int clusterCheckWin = 5; for (int y = 0; y < linePtNum; y++) { for (int x = 0; x < lineNum; x++) { SSG_clusterLabel& a_seedInfo = labelMask[x][y]; if ((0 == a_seedInfo.validFlag) || (a_seedInfo.clusterID > 0)) //非特征或已经处理 continue; SVzNL3DPoint a_feature3DValue = clusterSrcData[x][y]; SVzNL3DRangeD a_clusterRoi; a_clusterRoi.xRange.min = a_feature3DValue.x; a_clusterRoi.xRange.max = a_feature3DValue.x; a_clusterRoi.yRange.min = a_feature3DValue.y; a_clusterRoi.yRange.max = a_feature3DValue.y; a_clusterRoi.zRange.min = a_feature3DValue.z; a_clusterRoi.zRange.max = a_feature3DValue.z; SVzNL2DPoint a_seedPos = { x, y }; std::vector< SVzNL2DPoint> a_cluster; a_cluster.push_back(a_seedPos); wd_gridPointClustering_labelling_2( clusterSrcData, labelMask, clusterID, //当前Cluster的ID a_cluster, //result a_clusterRoi //roi2D ); clusters.push_back(a_cluster); SWD_clustersInfo a_info; a_info.clusterIdx = clusterID; a_info.ptSize = (int)a_cluster.size(); a_info.roi3D = a_clusterRoi; clustersInfo.push_back(a_info); clusterID++; } } } //根据孔洞大小过滤合格目标 void clusterSizeMatching( std::vector>& clusters, std::vector& clustersInfo, const SWD_statorParam statorParam, std::vector& validClusterIndice //合格目标的序号。此处使用序号,避免数据拷贝 ) { double objSizeTh_min = statorParam.statorInnerD * 0.75; double objSizeTh_max = statorParam.statorInnerD * 1.25; int clusterNum = (int)clusters.size(); for (int i = 0; i < clusterNum; i++) { double xw = clustersInfo[i].roi3D.xRange.max - clustersInfo[i].roi3D.xRange.min; double yw = clustersInfo[i].roi3D.yRange.max - clustersInfo[i].roi3D.yRange.min; if ((xw >= objSizeTh_min) && (xw <= objSizeTh_max) && (yw >= objSizeTh_min) && (yw <= objSizeTh_max)) validClusterIndice.push_back(i); } return; } SSG_peakRgnInfo _computeObjInfo(int cluterIndex, std::vector>& clusters, std::vector& clustersInfo, std::vector>& srcData, const double quatiStatorR ) { SSG_peakRgnInfo a_obj; memset(&a_obj, 0, sizeof(SSG_peakRgnInfo)); a_obj.pkRgnIdx = -1; int counterNum = (int)clusters[cluterIndex].size(); if (counterNum == 0) return a_obj; SVzNL3DPoint centerPos = { 0, 0, 0 }; SVzNL2DPoint center2D = { 0, 0 }; for (int i = 0; i < counterNum; i++) { SVzNL2DPoint& a_pt = clusters[cluterIndex][i]; center2D.x += a_pt.x; center2D.y += a_pt.y; centerPos.x += srcData[a_pt.x][a_pt.y].x; centerPos.y += srcData[a_pt.x][a_pt.y].y; } a_obj.pkRgnIdx = cluterIndex; a_obj.pos2D.x = center2D.x / counterNum; a_obj.pos2D.y = center2D.y / counterNum; //计算Z:取周围的Z的最小值 int winR = (int)quatiStatorR + 1; int xMax = srcData.size(); int yMax = srcData[0].size(); double cx = a_obj.pos2D.x; double cy = a_obj.pos2D.y; double minZ = DBL_MAX; for (int x = a_obj.pos2D.x - winR; x <= a_obj.pos2D.x + winR; x++) { for (int y = a_obj.pos2D.y - winR; y <= a_obj.pos2D.y + winR; y++) { if ((x >= 0) && (x < xMax) && (y >= 0) && (y < yMax)) { double dx = x; double dy = y; double dist = sqrt(pow(dx - cx, 2) + pow(dy - cy, 2)); if (dist < quatiStatorR) { if ((srcData[x][y].z > 1e-4) && (minZ > srcData[x][y].z)) { minZ = srcData[x][y].z; } } } } } a_obj.centerPos.x = centerPos.x / counterNum; a_obj.centerPos.y = centerPos.y / counterNum; a_obj.centerPos.z = minZ; a_obj.objSize.dWidth = clustersInfo[cluterIndex].roi3D.xRange.max - clustersInfo[cluterIndex].roi3D.xRange.min; a_obj.objSize.dHeight = clustersInfo[cluterIndex].roi3D.yRange.max - clustersInfo[cluterIndex].roi3D.yRange.min; return a_obj; } void computeBestObject( std::vector& objectRgns, const SWD_statorParam statorParam, SWD_statorGriperState* opState, std::vector& resultObjPositions) { int objNumber = (int)objectRgns.size(); //计算抓取点 //建立邻接关系,每个目标最多有6个邻接目标 std::vector< int> objLinkings; objLinkings.resize(objNumber);//第一个不使用。目标ID从1开始,保持ID与数组下标一致 for (int i = 0; i < objNumber; i++) { objLinkings[i] = 0; } double linkPixDistTh = statorParam.statorOuterD * 1.5; for (int i = 0; i < objNumber; i++) { SSG_peakRgnInfo& obj_0 = objectRgns[i]; for (int j = i + 1; j < objNumber; j++) { SSG_peakRgnInfo& obj_1 = objectRgns[j]; double dy = obj_1.pos2D.y - obj_0.pos2D.y; double dx = obj_1.pos2D.x - obj_0.pos2D.x; double dist = sqrt(pow(dx, 2) + pow(dy, 2)); if (dist < linkPixDistTh) { objLinkings[i]++; objLinkings[j]++; } } } //确定最佳抓取点 if (opState->refPos.z > 1e-4) //根据上一次给定的位置进行抓取 { int objID = -1; double minDist = -1; double refx = opState->refPos.x; double refy = opState->refPos.y; for (int i = 0; i < objNumber; i++) //以最近的目标作为抓取目标 { double dist = sqrt(pow(objectRgns[i].centerPos.x - refx, 2) + pow(objectRgns[i].centerPos.y - refy, 2)); if (minDist < 0) { minDist = dist; objID = i; } else { if (minDist > dist) { minDist = dist; objID = i; } } } SWD_statorInnerGrasper innerGrasper; innerGrasper.objID = objID; innerGrasper.opCenter = objectRgns[objID].centerPos; resultObjPositions.push_back(innerGrasper); //将其它目标列出,用于Debug,检查其它目标是否准确 for (int i = 0; i < objNumber; i++) { if (i != objID) { innerGrasper.objID = i; innerGrasper.opCenter = objectRgns[i].centerPos; resultObjPositions.push_back(innerGrasper); } } } else { //统计相邻目标数量 int minLinkNum = -1; int minLindID = -1; for (int i = 0; i < objNumber; i++) { if (minLinkNum < 0) { minLinkNum = objLinkings[i]; minLindID = i; } else { if (minLinkNum > objLinkings[i]) { minLinkNum = objLinkings[i]; minLindID = i; } } } //将相邻目标数量最少的目标定义为抓取目标 SWD_statorInnerGrasper innerGrasper; innerGrasper.objID = minLindID; innerGrasper.opCenter = objectRgns[minLindID].centerPos; resultObjPositions.push_back(innerGrasper); //将其它目标列出,用于Debug,检查其它目标是否准确 for (int i = 0; i < objNumber; i++) { if (i != minLindID) { innerGrasper.objID = i; innerGrasper.opCenter = objectRgns[i].centerPos; resultObjPositions.push_back(innerGrasper); } } } } void getDistanceTransformMaskData( std::vector>& quantiData, cv::Mat& projectionData//投影量化数据,初始化为一个极大值1e+6 ) { for (int row = 0; row < (int)quantiData.size(); row++) { for (int col = 0; col < (int)quantiData[row].size(); col++) { if(quantiData[row][col].z > 1e-4) projectionData.at(row, col) = 1e+6; } } return; } //电机定子定位。 //输出:(1)定子目标(2)隔板抓取点 //算法逻辑:找到定子的高度->设置截取Z去掉底面-> // 投影(注意此时边框也同时投影)->距离变换->提取定子目标-> // 根据相邻和边框寻找最佳抓取目标和抓取点 //使用状态机记忆每一步操作。这种状态机适用于料筐中多层工件的按层抓取 void wd_motorStatorPosition( std::vector< std::vector>& scanLinesInput, const SWD_statorParam statorParam, const SSG_planeCalibPara groundCalibPara, const SWD_statorPositonParam algoParam, SWD_statorGriperState* opState, //操作状态机。指示当前状态 int* errCode, std::vector& resultObjPositions) { int lineNum = (int)scanLinesInput.size(); if (lineNum == 0) { *errCode = SG_ERR_3D_DATA_NULL; return; } std::vector< std::vector> scanLines; scanLines.resize(lineNum); int linePtNum = (int)scanLinesInput[0].size(); bool isGridData = true; for (int i = 0; i < lineNum; i++) { if (linePtNum != (int)scanLinesInput[i].size()) isGridData = false; scanLines[i].resize(scanLinesInput[i].size()); std::copy(scanLinesInput[i].begin(), scanLinesInput[i].end(), scanLines[i].begin()); // 使用std::copy算法 } if (false == isGridData)//数据不是网格格式 { *errCode = SG_ERR_NOT_GRID_FORMAT; return; } for (int i = 0; i < lineNum; i++) { //行处理 //调平,去除地面 wd_lineDataR(scanLines[i], groundCalibPara.planeCalib, -1); } //噪点过滤 wd_noiseFilter(scanLines, algoParam.filterParam, errCode); if (*errCode != 0) return; ///开始数据处理 double cutZ_topLevel = groundCalibPara.planeHeight - statorParam.plateThickness - statorParam.statorHeight * 1.5; double cutZ_btmLevel = groundCalibPara.planeHeight - statorParam.statorHeight * 0.5; //顶层数据 std::vector< std::vector> cutData_topLevel; cutData_topLevel.resize(lineNum); //底层数据 std::vector< std::vector> cutData_btmLevel; cutData_btmLevel.resize(lineNum); int ptNum_total = 0; int ptNum_topLevel = 0; int ptNum_btmLevel = 0; //使用cutZ_level进行切割 for (int line = 0; line < lineNum; line++) { //top level cutData_topLevel[line].resize(linePtNum); std::fill(cutData_topLevel[line].begin(), cutData_topLevel[line].end(), SVzNL3DPosition{ 0,{0,0,0} }); //bottom level cutData_btmLevel[line].resize(linePtNum); std::fill(cutData_btmLevel[line].begin(), cutData_btmLevel[line].end(), SVzNL3DPosition{ 0,{0,0,0} }); for (int j = 0; j < linePtNum; j++) { if (scanLines[line][j].pt3D.z > 1e-4) { ptNum_total++; if (scanLines[line][j].pt3D.z < cutZ_topLevel) { cutData_topLevel[line][j] = scanLines[line][j]; ptNum_topLevel++; } if (scanLines[line][j].pt3D.z < cutZ_btmLevel) { cutData_btmLevel[line][j] = scanLines[line][j]; ptNum_btmLevel++; } } } } double quatiScale = 1.0; double quatiStatorR = (statorParam.statorOuterD / 2.0) / quatiScale; int quatiEdgeSkip = 8; double inerPolateDistTh = quatiScale * 3; SVzNL3DRangeD roi3D = sg_getScanDataROI_vector(scanLines); #if 0 //对全图进行量化,作为层判断基础 std::vector> quantiData_all; std::vector> backIndexing_all; pointCloud2DQuantization( scanLines, roi3D.xRange, roi3D.yRange, quatiScale, quatiEdgeSkip, inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值 quantiData_all, //量化数据,初始化为一个极大值1e+6 backIndexing_all //标记坐标索引,用于回找3D坐标 ); int totalQuantiSize = countQuantiSize(quantiData_all, KeWD_Mask_ValidPt); #endif bool topLevelIsEmpty = false; if ((keSG_OPERATE_BTM_LAYER == opState->opState) || (keSG_OPERATE_PLATE == opState->opState)) topLevelIsEmpty = true; if ((keWD_OPERATE_Uknown == opState->opState) || (keSG_OPERATE_TOP_LAYER == opState->opState)) //当状态机为未知状态或处于上层抓取时,对顶层进行量化 { //对顶层进行量化 //在量化图像上处理,否则空间处没有坐标,无法确定空间区域的面积,无法确定孔的大小 std::vector> quantiData_topLevel; std::vector> backIndexing_topLevel; pointCloud2DQuantization( cutData_topLevel, roi3D.xRange, roi3D.yRange, quatiScale, quatiEdgeSkip, inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值 quantiData_topLevel, //量化数据,初始化为一个极大值1e+6 backIndexing_topLevel //标记坐标索引,用于回找3D坐标 ); int topLevelQuantiSize = countQuantiSize(quantiData_topLevel, KeWD_Mask_ValidPt); double ringArea = (pow(statorParam.statorOuterD, 2) - pow(statorParam.statorInnerD, 2)) * PI * 0.25; if (topLevelQuantiSize < (ringArea * 0.5)) { topLevelIsEmpty = true; } //顶层有点云数据 if (false == topLevelIsEmpty) { //生成点的Mask, 使用SSG_clusterLabel结构 std::vector> labelMask_topLevel; genValidPtLabelMask(quantiData_topLevel, labelMask_topLevel, KeWD_Mask_NullPt); //目的:提取孔点 // //使用聚类方法进行连通域标记 std::vector> clusters; std::vector clustersInfo; cloutPointsClustering_2( quantiData_topLevel, labelMask_topLevel, clusters, //只记录位置 clustersInfo); if (clusters.size() == 1) //如果只有一个目标,说明整个topLevel都是空点,上层为空 { topLevelIsEmpty = true; } else { //过滤目标 std::vector validClusterIndice;//合格目标的序号。此处使用序号,避免数据拷贝 //根据大小提取目标 clusterSizeMatching(clusters, clustersInfo, statorParam, validClusterIndice); //合格目标的序号。此处使用序号,避免数据拷贝 if (validClusterIndice.size() == 0) //没有找到目标(此时有点云数据,因此报错) { *errCode = SX_ERR_ZERO_OBJ_TOPLAYER; return; } else { std::vector objectRgns; int objNum = (int)validClusterIndice.size(); for (int i = 0; i < objNum; i++) { //计算目标位置 SSG_peakRgnInfo a_obj = _computeObjInfo( validClusterIndice[i], clusters, clustersInfo, quantiData_topLevel, quatiStatorR ); if (a_obj.pkRgnIdx < 0) continue; objectRgns.push_back(a_obj); } if (objectRgns.size() == 0) //没有找到目标(此时有点云数据,因此报错) { *errCode = SX_ERR_ZERO_OBJ_TOPLAYER; return; } //生成结果 computeBestObject( objectRgns, statorParam, opState, resultObjPositions ); //更新状态机 opState->refPos.x = resultObjPositions[0].opCenter.x; opState->refPos.y = resultObjPositions[0].opCenter.y; opState->refPos.z = resultObjPositions[0].opCenter.z; opState->opState = keSG_OPERATE_TOP_LAYER; } } } } //顶层为空,处理底层 if (true == topLevelIsEmpty) { //对底层进行处理 //在量化图像上处理,否则空间处没有坐标,无法确定空间区域的面积,无法确定孔的大小 std::vector> quantiData_btmLevel; std::vector> backIndexing_btmLevel; pointCloud2DQuantization( cutData_btmLevel, roi3D.xRange, roi3D.yRange, quatiScale, quatiEdgeSkip, inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值 quantiData_btmLevel, //量化数据,初始化为一个极大值1e+6 backIndexing_btmLevel //标记坐标索引,用于回找3D坐标 ); int bmtLevelQuantiSize = countQuantiSize(quantiData_btmLevel, KeWD_Mask_ValidPt); double ringArea = (pow(statorParam.statorOuterD, 2) - pow(statorParam.statorInnerD, 2)) * PI * 0.25; if (bmtLevelQuantiSize < (ringArea * 0.5)) { //空框 *errCode = SX_ERR_ZERO_OBJ_BTMLAYER; return; } //距离变换判断隔板 if (opState->opState != keSG_OPERATE_BTM_LAYER) { int maskRow = quantiData_btmLevel.size(); int maskCol = quantiData_btmLevel[0].size(); cv::Mat projectionImg(maskRow, maskCol, CV_32FC1, 0.0f); //距离变换Mask,初始化为一个极大值1e+6 getDistanceTransformMaskData(quantiData_btmLevel, projectionImg ); // 投影量化数据,初始化为一个极大值1e + 6 cv::Mat distTransform; sg_distanceTrans(projectionImg, distTransform, 0); #if DEBUG_OUT_IMAGE //debug cv::Mat maskImage; cv::normalize(projectionImg, maskImage, 0, 255, cv::NORM_MINMAX, CV_8U); cv::imwrite("distTransformMask.png", maskImage); cv::Mat dtImage; cv::normalize(distTransform, dtImage, 0, 255, cv::NORM_MINMAX, CV_8U); cv::Mat dtImage_color; cv::cvtColor(dtImage, dtImage_color, cv::COLOR_GRAY2BGR); cv::imwrite("distTransform.png", dtImage_color); #endif //取最大的距离变换值,若 double distancePeakTh = statorParam.plateH < statorParam.plateW ? statorParam.plateH : statorParam.plateW; distancePeakTh = distancePeakTh / quatiScale; distancePeakTh = (distancePeakTh / 2) * 0.75; double maxDistTransformValue = 0; for (int y = 0; y < maskRow; y++) { for (int x = 0; x < maskCol; x++) { if (distTransform.at(y, x) > maxDistTransformValue) maxDistTransformValue = distTransform.at(y, x); } } if (maxDistTransformValue > distancePeakTh) { cv::Mat bwImg = cv::Mat::zeros(maskRow, maskCol, CV_8UC1);//rows, cols for (int row = 0; row < maskRow; row++) { for (int col = 0; col < maskCol; col++) { if (quantiData_btmLevel[row][col].z > 1e-4) bwImg.at(row, col) = 1; } } //连通域标注 cv::Mat labImg; std::vector labelRgns; SG_TwoPassLabel(bwImg, labImg, labelRgns, 8); //将最大目标的中心作为隔板抓取中心点 SSG_Region maxRgn; memset(&maxRgn, 0, sizeof(SSG_Region)); int maxId = -1; for (int i = 0; i < (int)labelRgns.size(); i++) { if (maxRgn.ptCounter < labelRgns[i].ptCounter) { maxRgn = labelRgns[i]; maxId = i; } } if (maxId < 0) { //隔板提取错误 *errCode = SX_ERR_GET_INVALID_PALTE; return; } double plate_cx = (double)(labelRgns[maxId].roi.right + labelRgns[maxId].roi.left) / 2; double plate_cy = (double)(labelRgns[maxId].roi.bottom + labelRgns[maxId].roi.top) / 2; int cx_i = (int)plate_cx; int cy_i = (int)plate_cy; double op_z = quantiData_btmLevel[cx_i][cy_i].z; plate_cx = (plate_cx - quatiEdgeSkip) * quatiScale + roi3D.xRange.min; plate_cy = (plate_cy - quatiEdgeSkip) * quatiScale + roi3D.yRange.min; SWD_statorInnerGrasper plateGrasper; plateGrasper.objID = 0; plateGrasper.opCenter = { plate_cx , plate_cy, op_z , 0,0,0 }; resultObjPositions.push_back(plateGrasper); opState->opState = keSG_OPERATE_PLATE; opState->refPos = { 0,0,0 }; return; } } //处理下层目标 //生成点的Mask, 使用SSG_clusterLabel结构 std::vector> labelMask_btmLevel; genValidPtLabelMask(quantiData_btmLevel, labelMask_btmLevel, KeWD_Mask_NullPt); //目的:提取孔点 //使用聚类方法进行连通域标记 std::vector> clusters; std::vector clustersInfo; cloutPointsClustering_2( quantiData_btmLevel, labelMask_btmLevel, clusters, //只记录位置 clustersInfo); if (clusters.size() == 1) //如果只有一个目标,说明整个topLevel都是空点,上层为空 { //空框 *errCode = SX_ERR_ZERO_OBJ_BTMLAYER; return; } else { //过滤目标 std::vector validClusterIndice;//合格目标的序号。此处使用序号,避免数据拷贝 //根据大小提取目标 clusterSizeMatching(clusters, clustersInfo, statorParam, validClusterIndice); //合格目标的序号。此处使用序号,避免数据拷贝 if (validClusterIndice.size() == 0) //没有找到目标(此时有点云数据,因此报错) { *errCode = SX_ERR_ZERO_OBJ_TOPLAYER; return; } else { std::vector objectRgns; int objNum = (int)validClusterIndice.size(); for (int i = 0; i < objNum; i++) { //计算目标位置 SSG_peakRgnInfo a_obj = _computeObjInfo( validClusterIndice[i], clusters, clustersInfo, quantiData_btmLevel, quatiStatorR ); if (a_obj.pkRgnIdx < 0) continue; objectRgns.push_back(a_obj); } if (objectRgns.size() == 0) //没有找到目标(此时有点云数据,因此报错) { *errCode = SX_ERR_ZERO_OBJ_BTMLAYER; return; } //生成结果 computeBestObject( objectRgns, statorParam, opState, resultObjPositions); //更新状态机 opState->refPos.x = resultObjPositions[0].opCenter.x; opState->refPos.y = resultObjPositions[0].opCenter.y; opState->refPos.z = resultObjPositions[0].opCenter.z; opState->opState = keSG_OPERATE_BTM_LAYER; } } } return; }