Files
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

6.5 KiB
Raw Permalink Blame History

stereo_yolo(Python 版)

双目 YOLO 检测前端:在左右目图像上独立运行 YOLO 检测器,输出符合 stereo_ranger 输入契约的检测框(每目一个 DetectionFrame、共用 frame_id)。 不做测距、左右目关联与立体校正。

规格:docs/双目YOLO目标识别需求说明.md(接口契约以其为准;几何语义权威 docs/双目测距技术路线.md)。

⚠ 全部参数为占位值、demo 素材为 AI 合成图:标定与地面真值验证完成前, 仅限台架/回放验证,不得接入实飞控制回路(规格 §7 红线同源)。

接口清单

名称 说明
StereoYoloFrontend(config, detector=None) 主入口;detector 可注入(接口 detect(img) -> list[Detection],单测用假检测器)
.detect(left_img, right_img, frame_id) -> (DetectionFrame, DetectionFrame) 单帧检测;BGR ndarray(cv2 约定),输出框位于输入图原生坐标系
.close() 释放并发线程池
.config 只读配置访问
StereoYoloConfig / load_from_json(path) 7 项配置;缺项/未知字段/类型错误/非法取值一律 ValueError(fail-fast)
UltralyticsDetector ultralytics YOLO .pt 后端(双实例池;ultralytics 延迟导入)
split_sbs(image) -> (left, right) side-by-side 单流裁切(宽须为偶数)
Detection / DetectionFrame 直接复用 stereo_ranger.types(纯值语义,§2.1 推荐)
WarnThrottle 告警节流(第 1 次 + 每满 100 次,带累计计数)

logger 名称为 stereo_yolo,走标准 logging。

单帧流程(detect 四步)

  1. 形状一致性:左右图形状不一致 → 整帧拒收输出空 + WARN 节流 (§2.2 第 2 条:禁止左右各自缩放/填充);
  2. 左右推理:parallel(默认,ThreadPoolExecutor(2),§5 硬性要求)或 sequential(基准对照);单目异常被隔离,该目输出空帧列表(§5 失败处理);
  3. 出口自检 + 白名单:丢弃非有限值 / x2<=x1 / y2<=y1 的框 (§2.2 第 6 条);class_whitelist 非空时过滤类别;
  4. 封装:每目一个 DetectionFrame(frame_id, detections),frame_id 左右共用(§2.3 同帧契约)。

实现决策(v1.0 冻结)

  1. ultralytics 直跑 .pt(项目拍板口径):letterbox、输出解码、NMS、 框映射回原生坐标系全部由库内完成;识别端只负责阈值外置、出口自检与 契约封装。
  2. 双实例池:ultralytics 单实例非线程安全,左右并发各持一份模型 (§4 并发策略的"双实例"形态)。
  3. 无跨帧状态:前端不持有任何历史(无追踪、无平滑、无 Coast)—— 结构性满足"只送本帧原生实测框"(§2.5 第 1 条)。
  4. 形状不一致不 raise:按"拒收 + 空帧 + 节流 WARN"处理,与 stereo_ranger 非同帧拒收同哲学(不阻塞整体)。
  5. 几何参数单一来源:stereo_yolo.json 不存内参/原分辨率(输出框天然在 输入图坐标系),F/B/cx/cy 唯一来源是 utils/config/stereo_ranger.json, 避免两处漂移(§4 配置一致性)。
  6. track_id 留 None:外部追踪器注入属链路后段(§2.1;v1.1 可选)。

移植注记(C++ 侧需自行复现的库内行为)

本次为 ultralytics 直跑口径,以下行为在库内完成、C++ 实现必须逐项复现 (不一致会在视差取点上引入系统性偏差):

库内行为 C++ 需复现要点
letterbox 等比缩放到 imgsz、灰边填充(ultralytics 预测默认填充值 114);左右同一套参数(形状一致时天然满足)
输出解码 YOLO26 检测头输出布局与解码(建议导出一份 ONNX 作为参照物;本仓环境:ultralytics 8.4 / torch 2.11 / onnx 1.18)
NMS conf 先过滤、NMS 用配置 IoU 门限;max_det 默认 300
坐标回映 letterbox 逆变换(scale/pad)到原生分辨率;输出 x1,y1,x2,y2 浮点
并发 双实例双线程(单实例并发调用不安全)

配置项(全部外置,占位值)

键 占位值 说明
model_path models/yolo26n-drone.pt 相对路径按启动工作目录解析(demo 内按仓库根兜底)
imgsz 640 须为 32 的整数倍(YOLO 步长约束)
conf_threshold 0.25 识别端自身过滤口径(测距端不消费检测置信度,§2.4)
iou_threshold 0.7 NMS IoU 门限
class_whitelist null 可选白名单;同一目标左右目类别必须一致(§2.2 第 5 条)
device null null = 自动选择(cpu / cuda:0 / …)
concurrency parallel parallel / sequential

测试与演示

cd utils
"D:/program file/conda/envs/pytorch/python.exe" -m pytest tests/stereo_yolo -q   # 36 条
python utils/demo_stereo_yolo.py                                                 # 真模型 + 占位素材

单测全部用注入的假检测器驱动(无模型、无素材、无硬件):frame_id 左右共用、 出口自检丢弃退化框、白名单过滤、单目失败隔离、形状不一致拒收、双人屏障 断言真并发(串行实现会屏障超时)。

demo(真模型 + AI 合成素材 utils/assets/stereo_yolo/drone_sky.png): ① 单图冒烟 → ② 合成双目对(同一 patch 羽化粘贴、已知视差 12 px,实测 |ΔD|≈0.3 px、|Δy|≈0.2 px)→ 真检测框喂 StereoRanger.update(),记录值与 解析值 Z=F·B/D 同量级 → ③ 空场景(正常空帧不告警)→ ④ 形状不一致拒收 → ⑤ 帧周期实测(台架开发机 ≈34 ms/帧,占位)。替换素材文件即可换真实图片。

验收对照(规格 §6)

项 状态
坐标回映误差 <0.5 px 由库内完成,无法构件级单测;以合成对整体校验(
垂直对齐 P95 ≤1 px 合成对实测
类别一致性 模型单类 Drone,合成对天然一致;待真实素材
输出合法性 ✓ 单测(非有限值/退化框构造用例)
输入纯净性 ✓ 结构性保证(前端无跨帧状态)
下游对接 ✓ demo 场景②;δZ ∝ Z² 趋势由测距端单测覆盖
帧周期 △ 台架开发机实测 ≈34 ms/帧(30 fps 预算 33.3 ms);待平台实测(P7)