轮胎检测增加TCP协议
This commit is contained in:
parent
5dc4feab00
commit
1f8cf970be
131
App/WheelMeasure/Doc/TCP_Protocol.md
Normal file
131
App/WheelMeasure/Doc/TCP_Protocol.md
Normal 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. **数据精度**:返回的距离值已四舍五入为整数
|
||||
@ -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++;
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
continueSequentialDetection();
|
||||
}, Qt::QueuedConnection);
|
||||
} else if (m_tcpDetectionMode) {
|
||||
|
||||
// TCP模式下,检查是否所有相机都检测完成
|
||||
if (m_tcpResults.size() >= m_sequentialTotalCount) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
|
||||
#define WHEELMEASURE_VERSION_STRING "1.0.1"
|
||||
#define WHEELMEASURE_BUILD_STRING "5"
|
||||
#define WHEELMEASURE_FULL_VERSION_STRING "V" WHEELMEASURE_VERSION_STRING "_" WHEELMEASURE_BUILD_STRING
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
inline const char* GetWheelMeasureVersion() {
|
||||
return WHEELMEASURE_VERSION_STRING;
|
||||
}
|
||||
|
||||
inline const char* GetWheelMeasureBuild() {
|
||||
return WHEELMEASURE_BUILD_STRING;
|
||||
}
|
||||
|
||||
inline const char* GetWheelMeasureFullVersion() {
|
||||
return WHEELMEASURE_FULL_VERSION_STRING;
|
||||
}
|
||||
|
||||
#endif // VERSION_H
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
|
||||
#define WHEELMEASURE_VERSION_STRING "1.0.1"
|
||||
#define WHEELMEASURE_BUILD_STRING "6"
|
||||
#define WHEELMEASURE_FULL_VERSION_STRING "V" WHEELMEASURE_VERSION_STRING "_" WHEELMEASURE_BUILD_STRING
|
||||
|
||||
// 获取版本信息的便捷函数
|
||||
inline const char* GetWheelMeasureVersion() {
|
||||
return WHEELMEASURE_VERSION_STRING;
|
||||
}
|
||||
|
||||
inline const char* GetWheelMeasureBuild() {
|
||||
return WHEELMEASURE_BUILD_STRING;
|
||||
}
|
||||
|
||||
inline const char* GetWheelMeasureFullVersion() {
|
||||
return WHEELMEASURE_FULL_VERSION_STRING;
|
||||
}
|
||||
|
||||
#endif // VERSION_H
|
||||
|
||||
@ -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
|
||||
@ -38,8 +39,9 @@ win32:CONFIG(debug, debug|release) {
|
||||
LIBS += -L../../../Utils/VrUtils/debug -lVrUtils
|
||||
LIBS += -L../../../Device/VrEyeDevice/debug -lVrEyeDevice
|
||||
LIBS += -L../../../AppUtils/UICommon/debug -lUICommon
|
||||
LIBS += -L../../../Utils/CloudUtils/debug -lCloudUtils
|
||||
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) {
|
||||
@ -48,8 +50,9 @@ win32:CONFIG(debug, debug|release) {
|
||||
LIBS += -L../../../Utils/VrUtils/release -lVrUtils
|
||||
LIBS += -L../../../Device/VrEyeDevice/release -lVrEyeDevice
|
||||
LIBS += -L../../../AppUtils/UICommon/release -lUICommon
|
||||
LIBS += -L../../../Utils/CloudUtils/release -lCloudUtils
|
||||
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,7 +60,8 @@ win32:CONFIG(debug, debug|release) {
|
||||
LIBS += -L../WheelMeasureConfig -lWheelMeasureConfig
|
||||
LIBS += -L../../../AppUtils/AppCommon -lAppCommon
|
||||
LIBS += -L../../../AppUtils/UICommon -lUICommon
|
||||
LIBS += -L../../../Utils/CloudUtils -lCloudUtils
|
||||
LIBS += -L../../../Module/AuthModule -lAuthModule
|
||||
LIBS += -L../../../Utils/CloudUtils -lCloudUtils
|
||||
LIBS += -L../../../Device/VrEyeDevice -lVrEyeDevice
|
||||
LIBS += -L../../../VrNets -lVrTcpClient -lVrTcpServer
|
||||
LIBS += -L../../../Utils/VrUtils -lVrUtils
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,118 +1,123 @@
|
||||
#ifndef DIALOGCAMERALEVEL_H
|
||||
#define DIALOGCAMERALEVEL_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include "IVrEyeDevice.h"
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
#include "VZNL_Types.h"
|
||||
|
||||
// 前向声明
|
||||
class WheelMeasurePresenter;
|
||||
|
||||
namespace Ui {
|
||||
class DialogCameraLevel;
|
||||
}
|
||||
|
||||
class DialogCameraLevel : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DialogCameraLevel(QWidget *parent = nullptr);
|
||||
~DialogCameraLevel();
|
||||
|
||||
/**
|
||||
* @brief 设置相机列表和presenter
|
||||
* @param cameraList 相机列表
|
||||
* @param presenter Presenter指针
|
||||
*/
|
||||
void setCameraList(const std::vector<std::pair<std::string, IVrEyeDevice*>>& cameraList,
|
||||
WheelMeasurePresenter* presenter);
|
||||
|
||||
/**
|
||||
* @brief 设置配置对象
|
||||
* @param config 配置接口
|
||||
* @param configResult 配置结果
|
||||
*/
|
||||
void setConfig(IVrWheelMeasureConfig* config, WheelMeasureConfigResult* configResult);
|
||||
|
||||
private slots:
|
||||
void on_btn_apply_clicked();
|
||||
void on_btn_cancel_clicked();
|
||||
void on_combo_camera_currentIndexChanged(int index);
|
||||
void on_btn_save_compensation_clicked();
|
||||
|
||||
private:
|
||||
Ui::DialogCameraLevel *ui;
|
||||
|
||||
// 相机列表和名称
|
||||
std::vector<std::pair<std::string, IVrEyeDevice*>> m_cameraList;
|
||||
WheelMeasurePresenter* m_presenter = nullptr;
|
||||
|
||||
// 配置对象
|
||||
IVrWheelMeasureConfig* m_pConfig = nullptr;
|
||||
WheelMeasureConfigResult* m_pConfigResult = nullptr;
|
||||
|
||||
// 当前选中的相机索引
|
||||
int m_currentCameraIndex = -1;
|
||||
|
||||
// 扫描数据缓存
|
||||
std::vector<std::vector<SVzNL3DPosition>> m_scanDataCache;
|
||||
std::mutex m_scanDataMutex;
|
||||
|
||||
// 状态回调相关
|
||||
std::atomic<bool> m_swingFinished{false};
|
||||
std::atomic<bool> m_callbackRestored{false};
|
||||
|
||||
// 初始化相机选择框
|
||||
void initializeCameraCombo();
|
||||
|
||||
// 执行相机调平
|
||||
bool performCameraLeveling();
|
||||
|
||||
// 直接使用相机接口进行扫描
|
||||
bool startCameraScan(int cameraIndex);
|
||||
bool stopCameraScan(int cameraIndex);
|
||||
|
||||
// 检测数据回调函数
|
||||
static void StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData);
|
||||
void DetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint);
|
||||
|
||||
// 状态回调函数
|
||||
static void StaticStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam);
|
||||
void StatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam);
|
||||
|
||||
// 设置和恢复状态回调
|
||||
void setLevelingStatusCallback();
|
||||
void restorePresenterStatusCallback();
|
||||
|
||||
// 处理扫描到的地面数据进行调平计算
|
||||
bool calculatePlaneCalibration(double planeCalib[9], double& planeHeight, double invRMatrix[9]);
|
||||
|
||||
// 清空扫描数据缓存
|
||||
void clearScanDataCache();
|
||||
|
||||
// 更新调平结果显示
|
||||
void updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9]);
|
||||
|
||||
// 保存调平结果到配置
|
||||
bool saveLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9],
|
||||
int cameraIndex, const QString& cameraName);
|
||||
|
||||
// 加载相机标定数据
|
||||
bool loadCameraCalibrationData(int cameraIndex, const QString& cameraName,
|
||||
double planeCalib[9], double& planeHeight, double invRMatrix[9]);
|
||||
|
||||
// 检查并显示相机标定状态
|
||||
void checkAndDisplayCalibrationStatus(int cameraIndex);
|
||||
};
|
||||
|
||||
#endif // DIALOGCAMERALEVEL_H
|
||||
#ifndef DIALOGCAMERALEVEL_H
|
||||
#define DIALOGCAMERALEVEL_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include "IVrEyeDevice.h"
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
#include "VZNL_Types.h"
|
||||
|
||||
// 前向声明
|
||||
class WheelMeasurePresenter;
|
||||
|
||||
namespace Ui {
|
||||
class DialogCameraLevel;
|
||||
}
|
||||
|
||||
class DialogCameraLevel : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DialogCameraLevel(QWidget *parent = nullptr);
|
||||
~DialogCameraLevel();
|
||||
|
||||
/**
|
||||
* @brief 设置相机列表和presenter
|
||||
* @param cameraList 相机列表
|
||||
* @param presenter Presenter指针
|
||||
*/
|
||||
void setCameraList(const std::vector<std::pair<std::string, IVrEyeDevice*>>& cameraList,
|
||||
WheelMeasurePresenter* presenter);
|
||||
|
||||
/**
|
||||
* @brief 设置配置对象
|
||||
* @param config 配置接口
|
||||
* @param configResult 配置结果
|
||||
*/
|
||||
void setConfig(IVrWheelMeasureConfig* config, WheelMeasureConfigResult* configResult);
|
||||
|
||||
private slots:
|
||||
void on_btn_apply_clicked();
|
||||
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;
|
||||
|
||||
// 相机列表和名称
|
||||
std::vector<std::pair<std::string, IVrEyeDevice*>> m_cameraList;
|
||||
WheelMeasurePresenter* m_presenter = nullptr;
|
||||
|
||||
// 配置对象
|
||||
IVrWheelMeasureConfig* m_pConfig = nullptr;
|
||||
WheelMeasureConfigResult* m_pConfigResult = nullptr;
|
||||
|
||||
// 当前选中的相机索引
|
||||
int m_currentCameraIndex = -1;
|
||||
|
||||
// 扫描数据缓存
|
||||
std::vector<std::vector<SVzNL3DPosition>> m_scanDataCache;
|
||||
std::mutex m_scanDataMutex;
|
||||
|
||||
// 状态回调相关
|
||||
std::atomic<bool> m_swingFinished{false};
|
||||
std::atomic<bool> m_callbackRestored{false};
|
||||
|
||||
// 初始化相机选择框
|
||||
void initializeCameraCombo();
|
||||
|
||||
// 执行相机调平
|
||||
bool performCameraLeveling();
|
||||
|
||||
// 直接使用相机接口进行扫描
|
||||
bool startCameraScan(int cameraIndex);
|
||||
bool stopCameraScan(int cameraIndex);
|
||||
|
||||
// 检测数据回调函数
|
||||
static void StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData);
|
||||
void DetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint);
|
||||
|
||||
// 状态回调函数
|
||||
static void StaticStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam);
|
||||
void StatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam);
|
||||
|
||||
// 设置和恢复状态回调
|
||||
void setLevelingStatusCallback();
|
||||
void restorePresenterStatusCallback();
|
||||
|
||||
// 处理扫描到的地面数据进行调平计算
|
||||
bool calculatePlaneCalibration(double planeCalib[9], double& planeHeight, double invRMatrix[9]);
|
||||
|
||||
// 清空扫描数据缓存
|
||||
void clearScanDataCache();
|
||||
|
||||
// 更新调平结果显示
|
||||
void updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9]);
|
||||
|
||||
// 保存调平结果到配置
|
||||
bool saveLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9],
|
||||
int cameraIndex, const QString& cameraName);
|
||||
|
||||
// 加载相机标定数据
|
||||
bool loadCameraCalibrationData(int cameraIndex, const QString& cameraName,
|
||||
double planeCalib[9], double& planeHeight, double invRMatrix[9]);
|
||||
|
||||
// 检查并显示相机标定状态
|
||||
void checkAndDisplayCalibrationStatus(int cameraIndex);
|
||||
|
||||
// 加载和保存ROI范围
|
||||
void loadCameraRoiRange(int cameraIndex);
|
||||
void saveCameraRoiRange();
|
||||
};
|
||||
|
||||
#endif // DIALOGCAMERALEVEL_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,75 +1,82 @@
|
||||
#include "mainwindow.h"
|
||||
#include "IWheelMeasureStatus.h"
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
#include "CrashHandler.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMetaType>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#include "Version.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
// 设置应用程序信息
|
||||
a.setApplicationName("WheelMeasureApp");
|
||||
a.setApplicationVersion(GetWheelMeasureFullVersion());
|
||||
a.setOrganizationName("VisionRobot");
|
||||
|
||||
// 注册Qt元类型
|
||||
qRegisterMetaType<QVector<int>>("QVector<int>");
|
||||
qRegisterMetaType<QList<QPersistentModelIndex>>("QList<QPersistentModelIndex>");
|
||||
qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>("QAbstractItemModel::LayoutChangeHint");
|
||||
qRegisterMetaType<Qt::SortOrder>("Qt::SortOrder");
|
||||
|
||||
// 注册自定义元类型
|
||||
qRegisterMetaType<WheelCameraParam>("WheelCameraParam");
|
||||
qRegisterMetaType<WheelCameraPlaneCalibParam>("WheelCameraPlaneCalibParam");
|
||||
qRegisterMetaType<WheelMeasureConfigResult>("WheelMeasureConfigResult");
|
||||
qRegisterMetaType<WheelMeasureData>("WheelMeasureData");
|
||||
qRegisterMetaType<WheelMeasureResult>("WheelMeasureResult");
|
||||
|
||||
// 单实例检查
|
||||
const QString appKey = "WheelMeasureApp_SingleInstance_Key";
|
||||
|
||||
QSystemSemaphore semaphore(appKey + "_semaphore", 1);
|
||||
semaphore.acquire();
|
||||
|
||||
QSharedMemory sharedMemory(appKey + "_memory");
|
||||
|
||||
bool isRunning = false;
|
||||
|
||||
if (sharedMemory.attach()) {
|
||||
isRunning = true;
|
||||
} else {
|
||||
if (!sharedMemory.create(1)) {
|
||||
qDebug() << "Unable to create shared memory segment:" << sharedMemory.errorString();
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
semaphore.release();
|
||||
|
||||
if (isRunning) {
|
||||
QMessageBox::information(nullptr,
|
||||
QObject::tr("应用程序已运行"),
|
||||
QObject::tr("车轮拱高测量应用程序已经在运行中,请勿重复启动!"),
|
||||
QMessageBox::Ok);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
#include "mainwindow.h"
|
||||
#include "IWheelMeasureStatus.h"
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
#include "CrashHandler.h"
|
||||
#include "AuthView.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMetaType>
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSharedMemory>
|
||||
#include <QSystemSemaphore>
|
||||
#include <QMessageBox>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#include "Version.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
// 设置应用程序信息
|
||||
a.setApplicationName("WheelMeasureApp");
|
||||
a.setApplicationVersion(GetWheelMeasureFullVersion());
|
||||
a.setOrganizationName("VisionRobot");
|
||||
|
||||
// 注册Qt元类型
|
||||
qRegisterMetaType<QVector<int>>("QVector<int>");
|
||||
qRegisterMetaType<QList<QPersistentModelIndex>>("QList<QPersistentModelIndex>");
|
||||
qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>("QAbstractItemModel::LayoutChangeHint");
|
||||
qRegisterMetaType<Qt::SortOrder>("Qt::SortOrder");
|
||||
|
||||
// 注册自定义元类型
|
||||
qRegisterMetaType<WheelCameraParam>("WheelCameraParam");
|
||||
qRegisterMetaType<WheelCameraPlaneCalibParam>("WheelCameraPlaneCalibParam");
|
||||
qRegisterMetaType<WheelMeasureConfigResult>("WheelMeasureConfigResult");
|
||||
qRegisterMetaType<WheelMeasureData>("WheelMeasureData");
|
||||
qRegisterMetaType<WheelMeasureResult>("WheelMeasureResult");
|
||||
|
||||
// 单实例检查
|
||||
const QString appKey = "WheelMeasureApp_SingleInstance_Key";
|
||||
|
||||
QSystemSemaphore semaphore(appKey + "_semaphore", 1);
|
||||
semaphore.acquire();
|
||||
|
||||
QSharedMemory sharedMemory(appKey + "_memory");
|
||||
|
||||
bool isRunning = false;
|
||||
|
||||
if (sharedMemory.attach()) {
|
||||
isRunning = true;
|
||||
} else {
|
||||
if (!sharedMemory.create(1)) {
|
||||
qDebug() << "Unable to create shared memory segment:" << sharedMemory.errorString();
|
||||
isRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
semaphore.release();
|
||||
|
||||
if (isRunning) {
|
||||
QMessageBox::information(nullptr,
|
||||
QObject::tr("应用程序已运行"),
|
||||
QObject::tr("车轮拱高测量应用程序已经在运行中,请勿重复启动!"),
|
||||
QMessageBox::Ok);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 检查授权,无授权则显示授权对话框
|
||||
if (!AuthView::CheckAndShow(nullptr)) {
|
||||
// 用户取消授权或授权失败,退出程序
|
||||
return 0;
|
||||
}
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,236 +1,246 @@
|
||||
#ifndef IVRWHEELMEASURECONFIG_H
|
||||
#define IVRWHEELMEASURECONFIG_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <QtCore/QMetaType>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImage>
|
||||
#include "VrCommonConfig.h" // 包含公共配置结构体
|
||||
|
||||
/**
|
||||
* @brief 数据类型枚举
|
||||
*/
|
||||
enum class WheelMeasureDataType {
|
||||
Text = 0x01,
|
||||
Image = 0x02,
|
||||
ReadConfig = 0x03,
|
||||
WriteConfig = 0x04,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务器信息结构
|
||||
*/
|
||||
struct WheelServerInfo
|
||||
{
|
||||
std::string name; // 服务器名称
|
||||
std::string ip; // 服务器IP地址
|
||||
int port = 5800; // 服务器端口
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相机配置参数
|
||||
*/
|
||||
struct WheelCameraParam
|
||||
{
|
||||
int cameraIndex = 0; // 相机索引(1-based)
|
||||
std::string name = ""; // 相机名称
|
||||
std::string cameraIP = ""; // 相机IP地址
|
||||
bool enabled = true; // 是否启用
|
||||
|
||||
// 相机工作参数(参考 GrabBag)
|
||||
double exposure = 100.0; // 曝光时间 (微秒)
|
||||
double gain = 1.0; // 增益值
|
||||
double frameRate = 500.0; // 帧率
|
||||
double swingSpeed = 30.0; // 摆动速度
|
||||
double swingStartAngle = 0.0; // 开始角度
|
||||
double swingStopAngle = 76.0; // 结束角度
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相机调平参数
|
||||
*/
|
||||
struct WheelCameraPlaneCalibParam
|
||||
{
|
||||
int cameraIndex = 0; // 相机索引
|
||||
std::string cameraName = ""; // 相机名称
|
||||
double planeCalib[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; // 旋转矩阵(将点云旋转到水平)
|
||||
double planeHeight = -1; // 地面高度
|
||||
double invRMatrix[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; // 逆旋转矩阵(回到原坐标系)
|
||||
bool isCalibrated = false; // 是否已标定
|
||||
double errorCompensation = -5.0; // 误差补偿(mm)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 角点参数
|
||||
*/
|
||||
struct WheelCornerParam
|
||||
{
|
||||
double minEndingGap = 20.0; // y方向,最短结束间隙
|
||||
double minEndingGap_z = 20.0; // z方向,最短结束间隙
|
||||
double scale = 50.0; // 计算方向角的窗口比例
|
||||
double cornerTh = 45.0; // 空角阈值,大于此阈值为有效空点
|
||||
double jumpCornerTh_1 = 10.0; // 判断空角是否为跳变的阈值
|
||||
double jumpCornerTh_2 = 60.0; // 判断空角是否为跳变的阈值
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 线段参数
|
||||
*/
|
||||
struct WheelLineSegParam
|
||||
{
|
||||
double segGapTh_y = 5.0; // y方向,最短段间隙阈值
|
||||
double segGapTh_z = 10.0; // z方向,最短段间隙阈值
|
||||
double maxDist = 1.0; // 最大距离
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 离群点过滤参数
|
||||
*/
|
||||
struct WheelOutlierFilterParam
|
||||
{
|
||||
double continuityTh = 20.0; // 连续性阈值
|
||||
double outlierTh = 5.0; // 离群点阈值
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 树生长参数
|
||||
*/
|
||||
struct WheelTreeGrowParam
|
||||
{
|
||||
double yDeviation_max = 5.0; // 生长时允许的最大Y偏差
|
||||
double zDeviation_max = 2.0; // 生长时允许的最大Z偏差
|
||||
int maxLineSkipNum = 10; // 生长时允许的最大跳线数
|
||||
double maxSkipDistance = 5.0; // 当maxLineSkipNum为-1时使用
|
||||
double minLTypeTreeLen = 100.0; // 最少的L型节点数目
|
||||
double minVTypeTreeLen = 100.0; // 最少的V型节点数目
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 车轮拱高测量算法参数
|
||||
*/
|
||||
struct WheelMeasureAlgorithmParams
|
||||
{
|
||||
WheelCornerParam cornerParam; // 角点参数
|
||||
WheelLineSegParam lineSegParam; // 线段参数
|
||||
WheelOutlierFilterParam filterParam; // 离群点过滤参数
|
||||
WheelTreeGrowParam growParam; // 树生长参数
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置加载结果
|
||||
*/
|
||||
struct WheelMeasureConfigResult
|
||||
{
|
||||
std::vector<WheelCameraParam> cameras; // 相机列表
|
||||
std::vector<WheelCameraPlaneCalibParam> planeCalibParams; // 相机调平参数列表
|
||||
std::vector<WheelServerInfo> servers; // 服务器列表
|
||||
WheelMeasureAlgorithmParams algorithmParams; // 算法参数
|
||||
VrDebugParam debugParam; // 调试参数(使用公共VrDebugParam)
|
||||
|
||||
int serverPort = 5900; // 服务器端口
|
||||
int tcpPort = 5800; // TCP协议端口
|
||||
|
||||
// 构造函数
|
||||
WheelMeasureConfigResult() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 测量结果数据
|
||||
*/
|
||||
struct WheelMeasureData
|
||||
{
|
||||
int id = 0; // 测量ID
|
||||
double archToCenterHeight = 0.0; // 拱高到中心的高度
|
||||
double archToGroundHeight = 0.0; // 拱高到地面的高度
|
||||
double wheelArchPosX = 0.0; // 拱点X坐标
|
||||
double wheelArchPosY = 0.0; // 拱点Y坐标
|
||||
double wheelArchPosZ = 0.0; // 拱点Z坐标
|
||||
double wheelUpPosX = 0.0; // 上点X坐标
|
||||
double wheelUpPosY = 0.0; // 上点Y坐标
|
||||
double wheelUpPosZ = 0.0; // 上点Z坐标
|
||||
double wheelDownPosX = 0.0; // 下点X坐标
|
||||
double wheelDownPosY = 0.0; // 下点Y坐标
|
||||
double wheelDownPosZ = 0.0; // 下点Z坐标
|
||||
QString timestamp = ""; // 时间戳
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 测量结果
|
||||
*/
|
||||
struct WheelMeasureResult
|
||||
{
|
||||
QString cameraName = ""; // 相机名称
|
||||
QString aliasName = ""; // 别名
|
||||
QImage image; // 图像
|
||||
bool bImageValid = false; // 图像是否有效
|
||||
bool bResultValid = false; // 结果是否有效
|
||||
std::vector<WheelMeasureData> result; // 测量结果列表
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置改变通知接口
|
||||
*/
|
||||
class IVrWheelMeasureConfigChangeNotify
|
||||
{
|
||||
public:
|
||||
virtual ~IVrWheelMeasureConfigChangeNotify() {}
|
||||
|
||||
/**
|
||||
* @brief 配置数据改变通知
|
||||
* @param configResult 新的配置数据
|
||||
*/
|
||||
virtual void OnConfigChanged(const WheelMeasureConfigResult& configResult) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief WheelMeasureConfig接口类
|
||||
*/
|
||||
class IVrWheelMeasureConfig
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
virtual ~IVrWheelMeasureConfig() = default;
|
||||
|
||||
/**
|
||||
* @brief 创建实例
|
||||
* @return 实例
|
||||
*/
|
||||
static bool CreateInstance(IVrWheelMeasureConfig** ppVrConfig);
|
||||
|
||||
/**
|
||||
* @brief 加载配置文件
|
||||
* @param filePath 配置文件路径
|
||||
* @return 加载的配置结果
|
||||
*/
|
||||
virtual WheelMeasureConfigResult LoadConfig(const std::string& filePath) = 0;
|
||||
|
||||
/**
|
||||
* @brief 保存配置文件
|
||||
* @param filePath 配置文件路径
|
||||
* @param configResult 配置结果
|
||||
* @return 是否保存成功
|
||||
*/
|
||||
virtual bool SaveConfig(const std::string& filePath, WheelMeasureConfigResult& configResult) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置配置改变通知回调
|
||||
* @param notify 通知接口指针
|
||||
*/
|
||||
virtual void SetConfigChangeNotify(IVrWheelMeasureConfigChangeNotify* notify) = 0;
|
||||
};
|
||||
|
||||
// 声明元类型,以便在QVariant中使用
|
||||
Q_DECLARE_METATYPE(WheelServerInfo)
|
||||
Q_DECLARE_METATYPE(WheelCameraParam)
|
||||
Q_DECLARE_METATYPE(WheelCameraPlaneCalibParam)
|
||||
Q_DECLARE_METATYPE(WheelMeasureConfigResult)
|
||||
Q_DECLARE_METATYPE(WheelMeasureData)
|
||||
Q_DECLARE_METATYPE(WheelMeasureResult)
|
||||
|
||||
#endif // IVRWHEELMEASURECONFIG_H
|
||||
#ifndef IVRWHEELMEASURECONFIG_H
|
||||
#define IVRWHEELMEASURECONFIG_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <QtCore/QMetaType>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImage>
|
||||
#include "VrCommonConfig.h" // 包含公共配置结构体
|
||||
|
||||
/**
|
||||
* @brief 数据类型枚举
|
||||
*/
|
||||
enum class WheelMeasureDataType {
|
||||
Text = 0x01,
|
||||
Image = 0x02,
|
||||
ReadConfig = 0x03,
|
||||
WriteConfig = 0x04,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务器信息结构
|
||||
*/
|
||||
struct WheelServerInfo
|
||||
{
|
||||
std::string name; // 服务器名称
|
||||
std::string ip; // 服务器IP地址
|
||||
int port = 5000; // 服务器端口
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相机配置参数
|
||||
*/
|
||||
struct WheelCameraParam
|
||||
{
|
||||
int cameraIndex = 0; // 相机索引(1-based)
|
||||
std::string name = ""; // 相机名称
|
||||
std::string cameraIP = ""; // 相机IP地址
|
||||
bool enabled = true; // 是否启用
|
||||
|
||||
// 相机工作参数(参考 GrabBag)
|
||||
double exposure = 100.0; // 曝光时间 (微秒)
|
||||
double gain = 1.0; // 增益值
|
||||
double frameRate = 500.0; // 帧率
|
||||
double swingSpeed = 30.0; // 摆动速度
|
||||
double swingStartAngle = 0.0; // 开始角度
|
||||
double swingStopAngle = 76.0; // 结束角度
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相机调平参数
|
||||
*/
|
||||
struct WheelCameraPlaneCalibParam
|
||||
{
|
||||
int cameraIndex = 0; // 相机索引
|
||||
std::string cameraName = ""; // 相机名称
|
||||
double planeCalib[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1}; // 旋转矩阵(将点云旋转到水平)
|
||||
double planeHeight = -1; // 地面高度
|
||||
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)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 角点参数
|
||||
*/
|
||||
struct WheelCornerParam
|
||||
{
|
||||
double minEndingGap = 20.0; // y方向,最短结束间隙
|
||||
double minEndingGap_z = 20.0; // z方向,最短结束间隙
|
||||
double scale = 50.0; // 计算方向角的窗口比例
|
||||
double cornerTh = 45.0; // 空角阈值,大于此阈值为有效空点
|
||||
double jumpCornerTh_1 = 10.0; // 判断空角是否为跳变的阈值
|
||||
double jumpCornerTh_2 = 60.0; // 判断空角是否为跳变的阈值
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 线段参数
|
||||
*/
|
||||
struct WheelLineSegParam
|
||||
{
|
||||
double segGapTh_y = 5.0; // y方向,最短段间隙阈值
|
||||
double segGapTh_z = 10.0; // z方向,最短段间隙阈值
|
||||
double maxDist = 1.0; // 最大距离
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 离群点过滤参数
|
||||
*/
|
||||
struct WheelOutlierFilterParam
|
||||
{
|
||||
double continuityTh = 20.0; // 连续性阈值
|
||||
double outlierTh = 5.0; // 离群点阈值
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 树生长参数
|
||||
*/
|
||||
struct WheelTreeGrowParam
|
||||
{
|
||||
double yDeviation_max = 5.0; // 生长时允许的最大Y偏差
|
||||
double zDeviation_max = 2.0; // 生长时允许的最大Z偏差
|
||||
int maxLineSkipNum = 10; // 生长时允许的最大跳线数
|
||||
double maxSkipDistance = 5.0; // 当maxLineSkipNum为-1时使用
|
||||
double minLTypeTreeLen = 100.0; // 最少的L型节点数目
|
||||
double minVTypeTreeLen = 100.0; // 最少的V型节点数目
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 车轮拱高测量算法参数
|
||||
*/
|
||||
struct WheelMeasureAlgorithmParams
|
||||
{
|
||||
WheelCornerParam cornerParam; // 角点参数
|
||||
WheelLineSegParam lineSegParam; // 线段参数
|
||||
WheelOutlierFilterParam filterParam; // 离群点过滤参数
|
||||
WheelTreeGrowParam growParam; // 树生长参数
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置加载结果
|
||||
*/
|
||||
struct WheelMeasureConfigResult
|
||||
{
|
||||
std::vector<WheelCameraParam> cameras; // 相机列表
|
||||
std::vector<WheelCameraPlaneCalibParam> planeCalibParams; // 相机调平参数列表
|
||||
std::vector<WheelServerInfo> servers; // 服务器列表
|
||||
WheelMeasureAlgorithmParams algorithmParams; // 算法参数
|
||||
VrDebugParam debugParam; // 调试参数(使用公共VrDebugParam)
|
||||
|
||||
int serverPort = 5900; // 服务器端口
|
||||
int tcpPort = 5800; // TCP协议端口
|
||||
|
||||
// 构造函数
|
||||
WheelMeasureConfigResult() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 测量结果数据
|
||||
*/
|
||||
struct WheelMeasureData
|
||||
{
|
||||
int id = 0; // 测量ID
|
||||
double archToCenterHeight = 0.0; // 拱高到中心的高度
|
||||
double archToGroundHeight = 0.0; // 拱高到地面的高度
|
||||
double wheelArchPosX = 0.0; // 拱点X坐标
|
||||
double wheelArchPosY = 0.0; // 拱点Y坐标
|
||||
double wheelArchPosZ = 0.0; // 拱点Z坐标
|
||||
double wheelUpPosX = 0.0; // 上点X坐标
|
||||
double wheelUpPosY = 0.0; // 上点Y坐标
|
||||
double wheelUpPosZ = 0.0; // 上点Z坐标
|
||||
double wheelDownPosX = 0.0; // 下点X坐标
|
||||
double wheelDownPosY = 0.0; // 下点Y坐标
|
||||
double wheelDownPosZ = 0.0; // 下点Z坐标
|
||||
QString timestamp = ""; // 时间戳
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 测量结果
|
||||
*/
|
||||
struct WheelMeasureResult
|
||||
{
|
||||
QString cameraName = ""; // 相机名称
|
||||
QString aliasName = ""; // 别名
|
||||
QImage image; // 图像
|
||||
bool bImageValid = false; // 图像是否有效
|
||||
bool bResultValid = false; // 结果是否有效
|
||||
int errorCode = 0; // 错误码(0表示成功,401表示工件为空)
|
||||
QString errorMessage = ""; // 错误信息
|
||||
std::vector<WheelMeasureData> result; // 测量结果列表
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置改变通知接口
|
||||
*/
|
||||
class IVrWheelMeasureConfigChangeNotify
|
||||
{
|
||||
public:
|
||||
virtual ~IVrWheelMeasureConfigChangeNotify() {}
|
||||
|
||||
/**
|
||||
* @brief 配置数据改变通知
|
||||
* @param configResult 新的配置数据
|
||||
*/
|
||||
virtual void OnConfigChanged(const WheelMeasureConfigResult& configResult) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief WheelMeasureConfig接口类
|
||||
*/
|
||||
class IVrWheelMeasureConfig
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*/
|
||||
virtual ~IVrWheelMeasureConfig() = default;
|
||||
|
||||
/**
|
||||
* @brief 创建实例
|
||||
* @return 实例
|
||||
*/
|
||||
static bool CreateInstance(IVrWheelMeasureConfig** ppVrConfig);
|
||||
|
||||
/**
|
||||
* @brief 加载配置文件
|
||||
* @param filePath 配置文件路径
|
||||
* @return 加载的配置结果
|
||||
*/
|
||||
virtual WheelMeasureConfigResult LoadConfig(const std::string& filePath) = 0;
|
||||
|
||||
/**
|
||||
* @brief 保存配置文件
|
||||
* @param filePath 配置文件路径
|
||||
* @param configResult 配置结果
|
||||
* @return 是否保存成功
|
||||
*/
|
||||
virtual bool SaveConfig(const std::string& filePath, WheelMeasureConfigResult& configResult) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置配置改变通知回调
|
||||
* @param notify 通知接口指针
|
||||
*/
|
||||
virtual void SetConfigChangeNotify(IVrWheelMeasureConfigChangeNotify* notify) = 0;
|
||||
};
|
||||
|
||||
// 声明元类型,以便在QVariant中使用
|
||||
Q_DECLARE_METATYPE(WheelServerInfo)
|
||||
Q_DECLARE_METATYPE(WheelCameraParam)
|
||||
Q_DECLARE_METATYPE(WheelCameraPlaneCalibParam)
|
||||
Q_DECLARE_METATYPE(WheelMeasureConfigResult)
|
||||
Q_DECLARE_METATYPE(WheelMeasureData)
|
||||
Q_DECLARE_METATYPE(WheelMeasureResult)
|
||||
|
||||
#endif // IVRWHEELMEASURECONFIG_H
|
||||
|
||||
@ -1,435 +1,461 @@
|
||||
#include "VrWheelMeasureConfig.h"
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include "VrLog.h"
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QTextCodec>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
VrWheelMeasureConfig::VrWheelMeasureConfig()
|
||||
: m_notify(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
VrWheelMeasureConfig::~VrWheelMeasureConfig()
|
||||
{
|
||||
}
|
||||
|
||||
// 静态工厂方法
|
||||
bool IVrWheelMeasureConfig::CreateInstance(IVrWheelMeasureConfig** ppVrConfig)
|
||||
{
|
||||
if (!ppVrConfig) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*ppVrConfig = new VrWheelMeasureConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& filePath)
|
||||
{
|
||||
WheelMeasureConfigResult result;
|
||||
|
||||
// 使用QString处理可能包含中文的路径
|
||||
QString qFilePath = QString::fromStdString(filePath);
|
||||
QFile file(qFilePath);
|
||||
|
||||
// 检查文件是否存在并可读
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
LOG_DEBUG("Failed to open file: %s\n", filePath.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
// 使用QXmlStreamReader解析XML内容
|
||||
QXmlStreamReader xml(&file);
|
||||
|
||||
// 读取到根元素
|
||||
if (xml.readNextStartElement()) {
|
||||
if (xml.name() != "WheelMeasureConfig") {
|
||||
xml.raiseError(QObject::tr("Not a WheelMeasureConfig file"));
|
||||
}
|
||||
} else {
|
||||
xml.raiseError(QObject::tr("Failed to read root element"));
|
||||
}
|
||||
|
||||
// 解析XML内容
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
xml.readNext();
|
||||
|
||||
// 解析相机配置
|
||||
if (xml.isStartElement() && xml.name() == "Cameras") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "Camera") {
|
||||
WheelCameraParam camera;
|
||||
camera.cameraIndex = xml.attributes().value("index").toInt();
|
||||
camera.name = xml.attributes().value("name").toString().toStdString();
|
||||
camera.cameraIP = xml.attributes().value("ip").toString().toStdString();
|
||||
camera.enabled = xml.attributes().value("enabled").toInt() != 0;
|
||||
result.cameras.push_back(camera);
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析相机调平参数
|
||||
else if (xml.isStartElement() && xml.name() == "PlaneCalibParams") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "CameraCalib") {
|
||||
WheelCameraPlaneCalibParam calibParam;
|
||||
calibParam.cameraIndex = xml.attributes().value("index").toInt();
|
||||
calibParam.cameraName = xml.attributes().value("name").toString().toStdString();
|
||||
calibParam.planeHeight = xml.attributes().value("planeHeight").toDouble();
|
||||
calibParam.isCalibrated = xml.attributes().value("isCalibrated").toInt() != 0;
|
||||
// 读取误差补偿参数,默认值为-5.0
|
||||
if (xml.attributes().hasAttribute("errorCompensation")) {
|
||||
calibParam.errorCompensation = xml.attributes().value("errorCompensation").toDouble();
|
||||
} else {
|
||||
calibParam.errorCompensation = -5.0;
|
||||
}
|
||||
|
||||
// 读取planeCalib矩阵
|
||||
QString planeCalibStr = xml.attributes().value("planeCalib").toString();
|
||||
QStringList planeCalibList = planeCalibStr.split(",");
|
||||
for (int i = 0; i < 9 && i < planeCalibList.size(); ++i) {
|
||||
calibParam.planeCalib[i] = planeCalibList[i].toDouble();
|
||||
}
|
||||
|
||||
// 读取invRMatrix矩阵
|
||||
QString invRMatrixStr = xml.attributes().value("invRMatrix").toString();
|
||||
QStringList invRMatrixList = invRMatrixStr.split(",");
|
||||
for (int i = 0; i < 9 && i < invRMatrixList.size(); ++i) {
|
||||
calibParam.invRMatrix[i] = invRMatrixList[i].toDouble();
|
||||
}
|
||||
|
||||
result.planeCalibParams.push_back(calibParam);
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析算法参数
|
||||
else if (xml.isStartElement() && xml.name() == "AlgorithmParams") {
|
||||
while (xml.readNextStartElement()) {
|
||||
// 角点参数
|
||||
if (xml.name() == "CornerParam") {
|
||||
result.algorithmParams.cornerParam.minEndingGap =
|
||||
xml.attributes().value("minEndingGap").toDouble();
|
||||
result.algorithmParams.cornerParam.minEndingGap_z =
|
||||
xml.attributes().value("minEndingGap_z").toDouble();
|
||||
result.algorithmParams.cornerParam.scale =
|
||||
xml.attributes().value("scale").toDouble();
|
||||
result.algorithmParams.cornerParam.cornerTh =
|
||||
xml.attributes().value("cornerTh").toDouble();
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_1 =
|
||||
xml.attributes().value("jumpCornerTh_1").toDouble();
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_2 =
|
||||
xml.attributes().value("jumpCornerTh_2").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.cornerParam.minEndingGap == 0.0) {
|
||||
result.algorithmParams.cornerParam.minEndingGap = 3.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.minEndingGap_z == 0.0) {
|
||||
result.algorithmParams.cornerParam.minEndingGap_z = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.scale == 0.0) {
|
||||
result.algorithmParams.cornerParam.scale = 10.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.cornerTh == 0.0) {
|
||||
result.algorithmParams.cornerParam.cornerTh = 130.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.jumpCornerTh_1 == 0.0) {
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_1 = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.jumpCornerTh_2 == 0.0) {
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_2 = 2.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 线段参数
|
||||
else if (xml.name() == "LineSegParam") {
|
||||
result.algorithmParams.lineSegParam.segGapTh_y =
|
||||
xml.attributes().value("segGapTh_y").toDouble();
|
||||
result.algorithmParams.lineSegParam.segGapTh_z =
|
||||
xml.attributes().value("segGapTh_z").toDouble();
|
||||
result.algorithmParams.lineSegParam.maxDist =
|
||||
xml.attributes().value("maxDist").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.lineSegParam.segGapTh_y == 0.0) {
|
||||
result.algorithmParams.lineSegParam.segGapTh_y = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.lineSegParam.segGapTh_z == 0.0) {
|
||||
result.algorithmParams.lineSegParam.segGapTh_z = 10.0;
|
||||
}
|
||||
if (result.algorithmParams.lineSegParam.maxDist == 0.0) {
|
||||
result.algorithmParams.lineSegParam.maxDist = 50.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 离群点过滤参数
|
||||
else if (xml.name() == "OutlierFilterParam") {
|
||||
result.algorithmParams.filterParam.continuityTh =
|
||||
xml.attributes().value("continuityTh").toDouble();
|
||||
result.algorithmParams.filterParam.outlierTh =
|
||||
xml.attributes().value("outlierTh").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.filterParam.continuityTh == 0.0) {
|
||||
result.algorithmParams.filterParam.continuityTh = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.filterParam.outlierTh == 0.0) {
|
||||
result.algorithmParams.filterParam.outlierTh = 3.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 树生长参数
|
||||
else if (xml.name() == "TreeGrowParam") {
|
||||
result.algorithmParams.growParam.yDeviation_max =
|
||||
xml.attributes().value("yDeviation_max").toDouble();
|
||||
result.algorithmParams.growParam.zDeviation_max =
|
||||
xml.attributes().value("zDeviation_max").toDouble();
|
||||
result.algorithmParams.growParam.maxLineSkipNum =
|
||||
xml.attributes().value("maxLineSkipNum").toInt();
|
||||
result.algorithmParams.growParam.maxSkipDistance =
|
||||
xml.attributes().value("maxSkipDistance").toDouble();
|
||||
result.algorithmParams.growParam.minLTypeTreeLen =
|
||||
xml.attributes().value("minLTypeTreeLen").toDouble();
|
||||
result.algorithmParams.growParam.minVTypeTreeLen =
|
||||
xml.attributes().value("minVTypeTreeLen").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.growParam.yDeviation_max == 0.0) {
|
||||
result.algorithmParams.growParam.yDeviation_max = 20.0;
|
||||
}
|
||||
if (result.algorithmParams.growParam.zDeviation_max == 0.0) {
|
||||
result.algorithmParams.growParam.zDeviation_max = 30.0;
|
||||
}
|
||||
if (result.algorithmParams.growParam.maxLineSkipNum == 0) {
|
||||
result.algorithmParams.growParam.maxLineSkipNum = 5;
|
||||
}
|
||||
if (result.algorithmParams.growParam.minLTypeTreeLen == 0.0) {
|
||||
result.algorithmParams.growParam.minLTypeTreeLen = 10.0;
|
||||
}
|
||||
if (result.algorithmParams.growParam.minVTypeTreeLen == 0.0) {
|
||||
result.algorithmParams.growParam.minVTypeTreeLen = 10.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析调试参数
|
||||
else if (xml.isStartElement() && xml.name() == "DebugParam") {
|
||||
result.debugParam.enableDebug = xml.attributes().value("enableDebug").toInt();
|
||||
result.debugParam.savePointCloud = xml.attributes().value("savePointCloud").toInt();
|
||||
result.debugParam.saveDebugImage = xml.attributes().value("saveDebugImage").toInt();
|
||||
result.debugParam.printDetailLog = xml.attributes().value("printDetailLog").toInt();
|
||||
result.debugParam.debugOutputPath = xml.attributes().value("debugOutputPath").toString().toStdString();
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
// 解析服务端配置
|
||||
else if (xml.isStartElement() && xml.name() == "LocalServerConfig") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "ServerPort") {
|
||||
result.serverPort = xml.attributes().value("port").toInt();
|
||||
xml.skipCurrentElement();
|
||||
} else if (xml.name() == "TcpPort") {
|
||||
result.tcpPort = xml.attributes().value("port").toInt();
|
||||
if (result.tcpPort == 0) {
|
||||
result.tcpPort = 5800; // 默认值
|
||||
}
|
||||
xml.skipCurrentElement();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析服务器列表
|
||||
else if (xml.isStartElement() && xml.name() == "Servers") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "Server") {
|
||||
WheelServerInfo server;
|
||||
server.name = xml.attributes().value("name").toString().toStdString();
|
||||
server.ip = xml.attributes().value("ip").toString().toStdString();
|
||||
server.port = xml.attributes().value("port").toInt();
|
||||
if (server.port == 0) {
|
||||
server.port = 5800; // 默认端口
|
||||
}
|
||||
result.servers.push_back(server);
|
||||
xml.skipCurrentElement();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// 检查解析错误
|
||||
if (xml.hasError()) {
|
||||
LOG_ERROR("XML parsing error: %s\n", xml.errorString().toStdString().c_str());
|
||||
return WheelMeasureConfigResult(); // 返回空结果
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureConfigResult& configResult)
|
||||
{
|
||||
// 使用QString处理可能包含中文的路径
|
||||
QString qFilePath = QString::fromStdString(filePath);
|
||||
QFile file(qFilePath);
|
||||
|
||||
// 打开文件进行写入
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
LOG_DEBUG("Failed to open file for writing: %s\n", filePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用QXmlStreamWriter写入XML内容
|
||||
QXmlStreamWriter xml(&file);
|
||||
xml.setAutoFormatting(true);
|
||||
xml.setCodec("UTF-8");
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement("WheelMeasureConfig");
|
||||
|
||||
// 保存相机配置
|
||||
xml.writeStartElement("Cameras");
|
||||
for (const auto& camera : configResult.cameras) {
|
||||
xml.writeStartElement("Camera");
|
||||
xml.writeAttribute("index", QString::number(camera.cameraIndex));
|
||||
xml.writeAttribute("name", QString::fromStdString(camera.name));
|
||||
xml.writeAttribute("ip", QString::fromStdString(camera.cameraIP));
|
||||
xml.writeAttribute("enabled", QString::number(camera.enabled ? 1 : 0));
|
||||
xml.writeEndElement(); // Camera
|
||||
}
|
||||
xml.writeEndElement(); // Cameras
|
||||
|
||||
// 保存相机调平参数
|
||||
xml.writeStartElement("PlaneCalibParams");
|
||||
for (const auto& calibParam : configResult.planeCalibParams) {
|
||||
xml.writeStartElement("CameraCalib");
|
||||
xml.writeAttribute("index", QString::number(calibParam.cameraIndex));
|
||||
xml.writeAttribute("name", QString::fromStdString(calibParam.cameraName));
|
||||
xml.writeAttribute("planeHeight", QString::number(calibParam.planeHeight, 'f', 6));
|
||||
xml.writeAttribute("isCalibrated", QString::number(calibParam.isCalibrated ? 1 : 0));
|
||||
xml.writeAttribute("errorCompensation", QString::number(calibParam.errorCompensation, 'f', 2));
|
||||
|
||||
// 保存planeCalib矩阵
|
||||
QStringList planeCalibList;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
planeCalibList.append(QString::number(calibParam.planeCalib[i], 'f', 8));
|
||||
}
|
||||
xml.writeAttribute("planeCalib", planeCalibList.join(","));
|
||||
|
||||
// 保存invRMatrix矩阵
|
||||
QStringList invRMatrixList;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
invRMatrixList.append(QString::number(calibParam.invRMatrix[i], 'f', 8));
|
||||
}
|
||||
xml.writeAttribute("invRMatrix", invRMatrixList.join(","));
|
||||
|
||||
xml.writeEndElement(); // CameraCalib
|
||||
}
|
||||
xml.writeEndElement(); // PlaneCalibParams
|
||||
|
||||
// 保存算法参数
|
||||
xml.writeStartElement("AlgorithmParams");
|
||||
|
||||
// 角点参数
|
||||
xml.writeStartElement("CornerParam");
|
||||
xml.writeAttribute("minEndingGap", QString::number(configResult.algorithmParams.cornerParam.minEndingGap));
|
||||
xml.writeAttribute("minEndingGap_z", QString::number(configResult.algorithmParams.cornerParam.minEndingGap_z));
|
||||
xml.writeAttribute("scale", QString::number(configResult.algorithmParams.cornerParam.scale));
|
||||
xml.writeAttribute("cornerTh", QString::number(configResult.algorithmParams.cornerParam.cornerTh));
|
||||
xml.writeAttribute("jumpCornerTh_1", QString::number(configResult.algorithmParams.cornerParam.jumpCornerTh_1));
|
||||
xml.writeAttribute("jumpCornerTh_2", QString::number(configResult.algorithmParams.cornerParam.jumpCornerTh_2));
|
||||
xml.writeEndElement(); // CornerParam
|
||||
|
||||
// 线段参数
|
||||
xml.writeStartElement("LineSegParam");
|
||||
xml.writeAttribute("segGapTh_y", QString::number(configResult.algorithmParams.lineSegParam.segGapTh_y));
|
||||
xml.writeAttribute("segGapTh_z", QString::number(configResult.algorithmParams.lineSegParam.segGapTh_z));
|
||||
xml.writeAttribute("maxDist", QString::number(configResult.algorithmParams.lineSegParam.maxDist));
|
||||
xml.writeEndElement(); // LineSegParam
|
||||
|
||||
// 离群点过滤参数
|
||||
xml.writeStartElement("OutlierFilterParam");
|
||||
xml.writeAttribute("continuityTh", QString::number(configResult.algorithmParams.filterParam.continuityTh));
|
||||
xml.writeAttribute("outlierTh", QString::number(configResult.algorithmParams.filterParam.outlierTh));
|
||||
xml.writeEndElement(); // OutlierFilterParam
|
||||
|
||||
// 树生长参数
|
||||
xml.writeStartElement("TreeGrowParam");
|
||||
xml.writeAttribute("yDeviation_max", QString::number(configResult.algorithmParams.growParam.yDeviation_max));
|
||||
xml.writeAttribute("zDeviation_max", QString::number(configResult.algorithmParams.growParam.zDeviation_max));
|
||||
xml.writeAttribute("maxLineSkipNum", QString::number(configResult.algorithmParams.growParam.maxLineSkipNum));
|
||||
xml.writeAttribute("maxSkipDistance", QString::number(configResult.algorithmParams.growParam.maxSkipDistance));
|
||||
xml.writeAttribute("minLTypeTreeLen", QString::number(configResult.algorithmParams.growParam.minLTypeTreeLen));
|
||||
xml.writeAttribute("minVTypeTreeLen", QString::number(configResult.algorithmParams.growParam.minVTypeTreeLen));
|
||||
xml.writeEndElement(); // TreeGrowParam
|
||||
|
||||
xml.writeEndElement(); // AlgorithmParams
|
||||
|
||||
// 保存调试参数
|
||||
xml.writeStartElement("DebugParam");
|
||||
xml.writeAttribute("enableDebug", QString::number(configResult.debugParam.enableDebug));
|
||||
xml.writeAttribute("savePointCloud", QString::number(configResult.debugParam.savePointCloud));
|
||||
xml.writeAttribute("saveDebugImage", QString::number(configResult.debugParam.saveDebugImage));
|
||||
xml.writeAttribute("printDetailLog", QString::number(configResult.debugParam.printDetailLog));
|
||||
xml.writeAttribute("debugOutputPath", QString::fromStdString(configResult.debugParam.debugOutputPath));
|
||||
xml.writeEndElement(); // DebugParam
|
||||
|
||||
// 保存服务端配置
|
||||
xml.writeStartElement("LocalServerConfig");
|
||||
xml.writeStartElement("ServerPort");
|
||||
xml.writeAttribute("port", QString::number(configResult.serverPort));
|
||||
xml.writeEndElement(); // ServerPort
|
||||
xml.writeStartElement("TcpPort");
|
||||
xml.writeAttribute("port", QString::number(configResult.tcpPort));
|
||||
xml.writeEndElement(); // TcpPort
|
||||
xml.writeEndElement(); // LocalServerConfig
|
||||
|
||||
// 保存服务器列表
|
||||
xml.writeStartElement("Servers");
|
||||
for (const auto& server : configResult.servers) {
|
||||
xml.writeStartElement("Server");
|
||||
xml.writeAttribute("name", QString::fromStdString(server.name));
|
||||
xml.writeAttribute("ip", QString::fromStdString(server.ip));
|
||||
xml.writeAttribute("port", QString::number(server.port));
|
||||
xml.writeEndElement(); // Server
|
||||
}
|
||||
xml.writeEndElement(); // Servers
|
||||
|
||||
xml.writeEndElement(); // WheelMeasureConfig
|
||||
xml.writeEndDocument();
|
||||
|
||||
file.close();
|
||||
|
||||
// 通知配置改变
|
||||
if (m_notify) {
|
||||
m_notify->OnConfigChanged(configResult);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VrWheelMeasureConfig::SetConfigChangeNotify(IVrWheelMeasureConfigChangeNotify* notify)
|
||||
{
|
||||
m_notify = notify;
|
||||
}
|
||||
#include "VrWheelMeasureConfig.h"
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include "VrLog.h"
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QTextCodec>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
VrWheelMeasureConfig::VrWheelMeasureConfig()
|
||||
: m_notify(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
VrWheelMeasureConfig::~VrWheelMeasureConfig()
|
||||
{
|
||||
}
|
||||
|
||||
// 静态工厂方法
|
||||
bool IVrWheelMeasureConfig::CreateInstance(IVrWheelMeasureConfig** ppVrConfig)
|
||||
{
|
||||
if (!ppVrConfig) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*ppVrConfig = new VrWheelMeasureConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& filePath)
|
||||
{
|
||||
WheelMeasureConfigResult result;
|
||||
|
||||
// 使用QString处理可能包含中文的路径
|
||||
QString qFilePath = QString::fromStdString(filePath);
|
||||
QFile file(qFilePath);
|
||||
|
||||
// 检查文件是否存在并可读
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
LOG_DEBUG("Failed to open file: %s\n", filePath.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
// 使用QXmlStreamReader解析XML内容
|
||||
QXmlStreamReader xml(&file);
|
||||
|
||||
// 读取到根元素
|
||||
if (xml.readNextStartElement()) {
|
||||
if (xml.name() != "WheelMeasureConfig") {
|
||||
xml.raiseError(QObject::tr("Not a WheelMeasureConfig file"));
|
||||
}
|
||||
} else {
|
||||
xml.raiseError(QObject::tr("Failed to read root element"));
|
||||
}
|
||||
|
||||
// 解析XML内容
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
xml.readNext();
|
||||
|
||||
// 解析相机配置
|
||||
if (xml.isStartElement() && xml.name() == "Cameras") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "Camera") {
|
||||
WheelCameraParam camera;
|
||||
camera.cameraIndex = xml.attributes().value("index").toInt();
|
||||
camera.name = xml.attributes().value("name").toString().toStdString();
|
||||
camera.cameraIP = xml.attributes().value("ip").toString().toStdString();
|
||||
camera.enabled = xml.attributes().value("enabled").toInt() != 0;
|
||||
result.cameras.push_back(camera);
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析相机调平参数
|
||||
else if (xml.isStartElement() && xml.name() == "PlaneCalibParams") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "CameraCalib") {
|
||||
WheelCameraPlaneCalibParam calibParam;
|
||||
calibParam.cameraIndex = xml.attributes().value("index").toInt();
|
||||
calibParam.cameraName = xml.attributes().value("name").toString().toStdString();
|
||||
calibParam.planeHeight = xml.attributes().value("planeHeight").toDouble();
|
||||
calibParam.isCalibrated = xml.attributes().value("isCalibrated").toInt() != 0;
|
||||
// 读取误差补偿参数,默认值为-5.0
|
||||
if (xml.attributes().hasAttribute("errorCompensation")) {
|
||||
calibParam.errorCompensation = xml.attributes().value("errorCompensation").toDouble();
|
||||
} else {
|
||||
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(",");
|
||||
for (int i = 0; i < 9 && i < planeCalibList.size(); ++i) {
|
||||
calibParam.planeCalib[i] = planeCalibList[i].toDouble();
|
||||
}
|
||||
|
||||
// 读取invRMatrix矩阵
|
||||
QString invRMatrixStr = xml.attributes().value("invRMatrix").toString();
|
||||
QStringList invRMatrixList = invRMatrixStr.split(",");
|
||||
for (int i = 0; i < 9 && i < invRMatrixList.size(); ++i) {
|
||||
calibParam.invRMatrix[i] = invRMatrixList[i].toDouble();
|
||||
}
|
||||
|
||||
result.planeCalibParams.push_back(calibParam);
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析算法参数
|
||||
else if (xml.isStartElement() && xml.name() == "AlgorithmParams") {
|
||||
while (xml.readNextStartElement()) {
|
||||
// 角点参数
|
||||
if (xml.name() == "CornerParam") {
|
||||
result.algorithmParams.cornerParam.minEndingGap =
|
||||
xml.attributes().value("minEndingGap").toDouble();
|
||||
result.algorithmParams.cornerParam.minEndingGap_z =
|
||||
xml.attributes().value("minEndingGap_z").toDouble();
|
||||
result.algorithmParams.cornerParam.scale =
|
||||
xml.attributes().value("scale").toDouble();
|
||||
result.algorithmParams.cornerParam.cornerTh =
|
||||
xml.attributes().value("cornerTh").toDouble();
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_1 =
|
||||
xml.attributes().value("jumpCornerTh_1").toDouble();
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_2 =
|
||||
xml.attributes().value("jumpCornerTh_2").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.cornerParam.minEndingGap == 0.0) {
|
||||
result.algorithmParams.cornerParam.minEndingGap = 3.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.minEndingGap_z == 0.0) {
|
||||
result.algorithmParams.cornerParam.minEndingGap_z = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.scale == 0.0) {
|
||||
result.algorithmParams.cornerParam.scale = 10.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.cornerTh == 0.0) {
|
||||
result.algorithmParams.cornerParam.cornerTh = 130.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.jumpCornerTh_1 == 0.0) {
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_1 = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.cornerParam.jumpCornerTh_2 == 0.0) {
|
||||
result.algorithmParams.cornerParam.jumpCornerTh_2 = 2.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 线段参数
|
||||
else if (xml.name() == "LineSegParam") {
|
||||
result.algorithmParams.lineSegParam.segGapTh_y =
|
||||
xml.attributes().value("segGapTh_y").toDouble();
|
||||
result.algorithmParams.lineSegParam.segGapTh_z =
|
||||
xml.attributes().value("segGapTh_z").toDouble();
|
||||
result.algorithmParams.lineSegParam.maxDist =
|
||||
xml.attributes().value("maxDist").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.lineSegParam.segGapTh_y == 0.0) {
|
||||
result.algorithmParams.lineSegParam.segGapTh_y = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.lineSegParam.segGapTh_z == 0.0) {
|
||||
result.algorithmParams.lineSegParam.segGapTh_z = 10.0;
|
||||
}
|
||||
if (result.algorithmParams.lineSegParam.maxDist == 0.0) {
|
||||
result.algorithmParams.lineSegParam.maxDist = 50.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 离群点过滤参数
|
||||
else if (xml.name() == "OutlierFilterParam") {
|
||||
result.algorithmParams.filterParam.continuityTh =
|
||||
xml.attributes().value("continuityTh").toDouble();
|
||||
result.algorithmParams.filterParam.outlierTh =
|
||||
xml.attributes().value("outlierTh").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.filterParam.continuityTh == 0.0) {
|
||||
result.algorithmParams.filterParam.continuityTh = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.filterParam.outlierTh == 0.0) {
|
||||
result.algorithmParams.filterParam.outlierTh = 3.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 树生长参数
|
||||
else if (xml.name() == "TreeGrowParam") {
|
||||
result.algorithmParams.growParam.yDeviation_max =
|
||||
xml.attributes().value("yDeviation_max").toDouble();
|
||||
result.algorithmParams.growParam.zDeviation_max =
|
||||
xml.attributes().value("zDeviation_max").toDouble();
|
||||
result.algorithmParams.growParam.maxLineSkipNum =
|
||||
xml.attributes().value("maxLineSkipNum").toInt();
|
||||
result.algorithmParams.growParam.maxSkipDistance =
|
||||
xml.attributes().value("maxSkipDistance").toDouble();
|
||||
result.algorithmParams.growParam.minLTypeTreeLen =
|
||||
xml.attributes().value("minLTypeTreeLen").toDouble();
|
||||
result.algorithmParams.growParam.minVTypeTreeLen =
|
||||
xml.attributes().value("minVTypeTreeLen").toDouble();
|
||||
|
||||
// 设置默认值
|
||||
if (result.algorithmParams.growParam.yDeviation_max == 0.0) {
|
||||
result.algorithmParams.growParam.yDeviation_max = 20.0;
|
||||
}
|
||||
if (result.algorithmParams.growParam.zDeviation_max == 0.0) {
|
||||
result.algorithmParams.growParam.zDeviation_max = 30.0;
|
||||
}
|
||||
if (result.algorithmParams.growParam.maxLineSkipNum == 0) {
|
||||
result.algorithmParams.growParam.maxLineSkipNum = 5;
|
||||
}
|
||||
if (result.algorithmParams.growParam.minLTypeTreeLen == 0.0) {
|
||||
result.algorithmParams.growParam.minLTypeTreeLen = 10.0;
|
||||
}
|
||||
if (result.algorithmParams.growParam.minVTypeTreeLen == 0.0) {
|
||||
result.algorithmParams.growParam.minVTypeTreeLen = 10.0;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析调试参数
|
||||
else if (xml.isStartElement() && xml.name() == "DebugParam") {
|
||||
result.debugParam.enableDebug = xml.attributes().value("enableDebug").toInt();
|
||||
result.debugParam.savePointCloud = xml.attributes().value("savePointCloud").toInt();
|
||||
result.debugParam.saveDebugImage = xml.attributes().value("saveDebugImage").toInt();
|
||||
result.debugParam.printDetailLog = xml.attributes().value("printDetailLog").toInt();
|
||||
result.debugParam.debugOutputPath = xml.attributes().value("debugOutputPath").toString().toStdString();
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
// 解析服务端配置
|
||||
else if (xml.isStartElement() && xml.name() == "LocalServerConfig") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "ServerPort") {
|
||||
result.serverPort = xml.attributes().value("port").toInt();
|
||||
xml.skipCurrentElement();
|
||||
} else if (xml.name() == "TcpPort") {
|
||||
result.tcpPort = xml.attributes().value("port").toInt();
|
||||
if (result.tcpPort == 0) {
|
||||
result.tcpPort = 5800; // 默认值
|
||||
}
|
||||
xml.skipCurrentElement();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析服务器列表
|
||||
else if (xml.isStartElement() && xml.name() == "Servers") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "Server") {
|
||||
WheelServerInfo server;
|
||||
server.name = xml.attributes().value("name").toString().toStdString();
|
||||
server.ip = xml.attributes().value("ip").toString().toStdString();
|
||||
server.port = xml.attributes().value("port").toInt();
|
||||
if (server.port == 0) {
|
||||
server.port = 5800; // 默认端口
|
||||
}
|
||||
result.servers.push_back(server);
|
||||
xml.skipCurrentElement();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// 检查解析错误
|
||||
if (xml.hasError()) {
|
||||
LOG_ERROR("XML parsing error: %s\n", xml.errorString().toStdString().c_str());
|
||||
return WheelMeasureConfigResult(); // 返回空结果
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureConfigResult& configResult)
|
||||
{
|
||||
// 使用QString处理可能包含中文的路径
|
||||
QString qFilePath = QString::fromStdString(filePath);
|
||||
QFile file(qFilePath);
|
||||
|
||||
// 打开文件进行写入
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
LOG_DEBUG("Failed to open file for writing: %s\n", filePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 使用QXmlStreamWriter写入XML内容
|
||||
QXmlStreamWriter xml(&file);
|
||||
xml.setAutoFormatting(true);
|
||||
xml.setCodec("UTF-8");
|
||||
xml.writeStartDocument();
|
||||
xml.writeStartElement("WheelMeasureConfig");
|
||||
|
||||
// 保存相机配置
|
||||
xml.writeStartElement("Cameras");
|
||||
for (const auto& camera : configResult.cameras) {
|
||||
xml.writeStartElement("Camera");
|
||||
xml.writeAttribute("index", QString::number(camera.cameraIndex));
|
||||
xml.writeAttribute("name", QString::fromStdString(camera.name));
|
||||
xml.writeAttribute("ip", QString::fromStdString(camera.cameraIP));
|
||||
xml.writeAttribute("enabled", QString::number(camera.enabled ? 1 : 0));
|
||||
xml.writeEndElement(); // Camera
|
||||
}
|
||||
xml.writeEndElement(); // Cameras
|
||||
|
||||
// 保存相机调平参数
|
||||
xml.writeStartElement("PlaneCalibParams");
|
||||
for (const auto& calibParam : configResult.planeCalibParams) {
|
||||
xml.writeStartElement("CameraCalib");
|
||||
xml.writeAttribute("index", QString::number(calibParam.cameraIndex));
|
||||
xml.writeAttribute("name", QString::fromStdString(calibParam.cameraName));
|
||||
xml.writeAttribute("planeHeight", QString::number(calibParam.planeHeight, 'f', 6));
|
||||
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) {
|
||||
planeCalibList.append(QString::number(calibParam.planeCalib[i], 'f', 8));
|
||||
}
|
||||
xml.writeAttribute("planeCalib", planeCalibList.join(","));
|
||||
|
||||
// 保存invRMatrix矩阵
|
||||
QStringList invRMatrixList;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
invRMatrixList.append(QString::number(calibParam.invRMatrix[i], 'f', 8));
|
||||
}
|
||||
xml.writeAttribute("invRMatrix", invRMatrixList.join(","));
|
||||
|
||||
xml.writeEndElement(); // CameraCalib
|
||||
}
|
||||
xml.writeEndElement(); // PlaneCalibParams
|
||||
|
||||
// 保存算法参数
|
||||
xml.writeStartElement("AlgorithmParams");
|
||||
|
||||
// 角点参数
|
||||
xml.writeStartElement("CornerParam");
|
||||
xml.writeAttribute("minEndingGap", QString::number(configResult.algorithmParams.cornerParam.minEndingGap));
|
||||
xml.writeAttribute("minEndingGap_z", QString::number(configResult.algorithmParams.cornerParam.minEndingGap_z));
|
||||
xml.writeAttribute("scale", QString::number(configResult.algorithmParams.cornerParam.scale));
|
||||
xml.writeAttribute("cornerTh", QString::number(configResult.algorithmParams.cornerParam.cornerTh));
|
||||
xml.writeAttribute("jumpCornerTh_1", QString::number(configResult.algorithmParams.cornerParam.jumpCornerTh_1));
|
||||
xml.writeAttribute("jumpCornerTh_2", QString::number(configResult.algorithmParams.cornerParam.jumpCornerTh_2));
|
||||
xml.writeEndElement(); // CornerParam
|
||||
|
||||
// 线段参数
|
||||
xml.writeStartElement("LineSegParam");
|
||||
xml.writeAttribute("segGapTh_y", QString::number(configResult.algorithmParams.lineSegParam.segGapTh_y));
|
||||
xml.writeAttribute("segGapTh_z", QString::number(configResult.algorithmParams.lineSegParam.segGapTh_z));
|
||||
xml.writeAttribute("maxDist", QString::number(configResult.algorithmParams.lineSegParam.maxDist));
|
||||
xml.writeEndElement(); // LineSegParam
|
||||
|
||||
// 离群点过滤参数
|
||||
xml.writeStartElement("OutlierFilterParam");
|
||||
xml.writeAttribute("continuityTh", QString::number(configResult.algorithmParams.filterParam.continuityTh));
|
||||
xml.writeAttribute("outlierTh", QString::number(configResult.algorithmParams.filterParam.outlierTh));
|
||||
xml.writeEndElement(); // OutlierFilterParam
|
||||
|
||||
// 树生长参数
|
||||
xml.writeStartElement("TreeGrowParam");
|
||||
xml.writeAttribute("yDeviation_max", QString::number(configResult.algorithmParams.growParam.yDeviation_max));
|
||||
xml.writeAttribute("zDeviation_max", QString::number(configResult.algorithmParams.growParam.zDeviation_max));
|
||||
xml.writeAttribute("maxLineSkipNum", QString::number(configResult.algorithmParams.growParam.maxLineSkipNum));
|
||||
xml.writeAttribute("maxSkipDistance", QString::number(configResult.algorithmParams.growParam.maxSkipDistance));
|
||||
xml.writeAttribute("minLTypeTreeLen", QString::number(configResult.algorithmParams.growParam.minLTypeTreeLen));
|
||||
xml.writeAttribute("minVTypeTreeLen", QString::number(configResult.algorithmParams.growParam.minVTypeTreeLen));
|
||||
xml.writeEndElement(); // TreeGrowParam
|
||||
|
||||
xml.writeEndElement(); // AlgorithmParams
|
||||
|
||||
// 保存调试参数
|
||||
xml.writeStartElement("DebugParam");
|
||||
xml.writeAttribute("enableDebug", QString::number(configResult.debugParam.enableDebug));
|
||||
xml.writeAttribute("savePointCloud", QString::number(configResult.debugParam.savePointCloud));
|
||||
xml.writeAttribute("saveDebugImage", QString::number(configResult.debugParam.saveDebugImage));
|
||||
xml.writeAttribute("printDetailLog", QString::number(configResult.debugParam.printDetailLog));
|
||||
xml.writeAttribute("debugOutputPath", QString::fromStdString(configResult.debugParam.debugOutputPath));
|
||||
xml.writeEndElement(); // DebugParam
|
||||
|
||||
// 保存服务端配置
|
||||
xml.writeStartElement("LocalServerConfig");
|
||||
xml.writeStartElement("ServerPort");
|
||||
xml.writeAttribute("port", QString::number(configResult.serverPort));
|
||||
xml.writeEndElement(); // ServerPort
|
||||
xml.writeStartElement("TcpPort");
|
||||
xml.writeAttribute("port", QString::number(configResult.tcpPort));
|
||||
xml.writeEndElement(); // TcpPort
|
||||
xml.writeEndElement(); // LocalServerConfig
|
||||
|
||||
// 保存服务器列表
|
||||
xml.writeStartElement("Servers");
|
||||
for (const auto& server : configResult.servers) {
|
||||
xml.writeStartElement("Server");
|
||||
xml.writeAttribute("name", QString::fromStdString(server.name));
|
||||
xml.writeAttribute("ip", QString::fromStdString(server.ip));
|
||||
xml.writeAttribute("port", QString::number(server.port));
|
||||
xml.writeEndElement(); // Server
|
||||
}
|
||||
xml.writeEndElement(); // Servers
|
||||
|
||||
xml.writeEndElement(); // WheelMeasureConfig
|
||||
xml.writeEndDocument();
|
||||
|
||||
file.close();
|
||||
|
||||
// 通知配置改变
|
||||
if (m_notify) {
|
||||
m_notify->OnConfigChanged(configResult);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VrWheelMeasureConfig::SetConfigChangeNotify(IVrWheelMeasureConfigChangeNotify* notify)
|
||||
{
|
||||
m_notify = notify;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,22 +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
|
||||
#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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,30 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#define SG_ERR_3D_DATA_INVLD -1000
|
||||
#define SG_ERR_3D_DATA_NULL -1001
|
||||
#define SG_ERR_FOUND_NO_TOP_PLANE -1002
|
||||
#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
|
||||
#define SX_ERR_INVLD_HTREE_NUM -2002
|
||||
#define SX_ERR_INVLD_EDGE_LINK_NUM -2003
|
||||
#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_MARK_NUM -2008
|
||||
|
||||
//定子抓取
|
||||
#define SX_ERR_INVLID_CUTTING_Z -2101
|
||||
#define SX_ERR_ZERO_OBJ -2102
|
||||
|
||||
//拆包
|
||||
#define SX_BAG_TRAY_EMPTY -2201
|
||||
|
||||
//汽车轮眉高度测量
|
||||
#define SX_ERR_INVALID_ARC -2301
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SG_ERR_3D_DATA_INVLD -1000
|
||||
#define SG_ERR_3D_DATA_NULL -1001
|
||||
#define SG_ERR_FOUND_NO_TOP_PLANE -1002
|
||||
#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
|
||||
#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
|
||||
#define SX_ERR_INVLD_HTREE_NUM -2002
|
||||
#define SX_ERR_INVLD_EDGE_LINK_NUM -2003
|
||||
#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_MARK_NUM -2008
|
||||
|
||||
//¶¨×Óץȡ
|
||||
#define SX_ERR_INVLID_CUTTING_Z -2101
|
||||
#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
|
||||
|
||||
//Æû³µÂÖü¸ß¶È²âÁ¿
|
||||
#define SX_ERR_INVALID_ARC -2301
|
||||
|
||||
//ÌÇ´ü×Ó²ðÏß
|
||||
#define SX_ERR_NO_MARK -2401
|
||||
|
||||
|
||||
@ -1,44 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "SG_algo_Export.h"
|
||||
#include <vector>
|
||||
|
||||
#define _OUTPUT_DEBUG_DATA 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SVzNL3DPoint wheelArchPos;
|
||||
SVzNL3DPoint wheelUpPos;
|
||||
SVzNL3DPoint wheelDownPos;
|
||||
SVzNL3DPoint arcLine[2];
|
||||
SVzNL3DPoint upLine[2];
|
||||
SVzNL3DPoint downLine[2];
|
||||
SVzNL3DPoint centerLine[2];
|
||||
double archToCenterHeigth;
|
||||
double archToGroundHeigth;
|
||||
}WD_wheelArchInfo;
|
||||
|
||||
//读版本号
|
||||
SG_APISHARED_EXPORT const char* wd_wheelArchHeigthMeasureVersion(void);
|
||||
|
||||
//相机水平安装计算地面调平参数。。
|
||||
//相机Z轴基本平行地面时,需要以地面为参照,将相机调水平
|
||||
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
|
||||
SG_APISHARED_EXPORT SSG_planeCalibPara wd_horizonCamera_getGroundCalibPara(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
|
||||
|
||||
//相机水平时姿态调平,并去除地面
|
||||
SG_APISHARED_EXPORT void wd_horizonCamera_lineDataR(
|
||||
std::vector< SVzNL3DPosition>& a_line,
|
||||
const double* camPoseR,
|
||||
double groundH);
|
||||
|
||||
//提取工件角点及定位长度信息
|
||||
SG_APISHARED_EXPORT WD_wheelArchInfo wd_wheelArchHeigthMeasure(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
|
||||
const SSG_cornerParam cornerPara,
|
||||
const SSG_lineSegParam lineSegPara,
|
||||
const SSG_outlierFilterParam filterParam,
|
||||
const SSG_treeGrowParam growParam,
|
||||
const SSG_planeCalibPara groundCalibPara,
|
||||
int* errCode);
|
||||
#pragma once
|
||||
|
||||
#include "SG_algo_Export.h"
|
||||
#include <vector>
|
||||
|
||||
#define _OUTPUT_DEBUG_DATA 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SVzNL3DPoint wheelArchPos;
|
||||
SVzNL3DPoint wheelUpPos;
|
||||
SVzNL3DPoint wheelDownPos;
|
||||
SVzNL3DPoint arcLine[2];
|
||||
SVzNL3DPoint upLine[2];
|
||||
SVzNL3DPoint downLine[2];
|
||||
SVzNL3DPoint centerLine[2];
|
||||
double archToCenterHeigth;
|
||||
double archToGroundHeigth;
|
||||
}WD_wheelArchInfo;
|
||||
|
||||
//读版本号
|
||||
SG_APISHARED_EXPORT const char* wd_wheelArchHeigthMeasureVersion(void);
|
||||
|
||||
//相机水平安装计算地面调平参数。。
|
||||
//相机Z轴基本平行地面时,需要以地面为参照,将相机调水平
|
||||
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
|
||||
SG_APISHARED_EXPORT SSG_planeCalibPara wd_horizonCamera_getGroundCalibPara(
|
||||
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
|
||||
|
||||
//相机水平时姿态调平,并去除地面
|
||||
SG_APISHARED_EXPORT void wd_horizonCamera_lineDataR(
|
||||
std::vector< SVzNL3DPosition>& a_line,
|
||||
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,
|
||||
const SSG_cornerParam cornerPara,
|
||||
const SSG_lineSegParam lineSegPara,
|
||||
const SSG_outlierFilterParam filterParam,
|
||||
const SSG_treeGrowParam growParam,
|
||||
const SSG_planeCalibPara groundCalibPara,
|
||||
int* errCode);
|
||||
|
||||
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
# 获取文件名用于比较
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user