结论:pandad 的三个设备测试给出三种不同强度的证据:恢复测试只证明设备可见性,CAN loopback 证明期望集合被逐条消耗,SPI 注错证明服务存活与粗时序。它们都会复位/刷写 panda、切换 allOutput safety 并发送随机 CAN,绝不可在连接车辆的环境运行。源文档明确,三者都不能证明永久 NACK、非法长度、短健康包、热插拔、多 Panda、心跳失效后的 MCU 静默或实际车型 RX/TX safety 钩子。本文为静态核对,未启动测试、未刷写、未发送 CAN、未连接任何设备。
恢复测试:只证明设备可被看到
TestPandad 要求 comma 硬件;每个测试的 setUp() 先执行测试基类的硬件初始化,再停止已有 pandad,如果当前枚举不到 Panda 就尝试启动包装器恢复。核心 _run_test() 启动 pandad,在 30 秒内订阅 pandaStates,看到至少一块且首块 pandaType != unknown 即停止并返回耗时。
源文档强调它没有断言 CAN 能收发、固件签名值、心跳健康或目标安全模式。因此「看到 pandaStates」不等于整条安全链通过。
| 场景 | 实际操作 | 静态覆盖范围 |
|---|---|---|
test_in_dfu() |
主机 GPIO 进入 ROM DFU,再让包装器恢复 | 验证可恢复到非 unknown 状态,不测刷写中断 |
test_in_bootstub() |
经 Panda API 进入 bootstub,确认标志,再让包装器恢复 | 验证 bootstub 起点的正常恢复 |
test_in_reset() |
操作 STM_RST_N,确认暂时枚举不到 Panda,再启动包装器 |
属于物理复位线测试,不是纯软件 mock |
test_release_to_devel_bootstub() |
_flash_bootstub() 选择 MCU 配套 bootstub 文件并通过 PandaDFU.program_bootstub() 写入 |
有真实固件修改;名称里的 release/devel 不能替代证书/签名核验 |
test_recover_from_bad_bootstub() |
进入 DFU 后向 bootstub 起始地址写入 100 个零字节,复位并确认普通/DFU 枚举为空,再靠包装器恢复 | 主动制造启动损坏,失败时可能需要外部救援 |
公共台架夹具与 allOutput 的放开范围
setup_pandad() 有三次重试:清除当前测试 Prefix 下的 Params,反复发布 deviceState.started=False 等待非 unknown pandaStates,再写 FirmwareQueryDone、ControlsReady 和仅含 allOutput 的 CarParams.safetyConfigs,发布 started=True。
两处边界需要注意:测试基类的 OpenpilotPrefix 隔离的是软件路径,不隔离接在 panda 上的物理 CAN;allOutput 的 tx_hook 无帧内容限制,通用发送门也对该模式放开白名单,这不是车型量产 safety 策略。台架必须与车辆 CAN 物理断开,不能仅依靠软件设置「loopback」。
loopback 与 SPI 注错的断言边界
send_random_can_messages() 对 0~2 号总线生成随机 29 位以内地址和 1~8 字节载荷,用 sendcan 发布;没有 CAN FD 长帧。TestBoarddLoopback.test_loopback() 运行 200 轮,每轮除原总线集合外还期待 src+128 的发送回执集合;收到消息就从集合移除,最后要求所有期望集合清空。源文档指出这比只看「有 CAN 流量」强,但不测负载极限、重复帧保序、超时恢复或车辆控制的安全限幅;每轮接收循环最多 500 次 wait_for_one,没有外层总时间上限。
TestBoarddSpi.setup_class() 设置 STARTED=1、SPI_ERR_PROB=0.001,默认还开启 BOARDD_LOOPBACK;SPI 层在配置概率下扰动传输长度、TX/RX 缓冲,然后继续真实 ioctl,故它是传输层随机注错而非确定性故障脚本。test_spi_corruption() 在默认约 20 秒循环中发送随机 CAN,检查状态消息有效与设备类型/点火/电压/风扇范围,并统计三种主题的平均/最大消息间隔和数量。
| 测试 | 断言边界 | 不能证明 |
|---|---|---|
test_loopback() |
200 轮,期望集合(含 src+128 回执)全部清空 |
负载极限、重复帧保序、超时恢复、安全限幅 |
test_spi_corruption() |
CAN Event 即使 valid=False 仍进入测试;只断言收到且 src<=4 的(地址,载荷)曾在对应发送集合中;最后只要求 total_recv_count > 20(集合移除被注释) |
接收数等于发送数、每条只到达一次或全部发送帧均回传 |
JUNGLE_SPAM 模式 |
校验地址、源总线和 0xff 载荷模式 |
不产生本测试自己的发送集合 |
三种证据各自不证明什么
源文档收束得很清楚:恢复测试的设备可见性、loopback 的期望集合消耗、SPI 注错时的服务存活/粗时序,三者都不能证明永久 NACK、非法长度、短健康包、热插拔、多 Panda、心跳失效后的 MCU 静默或实际车型 RX/TX safety 钩子。
硬件插件验证的推荐顺序是:先用离线消息/协议测试,再在断开车辆的台架做可重复错误矩阵与固件恢复,最后另由独立 safety 层和受监督实车流程验收。已静态核对三个测试文件、公共 helper、SPI 注错点与 allOutput 安全钩子;未运行设备测试或执行任何刷写/CAN 操作。
源文档也把恢复包装器列为待验证对象:真实包装器的复位、签名和重试边界需要另做专题核对,本节列出的是测试证据。分支和计数结论是源码可见的断言范围,目标硬件时序和异常恢复结果仍需专用台架验证;全项目其他组件仍待逐项审查。
常见问题
pandad 恢复测试通过是否代表固件与安全链正常?
不代表。源文档明确 _run_test() 只要求在 30 秒内看到至少一块非 unknown 的 pandaStates,没有断言 CAN 能收发、固件签名值、心跳健康或目标安全模式。
为什么 SPI 注错测试通过不能说明接收完整?
因为它的断言很窄。源文档指出,test_spi_corruption() 只断言收到且 src<=4 的帧曾在对应发送集合中,集合移除被注释,最后只要求 total_recv_count > 20,并不要求接收数等于发送数或全部发送帧均回传。
台架上把 safety 设成 allOutput 就安全吗?
不安全。allOutput 的 tx_hook 无帧内容限制,通用发送门也放开白名单,它不是车型量产 safety 策略;且 OpenpilotPrefix 只隔离软件路径,不隔离物理 CAN,台架必须与车辆 CAN 物理断开。
这些测试能在连接车辆的环境下跑吗?
绝对不能。源文档在开头即警示:它们会复位/刷写 panda、切换 allOutput 并发送随机 CAN,绝不可在连接车辆的环境运行。
延伸阅读
- pandad 主机复位与 DFU 恢复回路 —— 包装器复位、签名与重试边界的展开。
- panda bootstub 签名与刷写边界 —— 刷写测试背后的签名核验前提。
- 风扇/主机/panda 反馈契约 —— 台架状态消息的另一条反馈链。
- 从使能到车轮的八道门 —— 说明台架证据为什么不能替代安全链验收。
参考资料
- 项目 02 ——
openpilot-research/PANDAD_HARDWARE_TEST_DFU_SPI_LOOPBACK_SAFETY_BOUNDARY_DEEP_DIVE.md(取用了恢复测试场景表、公共台架夹具、loopback 与 SPI 注错的断言范围、三类证据边界)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复