螺杆更新
This commit is contained in:
parent
683eb03f25
commit
e9cb8681d0
@ -5,7 +5,7 @@ TEMPLATE = subdirs
|
||||
# 可用值:GrabBag,
|
||||
# BeltTearing,
|
||||
# LapWeld, Workpiece, ParticleSize,
|
||||
# BinocularMark, WorkpieceProject, TunnelChannel, WheelMeasure, ScrewPosition, BagThreadPosition, FireBrickPosition, WorkpieceHole, StatorPosition, HoleDetection, RodAndBarPosition
|
||||
# BinocularMark, WorkpieceProject, TunnelChannel, WheelMeasure, ScrewPosition, BagThreadPosition, FireBrickPosition, WorkpieceHole, StatorPosition, HoleDetection, RodAndBarPosition, RodWeldSeam
|
||||
|
||||
isEmpty(TARGET_APP) {
|
||||
# 未指定则编译全部
|
||||
@ -25,6 +25,7 @@ isEmpty(TARGET_APP) {
|
||||
SUBDIRS += HoleDetection/HoleDetection.pro # 孔洞检测
|
||||
SUBDIRS += HolePitPosition/HolePitPosition.pro # 坑孔定位
|
||||
SUBDIRS += RodAndBarPosition/RodAndBarPosition.pro # 棒材定位
|
||||
SUBDIRS += RodWeldSeam/RodWeldSeam.pro # 钢筋焊缝定位
|
||||
} else {
|
||||
equals(TARGET_APP, "GrabBag") {
|
||||
SUBDIRS += GrabBag/GrabBag.pro
|
||||
@ -58,6 +59,8 @@ isEmpty(TARGET_APP) {
|
||||
SUBDIRS += HolePitPosition/HolePitPosition.pro
|
||||
} else:equals(TARGET_APP, "RodAndBarPosition") {
|
||||
SUBDIRS += RodAndBarPosition/RodAndBarPosition.pro
|
||||
} else:equals(TARGET_APP, "RodWeldSeam") {
|
||||
SUBDIRS += RodWeldSeam/RodWeldSeam.pro
|
||||
} else {
|
||||
error("无效的 TARGET_APP: $$TARGET_APP")
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
App下新建StatorPosition项目
|
||||
1. 复制workpieceHole进行修改
|
||||
2. 使用的算法是 @AppAlgo/motorStatorPosition
|
||||
App下新建RodWeldSeam项目
|
||||
1. 复制RodAndBarPosition进行修改
|
||||
2. 使用的算法是 @AppAlgo/rodAndBarDetection
|
||||
3. 使用VzNLSDK 设备
|
||||
4. 结果参考算法目录下*test.cpp 参数也参考这个内容
|
||||
4. 结果参考算法目录下*test.cpp rodWeldSeamPosition_test接口的测试,参数也参考这个内容
|
||||
5. 结果列表进行更新
|
||||
6. 根据算法需要的参数进行修改config,并实现从页面进行修改参数
|
||||
7. App.pro 中增加编译选项,并且在GrabBagPrj/buildArmPrj.sh编译选项
|
||||
8. 增加arm下的pkg脚本
|
||||
9. 项目的协议文档也给搞一份
|
||||
9. 项目的协议文档也给搞一份ModbusTCP的
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
#define SCREWPOSITION_APP_NAME "螺杆定位"
|
||||
#define SCREWPOSITION_VERSION_STRING "1.1.9"
|
||||
#define SCREWPOSITION_VERSION_STRING "1.1.10"
|
||||
#define SCREWPOSITION_BUILD_STRING "1"
|
||||
#define SCREWPOSITION_FULL_VERSION_STRING "V" SCREWPOSITION_VERSION_STRING "_" SCREWPOSITION_BUILD_STRING
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
# 1.1.10 2026-05-15
|
||||
## build_1
|
||||
1. 更新算法:螺杆检测
|
||||
|
||||
# 1.1.8 2026-05-13
|
||||
## build_1
|
||||
1. 更新算法:螺杆检测
|
||||
|
||||
@ -22,7 +22,7 @@ struct VrCornerParam
|
||||
double minEndingGap = 20.0;
|
||||
double minEndingGap_z = 5.0;
|
||||
double scale = 2.5;
|
||||
double cornerTh = 60.0;
|
||||
double cornerTh = 30.0;
|
||||
double jumpCornerTh_1 = 15.0;
|
||||
double jumpCornerTh_2 = 60.0;
|
||||
};
|
||||
@ -35,12 +35,12 @@ struct VrOutlierFilterParam
|
||||
|
||||
struct VrTreeGrowParam
|
||||
{
|
||||
double yDeviation_max = 20.0;
|
||||
double yDeviation_max = 5.0;
|
||||
double zDeviation_max = 50.0;
|
||||
int maxLineSkipNum = 10;
|
||||
double maxSkipDistance = 20.0;
|
||||
double minLTypeTreeLen = 10.0;
|
||||
double minVTypeTreeLen = 10.0;
|
||||
double maxSkipDistance = 30.0;
|
||||
double minLTypeTreeLen = 50.0;
|
||||
double minVTypeTreeLen = 50.0;
|
||||
};
|
||||
|
||||
struct VrAlgorithmParams
|
||||
|
||||
@ -141,11 +141,6 @@ public:
|
||||
*/
|
||||
void StopAllDetection();
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在进行顺序检测
|
||||
*/
|
||||
bool IsSequentialDetecting() const { return m_sequentialDetecting; }
|
||||
|
||||
/**
|
||||
* @brief 设置默认相机索引
|
||||
*/
|
||||
@ -194,19 +189,10 @@ private:
|
||||
IWheelMeasureStatus* m_statusUpdate = nullptr;
|
||||
int m_currentCameraIndex = 1; // 默认相机索引(1-based)
|
||||
|
||||
// 顺序检测相关
|
||||
bool m_sequentialDetecting = false; // 是否正在顺序检测所有设备
|
||||
bool m_stopSequentialRequested = false; // 是否请求停止顺序检测
|
||||
int m_sequentialCurrentIndex = 0; // 当前顺序检测的设备索引(0-based)
|
||||
int m_sequentialTotalCount = 0; // 需要顺序检测的设备总数
|
||||
|
||||
// TCP协议
|
||||
WheelMeasureTCPProtocol m_tcpProtocol; // TCP服务器协议
|
||||
bool m_tcpDetectionMode = false; // 是否为TCP触发的检测
|
||||
QMap<int, WheelMeasureTCPProtocol::CameraMeasureResult> m_tcpResults; // TCP检测结果缓存
|
||||
|
||||
// 继续检测下一个设备
|
||||
void continueSequentialDetection();
|
||||
};
|
||||
|
||||
#endif // WHEELMEASUREPRESENTER_H
|
||||
|
||||
@ -36,9 +36,8 @@ WheelMeasurePresenter::~WheelMeasurePresenter()
|
||||
// 清除状态回调,防止后续回调访问已销毁对象
|
||||
m_statusUpdate = nullptr;
|
||||
|
||||
// 停止顺序检测
|
||||
m_stopSequentialRequested = true;
|
||||
m_sequentialDetecting = false;
|
||||
// 停止检测
|
||||
StopDetection();
|
||||
|
||||
// 处理待处理的 Qt 事件,确保 QueuedConnection 的回调不会访问已销毁对象
|
||||
QCoreApplication::processEvents();
|
||||
@ -65,6 +64,9 @@ int WheelMeasurePresenter::InitApp()
|
||||
return ERR_CODE(DEV_CONFIG_ERR);
|
||||
}
|
||||
|
||||
// 传入扫描配置到基类
|
||||
SetScanConfig(m_configResult.scanConfig);
|
||||
|
||||
// 初始化相机
|
||||
if (!initializeCameras()) {
|
||||
LOG_ERROR("Failed to initialize cameras\n");
|
||||
@ -168,6 +170,14 @@ void WheelMeasurePresenter::OnWorkStatusChanged(WorkStatus status)
|
||||
m_statusUpdate->OnWorkStatusChanged(status);
|
||||
}
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
// TCP模式下检测完成,发送所有结果
|
||||
if (status == WorkStatus::Completed && m_tcpDetectionMode) {
|
||||
LOG_INFO("TCP模式:所有相机检测完成,准备发送结果\n");
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
sendTCPMeasureResults();
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::OnCameraCountChanged(int count)
|
||||
@ -285,117 +295,22 @@ void WheelMeasurePresenter::ResetDetect(int cameraIndex)
|
||||
|
||||
void WheelMeasurePresenter::StartAllDetection()
|
||||
{
|
||||
LOG_INFO("Starting sequential detection for all cameras\n");
|
||||
|
||||
// 计算启用的相机数量
|
||||
m_sequentialTotalCount = 0;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) {
|
||||
m_sequentialTotalCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_sequentialTotalCount == 0) {
|
||||
LOG_WARNING("No enabled cameras to detect\n");
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(QString("没有可用的相机设备"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化顺序检测状态
|
||||
m_sequentialDetecting = true;
|
||||
m_stopSequentialRequested = false;
|
||||
m_sequentialCurrentIndex = 0;
|
||||
LOG_INFO("Starting all cameras detection\n");
|
||||
|
||||
// 清空之前的检测结果
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnClearMeasureData();
|
||||
m_statusUpdate->OnStatusUpdate(QString("开始所有设备的检测"));
|
||||
}
|
||||
|
||||
LOG_INFO("Sequential detection started, total cameras: %d\n", m_sequentialTotalCount);
|
||||
|
||||
|
||||
m_statusUpdate->OnStatusUpdate(QString("开始所有设备的检测"));
|
||||
|
||||
// 开始检测第一个设备
|
||||
continueSequentialDetection();
|
||||
// 委托基类,根据 m_scanConfig.simultaneousCount 自动决定扫描策略
|
||||
StartDetection(-1);
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::StopAllDetection()
|
||||
{
|
||||
LOG_INFO("Stop sequential detection requested\n");
|
||||
|
||||
if (m_sequentialDetecting) {
|
||||
// 设置停止标志,等待当前设备检测完成后停止
|
||||
m_stopSequentialRequested = true;
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(QString("正在完成当前设备检测,之后将停止..."));
|
||||
}
|
||||
} else {
|
||||
// 如果不是顺序检测模式,直接停止
|
||||
StopDetection();
|
||||
}
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::continueSequentialDetection()
|
||||
{
|
||||
// 检查是否应该停止
|
||||
if (m_stopSequentialRequested) {
|
||||
LOG_INFO("Sequential detection stopped by user request\n");
|
||||
m_sequentialDetecting = false;
|
||||
m_stopSequentialRequested = false;
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(QString("顺序检测已停止"));
|
||||
}
|
||||
SetWorkStatus(WorkStatus::Ready);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否还有设备需要检测
|
||||
if (m_sequentialCurrentIndex >= m_sequentialTotalCount) {
|
||||
LOG_INFO("Sequential detection completed, all %d cameras processed\n", m_sequentialTotalCount);
|
||||
m_sequentialDetecting = false;
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(QString("所有 %1 个设备检测完成").arg(m_sequentialTotalCount));
|
||||
}
|
||||
SetWorkStatus(WorkStatus::Completed);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前要检测的相机索引(1-based)
|
||||
int cameraIndex = m_sequentialCurrentIndex + 1;
|
||||
m_currentCameraIndex = cameraIndex;
|
||||
|
||||
// 获取相机名称
|
||||
QString cameraName;
|
||||
int enabledIndex = 0;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) {
|
||||
enabledIndex++;
|
||||
if (enabledIndex == cameraIndex) {
|
||||
cameraName = QString::fromStdString(cameraConfig.name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("Starting detection for camera %d/%d: %s\n",
|
||||
m_sequentialCurrentIndex + 1, m_sequentialTotalCount,
|
||||
cameraName.toStdString().c_str());
|
||||
|
||||
if (m_statusUpdate) {
|
||||
m_statusUpdate->OnStatusUpdate(QString("正在检测设备 %1/%2: %3")
|
||||
.arg(m_sequentialCurrentIndex + 1)
|
||||
.arg(m_sequentialTotalCount)
|
||||
.arg(cameraName));
|
||||
}
|
||||
|
||||
// 清空数据缓存
|
||||
ClearDetectionDataCache();
|
||||
|
||||
// 开始检测当前相机
|
||||
StartDetection(cameraIndex);
|
||||
LOG_INFO("Stop all detection requested\n");
|
||||
StopDetection();
|
||||
}
|
||||
|
||||
void WheelMeasurePresenter::OnConfigChanged(const WheelMeasureConfigResult& configResult)
|
||||
@ -403,7 +318,8 @@ void WheelMeasurePresenter::OnConfigChanged(const WheelMeasureConfigResult& conf
|
||||
LOG_INFO("Config changed notification received\n");
|
||||
m_configResult = configResult;
|
||||
|
||||
// 更新基类调试参数
|
||||
// 更新基类扫描配置和调试参数
|
||||
SetScanConfig(m_configResult.scanConfig);
|
||||
SetDebugParam(m_configResult.debugParam);
|
||||
|
||||
emit configUpdated();
|
||||
@ -505,13 +421,6 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
|
||||
SetWorkStatus(WorkStatus::Error);
|
||||
|
||||
// 如果正在进行顺序检测,继续检测下一个设备
|
||||
if (m_sequentialDetecting) {
|
||||
m_sequentialCurrentIndex++;
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
continueSequentialDetection();
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -595,23 +504,6 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
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;
|
||||
}
|
||||
|
||||
@ -819,8 +711,6 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
m_statusUpdate->OnMeasureResult(result);
|
||||
}
|
||||
|
||||
SetWorkStatus(WorkStatus::Completed);
|
||||
|
||||
// 如果是TCP触发的检测,缓存结果
|
||||
if (m_tcpDetectionMode) {
|
||||
WheelMeasureTCPProtocol::CameraMeasureResult tcpResult;
|
||||
@ -842,25 +732,6 @@ void WheelMeasurePresenter::processScanData(std::vector<std::pair<EVzResultDataT
|
||||
m_tcpResults[m_currentCameraIndex] = tcpResult;
|
||||
LOG_INFO("TCP检测结果已缓存: 相机%d, 错误码=%d\n", tcpResult.cameraId, tcpResult.errorCode);
|
||||
}
|
||||
|
||||
// 如果正在进行顺序检测,继续检测下一个设备
|
||||
if (m_sequentialDetecting) {
|
||||
m_sequentialCurrentIndex++;
|
||||
|
||||
// TCP模式下,检查是否所有相机都检测完成
|
||||
if (m_tcpDetectionMode && m_tcpResults.size() >= m_sequentialTotalCount) {
|
||||
LOG_INFO("TCP模式:所有相机检测完成,准备发送结果\n");
|
||||
// 先结束顺序检测
|
||||
m_sequentialDetecting = false;
|
||||
// 发送TCP结果
|
||||
sendTCPMeasureResults();
|
||||
} else {
|
||||
// 继续检测下一个设备
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
continueSequentialDetection();
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WheelCameraPlaneCalibParam* WheelMeasurePresenter::getPlaneCalibParam(int cameraIndex)
|
||||
@ -1063,13 +934,19 @@ bool WheelMeasurePresenter::onTCPDetectionTriggered(int param)
|
||||
|
||||
void WheelMeasurePresenter::sendTCPMeasureResults()
|
||||
{
|
||||
// 计算期望的相机数量
|
||||
int expectedCount = 0;
|
||||
for (const auto& cameraConfig : m_configResult.cameras) {
|
||||
if (cameraConfig.enabled) expectedCount++;
|
||||
}
|
||||
|
||||
LOG_INFO("发送TCP测量结果,共 %d 个相机\n", m_tcpResults.size());
|
||||
|
||||
// 构建结果向量(按相机ID排序)
|
||||
std::vector<WheelMeasureTCPProtocol::CameraMeasureResult> results;
|
||||
|
||||
// 按相机ID顺序添加结果
|
||||
for (int cameraId = 1; cameraId <= m_sequentialTotalCount; ++cameraId) {
|
||||
for (int cameraId = 1; cameraId <= expectedCount; ++cameraId) {
|
||||
if (m_tcpResults.contains(cameraId)) {
|
||||
results.push_back(m_tcpResults[cameraId]);
|
||||
} else {
|
||||
|
||||
@ -19,9 +19,67 @@ DialogCamera::DialogCamera(QWidget *parent) :
|
||||
// 隐藏标题栏
|
||||
// setWindowFlags(Qt::FramelessWindowHint);
|
||||
|
||||
// 增加对话框高度以容纳新控件
|
||||
this->resize(700, 600);
|
||||
|
||||
// 初始化表格
|
||||
InitTable();
|
||||
|
||||
// ===== 添加扫描配置区域 =====
|
||||
QGroupBox* groupScan = new QGroupBox("扫描配置", this);
|
||||
groupScan->setGeometry(20, 460, 660, 60);
|
||||
groupScan->setStyleSheet(
|
||||
"QGroupBox {"
|
||||
" color: rgb(221, 225, 233);"
|
||||
" font-size: 14px;"
|
||||
" border: 1px solid rgb(60, 62, 70);"
|
||||
" border-radius: 4px;"
|
||||
" margin-top: 10px;"
|
||||
" padding-top: 15px;"
|
||||
"}"
|
||||
"QGroupBox::title {"
|
||||
" subcontrol-origin: margin;"
|
||||
" left: 10px;"
|
||||
" padding: 0 5px;"
|
||||
"}"
|
||||
);
|
||||
|
||||
QLabel* labelScan = new QLabel("同时扫描数量:", groupScan);
|
||||
labelScan->setGeometry(15, 22, 130, 28);
|
||||
labelScan->setStyleSheet("color: rgb(221, 225, 233); font-size: 14px;");
|
||||
|
||||
m_spinSimultaneousCount = new QSpinBox(groupScan);
|
||||
m_spinSimultaneousCount->setGeometry(155, 18, 180, 32);
|
||||
m_spinSimultaneousCount->setMinimum(0); // 0 = 全部同时
|
||||
m_spinSimultaneousCount->setMaximum(4); // 最多4个相机
|
||||
m_spinSimultaneousCount->setValue(1); // 默认单相机
|
||||
m_spinSimultaneousCount->setStyleSheet(
|
||||
"QSpinBox {"
|
||||
" color: rgb(221, 225, 233);"
|
||||
" background-color: rgb(47, 48, 52);"
|
||||
" border: 1px solid rgb(60, 62, 70);"
|
||||
" border-radius: 3px;"
|
||||
" padding: 3px 8px;"
|
||||
" font-size: 14px;"
|
||||
"}"
|
||||
"QSpinBox::up-button, QSpinBox::down-button {"
|
||||
" width: 20px;"
|
||||
"}"
|
||||
);
|
||||
m_spinSimultaneousCount->setToolTip(
|
||||
"0 = 全部相机同时扫描\n"
|
||||
"1 = 单相机顺序扫描\n"
|
||||
"2-4 = 每批N个相机同时扫描"
|
||||
);
|
||||
|
||||
QLabel* labelHint = new QLabel("0=全部同时, 1=单相机, 2-4=每批N个", groupScan);
|
||||
labelHint->setGeometry(350, 22, 290, 28);
|
||||
labelHint->setStyleSheet("color: rgb(140, 142, 150); font-size: 12px;");
|
||||
|
||||
// 调整保存和取消按钮位置
|
||||
ui->btn_save->move(170, 540);
|
||||
ui->btn_cancel->move(370, 540);
|
||||
|
||||
// 加载已有的相机配置
|
||||
LoadExistingCameras();
|
||||
|
||||
@ -97,6 +155,11 @@ void DialogCamera::LoadExistingCameras()
|
||||
m_cameraConfigs.push_back(camera);
|
||||
}
|
||||
LOG_INFO("Loaded %zu existing cameras from config\n", m_cameraConfigs.size());
|
||||
|
||||
// 加载扫描配置
|
||||
if (m_spinSimultaneousCount) {
|
||||
m_spinSimultaneousCount->setValue(configResult->scanConfig.simultaneousCount);
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING("ConfigResult is null\n");
|
||||
}
|
||||
@ -457,12 +520,20 @@ bool DialogCamera::SaveConfigToFile()
|
||||
camera.cameraIP.c_str());
|
||||
}
|
||||
|
||||
// 更新扫描配置
|
||||
if (m_spinSimultaneousCount) {
|
||||
configResult->scanConfig.simultaneousCount = m_spinSimultaneousCount->value();
|
||||
LOG_INFO(" simultaneousCount: %d\n", configResult->scanConfig.simultaneousCount);
|
||||
}
|
||||
|
||||
// 保存配置到文件
|
||||
std::string configFilePath = PathManager::GetInstance().GetConfigFilePath().toStdString();
|
||||
bool saveResult = vrConfig->SaveConfig(configFilePath, *configResult);
|
||||
|
||||
if (saveResult) {
|
||||
LOG_INFO("Camera configuration saved successfully to: %s\n", configFilePath.c_str());
|
||||
// 同步扫描配置到BasePresenter
|
||||
m_presenter->SetScanConfig(configResult->scanConfig);
|
||||
} else {
|
||||
LOG_ERROR("Failed to save camera configuration to file\n");
|
||||
}
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
#include <QDialog>
|
||||
#include <QTableWidget>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QGroupBox>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "IVrWheelMeasureConfig.h"
|
||||
@ -70,6 +73,7 @@ private:
|
||||
std::vector<WheelCameraParam> m_cameraConfigs; // 相机配置列表
|
||||
const int MAX_CAMERAS = 4; // 最多支持4个相机
|
||||
WheelMeasurePresenter* m_presenter = nullptr; // Presenter用于访问配置
|
||||
QSpinBox* m_spinSimultaneousCount = nullptr; // 同时扫描数量
|
||||
};
|
||||
|
||||
#endif // DIALOGCAMERA_H
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
#include <QThread>
|
||||
#include <QApplication>
|
||||
#include <QLabel>
|
||||
#include <QListView>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
@ -25,6 +26,46 @@ DialogCameraLevel::DialogCameraLevel(QWidget *parent)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// 创建地面高度编辑控件
|
||||
QFont font14("Arial", 14);
|
||||
QFont font14Btn("Arial", 14);
|
||||
|
||||
QLabel* labelPlaneHeight = new QLabel("地面高度:", this);
|
||||
labelPlaneHeight->setFont(font14);
|
||||
labelPlaneHeight->setStyleSheet("color: rgb(221, 225, 233);");
|
||||
labelPlaneHeight->setGeometry(130, 340, 100, 31);
|
||||
|
||||
m_editPlaneHeight = new QLineEdit(this);
|
||||
m_editPlaneHeight->setFont(font14);
|
||||
m_editPlaneHeight->setStyleSheet("color: rgb(221, 225, 233); background-color: rgb(47, 48, 52);");
|
||||
m_editPlaneHeight->setGeometry(230, 340, 100, 31);
|
||||
m_editPlaneHeight->setAlignment(Qt::AlignCenter);
|
||||
|
||||
QLabel* labelPlaneHeightUnit = new QLabel("mm", this);
|
||||
labelPlaneHeightUnit->setFont(QFont("Arial", 13));
|
||||
labelPlaneHeightUnit->setStyleSheet("color: rgb(221, 225, 233);");
|
||||
labelPlaneHeightUnit->setGeometry(340, 340, 40, 31);
|
||||
|
||||
m_btnSavePlaneHeight = new QPushButton("保存", this);
|
||||
m_btnSavePlaneHeight->setFont(font14Btn);
|
||||
m_btnSavePlaneHeight->setMinimumSize(60, 31);
|
||||
m_btnSavePlaneHeight->setMaximumSize(60, 31);
|
||||
m_btnSavePlaneHeight->setGeometry(390, 340, 60, 31);
|
||||
m_btnSavePlaneHeight->setStyleSheet(
|
||||
"QPushButton {"
|
||||
" color: rgb(221, 225, 233);"
|
||||
" background-color: rgb(60, 63, 65);"
|
||||
" border: 1px solid #3B3D47;"
|
||||
" border-radius: 4px;"
|
||||
"}"
|
||||
"QPushButton:hover {"
|
||||
" background-color: rgb(80, 83, 85);"
|
||||
"}"
|
||||
"QPushButton:pressed {"
|
||||
" background-color: rgb(45, 48, 50);"
|
||||
"}");
|
||||
connect(m_btnSavePlaneHeight, &QPushButton::clicked, this, &DialogCameraLevel::on_btn_save_planeHeight_clicked);
|
||||
|
||||
// 初始化结果显示区域
|
||||
ui->label_level_result->setText("请选择相机,然后点击调平按钮\n开始相机调平操作");
|
||||
ui->label_level_result->setAlignment(Qt::AlignCenter);
|
||||
@ -67,8 +108,13 @@ void DialogCameraLevel::setConfig(IVrWheelMeasureConfig* config, WheelMeasureCon
|
||||
m_pConfig = config;
|
||||
m_pConfigResult = configResult;
|
||||
|
||||
// 重新打开页面时,从文件重新加载配置以获取最新数据
|
||||
if (m_pConfig && m_pConfigResult) {
|
||||
QString configPath = PathManager::GetInstance().GetConfigFilePath();
|
||||
*m_pConfigResult = m_pConfig->LoadConfig(configPath.toStdString());
|
||||
}
|
||||
|
||||
// 如果相机已经选择,重新加载当前相机的标定状态
|
||||
// 修复:打开页面时配置可能在相机列表之后设置,导致初始加载失败
|
||||
if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast<int>(m_cameraList.size())) {
|
||||
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
|
||||
loadCameraRoiRange(m_currentCameraIndex);
|
||||
@ -281,6 +327,9 @@ bool DialogCameraLevel::performCameraLeveling()
|
||||
|
||||
void DialogCameraLevel::updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9])
|
||||
{
|
||||
// 更新地面高度编辑框
|
||||
m_editPlaneHeight->setText(QString::number(planeHeight, 'f', 2));
|
||||
|
||||
// 构建显示文本
|
||||
QString resultText;
|
||||
|
||||
@ -568,7 +617,8 @@ bool DialogCameraLevel::loadCameraCalibrationData(int cameraIndex, const QString
|
||||
}
|
||||
planeHeight = param.planeHeight;
|
||||
|
||||
// 加载该相机的误差补偿值到UI
|
||||
// 加载该相机的地面高度和误差补偿值到UI
|
||||
m_editPlaneHeight->setText(QString::number(param.planeHeight, 'f', 2));
|
||||
ui->edit_error_compensation->setText(QString::number(param.errorCompensation, 'f', 1));
|
||||
|
||||
LOG_INFO("Calibration data loaded successfully for camera %d (%s)\n",
|
||||
@ -611,9 +661,10 @@ void DialogCameraLevel::checkAndDisplayCalibrationStatus(int cameraIndex)
|
||||
LOG_INFO("Displaying existing calibration data for camera %s\n", cameraName.toUtf8().constData());
|
||||
updateLevelingResults(planeCalib, planeHeight, invRMatrix);
|
||||
} else {
|
||||
// 没有标定数据
|
||||
LOG_INFO("No calibration data found for camera %s\n", cameraName.toUtf8().constData());
|
||||
ui->label_level_result->setText(QString("相机: %1\n\n请点击调平按钮开始调平操作").arg(cameraName));
|
||||
// 没有标定数据,显示未标定
|
||||
LOG_INFO("No calibration data found for camera %s, showing uncalibrated\n", cameraName.toUtf8().constData());
|
||||
m_editPlaneHeight->setText("-1.00");
|
||||
ui->label_level_result->setText(QString("相机: %1\n\n未标定").arg(cameraName));
|
||||
ui->label_level_result->setAlignment(Qt::AlignCenter);
|
||||
}
|
||||
}
|
||||
@ -718,6 +769,52 @@ void DialogCameraLevel::on_btn_save_compensation_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void DialogCameraLevel::on_btn_save_planeHeight_clicked()
|
||||
{
|
||||
if (!m_pConfig || !m_pConfigResult) {
|
||||
LOG_ERROR("Config is null, cannot save plane height\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
|
||||
LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
double planeHeight = m_editPlaneHeight->text().toDouble();
|
||||
int cameraIndex = m_currentCameraIndex + 1;
|
||||
|
||||
// 查找或创建相机调平参数
|
||||
bool found = false;
|
||||
for (auto& param : m_pConfigResult->planeCalibParams) {
|
||||
if (param.cameraIndex == cameraIndex) {
|
||||
param.planeHeight = planeHeight;
|
||||
param.isCalibrated = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
WheelCameraPlaneCalibParam newParam;
|
||||
newParam.cameraIndex = cameraIndex;
|
||||
newParam.planeHeight = planeHeight;
|
||||
newParam.isCalibrated = true;
|
||||
m_pConfigResult->planeCalibParams.push_back(newParam);
|
||||
}
|
||||
|
||||
// 保存配置到文件
|
||||
QString configPath = PathManager::GetInstance().GetConfigFilePath();
|
||||
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
|
||||
if (saveResult) {
|
||||
LOG_INFO("Plane height saved successfully for camera %d: %.2f\n", cameraIndex, planeHeight);
|
||||
// 刷新界面显示
|
||||
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
|
||||
} else {
|
||||
LOG_ERROR("Failed to save plane height\n");
|
||||
}
|
||||
}
|
||||
|
||||
void DialogCameraLevel::loadCameraRoiRange(int cameraIndex)
|
||||
{
|
||||
if (!m_pConfig || !m_pConfigResult) {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
#include <vector>
|
||||
@ -50,6 +51,7 @@ private slots:
|
||||
void on_combo_camera_currentIndexChanged(int index);
|
||||
void on_btn_save_compensation_clicked();
|
||||
void on_btn_save_roi_clicked();
|
||||
void on_btn_save_planeHeight_clicked();
|
||||
|
||||
private:
|
||||
Ui::DialogCameraLevel *ui;
|
||||
@ -62,6 +64,10 @@ private:
|
||||
IVrWheelMeasureConfig* m_pConfig = nullptr;
|
||||
WheelMeasureConfigResult* m_pConfigResult = nullptr;
|
||||
|
||||
// planeHeight编辑控件
|
||||
QLineEdit* m_editPlaneHeight = nullptr;
|
||||
QPushButton* m_btnSavePlaneHeight = nullptr;
|
||||
|
||||
// 当前选中的相机索引
|
||||
int m_currentCameraIndex = -1;
|
||||
|
||||
|
||||
@ -109,7 +109,7 @@
|
||||
<x>130</x>
|
||||
<y>110</y>
|
||||
<width>511</width>
|
||||
<height>271</height>
|
||||
<height>221</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
|
||||
@ -139,6 +139,7 @@ struct WheelMeasureConfigResult
|
||||
std::vector<WheelServerInfo> servers; // 服务器列表
|
||||
WheelMeasureAlgorithmParams algorithmParams; // 算法参数
|
||||
VrDebugParam debugParam; // 调试参数(使用公共VrDebugParam)
|
||||
VrScanConfig scanConfig; // 多相机同时扫描配置
|
||||
|
||||
int serverPort = 5900; // 服务器端口
|
||||
int tcpPort = 5800; // TCP协议端口
|
||||
|
||||
@ -265,6 +265,16 @@ WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& fil
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
// 解析多相机扫描配置
|
||||
else if (xml.isStartElement() && xml.name() == "ScanConfig") {
|
||||
result.scanConfig.simultaneousCount =
|
||||
xml.attributes().value("simultaneousCount").toInt();
|
||||
if (result.scanConfig.simultaneousCount < 0) {
|
||||
result.scanConfig.simultaneousCount = 1; // 默认值
|
||||
}
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
// 解析服务端配置
|
||||
else if (xml.isStartElement() && xml.name() == "LocalServerConfig") {
|
||||
while (xml.readNextStartElement()) {
|
||||
@ -429,6 +439,11 @@ bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureC
|
||||
xml.writeAttribute("debugOutputPath", QString::fromStdString(configResult.debugParam.debugOutputPath));
|
||||
xml.writeEndElement(); // DebugParam
|
||||
|
||||
// 保存多相机扫描配置
|
||||
xml.writeStartElement("ScanConfig");
|
||||
xml.writeAttribute("simultaneousCount", QString::number(configResult.scanConfig.simultaneousCount));
|
||||
xml.writeEndElement(); // ScanConfig
|
||||
|
||||
// 保存服务端配置
|
||||
xml.writeStartElement("LocalServerConfig");
|
||||
xml.writeStartElement("ServerPort");
|
||||
|
||||
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.
@ -224,16 +224,24 @@ void _outputChanneltInfo(char* fileName, std::vector<SSX_rodPoseInfo>& screwInfo
|
||||
sw.close();
|
||||
}
|
||||
|
||||
void _outputPlatePiseInfo(char* fileName, SSX_pointPoseInfo& centerInfo)
|
||||
void _outputPlatePiseInfo(char* fileName, SSX_platePoseInfo& centerInfo)
|
||||
{
|
||||
std::ofstream sw(fileName);
|
||||
char dataStr[250];
|
||||
sprintf_s(dataStr, 250, "定位盘: center_( %g, %g, %g ), normalDir_( %g, %g, %g ), xDir_( %g, %g, %g ), yDir_( %g, %g, %g )",
|
||||
centerInfo.center.x, centerInfo.center.y, centerInfo.center.z,
|
||||
centerInfo.normalDir.x, centerInfo.normalDir.y, centerInfo.normalDir.z,
|
||||
centerInfo.xDir.x, centerInfo.xDir.y, centerInfo.xDir.z,
|
||||
centerInfo.yDir.x, centerInfo.yDir.y, centerInfo.yDir.z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, "定位盘: \n");
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " holeLT_(% g, % g, % g)\n", centerInfo.holeLT.x, centerInfo.holeLT.y, centerInfo.holeLT.z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " holeRB_(% g, % g, % g)\n", centerInfo.holeRB.x, centerInfo.holeRB.y, centerInfo.holeRB.z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " center_(% g, % g, % g)\n", centerInfo.center.x, centerInfo.center.y, centerInfo.center.z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " normalDir_(% g, % g, % g)\n", centerInfo.normalDir.x, centerInfo.normalDir.y, centerInfo.normalDir.z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " xDir_(% g, % g, % g)\n", centerInfo.xDir.x, centerInfo.xDir.y, centerInfo.xDir.z);
|
||||
sw << dataStr << std::endl;
|
||||
sprintf_s(dataStr, 250, " yDir_(% g, % g, % g)\n", centerInfo.yDir.x, centerInfo.yDir.y, centerInfo.yDir.z);
|
||||
sw << dataStr << std::endl;
|
||||
sw.close();
|
||||
}
|
||||
|
||||
@ -325,9 +333,13 @@ void _outputRGBDScan_RGBD(
|
||||
else if (pt3D->nPointIdx == 2)
|
||||
{
|
||||
rgb = { 250, 0, 0 };
|
||||
size = 5;
|
||||
size = 3;
|
||||
}
|
||||
else if (pt3D->nPointIdx == 4)
|
||||
{
|
||||
rgb = { 250, 250, 0 };
|
||||
size = 6;
|
||||
}
|
||||
|
||||
else //if (pt3D->nPointIdx == 0)
|
||||
{
|
||||
rgb = { 200, 200, 200 };
|
||||
@ -366,16 +378,17 @@ void _outputRGBDScan_RGBD(
|
||||
|
||||
//输出法向
|
||||
size = 1;
|
||||
double len = 60;
|
||||
double len1 = 30;
|
||||
double len2 = 200;
|
||||
lineIdx = 0;
|
||||
for (int i = 0; i < objNum; i++)
|
||||
{
|
||||
SVzNL3DPoint pt0 = { screwInfo[i].center.x - len * screwInfo[i].axialDir.x,
|
||||
screwInfo[i].center.y - len * screwInfo[i].axialDir.y,
|
||||
screwInfo[i].center.z - len * screwInfo[i].axialDir.z };
|
||||
SVzNL3DPoint pt1 = { screwInfo[i].center.x + len * screwInfo[i].axialDir.x,
|
||||
screwInfo[i].center.y + len * screwInfo[i].axialDir.y,
|
||||
screwInfo[i].center.z + len * screwInfo[i].axialDir.z };
|
||||
SVzNL3DPoint pt0 = { screwInfo[i].center.x - len1 * screwInfo[i].axialDir.x,
|
||||
screwInfo[i].center.y - len1 * screwInfo[i].axialDir.y,
|
||||
screwInfo[i].center.z - len1 * screwInfo[i].axialDir.z };
|
||||
SVzNL3DPoint pt1 = { screwInfo[i].center.x + len2 * screwInfo[i].axialDir.x,
|
||||
screwInfo[i].center.y + len2 * screwInfo[i].axialDir.y,
|
||||
screwInfo[i].center.z + len2 * screwInfo[i].axialDir.z };
|
||||
//显示法向量
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
@ -386,22 +399,6 @@ void _outputRGBDScan_RGBD(
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
}
|
||||
//多输出一个,修正显示工具bug
|
||||
SVzNL3DPoint pt0 = { screwInfo[0].center.x - len * screwInfo[0].axialDir.x,
|
||||
screwInfo[0].center.y - len * screwInfo[0].axialDir.y,
|
||||
screwInfo[0].center.z - len * screwInfo[0].axialDir.z };
|
||||
SVzNL3DPoint pt1 = { screwInfo[0].center.x + len * screwInfo[0].axialDir.x,
|
||||
screwInfo[0].center.y + len * screwInfo[0].axialDir.y,
|
||||
screwInfo[0].center.z + len * screwInfo[0].axialDir.z };
|
||||
//显示法向量
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << pt1.x << "," << pt1.y << "," << pt1.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
}
|
||||
sw.close();
|
||||
}
|
||||
@ -409,7 +406,7 @@ void _outputRGBDScan_RGBD(
|
||||
void _outputRGBDScan_RGBD_centerPose(
|
||||
char* fileName,
|
||||
std::vector<std::vector<SVzNL3DPosition>>& scanLines,
|
||||
SSX_pointPoseInfo& poseInfo
|
||||
SSX_platePoseInfo& poseInfo
|
||||
)
|
||||
{
|
||||
int lineNum = (int)scanLines.size();
|
||||
@ -477,7 +474,7 @@ void _outputRGBDScan_RGBD_centerPose(
|
||||
}
|
||||
|
||||
{
|
||||
sw << "Line_" << lineIdx << "_0_1" << std::endl;
|
||||
sw << "Line_" << lineIdx << "_0_3" << std::endl;
|
||||
rgb = { 250, 0, 0 };
|
||||
size = 8;
|
||||
float x = (float)poseInfo.center.x;
|
||||
@ -487,14 +484,28 @@ void _outputRGBDScan_RGBD_centerPose(
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
|
||||
x = (float)poseInfo.holeLT.x;
|
||||
y = (float)poseInfo.holeLT.y;
|
||||
z = (float)poseInfo.holeLT.z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
|
||||
x = (float)poseInfo.holeRB.x;
|
||||
y = (float)poseInfo.holeRB.y;
|
||||
z = (float)poseInfo.holeRB.z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
|
||||
//输出法向
|
||||
size = 1;
|
||||
double len = 60;
|
||||
lineIdx = 0;
|
||||
{
|
||||
SVzNL3DPoint pt0 = { poseInfo.center.x - len * poseInfo.normalDir.x,
|
||||
poseInfo.center.y - len * poseInfo.normalDir.y,
|
||||
poseInfo.center.z - len * poseInfo.normalDir.z };
|
||||
SVzNL3DPoint pt0 = { poseInfo.center.x, // - len * poseInfo.normalDir.x,
|
||||
poseInfo.center.y, // - len * poseInfo.normalDir.y,
|
||||
poseInfo.center.z }; // - len * poseInfo.normalDir.z };
|
||||
SVzNL3DPoint pt1 = { poseInfo.center.x + len * poseInfo.normalDir.x,
|
||||
poseInfo.center.y + len * poseInfo.normalDir.y,
|
||||
poseInfo.center.z + len * poseInfo.normalDir.z };
|
||||
@ -535,6 +546,37 @@ void _outputRGBDScan_RGBD_centerPose(
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
|
||||
rgb = { 0, 250, 0 };
|
||||
basePt = { poseInfo.holeLT.x - len * poseInfo.xDir.x,
|
||||
poseInfo.holeLT.y - len * poseInfo.xDir.y,
|
||||
poseInfo.holeLT.z - len * poseInfo.xDir.z };
|
||||
pt2 = { poseInfo.holeLT.x + len * poseInfo.xDir.x,
|
||||
poseInfo.holeLT.y + len * poseInfo.xDir.y,
|
||||
poseInfo.holeLT.z + len * poseInfo.xDir.z };
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)basePt.x << "," << (float)basePt.y << "," << (float)basePt.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << pt2.x << "," << pt2.y << "," << pt2.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
|
||||
basePt = { poseInfo.holeRB.x - len * poseInfo.xDir.x,
|
||||
poseInfo.holeRB.y - len * poseInfo.xDir.y,
|
||||
poseInfo.holeRB.z - len * poseInfo.xDir.z };
|
||||
pt2 = { poseInfo.holeRB.x + len * poseInfo.xDir.x,
|
||||
poseInfo.holeRB.y + len * poseInfo.xDir.y,
|
||||
poseInfo.holeRB.z + len * poseInfo.xDir.z };
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)basePt.x << "," << (float)basePt.y << "," << (float)basePt.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << pt2.x << "," << pt2.y << "," << pt2.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
}
|
||||
}
|
||||
sw.close();
|
||||
@ -727,16 +769,22 @@ void _outputRGBDScan_RGBD_weldSeam(
|
||||
int centerFlag = pt3D->nPointIdx >> 4;
|
||||
if (centerFlag > 0)
|
||||
{
|
||||
rgb = { 180, 0, 0 };
|
||||
size = 2;
|
||||
if (centerFlag <= 2)
|
||||
{
|
||||
rgb = { 180, 0, 0 };
|
||||
size = 2;
|
||||
}
|
||||
else if(centerFlag == 4)
|
||||
{
|
||||
rgb = { 0, 250, 0 };
|
||||
size = 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rgb = objColor[pt3D->nPointIdx % 8];
|
||||
size = 2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else //if (pt3D->nPointIdx == 0)
|
||||
{
|
||||
@ -754,52 +802,75 @@ void _outputRGBDScan_RGBD_weldSeam(
|
||||
|
||||
if (objNum > 0)
|
||||
{
|
||||
sw << "Line_" << lineIdx << "_0_" << objNum << std::endl;
|
||||
size = 12;
|
||||
std::vector<SVzNL3DPoint> weldPoints;
|
||||
for (int i = 0; i < objNum; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
rgb = { 250, 255, 0 };
|
||||
if (weldSeamInfo[i].weldType == KeWD_WELD_POINT)
|
||||
weldPoints.push_back(weldSeamInfo[i].center);
|
||||
else
|
||||
rgb = { 250, 0, 0 };
|
||||
float x = (float)weldSeamInfo[i].center.x;
|
||||
float y = (float)weldSeamInfo[i].center.y;
|
||||
float z = (float)weldSeamInfo[i].center.z;
|
||||
{
|
||||
weldPoints.push_back(weldSeamInfo[i].startPt);
|
||||
weldPoints.push_back(weldSeamInfo[i].center);
|
||||
weldPoints.push_back(weldSeamInfo[i].endPt);
|
||||
}
|
||||
}
|
||||
|
||||
sw << "Line_" << lineIdx << "_0_" << (int)weldPoints.size() << std::endl;
|
||||
size = 15;
|
||||
for (int i = 0; i < (int)weldPoints.size(); i++)
|
||||
{
|
||||
rgb = { 250, 0, 0 };
|
||||
float x = (float)weldPoints[i].x;
|
||||
float y = (float)weldPoints[i].y;
|
||||
float z = (float)weldPoints[i].z;
|
||||
sw << "{" << x << "," << y << "," << z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << rgb.r << "," << rgb.g << "," << rgb.b << "," << size << " }" << std::endl;
|
||||
}
|
||||
//输出法向
|
||||
size = 8;
|
||||
double len = 60;
|
||||
size = 4;
|
||||
double len1 = 20;
|
||||
double len2 = 20;
|
||||
lineIdx = 0;
|
||||
for (int i = 0; i < objNum; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
rgb = { 250, 255, 0 };
|
||||
SVzNL3DPoint pt0, pt1;
|
||||
if (weldSeamInfo[i].weldType == KeWD_WELD_POINT)
|
||||
{
|
||||
pt0 = weldSeamInfo[i].center;
|
||||
pt1 = { weldSeamInfo[i].center.x + len1 * weldSeamInfo[i].axialDir.x,
|
||||
weldSeamInfo[i].center.y + len1 * weldSeamInfo[i].axialDir.y,
|
||||
weldSeamInfo[i].center.z + len1 * weldSeamInfo[i].axialDir.z };
|
||||
}
|
||||
else
|
||||
rgb = { 250, 0, 0 };
|
||||
SVzNL3DPoint pt0 = { weldSeamInfo[i].center.x, weldSeamInfo[i].center.y, weldSeamInfo[i].center.z };
|
||||
SVzNL3DPoint pt2 = { weldSeamInfo[i].center.x + len * weldSeamInfo[i].normalDir.x,
|
||||
weldSeamInfo[i].center.y + len * weldSeamInfo[i].normalDir.y,
|
||||
weldSeamInfo[i].center.z + len * weldSeamInfo[i].normalDir.z };
|
||||
{
|
||||
pt0 = weldSeamInfo[i].startPt;
|
||||
pt1 = weldSeamInfo[i].endPt;
|
||||
}
|
||||
|
||||
SVzNL3DPoint pt2 = { weldSeamInfo[i].center.x, weldSeamInfo[i].center.y, weldSeamInfo[i].center.z };
|
||||
SVzNL3DPoint pt3 = { weldSeamInfo[i].center.x + len2 * weldSeamInfo[i].normalDir.x,
|
||||
weldSeamInfo[i].center.y + len2 * weldSeamInfo[i].normalDir.y,
|
||||
weldSeamInfo[i].center.z + len2 * weldSeamInfo[i].normalDir.z };
|
||||
//显示轴向量
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)weldSeamInfo[i].startPt.x << "," << (float)weldSeamInfo[i].startPt.y << "," << (float)weldSeamInfo[i].startPt.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << (float)weldSeamInfo[i].endPt.x << "," << (float)weldSeamInfo[i].endPt.y << "," << (float)weldSeamInfo[i].endPt.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
//显示法向量
|
||||
rgb = { 0, 0, 250 };
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt0.x << "," << (float)pt0.y << "," << (float)pt0.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << (float)pt1.x << "," << (float)pt1.y << "," << (float)pt1.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
//显示法向量
|
||||
rgb = { 250, 0, 0 };
|
||||
sw << "Poly_" << lineIdx << "_2" << std::endl;
|
||||
sw << "{" << (float)pt2.x << "," << (float)pt2.y << "," << (float)pt2.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
sw << "{" << (float)pt3.x << "," << (float)pt3.y << "," << (float)pt3.z << "}-";
|
||||
sw << "{0,0}-{0,0}-";
|
||||
sw << "{" << (int)rgb.r << "," << (int)rgb.g << "," << (int)rgb.b << "," << size << "}" << std::endl;
|
||||
lineIdx++;
|
||||
}
|
||||
lineIdx++;
|
||||
@ -807,27 +878,38 @@ void _outputRGBDScan_RGBD_weldSeam(
|
||||
sw.close();
|
||||
}
|
||||
|
||||
#define SCREW_TEST_GROUP 1
|
||||
#define SCREW_TEST_GROUP 7
|
||||
void screwTest(void)
|
||||
{
|
||||
const char* dataPath[SCREW_TEST_GROUP] = {
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/数据/模拟数据/", //0
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云2/上方两根/", //1
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云3/", //2
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置1/", //3
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置2/", //4
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置2未动螺杆拧进去100mm左右/", //5
|
||||
"F:/ShangGu/项目/冠钦项目/螺杆测量/配天现场点云/螺杆点云4/位置2向前100mm螺杆拧进去100mm左右/", //6
|
||||
};
|
||||
|
||||
SVzNLRange fileIdx[SCREW_TEST_GROUP] = {
|
||||
{1,4},
|
||||
{1,4},{1,30},{1,11},
|
||||
{1,20}, {1,20}, {1,5}, {1,21}
|
||||
};
|
||||
|
||||
const char* ver = wd_rodAndBarDetectionVersion();
|
||||
printf("ver:%s\n", ver);
|
||||
|
||||
for (int grp = 0; grp < SCREW_TEST_GROUP; grp++)
|
||||
for (int grp = 3; grp < SCREW_TEST_GROUP; grp++)
|
||||
{
|
||||
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
|
||||
{
|
||||
//fidx =7;
|
||||
//fidx =3;
|
||||
char _scan_file[256];
|
||||
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
|
||||
|
||||
if(0 == grp)
|
||||
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
|
||||
else
|
||||
sprintf_s(_scan_file, "%s%d_LaserData_Jl26C299.txt", dataPath[grp], fidx);
|
||||
|
||||
std::vector<std::vector< SVzNL3DPosition>> scanLines;
|
||||
wdReadLaserScanPointFromFile_XYZ_vector(_scan_file, scanLines);
|
||||
@ -838,10 +920,16 @@ void screwTest(void)
|
||||
|
||||
long t1 = (long)GetTickCount64();//统计时间
|
||||
|
||||
double rodDiameter = 10.0;
|
||||
double rodDiameter;
|
||||
if (grp == 0)
|
||||
rodDiameter = 10.0;
|
||||
else
|
||||
rodDiameter = 28.0; //现场螺杆直径28mm
|
||||
|
||||
//double rodDiameter = 10.0;
|
||||
|
||||
SSG_cornerParam cornerParam;
|
||||
cornerParam.cornerTh = 60; //45度角
|
||||
cornerParam.cornerTh = 30; //45度角
|
||||
cornerParam.scale = rodDiameter/4; // algoParam.bagParam.bagH / 8; // 15; // algoParam.bagParam.bagH / 8;
|
||||
cornerParam.minEndingGap = 20; // algoParam.bagParam.bagW / 4;
|
||||
cornerParam.minEndingGap_z = 5.0;
|
||||
@ -853,12 +941,12 @@ void screwTest(void)
|
||||
filterParam.outlierTh = 5;
|
||||
|
||||
SSG_treeGrowParam growParam;
|
||||
growParam.maxLineSkipNum = 10;
|
||||
growParam.yDeviation_max = 20.0;
|
||||
growParam.maxSkipDistance = 20.0;
|
||||
growParam.zDeviation_max = 50.0;//
|
||||
growParam.minLTypeTreeLen = 10; //mm, 螺杆长度
|
||||
growParam.minVTypeTreeLen = 10; //mm
|
||||
growParam.maxLineSkipNum = 30;
|
||||
growParam.yDeviation_max = 5.0;
|
||||
growParam.maxSkipDistance = 30.0;
|
||||
growParam.zDeviation_max = 50;//
|
||||
growParam.minLTypeTreeLen = 50; //mm, 螺杆长度
|
||||
growParam.minVTypeTreeLen = 50; //mm
|
||||
|
||||
bool isHorizonScan = true; //true:激光线平行槽道;false:激光线垂直槽道
|
||||
int errCode = 0;
|
||||
@ -874,7 +962,7 @@ void screwTest(void)
|
||||
&errCode);
|
||||
|
||||
long t2 = (long)GetTickCount64();
|
||||
printf("%s: %d(ms)!\n", _scan_file, (int)(t2 - t1));
|
||||
printf("%s: %d(ms), errCode=%d\n", _scan_file, (int)(t2 - t1), errCode);
|
||||
//输出测试结果
|
||||
sprintf_s(_scan_file, "%sresult\\%d_result.txt", dataPath[grp], fidx);
|
||||
_outputRGBDScan_RGBD(_scan_file, scanLines, screwInfo);
|
||||
@ -892,7 +980,7 @@ void locatingPlateTest(void)
|
||||
};
|
||||
|
||||
SVzNLRange fileIdx[LOCATING_PALTE_TEST_GROUP] = {
|
||||
{1,16},
|
||||
{1,17},
|
||||
};
|
||||
|
||||
const char* ver = wd_rodAndBarDetectionVersion();
|
||||
@ -902,7 +990,7 @@ void locatingPlateTest(void)
|
||||
{
|
||||
for (int fidx = fileIdx[grp].nMin; fidx <= fileIdx[grp].nMax; fidx++)
|
||||
{
|
||||
//fidx =4;
|
||||
//fidx =2;
|
||||
char _scan_file[256];
|
||||
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
|
||||
|
||||
@ -926,7 +1014,7 @@ void locatingPlateTest(void)
|
||||
cornerParam.jumpCornerTh_2 = 60;
|
||||
|
||||
int errCode = 0;
|
||||
SSX_pointPoseInfo centerPose = sx_getLocationPlatePose(
|
||||
SSX_platePoseInfo centerPose = sx_getLocationPlatePose(
|
||||
scanLines,
|
||||
cornerParam,
|
||||
& errCode);
|
||||
@ -942,21 +1030,22 @@ void locatingPlateTest(void)
|
||||
}
|
||||
}
|
||||
|
||||
#define ROD_POSITION_TEST_GROUP 1
|
||||
#define ROD_POSITION_TEST_GROUP 2
|
||||
void rodPositionTest(void)
|
||||
{
|
||||
const char* dataPath[ROD_POSITION_TEST_GROUP] = {
|
||||
"F:/ShangGu/项目/冠钦项目/棒材抓取/", //0
|
||||
"F:/ShangGu/项目/冠钦项目/矩森棒材抓取/", //0
|
||||
"F:/ShangGu/项目/冠钦项目/胶布圆棒抓取/模拟测试数据/", //1
|
||||
};
|
||||
|
||||
SVzNLRange fileIdx[ROD_POSITION_TEST_GROUP] = {
|
||||
{1,8},
|
||||
{1,8}, {1,5}
|
||||
};
|
||||
|
||||
const char* ver = wd_rodAndBarDetectionVersion();
|
||||
printf("ver:%s\n", ver);
|
||||
|
||||
for (int grp = 0; grp < ROD_POSITION_TEST_GROUP; grp++)
|
||||
for (int grp = 1; grp < ROD_POSITION_TEST_GROUP; grp++)
|
||||
{
|
||||
SSG_planeCalibPara poseCalibPara;
|
||||
//初始化成单位阵
|
||||
@ -980,7 +1069,10 @@ void rodPositionTest(void)
|
||||
{
|
||||
//fidx =1;
|
||||
char _scan_file[256];
|
||||
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
|
||||
if(1 == grp)
|
||||
sprintf_s(_scan_file, "%s%d_LaserData_Hi229156.txt", dataPath[grp], fidx);
|
||||
else
|
||||
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
|
||||
|
||||
std::vector<std::vector< SVzNL3DPosition>> scanLines;
|
||||
wdReadLaserScanPointFromFile_XYZ_vector(_scan_file, scanLines);
|
||||
@ -992,8 +1084,16 @@ void rodPositionTest(void)
|
||||
long t1 = (long)GetTickCount64();//统计时间
|
||||
|
||||
SSX_rodParam rodParam;
|
||||
rodParam.diameter = 52.0; //圆棒直径
|
||||
rodParam.len = 290;
|
||||
if (0 == grp)
|
||||
{
|
||||
rodParam.diameter = 52.0; //圆棒直径
|
||||
rodParam.len = 290;
|
||||
}
|
||||
else
|
||||
{
|
||||
rodParam.diameter = 68.0; //圆棒直径
|
||||
rodParam.len = 187;
|
||||
}
|
||||
|
||||
SSG_cornerParam cornerParam;
|
||||
cornerParam.cornerTh = 60; //45度角
|
||||
@ -1079,7 +1179,7 @@ void rodWeldSeamPosition_test(void)
|
||||
{
|
||||
//fidx =1;
|
||||
char _scan_file[256];
|
||||
sprintf_s(_scan_file, "%sLaserData_%d.txt", dataPath[grp], fidx);
|
||||
sprintf_s(_scan_file, "%s%d_LaserData_ID019567.txt", dataPath[grp], fidx);
|
||||
|
||||
std::vector<std::vector< SVzNL3DPosition>> scanLines;
|
||||
wdReadLaserScanPointFromFile_XYZ_vector(_scan_file, scanLines);
|
||||
@ -1091,8 +1191,8 @@ void rodWeldSeamPosition_test(void)
|
||||
long t1 = (long)GetTickCount64();//统计时间
|
||||
|
||||
SSX_rodParam rodParam;
|
||||
rodParam.diameter = 52.0; //圆棒直径
|
||||
rodParam.len = 290;
|
||||
rodParam.diameter = 16.0; //钢筋直径
|
||||
rodParam.len = 50;
|
||||
|
||||
SSG_cornerParam cornerParam;
|
||||
cornerParam.cornerTh = 60; //45度角
|
||||
@ -1103,18 +1203,19 @@ void rodWeldSeamPosition_test(void)
|
||||
cornerParam.jumpCornerTh_2 = 60;
|
||||
|
||||
SSG_outlierFilterParam filterParam;
|
||||
filterParam.continuityTh = 5.0; //噪声滤除。当相邻点的z跳变大于此门限时,检查是否为噪声。若长度小于outlierLen, 视为噪声
|
||||
filterParam.outlierTh = 5;
|
||||
filterParam.continuityTh = 4.0; //噪声滤除。当相邻点的z跳变大于此门限时,检查是否为噪声。若长度小于outlierLen, 视为噪声
|
||||
filterParam.outlierTh = 4;
|
||||
|
||||
SSG_treeGrowParam growParam;
|
||||
growParam.maxLineSkipNum = 5;
|
||||
growParam.yDeviation_max = 10.0;
|
||||
growParam.maxSkipDistance = 10.0;
|
||||
growParam.zDeviation_max = 10.0;//
|
||||
growParam.minLTypeTreeLen = 100; //mm, 螺杆长度
|
||||
growParam.minVTypeTreeLen = 100; //mm
|
||||
growParam.yDeviation_max = 5.0;
|
||||
growParam.maxSkipDistance = 20.0;
|
||||
growParam.zDeviation_max = 3.0;//
|
||||
growParam.minLTypeTreeLen = 50; //mm, 螺杆长度
|
||||
growParam.minVTypeTreeLen = 50; //mm
|
||||
|
||||
bool isHorizonScan = true; //true:激光线平行槽道;false:激光线垂直槽道
|
||||
double weldSeanRange = 100; //焊缝距钢筋交叉点的范围(最大值)
|
||||
int errCode = 0;
|
||||
std::vector<SSX_weldSeamInfo> weldSeamInfo;
|
||||
sx_rebarWeldSeamPositioning(
|
||||
@ -1124,6 +1225,7 @@ void rodWeldSeamPosition_test(void)
|
||||
filterParam,
|
||||
growParam,
|
||||
rodParam,
|
||||
weldSeanRange,
|
||||
weldSeamInfo,
|
||||
&errCode);
|
||||
long t2 = (long)GetTickCount64();
|
||||
@ -1140,13 +1242,13 @@ void rodWeldSeamPosition_test(void)
|
||||
int main()
|
||||
{
|
||||
#if 1 //螺杆定位测试
|
||||
#if 0
|
||||
#if 1
|
||||
screwTest();
|
||||
#else
|
||||
locatingPlateTest();
|
||||
#endif
|
||||
#else //棒材抓取定位测试
|
||||
#if 0
|
||||
#if 1
|
||||
rodPositionTest();
|
||||
#else
|
||||
rodWeldSeamPosition_test();
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <QObject>
|
||||
@ -19,6 +22,19 @@
|
||||
|
||||
// 前向声明
|
||||
class IYModbusTCPServer;
|
||||
class BasePresenter;
|
||||
|
||||
/**
|
||||
* @brief 相机回调上下文
|
||||
*
|
||||
* 携带 BasePresenter 指针和相机索引,解决多相机同时扫描时
|
||||
* 静态回调函数中无法识别数据来源相机的问题
|
||||
*/
|
||||
struct CameraCallbackContext
|
||||
{
|
||||
BasePresenter* presenter;
|
||||
int cameraIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 基础Presenter类
|
||||
@ -149,6 +165,18 @@ public:
|
||||
*/
|
||||
void SetWorkStatus(WorkStatus status);
|
||||
|
||||
/**
|
||||
* @brief 设置扫描配置
|
||||
* @param config 扫描配置
|
||||
*/
|
||||
void SetScanConfig(const VrScanConfig& config) { m_scanConfig = config; }
|
||||
|
||||
/**
|
||||
* @brief 获取扫描配置
|
||||
* @return 扫描配置
|
||||
*/
|
||||
VrScanConfig GetScanConfig() const { return m_scanConfig; }
|
||||
|
||||
// ============ ModbusTCP 服务相关 ============
|
||||
|
||||
/**
|
||||
@ -417,7 +445,6 @@ protected:
|
||||
m_debugParam = debugParam;
|
||||
}
|
||||
|
||||
|
||||
// ============ 公共实现方法 ============
|
||||
|
||||
/**
|
||||
@ -523,6 +550,46 @@ private:
|
||||
*/
|
||||
void StopCameraReconnectTimer();
|
||||
|
||||
// ============ 多相机同时扫描相关方法 ============
|
||||
|
||||
/**
|
||||
* @brief 添加数据到指定相机的独立缓存(同时扫描模式专用)
|
||||
* @param cameraIndex 相机索引(1-based)
|
||||
* @param dataType 数据类型
|
||||
* @param laserData 激光数据
|
||||
*/
|
||||
void AddDetectionDataToCameraCache(int cameraIndex, EVzResultDataType dataType,
|
||||
const SVzLaserLineData& laserData);
|
||||
|
||||
/**
|
||||
* @brief 清空所有相机的独立数据缓存
|
||||
*/
|
||||
void ClearAllCameraDataCaches();
|
||||
|
||||
/**
|
||||
* @brief 相机扫描完成通知(Swing_Finish 时调用)
|
||||
*
|
||||
* 增加批次完成计数
|
||||
*
|
||||
* @param cameraIndex 完成扫描的相机索引
|
||||
*/
|
||||
void OnCameraScanFinished(int cameraIndex);
|
||||
|
||||
/**
|
||||
* @brief 启动一批相机的扫描
|
||||
*
|
||||
* 从 m_batchStartIndex 开始,取 m_batchSize 个相机同时启动扫描
|
||||
*/
|
||||
void StartBatchScan();
|
||||
|
||||
/**
|
||||
* @brief 检查当前批次是否全部完成并逐个处理
|
||||
*
|
||||
* 在算法线程中调用,当前批次全部扫描完成后逐个处理每个相机的数据,
|
||||
* 处理完后自动推进到下一批或结束
|
||||
*/
|
||||
void ProcessBatchIfReady();
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief 相机重连定时器触发
|
||||
@ -577,6 +644,26 @@ protected:
|
||||
// 调试数据异步存储
|
||||
DebugDataSaver m_debugDataSaver;
|
||||
|
||||
// ============ 多相机同时扫描相关成员 ============
|
||||
|
||||
// 扫描配置
|
||||
VrScanConfig m_scanConfig;
|
||||
|
||||
// 每个相机的回调上下文(key = 相机索引 1-based)
|
||||
std::map<int, CameraCallbackContext*> m_cameraContexts;
|
||||
|
||||
// 每个相机的独立数据缓存区(key = 相机索引 1-based)
|
||||
std::map<int, std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>> m_perCameraDataCache;
|
||||
std::mutex m_perCameraDataMutex;
|
||||
|
||||
// 分批扫描调度相关
|
||||
std::vector<int> m_batchCameraList; // 本轮需扫描的相机列表
|
||||
int m_batchStartIndex = 0; // 当前批次起始位置
|
||||
int m_batchSize = 0; // 当前批次相机数
|
||||
int m_batchFinishedCount = 0; // 当前批次已完成计数
|
||||
bool m_batchInProgress = false; // 是否正在分批扫描
|
||||
std::mutex m_batchStateMutex; // 保护分批调度状态
|
||||
|
||||
private:
|
||||
// 启动ModbusTCP服务(在Init中调用)
|
||||
int StartModbusServer(int port = 5020);
|
||||
|
||||
@ -234,6 +234,32 @@ struct VrDebugParam
|
||||
VrDebugParam() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 多相机扫描配置
|
||||
*
|
||||
* 控制同时进行扫描的相机数量:
|
||||
* - 1: 单相机模式(默认,完全兼容现有行为)
|
||||
* - 0: 所有已连接相机同时扫描
|
||||
* - N: 每批 N 个相机同时扫描(N > 1)
|
||||
*/
|
||||
struct VrScanConfig
|
||||
{
|
||||
int simultaneousCount = 1;
|
||||
|
||||
VrScanConfig& operator=(const VrScanConfig& other) {
|
||||
if (this != &other) {
|
||||
simultaneousCount = other.simultaneousCount;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
VrScanConfig(const VrScanConfig& other)
|
||||
: simultaneousCount(other.simultaneousCount) {
|
||||
}
|
||||
|
||||
VrScanConfig() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 配置改变通知接口(前向声明ConfigResult)
|
||||
*/
|
||||
|
||||
@ -61,6 +61,16 @@ BasePresenter::~BasePresenter()
|
||||
}
|
||||
}
|
||||
m_vrEyeDeviceList.clear();
|
||||
|
||||
// 清理相机回调上下文
|
||||
for (auto& pair : m_cameraContexts) {
|
||||
delete pair.second;
|
||||
}
|
||||
m_cameraContexts.clear();
|
||||
|
||||
// 清理各相机独立数据缓存
|
||||
ClearAllCameraDataCaches();
|
||||
|
||||
LOG_INFO("BasePresenter destructor finished\n");
|
||||
}
|
||||
|
||||
@ -98,59 +108,138 @@ int BasePresenter::Init()
|
||||
|
||||
int BasePresenter::StartDetection(int cameraIndex, bool isAuto)
|
||||
{
|
||||
LOG_INFO("[BasePresenter] StartDetection - cameraIndex=%d, isAuto=%d\n", cameraIndex, isAuto);
|
||||
LOG_INFO("[BasePresenter] StartDetection - cameraIndex=%d, isAuto=%d, simultaneousCount=%d\n",
|
||||
cameraIndex, isAuto, m_scanConfig.simultaneousCount);
|
||||
|
||||
// 设置当前相机索引
|
||||
if (cameraIndex >= 0 && cameraIndex != -1) {
|
||||
// ===== 分支1: cameraIndex > 0,单相机模式 =====
|
||||
if (cameraIndex > 0) {
|
||||
m_currentCameraIndex = cameraIndex;
|
||||
}
|
||||
int currentCamera = m_currentCameraIndex;
|
||||
int currentCamera = m_currentCameraIndex;
|
||||
|
||||
// 检查相机列表是否为空
|
||||
if (m_vrEyeDeviceList.empty()) {
|
||||
LOG_ERROR("[BasePresenter] No camera device found\n");
|
||||
if (m_vrEyeDeviceList.empty()) {
|
||||
LOG_ERROR("[BasePresenter] No camera device found\n");
|
||||
return ERR_CODE(DEV_NOT_FIND);
|
||||
}
|
||||
|
||||
ClearDetectionDataCache();
|
||||
|
||||
int arrayIndex = currentCamera - 1;
|
||||
if (arrayIndex < 0 || arrayIndex >= static_cast<int>(m_vrEyeDeviceList.size()) ||
|
||||
m_vrEyeDeviceList[arrayIndex].second == nullptr) {
|
||||
LOG_ERROR("[BasePresenter] Camera %d is not connected or invalid\n", currentCamera);
|
||||
return ERR_CODE(DEV_NOT_FIND);
|
||||
}
|
||||
|
||||
SetWorkStatus(WorkStatus::Working);
|
||||
|
||||
IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrayIndex].second;
|
||||
EVzResultDataType eDataType = GetDetectionDataType();
|
||||
VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback();
|
||||
VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback();
|
||||
|
||||
// 使用相机专属的回调上下文
|
||||
CameraCallbackContext* ctx = m_cameraContexts[currentCamera];
|
||||
pDevice->SetStatusCallback(statusCallback, ctx);
|
||||
|
||||
int nRet = pDevice->StartDetect(detectCallback, eDataType, ctx);
|
||||
LOG_INFO("[BasePresenter] Camera %d start detection result: %d\n", currentCamera, nRet);
|
||||
|
||||
if (nRet == SUCCESS) {
|
||||
StartAlgoDetectThread();
|
||||
}
|
||||
LOG_INFO("[BasePresenter] StartDetection finish\n");
|
||||
return nRet;
|
||||
}
|
||||
|
||||
// ===== 分支2: cameraIndex <= 0 且为单相机模式,使用默认相机 =====
|
||||
if (m_scanConfig.simultaneousCount == 1) {
|
||||
cameraIndex = m_currentCameraIndex;
|
||||
if (cameraIndex <= 0) cameraIndex = 1;
|
||||
m_currentCameraIndex = cameraIndex;
|
||||
int currentCamera = m_currentCameraIndex;
|
||||
|
||||
if (m_vrEyeDeviceList.empty()) {
|
||||
LOG_ERROR("[BasePresenter] No camera device found\n");
|
||||
return ERR_CODE(DEV_NOT_FIND);
|
||||
}
|
||||
|
||||
ClearDetectionDataCache();
|
||||
|
||||
int arrayIndex = currentCamera - 1;
|
||||
if (arrayIndex < 0 || arrayIndex >= static_cast<int>(m_vrEyeDeviceList.size()) ||
|
||||
m_vrEyeDeviceList[arrayIndex].second == nullptr) {
|
||||
LOG_ERROR("[BasePresenter] Camera %d is not connected or invalid\n", currentCamera);
|
||||
return ERR_CODE(DEV_NOT_FIND);
|
||||
}
|
||||
|
||||
SetWorkStatus(WorkStatus::Working);
|
||||
|
||||
IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrayIndex].second;
|
||||
EVzResultDataType eDataType = GetDetectionDataType();
|
||||
VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback();
|
||||
VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback();
|
||||
|
||||
CameraCallbackContext* ctx = m_cameraContexts[currentCamera];
|
||||
pDevice->SetStatusCallback(statusCallback, ctx);
|
||||
|
||||
int nRet = pDevice->StartDetect(detectCallback, eDataType, ctx);
|
||||
LOG_INFO("[BasePresenter] Camera %d start detection result: %d\n", currentCamera, nRet);
|
||||
|
||||
if (nRet == SUCCESS) {
|
||||
StartAlgoDetectThread();
|
||||
}
|
||||
LOG_INFO("[BasePresenter] StartDetection finish\n");
|
||||
return nRet;
|
||||
}
|
||||
|
||||
// ===== 分支3: 多相机同时/分批扫描 =====
|
||||
LOG_INFO("[BasePresenter] 进入多相机分批扫描模式\n");
|
||||
|
||||
// 停止当前正在进行的检测
|
||||
if (m_batchInProgress || m_bAlgoDetectThreadRunning) {
|
||||
StopDetection();
|
||||
}
|
||||
|
||||
// 收集本轮所有已连接的相机
|
||||
m_batchCameraList.clear();
|
||||
for (int i = 0; i < static_cast<int>(m_vrEyeDeviceList.size()); i++) {
|
||||
if (m_vrEyeDeviceList[i].second != nullptr) {
|
||||
m_batchCameraList.push_back(i + 1); // 1-based index
|
||||
}
|
||||
}
|
||||
if (m_batchCameraList.empty()) {
|
||||
LOG_ERROR("[BasePresenter] No connected cameras for batch scan\n");
|
||||
return ERR_CODE(DEV_NOT_FIND);
|
||||
}
|
||||
|
||||
// 清空检测数据缓存
|
||||
ClearDetectionDataCache();
|
||||
|
||||
int nRet = SUCCESS;
|
||||
|
||||
// 启动指定相机(cameraIndex为相机ID,从1开始编号)
|
||||
int arrayIndex = currentCamera - 1; // 转换为数组索引(从0开始)
|
||||
|
||||
// 检查相机是否连接
|
||||
if (arrayIndex < 0 || arrayIndex >= static_cast<int>(m_vrEyeDeviceList.size()) ||
|
||||
m_vrEyeDeviceList[arrayIndex].second == nullptr) {
|
||||
LOG_ERROR("[BasePresenter] Camera %d is not connected or invalid\n", currentCamera);
|
||||
return ERR_CODE(DEV_NOT_FIND);
|
||||
// 计算每批大小
|
||||
int totalCameras = static_cast<int>(m_batchCameraList.size());
|
||||
if (m_scanConfig.simultaneousCount == 0) {
|
||||
m_batchSize = totalCameras;
|
||||
} else {
|
||||
m_batchSize = std::min(m_scanConfig.simultaneousCount, totalCameras);
|
||||
}
|
||||
|
||||
// 初始化分批状态
|
||||
m_batchStartIndex = 0;
|
||||
m_batchFinishedCount = 0;
|
||||
m_batchInProgress = true;
|
||||
|
||||
// 清空所有 per-camera 缓存
|
||||
ClearAllCameraDataCaches();
|
||||
|
||||
SetWorkStatus(WorkStatus::Working);
|
||||
|
||||
IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrayIndex].second;
|
||||
|
||||
// 获取数据类型(由子类决定)
|
||||
EVzResultDataType eDataType = GetDetectionDataType();
|
||||
|
||||
// 设置状态回调
|
||||
VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback();
|
||||
pDevice->SetStatusCallback(statusCallback, this);
|
||||
|
||||
// 获取检测回调函数(由子类提供)
|
||||
VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback();
|
||||
|
||||
// 开始检测
|
||||
nRet = pDevice->StartDetect(detectCallback, eDataType, this);
|
||||
LOG_INFO("[BasePresenter] Camera %d start detection result: %d\n", currentCamera, nRet);
|
||||
|
||||
if (nRet == SUCCESS) {
|
||||
// 启动算法检测线程
|
||||
// 启动算法检测线程
|
||||
if (!m_bAlgoDetectThreadRunning) {
|
||||
StartAlgoDetectThread();
|
||||
}
|
||||
LOG_INFO("[BasePresenter] StartDetection finish \n");
|
||||
return nRet;
|
||||
|
||||
// 启动第一批相机扫描
|
||||
StartBatchScan();
|
||||
|
||||
LOG_INFO("[BasePresenter] 分批扫描已启动: total=%d, batchSize=%d\n", totalCameras, m_batchSize);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int BasePresenter::StopDetection()
|
||||
@ -173,6 +262,15 @@ int BasePresenter::StopDetection()
|
||||
// 停止算法检测线程
|
||||
StopAlgoDetectThread();
|
||||
|
||||
// 清理多相机分批扫描状态
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_batchStateMutex);
|
||||
m_batchInProgress = false;
|
||||
m_batchFinishedCount = 0;
|
||||
m_batchCameraList.clear();
|
||||
}
|
||||
ClearAllCameraDataCaches();
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@ -420,9 +518,19 @@ int BasePresenter::OpenDevice(int cameraIndex, const char* cameraName, const cha
|
||||
pDevice = nullptr;
|
||||
} else {
|
||||
|
||||
// 设置状态回调(调用子类提供的回调函数)
|
||||
// 释放旧的回调上下文(重连场景)
|
||||
auto oldCtx = m_cameraContexts.find(cameraIndex);
|
||||
if (oldCtx != m_cameraContexts.end()) {
|
||||
delete oldCtx->second;
|
||||
}
|
||||
|
||||
// 创建新的回调上下文,携带相机索引
|
||||
CameraCallbackContext* ctx = new CameraCallbackContext{this, cameraIndex};
|
||||
m_cameraContexts[cameraIndex] = ctx;
|
||||
|
||||
// 设置状态回调(使用带相机索引的上下文)
|
||||
VzNL_OnNotifyStatusCBEx callback = GetCameraStatusCallback();
|
||||
nRet = pDevice->SetStatusCallback(callback, this);
|
||||
nRet = pDevice->SetStatusCallback(callback, ctx);
|
||||
LOG_DEBUG("[BasePresenter] SetStatusCallback result: %d\n", nRet);
|
||||
if (nRet != SUCCESS) {
|
||||
delete pDevice;
|
||||
@ -451,22 +559,25 @@ void BasePresenter::AlgoDetectThreadFunc()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_algoDetectMutex);
|
||||
|
||||
// 等待检测触发(子类需要调用 m_algoDetectCondition.notify_one() 来触发)
|
||||
// 等待检测触发
|
||||
m_algoDetectCondition.wait(lock);
|
||||
|
||||
if(!m_bAlgoDetectThreadRunning){
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_INFO("[BasePresenter] 检测线程被唤醒,开始执行检测任务\n");
|
||||
|
||||
// 执行检测任务
|
||||
int nRet = DetectTask();
|
||||
|
||||
if(nRet != SUCCESS){
|
||||
LOG_ERROR("[BasePresenter] 检测任务执行失败,错误码: %d\n", nRet);
|
||||
if (m_scanConfig.simultaneousCount > 1 || m_scanConfig.simultaneousCount == 0) {
|
||||
// 多相机分批模式:检查批次是否完成并处理
|
||||
ProcessBatchIfReady();
|
||||
} else {
|
||||
LOG_INFO("[BasePresenter] 检测任务执行成功\n");
|
||||
// 单相机模式:直接执行检测任务
|
||||
LOG_INFO("[BasePresenter] 检测线程被唤醒,开始执行检测任务\n");
|
||||
int nRet = DetectTask();
|
||||
if(nRet != SUCCESS){
|
||||
LOG_ERROR("[BasePresenter] 检测任务执行失败,错误码: %d\n", nRet);
|
||||
} else {
|
||||
LOG_INFO("[BasePresenter] 检测任务执行成功\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -543,7 +654,13 @@ int BasePresenter::DetectTask()
|
||||
int nRet = ProcessAlgoDetection(m_detectionDataCache);
|
||||
LOG_INFO("[BasePresenter] ProcessAlgoDetection 执行结果: %d\n", nRet);
|
||||
|
||||
SetWorkStatus(WorkStatus::Completed);
|
||||
// 批量模式下不在这里设置Completed,由ProcessBatchIfReady统一设置
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_batchStateMutex);
|
||||
if (!m_batchInProgress) {
|
||||
SetWorkStatus(WorkStatus::Completed);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("[BasePresenter] DetectTask - 检测任务执行成功\n");
|
||||
return nRet;
|
||||
@ -618,12 +735,14 @@ void BasePresenter::_StaticDetectionCallback(EVzResultDataType eDataType, SVzLas
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取 BasePresenter 实例指针
|
||||
BasePresenter* pThis = reinterpret_cast<BasePresenter*>(pUserData);
|
||||
if (!pThis) {
|
||||
LOG_ERROR("[BasePresenter Detection Callback] pUserData is null\n");
|
||||
// 提取回调上下文(多相机模式下携带相机索引)
|
||||
CameraCallbackContext* ctx = static_cast<CameraCallbackContext*>(pUserData);
|
||||
if (!ctx || !ctx->presenter) {
|
||||
LOG_ERROR("[BasePresenter Detection Callback] invalid context\n");
|
||||
return;
|
||||
}
|
||||
BasePresenter* pThis = ctx->presenter;
|
||||
int cameraIndex = ctx->cameraIndex;
|
||||
|
||||
// 创建 SVzLaserLineData 副本
|
||||
SVzLaserLineData lineData;
|
||||
@ -680,8 +799,14 @@ void BasePresenter::_StaticDetectionCallback(EVzResultDataType eDataType, SVzLas
|
||||
lineData.fSwingAngle = pLaserLinePoint->fSwingAngle;
|
||||
lineData.bEndOnceScan = pLaserLinePoint->bEndOnceScan;
|
||||
|
||||
// 添加到检测数据缓存
|
||||
pThis->AddDetectionDataToCache(eDataType, lineData);
|
||||
// 根据扫描模式分流存储
|
||||
if (pThis->m_scanConfig.simultaneousCount > 1 || pThis->m_scanConfig.simultaneousCount == 0) {
|
||||
// 多相机同时扫描模式:存入该相机的独立缓存
|
||||
pThis->AddDetectionDataToCameraCache(cameraIndex, eDataType, lineData);
|
||||
} else {
|
||||
// 单相机模式:存入共享缓存
|
||||
pThis->AddDetectionDataToCache(eDataType, lineData);
|
||||
}
|
||||
}
|
||||
|
||||
// 通用的静态相机状态回调函数实现
|
||||
@ -689,43 +814,44 @@ void BasePresenter::_StaticCameraStatusCallback(EVzDeviceWorkStatus eStatus, voi
|
||||
{
|
||||
LOG_DEBUG("[BasePresenter Camera Status Callback] received: status=%d\n", (int)eStatus);
|
||||
|
||||
// 获取 BasePresenter 实例指针
|
||||
BasePresenter* pThis = reinterpret_cast<BasePresenter*>(pInfoParam);
|
||||
if (!pThis) {
|
||||
LOG_ERROR("[BasePresenter Camera Status Callback] pInfoParam is null\n");
|
||||
// 提取回调上下文(携带相机索引)
|
||||
CameraCallbackContext* ctx = static_cast<CameraCallbackContext*>(pInfoParam);
|
||||
if (!ctx || !ctx->presenter) {
|
||||
LOG_ERROR("[BasePresenter Camera Status Callback] invalid context\n");
|
||||
return;
|
||||
}
|
||||
BasePresenter* pThis = ctx->presenter;
|
||||
int cameraIndex = ctx->cameraIndex;
|
||||
|
||||
switch (eStatus) {
|
||||
case EVzDeviceWorkStatus::keDeviceWorkStatus_Offline:
|
||||
{
|
||||
LOG_WARNING("[BasePresenter Camera Status Callback] Camera device offline/disconnected\n");
|
||||
LOG_WARNING("[BasePresenter Camera Status Callback] Camera %d offline/disconnected\n", cameraIndex);
|
||||
|
||||
// 更新相机连接状态
|
||||
pThis->m_bCameraConnected = false;
|
||||
|
||||
// 通知子类相机状态变更(这里暂时通知相机1,实际应用中可能需要区分)
|
||||
pThis->OnCameraStatusChanged(1, false);
|
||||
pThis->OnCameraStatusChanged(cameraIndex, false);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVzDeviceWorkStatus::keDeviceWorkStatus_Eye_Reconnect:
|
||||
{
|
||||
LOG_INFO("[BasePresenter Camera Status Callback] Camera device online/connected\n");
|
||||
LOG_INFO("[BasePresenter Camera Status Callback] Camera %d online/connected\n", cameraIndex);
|
||||
|
||||
// 更新相机连接状态
|
||||
pThis->m_bCameraConnected = true;
|
||||
|
||||
// 通知子类相机状态变更
|
||||
pThis->OnCameraStatusChanged(1, true);
|
||||
pThis->OnCameraStatusChanged(cameraIndex, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVzDeviceWorkStatus::keDeviceWorkStatus_Device_Swing_Finish:
|
||||
{
|
||||
LOG_INFO("[BasePresenter Camera Status Callback] Received scan finish signal from camera\n");
|
||||
LOG_INFO("[BasePresenter Camera Status Callback] Camera %d scan finished\n", cameraIndex);
|
||||
|
||||
// 通知算法检测线程开始处理
|
||||
// 多相机模式下记录批次完成
|
||||
if (pThis->m_scanConfig.simultaneousCount > 1 || pThis->m_scanConfig.simultaneousCount == 0) {
|
||||
pThis->OnCameraScanFinished(cameraIndex);
|
||||
}
|
||||
|
||||
// 唤醒算法检测线程
|
||||
pThis->m_algoDetectCondition.notify_one();
|
||||
break;
|
||||
}
|
||||
@ -919,3 +1045,168 @@ int BasePresenter::OnModbusWriteRegisters(uint8_t unitId, uint16_t startAddress,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============ 多相机同时扫描方法实现 ============
|
||||
|
||||
void BasePresenter::AddDetectionDataToCameraCache(int cameraIndex,
|
||||
EVzResultDataType dataType, const SVzLaserLineData& laserData)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_perCameraDataMutex);
|
||||
m_perCameraDataCache[cameraIndex].push_back(std::make_pair(dataType, laserData));
|
||||
}
|
||||
|
||||
void BasePresenter::ClearAllCameraDataCaches()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_perCameraDataMutex);
|
||||
for (auto& pair : m_perCameraDataCache) {
|
||||
m_dataLoader.FreeLaserScanData(pair.second);
|
||||
}
|
||||
m_perCameraDataCache.clear();
|
||||
}
|
||||
|
||||
void BasePresenter::OnCameraScanFinished(int cameraIndex)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_batchStateMutex);
|
||||
m_batchFinishedCount++;
|
||||
LOG_INFO("[BasePresenter] Camera %d finished, batch progress: %d/%d\n",
|
||||
cameraIndex, m_batchFinishedCount, m_batchSize);
|
||||
}
|
||||
|
||||
void BasePresenter::StartBatchScan()
|
||||
{
|
||||
int batchEnd = std::min(m_batchStartIndex + m_batchSize, static_cast<int>(m_batchCameraList.size()));
|
||||
int batchCount = batchEnd - m_batchStartIndex;
|
||||
|
||||
LOG_INFO("[BasePresenter] StartBatchScan: cameras [%d, %d), count=%d\n",
|
||||
m_batchStartIndex, batchEnd, batchCount);
|
||||
|
||||
if (batchCount <= 0) {
|
||||
LOG_WARNING("[BasePresenter] StartBatchScan: empty batch\n");
|
||||
return;
|
||||
}
|
||||
|
||||
EVzResultDataType eDataType = GetDetectionDataType();
|
||||
VzNL_AutoOutputLaserLineExCB detectCallback = GetDetectionCallback();
|
||||
VzNL_OnNotifyStatusCBEx statusCallback = GetCameraStatusCallback();
|
||||
|
||||
for (int i = m_batchStartIndex; i < batchEnd; i++) {
|
||||
int cameraIndex = m_batchCameraList[i];
|
||||
int arrIndex = cameraIndex - 1;
|
||||
|
||||
if (arrIndex < 0 || arrIndex >= static_cast<int>(m_vrEyeDeviceList.size())) {
|
||||
LOG_WARNING("[BasePresenter] Camera %d index out of range, skipping\n", cameraIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
IVrEyeDevice* pDevice = m_vrEyeDeviceList[arrIndex].second;
|
||||
if (!pDevice) {
|
||||
LOG_WARNING("[BasePresenter] Camera %d device is null, skipping\n", cameraIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 清空该相机的独立缓存
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_perCameraDataMutex);
|
||||
auto it = m_perCameraDataCache.find(cameraIndex);
|
||||
if (it != m_perCameraDataCache.end()) {
|
||||
m_dataLoader.FreeLaserScanData(it->second);
|
||||
m_perCameraDataCache.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用相机专属的回调上下文
|
||||
CameraCallbackContext* ctx = m_cameraContexts[cameraIndex];
|
||||
pDevice->SetStatusCallback(statusCallback, ctx);
|
||||
|
||||
int nRet = pDevice->StartDetect(detectCallback, eDataType, ctx);
|
||||
LOG_INFO("[BasePresenter] Camera %d start detect: %d\n", cameraIndex, nRet);
|
||||
}
|
||||
|
||||
LOG_INFO("[BasePresenter] Batch scan started: %d cameras\n", batchCount);
|
||||
}
|
||||
|
||||
void BasePresenter::ProcessBatchIfReady()
|
||||
{
|
||||
// 检查当前批次是否全部完成
|
||||
bool batchComplete = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_batchStateMutex);
|
||||
if (!m_batchInProgress) return;
|
||||
batchComplete = (m_batchFinishedCount >= m_batchSize);
|
||||
}
|
||||
|
||||
if (!batchComplete) {
|
||||
// 批次未完成,可能是被提前唤醒(Offline 等事件)
|
||||
return;
|
||||
}
|
||||
|
||||
int batchEnd = std::min(m_batchStartIndex + m_batchSize, static_cast<int>(m_batchCameraList.size()));
|
||||
|
||||
LOG_INFO("[BasePresenter] 批次完成,处理相机 [%d, %d)\n", m_batchStartIndex, batchEnd);
|
||||
|
||||
// 逐个处理批次内每个相机的数据
|
||||
for (int i = m_batchStartIndex; i < batchEnd; i++) {
|
||||
int cameraIndex = m_batchCameraList[i];
|
||||
|
||||
// 将相机数据从 per-camera 缓存移到 m_detectionDataCache
|
||||
{
|
||||
std::lock_guard<std::mutex> dataLock(m_perCameraDataMutex);
|
||||
auto it = m_perCameraDataCache.find(cameraIndex);
|
||||
if (it != m_perCameraDataCache.end() && !it->second.empty()) {
|
||||
std::lock_guard<std::mutex> detLock(m_detectionDataMutex);
|
||||
m_dataLoader.FreeLaserScanData(m_detectionDataCache);
|
||||
m_detectionDataCache.clear();
|
||||
m_detectionDataCache = std::move(it->second);
|
||||
m_perCameraDataCache.erase(it);
|
||||
} else {
|
||||
LOG_WARNING("[BasePresenter] 相机%d 无扫描数据,跳过\n", cameraIndex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
m_currentCameraIndex = cameraIndex;
|
||||
|
||||
LOG_INFO("[BasePresenter] 处理相机 %d (%d/%d)\n",
|
||||
cameraIndex, i - m_batchStartIndex + 1, m_batchSize);
|
||||
|
||||
int nRet = DetectTask();
|
||||
if (nRet != SUCCESS) {
|
||||
LOG_ERROR("[BasePresenter] 相机%d 检测失败: %d\n", cameraIndex, nRet);
|
||||
}
|
||||
}
|
||||
|
||||
// 推进到下一批
|
||||
bool hasMore = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_batchStateMutex);
|
||||
|
||||
m_batchStartIndex = batchEnd;
|
||||
m_batchFinishedCount = 0;
|
||||
|
||||
int remaining = static_cast<int>(m_batchCameraList.size()) - m_batchStartIndex;
|
||||
if (remaining > 0) {
|
||||
if (m_scanConfig.simultaneousCount == 0) {
|
||||
m_batchSize = remaining;
|
||||
} else {
|
||||
m_batchSize = std::min(m_scanConfig.simultaneousCount, remaining);
|
||||
}
|
||||
hasMore = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMore) {
|
||||
LOG_INFO("[BasePresenter] 启动下一批扫描: start=%d, size=%d\n", m_batchStartIndex, m_batchSize);
|
||||
// 在主线程中启动下一批扫描
|
||||
QMetaObject::invokeMethod(this, [this]() {
|
||||
StartBatchScan();
|
||||
}, Qt::QueuedConnection);
|
||||
} else {
|
||||
LOG_INFO("[BasePresenter] 所有批次扫描完成,共 %d 个相机\n",
|
||||
static_cast<int>(m_batchCameraList.size()));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_batchStateMutex);
|
||||
m_batchInProgress = false;
|
||||
}
|
||||
SetWorkStatus(WorkStatus::Completed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,4 +12,8 @@ SUBDIRS += \
|
||||
GalaxyDevice/GalaxyDevice.pro \
|
||||
HikDevice/HikDevice.pro \
|
||||
GlLineLaserDevice/GlLineLaserDevice.pro
|
||||
# RsLidarDevice/RsLidarDevice.pro
|
||||
|
||||
|
||||
win32-msvc {
|
||||
SUBDIRS += RsLidarDevice/RsLidarDevice.pro
|
||||
}
|
||||
|
||||
@ -15,8 +15,9 @@
|
||||
| 9 | 颗粒尺寸检测 | ParticleSize | 1.0.0.0 |
|
||||
| 10 | 双目标记检测 | BinocularMarkServer | 1.0.0.4 |
|
||||
| 11 | 铁路隧道槽道测量 | TunnelChannel | 1.0.0.3 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.1.8.1 |
|
||||
| 12 | 螺杆定位 | ScrewPosition | 1.1.10.1 |
|
||||
| 13 | 包裹拆线位置定位 | BagThreadPosition | 1.0.0.4 |
|
||||
| 14 | 工件孔定位 | WorkpieceHole | 1.1.5.1 |
|
||||
| 16 | 坑孔定位 | HolePitPosition | 无 |
|
||||
| 17 | 钢筋焊缝定位 | RodWeldSeam | 1.0.0.1 |
|
||||
|
||||
|
||||
@ -27,6 +27,8 @@ Device.depends = Utils AppUtils
|
||||
AppUtils.depends = Utils Module
|
||||
App.depends = Utils VrNets Module Device AppUtils
|
||||
Tools.depends = Module Utils Robot
|
||||
Test.depends = Device Module
|
||||
|
||||
# Test 测试
|
||||
SUBDIRS += ../Test/Test.pro
|
||||
Test.file = ../Test/Test.pro
|
||||
SUBDIRS += Test
|
||||
|
||||
@ -44,6 +44,7 @@ DESKTOP_PROJECTS=(
|
||||
"WorkpieceHole;workpieceHolePositioning"
|
||||
"StatorPosition;motorStatorPosition"
|
||||
"RodAndBarPosition;rodAndBarDetection"
|
||||
"RodWeldSeam;rodAndBarDetection"
|
||||
"HolePitPosition;workpieceHolePositioning"
|
||||
"WheelMeasure;wheelArchHeigthMeasure"
|
||||
|
||||
|
||||
@ -11,3 +11,8 @@ SUBDIRS += \
|
||||
AuthModule/AuthModule.pro \
|
||||
HandEyeCalib/HandEyeCalib.pro \
|
||||
ChessboardDetector/ChessboardDetector.pro
|
||||
|
||||
|
||||
win32-msvc {
|
||||
SUBDIRS += CloudShow/CloudShow.pro
|
||||
}
|
||||
@ -3,4 +3,8 @@ TEMPLATE = subdirs
|
||||
# 撕裂项目
|
||||
# SUBDIRS += tcpclient/tcpclient_test.pro
|
||||
# SUBDIRS += tcpserver/tcpserver_test.pro
|
||||
# SUBDIRS += RsLidarTest/RsLidarTest.pro
|
||||
|
||||
|
||||
win32-msvc {
|
||||
SUBDIRS += RsLidarTest/RsLidarTest.pro
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user