工件孔的检测算法更新,公共绘图接口的更新

This commit is contained in:
杰仔 2026-04-12 00:32:30 +08:00
parent 5f4be9b154
commit 7046696afb
22 changed files with 122 additions and 334 deletions

View File

@ -28,18 +28,6 @@ HECEulerOrder ToHandEyeEulerOrder(int eulerOrder)
}
}
HECCalibResult ToHandEyeCalibResult(const double clibMatrix[16])
{
HECCalibResult calibResult;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
calibResult.R.at(i, j) = clibMatrix[i * 4 + j];
}
calibResult.T.at(i) = clibMatrix[i * 4 + 3];
}
return calibResult;
}
} // namespace
DetectPresenter::DetectPresenter(/* args */)
@ -145,7 +133,7 @@ int DetectPresenter::DetectRod(
return ERR_CODE(DEV_NOT_FIND);
}
const HECCalibResult calibResult = ToHandEyeCalibResult(clibMatrix);
const HECCalibResult calibResult = HECCalibResult::fromHomogeneousArray(clibMatrix);
const HECEulerOrder hecEulerOrder = ToHandEyeEulerOrder(eulerOrder);
// 构建检测结果:生成点云图像

View File

@ -13,7 +13,6 @@
#include "IYWorkpieceHoleStatus.h"
#include "PointCloudImageUtils.h"
#include "VrDateUtils.h"
#include "CoordinateTransform.h"
class DetectPresenter
{

View File

@ -68,40 +68,6 @@ public:
// 手眼标定矩阵管理
CalibMatrix GetClibMatrix(int index) const;
// ============ 坐标转换接口 ============
/**
* @brief
*
* @param workpieceInfo
* @param robotPose 姿
* @param handEye
* @param targetPose 姿
*
* 使
* - Eye-in-Hand
* - x_dir, y_dir, z_dir
* - targetPose 姿
*/
void ConvertWorkpieceToRobotPose(const WD_workpieceInfo& workpieceInfo,
const CTRobotPose& robotPose,
const CTSixAxisCalibResult& handEye,
CTRobotPose& targetPose);
/**
* @brief Eye-to-Hand模式
*
* @param workpieceInfo
* @param cameraToBase
* @param targetPose 姿
*
* 使
* - Eye-to-Hand
*/
void ConvertWorkpieceToRobotPoseEyeToHand(const WD_workpieceInfo& workpieceInfo,
const CTSixAxisCalibResult& cameraToBase,
CTRobotPose& targetPose);
// 实现IVrConfigChangeNotify接口
virtual void OnConfigChanged(const ConfigResult& configResult) override;

View File

@ -2,13 +2,9 @@
#include "workpieceHolePositioning_Export.h"
#include "SG_baseAlgo_Export.h"
#include "AlgoParamConverter.h"
#include "IHandEyeCalib.h"
#include <fstream>
#include <QPainter>
#include <QPen>
#include <QColor>
#include <opencv2/opencv.hpp>
#include "CoordinateTransform.h"
#include "VrConvert.h"
#include <memory>
DetectPresenter::DetectPresenter(/* args */)
{
@ -91,15 +87,15 @@ int DetectPresenter::DetectWorkpieceHole(
AlgoParamConverter::LogAlgoParams("[Algo Thread]",
workpiecePara, lineSegPara, filterParam, growParam, groundCalibPara, clibMatrix);
}
#if 0
// 数据预处理:调平和去除地面(使用当前相机的调平参数)
if(cameraCalibParam){
LOG_DEBUG("Processing data with plane calibration\n");
double groundH = -1;
for(size_t i = 0; i < xyzData.size(); i++){
wd_lineDataR(xyzData[i], cameraCalibParam->planeCalib, groundH);
wd_lineDataR(xyzData[i], cameraCalibParam->planeCalib, -1);
}
}
#endif
int errCode = 0;
CVrTimeUtils oTimeUtils;
@ -124,19 +120,16 @@ int DetectPresenter::DetectWorkpieceHole(
LOG_INFO("wd_workpieceHolePositioning: found %zu workpieces, err=%d runtime=%.3fms\n", workpiecePositioning.size(), errCode, oTimeUtils.GetElapsedTimeInMilliSec());
ERR_CODE_RETURN(errCode);
// 从4x4齐次变换矩阵中提取旋转矩阵R(3x3)和平移向量T(3x1)
// clibMatrix 是行优先存储的4x4矩阵
cv::Mat R = cv::Mat::zeros(3, 3, CV_64F);
cv::Mat T = cv::Mat::zeros(3, 1, CV_64F);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
R.at<double>(i, j) = clibMatrix[i * 4 + j];
}
T.at<double>(i, 0) = clibMatrix[i * 4 + 3];
// 创建手眼标定实例
std::unique_ptr<IHandEyeCalib, decltype(&DestroyHandEyeCalibInstance)> handEyeCalib(
CreateHandEyeCalibInstance(),
DestroyHandEyeCalibInstance);
if (!handEyeCalib) {
LOG_ERROR("Failed to create HandEyeCalib instance\n");
return ERR_CODE(DEV_NOT_FIND);
}
// 构建用于可视化的点数组
std::vector<std::vector<SVzNL3DPoint>> objOps;
const HECCalibResult calibResult = HECCalibResult::fromHomogeneousArray(clibMatrix);
// 处理每个工件的检测结果
for (size_t i = 0; i < workpiecePositioning.size(); i++) {
@ -148,52 +141,44 @@ int DetectPresenter::DetectWorkpieceHole(
}
// 六轴转换:位置+姿态(转换到机械臂坐标系)
SVzNL3DPoint centerEye = workpiece.center;
HECPoint3D eyeCenter(workpiece.center.x, workpiece.center.y, workpiece.center.z);
// 1. 位置转换:使用 pointRT_2 进行坐标变换
cv::Point3d ptEye(centerEye.x, centerEye.y, centerEye.z);
cv::Point3d ptRobot;
pointRT_2(R, T, ptEye, ptRobot);
// 2. 姿态转换:按照测试代码的方式处理方向向量
// 从算法输出获取方向向量
std::vector<cv::Point3d> dirVectors_eye(3);
dirVectors_eye[0] = cv::Point3d(workpiece.x_dir.x, workpiece.x_dir.y, workpiece.x_dir.z);
dirVectors_eye[1] = cv::Point3d(workpiece.y_dir.x, workpiece.y_dir.y, workpiece.y_dir.z);
dirVectors_eye[2] = cv::Point3d(workpiece.z_dir.x, workpiece.z_dir.y, workpiece.z_dir.z);
// 构建方向向量(眼坐标系)
std::vector<HECPoint3D> dirVectors_eye(3);
dirVectors_eye[0] = HECPoint3D(workpiece.x_dir.x, workpiece.x_dir.y, workpiece.x_dir.z);
dirVectors_eye[1] = HECPoint3D(workpiece.y_dir.x, workpiece.y_dir.y, workpiece.y_dir.z);
dirVectors_eye[2] = HECPoint3D(workpiece.z_dir.x, workpiece.z_dir.y, workpiece.z_dir.z);
// 根据配置决定方向向量反向
switch (dirVectorInvert) {
case DIR_INVERT_NONE:
// 不反向
break;
case DIR_INVERT_XY:
// X和Y方向反向
dirVectors_eye[0] = cv::Point3d(-dirVectors_eye[0].x, -dirVectors_eye[0].y, -dirVectors_eye[0].z);
dirVectors_eye[1] = cv::Point3d(-dirVectors_eye[1].x, -dirVectors_eye[1].y, -dirVectors_eye[1].z);
dirVectors_eye[0] = dirVectors_eye[0] * (-1.0);
dirVectors_eye[1] = dirVectors_eye[1] * (-1.0);
break;
case DIR_INVERT_XZ:
// X和Z方向反向
dirVectors_eye[0] = cv::Point3d(-dirVectors_eye[0].x, -dirVectors_eye[0].y, -dirVectors_eye[0].z);
dirVectors_eye[2] = cv::Point3d(-dirVectors_eye[2].x, -dirVectors_eye[2].y, -dirVectors_eye[2].z);
dirVectors_eye[0] = dirVectors_eye[0] * (-1.0);
dirVectors_eye[2] = dirVectors_eye[2] * (-1.0);
break;
case DIR_INVERT_YZ:
default:
// Y和Z方向反向默认兼容原有行为
dirVectors_eye[1] = cv::Point3d(-dirVectors_eye[1].x, -dirVectors_eye[1].y, -dirVectors_eye[1].z);
dirVectors_eye[2] = cv::Point3d(-dirVectors_eye[2].x, -dirVectors_eye[2].y, -dirVectors_eye[2].z);
dirVectors_eye[1] = dirVectors_eye[1] * (-1.0);
dirVectors_eye[2] = dirVectors_eye[2] * (-1.0);
break;
}
// 使用 pointRotate 对方向向量进行旋转变换
std::vector<cv::Point3d> dirVectors_robot;
// 1. 位置转换R * P_eye + T
HECPoint3D ptRobot;
handEyeCalib->TransformPoint(calibResult.R, calibResult.T, eyeCenter, ptRobot);
// 2. 方向向量旋转变换(仅旋转,不平移)
std::vector<HECPoint3D> dirVectors_robot(3);
for (int j = 0; j < 3; j++) {
cv::Point3d rt_pt;
pointRotate(R, dirVectors_eye[j], rt_pt);
dirVectors_robot.push_back(rt_pt);
handEyeCalib->RotatePoint(calibResult.R, dirVectors_eye[j], dirVectors_robot[j]);
}
// 3. 构建旋转矩阵(按照测试代码的方式)
// 3. 构建旋转矩阵(列向量形式)
double R_pose[3][3];
R_pose[0][0] = dirVectors_robot[0].x;
R_pose[0][1] = dirVectors_robot[1].x;
@ -205,7 +190,7 @@ int DetectPresenter::DetectWorkpieceHole(
R_pose[2][1] = dirVectors_robot[1].z;
R_pose[2][2] = dirVectors_robot[2].z;
// 4. 使用 rotationMatrixToEulerZYX 转换为欧拉角外旋ZYX即RZ-RY-RX
// 4. 使用算法库的 rotationMatrixToEulerZYX 转换为欧拉角(返回角度
SSG_EulerAngles robotRpy = rotationMatrixToEulerZYX(R_pose);
// 将机器人坐标系下的位姿添加到positions列表
@ -213,21 +198,12 @@ int DetectPresenter::DetectWorkpieceHole(
centerRobotPos.x = ptRobot.x;
centerRobotPos.y = ptRobot.y;
centerRobotPos.z = ptRobot.z;
centerRobotPos.roll = robotRpy.roll; // 已经是角度
centerRobotPos.pitch = robotRpy.pitch; // 已经是角度
centerRobotPos.yaw = robotRpy.yaw; // 已经是角度
centerRobotPos.roll = robotRpy.roll;
centerRobotPos.pitch = robotRpy.pitch;
centerRobotPos.yaw = robotRpy.yaw;
detectionResult.positions.push_back(centerRobotPos);
// 添加中心点到可视化列表
std::vector<SVzNL3DPoint> holePoints;
for(size_t j = 0; j < workpiece.holes.size(); j++){
SVzNL3DPoint holeEye = workpiece.holes[j];
holePoints.push_back(holeEye);
}
holePoints.push_back(centerEye);
objOps.push_back(holePoints);
if(debugParam.enableDebug && debugParam.printDetailLog){
LOG_INFO("[Algo Thread] Direction vectors (eye): X=(%.3f,%.3f,%.3f), Y=(%.3f,%.3f,%.3f), Z=(%.3f,%.3f,%.3f)\n",
workpiece.x_dir.x, workpiece.x_dir.y, workpiece.x_dir.z,
@ -237,111 +213,29 @@ int DetectPresenter::DetectWorkpieceHole(
dirVectors_robot[0].x, dirVectors_robot[0].y, dirVectors_robot[0].z,
dirVectors_robot[1].x, dirVectors_robot[1].y, dirVectors_robot[1].z,
dirVectors_robot[2].x, dirVectors_robot[2].y, dirVectors_robot[2].z);
LOG_INFO("[Algo Thread] Center Eye Coords: X=%.2f, Y=%.2f, Z=%.2f\n",
centerEye.x, centerEye.y, centerEye.z);
LOG_INFO("[Algo Thread] Center Robot Coords: X=%.2f, Y=%.2f, Z=%.2f, R=%.4f, P=%.4f, Y=%.4f\n",
LOG_INFO("[Algo Thread] Center Eye Coords: X=%.3f, Y=%.3f, Z=%.3f\n",
workpiece.center.x, workpiece.center.y, workpiece.center.z);
LOG_INFO("[Algo Thread] Center Robot Coords: X=%.3f, Y=%.3f, Z=%.3f, R=%.4f, P=%.4f, Y=%.4f\n",
ptRobot.x, ptRobot.y, ptRobot.z,
centerRobotPos.roll, centerRobotPos.pitch, centerRobotPos.yaw);
}
}
// 从点云数据生成投影图像(单色灰色点云 + 红色孔位标记
// 使用 PointCloudCanvas 生成点云图像(灰色底图 + 红色孔位标记 + 中心点编号
{
// 固定图像尺寸
int imgRows = 992;
int imgCols = 1056;
int x_skip = 50;
int y_skip = 50;
// 计算点云范围
double xMin = 1e10, xMax = -1e10, yMin = 1e10, yMax = -1e10;
for (const auto& line : xyzData) {
for (const auto& pt : line) {
if (pt.pt3D.z < 1e-4) continue;
xMin = std::min(xMin, (double)pt.pt3D.x);
xMax = std::max(xMax, (double)pt.pt3D.x);
yMin = std::min(yMin, (double)pt.pt3D.y);
yMax = std::max(yMax, (double)pt.pt3D.y);
PointCloudCanvas canvas = PointCloudCanvas::Create(xyzData);
for (size_t i = 0; i < workpiecePositioning.size(); i++) {
const WD_workpieceInfo& workpiece = workpiecePositioning[i];
// 红色小圆点标记每个孔
for (size_t j = 0; j < workpiece.holes.size(); j++) {
canvas.drawPoint(workpiece.holes[j].x, workpiece.holes[j].y, QColor(255, 0, 0), 4);
}
// 中心点标记 + 白色编号
canvas.drawPoint(workpiece.center.x, workpiece.center.y, QColor(255, 0, 0), 4);
canvas.drawText(workpiece.center.x, workpiece.center.y,
QString("%1").arg(i + 1), Qt::white, 14);
}
// 不扩展边界,直接使用点云范围
// 计算投影比例
double y_rows = (double)(imgRows - y_skip * 2);
double x_cols = (double)(imgCols - x_skip * 2);
double x_scale = (xMax - xMin) / x_cols;
double y_scale = (yMax - yMin) / y_rows;
if (x_scale < y_scale)
x_scale = y_scale;
else
y_scale = x_scale;
// 创建图像
QImage image(imgCols, imgRows, QImage::Format_RGB888);
image.fill(Qt::black);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
// 绘制点云数据 - 使用单色灰色
QColor grayColor(150, 150, 150);
for (const auto& scanLine : xyzData) {
for (const auto& point : scanLine) {
if (point.pt3D.z < 1e-4) continue;
int px = (int)((point.pt3D.x - xMin) / x_scale + x_skip);
int py = (int)((point.pt3D.y - yMin) / y_scale + y_skip);
if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) {
painter.setPen(QPen(grayColor, 1));
painter.drawPoint(px, py);
}
}
}
// 绘制孔位标记 - 使用红色
if (!objOps.empty()) {
QColor holeColor(255, 0, 0); // 红色
painter.setPen(QPen(holeColor, 1));
painter.setBrush(QBrush(holeColor));
for (size_t i = 0; i < objOps.size(); i++) {
const auto& holeGroup = objOps[i];
size_t groupSize = holeGroup.size();
for (size_t j = 0; j < groupSize; j++) {
const SVzNL3DPoint& hole = holeGroup[j];
// 跳过全0的点
if (fabs(hole.x) < 0.0001 && fabs(hole.y) < 0.0001 && fabs(hole.z) < 0.0001) {
continue;
}
int px = (int)((hole.x - xMin) / x_scale + x_skip);
int py = (int)((hole.y - yMin) / y_scale + y_skip);
if (px >= 0 && px < imgCols && py >= 0 && py < imgRows) {
// 绘制圆点标记
int circleSize = 2;
painter.drawEllipse(px - circleSize/2, py - circleSize/2, circleSize, circleSize);
// 只有中心点(最后一个点)才绘制编号
if (j == groupSize - 1) {
painter.setPen(QPen(Qt::white, 1));
QFont font("Arial", 14, QFont::Bold);
painter.setFont(font);
painter.drawText(px + 8, py + 6, QString("%1").arg(i + 1));
// 恢复画笔
painter.setPen(QPen(holeColor, 1));
painter.setBrush(QBrush(holeColor));
}
}
}
}
}
detectionResult.image = image;
detectionResult.image = canvas.image();
}
if(debugParam.enableDebug && debugParam.saveDebugImage){

View File

@ -255,88 +255,6 @@ CalibMatrix WorkpieceHolePresenter::GetClibMatrix(int index) const
return clibMatrix;
}
// ============ 坐标转换实现 ============
void WorkpieceHolePresenter::ConvertWorkpieceToRobotPose(const WD_workpieceInfo& workpieceInfo,
const CTRobotPose& robotPose,
const CTSixAxisCalibResult& handEye,
CTRobotPose& targetPose)
{
// 1. 从方向向量构建旋转矩阵
CTRotationMatrix rotMatrix;
rotMatrix.at(0, 0) = workpieceInfo.x_dir.x;
rotMatrix.at(1, 0) = workpieceInfo.x_dir.y;
rotMatrix.at(2, 0) = workpieceInfo.x_dir.z;
rotMatrix.at(0, 1) = workpieceInfo.y_dir.x;
rotMatrix.at(1, 1) = workpieceInfo.y_dir.y;
rotMatrix.at(2, 1) = workpieceInfo.y_dir.z;
rotMatrix.at(0, 2) = workpieceInfo.z_dir.x;
rotMatrix.at(1, 2) = workpieceInfo.z_dir.y;
rotMatrix.at(2, 2) = workpieceInfo.z_dir.z;
// 2. 将旋转矩阵转换为欧拉角
CTEulerAngles eulerAngles = CCoordinateTransform::rotationMatrixToEuler(rotMatrix, CTEulerOrder::sZYX);
// 3. 构造相机坐标系下的目标位姿
CTCameraPose cameraPose(
workpieceInfo.center.x,
workpieceInfo.center.y,
workpieceInfo.center.z,
eulerAngles.roll,
eulerAngles.pitch,
eulerAngles.yaw
);
// 4. 使用六轴手眼转换Eye-in-Hand模式计算机器人目标位姿
CCoordinateTransform::sixAxisEyeInHandCalcGraspPose(cameraPose, robotPose, handEye, targetPose);
LOG_DEBUG("ConvertWorkpieceToRobotPose: Camera(%f, %f, %f, %f, %f, %f) -> Robot(%f, %f, %f, %f, %f, %f)\n",
cameraPose.x, cameraPose.y, cameraPose.z,
cameraPose.rx, cameraPose.ry, cameraPose.rz,
targetPose.x, targetPose.y, targetPose.z,
targetPose.rx, targetPose.ry, targetPose.rz);
}
void WorkpieceHolePresenter::ConvertWorkpieceToRobotPoseEyeToHand(const WD_workpieceInfo& workpieceInfo,
const CTSixAxisCalibResult& cameraToBase,
CTRobotPose& targetPose)
{
// 1. 从方向向量构建旋转矩阵
CTRotationMatrix rotMatrix;
rotMatrix.at(0, 0) = workpieceInfo.x_dir.x;
rotMatrix.at(1, 0) = workpieceInfo.x_dir.y;
rotMatrix.at(2, 0) = workpieceInfo.x_dir.z;
rotMatrix.at(0, 1) = workpieceInfo.y_dir.x;
rotMatrix.at(1, 1) = workpieceInfo.y_dir.y;
rotMatrix.at(2, 1) = workpieceInfo.y_dir.z;
rotMatrix.at(0, 2) = workpieceInfo.z_dir.x;
rotMatrix.at(1, 2) = workpieceInfo.z_dir.y;
rotMatrix.at(2, 2) = workpieceInfo.z_dir.z;
// 2. 将旋转矩阵转换为欧拉角
CTEulerAngles eulerAngles = CCoordinateTransform::rotationMatrixToEuler(rotMatrix, CTEulerOrder::sZYX);
// 3. 构造相机坐标系下的目标位姿
CTCameraPose cameraPose(
workpieceInfo.center.x,
workpieceInfo.center.y,
workpieceInfo.center.z,
eulerAngles.roll,
eulerAngles.pitch,
eulerAngles.yaw
);
// 4. 使用六轴手眼转换Eye-to-Hand模式计算机器人目标位姿
CCoordinateTransform::sixAxisEyeToHandCalcGraspPose(cameraPose, cameraToBase, targetPose);
LOG_DEBUG("ConvertWorkpieceToRobotPoseEyeToHand: Camera(%f, %f, %f, %f, %f, %f) -> Robot(%f, %f, %f, %f, %f, %f)\n",
cameraPose.x, cameraPose.y, cameraPose.z,
cameraPose.rx, cameraPose.ry, cameraPose.rz,
targetPose.x, targetPose.y, targetPose.z,
targetPose.rx, targetPose.ry, targetPose.rz);
}
void WorkpieceHolePresenter::CheckAndUpdateWorkStatus()
{
if (m_bCameraConnected) {

View File

@ -1,6 +1,7 @@
# 1.1.0
## build_0 2026-04-28
1. 更新内存泄露问题
1. 更新算法
2. 更新内存泄露问题
# 1.0.3
## build_0 2026-03-24

View File

@ -42,6 +42,8 @@ INCLUDEPATH += $$PWD/../../../Module/AuthModule/Inc
# 坐标变换库
INCLUDEPATH += $$PWD/../../../Utils/DataUtils/CoordinateTransform/Inc
# 手眼标定模块
INCLUDEPATH += $$PWD/../../../Module/HandEyeCalib/Inc
# eigen 依赖
INCLUDEPATH += $$PWD/../../../SDK/eigen-3.3.9
@ -110,6 +112,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../VrNets/debug -lVrModbus
LIBS += -L../../../VrNets/debug -lVrTcpServer
LIBS += -L../../../Utils/DataUtils/CoordinateTransform/debug -lCoordinateTransformd
LIBS += -L../../../Module/HandEyeCalib/debug -lHandEyeCalib
}else:win32:CONFIG(release, debug|release){
LIBS += -L../../../Utils/VrUtils/release -lVrUtils
@ -126,6 +129,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../VrNets/release -lVrModbus
LIBS += -L../../../VrNets/release -lVrTcpServer
LIBS += -L../../../Utils/DataUtils/CoordinateTransform/release -lCoordinateTransform
LIBS += -L../../../Module/HandEyeCalib/release -lHandEyeCalib
}else:unix:!macx {
# Unix/Linux平台库链接(包括交叉编译)
# 注意链接顺序:依赖关系从高到低(被依赖的库放在后面)
@ -143,6 +147,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../VrNets -lVrTcpServer
LIBS += -L../../../Utils/VrUtils -lVrUtils
LIBS += -L../../../Utils/DataUtils/CoordinateTransform -lCoordinateTransform
LIBS += -L../../../Module/HandEyeCalib -lHandEyeCalib
# 添加系统库依赖
LIBS += -lpthread

View File

@ -953,6 +953,19 @@ QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btn_camer_cancel">
<property name="minimumSize">

View File

@ -39,6 +39,13 @@ typedef struct
SWD3DPoint point;
}SWDIndexing3DPoint;
typedef struct
{
int lineIdx;
int ptIdx;
SVzNL3DPoint point;
}SWDIndexingVzPoint;
typedef struct
{
int start;
@ -604,6 +611,7 @@ typedef struct
double backwardDiffZ;
double pre_stepDist;
double post_stepDist;
double curr_z;
double forward_z;
double backward_z;
}SSG_pntDirAngle;

View File

@ -32,6 +32,10 @@
//汽车轮眉高度测量
#define SX_ERR_INVALID_ARC -2301
#define SX_ERR_INVALID_WHEEL_EDGE -2302
#define SX_ERR_NO_WHEEL_COMMON_EDGE -2303
#define SX_ERR_NO_WHEEL_ARC -2304
//糖袋子拆线
#define SX_ERR_NO_MARK -2401

View File

@ -29,6 +29,9 @@
<string notr="true">background-color: rgb(25, 26, 28);</string>
</property>
<layout class="QVBoxLayout" name="mainLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>40</number>
</property>
@ -41,15 +44,11 @@
<property name="bottomMargin">
<number>25</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="labelAppName">
<property name="font">
<font>
<pointsize>22</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
@ -82,22 +81,6 @@
</property>
</widget>
</item>
<item>
<spacer name="spacerAfterVersion">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="line">
<property name="styleSheet">
@ -109,32 +92,13 @@
</widget>
</item>
<item>
<spacer name="spacerAfterLine">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QGridLayout" name="infoGrid">
<layout class="QGridLayout" name="infoGrid" columnstretch="0,0">
<property name="horizontalSpacing">
<number>16</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<property name="columnStretch" stdset="0">
<string>1,4</string>
</property>
<item row="0" column="0">
<widget class="QLabel" name="labelMachineCodeTitle">
<property name="font">
@ -149,7 +113,7 @@
<string>机器码</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignVCenter</set>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -185,7 +149,7 @@
<string>CPU序列号</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignVCenter</set>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -221,7 +185,7 @@
<string>硬盘序列号</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignVCenter</set>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -257,7 +221,7 @@
<string>算法版本</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignVCenter</set>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
@ -301,6 +265,12 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
@ -331,6 +301,12 @@ border-radius: 4px;</string>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>

View File

@ -392,6 +392,22 @@ struct HECCalibResult
HECCalibResult() = default;
/**
* @brief 4x4齐次变换矩阵double[16]
* R(3x3)T(3x1)
*/
static HECCalibResult fromHomogeneousArray(const double matrix[16])
{
HECCalibResult result;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
result.R.at(i, j) = matrix[i * 4 + j];
}
result.T.at(i) = matrix[i * 4 + 3];
}
return result;
}
// 转换为齐次变换矩阵
HECHomogeneousMatrix toHomogeneousMatrix() const
{