fix:撕裂tcp没有maxid修复
This commit is contained in:
parent
b8ffc01f27
commit
4d9e24e62a
11
App/App.pro
11
App/App.pro
@ -1,23 +1,22 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
# 拆包项目
|
||||
SUBDIRS += ./GrabBag/GrabBag.pro
|
||||
# SUBDIRS += ./GrabBag/GrabBag.pro
|
||||
|
||||
# 撕裂项目
|
||||
SUBDIRS += ./BeltTearing/BeltTearing.pro
|
||||
|
||||
#焊接
|
||||
SUBDIRS += ./LapWeld/LapWeld.pro
|
||||
# SUBDIRS += ./LapWeld/LapWeld.pro
|
||||
|
||||
#工件定位
|
||||
SUBDIRS += ./Workpiece/Workpiece.pro
|
||||
|
||||
# 颗粒尺寸检测
|
||||
SUBDIRS += ./ParticleSize/ParticleSize.pro
|
||||
# SUBDIRS += ./ParticleSize/ParticleSize.pro
|
||||
|
||||
# 双目Mark检测
|
||||
SUBDIRS += ./BinocularMark/BinocularMark.pro
|
||||
# SUBDIRS += ./BinocularMark/BinocularMark.pro
|
||||
|
||||
# 工件项目(WorkpiecePosition和WorkpieceSplice)
|
||||
SUBDIRS += ./WorkpieceProject/WorkpieceProject.pro
|
||||
|
||||
# SUBDIRS += ./WorkpieceProject/WorkpieceProject.pro
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define VERSION_H
|
||||
|
||||
#define BELT_TEARING_APP_VERSION_STRING "2.0.5"
|
||||
#define BELT_TEARING_APP_VERSION_BUILD "1"
|
||||
#define BELT_TEARING_APP_VERSION_BUILD "2"
|
||||
#define BELT_TEARING_APP_PRODUCT_NAME "BeltTearingApp"
|
||||
#define BELT_TEARING_APP_COMPANY_NAME "VisionRobot"
|
||||
#define BELT_TEARING_APP_COPYRIGHT "Copyright (C) 2024-2025 VisionRobot. All rights reserved."
|
||||
|
||||
@ -45,6 +45,8 @@ BeltTearingPresenter::BeltTearingPresenter(QObject *parent)
|
||||
, m_pRobotProtocol(nullptr) // 初始化RobotProtocol指针
|
||||
, m_pModbusRTUMaster(nullptr) // 初始化ModbusRTUMaster指针
|
||||
, m_bRobotConnected(false) // 初始化连接状态
|
||||
, m_simulationTearIdCounter(1000) // 仿真撕裂ID计数器初始值
|
||||
, m_simulationCallCount(0) // 仿真调用次数初始值
|
||||
{
|
||||
// 设置静态实例
|
||||
s_instance = this;
|
||||
@ -313,67 +315,40 @@ void BeltTearingPresenter::sendSimulationData()
|
||||
if(m_pRobotProtocol){
|
||||
m_pRobotProtocol->SetWorkStatus(RobotProtocol::WORK_STATUS_WORKING);
|
||||
}
|
||||
|
||||
|
||||
// 创建模拟的撕裂结果数据
|
||||
std::vector<SSG_beltTearingInfo> simulationResults;
|
||||
|
||||
// 创建第一个模拟撕裂结果
|
||||
SSG_beltTearingInfo tear1;
|
||||
tear1.tearID = 11;
|
||||
tear1.tearStatus = keSG_tearStatus_New; // 假设1表示有效撕裂
|
||||
tear1.tearWidth = 15.5f; // 撕裂宽度 15.5mm
|
||||
tear1.tearDepth = 8.2f; // 撕裂深度 8.2mm
|
||||
tear1.roi.left = 0.0f;
|
||||
tear1.roi.top = 0.0f;
|
||||
tear1.roi.right = 60.0f;
|
||||
tear1.roi.bottom = 1.0f;
|
||||
// 其他字段由于结构定义不明确,暂时不填充
|
||||
|
||||
simulationResults.push_back(tear1);
|
||||
|
||||
// 创建第二个模拟撕裂结果
|
||||
SSG_beltTearingInfo tear2;
|
||||
tear2.tearID = 12;
|
||||
tear2.tearStatus = keSG_tearStatus_Growing; // 假设2表示无效撕裂
|
||||
tear2.tearWidth = 22.3f; // 撕裂宽度 22.3mm
|
||||
tear2.tearDepth = 12.7f; // 撕裂深度 12.7mm
|
||||
tear2.roi.left = 20.0f;
|
||||
tear2.roi.top = 0.0f;
|
||||
tear2.roi.right = 50.0f;
|
||||
tear2.roi.bottom = 1.0f;
|
||||
// 其他字段由于结构定义不明确,暂时不填充
|
||||
|
||||
simulationResults.push_back(tear2);
|
||||
// 创建一个模拟的图像
|
||||
QImage simulationImage(800, 600, QImage::Format_RGB888);
|
||||
|
||||
// 填充背景色
|
||||
simulationImage.fill(Qt::white);
|
||||
// 使用成员变量(开流时会清除)
|
||||
m_simulationCallCount++;
|
||||
|
||||
// 获取当前时间
|
||||
QDateTime currentTime = QDateTime::currentDateTime();
|
||||
QString timeString = currentTime.toString("yyyy-MM-dd hh:mm:ss");
|
||||
// 固定生成2个撕裂,长度逐渐增大
|
||||
for (int i = 0; i < 2; i++) {
|
||||
SSG_beltTearingInfo tear;
|
||||
tear.tearID = ++m_simulationTearIdCounter;
|
||||
tear.tearStatus = keSG_tearStatus_Growing;
|
||||
|
||||
// 在图像上绘制时间信息
|
||||
QPainter painter(&simulationImage);
|
||||
if (painter.isActive()) {
|
||||
QFont font("Arial", 16, QFont::Bold);
|
||||
painter.setFont(font);
|
||||
painter.setPen(Qt::black);
|
||||
// 撕裂长度:基础长度 + 调用次数 * 10
|
||||
float tearLength = 30.0f + m_simulationCallCount * 10.0f + i * 5.0f;
|
||||
|
||||
// 在图像顶部绘制时间
|
||||
painter.drawText(QRect(10, 10, 780, 40), Qt::AlignLeft | Qt::AlignVCenter, QString("时间: %1").arg(timeString));
|
||||
painter.end();
|
||||
// 设置ROI
|
||||
tear.roi.left = 10.0f + i * 50.0f;
|
||||
tear.roi.top = 0.0f;
|
||||
tear.roi.right = tear.roi.left + tearLength;
|
||||
tear.roi.bottom = 10.0f;
|
||||
|
||||
// 设置撕裂宽度和深度
|
||||
tear.tearWidth = 15.0f + i * 5.0f;
|
||||
tear.tearDepth = 8.0f + i * 2.0f;
|
||||
|
||||
simulationResults.push_back(tear);
|
||||
}
|
||||
|
||||
// 发送撕裂结果到所有客户端
|
||||
sendTearingResults(simulationResults);
|
||||
SendDetectionResultToRobot(simulationResults); // 发送检测结果到机械臂
|
||||
sendImageToClients(simulationImage);
|
||||
|
||||
m_tearingProtocol->sendDetectResult(simulationResults, simulationImage);
|
||||
|
||||
LOG_INFO("Simulation image data sent successfully (timestamp: %s)\n", timeString.toStdString().c_str());
|
||||
m_tearingProtocol->sendDetectResult(simulationResults, QImage());
|
||||
}
|
||||
|
||||
bool BeltTearingPresenter::initializeCamera()
|
||||
@ -1331,6 +1306,18 @@ void BeltTearingPresenter::ResetDetect()
|
||||
}
|
||||
}
|
||||
|
||||
// 清除TCP协议的历史最大撕裂数据
|
||||
if (m_tearingProtocol) {
|
||||
m_tearingProtocol->clearHistoryMaxData();
|
||||
}
|
||||
|
||||
// 清除简化协议的历史最大撕裂数据
|
||||
m_historyMaxTearing = HistoryMaxTearingData();
|
||||
|
||||
// 清除仿真数据计数器
|
||||
m_simulationTearIdCounter = 1000;
|
||||
m_simulationCallCount = 0;
|
||||
|
||||
result = startCamera();
|
||||
if (result != 0) {
|
||||
LOG_WARNING("Failed to start camera after reset, error code: %d\n", result);
|
||||
@ -1363,6 +1350,23 @@ int BeltTearingPresenter::StartWork()
|
||||
{
|
||||
LOG_INFO("Starting work - camera and detection\n");
|
||||
|
||||
// 清除TCP协议的历史最大撕裂数据
|
||||
if (m_tearingProtocol) {
|
||||
m_tearingProtocol->clearHistoryMaxData();
|
||||
}
|
||||
|
||||
// 清除简化协议的历史最大撕裂数据
|
||||
m_historyMaxTearing = HistoryMaxTearingData(); // 重置历史最大值
|
||||
if (m_pRobotProtocolSimplified) {
|
||||
m_pRobotProtocolSimplified->ClearAlarmData();
|
||||
LOG_INFO("Cleared simplified protocol alarm data on start work\n");
|
||||
}
|
||||
|
||||
// 清除仿真数据计数器
|
||||
m_simulationTearIdCounter = 1000;
|
||||
m_simulationCallCount = 0;
|
||||
LOG_INFO("Cleared simulation counters on start work\n");
|
||||
|
||||
// 启动相机
|
||||
int cameraResult = startCamera();
|
||||
if (cameraResult != SUCCESS) {
|
||||
@ -1740,16 +1744,15 @@ void BeltTearingPresenter::SendDetectionResultToRobot(const std::vector<SSG_belt
|
||||
{
|
||||
// 根据协议类型发送数据
|
||||
if (m_modbusTCPProtocolType == ModbusTCPProtocolType::Simplified) {
|
||||
// 简化协议:只发送最大撕裂信息
|
||||
// 简化协议:只发送历史最大撕裂信息(从开流开始的最大值)
|
||||
if (!m_pRobotProtocolSimplified) {
|
||||
LOG_WARNING("Simplified robot protocol not initialized, cannot send detection results\n");
|
||||
return;
|
||||
}
|
||||
|
||||
RobotProtocolSimplified::TearingAlarmData alarmData;
|
||||
|
||||
// 更新历史最大值
|
||||
if (!detectionResults.empty()) {
|
||||
// 找出最大撕裂(使用宽度和高度中的较大值)
|
||||
// 找出本次检测的最大撕裂
|
||||
auto maxTearIt = std::max_element(detectionResults.begin(), detectionResults.end(),
|
||||
[](const SSG_beltTearingInfo& a, const SSG_beltTearingInfo& b) {
|
||||
// 计算撕裂区域的宽度和高度,取较大值
|
||||
@ -1760,30 +1763,40 @@ void BeltTearingPresenter::SendDetectionResultToRobot(const std::vector<SSG_belt
|
||||
double widthB = b.roi.right - b.roi.left;
|
||||
double lengthB = b.roi.bottom - b.roi.top;
|
||||
double maxSizeB = widthB > lengthB ? widthB : lengthB;
|
||||
|
||||
// 返回true表示a应该排在b之前,即a的最大边长比b小
|
||||
|
||||
return maxSizeA < maxSizeB;
|
||||
});
|
||||
|
||||
// 设置报警标志
|
||||
alarmData.alarmFlag = 1; // 有撕裂报警
|
||||
|
||||
// 计算撕裂区域的宽度和长度
|
||||
// 计算本次最大撕裂的尺寸
|
||||
double dWidth = maxTearIt->roi.right - maxTearIt->roi.left;
|
||||
double dLength = maxTearIt->roi.bottom - maxTearIt->roi.top;
|
||||
// 计算撕裂区域的对角线长度,作为撕裂大小的更准确表示
|
||||
double diagonalLength = std::sqrt(dWidth * dWidth + dLength * dLength);
|
||||
alarmData.maxLength = static_cast<uint16_t>(diagonalLength); // 最大长度(毫米)
|
||||
alarmData.maxWidth = static_cast<uint16_t>(maxTearIt->tearWidth); // 最大宽度(毫米)
|
||||
alarmData.maxId = maxTearIt->tearID; // 最大撕裂ID
|
||||
uint16_t currentMaxLength = static_cast<uint16_t>(diagonalLength);
|
||||
uint16_t currentMaxWidth = static_cast<uint16_t>(maxTearIt->tearWidth);
|
||||
uint32_t currentMaxId = maxTearIt->tearID;
|
||||
|
||||
// 更新历史最大值
|
||||
if (!m_historyMaxTearing.hasData || currentMaxLength > m_historyMaxTearing.maxLength) {
|
||||
m_historyMaxTearing.maxLength = currentMaxLength;
|
||||
m_historyMaxTearing.maxWidth = currentMaxWidth;
|
||||
m_historyMaxTearing.maxId = currentMaxId;
|
||||
m_historyMaxTearing.hasData = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 构造报警数据(使用历史最大值)
|
||||
RobotProtocolSimplified::TearingAlarmData alarmData;
|
||||
if (m_historyMaxTearing.hasData) {
|
||||
alarmData.alarmFlag = 1; // 有撕裂报警
|
||||
alarmData.maxLength = m_historyMaxTearing.maxLength;
|
||||
alarmData.maxWidth = m_historyMaxTearing.maxWidth;
|
||||
alarmData.maxId = m_historyMaxTearing.maxId;
|
||||
} else {
|
||||
// 没有撕裂,清空报警
|
||||
// 没有历史数据
|
||||
alarmData.alarmFlag = 0;
|
||||
alarmData.maxLength = 0;
|
||||
alarmData.maxWidth = 0;
|
||||
alarmData.maxId = 0;
|
||||
|
||||
LOG_DEBUG("No tearing detected, clearing alarm\n");
|
||||
}
|
||||
|
||||
// 发送报警数据
|
||||
|
||||
@ -170,6 +170,21 @@ private:
|
||||
ModbusRTUMaster* m_pModbusRTUMaster = nullptr; // Modbus-RTU主端实例
|
||||
bool m_bModbusRTUConnected = false; // Modbus-RTU连接状态
|
||||
|
||||
// 历史最大撕裂数据(从开流开始累积,用于简化协议)
|
||||
struct HistoryMaxTearingData {
|
||||
uint16_t maxLength; // 历史最大撕裂长度
|
||||
uint16_t maxWidth; // 历史最大撕裂宽度
|
||||
uint32_t maxId; // 历史最大撕裂ID
|
||||
bool hasData; // 是否有数据
|
||||
|
||||
HistoryMaxTearingData() : maxLength(0), maxWidth(0), maxId(0), hasData(false) {}
|
||||
};
|
||||
HistoryMaxTearingData m_historyMaxTearing;
|
||||
|
||||
// 仿真数据计数器(开流时清除)
|
||||
uint32_t m_simulationTearIdCounter; // 仿真撕裂ID计数器
|
||||
int m_simulationCallCount; // 仿真调用次数
|
||||
|
||||
// 初始化机械臂协议
|
||||
int InitRobotProtocol();
|
||||
|
||||
|
||||
@ -101,9 +101,6 @@ int RobotProtocolSimplified::SetAlarmData(const TearingAlarmData& alarmData)
|
||||
// 更新Modbus寄存器(从地址0开始,更新所有寄存器)
|
||||
m_pModbusServer->updateHoldingRegisters(RESET_CMD_ADDR, data);
|
||||
|
||||
LOG_DEBUG("Alarm data updated: flag=%d, length=%d, width=%d, id=%u\n",
|
||||
alarmData.alarmFlag, alarmData.maxLength, alarmData.maxWidth, alarmData.maxId);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,8 @@ TearingTcpProtocol::TearingTcpProtocol(QObject *parent)
|
||||
, m_heartbeatInterval(30)
|
||||
, m_clientTimeout(90)
|
||||
, m_tcpPort(0)
|
||||
, m_historyMaxLength(0)
|
||||
, m_historyMaxId(0)
|
||||
{
|
||||
// 连接心跳定时器
|
||||
connect(m_heartbeatTimer, &QTimer::timeout, this, &TearingTcpProtocol::onHeartbeatTimeout);
|
||||
@ -95,25 +97,34 @@ void TearingTcpProtocol::sendDetectResult(const std::vector<SSG_beltTearingInfo>
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算撕裂个数和最大撕裂长度
|
||||
// 计算撕裂个数和本次检测的最大撕裂长度
|
||||
int count = static_cast<int>(results.size());
|
||||
int maxLength = 0;
|
||||
int currentMaxLength = 0;
|
||||
int currentMaxId = 0;
|
||||
|
||||
for (const auto& result : results) {
|
||||
double width = result.roi.right - result.roi.left;
|
||||
double length = result.roi.bottom - result.roi.top;
|
||||
int tearLength = static_cast<int>(width > length ? width : length);
|
||||
if (tearLength > maxLength) {
|
||||
maxLength = tearLength;
|
||||
if (tearLength > currentMaxLength) {
|
||||
currentMaxLength = tearLength;
|
||||
currentMaxId = result.tearID;
|
||||
}
|
||||
}
|
||||
|
||||
// 构造JSON消息
|
||||
// 更新历史最大值(从开流开始的最大值)
|
||||
if (currentMaxLength > m_historyMaxLength) {
|
||||
m_historyMaxLength = currentMaxLength;
|
||||
m_historyMaxId = currentMaxId;
|
||||
}
|
||||
|
||||
// 构造JSON消息(使用历史最大值)
|
||||
QJsonObject jsonObj;
|
||||
jsonObj["msgType"] = "DETECT_RESULT";
|
||||
jsonObj["timestamp"] = QDateTime::currentMSecsSinceEpoch();
|
||||
jsonObj["count"] = count;
|
||||
jsonObj["max"] = maxLength;
|
||||
jsonObj["max"] = m_historyMaxLength; // 使用历史最大长度
|
||||
jsonObj["maxId"] = m_historyMaxId; // 使用历史最大撕裂ID
|
||||
|
||||
// 将图像转换为Base64
|
||||
if (!visImage.isNull()) {
|
||||
@ -132,7 +143,8 @@ void TearingTcpProtocol::sendDetectResult(const std::vector<SSG_beltTearingInfo>
|
||||
// 发送给所有客户端
|
||||
bool success = m_tcpServer->SendAllData(frame.constData(), frame.size());
|
||||
if (success) {
|
||||
LOG_DEBUG("Sent DETECT_RESULT to all clients, count=%d, max=%d\n", count, maxLength);
|
||||
LOG_DEBUG("Sent DETECT_RESULT to all clients, count=%d, historyMax=%d, historyMaxId=%d\n",
|
||||
count, m_historyMaxLength, m_historyMaxId);
|
||||
} else {
|
||||
LOG_WARNING("Failed to send DETECT_RESULT to clients\n");
|
||||
}
|
||||
@ -182,6 +194,13 @@ void TearingTcpProtocol::setTcpPort(quint16 port)
|
||||
m_tcpPort = port;
|
||||
}
|
||||
|
||||
void TearingTcpProtocol::clearHistoryMaxData()
|
||||
{
|
||||
m_historyMaxLength = 0;
|
||||
m_historyMaxId = 0;
|
||||
LOG_INFO("Cleared history max data (开流清除)\n");
|
||||
}
|
||||
|
||||
void TearingTcpProtocol::onHeartbeatTimeout()
|
||||
{
|
||||
// 可以在这里主动发送心跳给客户端(如果需要)
|
||||
|
||||
@ -77,6 +77,7 @@ public:
|
||||
* @brief 发送撕裂检测结果
|
||||
* @param results 检测结果数组
|
||||
* @param visImage 可视化图像
|
||||
* @note 发送的JSON消息包含:count(撕裂个数)、max(最大撕裂长度)、maxId(最大撕裂ID)、visimg(Base64图像)
|
||||
*/
|
||||
void sendDetectResult(const std::vector<SSG_beltTearingInfo>& results, const QImage& visImage);
|
||||
|
||||
@ -104,6 +105,21 @@ public:
|
||||
*/
|
||||
void setTcpPort(quint16 port);
|
||||
|
||||
/**
|
||||
* @brief 清除历史最大撕裂数据(开流时调用)
|
||||
*/
|
||||
void clearHistoryMaxData();
|
||||
|
||||
/**
|
||||
* @brief 获取历史最大撕裂长度
|
||||
*/
|
||||
int getHistoryMaxLength() const { return m_historyMaxLength; }
|
||||
|
||||
/**
|
||||
* @brief 获取历史最大撕裂ID
|
||||
*/
|
||||
int getHistoryMaxId() const { return m_historyMaxId; }
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief 心跳定时器超时处理
|
||||
@ -212,6 +228,10 @@ private:
|
||||
// 回调函数
|
||||
SpeedCallback m_speedCallback;
|
||||
ControlCallback m_controlCallback;
|
||||
|
||||
// 历史最大撕裂数据(从开流开始累积)
|
||||
int m_historyMaxLength; // 历史最大撕裂长度
|
||||
int m_historyMaxId; // 历史最大撕裂对应的ID
|
||||
};
|
||||
|
||||
#endif // TEARINGTCPPROTOCOL_H
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#define VERSION_H
|
||||
|
||||
#define BELT_TEARING_SERVER_VERSION_STRING "2.0.5"
|
||||
#define BELT_TEARING_SERVER_VERSION_BUILD "1"
|
||||
#define BELT_TEARING_SERVER_VERSION_BUILD "2"
|
||||
#define BELT_TEARING_SERVER_PRODUCT_NAME "BeltTearingServer"
|
||||
#define BELT_TEARING_SERVER_COMPANY_NAME "VisionRobot"
|
||||
#define BELT_TEARING_SERVER_COPYRIGHT "Copyright (C) 2024-2025 VisionRobot. All rights reserved."
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
# 2.0.5
|
||||
## build_2 2025-12-11
|
||||
1. 修复撕裂结果发送
|
||||
|
||||
## build_1 2025-11-30
|
||||
1. 协议增加最大撕裂的ID
|
||||
2. 页面修改:上下去掉间隙
|
||||
|
||||
@ -1,474 +0,0 @@
|
||||
# 工件角点检测功能实现说明
|
||||
|
||||
## 功能概述
|
||||
本文档说明如何在 WorkpieceApp 中集成工件角点提取功能,使用 SDK/workpieceCornerExtraction 算法库进行检测,并通过 JSON 格式将检测结果发送给客户端。
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### 1. 配置结构更新 ✅
|
||||
|
||||
#### 1.1 更新 IVrConfig.h
|
||||
在 `VrWorkpieceParam` 结构中添加了 `lineLen` 参数:
|
||||
```cpp
|
||||
struct VrWorkpieceParam
|
||||
{
|
||||
double lapHeight = 2.0; // 搭接厚度
|
||||
double weldMinLen = 2.0; // 最小焊缝长度
|
||||
int weldRefPoints = 2; // 输出参考点数量
|
||||
WeldScanMode scanMode = WeldScanMode::ScanMode_V;
|
||||
double lineLen = 100.0; // 工件角点提取:直线段长度阈值
|
||||
};
|
||||
```
|
||||
|
||||
#### 1.2 更新配置文件 config.xml
|
||||
```xml
|
||||
<WorkpieceParam lapHeight="2.0" weldMinLen="2.0" weldRefPoints="2" lineLen="100.0" />
|
||||
```
|
||||
|
||||
#### 1.3 更新 VrConfig.cpp
|
||||
- 加载配置时读取 `lineLen` 参数
|
||||
- 保存配置时写入 `lineLen` 参数
|
||||
|
||||
### 2. DetectPresenter 算法集成 (待实现)
|
||||
|
||||
#### 2.1 添加SDK头文件引用
|
||||
```cpp
|
||||
#include "BQ_workpieceCornerExtraction_Export.h"
|
||||
```
|
||||
|
||||
#### 2.2 实现角点检测方法
|
||||
在 `DetectWorkpiece` 方法中添加工件角点提取调用:
|
||||
|
||||
```cpp
|
||||
int DetectPresenter::DetectWorkpiece(
|
||||
int cameraIndex,
|
||||
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines,
|
||||
const VrAlgorithmParams& algorithmParams,
|
||||
const VrDebugParam& debugParam,
|
||||
LaserDataLoader& dataLoader,
|
||||
const double clibMatrix[16],
|
||||
DetectionResult& detectionResult)
|
||||
{
|
||||
// 1. 转换激光线数据为算法需要的格式
|
||||
std::vector<std::vector<SVzNL3DPosition>> scanLines;
|
||||
// ... 数据转换代码 ...
|
||||
|
||||
// 2. 准备算法参数
|
||||
SSX_BQworkpiecePara workpieceParam;
|
||||
workpieceParam.lineLen = algorithmParams.workpieceParam.lineLen;
|
||||
|
||||
SSG_cornerParam cornerParam;
|
||||
cornerParam.cornerTh = algorithmParams.cornerParam.cornerTh;
|
||||
cornerParam.scale = algorithmParams.cornerParam.scale;
|
||||
cornerParam.minEndingGap = algorithmParams.cornerParam.minEndingGap;
|
||||
cornerParam.minEndingGap_z = algorithmParams.cornerParam.minEndingGap_z;
|
||||
cornerParam.jumpCornerTh_1 = algorithmParams.cornerParam.jumpCornerTh_1;
|
||||
cornerParam.jumpCornerTh_2 = algorithmParams.cornerParam.jumpCornerTh_2;
|
||||
|
||||
SSG_outlierFilterParam filterParam;
|
||||
filterParam.continuityTh = algorithmParams.filterParam.continuityTh;
|
||||
filterParam.outlierTh = algorithmParams.filterParam.outlierTh;
|
||||
|
||||
SSG_treeGrowParam growParam;
|
||||
growParam.maxLineSkipNum = algorithmParams.growParam.maxLineSkipNum;
|
||||
growParam.yDeviation_max = algorithmParams.growParam.yDeviation_max;
|
||||
growParam.maxSkipDistance = algorithmParams.growParam.maxSkipDistance;
|
||||
growParam.zDeviation_max = algorithmParams.growParam.zDeviation_max;
|
||||
growParam.minLTypeTreeLen = algorithmParams.growParam.minLTypeTreeLen;
|
||||
growParam.minVTypeTreeLen = algorithmParams.growParam.minVTypeTreeLen;
|
||||
|
||||
// 3. 获取调平参数
|
||||
SSG_planeCalibPara groundCalibPara;
|
||||
const VrCameraPlaneCalibParam* cameraCalib =
|
||||
algorithmParams.planeCalibParam.GetCameraCalibParam(cameraIndex);
|
||||
if (cameraCalib && cameraCalib->isCalibrated) {
|
||||
memcpy(groundCalibPara.planeCalib, cameraCalib->planeCalib, sizeof(double) * 9);
|
||||
memcpy(groundCalibPara.invRMatrix, cameraCalib->invRMatrix, sizeof(double) * 9);
|
||||
groundCalibPara.planeHeight = cameraCalib->planeHeight;
|
||||
} else {
|
||||
// 使用单位矩阵
|
||||
double identity[9] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
|
||||
memcpy(groundCalibPara.planeCalib, identity, sizeof(double) * 9);
|
||||
memcpy(groundCalibPara.invRMatrix, identity, sizeof(double) * 9);
|
||||
groundCalibPara.planeHeight = -1.0;
|
||||
}
|
||||
|
||||
// 4. 调用算法
|
||||
SSX_debugInfo debugContours[100]; // 调试信息
|
||||
int errCode = 0;
|
||||
|
||||
SSX_BQworkpieceResult result = sx_BQ_getWorkpieceCorners(
|
||||
scanLines,
|
||||
cornerParam,
|
||||
filterParam,
|
||||
growParam,
|
||||
groundCalibPara,
|
||||
workpieceParam,
|
||||
#if _OUTPUT_DEBUG_DATA
|
||||
debugContours,
|
||||
#endif
|
||||
&errCode
|
||||
);
|
||||
|
||||
if (errCode != 0) {
|
||||
LOG_ERROR("工件角点检测失败,错误码: %d\n", errCode);
|
||||
return errCode;
|
||||
}
|
||||
|
||||
// 5. 将结果转换为手眼坐标系
|
||||
// 手眼标定变换矩阵应用到检测结果
|
||||
// ... 坐标转换代码 ...
|
||||
|
||||
// 6. 填充检测结果
|
||||
detectionResult.cameraIndex = cameraIndex;
|
||||
detectionResult.positions.clear();
|
||||
|
||||
// 添加左侧角点
|
||||
for (int i = 0; i < 3; i++) {
|
||||
WorkpiecePosition pos;
|
||||
pos.x = result.corner_L[i].x;
|
||||
pos.y = result.corner_L[i].y;
|
||||
pos.z = result.corner_L[i].z;
|
||||
detectionResult.positions.push_back(pos);
|
||||
}
|
||||
|
||||
// 添加右侧角点
|
||||
for (int i = 0; i < 3; i++) {
|
||||
WorkpiecePosition pos;
|
||||
pos.x = result.corner_R[i].x;
|
||||
pos.y = result.corner_R[i].y;
|
||||
pos.z = result.corner_R[i].z;
|
||||
detectionResult.positions.push_back(pos);
|
||||
}
|
||||
|
||||
// 添加顶部角点
|
||||
for (int i = 0; i < 3; i++) {
|
||||
WorkpiecePosition pos;
|
||||
pos.x = result.corner_T[i].x;
|
||||
pos.y = result.corner_T[i].y;
|
||||
pos.z = result.corner_T[i].z;
|
||||
detectionResult.positions.push_back(pos);
|
||||
}
|
||||
|
||||
// 添加底部角点
|
||||
for (int i = 0; i < 3; i++) {
|
||||
WorkpiecePosition pos;
|
||||
pos.x = result.corner_B[i].x;
|
||||
pos.y = result.corner_B[i].y;
|
||||
pos.z = result.corner_B[i].z;
|
||||
detectionResult.positions.push_back(pos);
|
||||
}
|
||||
|
||||
// 7. 生成可视化图像(如果需要)
|
||||
if (debugParam.saveDebugImage) {
|
||||
// 创建点云可视化图像
|
||||
// ... 图像生成代码 ...
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. WorkpiecePresenter 业务逻辑集成 (待实现)
|
||||
|
||||
#### 3.1 更新检测任务方法
|
||||
在 `_DetectTask()` 方法中调用 `DetectPresenter::DetectWorkpiece()`
|
||||
|
||||
```cpp
|
||||
int WorkpiecePresenter::_DetectTask()
|
||||
{
|
||||
LOG_INFO("[Algo Thread] Start workpiece corner extraction detection\n");
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_detectionDataMutex);
|
||||
|
||||
if (m_detectionDataCache.empty()) {
|
||||
LOG_WARNING("No cached detection data available\n");
|
||||
return ERR_CODE(DEV_DATA_INVALID);
|
||||
}
|
||||
|
||||
// 执行检测
|
||||
DetectionResult detectionResult;
|
||||
int nRet = m_pDetectPresenter->DetectWorkpiece(
|
||||
m_currentCameraIndex,
|
||||
m_detectionDataCache,
|
||||
m_algorithmParams,
|
||||
m_debugParam,
|
||||
m_dataLoader,
|
||||
m_clibMatrixList[m_currentCameraIndex - 1].clibMatrix,
|
||||
detectionResult
|
||||
);
|
||||
|
||||
if (nRet != SUCCESS) {
|
||||
LOG_ERROR("Detection failed with error: %d\n", nRet);
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnStatusUpdate("检测失败");
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
// 通知UI显示结果
|
||||
detectionResult.cameraIndex = m_currentCameraIndex;
|
||||
m_pStatus->OnDetectionResult(detectionResult);
|
||||
|
||||
// 发送结果到TCP客户端
|
||||
_SendDetectionResultToTCP(detectionResult, m_currentCameraIndex);
|
||||
|
||||
// 更新状态
|
||||
m_currentWorkStatus = WorkStatus::Completed;
|
||||
m_pStatus->OnWorkStatusChanged(WorkStatus::Completed);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 实现TCP结果发送方法
|
||||
```cpp
|
||||
void WorkpiecePresenter::_SendDetectionResultToTCP(
|
||||
const DetectionResult& detectionResult,
|
||||
int cameraIndex)
|
||||
{
|
||||
if (!m_pTCPServer || !m_bTCPConnected) {
|
||||
LOG_WARNING("TCP not connected, skip sending detection result\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建JSON格式的检测结果
|
||||
QJsonObject jsonResult;
|
||||
jsonResult["cameraIndex"] = cameraIndex;
|
||||
jsonResult["timestamp"] = QDateTime::currentMSecsSinceEpoch();
|
||||
jsonResult["cornerCount"] = static_cast<int>(detectionResult.positions.size());
|
||||
|
||||
QJsonArray cornersArray;
|
||||
for (const auto& pos : detectionResult.positions) {
|
||||
QJsonObject cornerObj;
|
||||
cornerObj["x"] = pos.x;
|
||||
cornerObj["y"] = pos.y;
|
||||
cornerObj["z"] = pos.z;
|
||||
cornerObj["roll"] = pos.roll;
|
||||
cornerObj["pitch"] = pos.pitch;
|
||||
cornerObj["yaw"] = pos.yaw;
|
||||
cornersArray.append(cornerObj);
|
||||
}
|
||||
jsonResult["corners"] = cornersArray;
|
||||
|
||||
QJsonDocument jsonDoc(jsonResult);
|
||||
QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
|
||||
|
||||
// 发送JSON数据
|
||||
m_pTCPServer->SendToAll(jsonData.data(), jsonData.size());
|
||||
|
||||
LOG_INFO("Sent detection result to TCP client: %d corners\n",
|
||||
detectionResult.positions.size());
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 参数配置界面 (待实现)
|
||||
|
||||
#### 4.1 更新 dialogalgoarg.ui
|
||||
在算法参数对话框中添加工件参数配置控件:
|
||||
- `lineEdit_lineLen`: 直线段长度阈值输入框
|
||||
- `label_lineLen`: 参数标签
|
||||
|
||||
#### 4.2 更新 dialogalgoarg.cpp
|
||||
```cpp
|
||||
void DialogAlgoarg::LoadConfigToUI()
|
||||
{
|
||||
// ... 现有加载代码 ...
|
||||
|
||||
// 加载工件参数
|
||||
ui->lineEdit_lineLen->setText(
|
||||
QString::number(m_configData.algorithmParams.workpieceParam.lineLen));
|
||||
}
|
||||
|
||||
bool DialogAlgoarg::SaveConfigFromUI()
|
||||
{
|
||||
// ... 现有保存代码 ...
|
||||
|
||||
// 保存工件参数
|
||||
m_configData.algorithmParams.workpieceParam.lineLen =
|
||||
ui->lineEdit_lineLen->text().toDouble();
|
||||
|
||||
// 保存到配置文件
|
||||
QString configPath = PathManager::GetConfigFilePath();
|
||||
return m_vrConfig->SaveConfig(configPath.toStdString(), m_configData);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 主窗口显示结果 (待实现)
|
||||
|
||||
#### 5.1 更新 mainwindow.cpp
|
||||
实现 `OnDetectionResult` 回调方法:
|
||||
|
||||
```cpp
|
||||
void MainWindow::OnDetectionResult(const DetectionResult& result)
|
||||
{
|
||||
// 1. 显示检测图像
|
||||
if (!result.image.isNull()) {
|
||||
ui->label_image->setPixmap(QPixmap::fromImage(result.image));
|
||||
}
|
||||
|
||||
// 2. 更新结果列表
|
||||
ui->listWidget_results->clear();
|
||||
for (size_t i = 0; i < result.positions.size(); i++) {
|
||||
const auto& pos = result.positions[i];
|
||||
QString resultText = QString("角点 %1: X=%.2f, Y=%.2f, Z=%.2f")
|
||||
.arg(i + 1)
|
||||
.arg(pos.x)
|
||||
.arg(pos.y)
|
||||
.arg(pos.z);
|
||||
ui->listWidget_results->addItem(resultText);
|
||||
}
|
||||
|
||||
// 3. 更新状态栏
|
||||
QString statusText = QString("检测完成,找到 %1 个角点")
|
||||
.arg(result.positions.size());
|
||||
ui->statusBar->showMessage(statusText);
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 项目配置更新 (待实现)
|
||||
|
||||
#### 6.1 更新 WorkpieceApp.pro
|
||||
添加工件角点提取SDK依赖:
|
||||
|
||||
```qmake
|
||||
# 工件角点提取算法SDK
|
||||
INCLUDEPATH += ../../../SDK/workpieceCornerExtraction/Inc
|
||||
|
||||
win32:CONFIG(release, debug|release): {
|
||||
LIBS += -L$$PWD/../../../SDK/workpieceCornerExtraction/Windows/x64/Release
|
||||
LIBS += -lBQ_workpieceCornerExtraction -lbaseAlgorithm
|
||||
}
|
||||
else:win32:CONFIG(debug, debug|release): {
|
||||
LIBS += -L$$PWD/../../../SDK/workpieceCornerExtraction/Windows/x64/Debug
|
||||
LIBS += -lBQ_workpieceCornerExtraction -lbaseAlgorithm
|
||||
}
|
||||
else:unix:!macx: {
|
||||
LIBS += -L$$PWD/../../../SDK/workpieceCornerExtraction/Arm/aarch64
|
||||
LIBS += -lworkpieceCornerExtraction -lbaseAlgorithm
|
||||
}
|
||||
```
|
||||
|
||||
### 7. TCP通信协议
|
||||
|
||||
#### 7.1 客户端触发检测
|
||||
客户端发送触发命令:
|
||||
```
|
||||
@,1,1,Trig,$
|
||||
```
|
||||
格式:`@,视觉号,视觉模版号,启动信息,$`
|
||||
|
||||
#### 7.2 服务端返回检测结果
|
||||
服务端以JSON格式返回角点检测结果:
|
||||
|
||||
```json
|
||||
{
|
||||
"cameraIndex": 1,
|
||||
"timestamp": 1640000000000,
|
||||
"cornerCount": 12,
|
||||
"corners": [
|
||||
{
|
||||
"x": 100.5,
|
||||
"y": 200.3,
|
||||
"z": 50.2,
|
||||
"roll": 0.0,
|
||||
"pitch": 0.0,
|
||||
"yaw": 0.0
|
||||
},
|
||||
// ... 更多角点数据 ...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 数据流程
|
||||
|
||||
```
|
||||
客户端触发 → TCPServerProtocol → WorkpiecePresenter::StartDetection
|
||||
↓
|
||||
相机采集点云数据 → _DetectionCallback → m_detectionDataCache
|
||||
↓
|
||||
相机扫描完成 → _AlgoDetectThread → _DetectTask
|
||||
↓
|
||||
DetectPresenter::DetectWorkpiece → sx_BQ_getWorkpieceCorners (SDK算法)
|
||||
↓
|
||||
坐标转换(手眼标定)→ DetectionResult
|
||||
↓
|
||||
MainWindow::OnDetectionResult (UI显示) + _SendDetectionResultToTCP (发送给客户端)
|
||||
```
|
||||
|
||||
## 关键技术点
|
||||
|
||||
### 1. 点云数据转换
|
||||
需要将 `SVzLaserLineData` 格式转换为 `std::vector<std::vector<SVzNL3DPosition>>` 格式
|
||||
|
||||
### 2. 调平参数应用
|
||||
使用相机的平面校准参数对点云数据进行调平处理
|
||||
|
||||
### 3. 手眼标定
|
||||
将相机坐标系下的检测结果转换到机械臂坐标系
|
||||
|
||||
### 4. JSON序列化
|
||||
使用Qt的QJsonObject/QJsonDocument进行JSON格式转换
|
||||
|
||||
### 5. 多线程安全
|
||||
- 使用 `m_detectionDataMutex` 保护检测数据缓存
|
||||
- 使用 `m_algoDetectCondition` 进行线程同步
|
||||
|
||||
## 编译和部署
|
||||
|
||||
### Windows平台
|
||||
1. 确保SDK库文件在正确路径
|
||||
2. 使用Qt Creator打开项目
|
||||
3. 选择Release或Debug配置
|
||||
4. 构建项目
|
||||
|
||||
### ARM/Linux平台
|
||||
1. 配置交叉编译环境
|
||||
2. 确保ARM版本的SDK库文件存在
|
||||
3. 使用qmake生成Makefile
|
||||
4. 执行make编译
|
||||
|
||||
```bash
|
||||
cd App/Workpiece/WorkpieceApp
|
||||
qmake WorkpieceApp.pro
|
||||
make
|
||||
```
|
||||
|
||||
## 测试验证
|
||||
|
||||
### 1. 参数配置测试
|
||||
- 打开算法参数配置对话框
|
||||
- 修改lineLen参数
|
||||
- 保存并验证config.xml文件
|
||||
|
||||
### 2. 算法检测测试
|
||||
- 加载调试点云数据
|
||||
- 触发检测
|
||||
- 验证检测结果
|
||||
|
||||
### 3. TCP通信测试
|
||||
- 启动TCP服务器
|
||||
- 客户端连接并发送触发命令
|
||||
- 验证JSON结果返回
|
||||
|
||||
### 4. UI显示测试
|
||||
- 验证检测图像显示
|
||||
- 验证角点结果列表
|
||||
- 验证状态信息更新
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **SDK版本兼容性**:确保使用的SDK版本与项目兼容
|
||||
2. **内存管理**:注意释放算法分配的内存资源
|
||||
3. **坐标系转换**:确保手眼标定矩阵正确应用
|
||||
4. **错误处理**:完善错误检测和日志输出
|
||||
5. **性能优化**:大点云数据处理时注意性能
|
||||
6. **线程安全**:确保多线程访问的数据安全
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. 增加算法参数实时调整功能
|
||||
2. 支持多种工件类型的角点检测
|
||||
3. 增加检测结果的3D可视化
|
||||
4. 优化大数据量下的处理速度
|
||||
5. 增加离线调试和参数优化工具
|
||||
@ -1,672 +0,0 @@
|
||||
# 工件角点检测功能 - 完整实现代码
|
||||
|
||||
## 已完成的工作 ✅
|
||||
|
||||
### 1. 配置结构更新 ✅
|
||||
- ✅ 更新 `IVrConfig.h` 添加 `lineLen` 参数
|
||||
- ✅ 更新 `config.xml` 配置文件
|
||||
- ✅ 更新 `VrConfig.cpp` 支持加载和保存新参数
|
||||
- ✅ 更新 `WorkpieceApp.pro` 添加SDK依赖
|
||||
|
||||
### 2. 算法集成 ✅
|
||||
- ✅ 更新 `DetectPresenter.cpp` 集成角点提取算法
|
||||
- ✅ 调用 `sx_BQ_getWorkpieceCorners` API
|
||||
- ✅ 处理12个角点结果(左、右、上、下各3个)
|
||||
- ✅ 坐标转换和可视化
|
||||
|
||||
## 待实现代码
|
||||
|
||||
### 1. 参数配置界面 (dialogalgoarg)
|
||||
|
||||
#### dialogalgoarg.ui 更新
|
||||
在算法参数对话框中添加以下控件(使用Qt Designer):
|
||||
|
||||
```xml
|
||||
<!-- 在<><E59CA8><EFBFBD>有的算法参数区域添加 -->
|
||||
<widget class="QLineEdit" name="lineEdit_lineLen">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>150</x>
|
||||
<y>200</y>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>100.0</string>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
<widget class="QLabel" name="label_lineLen">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>20</x>
|
||||
<y>200</y>
|
||||
<width>120</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>直线段长度:</string>
|
||||
</property>
|
||||
</widget>
|
||||
```
|
||||
|
||||
#### dialogalgoarg.cpp 更新
|
||||
|
||||
```cpp
|
||||
#include "dialogalgoarg.h"
|
||||
#include "ui_dialogalgoarg.h"
|
||||
#include "PathManager.h"
|
||||
#include <QMessageBox>
|
||||
|
||||
DialogAlgoarg::DialogAlgoarg(IVrConfig* vrConfig, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::DialogAlgoarg)
|
||||
, m_vrConfig(vrConfig)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// 加载配置文件路径
|
||||
m_configFilePath = PathManager::GetConfigFilePath();
|
||||
|
||||
// 加载当前配置
|
||||
if (m_vrConfig) {
|
||||
m_configData = m_vrConfig->LoadConfig(m_configFilePath.toStdString());
|
||||
}
|
||||
|
||||
// 将配置数据加载到UI
|
||||
LoadConfigToUI();
|
||||
}
|
||||
|
||||
DialogAlgoarg::~DialogAlgoarg()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void DialogAlgoarg::LoadConfigToUI()
|
||||
{
|
||||
// 加载角点参数
|
||||
ui->lineEdit_cornerTh->setText(
|
||||
QString::number(m_configData.algorithmParams.cornerParam.cornerTh));
|
||||
ui->lineEdit_scale->setText(
|
||||
QString::number(m_configData.algorithmParams.cornerParam.scale));
|
||||
ui->lineEdit_minEndingGap->setText(
|
||||
QString::number(m_configData.algorithmParams.cornerParam.minEndingGap));
|
||||
ui->lineEdit_minEndingGap_z->setText(
|
||||
QString::number(m_configData.algorithmParams.cornerParam.minEndingGap_z));
|
||||
ui->lineEdit_jumpCornerTh_1->setText(
|
||||
QString::number(m_configData.algorithmParams.cornerParam.jumpCornerTh_1));
|
||||
ui->lineEdit_jumpCornerTh_2->setText(
|
||||
QString::number(m_configData.algorithmParams.cornerParam.jumpCornerTh_2));
|
||||
|
||||
// 加载树生长参数
|
||||
ui->lineEdit_maxLineSkipNum->setText(
|
||||
QString::number(m_configData.algorithmParams.growParam.maxLineSkipNum));
|
||||
ui->lineEdit_yDeviation_max->setText(
|
||||
QString::number(m_configData.algorithmParams.growParam.yDeviation_max));
|
||||
ui->lineEdit_maxSkipDistance->setText(
|
||||
QString::number(m_configData.algorithmParams.growParam.maxSkipDistance));
|
||||
ui->lineEdit_zDeviation_max->setText(
|
||||
QString::number(m_configData.algorithmParams.growParam.zDeviation_max));
|
||||
ui->lineEdit_minLTypeTreeLen->setText(
|
||||
QString::number(m_configData.algorithmParams.growParam.minLTypeTreeLen));
|
||||
ui->lineEdit_minVTypeTreeLen->setText(
|
||||
QString::number(m_configData.algorithmParams.growParam.minVTypeTreeLen));
|
||||
|
||||
// 加载工件参数
|
||||
ui->lineEdit_lapHeight->setText(
|
||||
QString::number(m_configData.algorithmParams.workpieceParam.lapHeight));
|
||||
ui->lineEdit_weldMinLen->setText(
|
||||
QString::number(m_configData.algorithmParams.workpieceParam.weldMinLen));
|
||||
ui->lineEdit_weldRefPoints->setText(
|
||||
QString::number(m_configData.algorithmParams.workpieceParam.weldRefPoints));
|
||||
|
||||
// 加载工件角点提取参数
|
||||
ui->lineEdit_lineLen->setText(
|
||||
QString::number(m_configData.algorithmParams.workpieceParam.lineLen));
|
||||
}
|
||||
|
||||
bool DialogAlgoarg::SaveConfigFromUI()
|
||||
{
|
||||
// 保存角点参数
|
||||
m_configData.algorithmParams.cornerParam.cornerTh =
|
||||
ui->lineEdit_cornerTh->text().toDouble();
|
||||
m_configData.algorithmParams.cornerParam.scale =
|
||||
ui->lineEdit_scale->text().toDouble();
|
||||
m_configData.algorithmParams.cornerParam.minEndingGap =
|
||||
ui->lineEdit_minEndingGap->text().toDouble();
|
||||
m_configData.algorithmParams.cornerParam.minEndingGap_z =
|
||||
ui->lineEdit_minEndingGap_z->text().toDouble();
|
||||
m_configData.algorithmParams.cornerParam.jumpCornerTh_1 =
|
||||
ui->lineEdit_jumpCornerTh_1->text().toDouble();
|
||||
m_configData.algorithmParams.cornerParam.jumpCornerTh_2 =
|
||||
ui->lineEdit_jumpCornerTh_2->text().toDouble();
|
||||
|
||||
// 保存树生长参数
|
||||
m_configData.algorithmParams.growParam.maxLineSkipNum =
|
||||
ui->lineEdit_maxLineSkipNum->text().toInt();
|
||||
m_configData.algorithmParams.growParam.yDeviation_max =
|
||||
ui->lineEdit_yDeviation_max->text().toDouble();
|
||||
m_configData.algorithmParams.growParam.maxSkipDistance =
|
||||
ui->lineEdit_maxSkipDistance->text().toDouble();
|
||||
m_configData.algorithmParams.growParam.zDeviation_max =
|
||||
ui->lineEdit_zDeviation_max->text().toDouble();
|
||||
m_configData.algorithmParams.growParam.minLTypeTreeLen =
|
||||
ui->lineEdit_minLTypeTreeLen->text().toDouble();
|
||||
m_configData.algorithmParams.growParam.minVTypeTreeLen =
|
||||
ui->lineEdit_minVTypeTreeLen->text().toDouble();
|
||||
|
||||
// 保存工件参数
|
||||
m_configData.algorithmParams.workpieceParam.lapHeight =
|
||||
ui->lineEdit_lapHeight->text().toDouble();
|
||||
m_configData.algorithmParams.workpieceParam.weldMinLen =
|
||||
ui->lineEdit_weldMinLen->text().toDouble();
|
||||
m_configData.algorithmParams.workpieceParam.weldRefPoints =
|
||||
ui->lineEdit_weldRefPoints->text().toInt();
|
||||
|
||||
// 保存工件角点提取参数
|
||||
m_configData.algorithmParams.workpieceParam.lineLen =
|
||||
ui->lineEdit_lineLen->text().toDouble();
|
||||
|
||||
// 保存到配置文件
|
||||
if (!m_vrConfig->SaveConfig(m_configFilePath.toStdString(), m_configData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DialogAlgoarg::on_btn_camer_ok_clicked()
|
||||
{
|
||||
if (SaveConfigFromUI()) {
|
||||
QMessageBox::information(this, "提示", "参数保存成功!");
|
||||
accept();
|
||||
} else {
|
||||
QMessageBox::warning(this, "错误", "参数保存失败!");
|
||||
}
|
||||
}
|
||||
|
||||
void DialogAlgoarg::on_btn_camer_cancel_clicked()
|
||||
{
|
||||
reject();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. TCPServerProtocol JSON结果发送
|
||||
|
||||
#### TCPServerMethods.cpp 添加方法
|
||||
|
||||
```cpp
|
||||
#include "TCPServerProtocol.h"
|
||||
#include "VrLog.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDateTime>
|
||||
|
||||
int TCPServerProtocol::SendWorkpieceCornerResult(
|
||||
const std::vector<WorkpiecePosition>& corners,
|
||||
int cameraIndex,
|
||||
const TCPClient* pClient)
|
||||
{
|
||||
if (!m_pTCPServer || !m_bServerRunning) {
|
||||
LOG_ERROR("TCP server is not running\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 构造JSON格式的检测结果
|
||||
QJsonObject jsonResult;
|
||||
jsonResult["type"] = "workpiece_corner";
|
||||
jsonResult["cameraIndex"] = cameraIndex;
|
||||
jsonResult["timestamp"] = QDateTime::currentMSecsSinceEpoch();
|
||||
jsonResult["cornerCount"] = static_cast<int>(corners.size());
|
||||
jsonResult["success"] = true;
|
||||
jsonResult["message"] = "Detection completed successfully";
|
||||
|
||||
// 构造角点数组
|
||||
QJsonArray cornersArray;
|
||||
for (size_t i = 0; i < corners.size(); i++) {
|
||||
const auto& pos = corners[i];
|
||||
QJsonObject cornerObj;
|
||||
cornerObj["index"] = static_cast<int>(i);
|
||||
cornerObj["x"] = pos.x;
|
||||
cornerObj["y"] = pos.y;
|
||||
cornerObj["z"] = pos.z;
|
||||
cornerObj["roll"] = pos.roll;
|
||||
cornerObj["pitch"] = pos.pitch;
|
||||
cornerObj["yaw"] = pos.yaw;
|
||||
|
||||
// 添加角点分类信息(可选)
|
||||
if (i < 3) {
|
||||
cornerObj["position"] = "left";
|
||||
} else if (i < 6) {
|
||||
cornerObj["position"] = "right";
|
||||
} else if (i < 9) {
|
||||
cornerObj["position"] = "top";
|
||||
} else {
|
||||
cornerObj["position"] = "bottom";
|
||||
}
|
||||
|
||||
cornersArray.append(cornerObj);
|
||||
}
|
||||
jsonResult["corners"] = cornersArray;
|
||||
|
||||
// 转换为JSON字符串
|
||||
QJsonDocument jsonDoc(jsonResult);
|
||||
QByteArray jsonData = jsonDoc.toJson(QJsonDocument::Compact);
|
||||
|
||||
// 发送数据
|
||||
int result = -1;
|
||||
if (pClient) {
|
||||
// 发送给指定客户端
|
||||
result = m_pTCPServer->SendData(pClient, jsonData.data(), jsonData.size()) ? 0 : -1;
|
||||
} else {
|
||||
// 广播给所有客户端
|
||||
result = m_pTCPServer->SendAllData(jsonData.data(), jsonData.size()) ? 0 : -1;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
LOG_INFO("Sent workpiece corner detection result: %d corners, size: %d bytes\n",
|
||||
static_cast<int>(corners.size()), jsonData.size());
|
||||
} else {
|
||||
LOG_ERROR("Failed to send workpiece corner detection result\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
#### TCPServerProtocol.h 添加声明
|
||||
|
||||
```cpp
|
||||
class TCPServerProtocol
|
||||
{
|
||||
public:
|
||||
// ... 现有方法 ...
|
||||
|
||||
/**
|
||||
* @brief 发送工件角点检测结果
|
||||
* @param corners 角点位置数组
|
||||
* @param cameraIndex 相机索引
|
||||
* @param pClient 目标客户端(nullptr表示广播给所有客户端)
|
||||
* @return 0-成功,其他-失败
|
||||
*/
|
||||
int SendWorkpieceCornerResult(
|
||||
const std::vector<WorkpiecePosition>& corners,
|
||||
int cameraIndex,
|
||||
const TCPClient* pClient = nullptr);
|
||||
};
|
||||
```
|
||||
|
||||
### 3. WorkpiecePresenter 完整业务逻辑
|
||||
|
||||
#### WorkpiecePresenter.cpp 中的 _DetectTask 更新
|
||||
|
||||
```cpp
|
||||
int WorkpiecePresenter::_DetectTask()
|
||||
{
|
||||
LOG_INFO("[Algo Thread] Start workpiece corner extraction detection\n");
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_detectionDataMutex);
|
||||
|
||||
// 1. 检查数据
|
||||
if (m_detectionDataCache.empty()) {
|
||||
LOG_WARNING("No cached detection data available\n");
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnStatusUpdate("无缓存的检测数据");
|
||||
}
|
||||
return ERR_CODE(DEV_DATA_INVALID);
|
||||
}
|
||||
|
||||
LOG_INFO("[Algo Thread] Detection data cache size: %zu lines\n",
|
||||
m_detectionDataCache.size());
|
||||
|
||||
// 2. 获取手眼标定矩阵
|
||||
const CalibMatrix& calibMatrix = m_clibMatrixList[m_currentCameraIndex - 1];
|
||||
|
||||
// 3. 执行检测
|
||||
DetectionResult detectionResult;
|
||||
int nRet = m_pDetectPresenter->DetectWorkpiece(
|
||||
m_currentCameraIndex,
|
||||
m_detectionDataCache,
|
||||
m_algorithmParams,
|
||||
m_debugParam,
|
||||
m_dataLoader,
|
||||
calibMatrix.clibMatrix,
|
||||
detectionResult
|
||||
);
|
||||
|
||||
if (nRet != SUCCESS) {
|
||||
LOG_ERROR("Detection failed with error: %d\n", nRet);
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnStatusUpdate(QString("检测失败,错误码: %1").arg(nRet).toStdString());
|
||||
m_pStatus->OnWorkStatusChanged(WorkStatus::Error);
|
||||
}
|
||||
m_currentWorkStatus = WorkStatus::Error;
|
||||
return nRet;
|
||||
}
|
||||
|
||||
LOG_INFO("[Algo Thread] Detection completed successfully, found %zu corners\n",
|
||||
detectionResult.positions.size());
|
||||
|
||||
// 4. 更新检测结果
|
||||
detectionResult.cameraIndex = m_currentCameraIndex;
|
||||
|
||||
// 5. 通知UI显示结果
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnDetectionResult(detectionResult);
|
||||
QString statusMsg = QString("检测完成,找到 %1 个角点")
|
||||
.arg(detectionResult.positions.size());
|
||||
m_pStatus->OnStatusUpdate(statusMsg.toStdString());
|
||||
}
|
||||
|
||||
// 6. 发送结果到TCP客户端
|
||||
_SendDetectionResultToTCP(detectionResult, m_currentCameraIndex);
|
||||
|
||||
// 7. 更新工作状态
|
||||
m_currentWorkStatus = WorkStatus::Completed;
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnWorkStatusChanged(WorkStatus::Completed);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
#### WorkpiecePresenter.cpp 中的 _SendDetectionResultToTCP 实现
|
||||
|
||||
```cpp
|
||||
void WorkpiecePresenter::_SendDetectionResultToTCP(
|
||||
const DetectionResult& detectionResult,
|
||||
int cameraIndex)
|
||||
{
|
||||
if (!m_pTCPServer) {
|
||||
LOG_WARNING("TCP server not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_bTCPConnected) {
|
||||
LOG_WARNING("TCP not connected, skip sending detection result\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("Sending workpiece corner detection result to TCP client\n");
|
||||
|
||||
// 调用TCPServerProtocol发送JSON格式结果
|
||||
int result = m_pTCPServer->SendWorkpieceCornerResult(
|
||||
detectionResult.positions,
|
||||
cameraIndex,
|
||||
nullptr // 广播给所有客户端
|
||||
);
|
||||
|
||||
if (result == 0) {
|
||||
LOG_INFO("Successfully sent detection result to TCP client: %zu corners\n",
|
||||
detectionResult.positions.size());
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnStatusUpdate("检测结果已发送到客户端");
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Failed to send detection result to TCP client\n");
|
||||
if (m_pStatus) {
|
||||
m_pStatus->OnStatusUpdate("发送检测结果失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. MainWindow 显示检测结果
|
||||
|
||||
#### mainwindow.cpp 实现 OnDetectionResult
|
||||
|
||||
```cpp
|
||||
void MainWindow::OnDetectionResult(const DetectionResult& result)
|
||||
{
|
||||
LOG_INFO("Received detection result: %zu corners from camera %d\n",
|
||||
result.positions.size(), result.cameraIndex);
|
||||
|
||||
// 1. 显示检测图像
|
||||
if (!result.image.isNull()) {
|
||||
// 缩放图像以适应显示区域
|
||||
QPixmap pixmap = QPixmap::fromImage(result.image);
|
||||
QPixmap scaledPixmap = pixmap.scaled(
|
||||
ui->label_image->size(),
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation
|
||||
);
|
||||
ui->label_image->setPixmap(scaledPixmap);
|
||||
|
||||
LOG_DEBUG("Detection image displayed: %dx%d\n",
|
||||
result.image.width(), result.image.height());
|
||||
} else {
|
||||
LOG_WARNING("No detection image to display\n");
|
||||
}
|
||||
|
||||
// 2. 更新结果列表
|
||||
ui->listWidget_results->clear();
|
||||
|
||||
// 添加标题信息
|
||||
QString headerText = QString("=== 相机 %1 检测结果 ===").arg(result.cameraIndex);
|
||||
ui->listWidget_results->addItem(headerText);
|
||||
|
||||
QString countText = QString("检测到 %1 个角点:").arg(result.positions.size());
|
||||
ui->listWidget_results->addItem(countText);
|
||||
ui->listWidget_results->addItem(""); // 空行
|
||||
|
||||
// 添加每个角点的详细信息
|
||||
for (size_t i = 0; i < result.positions.size(); i++) {
|
||||
const auto& pos = result.positions[i];
|
||||
|
||||
// 确定角点位置(左、右、上、下)
|
||||
QString positionLabel;
|
||||
if (i < 3) {
|
||||
positionLabel = QString("左侧角点 %1").arg(i + 1);
|
||||
} else if (i < 6) {
|
||||
positionLabel = QString("右侧角点 %1").arg(i - 2);
|
||||
} else if (i < 9) {
|
||||
positionLabel = QString("顶部角点 %1").arg(i - 5);
|
||||
} else {
|
||||
positionLabel = QString("底部角点 %1").arg(i - 8);
|
||||
}
|
||||
|
||||
QString resultText = QString("%1: X=%.2f, Y=%.2f, Z=%.2f")
|
||||
.arg(positionLabel)
|
||||
.arg(pos.x)
|
||||
.arg(pos.y)
|
||||
.arg(pos.z);
|
||||
|
||||
ui->listWidget_results->addItem(resultText);
|
||||
|
||||
LOG_DEBUG("Corner %zu: (%.2f, %.2f, %.2f)\n", i, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
// 3. 更新状态栏
|
||||
QString statusText = QString("检测完成 - 找到 %1 个角点 - 相机 %2")
|
||||
.arg(result.positions.size())
|
||||
.arg(result.cameraIndex);
|
||||
ui->statusBar->showMessage(statusText, 5000); // 显示5秒
|
||||
|
||||
// 4. 更<><E69BB4><EFBFBD>检测时间戳
|
||||
QString timeText = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
|
||||
ui->label_detectTime->setText(timeText);
|
||||
|
||||
LOG_INFO("Detection result display updated successfully\n");
|
||||
}
|
||||
```
|
||||
|
||||
## JSON 通信协议格式
|
||||
|
||||
### 客户端触发检测请求
|
||||
```
|
||||
@,1,1,Trig,$
|
||||
```
|
||||
格式说明:`@,视觉号(相机ID),视觉模版号,启动信息,$`
|
||||
|
||||
### 服务端返回检测结果
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "workpiece_corner",
|
||||
"cameraIndex": 1,
|
||||
"timestamp": 1640000000000,
|
||||
"cornerCount": 12,
|
||||
"success": true,
|
||||
"message": "Detection completed successfully",
|
||||
"corners": [
|
||||
{
|
||||
"index": 0,
|
||||
"position": "left",
|
||||
"x": 100.523,
|
||||
"y": 200.341,
|
||||
"z": 50.218,
|
||||
"roll": 0.0,
|
||||
"pitch": 0.0,
|
||||
"yaw": 0.0
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"position": "left",
|
||||
"x": 100.234,
|
||||
"y": 250.567,
|
||||
"z": 50.123,
|
||||
"roll": 0.0,
|
||||
"pitch": 0.0,
|
||||
"yaw": 0.0
|
||||
},
|
||||
// ... 更多角点数据(共12个)...
|
||||
{
|
||||
"index": 11,
|
||||
"position": "bottom",
|
||||
"x": 450.789,
|
||||
"y": 650.234,
|
||||
"z": 50.456,
|
||||
"roll": 0.0,
|
||||
"pitch": 0.0,
|
||||
"yaw": 0.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 角点位置说明
|
||||
- **index 0-2**: 左侧角点(position: "left")
|
||||
- **index 3-5**: 右侧角点(position: "right")
|
||||
- **index 6-8**: 顶部角点(position: "top")
|
||||
- **index 9-11**: 底部角点(position: "bottom")
|
||||
|
||||
## 编译和测试
|
||||
|
||||
### 编译步骤
|
||||
```bash
|
||||
# Windows平台
|
||||
cd App/Workpiece/WorkpieceApp
|
||||
qmake WorkpieceApp.pro
|
||||
nmake # 或在Qt Creator中直接构建
|
||||
|
||||
# Linux/ARM平台
|
||||
cd App/Workpiece/WorkpieceApp
|
||||
qmake WorkpieceApp.pro
|
||||
make -j4
|
||||
```
|
||||
|
||||
### 测试步骤
|
||||
|
||||
#### 1. 参数配置测试
|
||||
1. 运行WorkpieceApp
|
||||
2. 点击"算法参数"按钮打开配置对话框
|
||||
3. 修改"直线段长度"参数(默认100.0)
|
||||
4. 点击"确定"保存
|
||||
5. 验证 `config.xml` 文件中的 `lineLen` 参数已更新
|
||||
|
||||
#### 2. 算法检测测试
|
||||
1. 准备点云数据文件(.txt格式)
|
||||
2. 在主界面点击"加载数据"
|
||||
3. 选择点云文件
|
||||
4. 点击"开始检测"
|
||||
5. 观察日志输出和UI显示
|
||||
|
||||
#### 3. TCP通信测试
|
||||
使用TCP客户端工具连接服务器:
|
||||
```python
|
||||
import socket
|
||||
import json
|
||||
|
||||
# 连接服务器
|
||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
client.connect(('127.0.0.1', 5020))
|
||||
|
||||
# 发送触发命令
|
||||
trigger_cmd = b'@,1,1,Trig,$'
|
||||
client.send(trigger_cmd)
|
||||
|
||||
# 接收JSON结果
|
||||
response = client.recv(4096)
|
||||
result = json.loads(response.decode('utf-8'))
|
||||
|
||||
print(f"检测到 {result['cornerCount']} 个角点")
|
||||
for corner in result['corners']:
|
||||
print(f"角点 {corner['index']}: ({corner['x']:.2f}, {corner['y']:.2f}, {corner['z']:.2f})")
|
||||
|
||||
client.close()
|
||||
```
|
||||
|
||||
## 调试建议
|
||||
|
||||
### 1. 日志级别设置
|
||||
在 `config.xml` 中启用调试模式:
|
||||
```xml
|
||||
<DebugParam enableDebug="true" savePointCloud="true"
|
||||
saveDebugImage="true" printDetailLog="true"
|
||||
debugOutputPath="./debug" />
|
||||
```
|
||||
|
||||
### 2. 查看调试输出
|
||||
- 点云数据:`./debug/Laserline_1_YYYYMMDDHHMMSS.txt`
|
||||
- 检测图像:`./debug/Image_1_YYYYMMDDHHMMSS.png`
|
||||
|
||||
### 3. 常见问题排查
|
||||
|
||||
#### 问题1:SDK库找不到
|
||||
**症状**:编译或运行时提示找不到 `BQ_workpieceCornerExtraction.dll`
|
||||
|
||||
**解决方案**:
|
||||
- Windows: 确保 `SDK/workpieceCornerExtraction/Windows/x64/Release` 路径正确
|
||||
- Linux: 设置 `LD_LIBRARY_PATH` 环境变量
|
||||
|
||||
#### 问题2:检测结果为空
|
||||
**症状**:`cornerCount` 为 0
|
||||
|
||||
**解决方案**:
|
||||
- 检查点云数据质量
|
||||
- 调整 `lineLen` 参数(尝试50-200范围)
|
||||
- 检查调平参数是否正确
|
||||
|
||||
#### 问题3:坐标转换错误
|
||||
**症状**:角点坐标异常
|
||||
|
||||
**解决方案**:
|
||||
- 验证手眼标定矩阵
|
||||
- 检查相机调平参数
|
||||
- 查看日志中的原始坐标和转换后坐标
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
1. **大数据量处理**:启用多线程处理
|
||||
2. **网络传输**:压缩JSON数据
|
||||
3. **图像显示**:使用异步更新
|
||||
4. **内存管理**:及时释放点云缓存
|
||||
|
||||
## 总结
|
||||
|
||||
✅ **已完成**:
|
||||
- 配置结构和文件更新
|
||||
- 算法SDK集成
|
||||
- 检测结果处理
|
||||
- 项目配置文件更新
|
||||
|
||||
📝 **待实现**:
|
||||
- 参数配置UI界面(需要Qt Designer编辑.ui文件)
|
||||
- TCP JSON结果发送方法
|
||||
- 主窗口结果显示逻辑
|
||||
|
||||
所有核心算法逻辑已经完成,剩余工作主要是UI界面调整和TCP通信完善。参考本文档中的代码示例即可快速完成剩余实现。
|
||||
@ -145,7 +145,7 @@ int DetectPresenter::DetectWorkpiece(
|
||||
|
||||
LOG_DEBUG("before sx_BQ_getWorkpieceCorners \n");
|
||||
// 调用工件角点提取算法
|
||||
SSX_debugInfo debugContours[4];
|
||||
std::vector<SSX_debugInfo> debugContours;
|
||||
SSX_BQworkpieceResult bqResult = sx_BQ_getWorkpieceCorners(
|
||||
xyzData,
|
||||
cornerParam,
|
||||
@ -174,36 +174,36 @@ int DetectPresenter::DetectWorkpiece(
|
||||
// 1. 添加左侧角点(从下到上,逆时针)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
SVzNL3DPoint pt;
|
||||
pt.x = bqResult.corner_L[i].x;
|
||||
pt.y = bqResult.corner_L[i].y;
|
||||
pt.z = bqResult.corner_L[i].z;
|
||||
pt.x = bqResult.corner_1[i].x;
|
||||
pt.y = bqResult.corner_1[i].y;
|
||||
pt.z = bqResult.corner_1[i].z;
|
||||
allCorners.push_back(pt);
|
||||
}
|
||||
|
||||
// 2. 添加顶部角点(从左到右,逆时针)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
SVzNL3DPoint pt;
|
||||
pt.x = bqResult.corner_T[i].x;
|
||||
pt.y = bqResult.corner_T[i].y;
|
||||
pt.z = bqResult.corner_T[i].z;
|
||||
pt.x = bqResult.corner_2[i].x;
|
||||
pt.y = bqResult.corner_2[i].y;
|
||||
pt.z = bqResult.corner_2[i].z;
|
||||
allCorners.push_back(pt);
|
||||
}
|
||||
|
||||
// 3. 添加右侧角点(从上到下,逆时针)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
SVzNL3DPoint pt;
|
||||
pt.x = bqResult.corner_R[i].x;
|
||||
pt.y = bqResult.corner_R[i].y;
|
||||
pt.z = bqResult.corner_R[i].z;
|
||||
pt.x = bqResult.corner_3[i].x;
|
||||
pt.y = bqResult.corner_3[i].y;
|
||||
pt.z = bqResult.corner_3[i].z;
|
||||
allCorners.push_back(pt);
|
||||
}
|
||||
|
||||
// 4. 添加底部角点(从右到左,逆时针)
|
||||
for (int i = 0; i < 3; i++) {
|
||||
SVzNL3DPoint pt;
|
||||
pt.x = bqResult.corner_B[i].x;
|
||||
pt.y = bqResult.corner_B[i].y;
|
||||
pt.z = bqResult.corner_B[i].z;
|
||||
pt.x = bqResult.corner_4[i].x;
|
||||
pt.y = bqResult.corner_4[i].y;
|
||||
pt.z = bqResult.corner_4[i].z;
|
||||
allCorners.push_back(pt);
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
#define VERSION_H
|
||||
|
||||
|
||||
#define WORKPIECE_VERSION_STRING "1.0.1"
|
||||
#define WORKPIECE_BUILD_STRING "2"
|
||||
#define WORKPIECE_FULL_VERSION_STRING "V1.0.1.2"
|
||||
#define WORKPIECE_VERSION_STRING "1.0.2"
|
||||
#define WORKPIECE_BUILD_STRING "1"
|
||||
#define WORKPIECE_FULL_VERSION_STRING "V1.0.2.1"
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
inline const char* GetWorkpieceVersion() {
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
#1.0.1 2025-11-08
|
||||
# 1.0.2 2025-12-11
|
||||
## build_0
|
||||
1. 更新算法
|
||||
|
||||
# 1.0.1 2025-11-08
|
||||
## build_2
|
||||
1. 修正开机相机重连【项目结构重构】
|
||||
|
||||
@ -6,7 +10,7 @@
|
||||
1. 更新算法,在应用日志中有算法版本: 1.1.0
|
||||
2. 修复在没有配置文件中无法连接相机问题
|
||||
|
||||
#1.0.0 2025-11-02
|
||||
# 1.0.0 2025-11-02
|
||||
## build_1
|
||||
1. 修复相机打开点亮激光器
|
||||
2. 修复相机调平
|
||||
|
||||
@ -168,3 +168,6 @@ unix {
|
||||
LIBS += -lrt
|
||||
}
|
||||
!isEmpty(target.path): INSTALLS += target
|
||||
|
||||
DISTFILES += \
|
||||
Version.md
|
||||
|
||||
@ -29,14 +29,23 @@
|
||||
<!-- 多相机平面校准参数 -->
|
||||
<PlaneCalibParams>
|
||||
<!-- 相机1的调平参数(默认未校准) -->
|
||||
<CameraCalibParam cameraIndex="1" cameraName="Camera1" isCalibrated="false"
|
||||
planeHeight="-1.0"
|
||||
planeCalib_00="1.0" planeCalib_01="0.0" planeCalib_02="0.0"
|
||||
planeCalib_10="0.0" planeCalib_11="1.0" planeCalib_12="0.0"
|
||||
planeCalib_20="0.0" planeCalib_21="0.0" planeCalib_22="1.0"
|
||||
invRMatrix_00="1.0" invRMatrix_01="0.0" invRMatrix_02="0.0"
|
||||
invRMatrix_10="0.0" invRMatrix_11="1.0" invRMatrix_12="0.0"
|
||||
invRMatrix_20="0.0" invRMatrix_21="0.0" invRMatrix_22="1.0" />
|
||||
<!-- <CameraCalibParam cameraIndex="1" cameraName="Camera1" isCalibrated="true"
|
||||
planeHeight="2422.07"
|
||||
planeCalib_00="0.999812" planeCalib_01="0.000120007" planeCalib_02="-0.0193725"
|
||||
planeCalib_10="0.000120007" planeCalib_11="0.999923" planeCalib_12="0.0123879"
|
||||
planeCalib_20="0.0193725" planeCalib_21="-0.0123879" planeCalib_22="0.999736"
|
||||
invRMatrix_00="0.999812" invRMatrix_01="0.000120007" invRMatrix_02="0.0193725"
|
||||
invRMatrix_10="0.000120007" invRMatrix_11="0.999923" invRMatrix_12="-0.0123879"
|
||||
invRMatrix_20="-0.0193725" invRMatrix_21="0.0123879" invRMatrix_22="0.999736" /> -->
|
||||
|
||||
<CameraCalibParam cameraIndex="1" cameraName="Camera1" isCalibrated="true"
|
||||
planeHeight="2259.85"
|
||||
planeCalib_00="0.999967" planeCalib_01="-2.59612e-05" planeCalib_02="0.0081093"
|
||||
planeCalib_10="-2.59612e-05" planeCalib_11="0.99998" planeCalib_12="0.00640265"
|
||||
planeCalib_20="-0.0081093" planeCalib_21="-0.00640265" planeCalib_22="0.999947"
|
||||
invRMatrix_00="0.999967" invRMatrix_01="-2.59612e-05" invRMatrix_02="-0.0081093"
|
||||
invRMatrix_10="-2.59612e-05" invRMatrix_11="0.99998" invRMatrix_12="-0.00640265"
|
||||
invRMatrix_20="0.0081093" invRMatrix_21="0.00640265" invRMatrix_22="0.999947" />
|
||||
</PlaneCalibParams>
|
||||
|
||||
</AlgorithmParams>
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "VrLog.h"
|
||||
#include "PathManager.h"
|
||||
|
||||
// 前置声明,具体定义由各应用的 IVrConfig.h 提供
|
||||
class IVrConfig;
|
||||
@ -145,7 +146,7 @@ public:
|
||||
|
||||
// 保存配置文件路径
|
||||
if (configFilePath.empty()) {
|
||||
QString defaultPath = QCoreApplication::applicationDirPath() + "/config.json";
|
||||
QString defaultPath = PathManager::GetInstance().GetConfigFilePath();
|
||||
m_configFilePath = defaultPath.toStdString();
|
||||
} else {
|
||||
m_configFilePath = configFilePath;
|
||||
|
||||
@ -242,6 +242,7 @@ int BasePresenter::InitCamera(std::vector<DeviceInfo>& cameraList, bool bRGB, bo
|
||||
int cameraCount = cameraList.size();
|
||||
OnCameraCountChanged(cameraCount);
|
||||
|
||||
LOG_INFO("[BasePresenter] init eyedevice list\n");
|
||||
// 初始化相机列表,预分配空间
|
||||
m_vrEyeDeviceList.resize(cameraCount, std::make_pair("", nullptr));
|
||||
for(int i = 0; i < cameraCount; i++)
|
||||
|
||||
@ -42,10 +42,23 @@ bool ConfigMonitor::Start(const std::string& sharedMemName)
|
||||
|
||||
// 启动监控线程
|
||||
m_bMonitorRunning = true;
|
||||
m_monitorThread = std::thread(&ConfigMonitor::MonitorThreadFunc, this);
|
||||
|
||||
LOG_INFO("[ConfigMonitor] Shared memory monitor started (name: %s)\n", sharedMemName.c_str());
|
||||
return true;
|
||||
try {
|
||||
m_monitorThread = std::thread(&ConfigMonitor::MonitorThreadFunc, this);
|
||||
LOG_INFO("[ConfigMonitor] Shared memory monitor started (name: %s)\n", sharedMemName.c_str());
|
||||
return true;
|
||||
} catch (const std::system_error& e) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to create monitor thread: %s (error code: %d)\n",
|
||||
e.what(), e.code().value());
|
||||
m_bMonitorRunning = false;
|
||||
CleanupSharedMemory();
|
||||
return false;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to create monitor thread: %s\n", e.what());
|
||||
m_bMonitorRunning = false;
|
||||
CleanupSharedMemory();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigMonitor::Stop()
|
||||
@ -161,36 +174,42 @@ bool ConfigMonitor::InitializeSharedMemory(const std::string& sharedMemName)
|
||||
return true; // 已经初始化
|
||||
}
|
||||
|
||||
m_pShareMem = CreateShareMemInstance();
|
||||
if (!m_pShareMem) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to create shared memory instance\n");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
m_pShareMem = CreateShareMemInstance();
|
||||
if (!m_pShareMem) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to create shared memory instance\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试打开已存在的共享内存,如果不存在则创建
|
||||
int ret = m_pShareMem->CreateOrOpen(sharedMemName, CONFIG_CMD_SHARED_MEM_SIZE, false);
|
||||
if (ret != SUCCESS) {
|
||||
// 如果打开失败,尝试创建新的
|
||||
ret = m_pShareMem->CreateOrOpen(sharedMemName, CONFIG_CMD_SHARED_MEM_SIZE, true);
|
||||
// 尝试打开已存在的共享内存,如果不存在则创建
|
||||
int ret = m_pShareMem->CreateOrOpen(sharedMemName, CONFIG_CMD_SHARED_MEM_SIZE, false);
|
||||
if (ret != SUCCESS) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to create or open shared memory, error: %d\n", ret);
|
||||
// 如果打开失败,尝试创建新的
|
||||
ret = m_pShareMem->CreateOrOpen(sharedMemName, CONFIG_CMD_SHARED_MEM_SIZE, true);
|
||||
if (ret != SUCCESS) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to create or open shared memory, error: %d\n", ret);
|
||||
CleanupSharedMemory();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 映射内存
|
||||
void* mappedAddr = m_pShareMem->MapView();
|
||||
if (!mappedAddr) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to map shared memory view\n");
|
||||
CleanupSharedMemory();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 映射内存
|
||||
void* mappedAddr = m_pShareMem->MapView();
|
||||
if (!mappedAddr) {
|
||||
LOG_ERROR("[ConfigMonitor] Failed to map shared memory view\n");
|
||||
LOG_INFO("[ConfigMonitor] Shared memory initialized successfully (name: %s, size: %zu)\n",
|
||||
sharedMemName.c_str(), CONFIG_CMD_SHARED_MEM_SIZE);
|
||||
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("[ConfigMonitor] Exception in InitializeSharedMemory: %s\n", e.what());
|
||||
CleanupSharedMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("[ConfigMonitor] Shared memory initialized successfully (name: %s, size: %zu)\n",
|
||||
sharedMemName.c_str(), CONFIG_CMD_SHARED_MEM_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigMonitor::CleanupSharedMemory()
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "ui_DeviceStatusWidget.h"
|
||||
#include <QPainter>
|
||||
#include <QBrush>
|
||||
#include <QThread>
|
||||
#include "VrLog.h"
|
||||
|
||||
DeviceStatusWidget::DeviceStatusWidget(QWidget *parent)
|
||||
@ -69,35 +70,81 @@ void DeviceStatusWidget::setRobotStatusImage(QWidget* widget, bool isOnline) {
|
||||
// 设置相机1名称
|
||||
void DeviceStatusWidget::setCamera1Name(const QString& name)
|
||||
{
|
||||
m_camera1Name = name;
|
||||
// 如果相机1已经初始化过状态,则更新显示文本
|
||||
if (ui->dev_camera_1_txt) {
|
||||
updateCamera1DisplayText(ui->dev_camera_1_txt->text().contains("在线"));
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
m_camera1Name = name;
|
||||
// 如果相机1已经初始化过状态,则更新显示文本
|
||||
if (ui->dev_camera_1_txt) {
|
||||
updateCamera1DisplayText(ui->dev_camera_1_txt->text().contains("在线"));
|
||||
}
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, name]() {
|
||||
m_camera1Name = name;
|
||||
// 如果相机1已经初始化过状态,则更新显示文本
|
||||
if (ui->dev_camera_1_txt) {
|
||||
updateCamera1DisplayText(ui->dev_camera_1_txt->text().contains("在线"));
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置相机2名称
|
||||
void DeviceStatusWidget::setCamera2Name(const QString& name)
|
||||
{
|
||||
m_camera2Name = name;
|
||||
// 如果相机2已经初始化过状态,则更新显示文本
|
||||
if (ui->dev_camera_2_txt) {
|
||||
updateCamera2DisplayText(ui->dev_camera_2_txt->text().contains("在线"));
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
m_camera2Name = name;
|
||||
// 如果相机2已经初始化过状态,则更新显示文本
|
||||
if (ui->dev_camera_2_txt) {
|
||||
updateCamera2DisplayText(ui->dev_camera_2_txt->text().contains("在线"));
|
||||
}
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, name]() {
|
||||
m_camera2Name = name;
|
||||
// 如果相机2已经初始化过状态,则更新显示文本
|
||||
if (ui->dev_camera_2_txt) {
|
||||
updateCamera2DisplayText(ui->dev_camera_2_txt->text().contains("在线"));
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新相机1状态
|
||||
void DeviceStatusWidget::updateCamera1Status(bool isConnected)
|
||||
{
|
||||
setCameraStatusImage(ui->dev_camer_1_img, isConnected);
|
||||
updateCamera1DisplayText(isConnected);
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
setCameraStatusImage(ui->dev_camer_1_img, isConnected);
|
||||
updateCamera1DisplayText(isConnected);
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, isConnected]() {
|
||||
setCameraStatusImage(ui->dev_camer_1_img, isConnected);
|
||||
updateCamera1DisplayText(isConnected);
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新相机2状态
|
||||
void DeviceStatusWidget::updateCamera2Status(bool isConnected)
|
||||
{
|
||||
setCameraStatusImage(ui->dev_camer_2_img, isConnected);
|
||||
updateCamera2DisplayText(isConnected);
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
setCameraStatusImage(ui->dev_camer_2_img, isConnected);
|
||||
updateCamera2DisplayText(isConnected);
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, isConnected]() {
|
||||
setCameraStatusImage(ui->dev_camer_2_img, isConnected);
|
||||
updateCamera2DisplayText(isConnected);
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新相机1显示文本的私有方法
|
||||
@ -135,50 +182,121 @@ void DeviceStatusWidget::updateCamera2DisplayText(bool isConnected)
|
||||
// 更新机械臂状态
|
||||
void DeviceStatusWidget::updateRobotStatus(bool isConnected)
|
||||
{
|
||||
setRobotStatusImage(ui->dev_robot_img, isConnected);
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
setRobotStatusImage(ui->dev_robot_img, isConnected);
|
||||
|
||||
QString statusText = isConnected ? "机械臂在线" : "机械臂离线";
|
||||
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
|
||||
QString statusText = isConnected ? "机械臂在线" : "机械臂离线";
|
||||
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
|
||||
|
||||
ui->dev_robot_txt->setText(statusText);
|
||||
ui->dev_robot_txt->setStyleSheet(colorStyle);
|
||||
ui->dev_robot_txt->setText(statusText);
|
||||
ui->dev_robot_txt->setStyleSheet(colorStyle);
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, isConnected]() {
|
||||
setRobotStatusImage(ui->dev_robot_img, isConnected);
|
||||
|
||||
QString statusText = isConnected ? "机械臂在线" : "机械臂离线";
|
||||
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
|
||||
|
||||
ui->dev_robot_txt->setText(statusText);
|
||||
ui->dev_robot_txt->setStyleSheet(colorStyle);
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新串口状态
|
||||
void DeviceStatusWidget::updateSerialStatus(bool isConnected)
|
||||
{
|
||||
setRobotStatusImage(ui->dev_robot_img, isConnected);
|
||||
QString statusText = isConnected ? "RS485在线" : "RS485离线";
|
||||
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
setRobotStatusImage(ui->dev_robot_img, isConnected);
|
||||
QString statusText = isConnected ? "RS485在线" : "RS485离线";
|
||||
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
|
||||
|
||||
ui->dev_robot_txt->setText(statusText);
|
||||
ui->dev_robot_txt->setStyleSheet(colorStyle);
|
||||
ui->dev_robot_txt->setText(statusText);
|
||||
ui->dev_robot_txt->setStyleSheet(colorStyle);
|
||||
} else {
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, isConnected]() {
|
||||
setRobotStatusImage(ui->dev_robot_img, isConnected);
|
||||
QString statusText = isConnected ? "RS485在线" : "RS485离线";
|
||||
QString colorStyle = isConnected ? "color: rgb(140, 180, 60);" : "color: rgb(220, 60, 60);";
|
||||
|
||||
ui->dev_robot_txt->setText(statusText);
|
||||
ui->dev_robot_txt->setStyleSheet(colorStyle);
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置相机数量(用于控制相机二的显示和布局方向)
|
||||
void DeviceStatusWidget::setCameraCount(int cameraCount)
|
||||
{
|
||||
// 如果相机数量小于2,隐藏相机二相关的UI元素
|
||||
bool showCamera2 = (cameraCount >= 2);
|
||||
LOG_DEBUG("setCameraCount cameraCount: %d \n", cameraCount);
|
||||
|
||||
LOG_DEBUG("setCameraCount: %d \n", cameraCount);
|
||||
// 检查是否在主线程中
|
||||
if (QThread::currentThread() == this->thread()) {
|
||||
// 在主线程中,直接执行
|
||||
// 如果相机数量小于2,隐藏相机二相关的UI元素
|
||||
bool showCamera2 = (cameraCount >= 2);
|
||||
|
||||
// 隐藏整个相机2的frame,而不是单独隐藏图片和文字
|
||||
if (ui->frame_camera_2) {
|
||||
ui->frame_camera_2->setVisible(showCamera2);
|
||||
LOG_DEBUG("showCamera2: %d \n", showCamera2);
|
||||
|
||||
// 隐藏整个相机2的frame,而不是单独隐藏图片和文字
|
||||
if (ui->frame_camera_2) {
|
||||
ui->frame_camera_2->setVisible(showCamera2);
|
||||
} else {
|
||||
// 如果frame_camera_2不存在,则单独隐藏图片和文字控件
|
||||
if (ui->dev_camer_2_img) {
|
||||
ui->dev_camer_2_img->setVisible(showCamera2);
|
||||
}
|
||||
if (ui->dev_camera_2_txt) {
|
||||
ui->dev_camera_2_txt->setVisible(showCamera2);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("change status \n");
|
||||
// 根据相机数量选择布局方式
|
||||
if (cameraCount >= 2) {
|
||||
// 多相机:纵向排列
|
||||
setVerticalLayout();
|
||||
} else {
|
||||
// 单相机:横向排列,相机和机械臂各占一半
|
||||
setHorizontalLayoutForSingleCamera();
|
||||
}
|
||||
LOG_DEBUG("change layout \n");
|
||||
} else {
|
||||
// 如果frame_camera_2不存在,则单独隐藏图片和文字控件
|
||||
ui->dev_camer_2_img->setVisible(showCamera2);
|
||||
ui->dev_camera_2_txt->setVisible(showCamera2);
|
||||
}
|
||||
// 在其他线程中,使用队列方式确保线程安全
|
||||
QMetaObject::invokeMethod(this, [this, cameraCount]() {
|
||||
// 如果相机数量小于2,隐藏相机二相关的UI元素
|
||||
bool showCamera2 = (cameraCount >= 2);
|
||||
|
||||
// 根据相机数量选择布局方式
|
||||
if (cameraCount >= 2) {
|
||||
// 多相机:纵向排列
|
||||
setVerticalLayout();
|
||||
} else {
|
||||
// 单相机:横向排列,相机和机械臂各占一半
|
||||
setHorizontalLayoutForSingleCamera();
|
||||
LOG_DEBUG("showCamera2: %d \n", showCamera2);
|
||||
|
||||
// 隐藏整个相机2的frame,而不是单独隐藏图片和文字
|
||||
if (ui->frame_camera_2) {
|
||||
ui->frame_camera_2->setVisible(showCamera2);
|
||||
} else {
|
||||
// 如果frame_camera_2不存在,则单独隐藏图片和文字控件
|
||||
if (ui->dev_camer_2_img) {
|
||||
ui->dev_camer_2_img->setVisible(showCamera2);
|
||||
}
|
||||
if (ui->dev_camera_2_txt) {
|
||||
ui->dev_camera_2_txt->setVisible(showCamera2);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("change status \n");
|
||||
// 根据相机数量选择布局方式
|
||||
if (cameraCount >= 2) {
|
||||
// 多相机:纵向排列
|
||||
setVerticalLayout();
|
||||
} else {
|
||||
// 单相机:横向排列,相机和机械臂各占一半
|
||||
setHorizontalLayoutForSingleCamera();
|
||||
}
|
||||
LOG_DEBUG("change layout \n");
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "BeltTearingApp"
|
||||
#define MyAppVersion "2.0.5.1"
|
||||
#define MyAppVersion "2.0.5.2"
|
||||
#define MyAppPublisher ""
|
||||
#define MyAppURL ""
|
||||
#define MyAppExeName "BeltTearingApp.exe"
|
||||
|
||||
@ -26,4 +26,5 @@ SUBDIRS += ../App/App.pro
|
||||
# App 依赖 AppUtils(确保 AppCommon、CloudUtils 等先构建)
|
||||
App.depends = AppUtils
|
||||
App.depends = Device
|
||||
App.depends = VrUtils
|
||||
|
||||
|
||||
@ -329,7 +329,7 @@ void ModbusTCPServer::processModbusRequest(std::shared_ptr<ClientConnection> cli
|
||||
}
|
||||
|
||||
// 根据功能码验证数据格式并执行回调
|
||||
LOG_DEBUG("Modbus %02X - Trans:%d Unit:%d\n", function, transactionId, unitId);
|
||||
// LOG_DEBUG("Modbus %02X - Trans:%d Unit:%d\n", function, transactionId, unitId);
|
||||
|
||||
switch (function) {
|
||||
case MODBUS_FC_WRITE_SINGLE_COIL:
|
||||
@ -420,8 +420,6 @@ void ModbusTCPServer::processModbusRequest(std::shared_ptr<ClientConnection> cli
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG("exceptionCode: %d\n", exceptionCode);
|
||||
|
||||
// 统一处理响应
|
||||
if (exceptionCode != 0) {
|
||||
modbus_reply_exception(client->modbusCtx, query, exceptionCode);
|
||||
|
||||
@ -81,6 +81,27 @@ void vzReadLaserScanPointFromFile_XYZ_vector(const char* fileName, std::vector<s
|
||||
return;
|
||||
}
|
||||
|
||||
void wdSavePlyTxt(const char* fileName, std::vector<std::vector< SVzNL3DPosition>> scanLines)
|
||||
{
|
||||
std::ofstream sw(fileName);
|
||||
int lineNum = scanLines.size();
|
||||
for (int line = 0; line < lineNum; line++)
|
||||
{
|
||||
int nPositionCnt = scanLines[line].size();
|
||||
for (int i = 0; i < nPositionCnt; i++)
|
||||
{
|
||||
SVzNL3DPoint* pt3D = &scanLines[line][i].pt3D;
|
||||
if (pt3D->z < 1e-4)
|
||||
continue;
|
||||
double x = (double)pt3D->x;
|
||||
double y = (double)pt3D->y;
|
||||
double z = (double)pt3D->z;
|
||||
sw << x << "," << y << "," << z << std::endl;
|
||||
}
|
||||
}
|
||||
sw.close();
|
||||
}
|
||||
|
||||
void _convertToGridData_XYZ_vector(std::vector<std::vector< SVzNL3DPosition>>& scanData, double _F, std::vector<std::vector< SVzNL3DPosition>>& scanData_grid)
|
||||
{
|
||||
int min_y = 100000000;
|
||||
@ -211,59 +232,77 @@ void _outputCalibPara(char* fileName, SSG_planeCalibPara calibPara)
|
||||
void _outputCornerInfo(char* fileName, SSX_BQworkpieceResult workpieceCorner)
|
||||
{
|
||||
std::ofstream sw(fileName);
|
||||
|
||||
|
||||
sw << "节点" << workpieceCorner.workpieceType << std::endl << std::endl;
|
||||
char dataStr[250];
|
||||
sw << "L:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_L[0].x, workpieceCorner.corner_L[0].y, workpieceCorner.corner_L[0].z);
|
||||
sw << "A:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_1[0].x, workpieceCorner.corner_1[0].y, workpieceCorner.corner_1[0].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_L[1].x, workpieceCorner.corner_L[1].y, workpieceCorner.corner_L[1].z);
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_1[1].x, workpieceCorner.corner_1[1].y, workpieceCorner.corner_1[1].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_L[2].x, workpieceCorner.corner_L[2].y, workpieceCorner.corner_L[2].z);
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_1[2].x, workpieceCorner.corner_1[2].y, workpieceCorner.corner_1[2].z);
|
||||
sw << dataStr << std::endl;
|
||||
double dist = sqrt(pow(workpieceCorner.corner_L[0].x - workpieceCorner.corner_L[2].x, 2) +
|
||||
pow(workpieceCorner.corner_L[0].y - workpieceCorner.corner_L[2].y, 2) +
|
||||
pow(workpieceCorner.corner_L[0].z - workpieceCorner.corner_L[2].z, 2));
|
||||
sprintf_s(dataStr, 250, " Len: %g", dist);
|
||||
sw << dataStr << std::endl;
|
||||
|
||||
sw << "T:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_T[0].x, workpieceCorner.corner_T[0].y, workpieceCorner.corner_T[0].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_T[1].x, workpieceCorner.corner_T[1].y, workpieceCorner.corner_T[1].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_T[2].x, workpieceCorner.corner_T[2].y, workpieceCorner.corner_T[2].z);
|
||||
sw << dataStr << std::endl;
|
||||
dist = sqrt(pow(workpieceCorner.corner_T[0].x - workpieceCorner.corner_T[2].x, 2) +
|
||||
pow(workpieceCorner.corner_T[0].y - workpieceCorner.corner_T[2].y, 2) +
|
||||
pow(workpieceCorner.corner_T[0].z - workpieceCorner.corner_T[2].z, 2));
|
||||
sprintf_s(dataStr, 250, " Len: %g", dist);
|
||||
sw << dataStr << std::endl;
|
||||
|
||||
sw << "R:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_R[0].x, workpieceCorner.corner_R[0].y, workpieceCorner.corner_R[0].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_R[1].x, workpieceCorner.corner_R[1].y, workpieceCorner.corner_R[1].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_R[2].x, workpieceCorner.corner_R[2].y, workpieceCorner.corner_R[2].z);
|
||||
sw << dataStr << std::endl;
|
||||
dist = sqrt(pow(workpieceCorner.corner_R[0].x - workpieceCorner.corner_R[2].x, 2) +
|
||||
pow(workpieceCorner.corner_R[0].y - workpieceCorner.corner_R[2].y, 2) +
|
||||
pow(workpieceCorner.corner_R[0].z - workpieceCorner.corner_R[2].z, 2));
|
||||
double dist = sqrt(pow(workpieceCorner.corner_1[0].x - workpieceCorner.corner_1[2].x, 2) +
|
||||
pow(workpieceCorner.corner_1[0].y - workpieceCorner.corner_1[2].y, 2) +
|
||||
pow(workpieceCorner.corner_1[0].z - workpieceCorner.corner_1[2].z, 2));
|
||||
sprintf_s(dataStr, 250, " Len: %g", dist);
|
||||
sw << dataStr << std::endl;
|
||||
|
||||
sw << "B:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_B[0].x, workpieceCorner.corner_B[0].y, workpieceCorner.corner_B[0].z);
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_2[0].x, workpieceCorner.corner_2[0].y, workpieceCorner.corner_3[0].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_B[1].x, workpieceCorner.corner_B[1].y, workpieceCorner.corner_B[1].z);
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_2[1].x, workpieceCorner.corner_2[1].y, workpieceCorner.corner_3[1].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_B[2].x, workpieceCorner.corner_B[2].y, workpieceCorner.corner_B[2].z);
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_2[2].x, workpieceCorner.corner_2[2].y, workpieceCorner.corner_3[2].z);
|
||||
sw << dataStr << std::endl;
|
||||
dist = sqrt(pow(workpieceCorner.corner_B[0].x - workpieceCorner.corner_B[2].x, 2) +
|
||||
pow(workpieceCorner.corner_B[0].y - workpieceCorner.corner_B[2].y, 2) +
|
||||
pow(workpieceCorner.corner_B[0].z - workpieceCorner.corner_B[2].z, 2));
|
||||
dist = sqrt(pow(workpieceCorner.corner_2[0].x - workpieceCorner.corner_2[2].x, 2) +
|
||||
pow(workpieceCorner.corner_2[0].y - workpieceCorner.corner_2[2].y, 2) +
|
||||
pow(workpieceCorner.corner_2[0].z - workpieceCorner.corner_2[2].z, 2));
|
||||
sprintf_s(dataStr, 250, " Len: %g", dist);
|
||||
sw << dataStr << std::endl;
|
||||
|
||||
if (workpieceCorner.workpieceType != 3)
|
||||
{
|
||||
sw << "C:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_3[0].x, workpieceCorner.corner_3[0].y, workpieceCorner.corner_3[0].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_3[1].x, workpieceCorner.corner_3[1].y, workpieceCorner.corner_3[1].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_3[2].x, workpieceCorner.corner_3[2].y, workpieceCorner.corner_3[2].z);
|
||||
sw << dataStr << std::endl;
|
||||
dist = sqrt(pow(workpieceCorner.corner_3[0].x - workpieceCorner.corner_3[2].x, 2) +
|
||||
pow(workpieceCorner.corner_3[0].y - workpieceCorner.corner_3[2].y, 2) +
|
||||
pow(workpieceCorner.corner_3[0].z - workpieceCorner.corner_3[2].z, 2));
|
||||
sprintf_s(dataStr, 250, " Len: %g", dist);
|
||||
sw << dataStr << std::endl;
|
||||
}
|
||||
|
||||
if ((workpieceCorner.workpieceType == 1) || (workpieceCorner.workpieceType == 4))
|
||||
{
|
||||
sw << "D:" << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_0: (%g, %g, %g)", workpieceCorner.corner_4[0].x, workpieceCorner.corner_4[0].y, workpieceCorner.corner_4[0].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_1: (%g, %g, %g)", workpieceCorner.corner_4[1].x, workpieceCorner.corner_4[1].y, workpieceCorner.corner_4[1].z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " corner_2: (%g, %g, %g)", workpieceCorner.corner_4[2].x, workpieceCorner.corner_4[2].y, workpieceCorner.corner_4[2].z);
|
||||
sw << dataStr << std::endl;
|
||||
dist = sqrt(pow(workpieceCorner.corner_4[0].x - workpieceCorner.corner_4[2].x, 2) +
|
||||
pow(workpieceCorner.corner_4[0].y - workpieceCorner.corner_4[2].y, 2) +
|
||||
pow(workpieceCorner.corner_4[0].z - workpieceCorner.corner_4[2].z, 2));
|
||||
sprintf_s(dataStr, 250, " Len: %g", dist);
|
||||
sw << dataStr << std::endl;
|
||||
}
|
||||
sw << std::endl;
|
||||
|
||||
sprintf_s(dataStr, 250, "len_A1 : %g", workpieceCorner.len135_A1);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, "len_A2 : %g", workpieceCorner.len225_A2);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, "len_B2 : %g", workpieceCorner.len315_B2);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, "len_B1 : %g", workpieceCorner.len45_B1);
|
||||
sw << dataStr << std::endl;
|
||||
sw.close();
|
||||
}
|
||||
|
||||
@ -374,14 +413,14 @@ void _outputRGBDScanLapWeld_RGBD(
|
||||
std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
||||
SSX_BQworkpieceResult workpieceCorner,
|
||||
bool outDebugInfo,
|
||||
SSX_debugInfo* debugData)
|
||||
std::vector<SSX_debugInfo>& debugData)
|
||||
{
|
||||
int lineNum = (int)scanLines.size();
|
||||
std::ofstream sw(fileName);
|
||||
int realLines = lineNum;
|
||||
if (workpieceCorner.workpieceType > 0)
|
||||
realLines++;
|
||||
if(debugData)
|
||||
if(debugData.size() > 0)
|
||||
realLines++;
|
||||
sw << "LineNum:" << realLines << std::endl;
|
||||
sw << "DataType: 0" << std::endl;
|
||||
@ -424,26 +463,11 @@ void _outputRGBDScanLapWeld_RGBD(
|
||||
featureType_v &= 0x0f;
|
||||
if (true == outDebugInfo)
|
||||
{
|
||||
if (LINE_FEATURE_L_JUMP_H2L == featureType_v)
|
||||
if (pt3D->nPointIdx < 0)
|
||||
{
|
||||
rgb = { 255, 97, 0 };
|
||||
size = 5;
|
||||
}
|
||||
else if (LINE_FEATURE_L_JUMP_L2H == featureType_v)
|
||||
{
|
||||
rgb = objColor[7];
|
||||
size = 5;
|
||||
}
|
||||
else if (LINE_FEATURE_L_JUMP_H2L == featureType_h)
|
||||
{
|
||||
rgb = objColor[6];
|
||||
size = 5;
|
||||
}
|
||||
else if (LINE_FEATURE_L_JUMP_L2H == featureType_h)
|
||||
{
|
||||
rgb = { 97, 255, 0 };
|
||||
size = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
rgb = { 200, 200, 200 };
|
||||
@ -466,24 +490,29 @@ void _outputRGBDScanLapWeld_RGBD(
|
||||
|
||||
if (workpieceCorner.workpieceType > 0)
|
||||
{
|
||||
int linePtNum = 12;
|
||||
std::vector<SVzNL3DPoint> ptBuffer;
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer.push_back(workpieceCorner.corner_1[i]);
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer.push_back(workpieceCorner.corner_2[i]);
|
||||
if (workpieceCorner.workpieceType != 3)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer.push_back(workpieceCorner.corner_3[i]);
|
||||
}
|
||||
if ((workpieceCorner.workpieceType == 1) || (workpieceCorner.workpieceType == 4))
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer.push_back(workpieceCorner.corner_4[i]);
|
||||
}
|
||||
ptBuffer.push_back(workpieceCorner.center);
|
||||
int linePtNum = (int)ptBuffer.size();
|
||||
sw << "Line_" << lineNum << "_0_" << linePtNum + 1 << std::endl;
|
||||
lineNum++;
|
||||
|
||||
SVzNL3DPoint ptBuffer[12];
|
||||
int idx = 0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer[idx++] = workpieceCorner.corner_L[i];
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer[idx++] = workpieceCorner.corner_R[i];
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer[idx++] = workpieceCorner.corner_T[i];
|
||||
for (int i = 0; i < 3; i++)
|
||||
ptBuffer[idx++] = workpieceCorner.corner_B[i];
|
||||
|
||||
rgb = { 255, 0, 0 };
|
||||
size = 15;
|
||||
for (int j = 0; j < 12; j++)
|
||||
for (int j = 0; j < linePtNum; j++)
|
||||
{
|
||||
float x = (float)ptBuffer[j].x;
|
||||
float y = (float)ptBuffer[j].y;
|
||||
@ -501,85 +530,99 @@ void _outputRGBDScanLapWeld_RGBD(
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
|
||||
if (debugData)
|
||||
if (workpieceCorner.workpieceType > 0)
|
||||
{
|
||||
int linePtNum = debugData[0].edge_size + debugData[0].edgeLink1_size + debugData[0].edgeLink2_size;
|
||||
linePtNum += debugData[1].edge_size + debugData[1].edgeLink1_size + debugData[1].edgeLink2_size;
|
||||
linePtNum += debugData[2].edge_size + debugData[2].edgeLink1_size + debugData[2].edgeLink2_size;
|
||||
linePtNum += debugData[3].edge_size + debugData[3].edgeLink1_size + debugData[3].edgeLink2_size;
|
||||
sw << "Line_" << lineNum << "_0_" << linePtNum + 1 << std::endl;
|
||||
lineNum++;
|
||||
std::vector< SWD_3DPointPair> lines;
|
||||
SWD_3DPointPair a_line;
|
||||
a_line.pt1 = workpieceCorner.corner_1[0];
|
||||
a_line.pt2 = workpieceCorner.corner_1[2];
|
||||
lines.push_back(a_line);
|
||||
a_line.pt1 = workpieceCorner.corner_1[1];
|
||||
a_line.pt2 = workpieceCorner.center;
|
||||
lines.push_back(a_line);
|
||||
|
||||
rgb = { 255, 0, 0 };
|
||||
size = 3;
|
||||
for (int i = 0; i < 4; i++)
|
||||
a_line.pt1 = workpieceCorner.corner_2[0];
|
||||
a_line.pt2 = workpieceCorner.corner_2[2];
|
||||
lines.push_back(a_line);
|
||||
a_line.pt1 = workpieceCorner.corner_2[1];
|
||||
a_line.pt2 = workpieceCorner.center;
|
||||
lines.push_back(a_line);
|
||||
|
||||
if (workpieceCorner.workpieceType != 3)
|
||||
{
|
||||
for (int j = 0; j < debugData[i].edge_size; j++)
|
||||
{
|
||||
float x = (float)debugData[i].edge[j].x;
|
||||
float y = (float)debugData[i].edge[j].y;
|
||||
float z = (float)debugData[i].edge[j].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
for (int j = 0; j < debugData[i].edgeLink1_size; j++)
|
||||
{
|
||||
float x = (float)debugData[i].edgeLink_1[j].x;
|
||||
float y = (float)debugData[i].edgeLink_1[j].y;
|
||||
float z = (float)debugData[i].edgeLink_1[j].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
for (int j = 0; j < debugData[i].edgeLink2_size; j++)
|
||||
{
|
||||
float x = (float)debugData[i].edgeLink_2[j].x;
|
||||
float y = (float)debugData[i].edgeLink_2[j].y;
|
||||
float z = (float)debugData[i].edgeLink_2[j].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
a_line.pt1 = workpieceCorner.corner_3[0];
|
||||
a_line.pt2 = workpieceCorner.corner_3[2];
|
||||
lines.push_back(a_line);
|
||||
a_line.pt1 = workpieceCorner.corner_3[1];
|
||||
a_line.pt2 = workpieceCorner.center;
|
||||
lines.push_back(a_line);
|
||||
}
|
||||
if ((workpieceCorner.workpieceType == 1) || (workpieceCorner.workpieceType == 4))
|
||||
{
|
||||
a_line.pt1 = workpieceCorner.corner_4[0];
|
||||
a_line.pt2 = workpieceCorner.corner_4[2];
|
||||
lines.push_back(a_line);
|
||||
a_line.pt1 = workpieceCorner.corner_4[1];
|
||||
a_line.pt2 = workpieceCorner.center;
|
||||
lines.push_back(a_line);
|
||||
}
|
||||
//加一个点,用于跳过显示工具bug
|
||||
float x = (float)debugData[0].edge[0].x;
|
||||
float y = (float)debugData[0].edge[0].y;
|
||||
float z = (float)debugData[0].edge[0].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
|
||||
if (debugData.size() > 0)
|
||||
{
|
||||
int linePtNum = 0;
|
||||
for (int i = 0; i < (int)debugData.size(); i++)
|
||||
linePtNum += debugData[i].edge_size + debugData[i].edgeLink1_size + debugData[i].edgeLink2_size;
|
||||
sw << "Line_" << lineNum << "_0_" << linePtNum + 1 << std::endl;
|
||||
lineNum++;
|
||||
|
||||
rgb = { 255, 0, 0 };
|
||||
size = 3;
|
||||
for (int i = 0; i < (int)debugData.size(); i++)
|
||||
{
|
||||
for (int j = 0; j < debugData[i].edge_size; j++)
|
||||
{
|
||||
float x = (float)debugData[i].edge[j].x;
|
||||
float y = (float)debugData[i].edge[j].y;
|
||||
float z = (float)debugData[i].edge[j].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
for (int j = 0; j < debugData[i].edgeLink1_size; j++)
|
||||
{
|
||||
float x = (float)debugData[i].edgeLink_1[j].x;
|
||||
float y = (float)debugData[i].edgeLink_1[j].y;
|
||||
float z = (float)debugData[i].edgeLink_1[j].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
for (int j = 0; j < debugData[i].edgeLink2_size; j++)
|
||||
{
|
||||
float x = (float)debugData[i].edgeLink_2[j].x;
|
||||
float y = (float)debugData[i].edgeLink_2[j].y;
|
||||
float z = (float)debugData[i].edgeLink_2[j].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
}
|
||||
//加一个点,用于跳过显示工具bug
|
||||
float x = (float)debugData[0].edge[0].x;
|
||||
float y = (float)debugData[0].edge[0].y;
|
||||
float z = (float)debugData[0].edge[0].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
//显示拟合直线
|
||||
rgb = { 255, 0, 0 };
|
||||
size = 3;
|
||||
int lineIdx = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < (int)lines.size(); i++)
|
||||
{
|
||||
SVzNL3DPoint pt0 = debugData[i].edge_ends[0];
|
||||
SVzNL3DPoint pt1 = debugData[i].edge_ends[1];
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << pt1.x << "," << pt1.y << "," << pt1.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
|
||||
pt0 = debugData[i].edge_link1_ends[0];
|
||||
pt1 = debugData[i].edge_link1_ends[1];
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << pt1.x << "," << pt1.y << "," << pt1.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
|
||||
pt0 = debugData[i].edge_link2_ends[0];
|
||||
pt1 = debugData[i].edge_link2_ends[1];
|
||||
SVzNL3DPoint pt0 = lines[i].pt1;
|
||||
SVzNL3DPoint pt1 = lines[i].pt2;
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
@ -590,8 +633,8 @@ void _outputRGBDScanLapWeld_RGBD(
|
||||
lineIdx++;
|
||||
}
|
||||
//加一个直线,用于跳过显示工具bug
|
||||
SVzNL3DPoint pt0 = debugData[0].edge_ends[0];
|
||||
SVzNL3DPoint pt1 = debugData[0].edge_ends[1];
|
||||
SVzNL3DPoint pt0 = lines[0].pt1;
|
||||
SVzNL3DPoint pt1 = lines[0].pt2;
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
@ -606,15 +649,21 @@ void _outputRGBDScanLapWeld_RGBD(
|
||||
#define CONVERT_TO_GRID 0
|
||||
#define TEST_COMPUTE_CALIB_PARA 0
|
||||
#define TEST_COMPUTE_CORNER 1
|
||||
#define TEST_GROUP 1
|
||||
#define TEST_GROUP 7
|
||||
int main()
|
||||
{
|
||||
const char* dataPath[TEST_GROUP] = {
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\" //0
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\", //0
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\工件点云\\角度1\\", //1
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\工件点云\\角度2\\", //2
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\工件点云\\角度3\\", //3
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\工件点云\\角度4\\", //4
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\工件点云\\", //5
|
||||
"F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\顶板样式\\", //6
|
||||
};
|
||||
|
||||
SVzNLRange fileIdx[TEST_GROUP] = {
|
||||
{1,3}
|
||||
{1,3}, {6,8}, {6,8}, {6,8}, {6,8}, {9,11}, {2,4}
|
||||
};
|
||||
|
||||
#if CONVERT_TO_GRID
|
||||
@ -641,7 +690,7 @@ int main()
|
||||
|
||||
#if TEST_COMPUTE_CALIB_PARA
|
||||
char _calib_datafile[256];
|
||||
sprintf_s(_calib_datafile, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\scanData_1.txt");
|
||||
sprintf_s(_calib_datafile, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\顶板样式\\节点4.txt");
|
||||
int lineNum = 0;
|
||||
float lineV = 0.0f;
|
||||
int dataCalib = 0;
|
||||
@ -677,10 +726,10 @@ int main()
|
||||
}
|
||||
//
|
||||
char calibFile[250];
|
||||
sprintf_s(calibFile, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\ground_calib_para.txt");
|
||||
sprintf_s(calibFile, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\顶板样式\\ground_calib_para.txt");
|
||||
_outputCalibPara(calibFile, calibPara);
|
||||
char _out_file[256];
|
||||
sprintf_s(_out_file, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\scanData_ground_1_calib.txt");
|
||||
sprintf_s(_out_file, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\顶板样式\\scanData_ground_1_calib.txt");
|
||||
int headNullLines = 0;
|
||||
_outputScanDataFile_vector(_out_file, scanData, false, &headNullLines);
|
||||
printf("%s: calib done!\n", _calib_datafile);
|
||||
@ -688,7 +737,10 @@ int main()
|
||||
#endif
|
||||
|
||||
#if TEST_COMPUTE_CORNER
|
||||
for (int grp = 0; grp <= 0; grp++)
|
||||
const char* ver = wd_BQWorkpieceCornerVersion();
|
||||
printf("ver:%s\n", ver);
|
||||
|
||||
for (int grp = 5; grp <= 6; grp++)
|
||||
{
|
||||
SSG_planeCalibPara poseCalibPara;
|
||||
//初始化成单位阵
|
||||
@ -705,20 +757,26 @@ int main()
|
||||
for (int i = 0; i < 9; i++)
|
||||
poseCalibPara.invRMatrix[i] = poseCalibPara.planeCalib[i];
|
||||
char calibFile[250];
|
||||
if (grp == 0)
|
||||
{
|
||||
sprintf_s(calibFile, "F:\\ShangGu\\项目\\冠钦_博清科技\\数据\\ground_calib_para.txt");
|
||||
poseCalibPara = _readCalibPara(calibFile);
|
||||
}
|
||||
sprintf_s(calibFile, "%sground_calib_para.txt", dataPath[grp]);
|
||||
poseCalibPara = _readCalibPara(calibFile);
|
||||
|
||||
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
|
||||
{
|
||||
//fidx =1;
|
||||
//fidx =4;
|
||||
char _scan_file[256];
|
||||
sprintf_s(_scan_file, "%sscanData_%d_grid.txt", dataPath[grp], fidx);
|
||||
if(0 == grp)
|
||||
sprintf_s(_scan_file, "%sscanData_%d_grid.txt", dataPath[grp], fidx);
|
||||
else if(6 == grp)
|
||||
sprintf_s(_scan_file, "%s节点%d.txt", dataPath[grp], fidx);
|
||||
else
|
||||
sprintf_s(_scan_file, "%s%d_LazerData_Hi229229.txt", dataPath[grp], fidx);
|
||||
std::vector<std::vector< SVzNL3DPosition>> scanLines;
|
||||
vzReadLaserScanPointFromFile_XYZ_vector(_scan_file, scanLines);
|
||||
|
||||
//转成plyTxt格式
|
||||
//sprintf_s(_scan_file, "%s%d_ply_Hi229229.txt", dataPath[grp], fidx);
|
||||
//wdSavePlyTxt(_scan_file, scanLines);
|
||||
|
||||
long t1 = (long)GetTickCount64();//统计时间
|
||||
|
||||
for (int i = 0, i_max = (int)scanLines.size(); i < i_max; i++)
|
||||
@ -727,7 +785,15 @@ int main()
|
||||
int kkk = 1;
|
||||
//行处理
|
||||
//调平,去除地面
|
||||
sx_BQ_lineDataR(scanLines[i], poseCalibPara.planeCalib, -1);
|
||||
double cuttingZ = -1;
|
||||
if (6 == grp)
|
||||
{
|
||||
if (2 == fidx)
|
||||
cuttingZ = 1760.0;
|
||||
else if(3 == fidx)
|
||||
cuttingZ = 1780.0;
|
||||
}
|
||||
sx_BQ_lineDataR(scanLines[i], poseCalibPara.planeCalib, cuttingZ);
|
||||
}
|
||||
#if 0
|
||||
char _out_file[256];
|
||||
@ -754,9 +820,9 @@ int main()
|
||||
growParam.minLTypeTreeLen = 100; //mm
|
||||
growParam.minVTypeTreeLen = 100; //mm
|
||||
SSX_BQworkpiecePara workpieceParam;
|
||||
workpieceParam.lineLen = 180.0; //直线段长度
|
||||
workpieceParam.lineLen = 160.0; //直线段长度
|
||||
int errCode = 0;
|
||||
SSX_debugInfo debug_conturs[4];
|
||||
std::vector<SSX_debugInfo> debug_conturs;
|
||||
SSX_BQworkpieceResult workpieceCorner = sx_BQ_getWorkpieceCorners(
|
||||
scanLines,
|
||||
cornerParam,
|
||||
@ -772,11 +838,7 @@ int main()
|
||||
printf("%s: %d(ms)!\n", _scan_file, (int)(t2 - t1));
|
||||
//输出测试结果
|
||||
sprintf_s(_scan_file, "%sresult\\LaserLine%d_result.txt", dataPath[grp], fidx);
|
||||
#if _OUTPUT_DEBUG_DATA
|
||||
_outputRGBDScanLapWeld_RGBD(_scan_file, scanLines, workpieceCorner, false, debug_conturs);
|
||||
#else
|
||||
_outputRGBDScanLapWeld_RGBD(_scan_file, scanLines, workpieceCorner, true, NULL);
|
||||
#endif
|
||||
sprintf_s(calibFile, "%sresult\\LaserLine%d_corner_info.txt", dataPath[grp], fidx);
|
||||
_outputCornerInfo(calibFile, workpieceCorner);
|
||||
}
|
||||
|
||||
@ -1,21 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
# define Q_DECL_EXPORT __declspec(dllexport)
|
||||
# define Q_DECL_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
# define Q_DECL_EXPORT __attribute__((visibility("default")))
|
||||
# define Q_DECL_IMPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#if defined(SG_API_LIBRARY)
|
||||
# define SG_WORKPIECESHARED_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define SG_WORKPIECESHARED_EXPORT Q_DECL_IMPORT
|
||||
|
||||
#endif
|
||||
|
||||
#include "SG_baseDataType.h"
|
||||
#include "SG_algo_Export.h"
|
||||
#include <vector>
|
||||
|
||||
#define _OUTPUT_DEBUG_DATA 1
|
||||
@ -27,44 +12,47 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int workpieceType;
|
||||
SVzNL3DPoint corner_L[3];
|
||||
SVzNL3DPoint corner_R[3];
|
||||
SVzNL3DPoint corner_T[3];
|
||||
SVzNL3DPoint corner_B[3];
|
||||
int workpieceType; //1-节点1; 2-节点2; 3-节点3;4-节点4;0-未知; 其它-非法
|
||||
//1,2,3,4为逆时针
|
||||
SVzNL3DPoint corner_1[3];
|
||||
SVzNL3DPoint corner_2[3];
|
||||
SVzNL3DPoint corner_3[3];
|
||||
SVzNL3DPoint corner_4[3];
|
||||
SVzNL3DPoint center; //工件中心点
|
||||
double len135_A1; //A1长度,从工件中心135度方向
|
||||
double len45_B1; //B1长度,从工件中心45度方向
|
||||
double len315_B2; //B2长度,从工件中心315度方向
|
||||
double len225_A2; //A2长度,从工件中心225度方向
|
||||
}SSX_BQworkpieceResult;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int rgnIdx;
|
||||
SVzNL3DPoint* edge;
|
||||
int edge_size;
|
||||
SVzNL3DPoint edge_ends[2];
|
||||
SVzNL3DPoint* edgeLink_1;
|
||||
int edgeLink1_size;
|
||||
SVzNL3DPoint edge_link1_ends[2];
|
||||
SVzNL3DPoint* edgeLink_2;
|
||||
int edgeLink2_size;
|
||||
SVzNL3DPoint edge_link2_ends[2];
|
||||
SVzNL3DPoint* edge;
|
||||
SVzNL3DPoint* edgeLink_1;
|
||||
SVzNL3DPoint* edgeLink_2;
|
||||
}SSX_debugInfo;
|
||||
|
||||
//读版本号
|
||||
SG_WORKPIECESHARED_EXPORT const char* wd_BQWorkpieceCornerVersion(void);
|
||||
SG_APISHARED_EXPORT const char* wd_BQWorkpieceCornerVersion(void);
|
||||
|
||||
//计算一个平面调平参数。
|
||||
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
|
||||
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
|
||||
SG_WORKPIECESHARED_EXPORT SSG_planeCalibPara sx_BQ_getBaseCalibPara(
|
||||
SG_APISHARED_EXPORT SSG_planeCalibPara sx_BQ_getBaseCalibPara(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
|
||||
|
||||
//相机姿态调平,并去除地面
|
||||
SG_WORKPIECESHARED_EXPORT void sx_BQ_lineDataR(
|
||||
SG_APISHARED_EXPORT void sx_BQ_lineDataR(
|
||||
std::vector< SVzNL3DPosition>& a_line,
|
||||
const double* camPoseR,
|
||||
double groundH);
|
||||
|
||||
//ÌáÈ¡´î½Óº¸·ì
|
||||
SG_WORKPIECESHARED_EXPORT SSX_BQworkpieceResult sx_BQ_getWorkpieceCorners(
|
||||
//提取工件角点及定位长度信息
|
||||
SG_APISHARED_EXPORT SSX_BQworkpieceResult sx_BQ_getWorkpieceCorners(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
|
||||
const SSG_cornerParam cornerPara,
|
||||
const SSG_outlierFilterParam filterParam,
|
||||
@ -72,6 +60,6 @@ SG_WORKPIECESHARED_EXPORT SSX_BQworkpieceResult sx_BQ_getWorkpieceCorners(
|
||||
SSG_planeCalibPara groundCalibPara,
|
||||
SSX_BQworkpiecePara workpieceParam,
|
||||
#if _OUTPUT_DEBUG_DATA
|
||||
SSX_debugInfo* debug_conturs,
|
||||
std::vector<SSX_debugInfo>& debug_contours,
|
||||
#endif
|
||||
int* errCode);
|
||||
|
||||
22
SDK/workpieceCornerExtraction/Inc/SG_algo_Export.h
Normal file
22
SDK/workpieceCornerExtraction/Inc/SG_algo_Export.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
# define Q_DECL_EXPORT __declspec(dllexport)
|
||||
# define Q_DECL_IMPORT __declspec(dllimport)
|
||||
#else
|
||||
# define Q_DECL_EXPORT __attribute__((visibility("default")))
|
||||
# define Q_DECL_IMPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#if defined(SG_API_LIBRARY)
|
||||
# define SG_APISHARED_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define SG_APISHARED_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#include "SG_baseDataType.h"
|
||||
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846 // pi
|
||||
#endif // !M_PI
|
||||
@ -177,6 +177,15 @@ typedef struct
|
||||
double valleyMaxW;
|
||||
}SSG_VFeatureParam;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int lineIdx;
|
||||
int startPtIdx;
|
||||
int endPtIdx;
|
||||
SVzNL3DPoint startPt;
|
||||
SVzNL3DPoint endPt;
|
||||
}SWD_segFeature;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double sameGapTh;
|
||||
@ -251,6 +260,15 @@ typedef struct
|
||||
std::vector< SSG_featureSemiCircle> treeNodes;
|
||||
}SSG_semiCircleFeatureTree;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int treeState;
|
||||
int sLineIdx;
|
||||
int eLineIdx;
|
||||
double tree_value;
|
||||
std::vector< SWD_segFeature> treeNodes;
|
||||
}SWD_segFeatureTree;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int vTreeFlag;
|
||||
@ -347,10 +365,16 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
SVzNL3DPoint opCenter; //定子中心位置
|
||||
int neighbourNum; //相邻目标数量
|
||||
double opAngle; //定子外部相隔120度操作点的操作角度。以Y形为基准角度(0度)
|
||||
double obstacleDist; //距离周边障碍的距离
|
||||
}SSG_motorStatorPosition;
|
||||
double objR;
|
||||
}SWD_motorStatorPosition;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int objID;
|
||||
double grasperAngle;
|
||||
double rotateAngle; //抓手在0度位置时为丫字形,间隔120度。
|
||||
double graspR;
|
||||
}SWD_statorOuterGrasper;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -447,3 +471,16 @@ typedef struct {
|
||||
std::vector<std::vector<int>> gray; // 灰度图
|
||||
std::vector<std::vector<int>> markers; // 标记图(-1表示分水岭,0表示未标记,>0表示区域)
|
||||
}SWD_waterShedImage;
|
||||
|
||||
//查科二维码Mark
|
||||
typedef struct
|
||||
{
|
||||
int markID;
|
||||
SVzNL3DPoint mark3D;
|
||||
}SWD_charuco3DMark;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SVzNL3DPoint pt1;
|
||||
SVzNL3DPoint pt2;
|
||||
}SWD_3DPointPair;
|
||||
@ -6,6 +6,7 @@
|
||||
#define SG_ERR_NOT_GRID_FORMAT -1003
|
||||
#define SG_ERR_LABEL_INFO_ERROR -1004
|
||||
#define SG_ERR_INVLD_SORTING_MODE -1005
|
||||
#define SG_ERR_INVLD_Q_SCALE -1006
|
||||
|
||||
//BQ_workpiece
|
||||
#define SX_ERR_INVLD_VTREE_NUM -2001
|
||||
@ -14,4 +15,8 @@
|
||||
#define SX_ERR_INVLD_CLOSES_PT -2004
|
||||
#define SX_ERR_ZERO_CONTOUR_PT -2005
|
||||
#define SX_ERR_INVLID_RPEAK_NUM -2006
|
||||
#define SX_ERR_INVLID_RPEAK_PAIR -2007
|
||||
#define SX_ERR_INVLID_RPEAK_PAIR -2007
|
||||
|
||||
//¶¨×Óץȡ
|
||||
#define SX_ERR_INVLID_CUTTING_Z -2101
|
||||
#define SX_ERR_ZERO_OBJ -2102
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user