673 lines
20 KiB
Markdown
673 lines
20 KiB
Markdown
# 工件角点检测功能 - 完整实现代码
|
||
|
||
## 已完成的工作 ✅
|
||
|
||
### 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通信完善。参考本文档中的代码示例即可快速完成剩余实现。
|