标签: 合规

  • rusEFI 开源 ECU 的 CAN 接口:GPL-3.0 三条附加条款与取证路径

    结论:把 rusEFI 当作 CAN 报文约定的来源时,第一件事不是找 ID,而是读许可证附加条款:它在 GPL-3.0 之上另加三条用途限制(仅限非道路的陆上/水上车辆、不符合汽车环保法规、未批准用于载人航空)。本次受检快照只保留 README.md 与 license.txt 两个文件,不含任何 DBC 或 CAN ID 表,因此报文定义必须回到固件源码、simulator 与 rusEFI console 三条路径上取证。

    三条附加条款先划出使用边界

    GPL-3.0 本身处理的是「改了要不要开源」,而 rusEFI 在许可证正文之前单独加了三条附加条款,处理的是「能不能用在这个场景」。对任何打算把它的报文约定或移植代码接入产品的团队,这三条比许可证传染性更早构成否决条件。

    条款 原文含义 对接入方的影响
    rusEFI#1 面向非道路的陆上与水上车辆 道路车辆项目须自行评估合规责任,不能把「开源」当作上路依据
    rusEFI#2 不符合汽车环保法规 排放相关标定与后处理功能不能引用其实现作为合规证据
    rusEFI#3 未批准用于载人航空应用 航空场景直接排除,无需再做技术评估
    GPL-3.0 主文 传染性,修改后分发须同许可证开源 闭源产品宜以运行期数据/独立进程隔离,不要静态链接固件代码

    这三条的表述方式值得注意:它们写在许可证正文之前的「附加条款」段,而不是放在 README 的免责声明里。也就是说,它们是许可证的组成部分,不是项目方的使用建议。

    快照里有什么、没有什么

    受检快照只含两个文件:README.md 与 license.txt。README 是一份目录索引,描述了仓库顶层各子目录的用途,但源码、硬件文件与 DBC 都没有随快照保留。这意味着本文不能给出任何报文 ID、波特率或信号缩放——任何这类数字都不在受检范围内。

    仓库子目录 官方说明中的定位 快照是否含该文件 能拿来做什么
    firmware 面向 STM32 芯片的开源发动机控制单元源码,含实现概述 否 CAN 报文定义的最终出处,需回上游仓库核对
    simulator 固件的 win32 或 posix 版本,无需任何硬件即可探索 否 不接 ECU 硬件时验证解析逻辑的主要手段
    java_console rusEFI 自有的原生标定软件 否 观察运行中变量与报文交互的参考实现
    hardware 较旧 PCB 的 KiCAD 文件 否 确认 CAN 收发器与引脚连接
    unit_tests 固件各部分的单元测试,公开覆盖率报告 否 判断哪些报文处理路径有回归保护
    java_tools 杂项开发工具集 否 格式转换与辅助脚本
    misc 软件开发流程说明 否 了解上游的变更与评审节奏
    firmware/CHANGELOG.md 发布变更日志 否 确认报文约定在哪一版发生变更

    把这张表理解成取证顺序而不是功能列表:firmware 是定义的唯一权威来源,simulator 是不接硬件时的验证手段,java_console 是观察交互行为的参考实现。三者都缺失时,任何关于报文的说法都只能是推断。

    不接硬件的三步取证路径

    CAN 接口类项目的常见错误是先写解析器再找真值。rusEFI 的目录结构给出了一条成本更低的顺序:先用 simulator 的固件版本在没有 ECU 的情况下跑通解析,再用 console 观察变量与报文的对应关系,最后回固件源码确认定义与校验逻辑。

    步骤 使用对象 能得到的结论 前置条件
    1. 跑通解析 simulator 解析器能否处理固件实际发出的帧序列 需要按 README 完成构建,win32 与 posix 二选一
    2. 对齐语义 java_console 报文字段与标定变量名的对应关系 需要与 simulator 或真实硬件建立连接
    3. 确认定义 firmware 报文 ID、字节布局、缩放与校验的真实出处 需要能阅读 C/C++ 源码并定位到对应版本
    4. 锁定版本 firmware/CHANGELOG.md 报文约定在哪次发布发生变更 需要把结论固定到具体发布版本号

    四份外部资料的分工

    README 同时指向四类外部资源,取证的可靠性差别很大:Doxygen 生成的源码文档在 rusefi.com/docs/html,反映代码现状,适合定位具体实现;wiki 承担使用说明,适合理解配置项含义;论坛承担问答,适合确认某条约定是否已有结论但不构成证据;变更日志随固件发布更新,是判断「某个行为从哪一版开始变化」的唯一顺序依据。做报文取证时应当以 Doxygen 与变更日志为准,把论坛结论当作待验证的线索。

    构建环节有三个必须确认的契约点

    README 对构建只给了三条硬性提示,但每一条都会直接影响报文取证的可复现性:仓库使用了 submodule,克隆后必须执行 git submodule update --init,否则依赖缺失会表现为编译失败而非功能缺失;推荐 GCC 版本写在 firmware/gcc_version_check.c 里,版本不符可能只在高优化级别下暴露问题;构建产物从 build server 取,说明本地构建与官方二进制可能存在差异,比对报文行为时须注明用的是哪一个。

    契约点 要求 跳过会怎样
    子模块 克隆后执行 git submodule update --init 依赖缺失,编译在早期阶段失败
    编译器版本 以 firmware/gcc_version_check.c 为准 可能构建成功但运行行为与官方二进制不一致
    产物来源 官方二进制来自 build server,需注明版本 不同来源的报文行为差异被误判为协议问题
    文档口径 Doxygen 文档与 wiki 分开维护 只看一处会漏掉近期的接口变更

    常见问题

    这份快照里能直接拿到 rusEFI 的 CAN 报文 ID 吗?

    不能。受检快照只有 README.md 与 license.txt 两个文件,README 仅描述顶层目录用途,源码、硬件文件与 DBC 均未随快照保留。报文 ID、波特率与信号缩放必须回到上游仓库的 firmware 目录,并配合 simulator 与 java_console 交叉确认。

    GPL-3.0 的三条附加条款是不是意味着完全不能上车?

    不等于禁止,但它把合规责任明确留给了使用方。三条条款分别声明:项目面向非道路的陆上与水上车辆、不符合汽车环保法规、未批准用于载人航空应用。道路车辆项目不能把「开源」本身当作上路或排放合规的依据,需要自行完成评估与认证。

    没有 ECU 硬件能不能先验证报文解析逻辑?

    可以。README 列出 simulator 目录提供固件的 win32 或 posix 版本,允许在没有任何硬件的情况下探索。配合 unit_tests 目录公开的覆盖率报告,可以判断哪些报文处理路径已有回归保护。但前提是先把构建跑通,包括子模块初始化与 GCC 版本对齐。

    克隆下来就能直接编译吗?

    不能只做一次克隆。README 明确说明仓库使用 submodule,必须在克隆后执行 git submodule update –init;同时推荐 GCC 版本写在 firmware/gcc_version_check.c 中,需按该文件核对。若只需二进制,官方构建产物从 build server 获取。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/rusefi-can/license.txt(GPL-3.0 主文与 rusEFI#1 至 rusEFI#3 三条附加条款)
    • 项目 01 —— public-can-dbc-datasets/additional/rusefi-can/README.md(顶层目录用途、子模块构建要求、GCC 版本检查与 build server 产物说明)

    免责声明

    本文仅记录受检快照中 README 与许可证文件所载内容,未包含任何未经材料支持的报文 ID、波特率或信号定义。rusEFI 为社区开源项目,文中引用的目录用途与许可证条款以材料原文为准;快照未包含源码与许可证以外的第三方组件信息。涉及道路车辆、排放合规与安全相关用途时,请以官方资料与实际测量为准,并自行完成合规评估。

    更新记录

    • 2026-10-15 初稿

  • OSCC 开源车辆控制:归一化命令、200 ms 保活与三层安全边界

    结论:OSCC 的价值在于它选了一条最难但最可辩护的路线:不欺骗 CAN 报文、不改原厂安全用例、不破解 ADAS 功能,而是把控制命令注入到转向盘扭矩传感器、油门位置传感器与制动位置传感器这三个物理接口上。代价是必须自己补上原厂本来承担的安全机制——文档里能明确数出来的有三层:enable/disable 门控、每 200 毫秒必须调用一次的看门狗、以及默认开启的操作者超控。任何把这三层当可选项的实现,都偏离了它的设计前提。

    接口定位:低层注入而不是报文欺骗

    这套系统由软件 API、固件与硬件设计三部分构成,通过车载通信网络与控制系统的模块化接口实现计算机控制。它的能力边界可以拆成三件事:向车辆发送控制命令、从车辆的 OBD-II CAN 网络读取控制报文、转发当前车辆控制状态报告(如转向角与轮速)。控制命令并不直接操纵执行器,而是作用于部件 ECU 的传感器输入端——转向盘扭矩传感器、油门位置传感器、制动位置传感器。汽油版起亚 Soul 不具备线控制动能力,因此额外加装了一套辅助执行器来实现制动。

    这种低层接口的意义在于完整保留原厂安全用例:原车的失效检测、冗余与机械链路都还在,注入的只是一个「驾驶员意图」。这与在总线上伪造一帧转向命令有本质区别——后者绕过了原厂所有校验,前者仍然在原厂设计的信号链里。目前官方支持的车型是 2014 年及以后的起亚 Soul(汽油版与纯电版),但 API 与固件在设计时就为新增车型留出结构:按既有车型头文件的格式新增一个头,并补充一个构建选项即可。

    版本矩阵:四件事必须同时对齐

    需要跟踪的版本有四件:传感器接口板(油门与转向,版本印在板正面)、车辆控制模块(纯电版制动)、执行器控制板(汽油版制动),以及共用同一版本号的 API 与固件。文档对版本更新给的评价很重——每次有新版本都必须更新,以确保没有使用带有已知安全问题的版本。这不是礼貌性提醒,因为板卡版本与固件版本的配对关系会直接影响启动自检能否通过。

    板卡 板卡版本 要求的固件版本 说明
    传感器接口板 大于等于 1.0.0 大于等于 1.0.0 承担油门与转向
    车辆控制模块 0.1.0 大于等于 1.1.0 纯电版制动
    执行器控制板 大于等于 1.2.0 大于等于 1.0.0 汽油版制动
    执行器控制板 小于 1.2.0 大于等于 1.0.1 需额外关闭启动自检

    最后一行是唯一需要额外操作的组合:较新的执行器控制板利用了新的引脚做启动阶段的附加安全检查,旧板没有这些引脚,因此构建时要显式关闭制动启动测试,否则自检会失败。这是硬件演进与安全检查相互耦合的直接例子。

    命令契约:归一化取值范围与 200 毫秒保活

    API 只暴露三个发布函数,参数全部归一化,与具体车辆的电压标定解耦:制动与油门位置取 0.0 到 1.0,转向扭矩取负 1.0 到正 1.0,其中负为逆时针、正为顺时针。API 负责把归一化值换算成需要注入的欺骗电压,并内置安全检查以确保不会发出超出车辆预期范围的电压。这个换算层是安全边界的一部分——应用层永远碰不到原始电压。

    保活要求是这套接口里最容易被忽略的硬约束:三个发布函数每一个都必须至少每 200 毫秒被调用一次,否则模块会判定与上位机失去通信并自行禁用。它把「上位机死机」和「主动松开控制」统一收敛成同一种失效结果,这正是看门狗该有的行为。反过来说,一个只在上位机有输出时才发命令的控制循环,一旦进入长时间稳定状态就会自己把系统关掉。

    接口 方向与取值 约束
    oscc_open / oscc_close 通道编号 建立与终止到固件的 CAN 通道连接
    oscc_enable / oscc_disable 无参数 禁用时仍可接收报告,但无法发送命令
    publish_brake_position 0.0 至 1.0 每 200 毫秒至少调用一次
    publish_throttle_position 0.0 至 1.0 每 200 毫秒至少调用一次
    publish_steering_torque 负 1.0 至正 1.0 负为逆时针、正为顺时针,同样需 200 毫秒保活
    subscribe_to_brake_reports 等 回调函数 制动、转向、油门、故障四类报告各自独立订阅
    subscribe_to_obd_messages 回调函数 转发非本系统的 CAN 帧,可按 ID 自行解析

    还有一条实现层面的注意事项:API 会捕获 CAN 帧到达时产生的信号并为其安装处理函数,把有效报文转发给应用。因此应用中对中断敏感的代码段,应在该段执行期间临时阻塞该信号、结束后再解除,否则回调可能在临界区中被触发。

    三层安全边界

    把这些约束放在一起看,OSCC 的安全设计是分层的,任何一层单独失效都不会立刻导致失控。

    层级 机制 失效时的行为
    应用层 oscc_enable 与 oscc_disable 门控 未使能时命令发不出去,但报告照常接收
    通信层 三个发布函数各需 200 毫秒内再次调用 超时即判定通信丢失,模块自行禁用
    信号层 API 内置电压范围安全检查 换算后的欺骗电压被限制在车辆预期范围内
    操作层 操作者超控检测,默认全部开启 驾驶员施加扭矩即退出,转向灵敏度可调
    硬件层 启动自检(新板用新引脚做附加检查) 自检不通过则不上线
    流程层 强制跟随版本更新 避免停留在带已知安全问题的版本

    转向超控为什么默认开启且不建议关闭

    所有模块的操作者超控检测默认开启,但文档对转向给出了额外警告:系统激活时试图抓住转向盘可能导致严重伤害,因此推荐的转向超控方式是制动踏板或急停按钮,而不是去扳方向盘。若确实需要关闭,可在构建时传入关闭标志,此时灵敏度由对应车型头文件中的扭矩差阈值控制。

    这个阈值是一个典型的双向权衡:调低会让模块对超控更敏感,但在转向齿条机械极限或快速反复变向等高扭矩区域产生误判;调高则误判更少,但人工接管转向变得更困难。文档没有给出默认值之外的推荐值,因为最优解取决于具体的转向系统与应用场景,这正是需要在实车上标定的参数。

    构建、刷写与测试命令

    固件用 CMake 构建而非 Arduino IDE,目的是绕开 IDE 的限制并支持命令行刷写。前置条件是 Arduino Core 1.8.5 及以上与 CMake 2.8 及以上。构建时通过车型标志选择目标:汽油版 Soul、纯电版 Soul 与 Niro 各有对应标志。生成的构建目标可以整体构建,也可以按模块单独构建,四个模块分别是制动、CAN 网关、转向与油门。

    环节 命令或标志 说明
    选择车型 构建时传入车型标志 汽油版 Soul、纯电版 Soul、Niro 三选一
    关闭转向超控 追加关闭超控标志 默认开启,不建议关闭
    关闭制动启动测试 追加关闭启动测试标志 仅旧版执行器控制板需要
    发行构建 指定构建类型为 Release 关闭调试符号并开启优化
    单模块构建 制动 / 网关 / 转向 / 油门四个目标 可只构建所需模块
    刷写 单模块上传目标与全量上传目标 按字母顺序连接模块可获得稳定端口分配
    单元测试 打开测试标志后运行单元测试目标 依赖 Cucumber 2.0.0 与 Boost
    属性测试 打开测试标志后运行属性测试目标 依赖 Rust、Cargo 与 libclang

    测试分两类且覆盖角度不同。单元测试以行为描述的方式验证报文处理,例如油门模块的「接收命令」用例会给出传感器读数与两路 DAC 输出值的对照表,四个样例点分别是 0 对应 0、256 对应 1024、512 对应 2048、1024 对应 4096。属性测试则验证控制器库的不变式,一组运行包含积分项、微分项、比例项、反转输入、相同输入产生相同控制、以及清零共七项。两类测试都有分模块的独立运行入口,也有一次性运行全部测试的便捷入口。

    许可证分工:硬件与软件不是同一种授权

    这一点常被忽略但对集成决策影响很大:硬件源材料——原理图、电路板布局、接线图、数据手册、物理安装文档、三维模型等——采用 CC BY-SA 4.0 授权;固件与软件源则采用 MIT 授权,除非第三方依赖另有声明。也就是说,照着板卡设计做改动后的再分发义务,与修改代码后的义务并不相同。此外文档整体声明按现状分发、不提供任何担保。

    组成部分 许可证 主要义务
    硬件源材料(原理图、布局、接线图、安装文档、三维模型) CC BY-SA 4.0 署名并以相同方式共享衍生作品
    固件与软件源(源码、头文件、文档、构建脚本) MIT 保留版权与许可声明
    第三方依赖 以各自声明为准 需逐项核对

    项目还提供商用的整车控制接口方案作为交钥匙选项,覆盖纯电版 Soul 与 Niro 混动,包含集成控制模块、车规级线束与急停开关。对研究者而言,开源版本与商业版本的区别在于支持范围与工程化程度,接口契约本身是一致的。

    常见问题

    OSCC 是在 CAN 总线上伪造报文吗?

    不是。它把控制命令作用于部件 ECU 的传感器输入端——转向盘扭矩传感器、油门位置传感器与制动位置传感器,属于低层接口,因此能在不改动原厂安全用例、不欺骗 CAN 报文、不破解 ADAS 功能的前提下实现全量程控制。纯电版以外的汽油车因不具备线控制动能力,需额外加装辅助执行器。

    为什么控制命令必须每 200 毫秒发一次?

    这是模块侧判定上位机是否存活的看门狗:三个发布函数中任意一个超过 200 毫秒未被调用,模块就认定与上位机失去通信并自行禁用。它把上位机死机与停止控制统一成同一结果。因此控制循环不能只在数值变化时才发命令,长时间稳态下也必须持续调用。

    可以关闭转向超控吗?

    可以在构建时传入相应标志关闭,但不建议。文档明确警告系统激活时抓握转向盘可能造成严重伤害,推荐的转向超控方式是制动踏板或急停按钮。若保留开启,灵敏度由车型头文件中的扭矩差阈值控制:调低会增加高扭矩区域的误判,调高会让人工接管更困难。

    板卡版本与固件版本不匹配会怎样?

    会直接影响启动自检。较新的执行器控制板用新引脚做附加的启动安全检查,旧板没有这些引脚,必须用关闭制动启动测试的构建标志处理。其余组合按官方配对表选择固件版本即可,文档同时强调每次发布新版本都应及时更新,以避开已知安全问题。

    硬件设计和软件代码的授权一样吗?

    不一样。原理图、电路板布局、接线图、数据手册、物理安装文档与三维模型等硬件源材料采用 CC BY-SA 4.0,衍生作品需署名并以相同方式共享;固件与软件源采用 MIT,只需保留版权与许可声明;第三方依赖以各自声明为准。集成前应按组成部分分别核对义务。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/polysync-oscc/README.md(接口定位、版本矩阵、API 契约与 200 ms 保活、构建与测试命令、许可证分工)
    • 项目 01 —— public-can-dbc-datasets/additional/polysync-oscc/LICENSE.md(硬件源材料的 CC BY-SA 4.0 条款与固件软件的 MIT 条款全文,含免责与责任限制)

    免责声明

    本文所述内容来自公开开源项目的说明文件,不代表车辆制造商的官方数据,文中数字均摘自上述文件,未经独立实车验证。该项目按现状分发、不提供任何担保:硬件源材料(原理图、电路板布局、接线图、数据手册、物理安装文档与三维模型)采用 CC BY-SA 4.0,衍生作品须署名并以相同方式共享;固件与软件源采用 MIT,须保留版权与许可声明;第三方依赖以各自声明为准,集成前应逐项核对。对车辆进行任何控制改装都涉及人身与财产安全,本文仅作技术资料梳理,不得作为上路或载人测试的依据;实际操作须在封闭场地、由具备资质的人员在急停开关与完善的失效处理机制下进行,并以官方资料与实际测量为准。

    更新记录

    • 2026-10-15 初稿

  • foxBMS 2 的 CAN 报文:41 条组件级定义与双许可证边界

    结论:foxBMS 2 的 CAN 报文定义是 41 条组件级报文,属于开源电池管理开发平台自身的协议,不是任何量产整车的网络地图。使用它之前有两件事必须先定:一是它的许可证是分裂的——软件走 BSD-3-Clause,硬件与文档走 CC-BY-4.0,两者的义务不同;二是它描述的是一个可自由配置的 BMS 平台,报文语义随配置而变,不能按固定车型档案的方式套用。

    41 条报文的定位:组件级而非整车级

    foxBMS 2 的自我定位是「第一个模块化的开源 BMS 开发平台」,提供硬件与软件的完整开源环境,用于控制各种规模的现代复杂电化学储能系统。它的 CAN 定义文件放在工具链目录下,共 41 条报文,覆盖的是 BMS 这个部件对外发布的量,而不是整车广播网络。

    这两者的差别在接入时非常关键:整车 DBC 里,同一个 ID 的含义由车型年款固定;而 BMS 平台的报文含义取决于使用者怎么配置电池包、从控数量和告警策略。因此把 foxBMS 2 的报文表当作某台车的解码依据,方向就是错的。

    判据维度 组件级 BMS 报文(本例) 整车级广播 DBC
    描述对象 BMS 部件自身对外接口 整车多个 ECU 的广播网络
    报文规模 41 条 常见数十至上百条
    语义决定方 使用者的平台配置 车型年款与制造商
    跨车复用 可在不同储能系统间复用 通常绑定具体车型
    验证途径 按自身配置与实测标定 抓包比对原车行为

    双许可证:软件与硬件文档分开授权

    这个项目采用了罕见的双轨授权,而且分界线非常清楚:软件部分用 BSD 3-Clause,硬件与文档部分用 CC BY 4.0。归档时两份许可证全文都被保留了下来,这本身就是一种可核验的合规姿态。

    授权对象 许可证 SPDX 标识 文本文件 核心义务
    软件 BSD 3-Clause License BSD-3-Clause BSD-3-Clause.txt 保留版权声明,不以作者名义背书
    硬件与文档 Creative Commons Attribution 4.0 International CC-BY-4.0 CC-BY-4.0.txt 署名;允许商用与再分发

    除了法定条款,项目还提出了一个软性请求:在硬件、软件、文档或宣传材料中使用时,建议采用「本产品使用了 foxBMS 的部分」「本产品包含 foxBMS 的部分」或「本产品衍生自 foxBMS」这类表述。这不构成额外义务,但对追溯来源有帮助。项目由 Fraunhofer IISB 维护,并已通过开源硬件协会认证,OSHWA UID 为 DE000128。

    为什么硬件走 CC-BY 而不是 BSD

    电路原理图与布局属于硬件设计成果,用面向代码的许可证去授权会留下大量解释空白。CC-BY-4.0 是面向任意作品的署名型许可,允许商用与再分发,只保留署名一项主要义务,与硬件设计的传播方式更匹配。这也是为什么在引用其 BMS 硬件设计时必须带上署名,而不能只保留软件侧那份 BSD 声明。

    平台能覆盖哪些储能体系

    判断一份 BMS 报文定义是否适用于自己的项目,先看它面向的电化学体系。foxBMS 2 明确列出了已成功应用的储能类型,覆盖范围远超常见的三元/铁锂电池。

    储能类型 备注
    锂离子电池 常规锂电体系
    固态电池 Solid State Batteries
    锂硫电池 Lithium-Sulfur
    钠离子电池 Sodium-Ion
    锂离子电容 LIC
    双电层电容 EDLC,超级电容
    液流电池 RFB,例如全钒液流
    燃料电池 FC

    项目还说明这些体系可以混合组合使用,并应用于多个领域。这一点直接影响报文的使用方式:不同化学体系的单体电压范围、温度窗口与均衡策略都不同,报文里的量纲与告警阈值必然要跟着配置走。

    仓库结构与文档入口

    归档保留了根目录的 README 与许可证说明。从 README 可以还原出仓库的标准目录划分,这对定位 CAN 定义文件与文档很有帮助。

    目录 内容
    仓库根目录 许可证信息、安装说明、变更日志链接、主构建脚本
    cli 与仓库交互的命令行工具
    conf 全部高层配置
    docs 文档源文件
    hardware 硬件原理图与布局信息
    src BMS 嵌入式软件的全部源文件
    tests 嵌入式源码、工具链与脚本的测试
    tools 构建 foxBMS 二进制所需的工具与附加工具

    每个子目录都带一份说明其内容的 README。变更日志位于 docs/general/changelog.rst,文档构建产物由项目方持续发布,最新发布版与历史构建均可查阅。CAN 定义文件本身位于工具链目录下的 dbc 子目录。

    常见问题

    foxBMS 2 的 41 条报文能用来解码某台量产电动车吗?

    不能。这 41 条是 BMS 平台自身部件的对外接口定义,属于组件级数据,不是生产车辆的 DBC。它的报文语义随使用者的平台配置而变,只能用于自建或参照该平台的储能系统。

    软件部分和硬件部分可以只遵守其中一个许可证吗?

    不可以。授权是分裂的:软件走 BSD-3-Clause,硬件与文档走 CC-BY-4.0,两者义务不同。引用硬件设计或文档时必须履行 CC-BY-4.0 的署名义务,不能只用软件侧那份 BSD 声明覆盖全部内容。

    CC-BY-4.0 允许商业使用吗?

    允许。CC BY 4.0 国际许可允许商用与再分发,主要义务是署名。项目另外建议在宣传材料中使用「本产品使用了 foxBMS 的部分」这类表述,这是请求而非强制条款。

    OSHWA UID DE000128 意味着什么?

    它表示 foxBMS 2 已通过开源硬件协会的开源硬件认证,认证编号 DE000128。这是对项目硬件开放程度的第三方确认,不等于对任何具体电池系统的安全认证或性能背书。

    这套 BMS 支持哪些电池化学体系?

    项目列出锂离子、固态、锂硫、钠离子、锂离子电容、双电层电容(超级电容)、液流电池(如全钒液流)以及燃料电池,并说明这些体系可以混合组合使用,覆盖多个应用领域。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/foxbms2/README.md(项目定位、支持的储能体系、仓库目录结构、开源硬件认证与许可证概述)
    • 项目 01 —— public-can-dbc-datasets/additional/foxbms2/LICENSE.md(软件 BSD-3-Clause 与硬件文档 CC-BY-4.0 的分工、SPDX 标识与署名请求)
    • 项目 01 —— public-can-dbc-datasets/additional/foxbms2/CC-BY-4.0.txt(硬件与文档所适用的许可全文)
    • 项目 01 —— public-can-dbc-datasets/additional/foxbms2/BSD-3-Clause.txt(软件所适用的许可全文)

    免责声明

    本文所述报文规模、许可证分工与平台能力均来自公开仓库的说明文件,未经在具体电池系统上的独立验证。报文语义取决于使用者的平台配置,不同电池化学体系、从控数量与告警策略会导致差异。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • PSA 架构逆向:94 份报文 YAML 与 DBMUXEv 格式

    结论:PSA-RE 不是 DBC 集合,而是一套自研的 DBMUXEv 描述格式:归档快照含 94 份总线报文 YAML、110 份车型映射文件,覆盖 AEE2001(VAN)、AEE2004(第一代 FullCAN)与 AEE2010 三代架构。它不用 DBC 的原因很具体——需要同时描述多个世代,而每个世代又包含多条网络,DBC 这种单网络格式装不下。

    为什么放弃 DBC:单网络格式的边界

    DBMUXE 格式的规范文档写明了动机:现有格式(包括 Vector 的 DBCan)无法满足需求,因为要描述多代设计,而每代设计又包含多条网络。格式名本身也是线索——DBMUX 取自 PSA 内部用于描述多路复用网络的软件名,后缀 ev 在 PSA 语境里常表示某个事物的演进版本。

    维度 DBC(DBCan) DBMUXEv
    组织单位 单一网络上的报文与信号 architecture、variant、network、bus、node 逐层嵌套
    多世代设计 需拆成多份彼此无关的文件 按目录分层;跨架构相同的报文只写一份,其余用符号链接
    诊断数据 不在格式范围内 同级 diag 目录下有 dids、dtcs、resets、routines 四类
    协议覆盖 主要面向 CAN 面向多种车载网络,规范仍列为待完成项的有 LIN、KWP2k、K-lines、PSA2k
    语言 单一语言 按 ISO 两字母码扩展,示例仅设置 en 与 fr
    转出工业格式 即用 规范 TODO 中:需编写生成器解析 YAML 产出 DBC、ODX

    代价也很清楚:这套格式目前没有官方工具链,想把它喂给 SavvyCAN 或 CANoe,要先自己写生成器。仓库 README 的求助清单里,「编写生成器把 YAML 转成 DBC、ODX 等工业标准格式」被列为最需要的贡献之一。

    目录结构即数据模型

    规范给出的目录树基本等同于它的概念模型:architecture 是一组设计模板的名字(如 AEE2004),variant 是其衍生版本;network 是共享节点的一组总线,bus 是节点之间的物理链路(可以是 CAN,也可以是 LIN);node 是挂在一或多条总线上的控制器;protocol 是在总线上发送数据的协议;message 是总线上的一帧;signal 是帧里的一个数据点。

    路径 存放内容 作用
    architectures.yml 架构定义 整个数据集的顶层索引
    cars/{id}.yml 车型到架构的映射 把具体车型挂到一或多个架构上
    nodes/{architecture}.{variant}.yml 节点定义 描述该架构变体下有哪些 ECU
    buses/{architecture}.{variant}/{network}.{bus}/{message}.yml 单帧报文 一帧一个文件,是快照里 94 份报文文件的所在
    diag/{architecture}.{variant}/{node}/ dids、dtcs、resets、routines 诊断侧数据,与总线报文分列

    命名上用点号 . 作分隔符,需要时嵌套。跨架构复用有一条明确规则:若某条报文在多个架构(及其版本)中完全相同,只在第一个实现它的架构的最相关版本里描述一次,其余位置创建符号链接。这条规则让 94 份报文文件能覆盖远多于 94 个「车型 × 总线」组合。

    车型命名规则:第二位数字是车身

    仓库单独写了一份车型命名说明。规则是:一个字母加一个数字构成基础车型,后面可能跟第二个数字表示车身形式。

    第二位数字 车身形式
    0 3 门
    1 5 门
    2 SW / Break(旅行版)
    3 4 门
    4 休闲车 / MPV / 大型多功能
    5 Coupe
    6 Coupe / Cabriolet(敞篷)
    7 Cabriolet(敞篷)
    8 Monospace / Minispace
    9 (小型)多功能 / 商用

    另外说明提到改款通常(但不总是)把第一个数字加 1。这条规则的价值在于把 110 份车型映射文件变成可推导的体系:看到车型代号就能预判它挂在哪个架构上,而不必逐份打开文件。

    证据边界:覆盖率与安全内容的取舍

    归档说明给出的边界必须先看。上游 README 声称理论上覆盖 1993 年雪铁龙 XM(首款使用 VAN 的车)到 2020 年代初的标致、雪铁龙与 DS,并说明 2022/2023 年起的新架构尚不清楚;但快照里的报文文件实际位于 AEE2001/CONF 与 AEE2004.full 的 HS.IS 或 LS.CONF 之下,也就是说 README 的车型覆盖表述并不能证明每个车型或信号都已收录。

    上游 107 份总线 YAML 中含有防盗或安全访问相关术语的 13 份被刻意剔除,未纳入本次归档。仓库声明 Apache-2.0 并保留 LICENCE,但 README 同时警告数据可能有误,并承认部分材料来自无法追溯的泄露,因此原始数据权利并未完全确立。留存的报文仍需要与被动抓包做独立的适配性与缩放核对。

    常见问题

    PSA-RE 里为什么没有 DBC 文件?

    因为它用的是自研的 DBMUXEv 格式。规范文档写明动机:现有格式(包括 Vector 的 DBCan)无法描述多代设计,而每代设计又包含多条网络。该项目用 YAML 按 architectures、cars、nodes、buses、diag 分层组织,把 YAML 转成 DBC 需要另外编写生成器,这一点被列在仓库的求助清单里。

    94 份报文 YAML 覆盖了哪些架构?

    项目目标是 PSA 的 AEE2001(VAN 架构)、AEE2004(第一代 FullCAN 架构)与 AEE2010(2010 年至约 2020 年的车型)三代。但本快照的报文文件实际位于 AEE2001/CONF 与 AEE2004.full 的 HS.IS 或 LS.CONF 之下,README 中关于覆盖 1993 年至 2020 年代初的宽泛表述并不能证明每个车型或信号都已收录。

    车型文件的命名规则是什么?

    规则是一个字母加一个数字构成基础车型,后面可跟第二个数字表示车身形式:0 为 3 门、1 为 5 门、2 为旅行版、3 为 4 门、4 为休闲车或 MPV、5 为 Coupe、6 为 Coupe 或敞篷、7 为敞篷、8 为 Monospace、9 为小型多功能车。此外改款通常(但不总是)把第一个数字加 1。

    为什么收录时刻意剔除了 13 份报文文件?

    因为上游 107 份总线 YAML 中含有防盗或安全访问相关术语的 13 份被判定为不宜纳入,未进入本次归档。同时仓库 README 警告数据可能有误,并承认部分材料来自无法追溯的泄露,原始数据权利未完全确立,因此留存报文仍需与被动抓包做独立的适配性与缩放核对。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/prototux-psa-re/README.md(项目目标、AEE2001/AEE2004/AEE2010 架构范围、覆盖声明与免责警告)
    • 项目 01 —— public-can-dbc-datasets/additional/prototux-psa-re/dbmuxev/doc/README.md(DBMUXEv v0.1 规范:术语定义、目录结构、符号链接复用规则与待完成项)
    • 项目 01 —— public-can-dbc-datasets/additional/prototux-psa-re/cars/README.md(PSA 车型代号命名规则与车身形式编码)
    • 项目 01 —— public-can-dbc-datasets/additional/prototux-psa-re/SOURCE.md(94 份报文文件、110 份车型映射、13 份剔除说明与权利状态)

    免责声明

    本文内容整理自公开开源仓库,属社区逆向工程成果,不代表标致、雪铁龙或 DS 的官方数据。原仓库明确警告信息可能不正确,且部分材料来源无法追溯,权利未完全确立。留存报文未经独立抓包验证,不同车型、年款与 ECU 版本可能存在差异,应以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • 商用车 DBC 知识包的许可证链:16 条报文与四类来源分级

    结论:拿到一份商用车 DBC 知识包,第一件事不是数报文,而是核对许可证链。这份包的 6 份 heavy-duty OEM DBC 合计只有 16 条报文,只覆盖 6 个制造商的部分 J1939 PGN;真正决定它能否进入产品的,是 21 个已收录来源里 2 个强制署名、3 组 GPL-3.0 与 2 份 CC-BY-SA-4.0 的传染性条款,以及 9 份被明确拦在 curated 目录之外的文件。

    为什么许可证链比报文数更先决定可用性

    DBC 是数据而非代码,但它的来源决定了分发边界。这份知识包的 LICENSES.md 开篇就写明了定位:它不授予任何访问许可,只保留公开来源本身的许可证。也就是说,把别人的 DBC 拷进自己的包,不会让许可证消失,只会把义务继承下来。

    因此合理的处理顺序是:先按许可证把来源分档,再决定哪些能进 curated/、哪些只能留在 raw/、哪些在闭源分发时必须整体剔除。报文数量在这条链里是最后才看的东西——16 条报文和 2000 条报文,合规成本可能完全一样。

    许可证档位 义务 代表来源 分发处理
    MIT / Apache-2.0 保留版权声明 commaai/opendbc(57 静态 + 39 生成器输出)、canboat/canboat、projectgus/kona-ev-dbc 可直接随包分发
    CC-BY-4.0 强制署名 berumiya/CAN_DBC_6thGenMazda(Mazda 6th-gen HSCAN/MSCAN) 分发须带署名串
    CC-BY-SA-4.0 署名 + 相同方式共享 fsfarmscaper/jaguar-xf-x250-can(MS/HS 总线各一份) 闭源分发须剔除
    GPL-3.0-only 传染性,须保留许可证文本 dalathegreat/leaf_can_bus_messages、qwec01/ARCFOX_dbc、gbt27930_2023.dbc 运行期动态加载或整体剔除
    无许可证 / 权利存疑 不可判定 见 DO-NOT-INTEGRATE 列表 停留在 raw/ 作研究记录

    六份商用车 OEM DBC 的实际覆盖面

    目录里的六份 DBC 来自上游 data/dbc/heavy_duty/,合计 16 条报文,按制造商分布为 Mercedes-Benz Actros 3 条、Caterpillar 2 条、Cummins 2 条、Detroit 3 条、Scania 3 条、Volvo 3 条。每份文件只覆盖选定的制造商私有 J1939 PGN,既不是完整的整车网络定义,也不构成车型年款的适配承诺。

    制造商 报文数 性质 证据边界
    Mercedes-Benz Actros 3 制造商私有 J1939 包内自产,非 SAE 标准
    Caterpillar 2 制造商私有 J1939 包内自产,非 SAE 标准
    Cummins 2 制造商私有 J1939 包内自产,非 SAE 标准
    Detroit 3 制造商私有 J1939 包内自产,非 SAE 标准
    Scania 3 制造商私有 J1939 包内自产,非 SAE 标准
    Volvo 3 制造商私有 J1939 包内自产,非 SAE 标准

    这六份文件在上游被描述为「由项目自身的 OEM decoder 定义生成」。这句话本身就是一条重要的证据边界:DBC 文件不确立底层测量方式,也不确立每个字段的校验依据。用它们做离线解码参考可以,拿来做 ASIL 相关功能的信号依据则超出其能力。

    同包内的其他来源量级

    同一个 LICENSES.md 还登记了若干量级明显更大的来源:ISOBUS 数据字典(isobus.net 的 AEF/ISO 11783-11 公开导出)生成出 102 条报文、1341 条信号的 isobus_dd11783.dbc;BogGyver 的 opendbc 分支贡献 28 份已提交 DBC,其中静态 7 份(hyundai_kia_generic、stellantis_dasm、tesla_can_pre1916、vw_golf_mk4、vw_mqb_2010、chrysler_pacifica_2017_hybrid、tesla_radar),生成 21 份(Lexus 6、Honda 6、Toyota 9)。把它们和 16 条报文的商用车部分放在一张表里看,才能判断这份包的重心到底在哪里。

    DO-NOT-INTEGRATE:九份被拦下的文件

    包里专门维护了一张「禁止整合」表,列出 9 个停留在 raw/ 的条目。它们的共同点是许可证状态未解决,因此不能拷进 curated/ 或 data/dbc/。

    风险类型 判定依据 处理动作
    未声明许可证 仓库未给出任何许可证 仅作研究记录,不进 curated
    内容未下载但已知无许可证 上游未标注,权利不可判定 不纳入任何分发包
    格式不兼容 是 Markdown 表格而非 DBC 不作为 DBC 来源整合
    标准组织版权风险 标注 MIT 但内容派生自 SAE J1939 标准,存在版权主张 整条排除,不进 curated

    这张表的价值在于把「不知道」显式化。多数 DBC 收集项目的问题不是抄错了文件,而是把许可证不明的产物直接混进了可分发目录,事后无法追溯。

    SAE 版权风险为什么单独列一档

    表中有一条的判定很典型:它被标注为 MIT,但内容派生自 SAE J1939 官方标准,上游仓库存在公开的版权主张记录。许可证标签与实际权利不一致时,以权利为准。这也是为什么来源表里会出现「标注 MIT 但仍排除」这种看似矛盾的处理。

    copyleft 的隔离方式:运行时数据载荷

    包内对 GPL-3.0 与 CC-BY-SA-4.0 采用了一套明确的隔离策略,核心是让这些 DBC 不构成软件代码的组成部分:

    1. 数据分离:这些文件不静态嵌入、不编译进二进制,而是在运行时由 cantools 以数据载荷方式动态读取。
    2. 闭源分发清理:在严格的商业或闭源分发中,可直接从 data/dbc/ 删除或排除这些文件;核心诊断、J1939、OBD-II 等 MIT/Apache-2.0 授权的 DBC 仍能独立工作。
    3. 合规登记:所有许可证与署名映射统一登记在仓库根目录的 REUSE.toml,符合 REUSE 3.0 / SPDX 规范。

    这套「气隙」设计的意义在于把传染性风险收敛到一个可删除的目录,而不是扩散到整个构建产物。是否需要真正删除,取决于分发形态;能不能删得干净,则取决于前期有没有做隔离。

    常见问题

    这份商用车 DBC 包能直接用于整车网络解析吗?

    不能。六份文件合计 16 条报文,每份只覆盖选定制造商的私有 J1939 PGN,上游明确说明它不是完整整车网络定义,也不保证车型年款适配。它适合作为特定 PGN 的解码参考与比对基准。

    为什么有些来源标注 MIT 却被排除在整合之外?

    因为许可证标签与真实权利可能不一致。DO-NOT-INTEGRATE 表中就有一项虽标注 MIT,但内容派生自 SAE J1939 官方标准,上游存在公开的版权主张记录。遇到这种情况以实际权利为准,整条排除。

    闭源产品里能不能保留 GPL-3.0 的 DBC 文件?

    可以保留但需要隔离:这些文件不得静态嵌入或编译进二进制,只能由 cantools 在运行时作为数据载荷读取,并且分发包要附带 GPL-3.0 许可证文本。若要彻底消除传染性影响,则须从 data/dbc/ 删除这些文件,其余 MIT/Apache-2.0 授权的 DBC 不受影响。

    CC-BY-4.0 与 CC-BY-SA-4.0 的差别在实际处理上是什么?

    CC-BY-4.0 只要求署名,分发时带上指定的署名串即可;CC-BY-SA-4.0 在署名之外还要求相同方式共享,因此在闭源分发中必须整体剔除。本包里前者对应 Mazda 6th-gen 的两份 DBC,后者对应 Jaguar XF X250 的 MS/HS 两份 DBC。

    DBC 里的信号定义能不能当作安全相关功能的依据?

    不能。包内免责声明写明,DBC 内容由社区逆向工程产生,不是车辆制造商的官方数据,在安全相关(ASIL)用途中只能作为参考。上游对这些文件的说明也指出,底层测量方式与各字段校验并未由 DBC 文件确立。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/ucanlab-heavy-duty-oem/SOURCE.md(六份 DBC 的 16 条报文、制造商分布与权利边界)
    • 项目 01 —— public-can-dbc-datasets/additional/ucanlab-heavy-duty-oem/LICENSES.md(21 个来源的许可证映射、copyleft 隔离策略与 DO-NOT-INTEGRATE 表)
    • 项目 01 —— public-can-dbc-datasets/additional/ucanlab-heavy-duty-oem/README.md(上游产品定位、协议覆盖与 data/PROVENANCE.md 的登记机制)

    免责声明

    本文所述的 DBC 内容均为社区逆向工程产物,不代表车辆制造商的官方数据。文中数字均来自上述公开仓库的说明文件,未经独立实车验证;不同车型年款、市场与 ECU 版本可能存在差异。涉及安全相关用途时请仅作参考,并以实际测量与官方资料为准。

    更新记录

    • 2026-10-14 初稿

  • 干净室插件架构实践:把遥测工作流拆成可替换插件

    结论:要做干净室、插件化的遥测工具重构,落点是把「宿主」压缩到最小、把能力全部下沉到可独立加载与卸载的插件。本文的取舍可以概括为三条:宿主只负责插件生命周期、工作区、命令路由与能力发现;所有数据模型与契约位于中立的 Abstractions 层,不依赖任何 UI 框架;单个插件加载失败只进入失败清单,不阻止无依赖插件运行。清单的 schemaVersion 当前为 1,插件 ID 使用反向域名风格,通道名使用稳定点分命名(如 speed.kmh)。

    七条设计原则

    架构文档给出的七条原则,决定了后续所有工程取舍:

    原则 含义
    宿主最小化 宿主只负责插件生命周期、工作区、命令路由和能力发现
    数据中立 会话、采样、圈、分段、视频引用与弯角定义位于 Abstractions,不依赖 UI 或第三方库
    多实现并存 同一契约可注册多个实现,例如 CSV、GeoJSON 与报告都实现同一个导出契约
    依赖显式 依赖写在 plugin.json,加载器做拓扑排序并检测循环
    故障隔离 单个插件加载失败进入失败清单,不阻止无依赖插件运行
    UI 是适配器 CLI、桌面与 Web 都是宿主外壳,不能持有核心业务算法
    安全默认值 更新源只接受 HTTPS;插件加载目录必须由宿主明确指定

    第四条与第五条合起来,是插件架构能否长期维护的关键:依赖必须显式声明,才能在加载阶段做拓扑排序、定位失败插件并隔离它。

    插件生命周期与加载隔离

    加载流程是固定的一段顺序,可按下面步骤核对:

    1. 发现 plugin.json;
    2. 验证 schemaVersion;
    3. 定位 entryAssembly;
    4. 依赖拓扑排序;
    5. 创建独立 AssemblyLoadContext;
    6. 实例化插件接口;
    7. 注册服务与命令;
    8. 启动(StartAsync);
    9. 正常运行;
    10. 停止(StopAsync);
    11. 卸载 AssemblyLoadContext。

    这里有一条容易踩坑的规则:共享的 Abstractions 程序集始终由默认加载上下文提供,插件加载上下文不会加载自己的契约副本。这么做是为了避免 CLR 类型身份不一致——如果每个插件各自加载一份契约,同一接口在不同上下文里会被当成不同类型,注册与调用都会失败。

    能力契约与功能拆分映射

    宿主通过一组能力契约暴露功能,插件只实现自己关心的契约:

    契约 用途
    数据导入契约 文件到遥测会话
    会话处理契约 导入后的派生通道与标准化处理
    数据导出契约 会话到外部文件
    弯角库服务 赛道弯角库读取
    圈速服务 门线、圈速、分段
    视频引用服务 视频关联和路径解析
    图表序列服务 图表序列和抽样
    地图路线服务 地图轨迹和简化
    洞察分析器 弯角分析
    配置存储 配置持久化
    更新源服务 安全更新元数据
    赛道库服务 赛道、门线和位置查询
    文件传输服务 会话文件分组和复制
    插件命令 可脚本化的用户命令

    原软件里集中的「服务」被拆成独立边界。功能域的映射关系如下:

    原功能域 对应插件
    文件读写与数据服务 数据导入插件
    弯角数据与赛道路径 弯角插件
    起终点数据库 赛道数据库插件
    开放赛道几何 开放赛道数据插件
    数学通道 数学插件
    圈与分段 圈速插件
    视频服务 视频插件(播放后端作为后续适配器)
    图表、地图、导出、报告、洞察 各自独立的图表 / 地图 / 导出 / 报告 / 洞察插件
    设置、自动更新、文件传输 设置 / 更新 / 文件传输插件

    桌面层延续同样原则:外壳只提供停靠区、菜单、状态栏与工作区选择;图表插件贡献图表面板、不直接引用导入插件;地图插件只读标准位置通道;视频插件可接入可替换的播放后端;PDF、打印、HTML 报告分别实现同一个报告渲染契约;在线地图、遥测上传与 AI 建议必须是显式安装、可禁用的网络插件。

    干净室边界:能用什么、不能带进什么

    干净室开发的核心是划一条清晰的输入边界,只允许来自可观察行为与公开资料的证据:

    可以使用 不进入新工程
    用户可观察的输入、输出和交互行为 原程序的反编译源码或轻微改名的等价复制
    公开文档、公开文件格式和独立测试数据 保护区域中恢复出的专有方法体
    为互操作性而确认的字段、单位和二进制布局 厂商图标、图片、文案、赛道数据库等受保护资源
    自行设计的数据模型、算法、插件接口和用户界面 厂商 DLL、许可组件、私有密钥或未授权在线服务
    — 绕过许可、签名验证或访问控制的逻辑

    工程上还有几条落地措施:源码、插件与测试目录不引用反编译目录;测试用的采集文件为人工合成数据;文件格式解析器只实现互操作,测试时从用户本机安装读取、不把数据库复制进仓库;可分发的赛道与弯角数据包只从明确许可的开放数据源生成,并在每个快照中保留来源、查询、署名和文件哈希;更新插件拒绝 HTTP 源且不自动下载或安装包。

    新增一个插件的五步

    1. 新建一个引用 Abstractions 的目标框架类库;
    2. 实现插件接口;
    3. 通过注册器注册服务或命令;
    4. 在项目根目录放置 plugin.json,声明入口程序集、入口类型、依赖和能力;
    5. 把项目加入解决方案并构建,宿主会从输出目录自动定位程序集。

    兼容性规则同时要求:插件不得引用反编译目录或厂商二进制;破坏性契约变化要发布新的 Abstractions 主版本。把这两条与前面的「依赖显式」放在一起看,就能理解为什么插件清单必须声明入口程序集、入口类型、依赖与能力——加载器据此排序、隔离并在失败时给出清单,而不是静默跳过。

    常见问题

    单个插件加载失败会不会拖垮整个工具?

    不会。架构原则要求故障隔离:单个插件加载失败进入失败清单,不阻止无依赖的插件运行。前提是依赖显式写在清单里,加载器才能做拓扑排序并隔离失败项。

    为什么每个插件各自加载一份契约会出问题?

    会造成 CLR 类型身份不一致:同一接口在不同加载上下文里被当成不同类型,注册与调用都会失败。因此共享的契约程序集始终由默认加载上下文提供,插件加载上下文不加载自己的契约副本。

    哪些数据可以来自原软件?

    只有用户可观察的输入、输出与交互行为,以及公开文档、公开文件格式和独立测试数据。反编译源码、保护区域恢复的专有方法体、厂商图标文案、厂商二进制与私有密钥都不进入新工程。

    网络、在线地图和 AI 能力怎么处理?

    它们必须是显式安装、可禁用的独立网络插件。更新源只接受 HTTPS,且不自动下载或安装包;这类能力应保持默认关闭、可审计。

    延伸阅读

    参考资料

    • 项目 03 —— nixiang-tracklab/PLUGIN_ARCHITECTURE.md(取用七条设计原则、插件生命周期、能力契约、功能拆分映射与兼容性规则)。
    • 项目 03 —— nixiang-tracklab/CLEAN_ROOM.md(取用干净室的可使用 / 不进入边界与工程措施)。
    • 项目 03 —— nixiang-tracklab/README.md(取用当前插件状态、构建运行方式与新增插件五步)。

    说明:本文为对整洁室重构文档与固定源码快照的静态结构整理,未运行完整测试套件、未连接任何厂商数据库或车辆、未验证各插件在实际数据上的输出。文中所有原则、契约与步骤均出自源文档,标注为未验证的结论保留其原有边界。

    更新记录

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

  • 开发日志工具的三个权限等级:转储、音频导出与状态刺激

    结论:四个开发脚本的权限差异很大,不应统称「开发插件」。dump.py 与 filter_log_message.py 主要读取消息/日志,extract_audio.py 会导出含乘员信息的音频,而 cycle_alerts.py 会主动发布核心状态主题(selfdriveState、deviceState、pandaStates)。至少两个反直觉的事实:--json 对 Cap’n Proto 原始字节调用 json.loads 通常无法解析,真正转 JSON 的是 --dump-json;cycle_alerts.py 先声明九个告警却随即重赋值为只剩两项,实际只循环 cameraMalfunction 与 cameraFrameRate。远端 --addr 不能当作权限、脱敏或租户隔离策略。

    四个脚本,三种权限等级

    把数据流和副作用放在一起看,权限差异一目了然:前三个工具的输出主要是「读出来的内容」,第四个则会争用发布入口。

    工具 数据流 实际输出或副作用
    dump.py raw_live_logreader() 订阅指定地址主题,默认为 SERVICE_LIST 全部服务;每条原始字节先按 log.Event 解码 可打印 Event、十六进制或字段;--addr 非环回时底层切换 ZMQ 并重置消息上下文
    filter_log_message.py 有路段参数时用排序后的 LogReader 查 logMessage、errorLogMessage、operatingSystemLog;无路段时实时订阅前者与 OS 日志 打印 JSON 结构化日志或 OS 日志;实时模式未订阅 errorLogMessage
    extract_audio.py ReadMode.AUTO_INTERACTIVE 选路段来源,LogReader.filter("rawAudioData") 得载荷,拼成 int16 样本 经 write_wav_file() 导出单声道 WAV,或经 play_audio() 本机播放
    cycle_alerts.py 构造 Honda 参数与随机化 SubMaster 状态,用 Events.create_alerts()、AlertManager 生成告警 每 200 个控制周期发布 selfdriveState、deviceState、pandaStates,循环不停

    dump.py 与 filter_log_message.py 的失效点

    dump.py 即使指定 --no-print 仍逐条解析 Event;--pipe 用 str(msg) 写标准输出,得到的是 Python 字节表示文本而不是原始二进制流;--json 对原始字节调用 json.loads 通常失败。--values 只检查路径第一段存在,后续 getattr 缺失可直接抛错。顶层 codecs.register_error("strict", ...) 还会影响进程内使用该错误处理器的解码行为,插件封装时不应默许这种全局修改。

    filter_log_message.py 的坑集中在时间与字段两处。等级过滤只作用于结构化日志,不影响 OS 日志;离线非 --absolute 的时间从第一条读取到的消息起算,而不是第一条匹配日志,且多个路段共享该时间原点、段间不重排序;实时模式则用原始 logMonoTime 纳秒换算秒数,两者语义不同。print_logmessage() 只捕获 JSON 解析异常,若 JSON 缺 levelnum、filename 或 msg 会抛键错误。

    工具 易踩边界 原因
    dump.py --json 通常解析失败 对 Cap’n Proto 原始字节调用 json.loads;转换应使用 --dump-json
    dump.py 解码行为被全局改写 顶层注册了 "strict" 错误处理器
    filter_log_message.py 相对时间原点不是首条匹配日志 离线时间从第一条读取到的消息起算,多路段共享
    filter_log_message.py 实时模式拿不到错误日志 实时订阅未包含 errorLogMessage

    两个工具都会打印原始事件或日志内容,可能包含标识符、位置、进程路径及异常栈,因此远端 --addr 不能作为生产环境的权限、脱敏或租户隔离策略。

    extract_audio.py:WAV 连续性与隐私

    extract_audio.py 把所有 rawAudioData.data 按 NumPy 原生端序的 int16 视为连续样本,再原样写成 WAV,未显式转换字节序。它只取第一条的 sampleRate,不检查之后的采样率变化、通道数、消息时间戳或缺包间隔,因此导出文件不承诺与原车时间轴严格连续。它先把所有消息和样本拼成内存数组,长路段可能占用较大内存。

    隐私侧还有一个关键前提:rawAudioData 缺省 should_log=False,loggerd 只有在启动时读取 RecordAudio 为真才订阅该主题。所以没有音频消息不等于麦克风硬件故障;而一旦录制,导出的语音可能含高度敏感的乘员信息。使用上,输出文件已存在时会交互确认,--play 会使用本机音频设备;只传 --play 或 -o 而不传路段名,主程序仍执行 args.route_or_segment_name.strip(),会报 AttributeError。

    cycle_alerts.py:发布核心状态的边界

    cycle_alerts.py 先声明九个可听告警,但随后把 alerts 重赋值为仅 cameraMalfunction 与 cameraFrameRate 两项,本快照实际只循环这两项。它随机改写 SubMaster 内的设备状态、模型速度、进程列表和主题 alive/valid/freq_ok,没有对这些订阅数据调用 sm.update(),由此产生的告警文本是随机夹具结果,不是车辆健康检查。

    发送消息由 new_message() 构造,默认 Event valid=False,脚本没有覆写。即便如此,在实际设备上争用 selfdriveState、deviceState、pandaStates 的发布入口仍会干扰 UI 和状态消费者,不能作为行车调试插件。脚本没有发送 sendcan 或直接控制 ECU,但这不等于发布核心状态没有风险。

    硬件与插件化约束

    建议把原始消息/日志检索、音频导出、状态刺激拆成三个权限等级,而不是统称「开发插件」:观测插件仅获指定主题、时间范围及字段脱敏后的只读视图;音频导出需要单独授权、留审计并限定本地文件生命周期;状态刺激仅允许仿真进程或隔离测试总线,不能发布到生产 deviceState/pandaStates。源文档明确这些是重构要求,不是当前工具已有的隔离保证。

    常见问题

    dump.py 的 –json 为什么常常解析失败?

    因为它对 Cap’n Proto 原始字节直接调用 json.loads(msg),通常无法作为 JSON 解析。源文档指出真正把 Event 转为 JSON 文本的是 --dump-json;--pipe 得到的也只是 Python 字节表示文本,不是原始二进制流。

    导出的音频能当作和原车同步的时间轴证据吗?

    不能。extract_audio.py 只取第一条的 sampleRate,不检查之后的采样率变化、通道数、消息时间戳或缺包间隔,而且是按 NumPy 原生端序把 int16 视为连续样本原样写 WAV,因此导出文件不承诺与原车时间轴严格连续。

    cycle_alerts.py 会循环几个告警?

    本快照实际只循环两个:cameraMalfunction 与 cameraFrameRate。脚本先声明九个可听告警,随后把 alerts 重赋值为这两项,且告警文本来自随机夹具结果,不是车辆健康检查。

    这些开发工具能当作「只读」吗?

    不能一概而论。前三个工具主要读取数据,但远端 --addr 不是权限或脱敏策略;cycle_alerts.py 则主动发布三个核心状态主题,会干扰 UI 与状态消费者。音频导出还涉及高度敏感的乘员信息,应单独授权与审计。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/DEV_LOG_AUDIO_ALERT_TOOL_BOUNDARIES_DEEP_DIVE.md(取用了四个开发脚本的数据流与副作用、转储与日志过滤失效点、音频导出连续性与隐私门、告警刺激的真实范围)。

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

    更新记录

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

  • 崩溃采集的三道门:100 MB 过滤、0o100640 模式与 12 小时暂缓

    结论:崩溃采集不是「一律上报」,而是一条跨进程链路,且至少被三道门约束:报告门(构建来源为官方 origin、本地已注册、非 PC 三者同时成立)、扫描过滤门(只接受精确 mode 0o100640 的 .crash,超过 100 MB 直接丢弃)、上传门(计费网络暂缓最近 12 小时的 crash/、HTTP 401/403/412 不再重试)。要特别记住两点:100 MB 阈值不是隐私过滤,完整 crash 副本可能含路径、进程环境与内存相关信息;而「已进入本地处理」也不等于云端已保存。

    入口:报告门只在进程启动时采样一次

    在固定源码快照里,tombstoned 被配置成非 PC 的常驻进程。它的 main() 启动时计算一次 should_report,要求构建元数据的官方 origin、is_registered_device() 与「非 PC」三项都成立。这里的「已注册」只依据本地 DongleId 非空且不为 UnregisteredDevice,不是每次崩溃都向后端重新鉴权。

    两个时序后果必须分开看。第一,should_report 不在循环中刷新,运行期间身份或来源发生变化不会立即改变本实例的处理门。第二,启动时 clear_apport_folder() 会遍历 /var/crash/* 尝试删除,之后才建立初始快照——这意味着重启前尚未处理的 crash 文件可能直接丢失,源文档给出的动机是避免重复报告。

    扫描与本地处理:三道门各自不管什么

    采集循环每五秒用 get_tombstones() 扫描至多 1000 个目录项,用「路径 + ctime」的集合差异找出新条目。下面这张表把各道门的条件和边界摊开——每一道的「不管什么」比它「管什么」更容易被误读。

    门 / 环节 条件 边界与后果
    报告门 官方 origin 且 is_registered_device() 且非 PC,仅在启动时计算一次 运行中改变返回值不会改变本实例的删除决策
    文件模式门 .crash 必须是精确 mode 0o100640 名字以 tombstone 开头的条目不受该 mode 门约束,但报告门打开时只记录「unknown crash type」,不进入解析与复制
    大小门 超过 100 MB 记错后返回 该分支不复制也不删除原文件;大小阈值不是隐私过滤
    目录常量 TOMBSTONE_DIR="/data/tombstones/" 已定义 该常量未被 get_tombstones() 使用,实际扫描的是 /var/crash/;不能据常量名推断 Android tombstone 也被处理

    解析与落盘阶段的关键行为见下表。摘要与完整副本走的是两条路径,清洗只作用于摘要,不改写完整文件。

    阶段 源码行为
    摘要 提取 ExecutablePath、Signal,经 apport-retrace -s 获取栈,优先找 openpilot/selfdrive 栈帧;去掉形似指针的参数并剥除圆括号内容后写 cloudlog.error。外部命令设 30 秒超时
    完整报告 用日期、提交哈希与清洗后路径组成文件名,复制完整原始 crash 文件到 Paths.log_root()/crash/,随后尝试删除 /var/crash 原件
    未启用报告 对新检测到的条目尝试删除;不会保留「本地可查但不上传」的独立模式
    处理异常 异常写 cloudlog,下一轮仍把当前集合设为基线;未变化的同一文件通常不会因本次失败自动重试

    需要强调的是:摘要去掉指针、剥掉圆括号内容,只是让日志更短,并不等于对 dump 做了脱敏审计。完整 crash 可能包含路径、进程环境和内存相关信息,不能因为 cloudlog 摘要删了指针就把副本视为匿名数据。

    上游处理与下游上传是两段独立判断

    Uploader 把 crash/ 放进 immediate_folders,next_file_to_upload() 会优先选该目录的未标记文件。但「优先」仍受网络与计费策略约束:在计费连接上,最近 12 小时的 crash/ 与 boot/ 文件会暂缓;无网络时主循环不上传。因此 tombstoned 的「reporting」日志只表示进入本地处理,不代表云端已收到文件。

    Uploader.upload() 把 HTTP 200/201/401/403/412 都列为停止重试的状态,并尝试写 user.upload xattr。其中 401/403 或 412 的情形要单独记忆:处理完成不等于服务端保存成功。回收侧 deleter_step() 在空间不足时优先删普通目录,把 boot/crash 列为最后删除,但这不是永久保留——存储紧张时崩溃副本最终仍可能被删掉。

    对自制硬件与插件平台的要求

    源文档给出的重构要求可以归纳为一组「必须分开授权」的清单:

    1. 崩溃诊断插件默认只读、驻车或后台运行;原始 dump 的收集、读取和外传分别授权,并提供本地留存但禁止上传的隐私模式,避免把「是否官方 origin」当成唯一隐私开关。
    2. 在受信任核心记录采集决策与数据流:设备身份状态、用户/车队同意、文件分类、脱敏结果、上传结果与删除原因。不要把文件名清洗或摘要去指针等同于 dump 脱敏。
    3. 重构时把「启动清空、扫描上限、失败后不自动重试、上报门固定不变」列为可复现的行为差异,并用模拟 crash、权限错误、断电、磁盘满和计费网络来测试新策略。
    4. 崩溃报告管线不应向 carControl、sendcan 或 panda 安全模式写入;诊断过载或上传阻塞不得影响实时控制与驾驶员告警。

    此外要承认覆盖局限:system/tombstoned.py 没有就近的专门单元测试,工作区六项隔离探针只覆盖扫描主循环与本地文件处理。1000 条目录项上限、启动残留清理与实际文件竞争仍只有静态证据,具体 crash 内容与服务端保留策略仍需在目标硬件和服务协议上验证。

    常见问题

    100 MB 的文件大小上限是隐私过滤吗?

    不是。源文档明确指出超过 100 MB 的文件只是记错后返回,该分支不复制也不删除原文件,并特别标注「大小阈值不是隐私过滤」。完整 crash 副本可能包含路径、进程环境或内存相关信息,不能因为摘要清洗了指针就把副本当匿名数据。

    为什么「本地已上报」不等于云端收到了文件?

    因为本地与远端是两段独立判断。tombstoned 的 reporting 日志只表示进入本地处理;上传侧还受网络与计费策略约束,计费连接上最近 12 小时的 crash 文件会暂缓、无网络时不上传,而 401/403 或 412 属于停止重试状态,处理完成不等于服务端保存成功。

    重启后没处理的崩溃文件会保留吗?

    可能不会。启动时 clear_apport_folder() 会遍历 /var/crash/* 尝试删除,之后才建立初始快照,源文档的动机是避免重复报告。这意味着重启前尚未处理的 crash 文件可能直接丢失。

    崩溃报告会影响实时控制或驾驶员告警吗?

    按源文档的重构要求不应影响。管线不应向 carControl、sendcan 或 panda 安全模式写入,诊断过载或上传阻塞也不得干扰实时控制与驾驶员告警。但这些是重构要求,不是当前实现已验证的隔离保证。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/CRASH_COLLECTION_PRIVACY_DEEP_DIVE.md(取用了崩溃采集的报告门、扫描与大小过滤、完整副本复制、上传与回收语义及插件化要求)。

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

    更新记录

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

  • Params 生命周期:注册键、清理时机与未上抛的写失败

    结论:Params 是低频配置与状态的持久层,不是高频消息总线,也不是独立的车辆安全授权。每个合法键都有注册类型、位标志和可选默认值;同名相近的键(CarParamsCache / CarParamsPersistent / CarParamsPrevRoute)用途与新鲜度完全不同。写入路径存在一个明确的缺口:文件系统返回的负错误码没有在 Python put() 中被检查,所以关键安全配置不能仅凭 put(..., block=True) 没抛异常就宣称持久化成功。固定快照基线为 c8fb906,本地归档缺编译后的 libparams_c,未运行原生参数测试或断电故障注入。

    注册、读写与类型

    params_keys.h 与 ParamKeyAttributes 规定每个合法键有类型、位标志、可选默认值。例如 CarParams 是 bytes 且在 manager 启动 / 上路时清除,CarParamsPersistent 则持久保存。动态插件不能只在自己进程里声明任意新键;必须设计命名空间、类型、迁移与生命周期,并纳入可信配置注册机制。

    Params.check_key() 让 Python 拒绝未注册键及含 NUL 的键;put() 仅接受与注册类型匹配的 Python 类型,get() 按声明反序列化。Python ParamKeyType 支持 string、bool、int、float、time、JSON、bytes;C++ Params 是字节存储,并不自行验证每个键的业务内容。get_bool() 只把 "1" 视为真,其余值都返回 false,因此它不能用来判断某键是否被明确写入;get() 与 get_bool() 在缺省处理上也不完全相同。

    组件 / 函数 已核对契约 对插件的影响
    params_keys.h / ParamKeyAttributes 每个合法键有类型、位标志、可选默认值;CarParams 启动 / 上路清除,CarParamsPersistent 持久。 插件须设计命名空间、类型、迁移与生命周期。
    check_key() 拒绝未注册键与含 NUL 的键;put() 只接受匹配类型。 不能靠自由字符串文件模拟已登记参数。
    get() / _cpp2python() 非阻塞空字节不请求默认时返回 None,请求默认时用注册表默认值;block=True 得到空字节时抛 KeyboardInterrupt;损坏 JSON 警告一次并尝试默认。 默认值回退可能掩盖原始损坏;空字符串不能区分「缺失」与「已写空值」。
    put() / params_put() 默认走 C++ 异步队列;block=True 直接调用文件写入;负返回码未在 Python 侧检查。 关键配置不能只凭无异常宣称持久化成功。

    存储层:原子替换与异步写队列

    Params::put() 走临时文件写入、fsync、目录锁、原子 rename、目录 fsync;异步写线程按队列逐项执行,析构时等待。单键替换具备原子写入路径,但多键配置不是原子事务;异步写也不是跨进程的严格提交确认。

    参数目录通过参数前缀软链接暴露,OpenpilotPrefix 能让测试进程隔离到单独前缀;C ABI 层负责异常翻译与返回缓冲复制。这两层保证的是「路径与接口」的正确性,并不校验业务内容是否符合调用方预期。

    Params::clearAll() 按位标志删除匹配键,同时删除注册表之外的文件;OpenpilotPrefix 能把测试进程隔离到单独前缀。这意味着未登记的插件键可能在清理时消失,验证环境也必须记录前缀与参数快照。

    manager 的清理时机与实际键例

    参数的新鲜度由 manager 在几个时点统一管理:

    时点 源码动作 示例影响
    manager 初始化 清除 CLEAR_ON_MANAGER_START、上路、下路、点火标志;发布渠道还清 DEVELOPMENT_ONLY,随后给缺失的有默认值键写默认。 CarParams、ControlsReady、FirmwareQueryDone 被清除;OpenpilotEnabledToggle 等持久键保留 / 补默认。
    上路边沿 检测 deviceState.started 从 false 到 true,清 CLEAR_ON_ONROAD_TRANSITION。 车型参数需 card 在新行车周期重新提供,插件不能缓存旧控制授权。
    下路边沿 清 CLEAR_ON_OFFROAD_TRANSITION,并更新 IsOffroad。 调试模式、Chestnut 活动状态不应跨周期视为有效。
    点火边沿 基于 pandaStates 点火值清 CLEAR_ON_IGNITION_ON。 DriverTooDistracted、ChestnutActive 等按点火周期复位。

    安全敏感的 SecOCKey 标为 PERSISTENT | DONT_LOG,而 DONT_LOG 的含义由 logger 实现为保留键名但不写值。它不是加密、访问控制或对所有外部插件日志的自动脱敏。

    失败路径与验证

    params_c.cc 只将抛出的 C++ 异常转成 last_error;params_put() 原样返回 Params::put() 的整数结果,而 Params.put() 丢弃该结果。原方法探针确认:若假 C 函数返回负值而 last_error 为空,_bind() 会把负值交还给 Python,两个写入方法都返回 None 且不抛异常;若 last_error 有值,_bind() 抛 RuntimeError。mkstemp、write、fsync 或 rename 的真实磁盘故障尚未复现。

    Params::clearAll() 对删除操作不检查每个 unlink 的返回值;FileLock 锁打开或加锁失败时记录错误,但构造并未抛异常。配置冻结与清理的失败检测不能仅依赖调用返回。现有参数测试覆盖同步 / 异步读写、未知键、默认值与按标志清理,日志测试检查 DONT_LOG 值被遮蔽;本次未运行。新插件参数还应测试断电、磁盘满、跨进程竞争、旧版本迁移、上路 / 下路与点火边沿,以及记录中是否泄露密钥。插件注册器应在 offroad 冻结配置版本、哈希及权限;行车进程读到的配置与 panda 固件安全模式必须可互相校验,失败时保持被动模式。

    常见问题

    put(block=True) 没报错,是否说明参数已经落盘?

    不能这样推断。文件系统调用返回的负错误码没有在 Python put() 中检查;探针确认假 C 函数返回负值且 last_error 为空时,写入方法仍返回 None 且不抛异常。关键安全配置需读回、版本校验及故障注入。

    CarParams 和 CarParamsPersistent 有什么区别?

    用途与新鲜度不同。CarParams 是 bytes 且在 manager 启动 / 上路时被清除,需要 card 在新行车周期重新提供;CarParamsPersistent 与 CarParamsPrevRoute 是持久键。名称相似但不能互相替代。

    DONT_LOG 会自动脱敏日志吗?

    不会。DONT_LOG 由 logger 实现为保留键名但不写值;它不是加密、访问控制,也不是对所有外部插件日志的自动脱敏。安全敏感的 SecOCKey 只是被标为 PERSISTENT | DONT_LOG。

    插件能自己声明新的参数键吗?

    不能只在自己进程里声明。合法键必须在注册表里有类型、位标志与默认值;check_key() 会拒绝未注册键。clearAll() 还会删除注册表之外的文件,所以未登记的插件键可能在清理时消失。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/PARAMS_LIFECYCLE_DEEP_DIVE.md(取用了键注册、读写类型、存储原子性、manager 清理时机与写失败路径分析)。

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

    更新记录

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

  • 视觉 5/8/13 秒 vs 触碰 5/15/25 秒:驾驶员监测双策略与锁定门

    结论:驾驶员监测不是一只倒计时器,而是两套策略 + 两套阈值 + 一套锁定规则的组合。视觉策略的一、二、三级阈值是 5 / 8 / 13 秒,触碰策略是 5 / 15 / 25 秒;三级持续 5 秒记为「持续无响应」,累计两次进入三级或一次持续无响应即触发锁定,锁定时长按第几次锁定取 1 / 5 / 15 / 30 分钟。这些是实现值,不是法规合规结论。

    两条策略,两套阈值

    系统在视觉策略与方向盘触碰策略之间切换:视觉策略看眼睛与头部姿态的分心,触碰策略用于「没有检测到人脸」或「模型不确定度持续偏高」时的回退。两条策略各自保存一份注意力分数(awareness)与衰减步长,不是共享一个百分比。

    切换有明确的约束方向:当视觉策略已进入二级告警以下时,系统会阻止切到更宽松的触碰策略;一旦进入三级,也不允许借切换清除红色告警。从触碰策略回视觉、或反向,会分别保存/恢复各自的记忆值。因此插件化后必须同时保留两份记忆、当前活动策略以及阈值与步长,只序列化一个分数是不够的。

    策略 一级 二级 三级 适用条件
    视觉(vision) 5 秒 8 秒 13 秒 眼睛 / 姿态分心可判定
    触碰(wheeltouch) 5 秒 15 秒 25 秒 无脸,或连续高模型方差回退

    分心判定:姿态、眨眼、手机与睡眠

    面部姿态的计算有一组固定参数:以 1928×1208 的参考座舱图像与计算焦距 598 为基准,用脸在图像中的位置修正模型输出的俯仰与偏航,再减去设备安装外参;横向向右、纵向向下的位置各增加对应的焦角,模型输出的偏航项取负。注意相机配置里原始座舱焦距是 567,策略这里另用 598,两者不能混作同一个已验证的物理焦距。

    分心类别由俯仰/偏航、眨眼、手机与睡眠概率共同标记,姿态的松紧度还会随车速与制动脱离概率插值:先按车速算一个系数(随车速平方衰减、下限 0.2),再把制动脱离概率除以该系数并截到 0 到 0.5 之间——这意味着驾驶模型输出的概率同样参与监测灵敏度,而不只是显示数据。

    判定项 门槛 / 数值 说明
    低速门 vEgo < 2.8 m/s 临近一级时可停止或恢复计时,不等同于解除告警
    驾驶位(RHD)学习 >11 m/s,任一侧有人脸,≥15 秒等效样本 由右侧方向盘概率判定,未完成前用默认值
    人脸可信 选中侧 faceProb > 0.7、姿态标准差 < 0.3 不满足时该帧分心类别不并入
    自然姿态校准 >13 m/s、低方差、未使能或未分心,≥60 秒等效样本 学习驾驶员的自然坐姿偏置
    眨眼判定 受双眼可见性与墨镜概率门控 遮挡或墨镜会改变可用信号

    告警与锁定:什么时候会锁

    告警状态在一级、二级、三级之间推进,注意力分数按动态倍率恢复。锁定的触发条件与告警级别相关:进入三级会累计一次「三级次数」,三级持续到无响应门会增加「无响应次数」,而真正的锁定检查发生在下一次事件更新时——也就是说,进入三级本身并不等于立即锁定。锁定时长按第几次锁定取 1 / 5 / 15 / 30 分钟。

    触发条件 阈值 结果
    持续无响应 三级持续 5 秒 累计无响应次数,下一次事件更新检查锁定
    累计三级 两次进入三级 触发锁定
    一次持续无响应 无响应次数达门 触发锁定
    锁定时长 按第几次锁定 1 / 5 / 15 / 30 分钟

    还有一条容易被误读的规则:当「始终监测」开关已使能、且档位合适,但辅助驾驶本身未使能时,监测会继续运行;临近三级时的豁免也只是阻止本帧继续衰减,若当前分数已低于二级阈值,仍可能显示二级告警。低速门同样不是「视为安全解除」的许可。

    输出与下游:顶层有效位与内层标志分离

    监测输出的顶层「消息有效」由进程的输入检查给出,而内层各级告警标志是独立的另一套概念:顶层无效时,消息里仍可能携带三级告警、强制减速标志与锁定标志。下游把监测等级映射为「分心 1/2/3」或「无响应 1/2/3」事件,锁定或始终监测锁定会产生禁止进入事件;强制减速标志会并入控制状态并写入控制输出,但实际纵向动作还受控制状态与车辆接口约束,不能把它直接理解为制动命令。锁定计数与「过度分心」两个持久键都带「管理器启动时清除」与「点火开启时清除」标志,不是跨点火永久锁定。

    插件化与测试边界

    做插件化时,座舱图像采集与模型推理可以先做影子插件,但安全权威状态机不应由普通插件替换或热更新。接口必须保留左右驾驶位、脸/眼/手机/睡眠概率、姿态方差、时间戳与消息有效位;硬件端需验证相机视角、遮挡、夜间与墨镜条件以及安装外参。插件故障应触发保守退出与人工接管,而不是把缺失的模型结果当成「驾驶员注意力正常」。

    常见问题

    手一直扶着方向盘,是不是就不会报警了?

    不是。触碰策略同样有 5 / 15 / 25 秒的阈值,只是判定依据从视觉分心换成方向盘交互;而在无脸或高模型方差不满足条件时才会回退到触碰策略,交互本身并不等于注意力充足。

    戴墨镜还能正常监测吗?

    会受限。眨眼判断由双眼可见性与墨镜概率门控,遮挡或墨镜会改变可用信号;因此硬件端需要单独验证夜间与墨镜条件,不能只依赖默认光照下的表现。

    被锁定后换一次点火就能解除吗?

    锁定计数与「过度分心」两个持久键都带「管理器启动时清除」与「点火开启时清除」标志,因此不是跨点火永久锁定;但锁定期内仍会按 1 / 5 / 15 / 30 分钟的时长约束相应行为。

    顶层消息标为无效,就表示没有在监测吗?

    不是一回事。顶层「消息有效」由进程输入检查给出,而内层告警标志是独立概念;顶层无效时消息里仍可能携带三级告警、强制减速标志与锁定标志,二者不能互相替代。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/DRIVER_MONITORING_POLICY_DEEP_DIVE.md(视觉/触碰策略阈值、分心判定、告警与锁定、下游安全链与插件边界)

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、时限与配置均出自源文档,标注为未验证的结论保留其原有边界;相关阈值是某一提交的实现值,不构成法规合规结论。

    更新记录

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