From b5ef4a0a5d43d2d1c85195e48ea6241ebbd544e1 Mon Sep 17 00:00:00 2001 From: yiyi Date: Sat, 28 Mar 2026 14:02:51 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=A3=92=E6=9D=90=E7=9A=84?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/RodAndBarPosition/Doc/MODBUS_PROTOCOL.md | 57 ++++++++++--- .../Inc/RodAndBarPositionPresenter.h | 12 ++- .../Src/RodAndBarPositionPresenter.cpp | 81 ++++++++++++++----- .../RodAndBarPositionApp.pro | 1 + .../RodAndBarPositionApp/Version.h | 2 +- .../RodAndBarPositionApp/Version.md | 8 ++ GrabBagPrj/pkg_binocularmark.sh | 6 ++ 7 files changed, 134 insertions(+), 33 deletions(-) create mode 100644 App/RodAndBarPosition/RodAndBarPositionApp/Version.md diff --git a/App/RodAndBarPosition/Doc/MODBUS_PROTOCOL.md b/App/RodAndBarPosition/Doc/MODBUS_PROTOCOL.md index c03eea9..1474147 100644 --- a/App/RodAndBarPosition/Doc/MODBUS_PROTOCOL.md +++ b/App/RodAndBarPosition/Doc/MODBUS_PROTOCOL.md @@ -17,7 +17,7 @@ | 地址 | 寄存器数 | 类型 | 读/写 | 说明 | |------|----------|------|-------|------| -| 0 | 1 | uint16 | R/W | 检测触发(写入非零值触发,1=相机1,2=相机2,触发后自动清零) | +| 0 | 1 | uint16 | R/W | 控制命令(写入后自动清零,命令见下表) | | 1 | 1 | uint16 | R | 状态码 | | 2 | 2 | float | R | X(中心点 X 坐标,mm) | | 4 | 2 | float | R | Y(中心点 Y 坐标,mm) | @@ -30,14 +30,22 @@ > **坐标系**:XYZ 为机械臂坐标系下的值(已经过手眼标定转换),RPY 为 ZYX 外旋欧拉角 +## 3.1 控制命令(地址 0) + +| 命令值 | 说明 | +|--------|------| +| 1 | 触发相机1执行检测 | +| 2 | 触发相机2执行检测 | +| 10 | 读取下一个缓存棒材结果(从上次检测结果中依次获取) | + ## 4. 状态码定义 | 状态码 | 说明 | |--------|------| | 0 | 正在检测 | -| 1 | 检测成功(地址 2~13 数据有效) | +| 1 | 成功(地址 2~13 数据有效) | | 2 | 检测失败 | -| 3 | 检测成功但结果为空 | +| 3 | 结果为空 / 无更多缓存结果 | ## 5. 通信流程 @@ -46,6 +54,7 @@ 1. 外部客户端写入地址 0,值为相机编号(1 或 2) 2. 视觉系统自动清零地址 0,并将地址 1 设为 0(正在检测) 3. 视觉系统触发对应相机的检测 +4. 检测完成后自动输出第一个棒材结果(地址 1=1,地址 2~13=坐标) ### 5.2 读取结果 @@ -55,27 +64,55 @@ 2. 外部客户端轮询地址 1,当值从 0 变为非零时表示检测完成 3. 状态码为 1 时,读取地址 2~13 获取定位结果 -### 5.3 典型时序 +### 5.3 典型时序(单棒材) ``` 客户端 视觉系统 | | - |--- 写入 地址0 = 1 (触发相机1) ----->| + |--- 写入 地址0 = 1 (触发相机1) ------>| | |-- 清零地址0,地址1=0(正在检测) | |-- 执行检测... | | - |<-- 读取 地址1 = 0 (正在检测) -------| + |<-- 读取 地址1 = 0 (正在检测) --------| | | - |<-- 读取 地址1 = 1 (检测成功) -------| - | |-- 写入地址1=1, 地址2~13=结果 - |<-- 读取 地址2~13 (XYZ+RPY) --------| + |<-- 读取 地址1 = 1 (检测成功) --------| + | |-- 写入地址1=1, 地址2~13=第1个棒材结果 + |<-- 读取 地址2~13 (XYZ+RPY) ---------| | | ``` +### 5.4 读取多棒材结果时序 + +当检测到多个棒材时,可通过写命令 10 依次获取后续结果: + +``` +客户端 视觉系统 + | | + |--- 写入 地址0 = 1 (触发相机1) ------>| + | |-- 清零地址0,地址1=0(正在检测) + | |-- 执行检测(发现N个棒材)... + | | + |<-- 读取 地址1 = 1 (成功) -----------| + | |-- 自动输出第1个棒材结果 + |<-- 读取 地址2~13 (棒材1坐标) --------| + | | + |--- 写入 地址0 = 10 (读取下一个) ---->| + | |-- 输出第2个棒材结果 + |<-- 读取 地址1 = 1 (成功) -----------| + |<-- 读取 地址2~13 (棒材2坐标) --------| + | | + |--- 写入 地址0 = 10 (继续读取) ------>| + | |-- 无更多结果 + |<-- 读取 地址1 = 3 (无更多结果) ------| + | | +``` + +> **注意**:触发新的检测(写1或写2)后,缓存索引自动归零,写10将重新从第2个棒材开始读取。 + ## 6. 注意事项 1. 触发寄存器写入后会自动清零,无需客户端手动清零 -2. 仅输出第一个检测到的棒材结果 +2. 检测完成后自动输出第一个棒材结果;写命令 10 可依次获取第2、3...个结果 3. 坐标数据为机械臂坐标系下的值(已经过手眼标定转换) 4. 欧拉角为 ZYX 外旋顺序(从轴向方向和法向方向构建旋转矩阵提取) 5. 浮点数字节序(大端/小端)由网络配置中的 byteOrder 参数决定 diff --git a/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Inc/RodAndBarPositionPresenter.h b/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Inc/RodAndBarPositionPresenter.h index e1fcd1d..f65fb7d 100644 --- a/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Inc/RodAndBarPositionPresenter.h +++ b/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Inc/RodAndBarPositionPresenter.h @@ -158,8 +158,11 @@ private: // 根据相机索引获取调平参数 SSG_planeCalibPara _GetCameraCalibParam(int cameraIndex); - // 发送检测结果到Modbus寄存器 - void _SendDetectionResultToModbus(const DetectionResult& result, int cameraIndex); + // 发送检测结果到Modbus寄存器(index指定发送第几个棒材结果) + void _SendDetectionResultToModbus(const DetectionResult& result, int cameraIndex, int index = 0); + + // 发送缓存中的下一个棒材结果 + void _SendNextCachedResult(); private: // RodAndBarPositionPresenter 特有的成员变量 @@ -170,6 +173,11 @@ private: // 手眼标定矩阵列表(从独立文件加载,暂时保留在Presenter中) std::vector m_clibMatrixList; + + // 多棒材结果缓存(支持写10逐个读取) + DetectionResult m_lastDetectionResult; + int m_resultIndex = 0; + std::mutex m_resultCacheMutex; }; #endif // RODANDBARPOSITIONPRESENTER_H diff --git a/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Src/RodAndBarPositionPresenter.cpp b/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Src/RodAndBarPositionPresenter.cpp index 184b1b4..cb9ee2c 100644 --- a/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Src/RodAndBarPositionPresenter.cpp +++ b/App/RodAndBarPosition/RodAndBarPositionApp/Presenter/Src/RodAndBarPositionPresenter.cpp @@ -232,8 +232,14 @@ int RodAndBarPositionPresenter::ProcessAlgoDetection(std::vector()) pStatus->OnStatusUpdate(statusMsg.toStdString()); - // 发送检测结果到Modbus寄存器 - _SendDetectionResultToModbus(detectionResult, m_currentCameraIndex); + // 缓存检测结果,重置索引(首次发送index=0,下次写10从1开始) + { + std::lock_guard lock(m_resultCacheMutex); + m_lastDetectionResult = detectionResult; + m_resultIndex = 1; + } + // 发送第0个结果 + _SendDetectionResultToModbus(detectionResult, m_currentCameraIndex, 0); // 9. 检测完成后,将工作状态更新为"完成" SetWorkStatus(WorkStatus::Completed); @@ -616,21 +622,30 @@ void RodAndBarPositionPresenter::SetAlgoParams(const AlgoParams& params) void RodAndBarPositionPresenter::OnModbusWriteCallback(uint16_t startAddress, const uint16_t* data, uint16_t count) { - // 检测触发寄存器 (地址 0):写入非零值触发检测 + // 检测触发寄存器 (地址 0):写入非零值触发检测或读取缓存结果 if (startAddress == 0 && count >= 1 && data[0] != 0) { - int cameraIndex = data[0]; // 1=相机1, 2=相机2 - LOG_INFO("Modbus trigger detection: cameraIndex=%d\n", cameraIndex); + int cmd = data[0]; // 1=触发相机1, 2=触发相机2, 10=读取下一个缓存结果 + LOG_INFO("Modbus command received: cmd=%d\n", cmd); // 清除触发寄存器 uint16_t clearValue = 0; WriteModbusRegisters(0, &clearValue, 1); - // 设置状态为"正在检测" - uint16_t detecting = 0; - WriteModbusRegisters(1, &detecting, 1); - - // 触发检测 - TriggerDetection(cameraIndex); + if (cmd == 10) { + // 读取缓存中的下一个棒材结果 + _SendNextCachedResult(); + } else { + // 新检测触发,索引归零 + { + std::lock_guard lock(m_resultCacheMutex); + m_resultIndex = 0; + } + // 设置状态为"正在检测" + uint16_t detecting = 0; + WriteModbusRegisters(1, &detecting, 1); + // 触发对应相机检测 + TriggerDetection(cmd); + } } } @@ -643,7 +658,7 @@ void RodAndBarPositionPresenter::OnModbusServerStatusChanged(bool isConnected) } } -void RodAndBarPositionPresenter::_SendDetectionResultToModbus(const DetectionResult& result, int cameraIndex) +void RodAndBarPositionPresenter::_SendDetectionResultToModbus(const DetectionResult& result, int cameraIndex, int index) { if (!IsModbusServerRunning()) { LOG_WARNING("Modbus server not running, skip sending result\n"); @@ -654,18 +669,18 @@ void RodAndBarPositionPresenter::_SendDetectionResultToModbus(const DetectionRes ConfigResult configResult = m_pConfigManager->GetConfigResult(); bool bigEndian = (configResult.byteOrder == 0); // 0=大端序, 1=小端序 - // 地址 1:状态码(0=正在检测,1=成功,2=失败,3=结果为空) + // 地址 1:状态码(0=正在检测,1=成功,2=失败,3=结果为空/无更多结果) uint16_t statusCode = 1; // 默认成功 if (!result.success) { statusCode = 2; // 检测失败 - } else if (result.positions.empty()) { - statusCode = 3; // 成功但结果为空 + } else if (result.positions.empty() || index >= (int)result.positions.size()) { + statusCode = 3; // 成功但结果为空或索引越界 } WriteModbusRegisters(1, &statusCode, 1); - // 地址 2~13:第一个棒材的 XYZ + RPY(6 个 float = 12 个寄存器) - if (!result.positions.empty()) { - const auto& pos = result.positions[0]; + // 地址 2~13:指定索引棒材的 XYZ + RPY(6 个 float = 12 个寄存器) + if (statusCode == 1) { + const auto& pos = result.positions[index]; float values[6] = { (float)pos.x, (float)pos.y, (float)pos.z, (float)pos.roll, (float)pos.pitch, (float)pos.yaw @@ -683,6 +698,32 @@ void RodAndBarPositionPresenter::_SendDetectionResultToModbus(const DetectionRes WriteModbusRegisters(2, regs, 12); } - LOG_INFO("Detection result sent via Modbus, camera: %d, status: %d\n", - cameraIndex, statusCode); + LOG_INFO("Detection result sent via Modbus, camera: %d, index: %d, status: %d\n", + cameraIndex, index, statusCode); +} + +void RodAndBarPositionPresenter::_SendNextCachedResult() +{ + if (!IsModbusServerRunning()) { + LOG_WARNING("Modbus server not running, skip sending cached result\n"); + return; + } + + std::lock_guard lock(m_resultCacheMutex); + if (m_lastDetectionResult.positions.empty() || + m_resultIndex >= (int)m_lastDetectionResult.positions.size()) { + // 无更多结果,写状态码3 + uint16_t statusCode = 3; + WriteModbusRegisters(1, &statusCode, 1); + LOG_INFO("No more cached results, index=%d, total=%zu\n", + m_resultIndex, m_lastDetectionResult.positions.size()); + return; + } + + LOG_INFO("Sending cached result index=%d, total=%zu\n", + m_resultIndex, m_lastDetectionResult.positions.size()); + _SendDetectionResultToModbus(m_lastDetectionResult, + m_lastDetectionResult.cameraIndex, + m_resultIndex); + m_resultIndex++; } diff --git a/App/RodAndBarPosition/RodAndBarPositionApp/RodAndBarPositionApp.pro b/App/RodAndBarPosition/RodAndBarPositionApp/RodAndBarPositionApp.pro index 3e8e7ed..0c8d248 100644 --- a/App/RodAndBarPosition/RodAndBarPositionApp/RodAndBarPositionApp.pro +++ b/App/RodAndBarPosition/RodAndBarPositionApp/RodAndBarPositionApp.pro @@ -103,6 +103,7 @@ win32:CONFIG(debug, debug|release) { LIBS += -L../RodAndBarPositionConfig -lRodAndBarPositionConfig LIBS += -L../../../AppUtils/UICommon -lUICommon LIBS += -L../../../AppUtils/AppCommon -lAppCommon + LIBS += -L../../../AppUtils/AppConfig -lAppConfig LIBS += -L../../../Module/AuthModule -lAuthModule LIBS += -L../../../Utils/CloudUtils -lCloudUtils LIBS += -L../../../Device/VrEyeDevice -lVrEyeDevice diff --git a/App/RodAndBarPosition/RodAndBarPositionApp/Version.h b/App/RodAndBarPosition/RodAndBarPositionApp/Version.h index c75909b..0963b13 100644 --- a/App/RodAndBarPosition/RodAndBarPositionApp/Version.h +++ b/App/RodAndBarPosition/RodAndBarPositionApp/Version.h @@ -5,7 +5,7 @@ // 应用名称 #define RODANDBARPOSITION_APP_NAME "棒材定位" -#define RODANDBARPOSITION_VERSION_STRING "1.0.0" +#define RODANDBARPOSITION_VERSION_STRING "1.0.1" #define RODANDBARPOSITION_BUILD_STRING "1" #define RODANDBARPOSITION_FULL_VERSION_STRING "V" RODANDBARPOSITION_VERSION_STRING "_" RODANDBARPOSITION_BUILD_STRING diff --git a/App/RodAndBarPosition/RodAndBarPositionApp/Version.md b/App/RodAndBarPosition/RodAndBarPositionApp/Version.md new file mode 100644 index 0000000..3a6baca --- /dev/null +++ b/App/RodAndBarPosition/RodAndBarPositionApp/Version.md @@ -0,0 +1,8 @@ +# 1.0.1 2026-03-27 +## build_1 +1. 修复协议,增加读取下一个目标的 + +# 1.0.0 2026-03-11 +## build_1 +1. 初始版本 + diff --git a/GrabBagPrj/pkg_binocularmark.sh b/GrabBagPrj/pkg_binocularmark.sh index d1c18f1..b2979a8 100644 --- a/GrabBagPrj/pkg_binocularmark.sh +++ b/GrabBagPrj/pkg_binocularmark.sh @@ -63,6 +63,12 @@ QT_LIB_PATH=/opt/sysroot/firefly-arm64-sysroot-20.04/lib/aarch64-linux-gnu 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 # 获取文件名用于比较