diff --git a/docs/吊舱解算验证方案-模拟飞控喂表3.md b/docs/吊舱解算验证方案-模拟飞控喂表3.md index 17d2ec5..d51b141 100644 --- a/docs/吊舱解算验证方案-模拟飞控喂表3.md +++ b/docs/吊舱解算验证方案-模拟飞控喂表3.md @@ -1,8 +1,50 @@ # 用「模拟飞控」喂表3 验证吊舱目标解算能力的实验方案 -> 状态:**代码已实现**(`sim_fc.py` 已落地,配套 21 个纯软单测), -> 参数已敲定见 §7,**待上机实跑**得到 Q1/Q2/Q3 的实测答案。 -> 日期:2026-09-29 关联:`docs/树莓派4B部署注意事项.md` §10 / §10.1 +> 状态:**Q1 已有实测答案 —— 无激光时吊舱不输出 表4**(2026-10-08,见 §0.1)。 +> 工具 `sim_fc.py` 已落地并配套 21 个纯软单测,参数已敲定见 §7。 +> 日期:2026-09-29 立项 / 2026-10-08 实测收敛 关联:`docs/树莓派4B部署注意事项.md` §10.0 + +--- + +## 0.1 🔴 实测结论(2026-10-08):Q1 = 否,无激光 → 表4 不输出 + +**实验**:`sim_fc.py` 以 20Hz 持续喂伪造 表3(lat 39.9042 / lon 116.4074 / +rel_alt 50m,真实 UTC 递增时戳),同时观测下行。 + +**结果**:只收到 表2-2 遥测(≈60Hz),**表4 一帧都没有**(退出码 1)。 + +**判读**:吊舱的目标地理解算**依赖激光测距**——没有斜距就无法把视场角换算成 +地面坐标,于是它**根本不发 表4**(不是发 0,也不是吐旧值)。原先设想的 +"是否假设目标贴地"随之作废:**它压根没解算**。 + +**三个问题的答案**: + +| 问题 | 实测答案 | +|---|---| +| **Q1** 无激光时是否输出表4 | ❌ **否**。表4 路径在本设备上不存在 | +| **Q2** 表4 频率 | ❌ 无从谈起(无输出)。`target_stale_ms` 对表4 无意义 | +| **Q3** 目标丢失后表4 行为 | ❌ 无法观测。目标丢失判据**不能**依赖表4(这与 §10.1 的预判一致) | + +**架构影响(分叉已选边)**: + +1. `guidance.py` 地理位置引导律在本设备上**永久刹车**——`bridge.py:169` 的 + `target_valid` 依赖表4,表4 恒缺则每周期走 `_brake()`。安全方向正确(不会乱飞), + 但**功能上等于吊舱侧不提供目标坐标**; +2. 目标距离与坐标**必须改由我方自有链路提供**:`stereo_yolo` 检测 → + `stereo_ranger` 双目视距。吊舱退化为"伺服稳定指向 + 表2-2 脱靶量"传感器; +3. 若要保留吊舱侧目标定位,只能换**带激光测距的吊舱型号**或外挂测距模块; +4. `--auto-unlock` **已实测禁用**(`pod_console.py:232` 把 `last_tgt_s is None` + 当成超时,会一锁定就误解锁并循环震荡)。 + +**尚未完全排除的两种可能**(值得再花 10 分钟钉死,钉死前不要把结论当成绝对): + +1. 本次测试**未必真的锁定成功**(bit2 未置位)→ 吊舱可能要求先锁定才解算。 + 复测加 `--detect person --auto-lock`,看汇总里"首次锁定"是否有值; + 若**锁定后仍无表4**,结论才完全坐实。 +2. 吊舱可能对 表3 的**时戳/位置有效性**有额外要求 → 用 `--freeze-time` 做一次对照。 + +**阶段2 / 阶段3 处置**:**取消**(精度比对与高度敏感性扫描都以表4 存在为前提)。 +除非上面第 1 条复测翻盘,否则不要再往这条路上投时间。 --- diff --git a/docs/树莓派4B部署注意事项.md b/docs/树莓派4B部署注意事项.md index 84b96a8..8fdb089 100644 --- a/docs/树莓派4B部署注意事项.md +++ b/docs/树莓派4B部署注意事项.md @@ -171,17 +171,15 @@ WantedBy=multi-user.target `journalctl -u pod-bridge -f`。 - 代码热路径按 10Hz 引导 + 20Hz 姿态下发,Pi 4B 单核占用可忽略,无需实时内核。 -## 10. 本设备实测特性(2026-09-28,厂商确认) +## 10. 本设备实测特性(2026-09-28 厂商确认 + 2026-10-08 实测) - **本吊舱不含激光测距模块**。后果: - 表2-2 激光测距字段恒为 0(无效),不可作为判据; - `pod_bridge.json` 的 `link.require_laser_valid` **必须为 false**,否则引导 永久刹车(该闸门是为"有激光且锁定位置位粘滞"的机型准备的); - - ⚠ 需向厂商确认:无激光时吊舱如何解算表4 的目标绝对/相对高度 - (是否假设目标在地面),这直接决定引导用的坐标精度。 - → 已给出**不依赖厂商答复的自测方案**:用树莓派伪造飞控数据喂 表3, - 逼吊舱吐 表4,见 `docs/吊舱解算验证方案-模拟飞控喂表3.md`。 - 该实验还顺带回答 §10.1 悬着的两个前置问题(表4 是否存在、实际频率)。 + - 🔴 **无激光时吊舱不输出 表4(目标经纬度)** —— **实测确认,2026-10-08**。 + 详见 §10.0,这是本项目迄今最重要的架构性结论。 + - **锁定位置位(表2-2 状态字节1 bit2)在目标丢失后不复位**——实测观察; 表2-2 脱靶量是定长字段且协议**没有无效标志位**,丢失时表现为 0, 无法区分"不输出"与"输出 0"。 @@ -193,16 +191,48 @@ WantedBy=multi-user.target 0x07 / 0x0F / 0x10(实测结论,2026-09-28 纠正)。只有发了 `0x08 目标识别关` 才真正退出识别,此时才需要按 `d` / `a` 重新开启。 -### 10.1 自动解锁机制(待定,勿贸然启用) +### 10.0 实测结论:无激光 → 表4 不输出(2026-10-08) + +**实验**:用 `sim_fc.py` 以 20Hz 持续喂伪造 表3(lat 39.9042 / lon 116.4074 / +rel_alt 50m,真实 UTC 递增时戳),同时观测下行。 + +**结果**:只收到 表2-2 遥测(约 60Hz),**表4 一帧都没有**。 + +**判读**:吊舱的目标地理解算**依赖激光测距**。没有距离输入时,它拿不到目标的 +斜距,无法把视场角换算成地面坐标,于是**根本不发 表4**(不是发 0、不是发旧值)。 +此前"无激光时如何解算高度/是否假设目标贴地"的问题随之作废——**根本没解算**。 + +**直接后果(务必读完再决定下一步)**: + +| 受影响项 | 后果 | +|---|---| +| `guidance.py` 地理位置引导律 | **永久刹车**。桥接层 `bridge.py:169` 的 `target_valid` 依赖表4;表4 恒缺 → `_latest_target` 恒 None → `GuidanceLaw.update()` 每周期走 `_brake("载机位置或目标不可用/超龄")`。安全方向正确(降级刹车、不会乱飞),但**吊舱表4 这条引导路径在本设备上等于不存在** | +| 目标距离/坐标来源 | 必须改由**我方自有链路**提供:`stereo_yolo` 检测 → `stereo_ranger` 双目视距。吊舱只当作"稳定指向 + 表2-2 脱靶量"的伺服/传感器用 | +| `--auto-unlock`(§10.1) | **必然误触发,已实测禁用**。见 §10.1 | +| 后续选型 | 若要保留吊舱侧目标定位,只能换**带激光测距的吊舱型号**,或外挂测距模块 | + +**尚未排除的两种可能(值得再花 10 分钟钉死)**: +1. 吊舱需要**先真正锁定**才解算,而本次测试未必锁定成功(bit2 未置位)。 + → 复测时加 `--detect person --auto-lock`,并在汇总里确认"首次锁定"有值; + 若锁定后仍无表4,结论才完全坐实。 +2. 吊舱要求 表3 的**时戳/位置有效性**满足某种条件(例如 GPS 有效标志)。 + → 可用 `--freeze-time` 做一次对照。 + +> 复测命令:`python -m pod_bridge.sim_fc /dev/ttyAMA0 --detect person --auto-lock +> --seconds 60 --json phase1.json`,退出码 1 = 仍无表4。 +### 10.1 自动解锁机制(🔴 本设备实测禁用,2026-10-08) `pod_console.py --auto-unlock SEC` 现按「锁定中 + 表4 超过 SEC 秒未更新」触发 0x0E。 -**该判据对本设备存疑**,开箱前需确认: +**该判据在本设备上 100% 误触发,不要用**:因为 §10.0 实测确认表4 根本不发, +`last_tgt_s` 恒为 None,而 `pod_console.py:232` 的条件把 +`last_tgt_s is None` **直接当成超时** → 一旦锁定满 SEC 秒就发 0x0E,并且每 +SEC 秒重复一次(与"假锁定"形成持续震荡:解锁 → 自动锁定 → 解锁 → …)。 -| 待确认项 | 影响 | +| 待确认项 | 现状(2026-10-08 更新) | |---|---| -| 本设备无激光,锁定后**到底发不发 表4** | 若表4 根本不发,`last_tgt_s` 恒空 → 一锁定就被误解锁,机制不可用 | -| 表4 的实际发送频率(规格 1~60Hz) | SEC 必须远大于实际周期(建议 ≥5s 且 ≥3 倍周期),否则误杀 | -| 解锁后是否自动重发 0x0F/0x10 重新捕获 | **不需要**:0x0F 是常驻模式,0x0E 后目标重新入画会自动锁定;自动重发反而可能与"假锁定"形成震荡 | +| 本设备无激光,锁定后到底发不发 表4 | ✅ **已实测:不发**。判据前提不成立 | +| 表4 的实际发送频率 | ❌ 无从谈起(无输出) | +| 解锁后是否自动重发 0x0F/0x10 重新捕获 | 不需要(0x0F 常驻),但**这反而让误解锁的震荡可以一直循环**,所以更不能用 | 候选替代判据(按可靠性排序): 1. **操作员按键 `u`**(台架阶段推荐,零误杀); diff --git a/utils/pod_bridge/pod_console.py b/utils/pod_bridge/pod_console.py index 34a4f39..ae79cdb 100644 --- a/utils/pod_bridge/pod_console.py +++ b/utils/pod_bridge/pod_console.py @@ -224,9 +224,10 @@ def main() -> int: latest_tgt = frame # ---- 自动解除锁定:锁定中但表4 长时间不更新 ---- - # ⚠ 本设备无激光测距,表4(目标经纬度)可能始终不发 → last_tgt_s 恒为 - # None,此判据会一锁定就误解锁。启用前先用控制台观察「目标帧 距今」, - # 确认锁定后表4 真的在发。触发判据的替代方案见部署文档 §10。 + # 🔴 本设备已实测(2026-10-08):无激光测距时吊舱**根本不发表4** → + # last_tgt_s 恒为 None,而下方条件把 None 当成超时,故本设备上 + # --auto-unlock 会一锁定就误解锁,并每 SEC 秒与「0x0F 常驻自动重锁」 + # 循环震荡。**不要再启用**,判据改用按键 u。详见部署文档 §10.0/§10.1。 if (args.auto_unlock > 0 and latest is not None and latest.is_target_locked and (last_tgt_s is None or now_s - last_tgt_s > args.auto_unlock)