- 伪造载机姿态/位置/时戳持续喂表3,单进程收发一体观测表2-2/表4 - 回答Q1(无激光是否仍解算表4)/Q2(表4实际频率)/Q3(丢失后行为) - 自动判读「目标贴地」假设: 表4相对高度是否约等于喂入的载机高度 - 退出码0/1/2/3便于脚本串测; --json落逐帧采样供阶段2聚类分析 - 默认参数已敲定: lat=39.9042 lon=116.4074 rel-alt=50 rate=20Hz - 纯软单测21条(注入fake serial), 全量回归372 passed
12 KiB
pod_bridge 移交树莓派 4B 部署注意事项
目标平台:树莓派 4B + Raspberry Pi OS 官方镜像(64 位,Bookworm / Trixie)。 (2026-09-23 由 Ubuntu 24.04 换回官方镜像:Ubuntu 镜像上串口配置与 libstdc++ 符号问题频出,官方镜像对 Pi 串口/固件支持最完整。) 范围:
utils/pod_bridge(及其依赖的visual_track_control控制原语库)。 核对日期:2026-09-23,对照规格《光电吊舱控制协议G版-PB.docx》; 依赖轮子已按 PyPI 2026-09-23 上游信息复核。
1. 系统与 Python 版本(硬性)
-
必须烧录 64 位镜像(Raspberry Pi Imager 里选 "Raspberry Pi OS (64-bit)"), 理由见 §3 MAVSDK 对 aarch64 的要求;32 位镜像不要碰。
-
烧完先确认:
cat /etc/os-release。Bookworm 自带 Python 3.11、 Trixie 自带 Python 3.13,均满足 PEP 604 标注(float | None/FrameParser | None)要求的 ≥3.10,直接用。 -
Bookworm 起系统 Python 受 PEP 668 保护,禁止
pip install进系统环境 (报 externally-managed-environment),一律建 venv;venv 支持若缺失先 apt 补:sudo apt update && sudo apt install -y python3-venv python3-pip python3 -m venv ~/venv-pod ~/venv-pod/bin/pip install -r requirements-pod.txt
2. 依赖裁剪(树莓派上别全装)
requirements.txt 是全仓库清单,吊舱桥接只需要其中两个,务必裁剪,
否则 torch/cv2 在 Pi 4B 上是几百 MB 且毫无意义:
# requirements-pod.txt(桥接最小集)
mavsdk<4 # ⚠ 锁版本,原因见 §3
pyserial
pytest # 仅上机自测需要,部署运行可省
pod_bridge本身纯 Python + 惰性导入:pyserial只在PodSerial实例化时加载,mavsdk只在Px4Gateway.connect/ 指令发送时加载——单测全绿不需要任何硬件库。- 不要在 Pi 上装 ultralytics/torch/opencv:那是
stereo_yolo检测链的依赖, Pi 4B 跑不动实时 YOLO,检测应留在机载算力更强的平台或吊舱内部。 - 若确实要装 opencv,用
opencv-python-headless(无 GUI 依赖), 并sudo apt install libopenblas-dev libatlas-base-dev。
3. MAVSDK 在 ARM 上的坑
-
mavsdk 4.x 是全新原生 API 重写,与经典 gRPC API 完全不兼容(2026-09-24 树莓派实测):没有
mavsdk.offboard/System/telemetry,本仓库全部 飞控代码(Px4Gateway / MavsdkCommandGateway / test.py)都是经典 API, 装 4.x 直接LegacyModuleError。必须锁mavsdk<4(最新 3.17.4, aarch64 官方 wheelmanylinux2014_aarch64,PyPI 已核对); 32 位 armv7l 支持不稳定——这是必须 64 位镜像的主因。 -
3.x 为 gRPC 架构,pip 包内嵌
mavsdk_server可执行文件,不加载 libcmavsdk.so,没有 libstdc++ 符号问题——此前两系统上轮番出现的undefined symbol: ..._M_futex_wait_until...是 4.x 原生库专属坑, 换回 <4 后自然消失。 -
安装后自检三步(缺一不可;单测全绿不证明 mavsdk 已装,测试已打桩脱钩):
pip list | grep -i mavsdk # 必须显示 3.x(如 3.17.4) python -c "from mavsdk import System; print('api OK')" find ~/venv-pod -name mavsdk_server # 应有输出;直接执行能起服务更佳 -
连接串写法(
pod_bridge.json的px4.connection_string):- Pi 串口直连飞控 TELEM:
serial:///dev/ttyUSB0:57600(波特率按飞控侧设置); - 经数传/仿真:
udp://:14540。
- Pi 串口直连飞控 TELEM:
-
PX4 侧需确认 TELEM 口已配 MAVLink 且 offboard 心跳未被占用。
4. 串口硬件(吊舱侧,最容易翻车)
- 协议为 115200 8N1 无校验(规格 §2.1)。先确认吊舱串口电平:
- RS232 → 必须加电平转换或直接用 USB 转串口模块(FTDI/CH340,推荐,
免折腾 GPIO UART,设备号
/dev/ttyUSB0); - TTL 3.3V → 可用 Pi GPIO14/15(
/dev/serial0)。
- RS232 → 必须加电平转换或直接用 USB 转串口模块(FTDI/CH340,推荐,
免折腾 GPIO UART,设备号
- 用 GPIO UART 时(官方镜像自带
raspi-config):sudo raspi-config→ Interface Options → Serial Port:控制台选 No、 串口硬件选 Yes(等价于enable_uart=1+ 关闭 serial-getty 抢占);- Pi 4B 上
/dev/serial0默认指向 mini-UART(ttyS0,波特率随主频漂移), 115200 下不稳——在/boot/firmware/config.txt加dtoverlay=disable-bt把 PL011(ttyAMA0)换回来,/dev/serial0随之改指 ttyAMA0 (想保留蓝牙改用dtoverlay=miniuart-bt); - 必须同时清掉内核控制台(2026-09-28 实测踩坑):
dmesg出现printk: legacy console [ttyS0] enabled说明内核 printk 也在往同一个口 吐日志,与吊舱协议流混在一起(表现为能收几帧后OSError errno 5或 持续坏帧)。编辑/boot/firmware/cmdline.txt(单行文件,务必先备份) 删掉console=serial0,115200/console=ttyS0,115200后重启; - 串口静默后三连验证(缺一不可):
ls -l /dev/serial0→ttyAMA0、ps aux | grep "[a]getty"无串口 getty、dmesg | grep -i "console \["不再出现 ttyS0; - Pi 是 3.3V 电平,绝不直连 5V/RS232 电平,会烧 GPIO。
- 接线三线:TX/RX 交叉、必须共地(GND),不共地是通讯乱码的第一大原因。
- 用户权限:
sudo usermod -aG dialout $USER,重登录生效,否则 open 串口 Permission denied。
5. 时钟同步(规格表3 依赖)
bridge._build_attitude用宿主机 UTC 墙钟填表3 的年月日时分秒; Pi 4B 无 RTC,断电后时间是错的,吊舱会拿到错误时间戳。- 上机后确认
timedatectl显示System clock synchronized: yes; 无网环境考虑给 Pi 加 RTC 模块,或由飞控 GPS 授时(需改代码,见 §8 已知问题)。
6. 配置文件(utils/config/pod_bridge.json)上机必改两处
| 字段 | 改成 |
|---|---|
pod_serial.port |
实际设备号,如 /dev/ttyUSB0(留空会 fail-fast 报错,这是刻意的) |
px4.connection_string |
实际飞控连接,如 serial:///dev/ttyUSB1:57600 |
- 引导参数(
guidance.*)全部是占位值,未经标定不得用于飞行决策; 首次实机只验证通讯收发,速度上限先调小。 - 配置加载拒绝未知键——改 JSON 时注意拼写,多写的键会直接报错(防呆设计)。
7. 上机验证顺序(每步过了再下一步)
- 纯软件自测(无需任何接线):
cd utils && ~/venv-pod/bin/python -m pytest tests/pod_bridge -q→ 应 116 全绿 (全量 372 条;pod_bridge 含 sim_fc 的 21 条。单测已打桩 mavsdk 设定点类型, 与 libcmavsdk 原生库健康状态无关,也无需真实串口)。 - 串口回环:USB 转串口模块 TX/RX 短接,跑一个 feed/echo 小脚本验证字节收发。
- 只接吊舱:用仓库自带诊断脚本
python utils/pod_bridge/listen_pod.py /dev/ttyUSB0 --seconds 5(加--center可同时下发归中 0x2B 验证下行);脚本自动统计帧数并判读, 遥测帧率 ≥48Hz(标称 60Hz 的 80%)退出码为 0 即链路正常。 - 验证吊舱能否解算目标坐标(不接飞控,避免绕过这一步):
python -m pod_bridge.sim_fc /dev/ttyAMA0 --seconds 60 --json phase1.json由树莓派伪造 表3 喂给吊舱,看是否回 表4。这一步的结论直接决定后续架构取舍, 详见docs/吊舱解算验证方案-模拟飞控喂表3.md。 ⚠ 跑它的时候关掉 listen_pod / pod_console / bridge —— 同一个串口不能两人占。 - 只接飞控:
Px4Gateway.connect后确认own_state().position_valid为 True。 - 全链路地面联试:电机不装桨,观察 offboard 心跳与引导输出。
- 最后才考虑带动力测试。
8. 已知问题处置状态(2026-09-23 更新)
代码审查(2026-09-22)发现 4 项,其中 3 项安全相关缺陷已于 2026-09-23 修复, 新增回归测试 8 条(pod_bridge 测试 84 → 92,全量 348 条全绿):
→ 已修:Px4Gateway四路遥测共用OwnState.time_msOwnState拆出position_time_ms(仅 position 流刷新),引导的位置超龄判定改用它;表2-2 锁定状态(bit2)未参与引导门控→ 已修:PodBridge._pod_locked要求遥测在link.telemetry_timeout_ms内且 bit2=锁定,表4 坐标才视为有效 (该配置项由此从死配置变为生效配置);→ 已修:单周期异常按「第 1 次 + 每满 100 次」告警并补发零速刹车保 offboard 心跳,任务不终止;PodBridge.run主循环无异常护栏- 表3 时间戳用宿主机墙钟而非 GPS 时间(见 §5)——仍未修,属可接受风险, 如需精确授时按 §5 对策处理。
9. 常驻运行(systemd 示例)
# /etc/systemd/system/pod-bridge.service
[Unit]
Description=Pod-PX4 Bridge
After=network.target
[Service]
ExecStart=/home/<user>/venv-pod/bin/python -m pod_bridge_app
WorkingDirectory=/home/<user>/code_drone-test/utils
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
- 热路径不打日志是设计使然(告警走 WarnThrottle),现场排查用
journalctl -u pod-bridge -f。 - 代码热路径按 10Hz 引导 + 20Hz 姿态下发,Pi 4B 单核占用可忽略,无需实时内核。
10. 本设备实测特性(2026-09-28,厂商确认)
- 本吊舱不含激光测距模块。后果:
- 表2-2 激光测距字段恒为 0(无效),不可作为判据;
pod_bridge.json的link.require_laser_valid必须为 false,否则引导 永久刹车(该闸门是为"有激光且锁定位置位粘滞"的机型准备的);- ⚠ 需向厂商确认:无激光时吊舱如何解算表4 的目标绝对/相对高度
(是否假设目标在地面),这直接决定引导用的坐标精度。
→ 已给出不依赖厂商答复的自测方案:用树莓派伪造飞控数据喂 表3,
逼吊舱吐 表4,见
docs/吊舱解算验证方案-模拟飞控喂表3.md。 该实验还顺带回答 §10.1 悬着的两个前置问题(表4 是否存在、实际频率)。
- 锁定位置位(表2-2 状态字节1 bit2)在目标丢失后不复位——实测观察; 表2-2 脱靶量是定长字段且协议没有无效标志位,丢失时表现为 0, 无法区分"不输出"与"输出 0"。 → 单靠 bit2 会在目标丢失后继续朝过期坐标飞,必须有第二判据(见 §8/§11)。
- 主动解除锁定有效:发
0x0E 停止跟踪后 bit2 复位为未锁定,厂商测试软件的 锁定框同步消失(2026-09-28 实测)。 - ⚠ 0x0F「发现目标自动锁定」是常驻模式,不是一次性动作:
0x0E只停止这一次 跟踪,并不退出自动锁定——目标重新进入画面时吊舱会自动重新锁定,无需重发 0x07 / 0x0F / 0x10(实测结论,2026-09-28 纠正)。只有发了0x08 目标识别关才真正退出识别,此时才需要按d/a重新开启。
10.1 自动解锁机制(待定,勿贸然启用)
pod_console.py --auto-unlock SEC 现按「锁定中 + 表4 超过 SEC 秒未更新」触发 0x0E。
该判据对本设备存疑,开箱前需确认:
| 待确认项 | 影响 |
|---|---|
| 本设备无激光,锁定后到底发不发 表4 | 若表4 根本不发,last_tgt_s 恒空 → 一锁定就被误解锁,机制不可用 |
| 表4 的实际发送频率(规格 1~60Hz) | SEC 必须远大于实际周期(建议 ≥5s 且 ≥3 倍周期),否则误杀 |
| 解锁后是否自动重发 0x0F/0x10 重新捕获 | 不需要:0x0F 是常驻模式,0x0E 后目标重新入画会自动锁定;自动重发反而可能与"假锁定"形成震荡 |
候选替代判据(按可靠性排序):
- 操作员按键
u(台架阶段推荐,零误杀); - 表4 超时(需先确认表4 存在);
- 锁定持续超过 T 秒且从未收到表4 → 判为假锁定,解锁一次并告警(不循环);
- 脱靶量恒为 0 持续 T 秒 —— 居中跟踪时也会为 0,误杀风险高,不推荐;
- 由我方视觉链路(stereo_yolo / target_tracker)交叉验证 —— 最可靠,待集成。
飞行期的正确行为是刹车保护(现有 target_stale_ms 已覆盖),与"解锁以便重新
搜索"是两件事,不要混为一谈。
对自动解锁风险评估的修正:既然 0x0E 之后自动锁定仍生效、目标重现会自动锁定, 误触发一次 0x0E 的代价只是"本次跟踪中断、(我方)刹车一次",吊舱会自行恢复捕获 ——所以自动解锁的副作用可控,启用与否主要取决于触发判据准不准(表4 是否存在), 而不是担心解锁后锁不回来。