精简仓库结构并增强任务链路:归档双目可选后端、新增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。
This commit is contained in:
keita
2026-10-21 14:48:41 +08:00
parent 53fdd3b13f
commit df91b1110e
159 changed files with 6121 additions and 1617 deletions
@@ -0,0 +1,79 @@
"""视差取点器测试(规格 §6.1/§11.4:同侧边缘、框宽差隔离、越窗回退、边界含等号)。"""
import pytest
from stereo_ranger import Detection, DisparitySource, StereoConfig, sample_point, select_disparity
CFG = StereoConfig()
def _box(x1: float, x2: float, *, y1: float = 500.0, y2: float = 540.0) -> Detection:
return Detection(x1, y1, x2, y2)
def _disparity(left: Detection, right: Detection):
return select_disparity(sample_point(left), sample_point(right), CFG)
def test_left_edge_used_as_primary_sampling():
d = _disparity(_box(1000.0, 1060.0), _box(990.4, 1050.4))
assert d.source is DisparitySource.LEFT_EDGE
assert d.d_px == pytest.approx(9.6)
assert d.in_domain and d.positive and not d.fallback_used
def test_box_width_difference_does_not_enter_edge_disparity():
# §11.4:构造两目不等宽框,断言同侧边缘视差相同(框宽差不进入视差)
left = _box(1000.0, 1100.0) # 宽 100
right_narrow = _box(994.0, 1024.0) # 宽 30,左边缘差同 6 px
right_wide = _box(994.0, 1094.0) # 宽 100
d_narrow = _disparity(left, right_narrow)
d_wide = _disparity(left, right_wide)
assert d_narrow.d_px == pytest.approx(6.0)
assert d_wide.d_px == pytest.approx(6.0)
def test_center_disparity_would_be_polluted_by_width_difference():
# 对照:中心视差 41 px = 边缘视差 6 px + 框宽差的一半 35 px,证明主取点选择边缘的理由
left = _box(1000.0, 1100.0) # 宽 100, center 1050
right_narrow = _box(994.0, 1024.0) # 宽 30, center 1009
assert (left.cx - right_narrow.cx) == pytest.approx(41.0)
def test_center_fallback_when_edge_out_of_window():
# 边缘视差 60 px 越窗(> 57.6),中心视差 40 px 在域内 → 回退中心并标记
left = _box(1000.0, 1060.0) # center 1030
right = _box(940.0, 1040.0) # center 990
d = _disparity(left, right)
assert d.fallback_used is True
assert d.source is DisparitySource.CENTER_FALLBACK
assert d.d_px == pytest.approx(40.0)
assert d.in_domain and d.positive
def test_both_out_of_window_keeps_edge_value_for_diagnostics():
left = _box(1000.0, 1200.0) # center 1100
right = _box(900.0, 1000.0) # center 950
d = _disparity(left, right)
assert d.d_px == pytest.approx(100.0) # 边缘值保留(诊断 + 违例告警)
assert not d.in_domain and not d.fallback_used and d.positive
def test_domain_boundaries_are_inclusive():
assert _disparity(_box(1000.0, 1060.0), _box(997.0, 1057.0)).in_domain # D = 3.0 = 下限
# 上限用二进制可精确表示的 57.5 构造:十进制字面量 57.6 无法精确表示,
# 框坐标相减会落到 57.6 常量之上,边界断言被浮点舍入干扰
exact_cfg = StereoConfig(disparity_max_px=57.5)
left, right = _box(1000.0, 1060.0), _box(942.5, 1002.5)
assert select_disparity(sample_point(left), sample_point(right), exact_cfg).in_domain # D = 57.5 = 上限
def test_negative_disparity_flagged_as_violation():
d = _disparity(_box(1000.0, 1060.0), _box(1010.0, 1070.0))
assert d.d_px == pytest.approx(-10.0)
assert not d.positive and not d.in_domain
def test_non_finite_box_marked_unusable():
assert sample_point(Detection(float("nan"), 0.0, 10.0, 10.0)).finite is False
assert sample_point(Detection(0.0, 0.0, 10.0, 10.0)).finite is True