rodAndBarDetection version 1.3.3 :

筑裕钢筋焊缝提取算法改进中间版本
This commit is contained in:
jerryzeng 2026-06-03 17:25:45 +08:00
parent 4580256e47
commit 2d837cb108
9 changed files with 616 additions and 54 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.33027.164
# Visual Studio Version 17
VisualStudioVersion = 17.14.37216.2 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fireBrickPositioning", "fireBrickPositioning\fireBrickPositioning.vcxproj", "{BB3FA81E-21D8-40AD-ACE6-49173477F32D}"
ProjectSection(ProjectDependencies) = postProject
@ -112,7 +112,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BQ_workpieceCornerExtractio
{95DC3F1A-902A-490E-BD3B-B10463CF0EBD} = {95DC3F1A-902A-490E-BD3B-B10463CF0EBD}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BQ_workpieceCornerExtract_test", "BQ_workpieceCornerExtract_test\BQ_workpieceCornerExtract_test.vcxproj", "{CF563709-0402-447E-BFCC-7701CC90D0AF}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BQ_workpieceCornerExtraction_test", "BQ_workpieceCornerExtract_test\BQ_workpieceCornerExtract_test.vcxproj", "{CF563709-0402-447E-BFCC-7701CC90D0AF}"
ProjectSection(ProjectDependencies) = postProject
{95DC3F1A-902A-490E-BD3B-B10463CF0EBD} = {95DC3F1A-902A-490E-BD3B-B10463CF0EBD}
{AD8415B7-A745-4184-87B8-95619E5066D6} = {AD8415B7-A745-4184-87B8-95619E5066D6}
@ -227,15 +227,15 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YouJiang_sheetPositioning_t
{BDE8197F-8FD3-4DEF-B1EA-F6A2C7C5DA9B} = {BDE8197F-8FD3-4DEF-B1EA-F6A2C7C5DA9B}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "steelPipeDetection", "steelPipeDetection\steelPipeDetection.vcxproj", "{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "steelPipeDetection", "steelPipeDetection\steelPipeDetection.vcxproj", "{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}"
ProjectSection(ProjectDependencies) = postProject
{95DC3F1A-902A-490E-BD3B-B10463CF0EBD} = {95DC3F1A-902A-490E-BD3B-B10463CF0EBD}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "steelPipeDetection_test", "steelPipeDetection_test\steelPipeDetection_test.vcxproj", "{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "steelPipeDetection_test", "steelPipeDetection_test\steelPipeDetection_test.vcxproj", "{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}"
ProjectSection(ProjectDependencies) = postProject
{95DC3F1A-902A-490E-BD3B-B10463CF0EBD} = {95DC3F1A-902A-490E-BD3B-B10463CF0EBD}
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d} = {8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D} = {8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}
EndProjectSection
EndProject
Global
@ -582,22 +582,6 @@ Global
{BDE8197F-8FD3-4DEF-B1EA-F6A2C7C5DA9B}.Release|x64.Build.0 = Release|x64
{BDE8197F-8FD3-4DEF-B1EA-F6A2C7C5DA9B}.Release|x86.ActiveCfg = Release|Win32
{BDE8197F-8FD3-4DEF-B1EA-F6A2C7C5DA9B}.Release|x86.Build.0 = Release|Win32
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Debug|x64.ActiveCfg = Debug|x64
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Debug|x64.Build.0 = Debug|x64
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Debug|x86.ActiveCfg = Debug|Win32
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Debug|x86.Build.0 = Debug|Win32
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Release|x64.ActiveCfg = Release|x64
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Release|x64.Build.0 = Release|x64
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Release|x86.ActiveCfg = Release|Win32
{8e5b3c2d-4a1f-4e9b-8d7c-3f2e5a6b7c8d}.Release|x86.Build.0 = Release|Win32
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Debug|x64.ActiveCfg = Debug|x64
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Debug|x64.Build.0 = Debug|x64
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Debug|x86.ActiveCfg = Debug|Win32
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Debug|x86.Build.0 = Debug|Win32
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Release|x64.ActiveCfg = Release|x64
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Release|x64.Build.0 = Release|x64
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Release|x86.ActiveCfg = Release|Win32
{1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d}.Release|x86.Build.0 = Release|Win32
{80CBF888-79F3-424F-A094-17EDFD452E1E}.Debug|x64.ActiveCfg = Debug|x64
{80CBF888-79F3-424F-A094-17EDFD452E1E}.Debug|x64.Build.0 = Debug|x64
{80CBF888-79F3-424F-A094-17EDFD452E1E}.Debug|x86.ActiveCfg = Debug|Win32
@ -606,6 +590,22 @@ Global
{80CBF888-79F3-424F-A094-17EDFD452E1E}.Release|x64.Build.0 = Release|x64
{80CBF888-79F3-424F-A094-17EDFD452E1E}.Release|x86.ActiveCfg = Release|Win32
{80CBF888-79F3-424F-A094-17EDFD452E1E}.Release|x86.Build.0 = Release|Win32
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Debug|x64.ActiveCfg = Debug|x64
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Debug|x64.Build.0 = Debug|x64
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Debug|x86.ActiveCfg = Debug|Win32
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Debug|x86.Build.0 = Debug|Win32
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Release|x64.ActiveCfg = Release|x64
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Release|x64.Build.0 = Release|x64
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Release|x86.ActiveCfg = Release|Win32
{8E5B3C2D-4A1F-4E9B-8D7C-3F2E5A6B7C8D}.Release|x86.Build.0 = Release|Win32
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Debug|x64.ActiveCfg = Debug|x64
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Debug|x64.Build.0 = Debug|x64
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Debug|x86.ActiveCfg = Debug|Win32
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Debug|x86.Build.0 = Debug|Win32
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Release|x64.ActiveCfg = Release|x64
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Release|x64.Build.0 = Release|x64
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Release|x86.ActiveCfg = Release|Win32
{1A2B3C4D-5E6F-4A7B-8C9D-0E1F2A3B4C5D}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -12,6 +12,7 @@
#include <opencv2/opencv.hpp>
#include <Windows.h>
#include <limits>
#include <SG_baseAlgo_Export.h>
#include <cmath>
typedef struct
@ -32,6 +33,14 @@ typedef struct
float b;
} SPointXYZRGB;
bool fileExists(const char* path) {
FILE* f = fopen(path, "r");
if (f != NULL) {
fclose(f);
return true;
}
return false;
}
// 点乘 dot
double _dotMultiply(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
@ -1512,23 +1521,39 @@ void rodPositionTest(void)
#endif
}
int counterValidPts(std::vector<SVzNL3DPosition>& lineData)
{
int num = 0;
for (int i = 0; i < (int)lineData.size(); i++)
{
if (lineData[i].pt3D.z > 1e-4)
num++;
else
{
lineData[i].pt3D = { 0, 0, 0 };
}
}
return num;
}
#define WELD_SEAM_TEST_GROUP 2
#define WELD_SEAM_TEST_GROUP 4
void rodWeldSeamPosition_test(void)
{
const char* dataPath[WELD_SEAM_TEST_GROUP] = {
"F:/ShangGu/项目/冠钦项目/筑裕视觉钢结构焊接/视觉照片1/", //0
"F:/ShangGu/项目/冠钦项目/筑裕视觉钢结构焊接/视觉照片2/", //0
"F:/ShangGu/项目/冠钦项目/筑裕视觉钢结构焊接/视觉照片2/", //1
"F:/ShangGu/项目/冠钦项目/筑裕视觉钢结构焊接/数据3/特征点1/", //2
"F:/ShangGu/项目/冠钦项目/筑裕视觉钢结构焊接/数据3/特征点2/", //3
};
SVzNLRange fileIdx[WELD_SEAM_TEST_GROUP] = {
{1,20},{1,20},
{1,20},{1,20},{1,27}, { 1,21 },
};
const char* ver = wd_rodAndBarDetectionVersion();
printf("ver:%s\n", ver);
for (int grp = 0; grp < WELD_SEAM_TEST_GROUP; grp++)
for (int grp = 2; grp < WELD_SEAM_TEST_GROUP; grp++)
{
SSG_planeCalibPara poseCalibPara;
//初始化成单位阵
@ -1552,15 +1577,95 @@ void rodWeldSeamPosition_test(void)
{
//fidx =1;
char _scan_file[256];
sprintf_s(_scan_file, "%s%d_LaserData_ID019567.txt", dataPath[grp], fidx);
sprintf_s(_scan_file, "%s%d_LaserData_groundAdjusted.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);
if ((grp == 2) || (grp == 3)) //计算调平矩阵
{
#if 0
std::vector<cv::Point3f> validPts;
std::vector<std::vector< SVzNL3DPosition>> copy_scanLines;
copy_scanLines.resize(scanLines.size());
for (int line = 0; line < (int)scanLines.size(); line++)
{
std::vector< SVzNL3DPosition>& a_line = scanLines[line];
copy_scanLines[line].resize(a_line.size());
for (int j = 0; j < (int)a_line.size(); j++)
{
copy_scanLines[line][j] = a_line[j];
if (a_line[j].pt3D.z > 1e-4)
{
cv::Point3f a_pt = cv::Point3f((float)a_line[j].pt3D.x, (float)a_line[j].pt3D.y, (float)a_line[j].pt3D.z);
validPts.push_back(a_pt);
}
}
}
std::vector<cv::Point3f> out_inliers;
Plane res = ransacFitPlane(validPts, out_inliers);
if (res.C < 0)
{
res.A = -res.A;
res.B = -res.B;
res.C = -res.C;
res.D = -res.D;
}
float meanH = 0;
int counter = 0;
for (int i = 0; i < (int)out_inliers.size(); i++)
{
meanH += out_inliers[i].z;
counter++;
}
//计算投影向量
SVzNL3DPoint vec_1 = { res.A, res.B, res.C };
SVzNL3DPoint vec_2 = { 0, 0, 1.0 };
SSG_planeCalibPara calibPara = wd_computeRTMatrix(vec_1, vec_2);
for (int m = 0; m < 9; m++)
{
poseCalibPara.planeCalib[m] = calibPara.planeCalib[m];
poseCalibPara.invRMatrix[m] = calibPara.invRMatrix[m];
}
poseCalibPara.planeHeight = meanH / counter;
char _out_file[256];
sprintf_s(_out_file, "%s%d_calib_para.txt", dataPath[grp], fidx);
_outputCalibPara(_out_file, poseCalibPara);
sprintf_s(_out_file, "%s%d_LaserData_groundAdjusted.txt", dataPath[grp], fidx);
//if (false == fileExists(_out_file))
{
//输出调平效果
int firstValidLine = -1;
int lastValidLine = -1;
for (int line = 0; line < (int)copy_scanLines.size(); line++)
{
//行处理
//调平,去除地面
double cuttingZ = poseCalibPara.planeHeight - 100;
sx_rodPosition_lineDataR(copy_scanLines[line], poseCalibPara.planeCalib, cuttingZ);
int ptsNum = counterValidPts(copy_scanLines[line]);
if (ptsNum > 10)
{
if (firstValidLine < 0)
firstValidLine = line;
lastValidLine = line;
}
}
if ((lastValidLine > 0) && (lastValidLine < copy_scanLines.size() - 1))
{
copy_scanLines.erase(copy_scanLines.begin() + lastValidLine + 1, copy_scanLines.end());
}
if(firstValidLine > 1)
{
copy_scanLines.erase(copy_scanLines.begin(), copy_scanLines.begin() + firstValidLine - 1);
}
_outputScanDataFile(_out_file, copy_scanLines, 0.0f, 0, 0);
}
#endif
}
long t1 = (long)GetTickCount64();//统计时间
SSX_rodParam rodParam;
@ -1583,7 +1688,7 @@ void rodWeldSeamPosition_test(void)
growParam.maxLineSkipNum = 5;
growParam.yDeviation_max = 5.0;
growParam.maxSkipDistance = 20.0;
growParam.zDeviation_max = 3.0;//
growParam.zDeviation_max = 5.0;//
growParam.minLTypeTreeLen = 50; //mm, 螺杆长度
growParam.minVTypeTreeLen = 50; //mm
@ -1603,11 +1708,14 @@ void rodWeldSeamPosition_test(void)
&errCode);
long t2 = (long)GetTickCount64();
printf("%s: %d(ms)!\n", _scan_file, (int)(t2 - t1));
//输出测试结果
sprintf_s(_scan_file, "%sresult\\%d_result.txt", dataPath[grp], fidx);
_outputRGBDScan_RGBD_weldSeam(_scan_file, scanLines, weldSeamInfo);
sprintf_s(_scan_file, "%sresult\\%d_screw_info.txt", dataPath[grp], fidx);
_outputWeldSeamInfo(_scan_file, weldSeamInfo);
if (errCode == 0)
{
//输出测试结果
sprintf_s(_scan_file, "%sresult\\%d_result.txt", dataPath[grp], fidx);
_outputRGBDScan_RGBD_weldSeam(_scan_file, scanLines, weldSeamInfo);
sprintf_s(_scan_file, "%sresult\\%d_screw_info.txt", dataPath[grp], fidx);
_outputWeldSeamInfo(_scan_file, weldSeamInfo);
}
}
}
}
@ -1627,8 +1735,8 @@ int main()
//ESG_testMode testMode = keSG_测试_配天螺杆定位;
//ESG_testMode testMode = keSG_测试_配天定位盘定位;
//ESG_testMode testMode = keSG_测试_配天新定位盘定位;
ESG_testMode testMode = keSG_测试_棒材抓取;
//ESG_testMode testMode = keSG_测试_筑裕钢筋焊缝定位;
//ESG_testMode testMode = keSG_测试_棒材抓取;
ESG_testMode testMode = keSG_测试_筑裕钢筋焊缝定位;
if(keSG_测试_配天螺杆定位 == testMode)
screwTest();

View File

@ -126,7 +126,7 @@
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\..\thirdParty\opencv320\build\x64\vc14\lib;..\build\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>opencv_world320d.lib;rodAndBarDetection.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>opencv_world320d.lib;baseAlgorithm.lib;rodAndBarDetection.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -145,7 +145,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\..\thirdParty\opencv320\build\x64\vc14\lib;..\build\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>opencv_world320.lib;rodAndBarDetection.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>opencv_world320.lib;baseAlgorithm.lib;rodAndBarDetection.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -173,7 +173,7 @@ SG_APISHARED_EXPORT void wd_lineDataSegment_dist_2(
std::vector< SVzNL3DPosition>& lineData,
std::vector<SSG_RUN>& segs,
const double maxDistTh,
const int minSegSize
const double minSegSize
);
SG_APISHARED_EXPORT void wd_lineDataSegment_zDist(
@ -215,6 +215,21 @@ SG_APISHARED_EXPORT void wd_getRodArcFeature_peakCornerMethod(
std::vector<SWD_rodArcFeature>& line_rodArcs //环
);
/// <summary>
/// 通过计算YZ平面内的曲率方法提取圆弧。
/// seg端点z距离大于门限
/// </summary>
SG_APISHARED_EXPORT void wd_getRodArcFeature_YZCurvatureMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double maxDistTh,
const double minSegSize,
const double curvatureWin,
const double rodDiameter,
const double arcTotalCornerMinValue,
std::vector<SWD_rodArcFeature>& line_rodArcs //环
);
/// <summary>
/// 提取激光线上的圆环的上半段弧。
/// seg端点z距离大于门限
@ -663,6 +678,11 @@ SG_APISHARED_EXPORT double fitCircleByLeastSquare_2(
const std::vector<SVzNL3DPosition>& pointArray,
SVzNL3DPoint& center,
double& radius);
//圆最小二乘拟合
SG_APISHARED_EXPORT double fitCircleByLeastSquare_3(
const std::vector<SVzNL2DPointD>& pointArray,
SVzNL2DPointD& center,
double& radius);
//抛物线最小二乘拟合 y=ax^2 + bx + c
SG_APISHARED_EXPORT bool leastSquareParabolaFitEigen(

View File

@ -291,6 +291,7 @@ typedef struct
SVzNL3DPoint endPt;
SVzNL3DPoint peakPt;
double featureValue;
int flag;
}SWD_rodArcFeature;
typedef struct
@ -441,6 +442,7 @@ typedef struct
int start;
int len;
int value;
double curveLen;
}SSG_RUN;
typedef struct

View File

@ -843,7 +843,7 @@ void wd_getRodArcFeatureGrowingTrees(
{
for (int i = 0, i_max = (int)all_lineFeatures.size(); i < i_max; i++)
{
if (i == 1304)
if (i == 770)
int kkk = 1;
std::vector<SWD_rodArcFeature>& a_lineFeatures = all_lineFeatures[i];
for (int j = 0, j_max = (int)a_lineFeatures.size(); j < j_max; j++)

View File

@ -2,6 +2,8 @@
#include "SG_baseAlgo_Export.h"
#include <vector>
const double EPS = 1e-8;
typedef struct
{
int flag;
@ -3883,12 +3885,12 @@ void wd_lineDataSegment_dist_2(
std::vector< SVzNL3DPosition>& lineData,
std::vector<SSG_RUN>& segs,
const double maxDistTh,
const int minSegSize
const double minSegSize
)
{
int dataSize = (int)lineData.size();
int runIdx = 1;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
SSG_RUN a_run = { 0, -1, 0, 0.0}; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
@ -3900,6 +3902,7 @@ void wd_lineDataSegment_dist_2(
a_run.start = i;
a_run.len = 1;
a_run.value = i;
a_run.curveLen = 0.0;
}
else
{
@ -3908,24 +3911,26 @@ void wd_lineDataSegment_dist_2(
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
a_run.curveLen += dist;
}
else
{
a_run.value = runIdx;
runIdx++;
if (a_run.len >= minSegSize)
if (a_run.curveLen >= minSegSize)
segs.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
a_run.curveLen = 0;
}
}
pre_y = lineData[i].pt3D.y;
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
if (a_run.curveLen > 0)
{
if (a_run.len >= minSegSize)
segs.push_back(a_run);
@ -4574,6 +4579,322 @@ void wd_getRodArcFeature_peakCornerMethod(
return;
}
/**
* @brief 线线
* @param points 2D点列线
* @param index
* @param neighbor 2~43
* @return k = 1/R线0
* @note 线/0线
*/
double calcYZCurvatureByThreePoints(const std::vector<SVzNL3DPosition>& points,
int index,
int neighbor = 3)
{
// 边界保护:防止越界
int n = (int)points.size();
if (index < neighbor || index >= n - neighbor) {
return 0.0; // 边缘点无法计算,返回直线曲率
}
// 取前后邻域点(增强稳定性,避免单点噪声)
const SVzNL3DPosition& pPrev = points[index - neighbor];
const SVzNL3DPosition& pCurr = points[index];
const SVzNL3DPosition& pNext = points[index + neighbor];
// 提取坐标
double x1 = pPrev.pt3D.y, y1 = pPrev.pt3D.z;
double x2 = pCurr.pt3D.y, y2 = pCurr.pt3D.z;
double x3 = pNext.pt3D.y, y3 = pNext.pt3D.z;
// 计算分母三角形面积×2判断是否共线
double area2 = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
if (fabs(area2) < 1e-8) {
return 0.0; // 三点共线 → 直线,曲率=0
}
// 计算外接圆半径 R
double a = hypot(x2 - x3, y2 - y3);
double b = hypot(x1 - x3, y1 - y3);
double c = hypot(x1 - x2, y1 - y2);
double radius = (a * b * c) / fabs(2.0 * area2);
// 曲率 k = 1/R带符号区分凹凸方向
double curvature = radius;
return (area2 > 0) ? curvature : -curvature;
}
//逐段计算曲率
void _computeCurvature_perSeg(
std::vector< SVzNL3DPosition>& linePts,
std::vector<SSG_RUN>& segs,
const double curvatureWin,
std::vector< double>& ptCurvatures
)
{
ptCurvatures.resize(linePts.size());
std::fill(ptCurvatures.begin(), ptCurvatures.end(), 0);
//逐段进行
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
if ((segs[segIdx].len < 2) || (segs[segIdx].curveLen < 1e-4))
continue;
double meanPtDist = segs[segIdx].curveLen / (segs[segIdx].len - 1);
int neighbourWin = (int)(curvatureWin / meanPtDist) + 1;
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
double curvature = calcYZCurvatureByThreePoints(linePts, i, neighbourWin);
ptCurvatures[i] = curvature;
}
}
}
// 圆结构体:圆心 + 半径
struct Circle2d
{
SVzNL2DPointD center;
double radius;
Circle2d() : radius(0) {}
Circle2d(SVzNL2DPointD c, double r) : center(c), radius(r) {}
};
/**
* @brief
* @param circle
* @param pStart
* @param pEnd
* @param outAngleDeg (°)
* @return
*/
double calcArcAngle(const Circle2d& circle,
const SVzNL2DPointD& pStart,
const SVzNL2DPointD& pEnd,
double& outAngleDeg)
{
SVzNL2DPointD O = circle.center;
double R = circle.radius;
if (R < EPS)
{
outAngleDeg = 0.0;
return 0.0;
}
// 向量 OP_s , OP_e
double dx1 = pStart.x - O.x;
double dy1 = pStart.y - O.y;
double dx2 = pEnd.x - O.x;
double dy2 = pEnd.y - O.y;
// 点积
double dot = dx1 * dx2 + dy1 * dy2;
double cosTheta = dot / (R * R);
// 数值钳位,避免浮点误差导致 acos 越界 [-1,1]
cosTheta = std::max(-1.0, std::min(1.0, cosTheta));
// 弧度制圆心角
double rad = acos(cosTheta);
// 转为角度
outAngleDeg = rad * 180.0 / PI;
return rad;
}
bool _fittingArc(
std::vector< SVzNL3DPosition>& lineData,
int startIdx, int endIdx,
double& outRadius, double& outArcAngle
)
{
std::vector<SVzNL2DPointD> pointArray;
for (int j = startIdx; j <= endIdx; j++)
{
if (lineData[j].pt3D.z > 1e-4)
{
SVzNL2DPointD pt2d = { lineData[j].pt3D.y, lineData[j].pt3D.z };
pointArray.push_back(pt2d);
}
}
if (pointArray.size() < 5)
return false;
//计算转角
SVzNL2DPointD center = { 0, 0 };
double radius = -1.0;
fitCircleByLeastSquare_3(pointArray, center, radius);
//计算转角
Circle2d circle = Circle2d(center, radius);
double outAngleDeg = 0;
double angleRad = calcArcAngle(circle, pointArray[0], pointArray.back(), outAngleDeg);
outRadius = radius;
outArcAngle = outAngleDeg;
return true;
}
int _findValidMidIdx(std::vector< SVzNL3DPosition>& lineData, int startIdx, int endIdx)
{
int midIdx = (startIdx + endIdx) / 2;
int minDiff = -1;
int validMidIdx = -1;
for (int i = startIdx; i <= endIdx; i++)
{
if (lineData[i].pt3D.z > 1e-4)
{
int diff = midIdx - i;
if (diff < 0)
diff = -diff;
if ( (validMidIdx < 0) || (minDiff > diff))
{
validMidIdx = i;
minDiff = diff;
}
}
}
return validMidIdx;
}
/// <summary>
/// 通过计算YZ平面内的曲率方法提取圆弧。
/// seg端点z距离大于门限
/// </summary>
void wd_getRodArcFeature_YZCurvatureMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double maxDistTh,
const double minSegSize,
const double curvatureWin,
const double rodDiameter,
const double arcTotalCornerMinValue,
std::vector<SWD_rodArcFeature>& line_rodArcs //环
)
{
std::vector<SSG_RUN> segs;
wd_lineDataSegment_dist_2(
lineData,
segs,
maxDistTh,
minSegSize
);
//计算前向角和后向角
std::vector<double> ptCurvatures;
_computeCurvature_perSeg(lineData, segs, curvatureWin, ptCurvatures);
//搜索曲率
std::vector<SSG_RUN> allArcs;
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
SSG_RUN an_arc = { 0, -1, 0, 0.0 }; //startIdx, len, lastIdx
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
if (lineData[i].pt3D.z < 1e-4)
continue;
if ((ptCurvatures[i] > rodDiameter / 4) && (ptCurvatures[i] < rodDiameter*4))
{
if (an_arc.len < 0)
{
an_arc.start = i;
an_arc.len = 1;
an_arc.value = segIdx;
an_arc.curveLen = 0.0;
}
else
{
an_arc.len = i - an_arc.start + 1;
an_arc.value = segIdx;
}
}
else if (an_arc.len > 0)
{
allArcs.push_back(an_arc);
an_arc = { 0, -1, 0, 0.0 };
}
}
if (an_arc.len > 0)
{
allArcs.push_back(an_arc);
}
}
if (allArcs.size() > 0)
{
for (int i = 0; i < (int)allArcs.size(); i++)
{
int vPtIdxStart = allArcs[i].start;
int vPtIdxEnd = vPtIdxStart + allArcs[i].len - 1;
double fittingR=0, fittingAngle = 0;
bool validResult = _fittingArc(
lineData,
vPtIdxStart, vPtIdxEnd,
fittingR, fittingAngle);
if(false == validResult)
continue;
if ( (fittingAngle > arcTotalCornerMinValue) && (fittingR > rodDiameter /8) && (fittingR < rodDiameter) )
{
SWD_rodArcFeature a_feature;
memset(&a_feature, 0, sizeof(SWD_rodArcFeature));
a_feature.startPtIdx = vPtIdxStart;
a_feature.startPt = lineData[vPtIdxStart].pt3D;
a_feature.endPtIdx = vPtIdxEnd;
a_feature.endPt = lineData[vPtIdxEnd].pt3D;
a_feature.lineIdx = lineIdx;
a_feature.featureValue = fittingR;
a_feature.flag = allArcs[i].value;
int validMidIdx = _findValidMidIdx(lineData, a_feature.startPtIdx, a_feature.endPtIdx);
a_feature.peakPtIdx = validMidIdx;
a_feature.peakPt = lineData[validMidIdx].pt3D;
//检查是否合并
if (line_rodArcs.size() > 0)
{
int lastIdx = static_cast<int>(line_rodArcs.size()) -1;
SWD_rodArcFeature& lastArc = line_rodArcs[lastIdx];
if (((a_feature.startPtIdx - lastArc.endPtIdx) < 5) &&
(a_feature.flag == lastArc.flag)) //同一个Segment合并
{
vPtIdxStart = lastArc.startPtIdx;
vPtIdxEnd = a_feature.endPtIdx;
validResult = _fittingArc(
lineData,
vPtIdxStart, vPtIdxEnd,
fittingR, fittingAngle);
if (false == validResult)
line_rodArcs.push_back(a_feature);
else
{
if ((fittingAngle > arcTotalCornerMinValue) && (fittingR > rodDiameter / 4) && (fittingR < rodDiameter))
{
lastArc.endPtIdx = a_feature.endPtIdx;
lastArc.endPt = a_feature.endPt;
lastArc.featureValue = fittingR;
validMidIdx = _findValidMidIdx(lineData, lastArc.startPtIdx, lastArc.endPtIdx);
lastArc.peakPtIdx = validMidIdx;
lastArc.peakPt = lineData[validMidIdx].pt3D;
}
else
line_rodArcs.push_back(a_feature);
}
}
else
line_rodArcs.push_back(a_feature);
}
else
line_rodArcs.push_back(a_feature);
}
}
}
return;
}
/// <summary>
/// 提取激光线上的圆环的上半段弧。
/// seg端点z距离大于门限
@ -4591,7 +4912,7 @@ void wd_getRodArcFeature_segmentPeakCornerMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double maxDistTh,
const int minSegSize,
const double minSegSize,
const double peakChkWin,
const SSG_cornerParam cornerPara,
std::vector<SWD_rodArcFeature>& line_rodArcs //环

View File

@ -253,6 +253,72 @@ double fitCircleByLeastSquare_2(
return err;
}
//Ô˛×îĐĄśţłËÄâşĎ
double fitCircleByLeastSquare_3(
const std::vector<SVzNL2DPointD>& pointArray,
SVzNL2DPointD& 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].x;
sumY += pointArray[pId].y;
double x2 = pointArray[pId].x * pointArray[pId].x;
double y2 = pointArray[pId].y * pointArray[pId].y;
sumX2 += x2;
sumY2 += y2;
sumX3 += x2 * pointArray[pId].x;
sumY3 += y2 * pointArray[pId].y;
sumXY += pointArray[pId].x * pointArray[pId].y;
sumXY2 += pointArray[pId].x * y2;
sumX2Y += x2 * pointArray[pId].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].x - center.x, 2) + pow(pointArray[pId].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

@ -23,7 +23,8 @@
//version 1.3.0 : 新的定位盘中心测量功能
//version 1.3.1 : 定位盘中心测量功能添加了噪声过滤
//version 1.3.2 : 矩森棒材抓取修正一个错误码返回错误测试了地面调平API
std::string m_strVersion = "1.3.2";
//version 1.3.3 : 筑裕钢筋焊缝提取算法改进中间版本
std::string m_strVersion = "1.3.3";
const char* wd_rodAndBarDetectionVersion(void)
{
return m_strVersion.c_str();
@ -246,7 +247,7 @@ void rodAarcFeatueDetection(
int linePtNum = (int)scanLines[0].size();
for (int line = 0; line < lineNum; line++)
{
if (line == 413)
if (line == 1062)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
@ -254,6 +255,19 @@ void rodAarcFeatueDetection(
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, filterParam);
// Extract rod arc features
std::vector<SWD_rodArcFeature> line_rodArcs;
#if 1
double arcTotalCornerMinValue = 22.5; //整个Arc的转角最小值, 360/16
wd_getRodArcFeature_YZCurvatureMethod(
lineData,
line,
rodDiameter / 4,
rodDiameter / 4,
rodDiameter / 4,
rodDiameter,
arcTotalCornerMinValue,
line_rodArcs //环
);
#else
wd_getRodArcFeature_segmentPeakCornerMethod(
lineData,
line,
@ -263,6 +277,7 @@ void rodAarcFeatueDetection(
cornerPara,
line_rodArcs //环
);
#endif
//wd_getRodArcFeature_peakCornerMethod(lineData, line, rodDiameter / 2, cornerPara, line_rodArcs);
arcFeatures.push_back(line_rodArcs);
}
@ -3675,6 +3690,7 @@ void sx_rebarWeldSeamPositioning(
int* errCode)
{
*errCode = 0;
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
@ -3706,7 +3722,11 @@ void sx_rebarWeldSeamPositioning(
int kkk = 1;
//行处理
//调平,去除地面
double cuttingZ = -1;
double cuttingZ;
if (poseCalibPara.planeHeight < 0)
cuttingZ = -1;
else
cuttingZ = poseCalibPara.planeHeight - 100;// -1;
sx_rodPosition_lineDataR(scanLines[i], poseCalibPara.planeCalib, cuttingZ);
}
@ -3748,15 +3768,40 @@ void sx_rebarWeldSeamPositioning(
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures_v;
rodAarcFeatueDetection(scanLines, cornerPara, filterParam, rodParam.diameter, arcFeatures_v);
//特征生长
std::vector<SWD_rodArcFeatureTree> rodArcTrees_v;
wd_getRodArcFeatureGrowingTrees(arcFeatures_v, rodArcTrees_v, growParam);
std::vector<SWD_rodArcFeatureTree> allRodArcTrees_v;
wd_getRodArcFeatureGrowingTrees(arcFeatures_v, allRodArcTrees_v, growParam);
//根据水平度过滤目标(已经调平)
double maxTanValue = tan(10.0 * PI / 180);
std::vector<SWD_rodArcFeatureTree> rodArcTrees_v;
for (int i = 0; i < (int)allRodArcTrees_v.size(); i++)
{
SWD_rodArcFeature node_first = allRodArcTrees_v[i].treeNodes[0];
SWD_rodArcFeature node_last = allRodArcTrees_v[i].treeNodes.back();
double dist_xy = sqrt(pow(node_first.peakPt.x - node_last.peakPt.x, 2) + pow(node_first.peakPt.y - node_last.peakPt.y, 2));
double dist_z = abs(node_first.peakPt.z - node_last.peakPt.z);
double tanValue = dist_z / dist_xy;
if (tanValue < maxTanValue)
rodArcTrees_v.push_back(allRodArcTrees_v[i]);
}
//水平方向
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures_h;
rodAarcFeatueDetection(hLines_raw, cornerPara, filterParam, rodParam.diameter, arcFeatures_h);
//特征生长
std::vector<SWD_rodArcFeatureTree> allRodArcTrees_h;
wd_getRodArcFeatureGrowingTrees(arcFeatures_h, allRodArcTrees_h, growParam);
//根据水平度过滤目标(已经调平)
std::vector<SWD_rodArcFeatureTree> rodArcTrees_h;
wd_getRodArcFeatureGrowingTrees(arcFeatures_h, rodArcTrees_h, growParam);
for (int i = 0; i < (int)allRodArcTrees_h.size(); i++)
{
SWD_rodArcFeature node_first = allRodArcTrees_h[i].treeNodes[0];
SWD_rodArcFeature node_last = allRodArcTrees_h[i].treeNodes.back();
double dist_xy = sqrt(pow(node_first.peakPt.x - node_last.peakPt.x, 2) + pow(node_first.peakPt.y - node_last.peakPt.y, 2));
double dist_z = abs(node_first.peakPt.z - node_last.peakPt.z);
double tanValue = dist_z / dist_xy;
if (tanValue < maxTanValue)
rodArcTrees_h.push_back(allRodArcTrees_h[i]);
}
if ((rodArcTrees_v.size() == 0) && (rodArcTrees_h.size() == 0))
{
@ -3885,7 +3930,7 @@ void sx_rebarWeldSeamPositioning(
double len_vRod = _compute2DDistance_indexingPt(vRod.line_start, vRod.line_end);
double cross_vRod_start = _compute2DDistance(vRod.line_start.point, crossPt);
double cross_vRod_end = _compute2DDistance(vRod.line_end.point, crossPt);
if ( (cross_vRod_start > len_vRod) || (cross_vRod_end > len_vRod)) //ÏßÍâ
if ( (cross_vRod_start > (len_vRod-rodParam.diameter/2)) || (cross_vRod_end > (len_vRod-rodParam.diameter/2))) //线外
{
SWD3DPointPostion nearestLinePt_vRod;
if (cross_vRod_start < cross_vRod_end)