panda 板级固件:三条 CAN 路径与 1 Hz 巡检的真实边界

作者:

在

结论:panda 板级固件不是一个「CAN 转发器」。主机候选发送、车辆报文接收、板内总线转发是三条独立路径,各自经过不同的安全门与回执机制。启动时安全模式先被置为 SILENT,只有车型模式才驱动拦截继电器;而 1 Hz 的 safety_tick() 只做超时、最低频率与有效性的巡检,不能当成毫秒级断线检测。本文为固定源码归档的静态阅读,未编译固件、未连接实板。

三条 CAN 路径与各自的安全门

把三条路径混为一谈,是理解 panda 安全边界时最常见的错误。主机写入的候选帧、车辆发来的报文、以及板内转发,处在不同的钩子和不同的准入条件之下。

路径 关键调用链 安全语义
主机候选发送 pandad::can_send_thread() → comms_can_write() → can_send(..., false) → safety_tx_hook() → process_can() 主机先丢弃超过 1 秒的 sendcan;板端每个完整 CAN 包经过 TX 白名单、车型限值和继电器故障门。被拒帧作为 rejected 回执进入 RX 队列,不会进 TX FIFO。
车辆报文接收 can_rx() → safety_rx_hook() → comms_can_read() → pandad::can_recv() 板端先从 FDCAN FIFO 取帧并附 bus/长度/FD 标志;安全 RX 仅对通过对应检查和白名单的帧调用车型状态钩子。
板内总线转发 can_rx() → safety_fwd_hook() → can_send(..., true) 转发准许由独立的 safety_fwd_hook() 决定;true 表示跳过主机发送的 TX 钩子。当前代码在 safety_rx_hook() 之前计算转发并入队,因此不能声称接收帧必须先通过 RX 有效性检查才会被转发。

safety_fwd_hook() 默认只映射 0↔2;继电器故障或模式设置 disable_forwarding 时阻断,并静态屏蔽需由 openpilot 替代发送的原车报文。另有细节:主机 CAN 组包维护跨传输分片的缓存,comms_can_reset() 在连接重建时清空它,避免半包污染下一连接。

启动顺序也值得单独看:main() 先将安全模式设为 SILENT,再启用收发器、定时中断和 USB/SPI。主机侧 PandaSafety::fetchCarParams() 要求 FirmwareQueryDone、ControlsReady,随后 setSafetyMode() 发送安全模式和参数。这道门是启动协调,不是对任意主机代码或插件的独立授权机制。

三条路径共享同一套健康上报(get_health_pkt())与继电器状态,但准入条件各不相同,这正是「主机能发」与「板端放行」必须分开验证的原因。

安全模式与心跳:秒级兜底不是唯一保护

安全模式不是普通运行时插件可任意替换的配置,切换会影响继电器和 CAN 队列,必须按车型、固件和线束验证。

函数/状态 当前实现 硬件含义
set_safety_mode() 调用 set_safety_hooks();未知模式退回 SAFETY_SILENT,连 SILENT 初始化都失败则停止。 SILENT/NOOUTPUT/ELM327 释放拦截继电器;车型模式才驱动继电器再初始化 CAN。
set_safety_hooks() 注册模式、重置安全状态。MG、PSA、BYD、Subaru Preglobal、Volkswagen MLB/PQ、ALLOUTPUT 等仅在 ALLOW_DEBUG 编译块注册。 有目录和测试不代表 release 固件可选择该安全模式;必须核对实际固件构建与主机 safetyConfigs。
safety_tx_hook() 车型模式检查地址、bus、长度白名单及车型 TX 约束;继电器故障会否决。 「panda 永远过滤任意主机帧」是错误表述;诊断与调试模式不具备车型执行边界。
safety_tick() 每秒检查期望 RX 报文的超时、最低频率和有效性,异常时清 controls_allowed。 不能将 1 Hz 巡检误写为毫秒级断线检测。
tick_handler() 8 Hz 调用风扇、线束、软件看门狗;1 Hz 处理点火、心跳、safety_tick()。点火时主机心跳 5 秒未到、非点火时 2 秒未到,切 SILENT。 心跳退出是秒级兜底,不是新硬件对快速危险动作的唯一保护。
get_health_pkt() 上报安全模式、参数、心跳、RX/TX 拦截计数、队列溢出、CAN 错误、线束状态、电压/电流/温度。 主机应把健康状态和车型配置对齐,而不是仅判断 USB 在线。

通信、线束与物理边界

这些边界决定了「代码静态链路」与「电气默认态」之间的差距。队列里的 XOR 校验只保证主机与固件之间的传输完整性,与车型 CAN 报文自身的 checksum 不是一回事。

组件 函数/实现 设计观察
CAN 队列与校验 can_push()、can_pop()、can_set_checksum() RX/TX 使用临界区环形队列;队列满时累计溢出。包内 XOR 是传输完整性检查,不等于车型 CAN 报文 checksum。
线束方向与继电器 harness_detect_orientation()、set_intercept_relay() SBU 电压判断正反插;方向改变时重新映射 bus、初始化 CAN 和安全模式。实际断电默认态须以原理图和台架证明。
控制请求 comms_control_handler() USB/SPI 命令能设安全模式、CAN 速率、FD 行为、继电器、心跳和电源;0xc5 继电器控制在代码中并无 ALLOW_DEBUG 条件(注释写 DEBUG)。
启动完整性 bootstub.main() 验证应用长度、版本、SHA 与 RSA 发布签名;启动签名约束固件映像,不代表运行时 USB 控制命令已获得逐操作鉴权。

电源与错误恢复另有边界:set_power_save_state() 省电时关部分中断/收发器但保留主总线用于点火检测,源码明确要求安全功能执行期间不进入硬件低功耗模式;FDCAN 的发送前包校验、总线 off、RX FIFO 丢失与 ACK 错误会进入健康计数与控制器恢复路径,CAN FD/BRS 可由收到的帧检测并改变总线配置。现有测试入口包括车型安全单元测试、USB 协议测试,以及 HITL 的线束、CAN 回环与延时、NOOUTPUT 三组;本地归档未连接实板、也未运行这些测试,代码静态链路不证明电气默认态或故障覆盖率。

自研车端网关的最低验收点

  1. 先复现 SILENT 启动、未知模式回退、车型 RX/TX/转发独立门、报文来源总线与长度、心跳丢失、原车 ECU 冲突检测;插件只可提交受限候选命令,不可获得原始控制端点。
  2. 分别注入心跳超时、软件 tick 间隔故障、FDCAN bus-off、队列满、主机失联、线束正反插与电源断开;记录 MCU 输出、继电器触点与原车总线波形,不能只看 health_t 计数。
  3. 板型 red、tres、cuatro 的 GPIO/收发器/供电差异要逐板核对;PANDA_CAN_CNT=3 与线束方向映射只是当前实现,不应直接推导新 PCB 的引脚或额定参数。
  4. 固件签名、版本、发布/调试模式、主机权限和可升级回滚是共同边界;不能用调试固件测试结果替代发布固件证据。

常见问题

panda 的 1 Hz safety_tick() 能替代毫秒级断线检测吗?

不能。safety_tick() 每秒检查期望 RX 报文的超时、最低频率和有效性,异常时清 controls_allowed,其丢帧容忍窗口偏保守。它是秒级巡检,额外的主机超时、CAN 控制器故障与硬件失效仍需单独验证。

主机发送的每一帧都会被 panda 无条件过滤吗?

不是。车型模式检查地址、bus、长度白名单及车型 TX 约束,但 ELM327 和调试固件的 ALLOUTPUT 特例会把白名单视为通过。因此「panda 永远过滤任意主机帧」是错误表述,诊断与调试模式不具备车型执行边界。

为什么板内转发不经过 safety_tx_hook()?

因为转发以 can_send(..., true) 调用,true 跳过主机发送的 TX 钩子,其准入由独立的 safety_fwd_hook() 决定(默认只映射 0↔2)。这与 USB/SPI 客户端经 can_send(..., false) 的发送许可不是同一机制。

固件签名能保证运行时 USB 命令被逐操作鉴权吗?

不能。启动签名只约束固件映像;运行时控制命令的准入需主机与通信入口另行约束,USB/SPI 控制请求应纳入可信计算基,不应直接向第三方插件暴露。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/PANDA_BOARD_DEEP_DIVE.md(取用了三条 CAN 路径与安全门、安全模式与心跳、通信/线束/物理边界,以及自研网关验收点)。
  • 项目 02 —— openpilot-research/CAR_PROTOCOL_IMPLEMENTATIONS.md(仅用于确认车型差异与插件契约的引用位置)。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注