2026-07-03 19:21:24 +08:00

292 lines
13 KiB
C++
Raw 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 "DetectPresenter.h"
#include "AlgorithmParamConverter.h"
#include "PoseAxesBuilder.h"
#include "JiuruiWorkpiecePoseTCPProtocol.h"
#include "workpieceHolePositioning_Export.h"
#include <QColor>
namespace {
QImage BuildWorkpiecePointCloudImage(const std::vector<std::vector<SVzNL3DPosition>>& xyzData,
const SSG_pointPose& pointPose,
bool hasResult)
{
PointCloudCanvas canvas = PointCloudCanvas::Create(xyzData);
if (!canvas.isValid()) {
return QImage();
}
if (hasResult) {
constexpr double kAxisLineLength = 60.0;
const QColor centerColor(0, 255, 0); // 绿色:中心点
const QColor colorX(255, 0, 0); // 红色X轴
const QColor colorY(0, 255, 0); // 绿色Y轴
const QColor colorZ(0, 0, 255); // 蓝色Z轴法向
const SVzNL3DPoint& c = pointPose.point;
const SVzNL3DPoint& xDir = pointPose.pose_x;
const SVzNL3DPoint& yDir = pointPose.pose_y;
const SVzNL3DPoint& zDir = pointPose.pose_z;
// 绘制工件中心点
canvas.drawPoint(c.x, c.y, centerColor, 10);
// 绘制 X 轴方向线
canvas.drawLine(c.x, c.y,
c.x + kAxisLineLength * xDir.x,
c.y + kAxisLineLength * xDir.y,
colorX, 2);
canvas.drawText(static_cast<int>(c.x + kAxisLineLength * xDir.x),
static_cast<int>(c.y + kAxisLineLength * xDir.y),
QStringLiteral("X"), colorX, 14, 6, -6);
// 绘制 Y 轴方向线
canvas.drawLine(c.x, c.y,
c.x + kAxisLineLength * yDir.x,
c.y + kAxisLineLength * yDir.y,
colorY, 2);
canvas.drawText(static_cast<int>(c.x + kAxisLineLength * yDir.x),
static_cast<int>(c.y + kAxisLineLength * yDir.y),
QStringLiteral("Y"), colorY, 14, 6, -6);
// 绘制 Z 轴方向线
canvas.drawLine(c.x, c.y,
c.x + kAxisLineLength * zDir.x,
c.y + kAxisLineLength * zDir.y,
colorZ, 2);
canvas.drawText(static_cast<int>(c.x + kAxisLineLength * zDir.x),
static_cast<int>(c.y + kAxisLineLength * zDir.y),
QStringLiteral("Z"), colorZ, 14, 6, -6);
}
return canvas.image().copy();
}
QImage BuildWorkpiecePointCloudImage(const std::vector<std::vector<SVzNL3DPosition>>& xyzData,
const std::vector<SSG_pointPose>& pointPoses)
{
PointCloudCanvas canvas = PointCloudCanvas::Create(xyzData);
if (!canvas.isValid()) {
return QImage();
}
constexpr double kAxisLineLength = 60.0;
const QColor centerColor(0, 255, 0);
const QColor colorX(255, 0, 0);
const QColor colorY(0, 255, 0);
const QColor colorZ(0, 0, 255);
for (size_t i = 0; i < pointPoses.size(); ++i) {
const SSG_pointPose& pp = pointPoses[i];
const SVzNL3DPoint& c = pp.point;
const SVzNL3DPoint& xDir = pp.pose_x;
const SVzNL3DPoint& yDir = pp.pose_y;
const SVzNL3DPoint& zDir = pp.pose_z;
canvas.drawPoint(c.x, c.y, centerColor, 10);
canvas.drawLine(c.x, c.y,
c.x + kAxisLineLength * xDir.x,
c.y + kAxisLineLength * xDir.y,
colorX, 2);
canvas.drawLine(c.x, c.y,
c.x + kAxisLineLength * yDir.x,
c.y + kAxisLineLength * yDir.y,
colorY, 2);
canvas.drawLine(c.x, c.y,
c.x + kAxisLineLength * zDir.x,
c.y + kAxisLineLength * zDir.y,
colorZ, 2);
canvas.drawText(static_cast<int>(c.x + kAxisLineLength * xDir.x),
static_cast<int>(c.y + kAxisLineLength * xDir.y),
QString("W%1").arg(static_cast<int>(i) + 1), colorX, 14, 6, -6);
}
return canvas.image().copy();
}
void SaveDebugImageIfNeeded(int cameraIndex,
const VrDebugParam& debugParam,
const QImage& image,
const QString& prefix)
{
if (!debugParam.enableDebug || !debugParam.saveDebugImage || image.isNull()) {
return;
}
const std::string timeStamp = CVrDateUtils::GetNowTime();
const std::string fileName = debugParam.debugOutputPath + "/" +
prefix.toStdString() + "_" + std::to_string(cameraIndex) + "_" + timeStamp + ".png";
LOG_INFO("[Algo Thread] Debug image saved image : %s\n", fileName.c_str());
image.save(QString::fromStdString(fileName));
}
} // namespace
DetectPresenter::DetectPresenter(/* args */)
{
LOG_DEBUG("DetectPresenter Init algo ver: %s\n", wd_workpieceHolePositioningVersion());
}
DetectPresenter::~DetectPresenter()
{
}
QString DetectPresenter::GetAlgoVersion()
{
return QString(wd_workpieceHolePositioningVersion());
}
int DetectPresenter::DetectWorkpiece(
int cameraIndex,
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines,
const VrAlgorithmParams& algorithmParams,
const SSG_planeCalibPara& groundCalibParam,
const VrDebugParam& debugParam,
LaserDataLoader& dataLoader,
const double clibMatrix[16],
const RobotPose6D& robotPose,
const HandEyeExtrinsic& extrinsic,
int poseOutputOrder,
DetectionResult& detectionResult)
{
if (laserLines.empty()) {
LOG_WARNING("No laser lines data available for workpiece detection\n");
return ERR_CODE(DEV_DATA_INVALID);
}
std::vector<std::vector<SVzNL3DPosition>> xyzData;
int convertResult = dataLoader.ConvertToSVzNL3DPosition(laserLines, xyzData);
if (convertResult != SUCCESS || xyzData.empty()) {
LOG_WARNING("Failed to convert workpiece data to XYZ format or no XYZ data available\n");
return ERR_CODE(DEV_DATA_INVALID);
}
// 工件定位算法参数
const JiuruiWorkpieceAlgorithmParams algoParams = AlgorithmParamConverter::ToJiuruiWorkpieceAlgorithmParams(algorithmParams);
const SSG_cornerParam& cornerParam = algoParams.cornerParam;
const WD_JiuruiWorkpieceParam& workpieceParam = algoParams.workpieceParam;
if (debugParam.enableDebug && debugParam.printDetailLog) {
LOG_INFO("[Algo Thread] JiuruiWorkpiece clibMatrix: \n\t[%.3f, %.3f, %.3f, %.3f] \n\t[%.3f, %.3f, %.3f, %.3f] \n\t[%.3f, %.3f, %.3f, %.3f] \n\t[%.3f, %.3f, %.3f, %.3f]\n",
clibMatrix[0], clibMatrix[1], clibMatrix[2], clibMatrix[3],
clibMatrix[4], clibMatrix[5], clibMatrix[6], clibMatrix[7],
clibMatrix[8], clibMatrix[9], clibMatrix[10], clibMatrix[11],
clibMatrix[12], clibMatrix[13], clibMatrix[14], clibMatrix[15]);
LOG_INFO("[Algo Thread] JiuruiWorkpiece Corner: cornerTh=%.1f, scale=%.1f, minEndingGap=%.1f, minEndingGap_z=%.1f, jumpCornerTh_1=%.1f, jumpCornerTh_2=%.1f\n",
cornerParam.cornerTh, cornerParam.scale, cornerParam.minEndingGap,
cornerParam.minEndingGap_z, cornerParam.jumpCornerTh_1, cornerParam.jumpCornerTh_2);
LOG_INFO("[Algo Thread] JiuruiWorkpiece Param: len=%.1f, width=%.1f, thickness=%.1f, planeHeight=%.3f\n",
workpieceParam.len, workpieceParam.width, workpieceParam.thickness, groundCalibParam.planeHeight);
LOG_INFO("[Algo Thread] JiuruiWorkpiece Pose Config: eulerOrder=%d, poseOutputOrder=%d, rotX=%.3f, rotY=%.3f, rotZ=%.3f, outRotX=%.3f, outRotY=%.3f, outRotZ=%.3f\n",
extrinsic.eulerOrder, poseOutputOrder, extrinsic.rotX, extrinsic.rotY, extrinsic.rotZ,
extrinsic.outRotX, extrinsic.outRotY, extrinsic.outRotZ);
LOG_INFO("[Algo Thread] JiuruiWorkpiece GroundCalib planeCalib:\n"
"\t[%.6f, %.6f, %.6f]\n"
"\t[%.6f, %.6f, %.6f]\n"
"\t[%.6f, %.6f, %.6f]\n",
groundCalibParam.planeCalib[0], groundCalibParam.planeCalib[1], groundCalibParam.planeCalib[2],
groundCalibParam.planeCalib[3], groundCalibParam.planeCalib[4], groundCalibParam.planeCalib[5],
groundCalibParam.planeCalib[6], groundCalibParam.planeCalib[7], groundCalibParam.planeCalib[8]);
LOG_INFO("[Algo Thread] JiuruiWorkpiece GroundCalib planeHeight=%.6f\n", groundCalibParam.planeHeight);
LOG_INFO("[Algo Thread] JiuruiWorkpiece GroundCalib invRMatrix:\n"
"\t[%.6f, %.6f, %.6f]\n"
"\t[%.6f, %.6f, %.6f]\n"
"\t[%.6f, %.6f, %.6f]\n",
groundCalibParam.invRMatrix[0], groundCalibParam.invRMatrix[1], groundCalibParam.invRMatrix[2],
groundCalibParam.invRMatrix[3], groundCalibParam.invRMatrix[4], groundCalibParam.invRMatrix[5],
groundCalibParam.invRMatrix[6], groundCalibParam.invRMatrix[7], groundCalibParam.invRMatrix[8]);
}
int errCode = 0;
CVrTimeUtils oTimeUtils;
LOG_DEBUG("before Jiurui_getWorkpiecePose\n");
// 调用北京工件定位算法
std::vector<SSG_pointPose> workpiecePositions;
Jiurui_getWorkpiecePose(xyzData, cornerParam, groundCalibParam, workpieceParam, workpiecePositions, &errCode);
LOG_DEBUG("after Jiurui_getWorkpiecePose\n");
LOG_INFO("Jiurui_getWorkpiecePose: err=%d count=%zu runtime=%.3fms\n",
errCode, workpiecePositions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
ERR_CODE_RETURN(errCode);
detectionResult.success = true;
detectionResult.errorCode = 0;
detectionResult.message = QStringLiteral("工件检测成功");
for (size_t i = 0; i < workpiecePositions.size(); ++i) {
const SSG_pointPose& pointPose = workpiecePositions[i];
double outX = 0.0;
double outY = 0.0;
double outZ = 0.0;
double outRoll = 0.0;
double outPitch = 0.0;
double outYaw = 0.0;
// 使用 PointPoseToEuler 从完整三轴姿态提取机器人位姿
// (算法已返回完整的 pose_x / pose_y / pose_z 三轴坐标系,无需从法向量构造)
if (!PoseAxesBuilder::PointPoseToEuler(
pointPose,
clibMatrix,
extrinsic,
robotPose,
outX, outY, outZ,
outRoll, outPitch, outYaw,
poseOutputOrder)) {
LOG_WARNING("[Algo Thread] JiuruiWorkpiece pose transform failed at index=%zu\n", i);
detectionResult.success = false;
detectionResult.errorCode = ERR_CODE(DEV_DATA_INVALID);
detectionResult.message = QStringLiteral("工件姿态计算失败");
return detectionResult.errorCode;
}
JiuruiWorkpiecePosition pos;
pos.x = outX;
pos.y = outY;
pos.z = outZ;
pos.roll = outRoll;
pos.pitch = outPitch;
pos.yaw = outYaw;
detectionResult.positions.push_back(pos);
// 填充工件原始信息(包含完整三轴姿态,用于调试显示)
JiuruiWorkpieceInfo info;
info.centerX = pointPose.point.x;
info.centerY = pointPose.point.y;
info.centerZ = pointPose.point.z;
info.poseXx = pointPose.pose_x.x;
info.poseXy = pointPose.pose_x.y;
info.poseXz = pointPose.pose_x.z;
info.poseYx = pointPose.pose_y.x;
info.poseYy = pointPose.pose_y.y;
info.poseYz = pointPose.pose_y.z;
info.poseZx = pointPose.pose_z.x;
info.poseZy = pointPose.pose_z.y;
info.poseZz = pointPose.pose_z.z;
detectionResult.workpieceInfoList.push_back(info);
if (debugParam.enableDebug && debugParam.printDetailLog) {
LOG_INFO("[Algo Thread] JiuruiWorkpiece[%zu] Eye Center: X=%.2f, Y=%.2f, Z=%.2f\n",
i, pointPose.point.x, pointPose.point.y, pointPose.point.z);
LOG_INFO("[Algo Thread] JiuruiWorkpiece[%zu] Eye PoseX: (%.3f, %.3f, %.3f)\n",
i, pointPose.pose_x.x, pointPose.pose_x.y, pointPose.pose_x.z);
LOG_INFO("[Algo Thread] JiuruiWorkpiece[%zu] Eye PoseY: (%.3f, %.3f, %.3f)\n",
i, pointPose.pose_y.x, pointPose.pose_y.y, pointPose.pose_y.z);
LOG_INFO("[Algo Thread] JiuruiWorkpiece[%zu] Eye PoseZ: (%.3f, %.3f, %.3f)\n",
i, pointPose.pose_z.x, pointPose.pose_z.y, pointPose.pose_z.z);
LOG_INFO("[Algo Thread] JiuruiWorkpiece[%zu] Robot Coords: X=%.2f, Y=%.2f, Z=%.2f, RPY=%.2f/%.2f/%.2f\n",
i, pos.x, pos.y, pos.z, pos.roll, pos.pitch, pos.yaw);
}
}
detectionResult.image = BuildWorkpiecePointCloudImage(xyzData, workpiecePositions);
SaveDebugImageIfNeeded(cameraIndex, debugParam, detectionResult.image, QStringLiteral("JiuruiWorkpiece_Image"));
return SUCCESS;
}