标签: 安全边界

  • Mazda 52/45 迟滞与 Nissan 12 帧平均:三品牌横控差异

    结论:Chrysler、Nissan、Mazda 三家都保留了原车的纵向巡航控制,但横向控制、取消策略与安全模式差异显著,不能抽象成同一套报文插件。Nissan 用后轮速度做安全侧转角限制、驾驶员扭矩按 12 帧平均;Mazda 的最低价速度门用 52/45 km/h 上/下行迟滞并等待 LKAS_BLOCK 清除;Chrysler 的 Ram HD 与 CUSW 被标为 dashcamOnly,CUSW 还使用独立的 chryslerCusw 模式且固件注册仅在 ALLOW_DEBUG。本文为对本地 source/ 与固定版本 opendbc_repo 的静态阅读,未运行测试、未连接实板、未验证任何年款。

    Chrysler:多平台 EPS 电机扭矩

    CarInterface._get_params() 根据 Pacifica/Jeep、Ram DT、Ram HD、CUSW 选择安全模式与参数;Ram HD、CUSW 标为 dashcamOnly,CUSW 使用独立的 chryslerCusw 模式,其固件注册仅存在于 ALLOW_DEBUG 编译块。此快照中雷达解析接口虽存在,但始终 radarUnavailable=True。EPS 固件/车型还会影响最低转向速度。

    CarState.update() 与 update_cusw() 使用不同的状态映射,Ram 的 ACC 状态来自相机总线。CarController.update() 在软件侧按 EPS 测得扭矩限幅,按平台采用最低转向速度迟滞,并在 LKAS 控制位下降后等待一段帧数再重新使能,以避免 EPS 故障。chryslercan.py 里的构帧函数各自编码仪表、转向与巡航按钮,车型报文与 checksum 不能仅靠品牌名复用。

    层 关键入口 已核对行为
    参数 CarInterface._get_params() 按 Pacifica/Jeep、Ram DT、Ram HD、CUSW 选安全模式;Ram HD、CUSW 标 dashcamOnly;CUSW 注册仅 ALLOW_DEBUG
    输入 CarState.update() / update_cusw() 主流平台与 CUSW 用不同状态映射;Ram 的 ACC 状态来自相机总线
    输出 CarController.update() 按 EPS 测得扭矩限幅,采用最低转向速度迟滞,LKAS 位下降后等一段帧数再重新使能
    构帧 chryslercan.py create_lkas_hud()、create_lkas_command()、create_cruise_buttons() 分别编码
    固件 RX/TX chrysler_rx_hook() / chrysler_tx_hook() / chrysler_init() Pacifica/Jeep 与 Ram DT/HD 分别设扭矩、变化率与电机扭矩误差限值;按钮只允许取消或恢复
    CUSW 固件 chrysler_cusw_init() / rx() / tx() 另套地址、RX 表、扭矩限值与按钮位定义;注释仍记载转向故障待解决

    验证入口是 Chrysler 与 CUSW 的安全测试以及通用车型接口测试;这三品牌各自目录下尚无独立车型测试套件,本次也未运行。CUSW 即使源码可编译,其安全模式注册也仅存在于调试固件,注释仍记载转向故障待解决,不可视为量产可用。

    Nissan:转角控制与车型专属取消

    Nissan 侧选择 nissan 安全模式,采用转角控制与原车纵向;Altima 通过安全参数切换 EPS 及相关状态报文总线。此快照中雷达不可用,但不能据此认定原车 ADAS 的内部感知已被软件接管。

    CarState.update() 里,Altima 与其他车型的 PT/相机 bus 对调;后轮速度用于安全侧转角限制的原始速度,四轮均值用于平滑估计,驾驶员扭矩按 12 帧平均以避免误报。CarController.update() 先用一阶滤波平滑低速大转角指令,再按速度/实际角度限制目标,驾驶员施力时降低 EPS 可用扭矩上限。

    取消策略是 Nissan 最不能包装成通用插件的部分:Rogue/X-Trail/Altima 发送 CRUISE_THROTTLE 取消,Leaf 则用 create_cancel_msg() 把 CANCEL_SEATBELT 置位,让 ProPILOT 将其解释为安全带未系而退出,源码说明会伴随蜂鸣和警告——这是明确的 HMI/安全语义。

    层 关键入口 已核对行为
    参数 CarInterface._get_params() 选 nissan 安全模式、转角控制、原车纵向;Altima 切换 EPS 相关报文总线
    输入 CarState.update() / get_can_parsers() Altima 与其余车型 PT/相机 bus 对调;后轮速度用于安全侧转角限制;驾驶员扭矩按 12 帧平均
    输出 CarController.update() 低速大转角先一阶滤波,再按速度/实际角度限幅;驾驶员施力时降低 EPS 可用扭矩上限
    Leaf 取消 create_cancel_msg() 置 CANCEL_SEATBELT 使 ProPILOT 解释为安全带未系退出,源码说明伴随蜂鸣与警告
    固件 RX/TX nissan_rx_hook() / nissan_tx_hook() / nissan_init() 转角幅度与速度相关变化率由固件检查;0x20b 按钮帧只允许取消位;Leaf 0x280 在白名单但无逐位附加约束

    Nissan 的 RX 表显式忽略 checksum/counter,因此不能声称该路径有独立的报文完整性检验。此外,MCU 的 RX 变体锁定、Altima 总线、0x20B 按钮掩码与 Leaf 0x280 载荷边界都需按具体 DBC 与测试核对,不能只凭品牌推断。

    Mazda:扭矩控制与速度迟滞

    Mazda 选择 mazda 安全模式与扭矩调校;此快照仅 CX-5 2022、CX-9 2021 不标 dashcamOnly,部分车型有最低转向车速门。CarState.update() 解析四轮速度、原车巡航、踏板、驾驶员/EPS 扭矩及相机 LKAS 状态。

    最低速度门使用 52/45 km/h 上/下行迟滞,并等待 LKAS_BLOCK 清除;低速告警与临时转向故障由状态逻辑产生。CarController.update() 按驾驶员扭矩限幅 LKAS,踩刹车后延迟发取消以规避与原车巡航主开关的竞态,停车恢复按间隔发 Resume。mazdacan.py 的构帧函数使用不同来源与计数器,需要保持相机/主总线消息时序。

    层 关键入口 已核对行为
    参数 CarInterface._get_params() 选 mazda 安全模式与扭矩调校;仅 CX-5 2022、CX-9 2021 不标 dashcamOnly
    输入 CarState.update() 最低速度门用 52/45 km/h 上/下行迟滞,并等待 LKAS_BLOCK 清除
    输出 CarController.update() 按驾驶员扭矩限幅 LKAS;踩刹车后延迟发取消;停车恢复按间隔发 Resume
    构帧 mazdacan.py create_steering_control()、create_alert_command()、create_button_cmd() 来源与计数器各异
    固件 RX/TX mazda_rx_hook() / mazda_tx_hook() / mazda_init() LKAS 扭矩受最大值、速率、驾驶员反向扭矩约束;未使能时按钮帧只能是取消

    Mazda 的 RX 表忽略 checksum/counter/质量位,同样不能声称有独立报文完整性检验。

    三者共用同一套通用边界:指纹 DBC、CarState、CarController 与 panda 的 RX/TX 钩子,但具体报文与状态各自实现。验证入口包括 Chrysler、CUSW、Nissan、Mazda 各自的 panda 安全测试以及通用车型接口测试;这些测试覆盖按钮、平台扭矩与部分总线变体,本次均未运行。

    对硬件与插件化的含义

    1. 车型契约必须区分 Pacifica/Ram/CUSW、Altima/Leaf、CX-5/CX-9 的报文地址、总线、控制类型、计数器与发布状态,不能只给出 brand 和 canfd 两个字段。
    2. 调试固件的 Chrysler CUSW 与 Ram HD 路径、Nissan Leaf 的安全带模拟取消、Mazda 的刹车后取消延迟,都应列为需要人机交互与实车故障分析的高风险策略,不开放为通用插件选项。
    3. 软件限幅、倒车/低速/停用状态和原车辅助系统冲突,必须与 panda 的 TX 白名单和 RX 状态独立核对;缺少 checksum/counter 的 RX 路径还需额外评估错误帧、重放与总线时序失效。
    4. 目标车型应先做只读采集、DBC/总线回放、错误帧注入、台架与封闭场地验证;本篇不是上车许可。

    常见问题

    三个品牌能共用一套转向控制插件吗?

    不能。三者的报文地址、总线、控制类型(Chrysler 为 EPS 电机扭矩、Nissan 为转角、Mazda 为扭矩)与计数器都不同,且取消策略各异,因此不能抽象成同一套报文插件。

    Nissan Leaf 是怎么取消巡航的?

    它不用普通取消按钮,而是用 create_cancel_msg() 把 CANCEL_SEATBELT 置位,让 ProPILOT 将其解释为安全带未系而退出,源码说明会伴随蜂鸣和警告,具有明确 HMI/安全语义。

    Mazda 的最低转向速度门是多少?

    源文档给出的最低速度门使用 52/45 km/h 上/下行迟滞,并等待 LKAS_BLOCK 清除后才允许。该阈值出自源文档,本文未在实车验证。

    为什么说这些品牌的 RX 路径没有独立完整性校验?

    Nissan 与 Mazda 的 RX 表都显式忽略 checksum/counter(Mazda 还忽略质量位),Chrysler 的 CUSW 也是另套定义。缺少这些字段意味着错误帧、重放与总线时序失效需要额外评估。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/CHRYSLER_NISSAN_MAZDA_DEEP_DIVE.md(三品牌参数、输入/输出、构帧与固件 RX/TX 对照)
    • 项目 02 —— openpilot-research/CHRYSLER_PANDA_SAFETY_MODE_DEEP_DIVE.md(源文档指向的 Chrysler panda 安全函数级专题)
    • 项目 02 —— openpilot-research/PANDA_BOARD_DEEP_DIVE.md(源文档指向的板级执行专题)

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

    更新记录

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

  • 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

  • 17 个车型目录与 DBC 编解码:车型指纹为什么不能只看一条报文

    结论:车型识别不是「看到某条报文就认出车」,而是一条固定链路:panda 收帧 → CAN 指纹 → ECU 固件匹配 → 生成 CarParams / safetyConfigs → DBC 编解码 → 车型 CarState/CarController。在固定源码快照里,17 个车型目录只证明「实现入口存在」,不构成对任何年款、平台的适用性声明;未识别或模糊匹配的候选会被降为被动(noOutput),而不是「先跑起来再说」。

    车型实现的共同骨架:五个模块各管一段

    车型能力的加载入口是 load_interfaces(),它由 BRANDS 加载车型 CarInterface,不是运行时扫描任意插件目录。也就是说,能不能被识别,先取决于是否在注册表里,而不是目录里有没有文件。

    每个品牌常规包含四个模块:values.py 负责车型枚举与物理配置;interface.py 负责车型参数与安全模式;carstate.py 把接收帧变成 CarState;carcontroller.py 把 CarControl 变成候选发送帧。部分品牌另有 radar_interface.py。四者职责不能互相替代——这是接口契约,不是风格问题。

    把整条链路摊开看:panda 接收 CAN,交给 card;card 做指纹与 ECU 固件匹配,得到 CarParams 与 safetyConfigs;同一路用 DBC 加 CANParser 解出车型 CarState.update;controlsd 产出 carControl,交回车型 CarController.update;再经 DBC 加 CANPacker 编码为 sendcan,由 pandad 经 USB 发出,最终落在 panda 固件的 safety_tx_hook。主机侧解码和编码只是前段,是否真正上总线由固件独立许可。

    17 个车型目录索引:是入口索引,不是适用性声明

    下表仅证明固定快照中的实现入口存在——每个目录的 interface.py、carstate.py、carcontroller.py、values.py 均已在目录中核对。它不代表已完成 17 个品牌、所有平台和年款的报文与安全审计。

    品牌目录 额外雷达接口 当前深度
    body 无 非汽车双轮平台链路已追踪
    byd 无 Atto 3 转角链路已追踪,仅调试安全模式
    chrysler 有 Pacifica/Ram/CUSW 主要链路已追踪
    ford 有 经典 CAN/CAN FD 主要链路已追踪
    gm 有 ASCM/相机 ACC 主要链路已追踪
    honda 有 Nidec/Bosch 主要链路已追踪
    hyundai 有 经典 CAN/CAN FD 主要链路已追踪
    mazda 无 扭矩/速度迟滞主要链路已追踪
    mg 无 扭矩链路已追踪,仅调试安全模式
    mock 无 非实车空发送回退已追踪
    nissan 无 转角/车型取消主要链路已追踪
    psa 无 三总线转角链路已追踪,仅调试安全模式
    rivian 有 Gen1/Gen2 主要链路已追踪
    subaru 无 Global/Preglobal 主要链路已追踪
    tesla 有 转角/原车转发主要链路已追踪
    toyota 有 本篇样本已逐层追踪
    volkswagen 有 MQB/MEB/MLB/PQ 主要链路已追踪

    这张表不能用同一品牌的安全文件代替平台差异验证。例如 Hyundai 有 CAN FD 分支,Volkswagen 有多种平台安全模式,Subaru 有 preglobal 分支。尤其 set_safety_hooks() 注册表把 MG、PSA、BYD、Subaru Preglobal、Volkswagen MLB/PQ 等模式置于 ALLOW_DEBUG 条件下——实现目录和测试存在,并不表示发布固件可启用执行。

    从 CAN 指纹到 safetyConfigs:启动阶段的门

    识别发生在启动阶段,顺序由若干入口固定。下面每一行的边界都需要单独记住:

    入口 行为与边界
    can_fingerprint() 在总线 0/1 以 CAN 地址及数据长度逐步排除候选;超过限定帧数后停止,不保证未知车型能识别。
    fingerprint() 优先查询 VIN、ECU 存在性及固件版本,再收集 CAN 指纹;单一固件候选可覆盖 CAN 结果;环境变量可强制指纹,仅用于受控开发。
    match_fw_to_car() 先尝试精确固件匹配,失败再尝试模糊或品牌自定义匹配;返回是否精确及候选集。
    get_car() 无候选时回退 MOCK;用候选接口生成 CarParams,并保留 VIN、固件、指纹来源及模糊匹配标记。
    Car.__init__() 发布 CarParams;无控制器、开关关闭或仅仪表记录时设为 passive,安全配置改为 noOutput。
    Car.controls_update() 仅在进程初始化、非被动、carControl 存活的路径调用车型 apply() 形成 sendcan;首次进入时设置 ControlsReady。

    MOCK、passive、dashcamOnly 是不可等同的三种状态,但都不能作为「未知车型可以开始执行」的理由。用户配置或环境变量强制指纹,也不构成车辆协议兼容性验证。

    DBC 编解码的函数级语义

    DBC 层决定「字节怎么变成数、数怎么变成字节」,但它既不负责安全,也不负责身份。以下入口的语义是这张表的核心:

    入口 关键实现与含义
    DBC.__init__() / get_checksum_state() 从生成 DBC 或文件加载消息、信号、枚举;按 DBC 系列选择校验算法。
    get_raw_value() 按大小端与 bit 位提取原始整数;符号、倍率和偏移由 MessageState.parse() 处理。
    MessageState.parse() 解码信号、计算校验和与计数器;失败时返回 False,不把该帧的部分值提交到 vl。
    MessageState.update_counter() 递增错误计数,达到阈值拒绝;有效后逐步恢复。
    CANParser.update() 仅接收指定 bus 和已注册地址,更新 vl、本次所有值和时间戳,返回成功解析的地址集合。
    CANParser.can_valid / bus_timeout 前者检查已注册报文的最近成功时间、计数器及累计缺失次数;后者单独观察整条总线空窗,两者语义不同。
    CANPacker.pack() 反向应用倍率/偏移,编码信号,自动补计数器与校验和;未知信号仅记录错误并跳过,不负责安全许可。

    两个容易踩的边界:CANParser 的 vl 只保存最近一次成功解析值,不能只看数值而忽略 can_valid、时间戳与 bus 健康;can_valid 在读取时也会更新缺失计数,所以调用频率会影响去抖过程,它不是纯无副作用的 getter。另外,CANPacker 是协议编码器,不是转向扭矩或加速度安全门。

    面向插件化的最低契约

    把上面几层抽象成可替换的插件时,有五条底线必须写进契约:

    1. 车型插件声明平台、ECU 固件范围、DBC 版本、总线拓扑、能力位、CarParams 与安全模式;未识别或模糊匹配默认被动。
    2. 解码插件输出值、时间戳、来源与校验/计数器/超时状态;不能把上次成功值伪装成当前有效观测。
    3. 控制插件只产生结构化 CarControl 或候选 CAN 帧;不能自行改固件白名单、安全模式、ControlsReady,或绕过 card/pandad。
    4. 硬件安全控制器独立于应用 CPU,实现 RX/TX/转发限制、心跳失效回退与板级故障处理;新硬件须用故障注入、SIL/HIL 与封闭场地验证。
    5. 每个目标车型逐一建立解析、编码、固件安全和回放基线;17 个品牌目录不是「一套通用线束或固件配置」规格。

    换句话说,能不能读到一条报文,与"这套配置能否在目标车上安全执行"是两个不同层级的问题。把前者当成后者,正是识别与安全最常被混淆的地方。

    常见问题

    为什么不能靠一条 CAN 报文就确定车型?

    因为 can_fingerprint() 是在总线 0/1 上以地址和数据长度逐步排除候选,超过限定帧数后会停止,源文档明确它不保证未知车型能识别;而 fingerprint() 还要结合 VIN、ECU 存在性与固件版本,单一固件候选才可能覆盖 CAN 结果。单条报文只是排除过程的一个输入。

    车型目录里有文件,是否代表这辆车就能用?

    不代表。17 个目录只证明实现入口存在,源文档特别说明它不代表已完成 17 个品牌、所有平台和年款的报文/安全审计;而且 MG、PSA、BYD 等模式被放在 ALLOW_DEBUG 条件下,实现目录和测试存在并不表示发布固件可启用执行。

    DBC 解析成功,是否意味着数据可信?

    不等于。CANParser 的 vl 只保存最近一次成功解析值,必须同时看 can_valid、时间戳与 bus 健康;can_valid 读取时会更新缺失计数,调用频率会影响去抖。数值正确但报文陈旧或总线超时,仍属于不可信输入。

    未识别或模糊匹配时,系统会怎样处理?

    get_car() 在无候选时回退 MOCK;当车型被设为 passive(无控制器、开关关闭或仅仪表记录)时,安全配置改为 noOutput。源文档强调 MOCK、passive、dashcamOnly 不可等同,且都不能作为未知车型开始执行的理由。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/CAR_PROTOCOL_IMPLEMENTATIONS.md(取用了 17 个车型目录索引、启动识别门、DBC 编解码函数语义与插件化最低契约)。
    • 项目 02 —— openpilot-research/CAN_PARSER_HEALTH_DEEP_DIVE.md(仅用于确认 can_valid/bus_timeout 的健康位语义边界,未取用其未公开内容)。

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

    更新记录

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

  • 从使能到车轮:执行链上的八道门,以及它们各自不保证什么

    结论:从「软件认为该动作」到「车轮真的动了」,中间至少有八层各自独立的门。任何一层通过,都不代表下一层会放行:使能状态为真不代表执行器活动,控制消息的 valid 只是元数据,主机发出去的帧还可能被板端白名单拒掉。把「发出去」当成「执行了」,是这类系统里最贵的误判。

    八层门各自检查什么

    本文基线为源码快照 c8fb906,静态串联「状态机 → 控制器 → 车型网关 → 主机发送 → 板端固件」这条链:

    层 真正检查了什么 不能据此推断什么
    使能状态 非被动模式且已初始化才计算使能与活动 使能为真不等于横纵执行器都活动,也不等于板端已放行
    横纵活动位 横向还要求无临/永久转向故障与静止转向许可;纵向要求纵向能力且无纵向覆盖事件 此处没有统一的全量检查;纵向活动位基于「使能」而非「活动」
    控制数值与发布 关闭活动位时重置控制器;曲率限幅;非有限数值置零 消息的 valid 是元数据,不是硬阻断
    车型网关 非被动且事件集不含初始化中,才进入控制更新;控制消息存活时才应用车型控制器 该分支只检查单条消息存活,不是全量检查;sendcan.valid=False 仍可能带着候选帧
    主机 CAN 回调 指纹、固件查询、初始化可另发帧;安全模式握手等两个完成信号才切车型安全模式 「所有发送都来自控制器」不成立;握手参数也不是本机代码的权限认证
    发送线程 事件时间距现在小于 1 秒且非测试发送才走硬件发送 该函数不读事件有效性、不读使能状态,一秒是陈旧帧门而非安全截止期限
    心跳线程 约 10 Hz 依据状态消息的存活/有效与使能计算是否接管 心跳与发送线程相互独立;未接管不是逐帧否决
    板端发送 完整 CAN 包经白名单、车型发送钩子与继电器故障门 白名单中有帧不等于动作获准;车型钩子通常还查许可以及扭矩/角度/加速度限值

    失效时各层怎么反应

    这是排查线上问题的实用部分——同一类故障在不同层的行为并不一致:

    情况 本快照中的处理 仍待验证
    状态消息失联或无效 心跳侧据此令后续心跳显示未接管;控制器本轮计算并未据此统一停止 各进程更新顺序、残留帧、车型钩子与原厂接管时延
    控制消息失联 网关不再发布新的控制帧 独立诊断/初始化回调、最后一帧生效时间、板端与车辆超时
    CAN 无效但仍发了控制帧 网关把发送标记为无效,但发送线程只查年龄与测试标志 车型控制器是否真生成帧、板端是否拒帧、故障消息调度顺序
    主机帧陈旧 年龄达到 1 秒的发送事件被丢弃,板端从未见到该包 低于 1 秒但超过车型安全周期的行为及总线/接口堵塞
    板端心跳中断 点火时约 5 秒、非点火约 2 秒阈值后切入静默并清除接管标志 中断与接口故障的最坏墙上延迟、继电器与车端实际恢复
    许可与接管标志不一致 板端 1 Hz 段连续累计 3 次后清许可;上位机侧也对不一致累计并进入不匹配路径 两条路径异步,不能把 3 次与 200 次解释成相同的失效时限

    三个容易被当成「安全证明」的东西

    1. 消息的 valid 字段。它由 CAN 有效位赋值,是元数据。它既不阻断车型控制器生成帧,也不阻断发送线程,更不能替代板端校验。
    2. 界面上的使能图标。使能状态为真只说明状态机走到了这一步,横向与纵向是否真的活动要分别看活动位,板端是否放行还要再往下一层。
    3. 安全模式握手的完成参数。握手表示板端可以切到对应车型的安全模式,它不是本机代码的权限认证,也不是「动作已被批准」的凭据。

    做故障注入该覆盖哪些场景

    如果自研硬件或做插件化改造,下面这些场景必须逐条注入,而且顺序上应先回放与软硬件在环,再进封闭场地:

    注入场景 要观察什么
    CAN 无效 是否会带着无效标记继续产生候选帧,板端是否拦截
    控制消息丢失 最后一帧的实际生效时刻与停帧确认
    状态消息丢失 心跳切换与控制器是否继续计算之间存在的时间差
    送板延迟与接口堵塞 1 秒陈旧门以下、车型安全周期以上的中间地带
    心跳中断 板端静默阈值的实际墙上时间
    校验错与原厂接管 原厂巡航/制动/转向接管与诊断回调并发时的行为

    插件化改造的硬要求

    把传感器、车型或控制算法做成可替换插件时,状态消息与控制消息主题不应成为无认证的任意写入口。核心要维持唯一控制权、消息来源与新鲜度校验,板端保留独立的发送限制与安全模式回读。

    特别要把这五个环节分开记录:候选帧生成、发送消息发布、接口发送、板端接受、车辆执行。把这五件事混成一个「已下发」的日志,等于放弃了事后定责的能力。

    常见问题

    为什么「使能」了动作却没执行?

    因为使能只是第一层。横向和纵向各有独立活动条件(故障、静止许可、纵向覆盖事件),网关、发送线程和板端白名单还会各自过滤。任何一层不通过,动作就不会发生。

    发送消息标记为无效,为什么还可能发出去?

    因为有效性是元数据,发送线程实际只看事件年龄和测试标志,不读有效性也不读使能。所以带无效标记的候选帧仍可能到达板端——真正的拦截点在车型钩子和板端。

    板端心跳中断后多久会停发?

    源代码里的名义阈值是点火时约 5 秒、非点火约 2 秒后切入静默并清除接管标志。但这是 1 Hz 检查的计数阈值,最坏墙上延迟还要看中断与接口故障情况。

    安全模式握手完成就代表动作被批准了吗?

    不是。握手只表示板端可以切换到对应车型的安全模式,它既不是权限认证,也不代表具体动作已获准。动作是否放行仍由板端白名单与车型钩子逐帧判定。

    延伸阅读

    参考资料

    • 项目 02 —— NAS 谭健强/openpilot-research/ACTUATION_GATE_CHAIN_DEEP_DIVE.md(执行链门控与失效边界,含源码行号索引)
    • 项目 02 —— openpilot-research/CAN_PARSER_HEALTH_DEEP_DIVE.md(解析健康语义)
    • 项目 02 —— openpilot-research/CAR_STATE_NORMALIZATION_DEEP_DIVE.md(车辆状态归一化)

    说明:本文为对固定源码快照的静态分析,描述的是多层控制请求与板端过滤,不是实车执行时延证明。本轮未运行测试、未刷写板卡、未发送 CAN 报文、未连接车辆。车型控制器、板级电气与硬件在环结果仍须逐车核验。

    更新记录

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

  • UDS 诊断工具为什么不能随手调:只读、会话与写 ECU 的分权设计

    结论:UDS 诊断客户端只做格式校验,不做权限分级。同一套客户端既提供读取 DTC 这类只读操作,也提供清 DTC、写内存、请求下载等下发原语——它只检查响应 SID 是否正确、DID 是否回显,不判断这个操作该不该被允许。所以「能不能调」必须由宿主层决定,而不是靠客户端。

    一条诊断指令要经过几层

    诊断脚本到 ECU 之间是一条明确的分层链路,每一层各管一段:

    层 职责 与整车其它链路的关系
    诊断脚本 组织请求、打印结果 直接调客户端,不走车况消息回调
    UDS 客户端 服务与子功能编码、正响应校验、负响应解析 面向单个目标 ECU
    请求构造 每次新建传输消息对象并等待应答 会话与超时在此层管理
    ISO-TP 单帧、多帧、连续帧序号与流控 把长报文拆合
    CAN 客户端 按 bus、RX 地址、子地址过滤 决定哪些响应会被看到
    硬件收发 直接调用收发接口 与车辆识别路径不同——识别走并行查询器

    这里有个关键区别:车辆识别走的是另一条并行查询路径,而诊断工具直接拿硬件收发接口。这意味着诊断工具绕过了识别阶段建立的上下文,也意味着它不受识别逻辑的任何约束。

    响应语义里的一个超时缺口

    正响应要求服务 ID 等于「请求 ID 加 0x40」,带子功能时还要校验回显。负响应以 0x7f 开头,解析错误码后抛出异常;而 0x78 表示「响应待定」,代码会继续等待下一轮。

    问题在于:没有跨多次「响应待定」的累计墙钟期限。每次收到待定状态都重设下一轮等待时间,理论上一个持续返回待定的 ECU 可以让调用一直循环下去。单次调用的超时参数,不保证整个请求过程的总时限。

    服务能力清单:哪些只读,哪些有副作用

    把服务按副作用分类,是设计权限边界的前提。客户端本身不做这个分类,但代码里的校验强度能反映风险等级:

    能力类别 代表操作 代码里的校验与副作用
    基本会话 诊断会话控制、会话保持 会发 CAN 请求,不是纯本地查询
    身份与数据读取 按数据标识符读取 除通用校验外还比对返回的 DID;解出的字节仍需按车型解释
    故障码读取 读取 DTC 信息 返回原始内容,注释标明尚未统一解析
    扩展读取 按地址读内存、请求上传 只限制地址/长度字段为 1 到 4 字节,未形成车端权限策略
    安全与通信控制 安全访问、通信控制 只检查种子/密钥参数形状;密钥派生不在此层;通信控制可影响 ECU 报文
    清除与复位 清除诊断信息、ECU 复位 对 ECU 状态有直接影响
    配置与执行 按标识符写数据、写内存、例程控制 按格式编码并检查部分回显,但没有写前写后事务、没有回滚
    数据传输 请求下载、传输数据、请求退出传输 提供刷写原语;字段与块序号校验不等于固件包签名或完整性验证

    四个现成工具入口的实际风险

    仓库里已经带了四个直接调用该客户端的脚本,它们最能说明「只读」这个标签有多不可靠:

    工具 静态调用链 需要注意的点
    读故障码 检查守护进程 → 连接硬件 → 设为诊断模式 → 开扩展会话 → 读 DTC 虽名为读取,仍然切换了 ECU 会话与硬件安全模式;进程名检查不是互斥锁
    清故障码 同样切换会话后执行清除 默认使用功能寻址;清除阶段的超时异常被吞掉,无论地址如何都可能继续打印完成提示——不能据此判断 DTC 已清除
    平台配置写入 读取控制器标识与编码 → 安全访问 → 写配置 → 回读 是实际会修改 ECU 配置的工具;有回读但没有通用事务回滚
    雷达点开启 检查固件版本 → 读配置 → 必要时写入新配置 只在写入之后才检查旧配置是否为预期默认值;异常路径可能先写入再报告中止

    这四个都在本项目的服务端源码里,本文没有运行其中任何一个。「可能继续」「可能先写入」是控制流结论,不代表已对具体车辆复现。

    插件化宿主该怎么划边界

    如果要把诊断能力接入行车软件,下面是建议而非现状:

    1. 按副作用分层授权。只读证据、会话维护、ECU 状态修改、数据传输四类分开授权。即使只读插件也通过单一代理发送,不直接拿到硬件收发对象或报文发布权限。
    2. 写入类限定场景。离车、点火与供电稳定、目标 ECU 与版本白名单、人工审批;请求、回执、旧值和回读结果都要审计留痕。超时或部分响应时状态记为「未知」,不得显示成功。
    3. 代理层实施独立约束。总墙钟期限、速率限制、总线/地址/服务白名单、并发互斥;会话结束与异常退出要有显式恢复流程。
    4. 保留硬件安全链与人工接管边界。工具代码里已有直接切换安全模式和修改 ECU 的入口,所以插件宿主绝不能把「加载成功」当成执行许可。

    常见问题

    只读诊断会改变车辆状态吗?

    会。读取类脚本本身就会切换 ECU 会话、把硬件置为诊断模式,这些都属于状态变更。名称里的「读」只描述返回值的用途,不描述副作用。

    清除故障码返回成功就算清干净了吗?

    不一定。清除阶段的超时异常被吞掉,无论功能寻址还是非功能寻址都可能继续打印完成提示。可靠做法是清除后再显式读取 DTC 回读确认。

    安全访问能替代权限控制吗?

    不能。客户端只检查种子与密钥参数的形状,密钥派生不在这一层。安全访问是 ECU 侧的准入机制,不等于宿主侧的授权机制。

    数据传输做完就等于固件可信吗?

    不等于。传输层的字段与块序号校验只能保证传输结构正确,不能验证固件包签名与完整性。

    延伸阅读

    参考资料

    • 项目 02 —— NAS 谭健强/openpilot-research/UDS_CLIENT_DIAGNOSTIC_TOOLS_DEEP_DIVE.md(UDS 客户端与车端诊断工具,含源码行号索引)
    • 项目 02 —— openpilot-research/ECU_DIAGNOSTIC_TRANSPORT_DEEP_DIVE.md(ECU 启动识别与 ISO-TP 传输)
    • 项目 02 —— openpilot-research/ACTUATION_GATE_CHAIN_DEEP_DIVE.md(执行链门控与失效边界)

    说明:本文为对固定源码快照的静态阅读,未连接车辆、未打开诊断会话、未清除 DTC、未改写任何 ECU。文中标注为建议的内容是重构意见,不是当前实现已有的机制。

    更新记录

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

  • 汽车 / 摩托 / 自行车:三类骑行场景对仪表的需求差异

    结论:汽车、摩托、自行车三类骑行场景看着都是「一块小屏」,实际是三条不同的产品线。汽车要的是真实 OBD 适配,摩托要的是耐候结构与手机后台链路,自行车要的是传感器生态——三者的门槛几乎不重叠,把一块屏做成三个尺寸解决不了任何一条。

    三条需求线的分野

    核对过 7 款在售产品的官网信息之后,需求的分化比价格的分化更值得注意:

    场景 用户真正在买什么 典型在售产品
    汽车 OBD 监测 可读车型、OBD 线束、安装与故障状态 ScanGauge 3(US$269.95)
    摩托小型导航 手机后台链路、支架、雨天与手套交互 Beeline Moto II(£179.99)
    摩托互联屏 无线投屏链路与耐候结构 Carpuride W702(US$269.99)
    摩托高亮大屏 户外可读性、供电、振动与生态附件 CHIGEE AIO-6 Max(US$499.00)
    自行车导航码表 导航、记录、传感器与 App 的闭环 Wahoo ELEMNT ACE(US$499.99)
    自行车生态码表 自定义数据页与传动系统集成 Hammerhead Karoo(US$525.00)

    口径先说清楚:以上均为厂商官网当时展示的商品信息与币种,不是中国到手价、销量或长期零售价;本轮没有购买竞品做拆机、亮度、续航或协议测试。

    汽车:真实 OBD 适配是门槛

    ScanGauge 3 官网强调的是 OBDII 车辆数据、彩色触控与故障码读取 / 清除,页面口径是「1996 年后的 OBDII 车辆兼容」。这是典型的汽车线逻辑:用户买的不是一块圆屏,而是「这辆车我读得到」。中国车型覆盖必须另测,不能从 OBDII 兼容这一句推出来。

    对自研产品来说,这条线最难的地方在车型适配而不是显示——可读字段、协议路径、刷新率都随车型与网关变化。

    摩托:耐候结构与手机后台链路才是主体

    Beeline Moto II 只做逐路口导航、骑行数据与数字速度,官网标称防水、抗震与 14 小时续航,不承诺读取摩托 ECU。这个小屏之所以成立,是因为它把手机后台链路、支架、雨天与手套交互都解决了。

    往上走的 Carpuride W702(IP67、1000 nit)与 CHIGEE AIO-6 Max(6 寸 1280×720、2300 nit)卖的是耐候与可读性。这些数字是厂商标称值,需要与实测区分表述。摩托线的门槛可以概括成一句话:外壳不等于防水整机,防水、振动与手套测试是上市门槛。

    自行车:传感器生态比屏幕重要

    Wahoo ELEMNT ACE 与 Hammerhead Karoo 的定位很明确——它们是导航、数据记录、传感器与 App 的闭环,Karoo 还强调自定义数据页与 SRAM AXS 集成。自行车线的竞争点不在屏幕,而在能连上多少传感器、能自定义多少数据页。因此标准 BLE 心率服务优先,自有协议只能作为后续扩展。

    大屏是第四类边界,不该混进来

    Carpuride W903 双摄版(US$249.99)是 9.3 寸车载多媒体屏,功能涉及手机互联、前后摄像头与语音控制。后视影像与导航属于另一个尺寸与安全边界——5 寸板只能先做非安全关键演示,不应纳入圆形 OBD 小表的首发范围。把这条线混进小表,会让显示、供电与法规边界一起失控。

    差异化验证清单

    产品线 首版主张 必须与竞品对照的实测项 暂不承诺
    汽车 OBD 小表 紧凑的可配置转速 / 车速 / G 值与清晰失联状态 目标车型 PID 覆盖、刷新率、夜间与日光可读性、安装与完整交付成本 通用适配所有车型、靠主控板直连 OBD、未核实的故障诊断能力
    摩托小表 手机路线转向 + 倾角;车型适配后才加入发动机数据 防水振动、手套交互、手机后台可靠性、目标车型线束与协议 1.85 寸完整 CarPlay / Android Auto、所有摩托通用 OBD
    自行车码表 轮速 / GPS 速度、转向提示、标准 BLE 心率优先 速度来源、心率兼容、续航、阳光可读、雨天操作 仅凭 IMU 算出可靠骑行速度、未经证明的戒指心率广播
    大屏探索 结构化导航与摄像头链路技术样机 阳光亮度、延迟、供电、环境可靠性与安装遮挡 可代替后视镜或安全关键影像显示

    三条线的工程资产可以复用:传感器数据模型、蓝牙协议、UI 组件、日志与 OTA 框架共用,但发布与 BSP 分开。采集与实时链路可以分别参照 SR-RaceBox 车载数据采集器 与 SR-Telemetry 实时遥测云。像 宁波国际赛车场 这类可高频复用的场地,适合用来同时验证三类场景的数据链路。

    FAQ

    同一块屏能同时服务汽车和摩托吗?

    硬件可以共用,产品线不能合并。汽车要解决 OBD 车型适配,摩托要解决耐候与手机链路,验证项几乎不重叠。

    为什么摩托仪表不承诺读 ECU?

    因为摩托的 ECU 协议与诊断接口远不如乘用车统一,车型线束与协议差异很大。在车型适配完成之前,只承诺手机发送的转向信息与 IMU 倾角更诚实。

    自行车码表为什么强调标准 BLE 心率?

    因为自有协议无法替代通用传感器生态。标准心率服务优先,戒指等自有协议只能作为后续扩展,且需协议资料到手后才接入。

    大屏导航能不能作为首发的第四类产品?

    不建议。后视影像与导航属于另一个尺寸与安全边界,未通过延迟、环境与法规评估前只能作为技术样机。

    参考资料

    • 项目 04 —— D:/AI/PROJECTS/vehicle-display-board-research/research/market-products.md(市场产品对照,查阅于 2026-10-03)
    • 项目 04 —— research/model-product-designs.md(三个产品族与目标价假设)
    • 厂商页面 —— scangauge.com、beeline.co、carpuride.com、chigee.com、wahoofitness.com、shop.sram.com

    更新记录

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

  • EdgeForge:嵌入式侧端框架的自编译方案

    结论:EdgeForge 的「自编译」不是把 Clang 塞进 MCU,而是让设备自己把一份受限描述语言编译成字节码——基础示例的 88 字节 EFIR 在端侧生成 36 字节 EdgeVM 代码,且与 Node 参考编译器的输出逐字节一致。这样页面、规则、阈值、迟滞与窗口统计的改动都不再需要重刷整机固件,只有外设驱动、协议栈、VM 漏洞修复才走固件更新。

    为什么不在 MCU 上放完整 C/C++ 编译器

    因为 ESP32-S3、STM32 和 RP2040 的内存、存储和安全边界不适合长期携带完整 Clang / GCC、系统头文件、链接器和动态加载器。

    源方案书写得很直接:即使勉强运行,编译延迟、RAM 峰值、ABI、恶意代码和崩溃恢复都会使产品难以量产。EdgeForge 采用的是受限自编译——把链路切成「描述语言 → 检查 → 平台无关 IR → 能力链接 → 字节码 / Wasm / 预审原生模块 → 签名安装」,MCU 端只负责最后一段:把 IR 快速编译成 EdgeVM 字节码并执行。复杂算法交给 Linux、Android 上的 Wasm,或可信工业场景下的预审原生模块。

    AI 在这个框架里的输出也是描述语言,而不是代码:AI 不能直接输出设备可运行的 C、JavaScript、Shell 或原始总线指令,只能产出 EdgeSpec 提案。即使不启用大模型,Studio 的表单和规则编辑器也能生成完全相同的 EdgeSpec。

    哪些改动从此不必重刷固件

    因为框架把「应用功能」与「平台能力」分开,前者可动态编译,后者仍需固件或系统更新。这条边界是承诺成立的前提:

    可动态编译(不重刷固件) 仍需固件或系统更新
    页面与组件、信号绑定、单位显示 新外设驱动
    阈值与迟滞、窗口统计、数据记录策略 新无线协议栈
    经过允许的信号解码、设备选择、本地提醒 底层 CAN / USB / 显示驱动
    有限状态机、经过审核的 Wasm 算法 VM 漏洞修复、加密库更新、启动链与电源管理逻辑

    所以正确的宣传口径是「多数应用功能无需重编译整机固件」,而不是「以后永远不更新底层软件」。这个边界与 OBD AI 插件化平台 里给出的分层判断是同一逻辑:能否插件化,取决于宿主是否已提供相应底层原语。

    从 EdgeSpec 到 36 字节字节码:链路上各产物有多大

    因为「链路短」是可以量化的,源方案给出了各阶段的真实字节数,全部来自当前原型而非估算:

    场景 EFB 功能包 EFIR 链接 IR EdgeVM 字节码 签名 EFP
    基础示例应用(ESP32-S3) 2,519 字节 88 字节 36 字节 3,421 字节
    Studio 四规则草稿 3,715 字节 212 字节 132 字节 4,742 字节
    8 样本平均窗口规则 2,294 字节 58 字节 20 字节 —

    关键在于端侧编译器的工作方式:它使用定长头与任务表,表达式只包含有限 opcode,事件动作使用索引表;ESP32、STM32 和 RP2040 的 C99 编译器在调用者提供的固定缓冲区内把 EFIR 转成字节码,不申请堆内存,也不携带 C/C++ 预处理器、汇编器或链接器。原生测试证明端侧输出与 Node 参考编译器逐字节一致。当前 EFIR 已覆盖基础表达式、信号有效性、本地事件、带固定状态槽的 GATE 持续条件、数值型低阈值 / 高阈值迟滞,以及固定样本数的 window_avg / window_min / window_max;通用状态变量与 UI 更新算子仍待扩展。

    包格式方面,当前 .efb v2 为「24 字节头 + 规范 JSON manifest + 二进制 EIDX v2 运行索引 + EdgeVM 字节码」。JSON manifest 供 Studio、诊断与审计使用,而 MCU 启动关键路径只需解析定长的 EIDX 记录,不需要携带通用 JSON DOM;加载器不使用堆内存,可验证包头、记录范围、字符串范围、任务连续性、UI 重叠与边界、栈深、跳转边界、信号 / 事件索引、opcode 与指令预算。

    EdgeVM 靠什么把插件关在笼子里

    因为「小体积」和「不拖垮宿主」是两件事,所以 EdgeVM 的设计目标是 C99 可移植、核心运行时保持在几十 KB 以内、使用固定或受限栈、不做运行时任意堆分配。每次任务有指令预算、墙钟预算和输出预算;非法 opcode、越界跳转、栈溢出和包截断会立即停止当前插件;关键采集线程不执行用户字节码。

    持续条件与迟滞是最能体现「有界」的两个机制。每条 pipeline 使用一个固定状态槽:条件连续成立 for_ms 后才通过;repeat_ms = 0 时本次条件保持期间只触发一次,大于 0 时按该间隔重复。低阈值在 < threshold 时进入、到 >= threshold + hysteresis 时恢复;高阈值在 > threshold 时进入、到 <= threshold - hysteresis 时恢复。使用迟滞的包要求 Host API 1.2.0,使用窗口的包要求 signal.window 能力与 Host API 1.3.0,旧包继续兼容。Studio 的四规则草稿里,四条 pipeline 均记录 for_ms = 3000、repeat_ms = 0、hysteresis = 0.1 bar。

    调度器负责合并相同信号订阅、按信号实际频率运行规则、超额时跳过低优先级任务而不阻塞采集,并记录执行耗时、峰值栈、失败原因与连续超限次数;多次越限后禁用插件并回退到已知可用版本。包卸载、A/B 切换或重新加载时必须清零状态槽,不能让旧版本的持续时间状态泄漏到新版本——这是「有界执行」之外容易被忽略的一条。

    五个平台的目标构建已经闭环,但还没有上实板

    因为「编译链接通过」与「实机运行通过」是两回事,源方案把两者分得很清楚。当前状态是:本地 Studio、通用 MCU Host 及五个平台的软件目标构建链已经闭环。

    平台 工具链 构建产物 余量 / 边界
    ESP32-S3 ESP-IDF 5.3.2 / Xtensa ESP GCC 13.2.0 应用 .bin 550,112 字节(镜像内容 550,004 字节) 1.5 MiB factory 分区剩 1,022,752 字节(约 65%)
    RP2040 Pico SDK 2.1.1 / Arm GNU Toolchain 14.2.1 .bin 85,776 字节,.uf2 172,032 字节 功能包预留区自 0x101b2000 起,按 256 字节块补齐后余 1,691,648 字节
    STM32F429ZI STM32CubeF4 HAL / Arm GNU Toolchain 14.2.1 BIN 37,024 字节,RAM 使用 49,776 字节 到 Bank 2 起点 0x08100000 余 1,011,552 字节
    Linux ARM64 AArch64 Linux,-O2 与最高警告等级 16 个对象链接为 4,798,248 字节完整 ELF 待 ARM64 目标机运行验证

    ESP32-S3 侧的余量值得单独看:DIRAM 使用 151,255 / 341,760 字节,专用 16 KiB IRAM 段只剩 1 字节;EdgeForge 组件占静态 RAM 60,808 字节、链接 Flash 31,733 字节。这些数字说明片内 RAM 是真正的约束——加入显示、触摸或 Wi-Fi 后必须重新链接,并在实板测量最小空闲堆、任务高水位、连接恢复、总线错误、刷新抖动与 CPU 占用。

    通用 MCU 侧的做法是不为每个芯片重写加载逻辑:STM32、RP2040、RP2350 与其他 C99 MCU 共用 hosts/mcu-common 无堆 Host,平台层只实现五类接口——双槽 Flash 与 96 字节事务状态记录、SHA-256 / Ed25519、UART / USB / BLE EdgeDeploy、单调时钟、具体传感器与显示驱动。启动链固定为「读取活动 A/B 槽中的 EFP → 校验目标 / Host API / 能力 / 摘要与签名 → 验证 EFB / EIDX → 解析 EFIR → 设备内编译到 RAM → 逐字节核对与签名 EFB 一致 → 执行」,任何不一致都拒绝加载。RP2040 的原生生命周期测试已安装三个签名 EFP,把 88 字节 EFIR 编译为 36 字节代码,输入 tpms.fl = 2.1 bar 触发低胎压事件并生成 4 个 UI 组件。

    必须说清楚的边界:ESP32-S3、STM32F4、RP2040 的实物烧录与启动验证尚未完成,Linux ARM64 目标机运行、EdgeDeploy 的真实 UART / BLE / USB 传输、掉电工装、具体 LCD 控制器与触摸、Android 实机兼容性也都在未完成清单里。自动测试当前共 46 项,本机 44 项通过,Linux POSIX 与 SocketCAN vcan0 两项集成测试在非 Linux 主机按条件跳过。显示链路的帧率口径可对照 车载 HMI 的 30 FPS:SPI 与 MIPI-DSI 显示链路差在哪,其中说明了 30 FPS 属验收目标而非硬件事实。

    签名只证明来源,不代表授权

    因为签名解决的是「包有没有被篡改」,而权限解决的是「这个包被允许做什么」,两者不能互相替代。当前 EFP v2 同时包含 EFB 与 EFIR,Ed25519 签名覆盖包头、ESEC 安全索引、审计 JSON 以及两个产物;ESEC 为 MCU 提供不依赖 JSON 的目标平台、应用版本、发布序列、密钥 ID、最低 Host API、能力清单和两个 SHA-256 摘要。但框架明确规定,实际权限是五项的交集:

    包声明 ∩ 发布者权限 ∩ 用户授权 ∩ 设备能力 ∩ 平台策略

    也就是说,签名证明来源和完整性,不代表插件自动获得其清单中请求的所有权限。包状态用 active / candidate / last-known-good 三态管理,固件用 A/B 槽;升级过程任意断电点都要能恢复到确定状态,回退受最低安全版本限制,不能恢复已撤销的漏洞版本。设备适配器与用户功能包也分开:用户可以把已注册的胎压信号放到任意页面,但不能因为改页面而获得原始射频、任意 BLE 写入或 CAN 发送权限。采集设备选型与信号质量的口径可参照 SR-RaceBox 车载数据采集器;屏幕形态与可读性的取舍见 圆屏还是方屏:车载仪表的显示形态该怎么选。

    常见问题

    EdgeForge 的「自编译」是要在 ESP32 上跑 C 编译器吗?

    不是。MCU 端只把平台无关 IR 编译成 EdgeVM 字节码,在调用者提供的固定缓冲区内完成,不申请堆内存,也不携带 C/C++ 预处理器、汇编器或链接器。完整 C/C++ 编译只在受控的构建服务或高性能平台上考虑。

    免重刷固件能免到什么程度?

    页面与组件、信号绑定、单位显示、阈值与迟滞、窗口统计、记录策略、经过允许的信号解码、有限状态机和经审核的 Wasm 算法都可以动态编译。新外设驱动、新无线协议栈、底层 CAN / USB / 显示驱动、VM 漏洞修复、加密库更新、启动链与电源管理逻辑仍需固件或系统更新。

    五个平台的构建都已经验证过实机了吗?

    还没有。当前完成的是软件目标构建链闭环,包括 ESP32-S3、RP2040、STM32F429ZI 与 Linux ARM64 的目标编译链接。ESP32-S3、STM32F4、RP2040 的实物烧录与启动验证,以及真实 UART / BLE / USB 传输、掉电恢复、具体 LCD 与触摸,均在未完成清单中。

    插件包签了名,是不是就拿到了清单里申请的全部权限?

    不是。实际权限取「包声明、发布者权限、用户授权、设备能力、平台策略」五项的交集。签名只证明来源和完整性,宿主必须独立拒绝所有未开放的操作。

    本文数据来自星核火花赛车科技内部 EdgeForge 自编译软件框架方案的 V1.4 稿(2026-09-21,状态为固定容量窗口统计、Studio 软件闭环、通用 MCU 自编译 Host 及五类常见硬件平台目标构建通过)。文中字节数、Flash / RAM 占用与余量均为该稿记录的原型构建结果,属软件目标构建证据,不证明开发板启动、掉电行为或实机传输;ESP32-S3、STM32F4、RP2040 的实物烧录与启动验证尚未完成。所述权限交集与实际授权行为以正式 SDK 与发布流程为准。