# DroneScrewbolt 项目设计文档 ## 1. 项目定位 DroneScrewbolt 采用远端服务端和地面端控制应用分离的结构。 - DroneScrewServer:部署在远端板卡,直接连接双目相机,负责采集、推流、算法检测和 ZMQ 协议服务。 - WDRemoteReceiver:地面端通信库,封装设备发现、控制命令、检测结果订阅和原始图像订阅。 - DroneScrewCtrlApp:地面端用户应用,负责连接远端服务、切换工作模式、展示图像和检测结果,并提供参数配置入口。 服务端是算法和设备控制中心,地面端不直接操作相机和算法,只通过 WDRemoteReceiver 调用服务端能力。 ## 2. 工作模式 ### 2.0 模式参数总表 | 模式 | 控制命令 | detectMode | 相机降采样 | 采集/处理帧率 | 图像输出 | 压缩参数 | 视频链路 | result 输出 | 地面端显示 | 测试入口 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 距离检测(RTMP/RTSP) | `start_stream` / `stop_stream` | 不使用 | 关闭 binning,使用 `DecimationHorizontal/Vertical=4x4` 相机端抽样下采样;4096x3000 输出 1024x750;曝光直接使用配置曝光 | `30fps` | 单路实时视频,默认 `1024x750` | H.264,`Rtsp.bitrateKbps` 默认 `4096Kbps`,GOP 固定为 `fps/2`,约 `0.5s` | DroneScrewServer 内部推 `rtmp://127.0.0.1:1935/live/dronescrew`,地面端拉 `rtsp://:8554/live/dronescrew` | `DetectDistance` 持续发布 boxes/distances,协议距离单位为 mm | DroneScrewCtrlApp 拉 RTSP,测距结果 overlay 和列表按 m 显示 | `Test\WDRemoteReceiverTest` 的 LiveStream | | 精准检测(双图 raw) | `set_detect_mode=binocular` 后 `start` / `stop` | `binocular` | `1x1`/关闭 binning | `2fps` | 左图 + 右图,全分辨率 raw | zlib 无损压缩,失败时 raw 兜底 | 关闭 RTSP/RTMP | `Detect` 精准检测结果持续发布 | 用于联调/测试双目 raw 链路;正式 DroneScrewCtrlApp 默认不进入该传输方式 | `Test\WDRemoteReceiverTest` 选择 binocular 后 Start | | 精准检测(单图 raw) | `set_detect_mode=mono` 后 `start` / `stop` | `mono` | `1x1`/关闭 binning | `2fps` | 左图,右图为空;4096x3000 输入压缩后约 1024x750 | 固定有损压缩:Mono8 先按 `1/4` 缩放,再 MJPEG `quality=25`;不动态算带宽、不跳帧 | 关闭 RTSP/RTMP | `Detect` 精准检测结果持续发布,result 坐标系固定为算法输入原图尺寸 | DroneScrewCtrlApp 使用压缩后左图显示,raw/result 按 frameId 对齐后按原图坐标比例叠框,图像中只标框和序号,列表显示每个目标 | `Test\WDRemoteReceiverTest` 选择 mono 后 Start | | 程序 | 定位 | 应覆盖的模式 | | --- | --- | --- | | `Test\WDRemoteReceiverTest` | 通信库和模式链路测试工具 | 距离检测、精准检测双图 raw、精准检测单图 raw | | `DroneScrewCtrlApp` | 正式地面端用户程序 | 距离检测(RTSP + result) 和精准检测单图 raw(MJPEG + result) | ### 2.1 距离检测模式 距离检测模式用于实时观察和距离检测。 服务端行为: - 双目相机关闭 binning,固定使用 `DecimationHorizontal/Vertical=4x4` 相机端抽样下采样采集。 - `Decimation` 是相机输出阶段的像素抽样降采样,不是压缩,也不是求平均/求和合并;它会改变相机输出分辨率,4096x3000 在 4x4 decimation 后输出 1024x750。 - MVS SDK 自带 `Features.xml` 中未提供 `BinningMode`、`Average`、`Sum` 这类 binning 合并模式选择;已确认的 `DecimationHorizontal/Vertical` 描述为 image sub-sampling,因此距离模式固定使用 decimation 来保持与精准检测相同的曝光语义。 - 距离检测和精准检测使用同一个配置曝光值;运行中通过 `set_exposure` 修改曝光时也直接按当前配置曝光下发,不再按 binning 倍率做 `/16` 换算。 - 当前设计帧率为 30fps。 - 现场双目相机分别连接两个独立 USB 控制器;1024x750 Mono8 30fps 单路原始数据约 23MB/s(约 184Mbps),两路分摊到独立 USB2.0 控制器后按 30fps 设计。该约束不适用于两台相机共用同一个 USB2.0 root hub 的接法。 - 算法走 DetectDistance 距离检测 pipeline。 - 启动实时视频链路:服务端内部 RTMP 推到本机 mediamtx,地面端通过 RTSP 拉取配置指定的单路相机画面。 - 同时通过 ZMQ result 通道发布距离检测结果。 地面端行为: - 通过 RTSP 拉流显示实时画面。 - 同时订阅 ZMQ result,接收距离检测结果。 - result 中的 boxes/distances 用于显示检测框和距离信息;协议内距离仍以 mm 保存,地面端距离检测 UI 按 m 显示。 - 该模式不依赖 ZMQ raw 图像通道显示画面。 设计要求: - 距离检测模式必须同时具备 RTSP 拉流和 result 订阅。 - 地面端进入距离检测模式时不能停止 result 订阅。 - 服务端 start_stream 不能只被理解为“单纯推流”,它代表“decimation 距离检测 + 实时推流”。 ### 2.2 精准螺栓检测模式 精准螺栓检测模式用于高分辨率螺栓检测。 服务端行为: - 双目相机关闭 binning,使用原始分辨率采集。 - 当前设计帧率为 2fps。 - 算法走 Detect 精准检测 pipeline。 - 停止实时视频链路。 - 通过 ZMQ raw 图像通道发布图像。 - 通过 ZMQ result 通道发布检测框和距离结果。 地面端行为: - 不拉 RTSP。 - 订阅 ZMQ raw 图像,用 raw 左目图作为显示画面。 - 订阅 ZMQ result,用检测结果叠加检测框并展示列表。正式地面端按最新帧刷新结果列表:精准检测显示每个目标,距离检测显示最新测距结果。 - raw 图像和 result 必须按 frameId 对齐后再叠加;单图 raw 显示图为压缩后尺寸,但 result 坐标系保持算法输入原图尺寸,由地面端按比例缩放绘制。 - 地面端绘制时必须把已经匹配的 raw/result 成对送入绘制队列,不能在绘制线程再读取“最新结果”;框坐标只按 result 的 imageWidth/imageHeight 到当前 QImage 像素尺寸缩放,QGraphicsView 的 fitInView 只负责最终显示缩放。 - 默认可使用 detectMode=mono,只传左目 raw 图以降低网络带宽;算法内部仍使用服务端双目输入。 设计要求: - detectMode 只影响 raw 图像传输内容,不代表算法变为单目。 - 精准检测模式启动后应持续检测,不应只处理一帧或两帧后停止。 ### 2.3 检测图像保存 服务端进入检测状态后,必须把进入算法的左右图像对异步保存到本地磁盘。保存线程独立于检测线程,检测线程只负责深拷贝图像并放入保存队列,PNG 编码和写盘由保存线程完成。 目录规则: - 根目录固定为 `/home/cat`。 - 二级目录为启动当天日期,格式 `yyyyMMdd`。 - 三级目录为当前检测模式:距离检测为 `distance`,精准单图 raw 为 `precision_mono`,精准双图 raw 为 `precision_binocular`。 - 文件名按本轮检测序号从 1 开始递增:`leftImage_N.png`、`rightImage_N.png`。 示例: - `/home/cat/20260629/distance/leftImage_1.png` - `/home/cat/20260629/distance/rightImage_1.png` 保存失败或队列满时只记录日志,不允许阻塞检测线程或导致检测流程退出。 ## 3. 通信通道 | 通道 | 默认端口 | 模式 | 用途 | | --- | ---: | --- | --- | | UDP discovery | 5554 | 广播/应答 | 地面端搜索 DroneScrewServer | | ZMQ control | 15555 | REQ/REP | start/stop/open/get_info/参数设置 | | ZMQ result | 15556 | PUB/SUB | 检测结果 boxes/distances | | ZMQ raw | 15557 | PUB/SUB | 精准检测模式下的 raw 图像 | | RTMP publish | 1935 | FFmpeg/FLV | DroneScrewServer 推到本机 mediamtx 的内部入口 | | RTSP pull | 8554 | RTSP/TCP | 地面端和 VLC 从 mediamtx 拉取距离检测实时画面 | ### 3.1 实时推流设计 实时推流只属于距离检测模式,也就是 `start_stream` 对应的 LiveStream 状态。它不是单独的视频功能,而是和 4x4 decimation 采集、DetectDistance 距离检测、result 发布一起启动。 服务端推流链路: 1. `start_stream` 到达 DroneScrewServer。 2. 服务端停止精准检测相关状态,配置相机为距离检测模式:binning=1x1、decimation=4x4,默认 30fps。 3. 服务端启动单路实时画面编码,输入为 Mono8,缩放到配置尺寸,转换为 NV12。 4. `CVrFFMediaPusher` 使用 Rockchip MPP 编码 H.264,码率由 `Rtsp.bitrateKbps` 配置,默认 4096Kbps;GOP 按当前 fps 的一半设置,30fps 时约 0.5s,减少新客户端等待关键帧的时间。 5. 服务端内部推流地址固定为 `rtmp://127.0.0.1:1935/live/dronescrew`。 6. mediamtx 接收 RTMP publish,并对外提供 RTSP 拉流。 7. 对外拉流地址通过 discovery/get_info/start_stream 返回给地面端,格式为 `rtsp://<板子IP>:8554/live/dronescrew`。 端口边界: - `1935` 是服务端内部 RTMP publish 端口,只给 DroneScrewServer 推到本机 mediamtx 使用。 - `8554` 是客户端 RTSP pull 端口,DroneScrewCtrlApp、VLC、ffplay 都必须拉这个端口。 - 配置文件 `` 表示对外 RTSP pull 端口,不表示内部 RTMP publish 端口。 - 如果旧配置误写成 `1935`,服务端应警告并自动改用 `8554` 作为对外拉流端口。 mediamtx 职责: - 监听 `rtmpAddress: :1935`,接收服务端内部 RTMP 推流。 - 监听 `rtspAddress: :8554`,给地面端提供 RTSP 拉流。 - 路径使用 `/live/dronescrew`,不需要为该路径预先创建固定配置,按 publisher 动态建立。 地面端拉流链路: 1. DroneScrewCtrlApp 通过 WDRemoteReceiver 发送 `start_stream`。 2. WDRemoteReceiver 启动 result 订阅,关闭 raw 订阅。 3. `start_stream` 成功后,DroneScrewCtrlApp 读取服务端返回或 get_info 返回的 `rtsp` 字段。 4. 拉流地址以服务端返回为准;如果服务端未返回地址,则用配置中的 `serverIp + rtspPort + rtspPath` 生成兜底地址。 5. 地面端必须把旧地址 `rtmp://...:1935/...` 规范化为 `rtsp://...:8554/...`。 6. 如果服务端返回 `0.0.0.0`、`127.0.0.1` 或 `localhost`,地面端必须替换为发现到的服务端 IP。 7. start_stream 成功返回后,认为服务端 RTSP 链路已经建立;DroneScrewCtrlApp 固定多次打开本地 RTSP puller,默认最多 5 次、间隔 500ms,每次 open/read 超时按 5s 处理。成功标准不是 puller `Start()` 返回,而是收到并解码出第一帧 RTSP 图像;探测参数为 `probesize=262144`、`analyzeduration=500000us`、`fflags=nobuffer`、`max_delay=0`、`reorder_queue_size=0`。 8. 本地 RTSP puller 多次启动或首帧等待全部失败时,只提示“服务端已开流但本地拉流未启动”,不能自动发送 `stop_stream`;服务端 LiveStream 状态必须保持运行,便于用户用 VLC/ffplay 或网络工具继续定位 8554 拉流问题。 低延迟约束: - 服务端 RTMP/FLV muxer 使用 flush packets、零 interleave delay,并且单视频流直接 `av_write_frame`,避免 muxer 为多流交织排序引入等待。 - 服务端每个 IDR 内嵌 SPS/PPS,GOP 约 0.5s,降低 VLC/DroneScrewCtrlApp 中途接入时等待关键帧的时间。 - 地面端点击“开始实时显示”后必须立即弹出“正在拉流,请稍候...”提示框,同时禁用开始/检测按钮;`StartLiveStream()` 返回后关闭提示框,并按结果切换 UI 状态。 - 地面端收到第一帧 RTSP 后显示“RTSP 已出图”,用于区分“服务端已开流”和“画面已经解码显示”两个阶段。 现场日志判定: - `FFMediaPusher: init done (...)` 表示编码器和封装器初始化成功。 - `[RTMP] pusher init ok ... push=rtmp://127.0.0.1:1935/live/dronescrew pull=rtsp://<板子IP>:8554/live/dronescrew` 表示内部推流地址和外部拉流地址已经拆清。 - `FFMediaPusher: connected to mediamtx, header written` 表示服务端已经连上 mediamtx,RTMP 头已写入。 - `[LIVE] started ... url=rtsp://<板子IP>:8554/live/dronescrew` 表示 LiveStream 状态已启动。 - `start_stream` 返回 `ok=true` 且 `rawImagePublish=false` 是正确状态,距离检测模式显示走 RTSP,不走 raw。 - start_stream 成功返回后如果地面端多次打开 RTSP 或等待首帧仍失败,应按真实错误处理,不自动发送 `stop_stream` 关闭服务端 LiveStream;优先检查拉流 URL、mediamtx 和 8554 端口。 - 同尺寸推流时服务端直接拷贝 Mono8 到 NV12 Y 平面,不调用 RGA;只有输入输出尺寸不一致时才尝试 RGA 缩放,失败后回退 CPU 缩放继续推流。 VLC 或地面端提示“输入无法被打开”时,优先检查: 1. 拉流 URL 是否为 `rtsp://<板子IP>:8554/live/dronescrew`,不能是 `rtsp://<板子IP>:1935/...`。 2. mediamtx 服务是否运行,且 `rtspAddress: :8554`、`rtmpAddress: :1935` 是否被监听。 3. 服务端是否打印 `connected to mediamtx, header written`。 4. 服务端是否已有相机帧进入 `pushRtspFrame`,日志中的 `rtsp_push` 是否增长、`rtsp_fail` 是否增长。 5. 网络侧是否允许地面端访问板卡 8554 端口。 ## 4. 控制命令 核心命令: - open:按 machineCode 打开指定服务端并返回服务信息。 - get_info:读取服务端运行状态、相机参数、算法参数和端口信息。 - start_stream:进入距离检测模式,开启 decimation 4x4 下采样、RTSP 推流和 result 发布。 - stop_stream:退出距离检测模式,停止推流和距离检测。 - start:进入精准螺栓检测模式,关闭 binning 和 decimation,开启 raw/result 发布。 - stop:退出精准螺栓检测模式。 - single:执行单次精准检测。 - set_detect_mode:设置精准检测模式下 raw 图像传输方式,支持 mono/binocular。 - set_exposure / set_gain:下发相机曝光和增益。 - set_algo_params:下发算法参数。 - swap_cameras:交换左右相机角色。 ## 5. 结果数据 检测结果由服务端通过 topic=result 发布,地面端由 WDRemoteReceiver 解析为 WDRemoteDetectionFrame。 主要字段: - frameId:帧号。 - timestampUs:时间戳。 - imageWidth / imageHeight:检测坐标对应的图像尺寸。 - success:算法是否成功。 - errorCode:错误码或算法拒绝原因。 - message:详细消息。 - boxes:检测框列表。 - distances:距离结果列表,协议单位为 mm;距离检测 UI 显示时换算为 m,精准螺杆/螺栓相关尺寸和坐标保持原图坐标语义。 距离检测模式和精准检测模式都应发布 result。区别在于图像显示来源不同:距离检测模式显示 RTSP,精准检测模式显示 raw。 ## 6. 原始图像数据 raw 图像使用 topic=raw 发布,主要服务于精准检测模式。 当前协议: - payload = WDRemoteBinocularRawImageHeader + leftData + rightData。 - magic 为 BIWI。 - 支持 raw、png、mjpeg、zlib 压缩标识。 - 精准检测双图 raw 使用 zlib 无损压缩,左右图同时发送;zlib 失败时回退 raw。 - 精准检测单图 raw 使用固定有损压缩比例:Mono8 左图先按 `1/4` 缩放,再用 MJPEG `quality=25` 编码;不按运行时 payload 动态计算带宽,不因为带宽丢 raw 帧。 - 单图 raw 的 result 仍使用算法原始图像坐标,服务端发布 result 前把 `imageWidth/imageHeight` 固定为算法输入左图尺寸,地面端按显示图尺寸做比例叠框。 - MJPEG 编码失败时可回退 zlib/raw,保证图像链路不断。 - detectMode=mono 时 right 字段为空,仅传 left。 ## 7. 组件职责边界 ### DroneScrewServer 负责: - 相机连接、左右角色、曝光/增益、binning/decimation、采集频率。 - 两种 pipeline 切换:DetectDistance 和 Detect。 - 实时视频链路:RTMP publish 到本机 mediamtx,并向地面端发布 RTSP pull URL。 - ZMQ control/result/raw 服务。 - UDP discovery。 - 算法参数加载和更新。 不负责: - 地面端 UI 状态。 - 用户交互流程。 ### WDRemoteReceiver 负责: - 搜索和连接服务端。 - 封装控制命令。 - 订阅 result。 - 订阅 raw。 - 解析服务端 JSON 和 raw payload。 - 通过回调把数据交给业务层。 不负责: - UI 显示。 - 算法业务判断。 - 画框叠加。 ### DroneScrewCtrlApp 负责: - 用户操作入口。 - 服务端连接状态展示。 - 距离检测模式:RTSP 画面 + result 距离结果展示。 - 精准检测模式:raw 图像 + result 检测框展示。 - 曝光、增益、算法参数配置。 - 单次检测测试。 不负责: - 直接操作相机。 - 直接调用算法。 - 解析底层 ZMQ 协议细节。 ## 8. 推荐状态流 ### 8.1 地面端启动 1. 创建 WDRemoteReceiver。 2. 注册 event/result/raw 回调。 3. UDP 搜索服务端。 4. 通过 machineCode open。 5. get_info 更新 UI 参数。 ### 8.2 进入距离检测模式 1. 地面端停止精准检测模式相关 raw 订阅。 2. 地面端保持或启动 result 订阅。 3. 地面端发送 start_stream。 4. 服务端开启 decimation 4x4 下采样采集。 5. 服务端启动 RTSP 推流。 6. 服务端持续执行 DetectDistance。 7. 地面端拉 RTSP 显示画面。 8. 地面端接收 result 显示距离检测结果。 ### 8.3 进入精准螺栓检测模式 1. 地面端停止 RTSP 拉流。 2. 地面端发送 stop_stream。 3. 地面端设置 detectMode,默认 mono。 4. 地面端启动 result/raw 订阅。 5. 地面端发送 start。 6. 服务端关闭 binning 和 decimation,停止 RTSP 推流。 7. 服务端持续执行 Detect。 8. 服务端发布 raw/result。 9. 地面端用 raw 图像叠加 result 检测框。 ### 8.4 停止 距离检测模式停止: - 地面端发送 stop_stream。 - 服务端停止 RTSP 推流和距离检测。 精准检测模式停止: - 地面端发送 stop。 - 服务端停止精准检测线程。 - 地面端停止 raw/result 订阅。 全部停止: - 地面端释放 RTSP puller。 - 发送 stop。 - 发送 stop_stream。 ## 9. 实现约束 - 两种模式互斥,不能同时启动 RTSP 距离检测和 raw 精准检测。 - 服务端算法始终使用双目输入。 - detectMode=mono 只减少地面端 raw 图像传输量。 - 距离检测模式必须订阅 result,否则只能看到画面,看不到距离检测结果。 - 精准检测模式依赖 raw 图像显示,不依赖 RTSP。 - 服务端开始检测后应持续处理最新有效帧;相机不同步时应使用最新缓存帧继续推进,不能因为 ready 标志被清空而停住。 ## 10. 状态机约束 ### 10.1 服务端状态 服务端只允许三种外部可见状态: | 状态 | RTSP | raw PUB | result PUB | 算法 pipeline | 相机降采样 | | --- | --- | --- | --- | --- | --- | | Idle | 关闭 | 关闭 | 无业务结果 | 无 | 精准默认配置:binning=1x1, decimation=1x1 | | LiveStream | 开启 | 关闭 | 持续发布 | DetectDistance | binning=1x1, decimation=4x4 | | Detection | 关闭 | 开启 | 持续发布 | Detect | binning=1x1, decimation=1x1 | 合法转换: - Idle -> LiveStream:收到 start_stream。 - Idle -> Detection:收到 start。 - LiveStream -> Idle:收到 stop_stream。 - Detection -> Idle:收到 stop。 - LiveStream -> Detection:必须先 stop_stream 成功,再 start 成功。 - Detection -> LiveStream:必须先 stop 成功,再 start_stream 成功。 非法或无效转换: - LiveStream 状态下收到 start,应返回模式冲突或由调用方先 stop_stream。 - Detection 状态下收到 start_stream,应返回模式冲突或由调用方先 stop。 - LiveStream 状态下收到 stop 不应关闭距离检测结果链路。 - Detection 状态下收到 stop_stream 不应关闭精准检测线程。 ### 10.2 WDRemoteReceiver 本地状态 WDRemoteReceiver 使用 `Idle / LiveStream / Detection` 记录最后一次已确认的远端模式: | 本地状态 | result 订阅 | raw 订阅 | 地面端图像来源 | | --- | --- | --- | --- | | Idle | 关闭 | 关闭 | 无 | | LiveStream | 开启 | 关闭 | RTSP puller | | Detection | 开启 | 开启 | ZMQ raw | 状态推进规则: - 只有控制命令返回成功后才更新本地状态。 - 停旧模式失败时保留旧状态,不能提前置为 Idle。 - 旧模式已确认停止,但新模式启动或订阅失败时,必须清理已打开订阅并进入 Idle。 - StopWork 只在停止当前活跃模式成功时关闭订阅并进入 Idle;停止非当前模式不能破坏当前模式的订阅。 - Disconnect 必须关闭 result/raw 订阅并进入 Idle。