轮胎检测增加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++;
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);
}
}
}

View File

@ -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

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
@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);

File diff suppressed because it is too large Load Diff

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
# 获取文件名用于比较