#include "DetectPresenter.h" #include "AlgorithmParamConverter.h" #include "PoseAxesBuilder.h" #include "TireHolePoseTCPProtocol.h" #include "workpieceHolePositioning_Export.h" #include namespace { QImage BuildTireHolePointCloudImage(const std::vector>& xyzData, const WD_HolePositionInfo& holeInfo, bool hasResult) { PointCloudCanvas canvas = PointCloudCanvas::Create(xyzData); if (!canvas.isValid()) { return QImage(); } if (hasResult) { constexpr double kNormalLineLength = 60.0; const QColor centerColor(0, 255, 0); const QColor normalColor(0, 180, 255); // 蓝色:法向 // 绘制孔中心点 canvas.drawPoint(holeInfo.center.x, holeInfo.center.y, centerColor, 10); // 绘制法向量方向线 const double normalEndX = holeInfo.center.x + kNormalLineLength * holeInfo.normDir.x; const double normalEndY = holeInfo.center.y + kNormalLineLength * holeInfo.normDir.y; canvas.drawLine(holeInfo.center.x, holeInfo.center.y, normalEndX, normalEndY, normalColor, 2); canvas.drawText(normalEndX, normalEndY, QStringLiteral("Z"), normalColor, 14, 6, -6); } return canvas.image().copy(); } QImage BuildTireHolePointCloudImage(const std::vector>& xyzData, const std::vector& holeInfos) { PointCloudCanvas canvas = PointCloudCanvas::Create(xyzData); if (!canvas.isValid()) { return QImage(); } constexpr double kNormalLineLength = 60.0; const QColor centerColor(0, 255, 0); const QColor normalColor(0, 180, 255); for (size_t i = 0; i < holeInfos.size(); ++i) { const WD_HolePositionInfo& holeInfo = holeInfos[i]; canvas.drawPoint(holeInfo.center.x, holeInfo.center.y, centerColor, 10); const double normalEndX = holeInfo.center.x + kNormalLineLength * holeInfo.normDir.x; const double normalEndY = holeInfo.center.y + kNormalLineLength * holeInfo.normDir.y; canvas.drawLine(holeInfo.center.x, holeInfo.center.y, normalEndX, normalEndY, normalColor, 2); canvas.drawText(normalEndX, normalEndY, QString("T%1").arg(static_cast(i) + 1), normalColor, 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)); } } DetectPresenter::DetectPresenter(/* args */) { LOG_DEBUG("DetectPresenter Init algo ver: %s\n", wd_workpieceHolePositioningVersion()); } DetectPresenter::~DetectPresenter() { } QString DetectPresenter::GetAlgoVersion() { return QString(wd_workpieceHolePositioningVersion()); } int DetectPresenter::DetectTireHole( int cameraIndex, std::vector>& 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 tire hole detection\n"); return ERR_CODE(DEV_DATA_INVALID); } std::vector> xyzData; int convertResult = dataLoader.ConvertToSVzNL3DPosition(laserLines, xyzData); if (convertResult != SUCCESS || xyzData.empty()) { LOG_WARNING("Failed to convert tire hole data to XYZ format or no XYZ data available\n"); return ERR_CODE(DEV_DATA_INVALID); } // 轮胎孔定位算法参数 const TireHoleAlgorithmParams algoParams = AlgorithmParamConverter::ToTireHoleAlgorithmParams(algorithmParams); const SSG_cornerParam& cornerParam = algoParams.cornerParam; const WD_tireParam& tireParam = algoParams.tireParam; if (debugParam.enableDebug && debugParam.printDetailLog) { LOG_INFO("[Algo Thread] TireHole 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] TireHole 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] TireHole Param: diameter=%.1f, thickness=%.1f, planeHeight=%.3f\n", tireParam.diameter, tireParam.thickness, groundCalibParam.planeHeight); LOG_INFO("[Algo Thread] TireHole 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] TireHole 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] TireHole GroundCalib planeHeight=%.6f\n", groundCalibParam.planeHeight); LOG_INFO("[Algo Thread] TireHole 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 sx_getTireHolePose\n"); std::vector tirePositions; sx_getTireHolePose(xyzData, cornerParam, groundCalibParam, tireParam, tirePositions, &errCode); LOG_DEBUG("after sx_getTireHolePose\n"); LOG_INFO("sx_getTireHolePose: err=%d count=%zu runtime=%.3fms\n", errCode, tirePositions.size(), oTimeUtils.GetElapsedTimeInMilliSec()); ERR_CODE_RETURN(errCode); detectionResult.success = true; detectionResult.errorCode = 0; detectionResult.message = QStringLiteral("轮胎检测成功"); for (size_t i = 0; i < tirePositions.size(); ++i) { const WD_HolePositionInfo& holeInfo = tirePositions[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; if (!PoseAxesBuilder::NormalDirToPoseAngles( holeInfo.center, holeInfo.normDir, clibMatrix, extrinsic, robotPose, outX, outY, outZ, outRoll, outPitch, outYaw, poseOutputOrder)) { LOG_WARNING("[Algo Thread] TireHole pose transform failed at index=%zu\n", i); detectionResult.success = false; detectionResult.errorCode = ERR_CODE(DEV_DATA_INVALID); detectionResult.message = QStringLiteral("轮胎姿态计算失败"); return detectionResult.errorCode; } TireHolePosition pos; pos.x = outX; pos.y = outY; pos.z = outZ; pos.roll = outRoll; pos.pitch = outPitch; pos.yaw = outYaw; detectionResult.positions.push_back(pos); TireHoleInfo info; info.centerX = holeInfo.center.x; info.centerY = holeInfo.center.y; info.centerZ = holeInfo.center.z; info.normDirX = holeInfo.normDir.x; info.normDirY = holeInfo.normDir.y; info.normDirZ = holeInfo.normDir.z; detectionResult.tireHoleInfoList.push_back(info); if (debugParam.enableDebug && debugParam.printDetailLog) { LOG_INFO("[Algo Thread] TireHole[%zu] Eye Center: X=%.2f, Y=%.2f, Z=%.2f\n", i, holeInfo.center.x, holeInfo.center.y, holeInfo.center.z); LOG_INFO("[Algo Thread] TireHole[%zu] Eye NormDir: X=%.3f, Y=%.3f, Z=%.3f\n", i, holeInfo.normDir.x, holeInfo.normDir.y, holeInfo.normDir.z); LOG_INFO("[Algo Thread] TireHole[%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 = BuildTireHolePointCloudImage(xyzData, tirePositions); SaveDebugImageIfNeeded(cameraIndex, debugParam, detectionResult.image, QStringLiteral("TireHole_Image")); return SUCCESS; }