轮胎检测增加TCP协议

This commit is contained in:
yiyi 2026-03-14 10:00:15 +08:00
parent 5dc4feab00
commit 1f8cf970be
31 changed files with 6894 additions and 5956 deletions

View File

@ -0,0 +1,131 @@
# 轮眉测量 TCP/IP 通信协议
## 概述
- **协议类型**: TCP/IP 文本协议
- **默认端口**: 5000可在配置文件中修改
- **数据格式**: 纯文本UTF-8编码
- **通信模式**: 客户端-服务器模式(视觉系统作为服务器)
## 协议格式
### 1. 触发检测命令(客户端 → 服务器)
```
start,100
```
**格式说明**
- `start`: 命令关键字
- `100`: 参数(当前未使用,保留用于扩展)
**示例**
```
start,100
```
### 2. 检测结果响应(服务器 → 客户端)
#### 成功情况(所有相机检测成功)
```
1,100,200;2,100,200;3,100,200;4,100,200
```
**格式说明**
- 多个相机结果用分号 `;` 分隔
- 每个相机结果格式:`相机ID,中心点到地面距离,轮眉到地面距离`
- `相机ID`: 1-4对应4个相机
- `中心点到地面距离`: 整数单位mm
- `轮眉到地面距离`: 整数单位mm
**示例**
```
1,150,250;2,148,252;3,151,249;4,149,251
```
表示:
- 相机1中心距离150mm轮眉距离250mm
- 相机2中心距离148mm轮眉距离252mm
- 相机3中心距离151mm轮眉距离249mm
- 相机4中心距离149mm轮眉距离251mm
#### 失败情况(部分相机检测失败)
```
1,400;2,100,200;3,100,200;4,100,200
```
**错误码说明**
- `400`: 扫描/匹配失败
- `401`: 工件为空
**格式说明**
- 失败的相机只返回:`相机ID,错误码`
- 成功的相机返回:`相机ID,中心距离,轮眉距离`
**示例**
```
1,400;2,150,250;3,401;4,149,251
```
表示:
- 相机1扫描失败错误码400
- 相机2成功中心距离150mm轮眉距离250mm
- 相机3工件为空错误码401
- 相机4成功中心距离149mm轮眉距离251mm
## 通信流程
```
客户端 服务器(视觉系统)
| |
|-------- start,100 ------------------------>|
| |
| | 触发所有相机顺序检测
| | 相机1 → 相机2 → 相机3 → 相机4
| |
|<------- 1,150,250;2,148,252;... -----------|
| |
```
**时序说明**
1. 客户端发送 `start,100` 命令
2. 服务器收到命令后,依次触发所有启用的相机进行检测
3. 所有相机检测完成后,服务器发送汇总结果
4. 客户端接收结果并处理
## 错误处理
### 连接错误
- 如果TCP连接断开服务器会清除当前客户端的请求状态
- 客户端需要重新连接并发送命令
### 超时处理
- 建议客户端设置接收超时推荐60秒
- 如果超时未收到响应,可以重新发送命令
### 数据解析
- 客户端应验证返回数据格式
- 检查相机ID是否在1-4范围内
- 检查是否包含错误码400/401
## 配置说明
TCP服务器端口可在配置文件中修改
```xml
<ServerList>
<Server>
<Name>TCPServer</Name>
<IP>0.0.0.0</IP>
<Port>5000</Port>
</Server>
</ServerList>
```
## 注意事项
1. **数据单位**所有距离值单位为毫米mm
2. **相机顺序**相机ID固定为1-4对应配置文件中的相机顺序
3. **检测时间**完整检测所有相机通常需要30-50秒请设置合理的超时时间
4. **并发限制**:服务器同时只处理一个检测请求,多个客户端连接时按先后顺序处理
5. **数据精度**:返回的距离值已四舍五入为整数

View File

@ -73,8 +73,11 @@ int WheelMeasurePresenter::InitApp()
}
}
// 初始化TCP服务器
int tcpPort = 6800; // 默认端口
// 初始化TCP服务器从配置读取端口
int tcpPort = 5000; // 默认端口
if (!m_configResult.servers.empty()) {
tcpPort = m_configResult.servers[0].port;
}
int tcpResult = m_tcpProtocol.Initialize(tcpPort);
if (tcpResult != 0) {
LOG_ERROR("Failed to initialize TCP server on port %d, error code: %d\n", tcpPort, tcpResult);
@ -306,6 +309,9 @@ void WheelMeasurePresenter::StartAllDetection()
LOG_INFO("Sequential detection started, total cameras: %d\n", m_sequentialTotalCount);
m_statusUpdate->OnStatusUpdate(QString("开始所有设备的检测"));
// 开始检测第一个设备
continueSequentialDetection();
}
@ -517,7 +523,7 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
SSG_lineSegParam lineSegParam;
lineSegParam.segGapTh_y = m_configResult.algorithmParams.lineSegParam.segGapTh_y;
lineSegParam.segGapTh_z = m_configResult.algorithmParams.lineSegParam.segGapTh_z;
lineSegParam.maxDist = m_configResult.algorithmParams.lineSegParam.maxDist;
lineSegParam.distScale = m_configResult.algorithmParams.lineSegParam.maxDist;
SSG_outlierFilterParam filterParam;
filterParam.continuityTh = m_configResult.algorithmParams.filterParam.continuityTh;
@ -531,7 +537,79 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
growParam.minLTypeTreeLen = m_configResult.algorithmParams.growParam.minLTypeTreeLen;
growParam.minVTypeTreeLen = m_configResult.algorithmParams.growParam.minVTypeTreeLen;
// 3. 准备调平参数(使用当前相机的调平参数)
// 3. 查找当前相机的调平参数用于后续的ROI检测和调平处理
WheelCameraPlaneCalibParam* calibParam = getPlaneCalibParam(m_currentCameraIndex);
// 3.5. 调用轮胎存在检测使用ROI范围过滤- 在调平处理之前
SVzNL3DRangeD wheelRoi3d;
if (calibParam) {
// 使用当前相机配置的ROI范围
wheelRoi3d.xRange.min = calibParam->wheelRoi3d_xMin;
wheelRoi3d.xRange.max = calibParam->wheelRoi3d_xMax;
wheelRoi3d.yRange.min = calibParam->wheelRoi3d_yMin;
wheelRoi3d.yRange.max = calibParam->wheelRoi3d_yMax;
wheelRoi3d.zRange.min = calibParam->wheelRoi3d_zMin;
wheelRoi3d.zRange.max = calibParam->wheelRoi3d_zMax;
LOG_INFO("Using ROI range for wheel presence detection:\n");
LOG_INFO(" X: [%.1f, %.1f], Y: [%.1f, %.1f], Z: [%.1f, %.1f]\n",
wheelRoi3d.xRange.min, wheelRoi3d.xRange.max,
wheelRoi3d.yRange.min, wheelRoi3d.yRange.max,
wheelRoi3d.zRange.min, wheelRoi3d.zRange.max);
} else {
// 使用默认ROI范围
wheelRoi3d.xRange.min = -1000.0;
wheelRoi3d.xRange.max = 1000.0;
wheelRoi3d.yRange.min = -1000.0;
wheelRoi3d.yRange.max = 1000.0;
wheelRoi3d.zRange.min = -1000.0;
wheelRoi3d.zRange.max = 1000.0;
LOG_INFO("Using default ROI range for wheel presence detection: ±1000.0 mm\n");
}
// 调用轮胎存在检测
bool wheelPresent = wd_wheelPresenseDetection(scanLines, wheelRoi3d);
LOG_INFO("Wheel presence detection result: %s\n", wheelPresent ? "PRESENT" : "NOT PRESENT");
if (!wheelPresent) {
LOG_WARNING("No wheel detected in ROI range, skipping measurement\n");
if (m_statusUpdate) {
m_statusUpdate->OnStatusUpdate(QString("未检测到轮胎请检查ROI范围配置"));
}
// 如果是TCP触发的检测缓存401错误结果
if (m_tcpDetectionMode) {
WheelMeasureTCPProtocol::CameraMeasureResult tcpResult;
tcpResult.cameraId = m_currentCameraIndex;
tcpResult.errorCode = 401; // 工件为空
tcpResult.centerDistance = 0.0;
tcpResult.archDistance = 0.0;
m_tcpResults[m_currentCameraIndex] = tcpResult;
LOG_INFO("TCP检测结果已缓存: 相机%d, 错误码=401 (未检测到轮胎)\n", m_currentCameraIndex);
}
// 如果正在进行顺序检测,继续检测下一个设备
if (m_sequentialDetecting) {
m_sequentialCurrentIndex++;
// TCP模式下检查是否所有相机都检测完成
if (m_tcpDetectionMode && m_tcpResults.size() >= m_sequentialTotalCount) {
LOG_INFO("TCP模式所有相机检测完成准备发送结果\n");
m_sequentialDetecting = false;
sendTCPMeasureResults();
} else {
// 继续检测下一个设备
QMetaObject::invokeMethod(this, [this]() {
continueSequentialDetection();
}, Qt::QueuedConnection);
}
}
return;
}
// 4. 准备调平参数并执行调平处理(使用当前相机的调平参数)
SSG_planeCalibPara groundCalibPara;
memset(&groundCalibPara, 0, sizeof(groundCalibPara));
@ -544,8 +622,6 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
groundCalibPara.invRMatrix[8] = 1.0;
groundCalibPara.planeHeight = 0.0;
// 查找当前相机的调平参数
WheelCameraPlaneCalibParam* calibParam = getPlaneCalibParam(m_currentCameraIndex);
if (calibParam && calibParam->isCalibrated) {
for (int i = 0; i < 9; ++i) {
groundCalibPara.planeCalib[i] = calibParam->planeCalib[i];
@ -568,8 +644,7 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
LOG_WARN("No calibration data for camera %d, using default parameters\n", m_currentCameraIndex);
}
// 4. 调用算法
// 5. 调用算法
int errCode = 0;
LOG_INFO("Calling wd_wheelArchHeigthMeasure...\n");
@ -756,13 +831,19 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
// 如果正在进行顺序检测,继续检测下一个设备
if (m_sequentialDetecting) {
m_sequentialCurrentIndex++;
// TCP模式下检查是否所有相机都检测完成
if (m_tcpDetectionMode && m_tcpResults.size() >= m_sequentialTotalCount) {
LOG_INFO("TCP模式所有相机检测完成准备发送结果\n");
// 先结束顺序检测
m_sequentialDetecting = false;
// 发送TCP结果
sendTCPMeasureResults();
} else {
// 继续检测下一个设备
QMetaObject::invokeMethod(this, [this]() {
continueSequentialDetection();
}, Qt::QueuedConnection);
} else if (m_tcpDetectionMode) {
// TCP模式下检查是否所有相机都检测完成
if (m_tcpResults.size() >= m_sequentialTotalCount) {
sendTCPMeasureResults();
}
}
}

View File

@ -3,7 +3,7 @@
#define WHEELMEASURE_VERSION_STRING "1.0.1"
#define WHEELMEASURE_BUILD_STRING "5"
#define WHEELMEASURE_BUILD_STRING "6"
#define WHEELMEASURE_FULL_VERSION_STRING "V" WHEELMEASURE_VERSION_STRING "_" WHEELMEASURE_BUILD_STRING
// 获取版本信息的便捷函数

View File

@ -27,6 +27,7 @@ INCLUDEPATH += ../../../Utils/VrCommon/Inc
INCLUDEPATH += ../../../AppUtils/UICommon/Inc
INCLUDEPATH += ../../../Utils/CloudUtils/Inc
INCLUDEPATH += ../../../AppUtils/AppCommon/Inc
INCLUDEPATH += ../../../Module/AuthModule/Inc
INCLUDEPATH += ../../../SDK/Device/VzNLSDK/Inc
INCLUDEPATH += ../../../AppAlgo/wheelArchHeigthMeasure/Inc
@ -40,6 +41,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../AppUtils/UICommon/debug -lUICommon
LIBS += -L../../../Utils/CloudUtils/debug -lCloudUtils
LIBS += -L../../../AppUtils/AppCommon/debug -lAppCommon
LIBS += -L../../../Module/AuthModule/debug -lAuthModule
LIBS += -L../../../Module/ModbusTCPServer/debug -lModbusTCPServer
LIBS += -L../../../VrNets/debug -lVrModbus
} else:win32:CONFIG(release, debug|release) {
@ -50,6 +52,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../../../AppUtils/UICommon/release -lUICommon
LIBS += -L../../../Utils/CloudUtils/release -lCloudUtils
LIBS += -L../../../AppUtils/AppCommon/release -lAppCommon
LIBS += -L../../../Module/AuthModule/release -lAuthModule
LIBS += -L../../../Module/ModbusTCPServer/release -lModbusTCPServer
LIBS += -L../../../VrNets/release -lVrModbus
}else:unix:!macx {
@ -57,6 +60,7 @@ win32:CONFIG(debug, debug|release) {
LIBS += -L../WheelMeasureConfig -lWheelMeasureConfig
LIBS += -L../../../AppUtils/AppCommon -lAppCommon
LIBS += -L../../../AppUtils/UICommon -lUICommon
LIBS += -L../../../Module/AuthModule -lAuthModule
LIBS += -L../../../Utils/CloudUtils -lCloudUtils
LIBS += -L../../../Device/VrEyeDevice -lVrEyeDevice
LIBS += -L../../../VrNets -lVrTcpClient -lVrTcpServer

View File

@ -71,6 +71,7 @@ void DialogCameraLevel::setConfig(IVrWheelMeasureConfig* config, WheelMeasureCon
// 修复:打开页面时配置可能在相机列表之后设置,导致初始加载失败
if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast<int>(m_cameraList.size())) {
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
loadCameraRoiRange(m_currentCameraIndex);
}
}
@ -614,6 +615,7 @@ void DialogCameraLevel::on_combo_camera_currentIndexChanged(int index)
QString::fromStdString(m_cameraList[index].first).toUtf8().constData());
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
loadCameraRoiRange(m_currentCameraIndex);
} else {
LOG_WARNING("Invalid camera index selected: %d\n", index);
ui->label_level_result->setText("无效的相机选择");
@ -703,3 +705,129 @@ void DialogCameraLevel::on_btn_save_compensation_clicked()
LOG_ERROR("Failed to save error compensation\n");
}
}
void DialogCameraLevel::loadCameraRoiRange(int cameraIndex)
{
if (!m_pConfig || !m_pConfigResult) {
LOG_WARNING("Config is null, cannot load ROI range\n");
return;
}
if (cameraIndex < 0 || cameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index: %d\n", cameraIndex);
return;
}
int configCameraIndex = cameraIndex + 1; // 转换为1-based索引
// 查找相机的调平参数
for (const auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == configCameraIndex) {
// 加载ROI范围到UI
ui->edit_roi_x_min->setText(QString::number(param.wheelRoi3d_xMin, 'f', 1));
ui->edit_roi_x_max->setText(QString::number(param.wheelRoi3d_xMax, 'f', 1));
ui->edit_roi_y_min->setText(QString::number(param.wheelRoi3d_yMin, 'f', 1));
ui->edit_roi_y_max->setText(QString::number(param.wheelRoi3d_yMax, 'f', 1));
ui->edit_roi_z_min->setText(QString::number(param.wheelRoi3d_zMin, 'f', 1));
ui->edit_roi_z_max->setText(QString::number(param.wheelRoi3d_zMax, 'f', 1));
LOG_INFO("Loaded ROI range for camera %d: X[%.1f, %.1f], Y[%.1f, %.1f], Z[%.1f, %.1f]\n",
configCameraIndex,
param.wheelRoi3d_xMin, param.wheelRoi3d_xMax,
param.wheelRoi3d_yMin, param.wheelRoi3d_yMax,
param.wheelRoi3d_zMin, param.wheelRoi3d_zMax);
return;
}
}
// 如果没有找到,使用默认值
ui->edit_roi_x_min->setText("-1000.0");
ui->edit_roi_x_max->setText("1000.0");
ui->edit_roi_y_min->setText("-1000.0");
ui->edit_roi_y_max->setText("1000.0");
ui->edit_roi_z_min->setText("-1000.0");
ui->edit_roi_z_max->setText("1000.0");
LOG_INFO("No ROI range found for camera %d, using default values\n", configCameraIndex);
}
void DialogCameraLevel::saveCameraRoiRange()
{
if (!m_pConfig || !m_pConfigResult) {
LOG_ERROR("Config is null, cannot save ROI range\n");
QMessageBox::warning(this, "错误", "配置对象为空无法保存ROI范围");
return;
}
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex);
QMessageBox::warning(this, "错误", "请先选择相机");
return;
}
// 获取UI中的ROI范围值
double xMin = ui->edit_roi_x_min->text().toDouble();
double xMax = ui->edit_roi_x_max->text().toDouble();
double yMin = ui->edit_roi_y_min->text().toDouble();
double yMax = ui->edit_roi_y_max->text().toDouble();
double zMin = ui->edit_roi_z_min->text().toDouble();
double zMax = ui->edit_roi_z_max->text().toDouble();
// 验证范围有效性
if (xMin >= xMax || yMin >= yMax || zMin >= zMax) {
QMessageBox::warning(this, "错误", "ROI范围无效最小值必须小于最大值");
return;
}
int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引
QString cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first);
LOG_INFO("Saving ROI range for camera %d (%s): X[%.1f, %.1f], Y[%.1f, %.1f], Z[%.1f, %.1f]\n",
cameraIndex, cameraName.toUtf8().constData(),
xMin, xMax, yMin, yMax, zMin, zMax);
// 查找或创建相机调平参数
bool found = false;
for (auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex) {
param.wheelRoi3d_xMin = xMin;
param.wheelRoi3d_xMax = xMax;
param.wheelRoi3d_yMin = yMin;
param.wheelRoi3d_yMax = yMax;
param.wheelRoi3d_zMin = zMin;
param.wheelRoi3d_zMax = zMax;
found = true;
break;
}
}
// 如果没有找到现有记录,创建一个新的
if (!found) {
WheelCameraPlaneCalibParam newParam;
newParam.cameraIndex = cameraIndex;
newParam.cameraName = cameraName.toStdString();
newParam.wheelRoi3d_xMin = xMin;
newParam.wheelRoi3d_xMax = xMax;
newParam.wheelRoi3d_yMin = yMin;
newParam.wheelRoi3d_yMax = yMax;
newParam.wheelRoi3d_zMin = zMin;
newParam.wheelRoi3d_zMax = zMax;
newParam.isCalibrated = false; // 尚未标定
m_pConfigResult->planeCalibParams.push_back(newParam);
}
// 保存配置到文件
QString configPath = PathManager::GetInstance().GetConfigFilePath();
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
if (saveResult) {
LOG_INFO("ROI range saved successfully for camera %d\n", cameraIndex);
QMessageBox::information(this, "成功", "ROI范围已保存");
} else {
LOG_ERROR("Failed to save ROI range\n");
QMessageBox::critical(this, "错误", "保存ROI范围失败");
}
}
void DialogCameraLevel::on_btn_save_roi_clicked()
{
saveCameraRoiRange();
}

View File

@ -49,6 +49,7 @@ private slots:
void on_btn_cancel_clicked();
void on_combo_camera_currentIndexChanged(int index);
void on_btn_save_compensation_clicked();
void on_btn_save_roi_clicked();
private:
Ui::DialogCameraLevel *ui;
@ -113,6 +114,10 @@ private:
// 检查并显示相机标定状态
void checkAndDisplayCalibrationStatus(int cameraIndex);
// 加载和保存ROI范围
void loadCameraRoiRange(int cameraIndex);
void saveCameraRoiRange();
};
#endif // DIALOGCAMERALEVEL_H

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>659</width>
<height>453</height>
<height>599</height>
</rect>
</property>
<property name="windowTitle">
@ -65,7 +65,7 @@
<property name="geometry">
<rect>
<x>170</x>
<y>400</y>
<y>550</y>
<width>101</width>
<height>38</height>
</rect>
@ -86,7 +86,7 @@
<property name="geometry">
<rect>
<x>380</x>
<y>400</y>
<y>550</y>
<width>111</width>
<height>38</height>
</rect>
@ -300,6 +300,454 @@ QPushButton:pressed {
</item>
</layout>
</widget>
<widget class="QGroupBox" name="groupBox_roi">
<property name="geometry">
<rect>
<x>20</x>
<y>390</y>
<width>621</width>
<height>150</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">QGroupBox {
color: rgb(221, 225, 233);
border: 1px solid #3B3D47;
border-radius: 4px;
margin-top: 10px;
padding-top: 10px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
padding: 0 5px;
}</string>
</property>
<property name="title">
<string>轮胎存在检测ROI范围 (mm)</string>
</property>
<layout class="QGridLayout" name="gridLayout_roi">
<property name="leftMargin">
<number>15</number>
</property>
<property name="rightMargin">
<number>15</number>
</property>
<item row="1" column="2">
<widget class="QLabel" name="label_to_2">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);</string>
</property>
<property name="text">
<string>~</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="edit_roi_z_min">
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);
background-color: rgb(47, 48, 52);</string>
</property>
<property name="text">
<string>-1000.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="btn_save_roi">
<property name="minimumSize">
<size>
<width>80</width>
<height>35</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>35</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
color: rgb(221, 225, 233);
background-color: rgb(60, 63, 65);
border: 1px solid #3B3D47;
border-radius: 4px;
}
QPushButton:hover {
background-color: rgb(80, 83, 85);
}
QPushButton:pressed {
background-color: rgb(45, 48, 50);
}</string>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLineEdit" name="edit_roi_z_max">
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);
background-color: rgb(47, 48, 52);</string>
</property>
<property name="text">
<string>500.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_x_range">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);</string>
</property>
<property name="text">
<string>X范围</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_z_range">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);</string>
</property>
<property name="text">
<string>Z范围</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="edit_roi_x_min">
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);
background-color: rgb(47, 48, 52);</string>
</property>
<property name="text">
<string>-500.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="label_to_3">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);</string>
</property>
<property name="text">
<string>~</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_to_1">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>20</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);</string>
</property>
<property name="text">
<string>~</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLineEdit" name="edit_roi_y_max">
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);
background-color: rgb(47, 48, 52);</string>
</property>
<property name="text">
<string>1000.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="edit_roi_x_max">
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);
background-color: rgb(47, 48, 52);</string>
</property>
<property name="text">
<string>1000.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="edit_roi_y_min">
<property name="minimumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>30</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);
background-color: rgb(47, 48, 52);</string>
</property>
<property name="text">
<string>-1000.0</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_y_range">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>13</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(221, 225, 233);</string>
</property>
<property name="text">
<string>Y范围</string>
</property>
</widget>
</item>
<item row="0" column="5" rowspan="3">
<spacer name="horizontalSpacer_roi">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>

View File

@ -2,6 +2,7 @@
#include "IWheelMeasureStatus.h"
#include "IVrWheelMeasureConfig.h"
#include "CrashHandler.h"
#include "AuthView.h"
#include <QApplication>
#include <QMetaType>
@ -69,6 +70,12 @@ int main(int argc, char *argv[])
return 0;
}
// 检查授权,无授权则显示授权对话框
if (!AuthView::CheckAndShow(nullptr)) {
// 用户取消授权或授权失败,退出程序
return 0;
}
MainWindow w;
w.show();
return a.exec();

View File

@ -531,6 +531,8 @@ void MainWindow::OnDeviceStatusChanged(const QString &deviceName, int deviceStat
void MainWindow::OnClearMeasureData()
{
if (m_logHelper) m_logHelper->clearLog();
if (m_measureResultWidget) {
m_measureResultWidget->clearAllResults();
}

View File

@ -28,7 +28,7 @@ struct WheelServerInfo
{
std::string name; // 服务器名称
std::string ip; // 服务器IP地址
int port = 5800; // 服务器端口
int port = 5000; // 服务器端口
};
/**
@ -62,6 +62,14 @@ struct WheelCameraPlaneCalibParam
double invRMatrix[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; // 逆旋转矩阵(回到原坐标系)
bool isCalibrated = false; // 是否已标定
double errorCompensation = -5.0; // 误差补偿mm
// 轮胎存在检测的3D ROI范围
double wheelRoi3d_xMin = -1000.0; // X方向最小值mm
double wheelRoi3d_xMax = 1000.0; // X方向最大值mm
double wheelRoi3d_yMin = -1000.0; // Y方向最小值mm
double wheelRoi3d_yMax = 1000.0; // Y方向最大值mm
double wheelRoi3d_zMin = -1000.0; // Z方向最小值mm
double wheelRoi3d_zMax = 1000.0; // Z方向最大值mm
};
/**
@ -168,6 +176,8 @@ struct WheelMeasureResult
QImage image; // 图像
bool bImageValid = false; // 图像是否有效
bool bResultValid = false; // 结果是否有效
int errorCode = 0; // 错误码0表示成功401表示工件为空
QString errorMessage = ""; // 错误信息
std::vector<WheelMeasureData> result; // 测量结果列表
};

View File

@ -91,6 +91,24 @@ WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& fil
calibParam.errorCompensation = -5.0;
}
// 读取轮胎存在检测的3D ROI范围
if (xml.attributes().hasAttribute("wheelRoi3d_xMin")) {
calibParam.wheelRoi3d_xMin = xml.attributes().value("wheelRoi3d_xMin").toDouble();
calibParam.wheelRoi3d_xMax = xml.attributes().value("wheelRoi3d_xMax").toDouble();
calibParam.wheelRoi3d_yMin = xml.attributes().value("wheelRoi3d_yMin").toDouble();
calibParam.wheelRoi3d_yMax = xml.attributes().value("wheelRoi3d_yMax").toDouble();
calibParam.wheelRoi3d_zMin = xml.attributes().value("wheelRoi3d_zMin").toDouble();
calibParam.wheelRoi3d_zMax = xml.attributes().value("wheelRoi3d_zMax").toDouble();
} else {
// 默认值
calibParam.wheelRoi3d_xMin = -1000.0;
calibParam.wheelRoi3d_xMax = 1000.0;
calibParam.wheelRoi3d_yMin = -1000.0;
calibParam.wheelRoi3d_yMax = 1000.0;
calibParam.wheelRoi3d_zMin = -1000.0;
calibParam.wheelRoi3d_zMax = 1000.0;
}
// 读取planeCalib矩阵
QString planeCalibStr = xml.attributes().value("planeCalib").toString();
QStringList planeCalibList = planeCalibStr.split(",");
@ -330,6 +348,14 @@ bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureC
xml.writeAttribute("isCalibrated", QString::number(calibParam.isCalibrated ? 1 : 0));
xml.writeAttribute("errorCompensation", QString::number(calibParam.errorCompensation, 'f', 2));
// 保存轮胎存在检测的3D ROI范围
xml.writeAttribute("wheelRoi3d_xMin", QString::number(calibParam.wheelRoi3d_xMin, 'f', 2));
xml.writeAttribute("wheelRoi3d_xMax", QString::number(calibParam.wheelRoi3d_xMax, 'f', 2));
xml.writeAttribute("wheelRoi3d_yMin", QString::number(calibParam.wheelRoi3d_yMin, 'f', 2));
xml.writeAttribute("wheelRoi3d_yMax", QString::number(calibParam.wheelRoi3d_yMax, 'f', 2));
xml.writeAttribute("wheelRoi3d_zMin", QString::number(calibParam.wheelRoi3d_zMin, 'f', 2));
xml.writeAttribute("wheelRoi3d_zMax", QString::number(calibParam.wheelRoi3d_zMax, 'f', 2));
// 保存planeCalib矩阵
QStringList planeCalibList;
for (int i = 0; i < 9; ++i) {

View File

@ -25,6 +25,26 @@ typedef struct
double yawAngle; //偏转角绕Z轴的偏转, 弧度
}SSG_6AxisAttitude;
typedef struct
{
double x;
double y;
double z;
}SWD3DPoint;
typedef struct
{
int lineIdx;
int ptIdx;
SWD3DPoint point;
}SWDIndexing3DPoint;
typedef struct
{
int start;
int end;
}SWD_Interval; //区间
typedef struct
{
bool validFlag; //指示结果是否有效
@ -41,6 +61,12 @@ typedef enum
keSG_PoseSorting_T2B_L2R, ///从上到下,从左到右排序
} ESG_poseSortingMode;
typedef enum
{
KeWD_Mask_ValidPt = 0,
KeWD_Mask_NullPt,
}EWD_maskMode;
typedef struct
{
int data_0;
@ -48,6 +74,13 @@ typedef struct
int idx;
}SSG_intPair;
typedef struct
{
int flag;
int validFlag;
int clusterID;
}SSG_clusterLabel;
typedef struct
{
int featurType;
@ -67,6 +100,18 @@ typedef struct
double bottom;
}SSG_ROIRectD;
typedef struct
{
double width;
double height;
}SSG_size2D;
typedef struct
{
SVzNL3DPoint center;
double radius;
}SWD_HoleInfo;
struct HSV {
double h; // 色相 (0-360)
double s; // 饱和度 (0-1)
@ -172,9 +217,16 @@ typedef struct
{
double segGapTh_y; //y方向连续段门限。大于此门限为不连续
double segGapTh_z; //z方向连续段门限。大于此门限为不连续
double maxDist; //计算方向角的窗口比例尺
double distScale; //计算方向角的窗口比例尺
}SSG_lineSegParam;
typedef struct
{
double minJumpZ; //z方向跳变门限
double minK; //跳变的最小斜率
SVzNLRangeD widthRange; //z方向连续段门限。大于此门限为不连续
}SSG_raisedFeatureParam;
typedef struct
{
double scale_angle; //计算方向角的窗口比例尺
@ -202,6 +254,7 @@ typedef struct
int endPtIdx;
SVzNL3DPoint startPt;
SVzNL3DPoint endPt;
double featureValue;
}SWD_segFeature;
typedef struct
@ -394,9 +447,9 @@ typedef struct
typedef struct
{
SVzNL3DPoint opCenter; //定子中心位置
double objR;
}SWD_motorStatorPosition;
int objID;
SSG_6DOF opCenter; //定子中心位置
}SWD_statorInnerGrasper;
typedef struct
{
@ -479,6 +532,20 @@ typedef struct
double invRMatrix[9]; //旋转矩阵,回到原坐标系
}SSG_planeCalibPara;
typedef struct
{
int pkId;
int lineIdx;
int ptIdx;
int cptIndex; //圆周扫描上的点序
//double cornerAngle; //以点为中心的两侧弦的夹角
double R;
double angle;
double x;
double y;
double z;
}SWD_polarPt;
typedef struct
{
int pntIdx;
@ -494,6 +561,18 @@ typedef struct
double backward_z;
}SSG_pntDirAngle;
typedef struct
{
double forwardAngle; //前向角
double backwardAngle; //后向角
double corner; //拐角
int pntIdx;
int forward_pntIdx;
int backward_pntIdx;
int flag;
SWD_polarPt point;
}SSG_dirCornerAngle;
// 图像数据结构
typedef struct {
int width;
@ -515,20 +594,6 @@ typedef struct
SVzNL3DPoint pt2;
}SWD_3DPointPair;
typedef struct
{
int pkId;
int lineIdx;
int ptIdx;
int cptIndex; //圆周扫描上的点序
//double cornerAngle; //以点为中心的两侧弦的夹角
double R;
double angle;
double x;
double y;
double z;
}SWD_polarPt;
typedef struct
{
int cptIndex;
@ -536,6 +601,8 @@ typedef struct
int L2_ptIndex;
double cornerAngle;
int cornerDir;
int flag;
SWD_polarPt point;
}SWD_polarPeakInfo;
typedef struct

View File

@ -7,6 +7,9 @@
#define SG_ERR_LABEL_INFO_ERROR -1004
#define SG_ERR_INVLD_SORTING_MODE -1005
#define SG_ERR_INVLD_Q_SCALE -1006
#define SG_ERR_ZERO_OBJECTS -1007
#define SG_ERR_LASER_DIR_NOT_SUPPORTED -1008
#define SG_ERR_SCAN_DIR_NOT_SUPPORTED -1009
//BQ_workpiece
#define SX_ERR_INVLD_VTREE_NUM -2001
@ -20,7 +23,9 @@
//定子抓取
#define SX_ERR_INVLID_CUTTING_Z -2101
#define SX_ERR_ZERO_OBJ -2102
#define SX_ERR_ZERO_OBJ_TOPLAYER -2102
#define SX_ERR_ZERO_OBJ_BTMLAYER -2103
#define SX_ERR_GET_INVALID_PALTE -2104 //¸ô°åÌáÈ¡´íÎó
//拆包
#define SX_BAG_TRAY_EMPTY -2201
@ -28,3 +33,6 @@
//汽车轮眉高度测量
#define SX_ERR_INVALID_ARC -2301
//ÌÇ´ü×Ó²ðÏß
#define SX_ERR_NO_MARK -2401

View File

@ -33,6 +33,11 @@ SG_APISHARED_EXPORT void wd_horizonCamera_lineDataR(
const double* camPoseR,
double groundH);
//相机水平时姿态调平,并去除地面
SG_APISHARED_EXPORT bool wd_wheelPresenseDetection(
std::vector<std::vector< SVzNL3DPosition>>& scanLine,
const SVzNL3DRangeD wheelRoi3d);
//提取工件角点及定位长度信息
SG_APISHARED_EXPORT WD_wheelArchInfo wd_wheelArchHeigthMeasure(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,

View File

@ -2637,7 +2637,7 @@ void _outputScanDataFile_removeZeros(char* fileName, SVzNL3DLaserLine * scanData
#define TEST_CONVERT_TO_GRID 0
#define TEST_COMPUTE_WHEEL_ARCH 1
#define TEST_COMPUTE_CALIB_PARA 0
#define TEST_COMPUTE_CALIB_PARA 1
#define TEST_GROUP 1
int main()
@ -2683,7 +2683,7 @@ int main()
#if TEST_COMPUTE_CALIB_PARA
char _calib_datafile[256];
sprintf_s(_calib_datafile, "F:/ShangGu/项目/冠钦_轮眉高度测量/测试数据/LaserLine1_grid.txt");
sprintf_s(_calib_datafile, "F:/ShangGu/项目/冠钦_轮眉高度测量/测试数据/现场数据/ground_LaserData.txt");
std::vector< std::vector<SVzNL3DPosition>> scanLines;
vzReadLaserScanPointFromFile_XYZ_vector(_calib_datafile, scanLines);
@ -2704,10 +2704,10 @@ int main()
#endif
//
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/项目/冠钦_轮眉高度测量/测试数据/LaserLine1_grid_calib.txt");
sprintf_s(_out_file, "F:/ShangGu/项目/冠钦_轮眉高度测量/测试数据/现场数据/LaserLine_grund_calib.txt");
_outputScanDataFile_self(_out_file, scanLines, 0, 0, 0);
printf("%s: calib done!\n", _calib_datafile);
}
@ -2716,11 +2716,11 @@ int main()
#if TEST_COMPUTE_WHEEL_ARCH
const char* dataPath[TEST_GROUP] = {
"F:/ShangGu/项目/冠钦_轮眉高度测量/测试数据/", //0
"F:/ShangGu/项目/冠钦_轮眉高度测量/测试数据/现场数据/", //0
};
SVzNLRange fileIdx[TEST_GROUP] = {
{1,1},
{1,2},
};
SSG_planeCalibPara poseCalibPara;
@ -2752,7 +2752,7 @@ int main()
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
{
//fidx = 193;
sprintf_s(_scan_file, "%sLaserLine1_grid.txt", dataPath[grp], fidx);
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
std::vector<std::vector< SVzNL3DPosition>> scanData;
vzReadLaserScanPointFromFile_XYZ_vector(_scan_file, scanData);
if (scanData.size() == 0)
@ -2762,21 +2762,10 @@ int main()
//algoParam.filterParam.outlierTh = 5;
long t1 = GetTickCount64();
int lineNum = (int)scanData.size();
for (int i = 0; i < lineNum; i++)
{
if (i == 14)
int kkk = 1;
//行处理
//调平,去除地面
wd_horizonCamera_lineDataR(scanData[i], poseCalibPara.planeCalib, poseCalibPara.planeHeight);
}
#if 0 //数据转存
sprintf_s(_scan_file, "%sLaserLine%d_grid_RTadjust.txt", dataPath[grp], fidx);
_outputScanDataFile_self(_scan_file, scanData, 0, 0, 0);
#endif
SVzNL3DRangeD wheelRoi3d = { {-1000, 1000}, {-2000, 2000}, {500, 2500} };
SSG_cornerParam cornerParam;
cornerParam.cornerTh = 60; //45度角
cornerParam.cornerTh = 45; //45度角
cornerParam.scale = 50; // algoParam.bagParam.bagH / 8; // 15; // algoParam.bagParam.bagH / 8;
cornerParam.minEndingGap = 20; // algoParam.bagParam.bagW / 4;
cornerParam.minEndingGap_z = 20;
@ -2784,7 +2773,7 @@ int main()
cornerParam.jumpCornerTh_2 = 60;
SSG_lineSegParam lineSegPara;
lineSegPara.maxDist = 1.0;
lineSegPara.distScale = 1.0;
lineSegPara.segGapTh_y = 5.0; //y方向间隔大于5mm认为是分段
lineSegPara.segGapTh_z = 10.0; //z方向间隔大于10mm认为是分段
@ -2800,6 +2789,27 @@ int main()
growParam.minLTypeTreeLen = 100; //mm
growParam.minVTypeTreeLen = 100; //mm
WD_wheelArchInfo wheelArcHeight;
memset(&wheelArcHeight, 0, sizeof(WD_wheelArchInfo));
bool wheePresense = wd_wheelPresenseDetection( scanData, wheelRoi3d);
if (true == wheePresense)
{
int lineNum = (int)scanData.size();
for (int i = 0; i < lineNum; i++)
{
if (i == 14)
int kkk = 1;
//行处理
//调平,去除地面
wd_horizonCamera_lineDataR(scanData[i], poseCalibPara.planeCalib, poseCalibPara.planeHeight - 5);
}
#if 0 //数据转存
sprintf_s(_scan_file, "%sLaserLine%d_grid_RTadjust.txt", dataPath[grp], fidx);
_outputScanDataFile_self(_scan_file, scanData, 0, 0, 0);
#endif
int errCode = 0;
WD_wheelArchInfo wheelArcHeight = wd_wheelArchHeigthMeasure(
scanData,
@ -2809,20 +2819,20 @@ int main()
growParam,
poseCalibPara,
&errCode);
#endif
}
long t2 = GetTickCount64();
char _dbg_file[256];
#if 1
sprintf_s(_dbg_file, "%sresult\\LaserLine%d_result.txt", dataPath[grp], fidx);
_outputScanDataFile_RGBD_obj(_dbg_file, scanData, 0, 0, 0, wheelArcHeight);
#endif
printf("%s: height=%f, %d(ms)!\n", _scan_file, wheelArcHeight.archToCenterHeigth, (int)(t2 - t1));
printf("%s: height=%f, arcToGrund=%f, time=%d(ms)!\n", _scan_file, wheelArcHeight.archToCenterHeigth, wheelArcHeight.archToGroundHeigth, (int)(t2 - t1));
}
}
#endif
printf("all done!\n");
}

View File

@ -65,6 +65,12 @@ echo "复制 Qt 运行时环境..."
cp -rfd ${QT_PKG_PATH}/ext ${PKG_PATH}/opt/firefly_qt5.15
cp ${QT_PKG_PATH}/target_qtEnv.sh ${PKG_PATH}/etc/profile.d/
echo "清理不需要的 Qt WebEngine 库文件..."
rm -f ${PKG_PATH}/opt/firefly_qt5.15/lib/libQt5WebEngine*
rm -rf ${PKG_PATH}/opt/firefly_qt5.15/libexec/QtWebEngineProcess
rm -rf ${PKG_PATH}/opt/firefly_qt5.15/resources/qtwebengine*
rm -rf ${PKG_PATH}/opt/firefly_qt5.15/translations/qtwebengine*
# 复制 Qt 库文件
for libfile in ${QT_LIB_PATH}/*.so*; do
# 获取文件名用于比较