Files
code-test-drone/legacy/docs/双目YOLO目标识别需求说明.md
T
keita df91b1110e 精简仓库结构并增强任务链路:归档双目可选后端、新增LOS判据与自定义返航
归档 legacy/:stereo_yolo/stereo_ranger/target_tracker 三包 + 175 条测试 + demo/配置/权重/三份规格;运行链路不再依赖,默认 pytest 446 条,归档测试按需运行。

目标有效性第二判据改为 LOS 运动学一致性(pod_bridge/los_consistency.py),删除 require_vision;pod_locked() 叠加该判据,状态机随之后退重搜。

自定义返航(task_runner/return_home.py):保持转场高度回起飞点 → 恢复初始航向 → 垂直降落,不再爬 RTL_RETURN_ALT;未起飞/无起飞点/超时/异常仍退回 PX4 原生 RTL。

任务链安全兜底:主循环异常→紧急返航、stop 幂等、landing_stop 保留遥测、in_air 流复用+超时、解锁/起飞与吊舱识别仅执行一次、rtl_timeout 上限。

入口共享件拆到 utils/sitl_support.py;pod_sitl_test.py→l1_sitl_test.py;删除旧架构 test.py;相机脚本移入 tools/。

新增 .gitignore 并剔除历史误跟踪的 59 个 .pyc;requirements 按实际依赖重写;文档全面对齐,新增 legacy/README.md 与 tools/README.md。
2026-10-21 14:48:41 +08:00

14 KiB
Raw Blame History

双目 YOLO 目标识别模块需求说明(检测前端)

定位:双目链路中的"检测前端",服务于已实现的 utils/stereo_ranger。本模块负责在左右目图像上独立运行 YOLO 类检测器,并输出符合 stereo_ranger 输入契约的检测框列表;不承担测距、左右目关联与立体校正。

版本:0.2(2026-09-11;按 utils/stereo_ranger v1.0 实现接口对齐,文档引用随《双目测距方案》更名为《双目测距技术路线》同步更新)。

语义权威:docs/双目测距技术路线.md(下称《测距路线》)——几何、取点、匹配、有效性、退化语义一律以其为准;字段级接口以 utils/stereo_ranger/types.py 为准(本文只做需求描述,不重复定义)。


1. 目的与定位

单目检测给出像素框,stereo_ranger 需要"同一逻辑帧、左右目各自"的检测结果来完成视差测距。本模块把"图像 → 原生坐标系检测框"的整段处理收敛为对测距端透明的单一输入源:

采集/同步 → 预处理(校正+letterbox)→ 左右目并行 YOLO 推理 → NMS
         → 框映射回原生分辨率 → 输出 DetectionFrame(左右各一)→ stereo_ranger.update(...)

1.1 明确不做的事

  • 不做视差计算、左右目关联、距离/方位估计(均属 stereo_ranger);
  • 不做立体校正本身(校正映射属于采集预处理,契约见《测距路线》§2.1"校正契约"与本文 §2.2 第 4 条);
  • 不做跨帧跟踪(track_id 可由外部追踪器注入;无追踪器时留空,见 §2.1)。

2. 与 stereo_ranger 的接口契约(硬性要求)

接口对齐 utils/stereo_ranger/types.py 与 utils/stereo_ranger/stereo_ranger.py 的实现,任何偏离都会破坏测距正确性。

2.1 输出数据定义

输出即 stereo_ranger 的输入:每目一个 DetectionFrame。字段名与语义以 utils/stereo_ranger/types.py 为准(下例仅为语义说明,不重复定义):

@dataclass
class Detection:
    x1: float            # 框左上角 x(已校正图像坐标,原生分辨率 px)
    y1: float            # 框左上角 y
    x2: float            # 框右下角 x
    y2: float            # 框右下角 y
    confidence: float = 1.0          # 检测置信度 [0,1]
    class_id: int = 0                # 类别 id(左右目同一目标必须一致,§2.2 第 5 条)
    track_id: int | None = None      # 检测侧轨道关联;无追踪器时留空(0/负数一律视为"无轨道")

@dataclass
class DetectionFrame:
    frame_id: int                        # 帧标识;左右两帧必须一致(同帧契约,§2.3)
    detections: Sequence[Detection] = () # 该目本帧检测框
  • 推荐直接复用 utils/stereo_ranger/types.py 的 Detection / DetectionFrame (纯值语义、无第三方依赖;只共享数据约定、不引入解算逻辑,不违反前端/测距解耦); 若因部署边界另建类型,字段名与语义必须逐字一致——两处定义漂移会静默破坏视差取点;
  • track_id 仅供测距端做目标隔离键(左右一致且为正时优先),不参与匹配与解算(§2.5);
  • 每目一帧内多目标的排列顺序无要求,按检测器输出自然顺序即可。

2.2 坐标与几何硬约束

  1. 原生分辨率坐标系:输出框必须位于校正后图像的原生分辨率(如 1920×1080)下;视差在原生坐标计算,才能保留传感器全部像素精度(《测距路线》§2.1"坐标基准一致")。检测器输入尺寸(如 640×640)上的框必须经 letter-box 逆变换精确映射回原生坐标。
  2. 左右目预处理几何位级一致:同一缩放、同一填充。禁止左右图各自自适应缩放/填充——那会破坏校正后的极线对齐性质。
  3. 框边语义:(x1, y1) 为左上角、x1 即框"左边缘"(测距端主方案直接取 x1_L − x1_R 作视差,《测距路线》§6.1)。实现方不得把框定义改为中心点+宽高而不转换。
  4. 立体校正前提:本模块输出的是"校正后图像"上的框。若输入为原始图,采集端须先做与测距端内参一致的重映射(校准映射、内参由标定给出)。
  5. 同一物理目标在左右目必然同时可见时,其两框 y 坐标应接近(垂直残差验收见 §6);x1 有系统偏移(≈ F·B/Z 视差)属正常,不得在识别端"对齐"掉。同一目标的类别在两目必须一致——类别一致是测距端匹配的硬约束(《测距路线》§6.2),不一致会退化为两条"仅方位"记录。
  6. 输出前自检:所有框坐标必须为有限值且满足 x2 > x1、y2 > y1。非有限值框会被测距端丢弃(不参与匹配、不发布记录),畸变框会污染框高约束与匹配代价——自检应在识别端出口完成,而不是依赖下游兜底。

2.3 帧配对要求

  • 每帧必须携带单调递增的帧标识(DetectionFrame.frame_id,int),左右两个容器共用同一值;
  • frame_id 不一致时测距端拒收整帧、返回空列表(《测距路线》§2.1 同帧契约、§5 第 ① 步)——下游兜底不等于可以松懈,正确配对是采集端的责任;
  • 无硬件同步的双独立流形态下,采集端按接收时间窗配对并输出帧标识,残余时序抖动应记录供误差评估(两种同步形态的取舍见《双目深度估计思路》§3.2,该文档为链路外部参考);
  • 同帧拼接(side-by-side)单流形态:解码一次、裁切为左右图,天然同曝光,推荐优先支持;
  • 帧标识由采集端按真实曝光顺序分配,禁止为凑成"同帧"而重编号(§2.5 第 5 条)。

2.4 与测距端的对接方式(示例)

from utils.stereo_ranger import StereoRanger
from utils.stereo_ranger.types import DetectionFrame   # 纯值类型,可安全复用

frontend = StereoYoloFrontend(cfg)          # 本模块主类
ranger = StereoRanger(stereo_cfg)           # 配置见 §4,实测值见 utils/config/stereo_ranger.json

left_frame, right_frame = frontend.detect(left_img, right_img, frame_id=n)  # 各一个 DetectionFrame
records = ranger.update(left_frame, right_frame, now_ms=monotonic_ms())
  • 第三个参数是 now_ms(平台单调时钟毫秒,仅用于输出打戳,默认 0),不是帧序号——按位置把 n 传进去会被静默当作时间戳,污染 timestamp_ms;
  • 返回 list[RangeRecord],顺序:先按左目框顺序(配对→完整记录 / 未配对→仅方位记录),再补未配对的右目框的"仅方位"记录;左目非空时记录数与左目框数一致;
  • 没有 matched 字段。单目检出的记录为 degradation = BEARING_ONLY、valid = False、forward_distance_m = None,方位取该目框中心;
  • 配对成功但域外/一致性异常时仍发布 degradation = COMPLETE 的记录(disparity_px、center_mid_px 可用),但 valid = False、forward_distance_m = None——域外不撒谎,也不丢方位(《测距路线》§7.3);
  • 测距端不消费检测置信度:完整记录的 confidence 是匹配质量 clamp(1 − 匹配代价)(连续跳变时乘降级系数,判 invalid 时置 0);仅方位记录的 confidence 才直接取检测置信度。故 conf 阈值/NMS 必须由识别端自行完成后再输出;
  • 两目检测均为空 → 返回空列表(正常空帧,不告警)。

2.5 输入纯净性硬约束(v0.2 新增,飞行安全相关)

  1. 只送本帧原生实测框:update() 的输入必须是本帧、在原生校正图上检测得到的框。跨帧外推框(追踪器预测/Coast 补帧)、平滑后的框、按上一帧位移推算的"虚拟框"一律不得送入;
  2. 外推框会伪装成合法测量:框的 x1 偏差直接进入视差 D = x1_L − x1_R,Z = F·B/D 没有任何机制识别"这不是本帧观测",误差随 Z² 放大且不触发 valid 门(《测距路线》§7.1);测距端的时序一致性监控最多把它降级,不能纠偏(《测距路线》§6.7);
  3. 目标丢失就该没有距离:本帧未检出 → 不发布该目标检测框;测距端对其记失效、连续 filter_reset_frames 帧后清空滤波状态(《测距路线》§6.5)。距离中断(coast)属于消费侧策略,且必须与 valid 语义分开标注;
  4. track_id 仅作隔离键:由外部追踪器注入时只用于目标状态隔离(《测距路线》§4);不得用追踪框充当检测框;左右目同一目标的 track_id 必须一致,否则隔离退化为最近邻兜底(§2.1);
  5. 两目同源:左右框必须来自同一次曝光(同帧契约);左右取自不同帧、仅凭"时间接近"拼成一对,即使 frame_id 相同也属违约——frame_id 由采集端保证真实(§2.3);
  6. 前端不做左右匹配、不做对齐:左右匹配、视差取点、方位解算全部在 stereo_ranger;识别端只需保证"每目各自把该目可见目标检测出来",不得为便于后端匹配而调整框位置(《测距路线》§1.4)。

3. 模块内部建议构成(实现参考)

子模块 职责 说明
采集/同步(可选) 双流按帧标识+时间窗配对,或 sbs 裁切 无硬同步时量化残余抖动;帧标识不得重编号(§2.3)
几何预处理 校正 remap → 左右完全一致的 letterbox letterbox 参数(缩放 s、填充)必须左右共用一份配置
推理调度 左右目各一次检测 必须并发:单设备双流或双实例双线程;串行使时延翻倍(《测距路线》§10.1:检测单核 20~30ms,NPU 预算成立)
YOLO 推理 + NMS 输出 640×640(或配置尺寸)坐标系下的框 类别表、conf/NMS 阈值外置
坐标回映 逆 letterbox 映射到原生分辨率 与预处理使用同一套变换参数,禁止各写一份
输出适配 出口自检(有限值、x2>x1、y2>y1)→ 每目封装 DetectionFrame(frame_id, detections) 字段严格按 §2.1;frame_id 左右共用

4. 配置项需求(全部外置,禁止硬编码)

配置 说明
模型权重 / 类别表 检测器选择(YOLOv5/v8/…)不设限
输入尺寸 如 640×640;左右共用
conf / NMS IoU 阈值 识别端自身过滤口径(测距端不消费检测置信度,§2.4)
输出类别过滤 可选白名单;同一目标左右目类别必须一致(§2.2 第 5 条)
原分辨率与内参 逆映射/校验用;fx_px/fy_px/cx_px/cy_px 必须与测距端配置同源同值(当前占位值见 utils/config/stereo_ranger.json,标定回填后同步)
并发策略 单实例多线程 or 双实例
帧同步方式 sbs / 双流 + 时间窗

配置一致性要求(§5"一致性"项的落地):letterbox 缩放与填充参数、内参、原分辨率 均由同一配置源驱动左右两目与两个模块;任何一处单独调整都会造成视差系统性偏差 (《测距路线》§9 全部参数外置、启动期校验的语义同样适用于本模块)。


5. 非功能需求(大致)

项目 要求
帧率 满足测距流水线帧周期(如 30 fps → ≤33 ms/帧)
左右推理 必须并发执行(《测距路线》§10.1:检测单核 20~30ms,串行会吃掉全部预算)
端到端时延 实测并留出余量:识别之外,匹配+测距在少量目标时 <1 ms(测距端为纯计算单元、无 IO 无阻塞,《测距路线》§11.3)
一致性 左右目预处理/回映参数由同一配置源驱动,杜绝两路漂移(§4 配置一致性要求)
失败处理 单目推理失败等异常不阻塞整体,该目输出空帧列表(测距端退化为另一目的仅方位记录);错误日志节流(《测距路线》§5:第 1 次 + 每满 100 次,带累计计数)

6. 验证与验收(大致)

项目 方法 通过标准(示意)
坐标回映 已知缩放/填充的合成图,比对回映框与真值 误差 <0.5 px(像素取整前)
垂直对齐 静态场景,左右匹配框 |Δy| 直方图 P95 ≤1 px(《测距路线》§12.1 校正质量;不达标触发自标定提前)
类别一致性 静态场景统计同一目标两目类别 不一致率 ≈0(不一致会被测距端匹配硬约束拦截,退化为两条仅方位记录)
输出合法性 出口自检单测(含非有限值、退化框构造用例) 无 x2 ≤ x1 / y2 ≤ y1 / 非有限值框输出(§2.2 第 6 条)
输入纯净性 代码审查 + 单测:断言送入 update() 的框集合恰为本帧检测输出 无预测/Coast/平滑框进入(§2.5)
下游对接 用本模块输出喂 StereoRanger.update() 与 utils/demo_stereo_ranger.py 真值投影法结果同量级;误差随距离趋势符合 δZ ∝ Z²(《测距路线》§7.1)
帧周期 双流满负荷计时 满足所选帧率预算

7. 依赖与边界

  • 依赖:YOLO 推理栈(如 onnxruntime/torch)、opencv(解码/预处理/校正 remap,若选用 numpy 实现 letterbox 亦可);不 import stereo_ranger 的测距逻辑(保持前端/测距解耦),仅可复用 stereo_ranger/types.py 的纯值类型(可选,见 §2.1)与同一份配置数值(§4);
  • 本模块不实现:跨帧跟踪(无追踪器时 track_id 留 None 可接受)、距离 coast/外推(属消费侧,§2.5 第 3 条)、自标定与立体校正本身、主测距传感器融合;
  • 若部署平台算力不足(如边缘设备),允许降低输入分辨率或帧率,但左右一致性与原生回映两条约束不可妥协(§2.2 第 1、2 条);
  • 红线同源:本模块输出同样受《测距路线》§12.3 约束——标定完成前内参等数值均为占位值,仅限台架/回放验证,不得接入实飞控制回路。