algoLib/sourceCode/motorStatorPosition.cpp
2025-06-11 22:14:58 +08:00

580 lines
16 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <vector>
#include "SG_baseDataType.h"
#include "SG_baseAlgo_Export.h"
#include "motorStatorPosition_Export.h"
#include <opencv2/opencv.hpp>
#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]; //对应LRTP
}SSG_statorNeighbourInfo;
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<uchar>(cy, m) > 0)
edge_x = m;
}
}
else
{
for (int m = cx; m < zSliceData.cols; m++)
{
if (zSliceData.at<uchar>(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<uchar>(m, cx) > 0)
edge_y = m;
}
}
else
{
for (int m = cy; m < zSliceData.rows; m++)
{
if (zSliceData.at<uchar>(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 SG_motorStatorPositionParam positionParam,
cv::Mat& zSliceData, //用于寻找边界(边框)
std::vector<SG_fittingInfo>& 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 compareByNeighbourDist(const SSG_motorStatorPosition& a, const SSG_motorStatorPosition& b)
{
return a.obstacleDist > b.obstacleDist;
}
bool compareByNeighbourNum(const SSG_motorStatorPosition& a, const SSG_motorStatorPosition& b)
{
return a.neighbourNum < b.neighbourNum;
}
typedef struct
{
cv::RotatedRect fittingPara;
SVzNL3DPoint objCenter;
}SG_fittingInfo;
void sg_motorStatorPosition(
SVzNL3DLaserLine* laser3DPoints,
int lineNum,
const SG_motorStatorPositionParam positionParam,
int* errCode,
std::vector<SSG_motorStatorPosition>& resultOpPositions
)
{
/// 统计整个视野大小,在滤噪之后进行
SVzNL3DRangeD roi3D = sg_getScanDataROI(
laser3DPoints,
lineNum);
SVzNLRangeD z_range = roi3D.zRange;
//Z方向统计。粒度为1mm
int zHistSize = (int)(z_range.max - z_range.min) + 1;
std::vector<int> zHist;
zHist.resize(zHistSize);
int totalPtNum = 0;
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if (pt3D->pt3D.z < 1e-4)
continue;
totalPtNum++;
int zPos = (int)(pt3D->pt3D.z - z_range.min);
zHist[zPos] ++;
}
}
//取定子顶面取5mm统计区间zHist数量大于总数量的10%
std::vector<int> sumHist;
sumHist.resize(zHistSize);
for (int i = 0; i < zHistSize; i++)
{
int data = 0;
for (int j = i - 2; j < i + 2; j++)
{
if ((j >= 0) && (j < zHistSize))
data += zHist[j];
}
sumHist[i] = data;
}
//取第一个大于大于10%的极值点
int dataTh = totalPtNum / 10;
int maxPos = -1;
for (int i = 1; i < zHistSize-1; i++)
{
int preData = sumHist[i-1];
int data = sumHist[i];
int nxtData = sumHist[i+1];
if ((data > preData) && (data > nxtData) && (data > dataTh))
{
maxPos = i;
break;
}
}
if (maxPos < 0)
return;
//取定子顶面截面
double zSliceTop = (double)maxPos - 10 + z_range.min;
double zSliceBtm = (double)maxPos + 8 + z_range.min;
//在XOY面上投影
//距离变换Mask以1mm为量化尺度
int maskX = (int)(roi3D.xRange.max - roi3D.xRange.min) + 1;
int maskY = (int)(roi3D.yRange.max - roi3D.yRange.min) + 1;
cv::Mat zSliceData = cv::Mat::zeros(maskY, maskX, CV_8U);
cv::Mat distTranformMask(maskY, maskX, CV_32FC1, 1e+6); //距离变换Mask初始化为一个极大值1e+6
//标记坐标索引,用于距离变换后回找坐标
cv::Mat distTranformIndexing(maskY, maskX, CV_32SC2, cv::Vec2i(0, 0)); //坐标索引
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if ( (pt3D->pt3D.z < zSliceTop) || (pt3D->pt3D.z > zSliceBtm))
continue;
double x = pt3D->pt3D.x;
double y = pt3D->pt3D.y;
int px = (int)(x - roi3D.xRange.min);
int py = (int)(y - roi3D.yRange.min);
cv::Vec2i v2i = { line, i };
zSliceData.at<uchar>(py, px) = (uchar)255;
distTranformIndexing.at<cv::Vec2i>(py, px) = v2i;
distTranformMask.at<float>(py, px) = 0;
}
}
cv::Mat zSliceData_origin = zSliceData.clone();
//距离变换
cv::Mat distTransform;
sg_distanceTrans(distTranformMask, distTransform, 0);
#if DEBUG_OUT_IMAGE //debug
cv::Mat dtImage;
cv::normalize(distTranformMask, dtImage, 0, 255, cv::NORM_MINMAX, CV_8U);
cv::imwrite("distTransform.png", dtImage);
//cv::normalize(zSliceData, dtImage, 64, 255, cv::NORM_MINMAX, CV_8U);
cv::imwrite("zSliceImage.png", zSliceData);
#endif
//对zSliceData进行处理1填充2腐蚀3连通域处理得到目标4椭圆拟合得到中心点
cv::Mat invertSliceData;
cv::bitwise_not(zSliceData, invertSliceData);
#if DEBUG_OUT_IMAGE
cv::imwrite("zSliceImage_invert.png", invertSliceData);
#endif
//连通域
cv::Mat labels, centroids, stats;
int nccomps = connectedComponentsWithStats(invertSliceData, labels, stats, centroids, 4);
for (int i = 1; i < nccomps; i++)
{
int size = stats.at<int>(i, cv::CC_STAT_AREA);
if (size < 20)
{
int roi_x = stats.at<int>(i, cv::CC_STAT_LEFT);
int roi_y = stats.at<int>(i, cv::CC_STAT_TOP);
int roi_w = stats.at<int>(i, cv::CC_STAT_WIDTH);
int roi_h = stats.at<int>(i, cv::CC_STAT_HEIGHT);
//对小于20的区域填充
for (int row = roi_y; row < roi_y+roi_h; row++)
{
for (int col = roi_x; col < roi_x+roi_w; col++)
{
if (labels.at<int>(row, col) == i)
zSliceData.at<uchar>(row, col) = (uchar)255;
}
}
}
}
#if DEBUG_OUT_IMAGE
cv::imwrite("zSliceImage_filled.png", zSliceData);
#endif
//腐蚀
cv::Mat test = cv::Mat::zeros(64, 64, CV_8UC1);
cv::rectangle(test, cv::Rect(30, 30, 5, 5), 255, -1);
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::Mat zSliceData_erode;
cv::erode(zSliceData, zSliceData_erode, element, cv::Point(-1,-1),2);
#if DEBUG_OUT_IMAGE
cv::imwrite("zSliceImage_erode.png", zSliceData_erode);
#endif
//提取目标
nccomps = connectedComponentsWithStats(zSliceData_erode, labels, stats, centroids, 4);
//目标大小的门限
int obj_size_min = (int)(positionParam.statorInnerD * 0.75);
int obj_size_max = (int)(positionParam.statorOuterD * 1.25);
std::vector<SG_fittingInfo> fittingObs;
for (int i = 1; i < nccomps; i++)
{
int roi_x = stats.at<int>(i, cv::CC_STAT_LEFT);
int roi_y = stats.at<int>(i, cv::CC_STAT_TOP);
int roi_w = stats.at<int>(i, cv::CC_STAT_WIDTH);
int roi_h = stats.at<int>(i, cv::CC_STAT_HEIGHT);
if( (roi_w > obj_size_min) && (roi_w < obj_size_max) && (roi_h > obj_size_min) && (roi_h < obj_size_max))
{
//合格的目标
//取轮廓点
std::vector<cv::Point > contourPts;
for (int row = roi_y; row < roi_y + roi_h; row++)
{
for (int col = roi_x; col < roi_x + roi_w; col++)
{
if (labels.at<int>(row, col) == i)
contourPts.push_back(cv::Point(col, row));
}
}
cv::RotatedRect ellipse = cv::fitEllipse(contourPts);
SG_fittingInfo a_fitting;
a_fitting.fittingPara = ellipse;
//根据轮廓点计算高度Z
double z_sum = 0;
double z_counter = 0;
for (int m = 0; m < contourPts.size(); m++)
{
cv::Vec2i ptIndexing = distTranformIndexing.at<cv::Vec2i>(contourPts[m].y, contourPts[m].x);
int line = ptIndexing[0];
int ptIdx = ptIndexing[1];
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[ptIdx];
if (pt3D->pt3D.z < 1e-4)
continue;
z_sum += pt3D->pt3D.z;
z_counter++;
}
if (z_counter > 0)
{
a_fitting.objCenter.x = ellipse.center.x + roi3D.xRange.min;
a_fitting.objCenter.y = ellipse.center.y + roi3D.yRange.min;
z_sum = z_sum / z_counter;
a_fitting.objCenter.z = z_sum;
a_fitting.fittingPara = ellipse;
fittingObs.push_back(a_fitting);
}
}
}
#if DEBUG_OUT_IMAGE
cv::Mat fittingImage;
cv::cvtColor(zSliceData_origin, fittingImage, cv::COLOR_GRAY2BGR);
for(int i = 0; i < fittingObs.size(); i ++)
cv::ellipse(fittingImage, fittingObs[i].fittingPara, cv::Scalar(0, 255, 0), 2); // 在原图上绘制椭圆
cv::imwrite("zSliceImage_fitting.png", fittingImage);
#endif
//根据邻接关系确定最佳抓取点
std::vector< SSG_statorNeighbourInfo> neighbouringInfo;
_getNeighbouringInfo(
positionParam,
zSliceData_origin, //用于寻找边界(边框)
fittingObs, //目标位置
neighbouringInfo //邻接关系
);
//根据邻接关系确定抓取点
std::vector< SSG_motorStatorPosition> grabPoses; //小于等于3个相邻目标的抓取点按距离排序
std::vector< SSG_motorStatorPosition> grabPoses_2; //大于3个相邻目标的抓取点按数量排序
//grabPoses.resize(neighbouringInfo.size());
for (int i = 0; i < neighbouringInfo.size(); i++)
{
double opAngle = -1;
double obstacleDist = -1;
_computeGripperPose(neighbouringInfo[i], positionParam.gripperR, &opAngle, &obstacleDist);
SSG_motorStatorPosition opPos;
memset(&opPos, 0, sizeof(SSG_motorStatorPosition));
opPos.neighbourNum = neighbouringInfo[i].neighbours.size();
opPos.obstacleDist = obstacleDist;
opPos.opAngle = opAngle;
opPos.opCenter = neighbouringInfo[i].objPos3D;
if (opPos.neighbourNum <= 3)
grabPoses.push_back(opPos);
else
grabPoses_2.push_back(opPos);
}
//选取抓取点1周边邻居最少2距离边的距离最大
std::sort(grabPoses.begin(), grabPoses.end(),compareByNeighbourDist);
std::sort(grabPoses_2.begin(), grabPoses_2.end(), compareByNeighbourNum);
//合并
grabPoses.insert(grabPoses.end(), grabPoses_2.begin(), grabPoses_2.end());
return;
}