标签: 车辆状态

  • BYD Atto 3 档案:Chassis 与 Private 双总线分工

    结论:这份档案的对象是一台 2024 年款、右舵出口版的 BYD Atto 3(元 PLUS EV)。它的数据特征是双总线分工:在 P13 摄像头连接器处接入后,Chassis CAN(引脚 4/8)为 500 kbit/s 经典 CAN、8 字节帧,承载转向、车速、踏板、挡位等整车状态,读与写都在这里;Private CAN(引脚 3/7)为 CAN-FD、数据段近 2 Mbit/s,承载摄像头、前向雷达与约 72 个来源未确认的经典格式 ID。整车消息在发送前需补一个字节校验,纵向与横向命令只允许三条报文。以下所有结论仅来自这一台车,中国国内版本与其他年款可能不同。

    车辆与适用范围

    源文档明确:全部数据来自一辆车——2024 年款右舵出口版 Atto 3;并把「换车前的检查项」单列一节。以下范围只对应这一辆。

    项 内容
    车型 BYD Atto 3 / 元 PLUS EV,2024 年款,右舵出口版(VIN 世界厂商标识 LGX)
    年款 / 市场版本 2024 右舵出口版;中国国内版本与其他年款可能不同(源文档明示)
    ECU / 改装状态 P13 摄像头连接器处旁接;以红熊猫板作 CAN 接口;无原厂设备,因此无 IMU,改用 CAN 桥接
    采集跨度 源文档未给出日志时长或帧数;给出整车质量 1750 kg、轴距 2.72 m、转向比 19.8
    适用范围 仅这一台 2024 右舵出口车;换车须重新测量连接器(BYD 用过两个摄像头供应商,连接器不同)

    总线与寻址

    数据来自 P13 摄像头连接器,共 8 个腔位、6 个接线。两条 CAN_H 同为粉色、两条 CAN_L 同为紫色,源文档要求按引脚位置而非颜色识别总线。

    项 内容
    Chassis CAN 引脚 4/8;经典 CAN,500 kbit/s,8 字节帧;本系统的读与写都在此
    Private CAN 引脚 3/7;CAN-FD,数据段近 2 Mbit/s;承载摄像头、雷达与约 72 个来源未确认的经典格式 ID
    寻址 / 帧格式 整车消息为经典 11 位 ID 格式;私有总线为 FD 帧,含 32 字节与 64 字节两类
    终端 在旁接点处为 Chassis CAN 增加终端,因为接入设备会在摄像头连接器处断开总线
    供电 引脚 1 = 摄像头地,引脚 2 = IG1(受控 12V);点火状态也可从 CAN 推断

    已解码信号

    下表抽取源文档中带明确报文、缩放或位域的信号。位置与解码保留原文写法;证据一列注明来源,凡源文档标注为「未确认」的均原样保留。

    信号 位置(报文 ID / 字段) 解码 证据
    转向角 STEER_MODULE_2 0x11F / STEER_ANGLE_2 0.1 度 / LSB 源文档状态解码表
    驾驶员力矩 STEERING_TORQUE 0x1FC / DRIVER_TORQUE 0.1 Nm / LSB;用于人工接管判定,阈值 18 Nm 源文档;符号正值为左
    EPS 电机力度 0x1FC / MAIN_TORQUE 电机输出,非驾驶员输入 源文档
    EPS 状态 0x1FC 低半字节(位而非枚举) bit0 LKS_PREPARED、bit1 CRUISE_ACTIVATED、bit3 恒 1;0x318 恒 0xFF 源文档;状态 11 需 0.5 s 去抖
    车速 WHEELSPEED_CLEAN 0x1F0 16 位原始计数,0.0713 km/h 每计数 源文档
    单轮转速 WHEEL_SPEED 0x122 / WHEELSPEED_BR 14 位;bit 62 为有效位 源文档
    挡位 / 点火 DRIVE_STATE 0x242 任意有效挡位即视为点火接通 源文档
    制动状态 DRIVE_STATE 0x242 / BRAKE_PRESSED 已去抖;须与安全模块读同一信号 源文档
    油门 PEDAL 0x342 / GAS_PEDAL 不采用 DRIVE_STATE.RAW_THROTTLE 源文档
    转向灯 STALKS 0x133 — 源文档
    方向盘按钮 PCM_BUTTONS 0x3B0 SET / RES / LKAS / 车距 / ACC 开 源文档
    巡航状态 ACC_HUD_ADAS 0x32D / ACC_STATE 2 可用、3 与 5 已启用、7 错误(摄像头总线) 源文档
    偏航角速度 YAW_RATE 0x222 / YawRate 0|12@1+ × 0.002133 − 2.094 rad/s;另含偏置字段需相减 源文档 IMU 桥接节
    平面加速度 AXAY 0x223 / Ax、Ay 0|12@1+ 与 24|12@1+,× 0.027167 与 × 0.027127 m/s² 源文档;Ay 需反号
    雷达目标 私有总线 0x280–0x289 轨道含 TRACK_ID、LONG_DIST、LAT_DIST、VLEAD 等 源文档;COUNTER12_1/2 字段已修正

    读取方法与注意事项

    接入前先做识别。源文档指出:查询固件版本要用 0xF195(SUPPLIER_SOFTWARE_VERSION),因为 BYD 的 ECU 拒绝本平台默认的 0xF188;总线 0 上有 24 个 ECU 应答 0xF195,其中六个返回固件版本(摄像头、制动/IPB、EPS、前向雷达、驱动单元、安全气囊),版本载荷是 6 个原始字节而非 ASCII。摄像头 ECU 不应答固件查询,须设为非必需。

    发送前必须补校验:Chassis CAN 上被签名的 12 条消息,其校验是最后一字节,data[7] = (~sum(data[0:7])) & 0xFF;本系统只发送三条——转向角命令 0x1E2、HUD 帧 0x316、纵向命令 0x32E。0x1E2 按 50 Hz 发送(每两帧中的一帧为 100 Hz),而原车摄像头是 33 Hz。转向命令须在启用期间持续发送,包括静止时也要发请求位为 0 的空闲帧——出现间隙会让 EPS 进入状态 11 并闭锁,单靠随后发 STEER_REQ = 1 无法解除。

    私有总线的完整性另有一套:每条帧的第 0 字节是校验,形式为 CRC8(poly=0x1D, init=0x00, data[1:32]) XOR K[id];64 字节 ID 的两半各自独立保护,K 是逐报文常量、并入 init/xorout/DataID,不是 CAN ID 的函数,需从本车日志逐 ID 采集。另需注意:源文档修正了雷达 DBC 中两个字段的解读——COUNTER12_1/COUNTER12_2 并非 12 位计数器,第 0 与第 32 字节是帧校验、计数器是第 1 与第 33 字节的低半字节。

    还有一处几何修正:雷达 LONG_DIST 以后轴中心(ISO 8855 车体坐标系)为原点,而非传感器面,需减去 3.60 m(前悬 885 mm + 轴距 2720 mm)后才是相对车头距离;不修正会让每个前车读数偏远 3.6 m,修正后雷达与视觉在 12 m 内一致到 0.5 m 以内。

    证据等级与边界

    本文属于单台实车观测:报文、缩放与位域都在源文档里按车辆实测列出,并标注了去抖时间、保活与几何修正等工程条件。但证据范围的边界同样清楚:源文档在末尾单列一节强调,全部数据来自一辆 2024 右舵出口版;中国国内版本与其他年款可能不同,BYD 还使用过两个摄像头供应商、连接器不同,换车必须重新测量连接器。此外,若用不带继电器直通的旁接线束,本系统运行时没有硬件回退;源文档建议使用带直通继电器的线束盒。

    一处不确定性必须原样保留:EPS 掉线前的状态字段被某份未合并的草稿 DBC 命名为 TORQUE_TEMP_FAILED(取值 1 "TooLarge"、2 "TooFast"),但源文档明确——没有 BYD 官方文档、没有 EPS 诊断码、也没有其他分支能确认该命名,应视为未确认;文档给出的转角上限结论并不依赖该命名。另据源文档,ABS/ESP 侧的转向限值表现为一个恒定的偏航率:EPS 在乘积约 3155 及以上放弃车道保持,故取 3000 留余量,不要抬高到 3155 或观测均值 3578。

    关于安全访问,源文档未提供任何绕过方法,且不建议尝试;本文只整理源文档记录的可读信号与发送约束。

    常见问题

    为什么查询固件版本要用 0xF195 而不是 0xF188?

    因为源文档记录 BYD 的 ECU 会拒绝 0xF188(本平台默认的那个)。改用 0xF195 后,总线 0 上有 24 个 ECU 应答,其中六个返回固件版本;版本载荷是 6 个原始字节而非 ASCII,摄像头的 MPC ECU 不应答、须设为非必需。

    Chassis CAN 上发送报文要注意什么?

    两条硬约束。其一,被签名的 12 条消息必须补末字节校验 data[7] = (~sum(data[0:7])) & 0xFF;其二,本系统只允许发送 0x1E2、0x316、0x32E 三条。转向命令还要 50 Hz 连续发送、静止时发请求位为 0 的空闲帧,避免 EPS 进入状态 11 并闭锁。

    雷达距离为什么要减 3.6 m?

    因为 LONG_DIST 的原点是后轴中心(ISO 8855 车体坐标系),不是传感器面。需减去前悬 885 mm 加轴距 2720 mm 共 3.60 m 才得到相对车头距离;不修正每个前车会偏远 3.6 m,修正后在 12 m 内与视觉一致到 0.5 m 以内。

    TORQUE_TEMP_FAILED 这个信号名可信吗?

    不可信。源文档明确:该命名来自一份未合并的草稿 DBC,没有 BYD 文档、没有 EPS 诊断码、也没有其他分支能确认,应视为未确认。文档给出的转角上限(约 3000 / 车速)并不依赖这个命名。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/shemps-byd-atto3/README.md(本项目车型档案主来源:线束与总线拓扑、指纹、状态解码、校验、横向与纵向控制、雷达与 IMU 桥接、适用性边界)。

    说明:本文整理自第三方开源项目的公开文档,仅代表该文档所记录的单个车辆样本,不代表同型号全系车型。CAN 与诊断操作存在风险,任何实车操作前请自行复核并遵守当地法规。

    更新记录

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

  • 横纵向控制算法:四种横向单位与纵向「最小加速度」边界

    结论:横向的四种控制器不共享相同单位——角度、曲率与归一化扭矩只能在各自的 CarParams 契约下解释,不能互相套用阈值;纵向的「最小加速度」合成是偏保守的候选合成,shouldStop 与所选加速度甚至不一定来自同一候选。本文还特别指出:非零求解状态会记录告警并 reset(),但清零后的轨迹仍会被规划器继续处理。以下数值均为源码设定,不是新硬件或新车型的合格阈值;本地缺少 acados 生成代码与模型实体,未构建求解器、未在车辆上验证。

    控制器选择与横向实现

    Controls.__init__() 按 CarParams.steerControlType 优先选择角度或曲率接口,否则按 lateralTuning 选择 PID 或扭矩控制器。Controls.state_control() 根据使能、转向故障、静止条件计算 latActive,由 clip_curvature() 限制曲率变化率、横向加速度和绝对曲率后再调用控制器;这一路仍不是最终 CAN 发送许可。

    实现 关键函数与算法 状态或故障边界
    公共基类 LatControl._check_saturation() 对控制器饱和或曲率受限按 dt 积分;速度、驾驶员施力及安全限幅可抑制计时 reset() 仅清 sat_time,不会自动清子类 PID、延迟缓冲或滤波器
    转角接口 LatControlAngle.update() 用车辆模型把目标曲率转为方向盘角并加学习角偏置 返回的是目标角;执行器变化率仍由车型控制器与 panda 策略约束;角偏置与车辆模型必须随车型校准
    曲率接口 LatControlCurvature.update() 从当前转角估计曲率;配置 PID 时做误差闭环,未配置或驾驶员施力时用前馈 输出限到 MAX_CURVATURE 的 PID 范围,不能把曲率值当作扭矩
    转角 PID LatControlPID.update() 以目标/当前转角误差闭环,转角前馈不含角偏置 安全限幅、驾驶员施力或低速时冻结积分;停用期间不调用 PID update(),原积分未由此清零
    扭矩控制 LatControlTorque.update() 把目标曲率换算成横向加速度,在该域做 PID,并补偿 roll、学习偏置、摩擦和前瞻 jerk 使用 1 秒目标加速度环形缓冲及 jerk 滤波;停用时仍更新缓冲/滤波但不调用 PID,继承的 reset() 不清积分

    PIDController.update() 按速度插值增益,组合 P/I/D 与前馈,对输出限幅并在已限幅方向抑制积分增长;其积分候选也直接以输出上下限裁剪,因此正下限或动态收紧限幅时的内部状态不能按普通「剩余裕度」积分推断。还应注意扭矩控制器的延迟 deque 在停用时仍追加目标,短停再启用可能先读到旧设定值;原测试只覆盖停用满一秒后的缓冲清零。四种横向实现中,扭矩控制的返回值还有符号翻转。

    还有两个跨层边界需要记住:其一,基类 reset() 只清 sat_time,不会清子类 PID、延迟缓冲或滤波器,因此「调用过 reset()」不等于内部状态全部归零;其二,selfdrived 还有独立的欠跟踪、转弯与驾驶员施力门,它们与控制器自身的饱和计时不是同一套判据。插件替换任一横向实现时,若不保留这些内部状态与门控语义,就可能出现停用再启用后的状态残留。

    纵向 MPC、候选合成与执行

    纵向链的关键在于层层限幅与状态门:MPC 生成轨迹,规划器在候选里选最小加速度并做车辆范围限幅,执行层再按车型加速度范围裁剪。任何一层都可能把上游的意图收窄,因此「规划器输出」与「最终发给车型控制器的目标」并不等价。此外,无前车时使用虚拟高速前车是为了维持同一求解模式,而不是真的存在一个前车目标。

    层次 关键函数与数据流 失效语义
    求解器构建 gen_long_model() 设位置/速度/加速度状态和 jerk 控制量;gen_long_ocp() 配目标距离、速度/加速度/障碍约束和软约束成本;SConscript 生成/链接 acados Cython 扩展 生成代码本地缺失;仅阅读 Python 建模不足以证明求解器数值、收敛或耗时
    前车目标 LongitudinalMpc.process_lead() 处理雷达前车;无前车时用虚拟高速前车维持同一求解模式 crash_cnt 用 5 秒内预测距离和前车模型概率累积,规划层再阈值化 FCW;不是独立碰撞检测认证
    求解失败 LongitudinalMpc.run() 设置每节点参数与初始约束、取解及耗时 非零求解状态按条件记录警告并 reset();解轨迹被清零,规划器仍会处理清零轨迹
    候选计划 LongitudinalPlanner.update() 用 MPC 轨迹、巡航加速度及实验模式 e2e 加速度组成候选,选最小加速度并做车辆范围限幅 所选加速度与 shouldStop 不一定来自同一候选;这是保守合成,不是可绕开车型限制的插件接口
    纵向执行 long_control_state_trans() 在 off、stopping、pid 间切换;LongControl.update() 积分/前馈控制并在车型加速度范围裁剪 输出仍需车型编码及硬件安全门,不能当成直接制动扭矩

    get_accel_from_plan() 与 get_curvature_from_plan() 将模型离散轨迹插值到动作时刻;get_cruise_accel() 综合巡航误差、弯道横向加速度、滑行/油门许可与 jerk 限制。插件替换规划器必须保留时间轴、单位、滞后补偿和输出有效位语义。

    变道与警告状态

    DesireHelper.update() 要求横向控制活动、单侧转向灯新沿和最低车速进入准备状态;驾驶员同向施力且无盲区报警后进入变道开始,再结合模型概率、时间与灯状态回到准备或关闭。它输出模型 desire,不直接授权转向。LaneDepartureWarning.update() 在足够车速、近期无转向灯且横向控制未活动时,按车道置信度、距离和变道意图置警告,输出同样不是执行器命令。

    测试与插件边界

    现有纵向状态测试、横向饱和测试与扭矩缓冲测试只覆盖指定车型和局部状态,本轮运行了纵向隔离探针但未运行上述原仓库测试。重构时应分别记录四种横向控制器的内部 PID/缓冲状态、规划器 MPC 状态、车型学习参数和最终 carOutput,并用进程回放、SIL/HIL 及故障注入验证停用、重启和切换。需要明确:selfdrived 的使能/退出与 panda 固件安全限制不能由普通控制插件覆盖。

    常见问题

    横向控制器的曲率值可以直接当扭矩用吗?

    不能。四种横向实现不共享单位:角度输出的是方向盘目标角,曲率输出被限到 MAX_CURVATURE 的 PID 范围,扭矩控制输出的是归一化扭矩,只能各自在对应 CarParams 契约下解释。

    纵向求解失败后会发生什么?

    run() 遇到非零求解状态会按条件记录警告并调用 reset(),将解轨迹与告警时间戳清零;探针确认连续失败可逐次记录,规划器仍会处理清零后的轨迹,实机故障频率未验证。

    「最小加速度」合成等于安全保证吗?

    不等于。它是偏保守的候选合成,所选加速度与 shouldStop 甚至可能来自不同候选;求解器失败、输入无效、车型加速度能力和实际制动执行仍分别由其他层处理。

    停用再启用横向控制会保留旧的积分或缓冲吗?

    可能。继承的 reset() 不清子类 PID 积分、延迟缓冲或滤波器;扭矩控制器的延迟 deque 停用时仍追加目标,短停再启用可能先读到旧设定值,再使能状态需实测。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/CONTROL_ALGORITHMS_DEEP_DIVE.md(取用了四种横向控制器、纵向 MPC 与候选合成、变道与警告状态、测试覆盖与插件边界)。
    • 项目 02 —— openpilot-research/PERCEPTION_CONTROL_DEEP_DIVE.md(仅用于确认感知到控制主链路与控制器位置)。

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

    更新记录

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

  • Opel Astra H 的 LS-CAN 与 MS-CAN 解码档案

    结论:这份档案记录的是欧宝 Astra H 的两条总线:单线的 LS-CAN(即 SW-CAN / GMLAN) 与 MS-CAN。在 LS-CAN 上,源文档把车速、转速、行驶距离、燃油喷射、冷却液温度、燃油液位、系统时间、电压与 TPMS 都逐字节解了出来;例如车速在 0x108 上除以 128、燃油液位是 94 - (值/2) 升。它是一份「按地址逐字节标注」的观测表,而不是厂商规格:大量字段被标为 ?? 或「见过 N 种取值」,这些不确定标记必须原样保留。

    车辆与适用范围

    源文档是一份公开的「车辆 CAN 报文数据库」,按 品牌/型号/代/总线/部件.md 组织。Astra H 这一支同时给出 LS-CAN 与 MS-CAN 两个目录,其中 LS-CAN 以 ALL_TYPES.md 汇总全部观测值。

    项 内容
    车型 Opel Astra H(MS-CAN 文档中点名发动机为 Z16XEP 1.6L)
    年款 / 市场版本 源文档未给出具体年款与市场版本
    ECU / 改装状态 源文档未给出;为车主对原车的观测
    采集跨度 源文档未给出日志数或帧数;部分报文注出周期(如 0x108 约 50ms、0x190/0x440 为 1000ms)
    适用范围 仅限源文档记录的这一台 Astra H;数值标定含多项未确认项

    总线与寻址

    这台车同时有 LS-CAN 与 MS-CAN。LS-CAN 被文档标注为「SW-CAN——单线或 GMLAN」,是单线制总线;MS-CAN 承载车身、信息显示等部件。两条总线都以小写十六进制地址登记(如 0x108、0x530、0x684)。

    项 内容
    物理层 LS-CAN:单线(SW-CAN / GMLAN);MS-CAN:源文档未给出
    链路层 / 比特率 源文档未给出
    寻址方式 表中地址为小写十六进制(如 100、530),源文档未标注位宽
    CAN FD 源文档未提及
    多帧报文 MS-CAN 的 Info-Display 报文超过 8 字节,使用 ISO-15765-2 分包

    已解码信号

    下表抽取 15 条最有代表性的条目,横跨 LS-CAN 与 MS-CAN。凡源文档标为 ??、「见过 N 种取值」或「计算取决于传感器/有时反向」的,均在证据列保留说明。

    信号 位置(ID / 字节) 解码 证据 / 置信度
    车速 LS-CAN 0x108 byte2+3 值 / 128 = km/h(例 0x2098=65.19) 源文档示例与说明
    发动机转速 LS-CAN 0x108 byte5+6 16 位(例 0x04E5=1253 rpm) 源文档示例;MS-CAN 版本在 0x4E8 上为「RPM/4」
    行驶距离(每驱动轮) LS-CAN 0x110 byte2+3 / byte4+5 一次增量 = 1.5748 cm;16 位满周期约 1032 m 源文档说明
    燃油喷射量 LS-CAN 0x130 byte2+3 一次增量 = 0.03054 ml(1.6L 机);满周期约 2 升 标定基于作者 1.6L 发动机
    冷却液温度 LS-CAN 0x145 byte4 值 – 40 = °C(例 0x28→-2 °C) 源文档示例
    发动机/点火状态 LS-CAN 0x145 byte1–3、5 多字节状态码(如 0x20=点火开未着、0x00=运行) 源文档逐值说明
    巡航控制状态 LS-CAN 0x145 byte6 0x04=未激活 / 0x06=激活 源文档说明
    转向灯/拨杆状态 LS-CAN 0x175 byte3 0x01~0x04 表示半推/半抬/全推/全抬 源文档说明
    转向灯控制帧 LS-CAN 0x260 三字节组合区分左/右/双闪 源文档逐组合表
    总里程 LS-CAN 0x190 byte3–5 里程量(周期 1000ms) 源文档标注;单位与系数未在本文展开
    燃油液位 LS-CAN 0x375 byte2 94 – (值/2) = 升(例 0xA0→14.0 L) 「计算取决于传感器,有时反向」
    系统时间 LS-CAN 0x440 byte1–3 时 : 分 : 秒÷4 源文档说明
    户外温度 LS-CAN 0x445 byte2 MS-CAN 同义项 0x682 为「值/2 – 40 = °C」 源文档说明
    电压 LS-CAN 0x500 byte2 源文档标注为电压(周期约 1500ms) 源文档标注;换算未在本页给出
    胎压 TPMS LS-CAN 0x530 byte3–6 每轮 值/25 = Bar;byte1/2 为前/后传感器状态 MS-CAN 版本在 0x684 上同式

    读取方法与注意事项

    源文档的记法本身就是方法约束:页首声明斜体值表示「至今只见固定值」,粗体值表示已被单独页面解释,括号中的数字是见过的不同取值个数。也就是说,一个标了 ? (4) 的字节,作者只见过 4 种取值——它可能是枚举状态,也可能是「从未触发的故障位」,文档对后者也如实保留。

    对使用者而言,最实际的两点:其一,MS-CAN 的 Info-Display 报文超过 8 字节,用 ISO-15765-2 打成多帧,数据块结构是「两字节命令 + 一字节负载长度 + 一字节消息类型 + 若干子包(ID + 字符数 + UTF-16 字符)」;文档给了 AC、音频、导航等消息类型与各类型下的行 ID 清单。其二,按位复用的字节必须做位运算(拨杆/方向盘按键就是「函数由位编码」)。

    证据等级与边界

    本档案属于单车观测:所有条目都来自一位车主对一台 Astra H 的记录,没有第三方旁证,也没有反汇编或厂商规格支撑。可核对的是「作者给出的一条条示例」(如 0x108 的 0x2098→65.1875 km/h、0x375 的 0xA0→14.0 L);不可核对的是这些标定是否适用于其他车。

    三处不能外推。第一,改装的线束、传感器与仪表版本会让个别字节不同,燃油液位文档自己就注明「取决于传感器,有时反向」。第二,作者在页首明说斜体值「可能是从未触发的故障指示」,把「只见固定值」当成「恒为定值」是过度解读。第三,发动机相关标定(燃油喷射的 0.03054 ml)是用作者自己的 1.6L 发动机推出来的,换机型即失效。

    关于「安全访问」类问题:本文只整理被动观测到的广播报文,未涉及任何诊断写入或安全访问绕过;源文档未提供绕过方法,且不建议尝试。

    常见问题

    Astra H 的 LS-CAN 和 MS-CAN 有什么区别?

    LS-CAN 被文档标注为单线的 SW-CAN / GMLAN,承载车速、转速、燃油、里程、TPMS 等;MS-CAN 承载车身与 Info-Display 等。两者的地址与编码各自独立。

    车速是怎么算出来的?

    在 LS-CAN 的 0x108 上取 byte2+3 组成 16 位值,再除以 128。文档示例:0x2098 = 8344,8344 / 128 = 65.1875 km/h。

    燃油液位为什么有时反向?

    文档注明该值「计算取决于传感器,有时反向」,公式为 94 - (值/2) 升。因此同一公式在不同传感器配置下可能得出相反趋势,不能无条件套用。

    Info-Display 的报文为什么超过 8 字节?

    因为它用 ISO-15765-2 把长报文分成多个 CAN 帧(首帧 + 连续帧)。数据块由两字节命令、一字节负载长度、一字节消息类型和若干子包组成,子包再带行 ID、字符数与 UTF-16 字符。

    延伸阅读

    参考资料

    • 项目 01 —— public-can-dbc-datasets/additional/trueffelwurm-opel-astra-h/Opel/Astra/H/LS-CAN/ALL_TYPES.md(本项目车型档案主来源:LS-CAN 全部观测值与逐条说明)。
    • 项目 01 —— public-can-dbc-datasets/additional/trueffelwurm-opel-astra-h/Opel/Astra/H/MS-CAN/Body.md(MS-CAN 的底盘与发动机条目)。
    • 项目 01 —— public-can-dbc-datasets/additional/trueffelwurm-opel-astra-h/Opel/Astra/H/MS-CAN/Info-Display.md(Info-Display 的 ISO-15765-2 分包与消息类型表)。

    说明:本文整理自第三方开源项目的公开文档,仅代表该文档所记录的单个车辆样本,不代表同型号全系车型。CAN 与诊断操作存在风险,任何实车操作前请自行复核并遵守当地法规。

    更新记录

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

  • Honda 控制输出:Bosch/Nidec 构帧与 50/10 Hz 节拍

    结论:Honda 控制输出分 Bosch 与 Nidec 两条构帧路径,并叠加三层节拍:转向每轮一条候选、纵向 50 Hz、仪表 10 Hz。Nidec 的正加速并不在 50 Hz 制动帧里,而由 10 Hz ACC_HUD 携带 PCM_SPEED/PCM_GAS,所以仪表链本身就是纵向执行链的一部分。Python 侧的限幅不等于 MCU 的安全保证:CarController 的 STEER_MAX 与变化率处理、panda honda_tx_hook() 的独立检查、继电器故障阻断是不同层次。下述 100/50/10 Hz 均以目标 100 Hz、每轮调用控制器一次且无丢周期为前提,只表示软件名义节拍;未做 DBC 回放、台架或实车验证。

    关键函数与组件

    入口 源码 作用
    CarController.update() carcontroller.py 读取控制目标与车况缓存,限幅、调度并返回候选 CAN 帧和软件回报值。
    compute_gas_brake() carcontroller.py Nidec 把加速度转换成 gas/brake;Bosch 分支当前返回两个零,实际纵向另由 ACC 帧表示。
    actuator_hysteresis() / brake_pump_hysteresis() carcontroller.py Nidec 制动去抖与泵请求;泵计时基于 frame * DT_CTRL。
    CarControllerParams / CanBus values.py、hondacan.py 车型扭矩上限、变化率及 PT/radar/camera/LKAS 路由。
    create_steering_control() / create_acc_commands() hondacan.py 转向和 Bosch 纵向字段编码。
    create_brake_command() / create_acc_hud() hondacan.py Nidec 制动、PCM 加速及仪表字段编码。
    honda_tx_hook() / honda_nidec_fwd_hook() honda.h MCU 独立检查候选帧并决定相机制动帧转发。

    公共转向与回报语义

    每轮先对归一化 actuators.torque 以 STEER_DELTA_UP/DOWN * DT_CTRL 限变化率,再取负号、夹到 [-1, 1] 并乘车型 STEER_MAX 构造 CAN 扭矩。create_steering_control() 始终生成一条 STEERING_CONTROL 候选;CC.latActive 为假时其扭矩和请求位都为零,BOSCH_TJA_CONTROL 还设置 STEER_DOWN_TO_ZERO。这段 Python 限制不替代 MCU 的未使能零转向规则,也不能从 latActive 为真推定车辆接受转向。

    new_actuators 中的 torque 是软件限速后的归一化值,torqueOutputCan 是编码前的整数;accel/gas/brake/speed 有部分字段只在对应分支更新,可能保持上轮值。它们是软件目标回报,不是 panda 发送回执或车辆执行测量。控制器仅在车控存活检查通过时才被调用并发布 sendcan;消息的 valid 使用 CS.canValid,具体总线发送仍需再经过 pandad/panda。

    纵向与按钮分支

    分支 触发条件 生成的候选帧
    原车纵向 未启用自控纵向 偶数轮 BOSCH_SUPPLEMENTAL_1;取消或恢复请求时 SCM_BUTTONS(radarless 发相机总线,其余用 PT 总线)
    自控纵向 Bosch 启用自控纵向 偶数轮 ACC_CONTROL;有雷达版再加 ACC_CONTROL_ON
    自控纵向 Nidec 启用自控纵向 50 Hz BRAKE_COMMAND + 10 Hz ACC_HUD 携带 PCM_SPEED/PCM_GAS

    若未启用自控纵向,偶数轮对非 radarless/CAN FD 平台尝试生成 BOSCH_SUPPLEMENTAL_1,取消或恢复请求则生成 SCM_BUTTONS;radarless 把按钮发到相机总线,其他平台用 PT 总线。源码的 supplemental 条件没有显式限定 Bosch,不能据此推定所有进入该分支的车型都获安全白名单许可。spam_buttons_command() 只生成一条候选,调用处没有独立按钮节流,实际发送还受当前模式的 TX 白名单和按钮安全门制约。

    启用自控纵向时偶数轮生成纵向候选。Bosch 把加速度夹在 BOSCH_ACCEL_MIN/MAX,用独立查表得到 gas;有雷达版同时生成 ACC_CONTROL_ON 与 ACC_CONTROL,无雷达版只生成 ACC_CONTROL。gas_command 在不活动或低于阈值时为 -30000,ACCEL_COMMAND 在不活动时为零;STANDSTILL 由 LongControlState.stopping 连续计数而来,无雷达版 IDLESTOP_ALLOW 要计数超过 200 个 50 Hz 周期。enabled、longActive、stopping 是不同条件,不能合并成单一「已启用」位。

    Nidec 的 compute_gb_honda_nidec() 在低于 2.3 m/s 时引入蠕行制动,再经刹车迟滞、每轮变化率限制、风阻扣减和 NIDEC_BRAKE_MAX - 1 上界量化为 BRAKE_COMMAND。泵请求在制动增大或超过 20 秒后重置计时,正制动下保持至少 0.2 秒。编码器把 AEB 执行相关字段显式置零,FCW 只映射警告和原车 chime。Nidec 正加速不是每个 50 Hz 制动周期的独立油门帧,而是在 10 Hz ACC_HUD 中携带 PCM_SPEED/PCM_GAS,因此 HUD 链也是其纵向执行链的一部分,应分别回放替代与普通分支。

    HUD、总线与 MCU 边界

    每 10 轮:自控纵向才生成 ACC_HUD,所有模式生成一个或两个 LKAS_HUD 候选;有雷达 Bosch 自控纵向还生成 RADAR_HUD,特定 Civic Bosch 补 LEGACY_BRAKE_COMMAND。原车纵向且 BOSCH_EXT_HUD 时 LKAS_HUD_A/B 分开发送。hud_v_cruise 在不可见时取 255,可见时除以 CS.v_cruise_factor,此值依赖 CarState 所选单位。create_acc_hud() 对 Bosch/Nidec 使用不同字段,Nidec 还复制原车 acc_hud 的若干 FCM/图标位;无新鲜度检查时不应将复制成功当作原车功能健康。

    CanBus 不能假定 PT 永远是 bus 0:有雷达 Bosch 的 PT 为 offset+1、radar 为 offset,LKAS 依据自控纵向决定去 PT 还是 radar;radarless/CAN FD 与 Nidec 的 PT 为 offset。panda honda_nidec_init() / honda_bosch_init() 选择地址、总线、长度白名单及 RX 检查;Bosch 自控纵向的 safetyParam 分支仍受 ALLOW_DEBUG 编译门约束,不能因 Python 能构帧就推定量产固件可发。

    honda_tx_hook() 对 Nidec BRAKE_COMMAND 的制动幅度、AEB 位和 ACC_HUD 的速度/gas 作检查;对 Bosch ACC_CONTROL 的加速度、gas 和 AEB 位作检查;未允许控制时转向输出必须为零。诊断地址只允许固定 tester-present 载荷,Bosch 原车纵向的按钮在控制不允许时只能取消。Nidec honda_nidec_fwd_hook() 还按相机原车 AEB 制动力与自控制动请求比较动态转发 0x1FA,不是简单的「总是屏蔽原车制动」。

    插件与硬件验收

    车型输出插件应分别声明转向、纵向、按钮、HUD、诊断保活和原车 ADAS 影响权限;每项绑定 DBC、车型指纹、物理总线、消息周期、数据年龄和 panda safety 模式。只允许插件产生候选控制帧,最终 TX 白名单、力矩/加速度/AEB 约束与超时失效仍在独立 MCU。对 Nidec 要单测 10 Hz ACC_HUD 与 50 Hz 制动的耦合、泵保持和原车 AEB 转发;对 Bosch 要单测雷达/无雷达、原车/自控纵向、ALLOW_DEBUG 配置和 ECU 静默失败。当前没有可用实板、目标车型回放或车辆执行证据,本文不能作为上车许可。

    常见问题

    Nidec 的加速信号在哪条帧里?

    不在 50 Hz 制动帧里。Nidec 正加速是在 10 Hz ACC_HUD 中携带 PCM_SPEED/PCM_GAS,因此 HUD 链也是其纵向执行链的一部分,不能只看 BRAKE_COMMAND。

    Bosch 的 gas 是怎么算出来的?

    自控纵向时 Bosch 把加速度夹在 BOSCH_ACCEL_MIN/MAX,再用独立查表得到 gas;gas_command 在不活动或低于阈值时为 -30000。compute_gas_brake() 的 Bosch 分支当前返回两个零,实际纵向另由 ACC 帧表示。

    PT 总线一定是 bus 0 吗?

    不是。有雷达 Bosch 的 PT 为 offset+1、radar 为 offset,LKAS 依据自控纵向决定去 PT 还是 radar;radarless/CAN FD 与 Nidec 的 PT 为 offset,因此不能假定固定编号。

    Python 的转向限幅算安全保证吗?

    不算。CarController 的 STEER_MAX 与变化率处理只是软件层;MCU 的 honda_tx_hook() 独立检查候选帧,未允许控制时转向输出必须为零,最终还受继电器故障阻断。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/HONDA_CONTROLLER_CAN_OUTPUT_DEEP_DIVE.md(Bosch/Nidec 构帧、50/10 Hz 节拍、HUD 与 MCU 边界)。
    • 项目 02 —— openpilot-research/ 同目录 Honda CarState 与接口专题(输入侧与雷达通信控制作为相邻边界参考)。

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

    更新记录

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

  • 巡航设定速度:VCruiseHelper 的 PCM 与非 PCM 两条路径

    结论:巡航设定速度(vCruise)是规划与显示的输入,不是车端已执行车速,也不代表系统已使能。源文档把它的产生拆成两条互斥路径:CP.pcmCruise=True 时直接读原车 cruiseState.speed/speedCluster 转 km/h,按键调速由原车系统负责;CP.pcmCruise=False 时由 VCruiseHelper 自己处理按键并步进。两条路径的单位(km/h)、哨兵(255/0/-1)与边界(8–145 km/h)都必须逐车型核对,不能按品牌名称猜测。

    一、数据流与单位

    Car.state_update() 在车型接口解析车况后,先用当前 sm['carControl'].enabled 调用 update_v_cruise(),在 carControl.enabled 从假变真时再用 CS_prev 调用 initialize_v_cruise()。源文档注释明确这是为了使用 selfdrived 使能那一步的按钮状态,避免按键边沿与 card 的下一次车况更新错位。

    随后发布的 CS.vCruise、CS.vCruiseCluster 单位都是 km/h;原始 cruiseState.speed 是 m/s,在 PCM 路径中乘 MS_TO_KPH。LongitudinalPlanner.update() 对 carState.vCruise 上限裁剪到 V_CRUISE_MAX 再转 m/s,并区分 unset;Controls.state_control() 将 CS.vCruise 转 m/s 传给车型 PID 加速度限幅,而 HUD 的设定速度取 vCruiseCluster。两种速度值可能不同,不能把仪表显示值当作规划器原始目标。

    二、PCM 与非 PCM 两条路径

    条件 update_v_cruise() 行为 影响
    原车巡航不可用 设定值及仪表值均为 255;非 PCM 按键计时器不在该分支清零 规划器将其识别为未初始化;不代表车辆实际时速 255 km/h。恢复时旧计时器可能仍在。
    CP.pcmCruise=True 直接使用 CS.cruiseState.speed/speedCluster 转 km/h 按键调速由原车系统负责,helper 不自行步进;speed==0 改为 unset,speed==-1 保留 -1 特殊值。
    CP.pcmCruise=False 且原车巡航可用 从本地状态处理按钮,然后令仪表设定值等于内部设定值 自管设定速度;只有 enabled=True 时按键会改目标。

    PCM 读取路径没有在本 helper 内对正常速度统一裁剪到 145 km/h;下游纵向规划器有自己的上限处理。量产适配需要分别核对原车报文中的 speed 和 speedCluster 含义、无效哨兵和单位,不能把任意 0 或 -1 当作普通速度。

    三、非 PCM 按键算法与阈值

    _update_v_cruise_non_pcm() 先找增/减速按钮的松开边沿;若找不到,再检查按住计时是否达到 50 帧倍数,将其视作长按重复。执行变更前有两个防重入门:增速键用于从静止巡航恢复时不调高设定值;若按钮按下时尚未 enabled,而松开前才完成使能,也不把同一次按键再次解释为调速。update_button_timers() 在当帧调速判断之后更新计时和按下时的 standstill/enabled 快照,这是边沿语义的一部分。

    参数 / 规则 取值 说明
    V_CRUISE_UNSET 255 未设定哨兵。
    V_CRUISE_MIN 8 km/h 结果下限。
    V_CRUISE_MAX 145 km/h 结果上限;上限注释指出模型训练分布外的速度预测可能不可靠。
    短按步长 公制 1 km/h;英制 IMPERIAL_INCREMENT=1.6 km/h 英制由 1 mph 转换并取一位小数。
    长按倍率 ×5 长按时若当前值不在 5 倍步长网格,增速先向上取整、减速先向下取整,再继续步进。
    长按判定 按住 50 帧倍数 最终结果取一位小数并限制在 8–145 km/h。

    油门覆盖时按减速键,会把新目标至少抬到当前 vEgo,随后仍受 145 km/h 上限约束。源码条件还写有 setCruise,但该函数的按键选择集合仅含增速/减速,因此当前路径不会选择到 setCruise;不能据该条件宣称 SET 键在此处有同样效果。

    四、使能初始化、上游门与测试

    initialize_v_cruise() 对 PCM 直接返回;非 PCM 首次使能时默认从当前车速与初始下限中取较大者,普通模式初始下限 40 km/h,实验模式 105 km/h,再限制到 145 km/h。若是增速/恢复按键触发且已有旧设定值,则恢复 v_cruise_kph_last,不是重新用当前车速初始化。这里的 105 km/h 是设定速度的初始下限/上界提示语义,实验模式仍由模型和下游规划裁决实际加速,不能理解为车辆会立即开到该速度。

    非 PCM 的按键使能来自 CarState.buttonEnable(基础接口由增/减速键松开边沿产生,车型可覆写),经 CarEvents 转成 buttonEnable 事件,再由状态机决定使能。VCruiseHelper 只管设定值,不负责判断驾驶员监测、车辆故障或 panda 的 controlsAllowed;取消键、刹车与安全事件仍可让系统退出。

    源文档的输入清单里还列出 IsMetric 与 ExperimentalMode:前者参与决定短按步长取公制 1 km/h 还是英制 1.6 km/h,后者决定首次使能的初始下限取 40 还是 105 km/h。二者都只影响设定值的产生,不影响车端是否已执行,也不替代使能与安全门。

    测试方面,test_cruise_speed.py 覆盖非 PCM 短按松开、使能中按键、静止 resume、油门覆盖和初始化,文件 TODO 明确 PCM 路径尚无对应测试;本工作区的八项原类隔离探针使用假 NumPy 裁剪和假车况,不依赖实际 capnp、CAN 或 UI,也不等于实车按键与执行器测试。

    常见问题

    设定速度和实际车速是一回事吗?

    不是。设定速度是规划/显示输入,不是车端已执行车速,也不代表系统已使能。源文档还强调 vCruise 与 HUD 取的 vCruiseCluster 可能不同,不能把仪表显示值当作规划器原始目标。

    PCM 车型的长按调速逻辑由谁负责?

    由原车系统负责。CP.pcmCruise=True 时 helper 直接使用 CS.cruiseState.speed/speedCluster 转 km/h,本 helper 不自行步进;因此 50 帧长按、5 倍步进这类算法只存在于非 PCM 路径。

    实验模式初始下限 105 km/h,是不是车会立刻加速到 105?

    不是。源文档明确 105 km/h 是设定速度的初始下限/上界提示语义,实验模式仍由模型和下游规划裁决实际加速,不能理解为车辆会立即开到该速度。

    按增速键从静止恢复会调高设定值吗?

    不会。源文档给出的防重入门之一就是:增速键用于从静止巡航恢复时不调高设定值;另一个是若按钮按下时尚未 enabled,而松开前才完成使能,也不把同一次按键再次解释为调速。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/CRUISE_SET_SPEED_DEEP_DIVE.md(取用了数据流与单位、PCM 与非 PCM 路径、非 PCM 按键算法、阈值常量与测试边界)。

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

    更新记录

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

  • Tesla 与 Rivian 不能共用一套插件:转角控制 vs 动态扭矩的两条链路

    结论:Tesla 与 Rivian 都属于电动车,但不能共用一套「电动车插件」:Tesla 是转角控制加选择性原车报文转发,Rivian 是动态扭矩加原车 ACC 状态取消,两者在总线拓扑、原车辅助系统与安全策略上均不同。两品牌的纵向参数读取都受 ALLOW_DEBUG 限制,车型/固件识别是执行前提;未识别、无法确认固件或未接线时保持被动。本文为源码静态分析,没有实车、线束、固件构建或 HIL 验证。

    Tesla:转角控制与选择性转发

    CarInterface._get_params() 选择 tesla 安全模式和转角控制,并通过指纹识别缺失 DAS_settings、新 3-bit DAS_steeringControlType 固件;Model X 或旧固件条件可标为 dashcamOnly。雷达支持需要特定 Continental 雷达、DBC 与物理接入 bus 1,不能只凭车型名推断。alpha_long 可写纵向安全参数,但固件实际纵向启用受 ALLOW_DEBUG 限制。

    CarState.update() 与 update_autopark_state() 从 party 与 autopilot-party 两条总线解析车速、踏板、EPS 手握级别/接管、巡航、Autopark、原车 AEB/LKAS,并保留 DAS_control 副本用于取消报文;状态机只在未巡航的上升沿锁存 Autopark。输出侧,CarController.update() 在 hands_on_level >= 3 时停用横向,每 2 帧按速度和车辆模型限制目标转角并定期发送允许转向报文;纵向启用时发送加速度,否则仅构造符合取消语义的 DAS_control。get_safety_CP() 以 Model Y 参数匹配固件转角限制。

    雷达接口读取 40 对 A/B 目标消息,要求索引一致,按状态删除旧目标并报告 CAN、遮挡及传感器故障;无物理雷达/未接线时退回基础接口。tesla_checksum() 实现发送帧校验,但编码器不是发送许可。

    层 关键入口 已核对行为
    安全 RX tesla_rx_hook() 从 EPAS、DI、ESP 采样转角、双车速、油门/制动、巡航与手动接管,识别原车 AEB、LKAS 与 Autopark
    安全 TX tesla_tx_hook() Autopark 锁存时拒绝所有候选;转向检查角度/速率/控制类型;原车 LKAS 运行时禁止主机转向
    纵向限制 tesla_tx_hook() 禁止主机 AEB 事件;原车 AEB 运行时拒绝纵向/取消帧;非纵向模式只允许无执行量的取消
    原车转发 tesla_fwd_hook() 按 Autopark、原车 LKAS、原车 AEB 选择性屏蔽相机/Autopilot 总线的转向与纵向报文
    初始化 tesla_init() 构造不同纵向白名单;先假定 Autopark/Summon 激活,直到状态消息澄清

    需要强调的是,tesla_fwd_hook() 的转发是根据状态选择性屏蔽,不能把「拦截原车控制帧」写成一条恒定规则。tesla_init() 启动先假定 Autopark/Summon 激活,是为降低上电时与原车系统争用而设的保守起点。

    Rivian:动态扭矩与原车 ACC 状态取消

    CarInterface._get_params() 选择 rivian 模式,以 0x321 消息是否存在区分 Gen2,并配置扭矩控制。本快照设 radarUnavailable=True;Gen2 自控纵向默认不开放,docs 可影响能力字段。即使软件选择纵向,固件白名单也仅在 ALLOW_DEBUG 接受参数。输入侧 CarState.update() 从 PT、相机、ADAS 三路解析车速、踏板、转向、巡航、AEB 和故障;Gen2 缺部分车门/安全带 CAN 信号,源码说明依赖原车 ACC 处理,不能等同软件已观测到这些状态。另外 cruiseState.speed 的注释说明当前是检测到的限速替代值,不是已核对的目标巡航速度。

    Rivian 会复制 ACM_lkaHbaCmd、Gen1 触摸方向盘帧,并保留每次 VDM_AdasSts 更新;后者可能一轮收到两帧,所以控制器逐帧重发以保留时序。输出侧,CarController.update() 根据速度插值得到扭矩上限,再按驾驶员扭矩限幅;Gen1 可发送触摸方向盘消息。自控纵向生成加速度帧;原车纵向取消时先发送数帧 available 再发 unavailable 状态,让 ACM 接受切换。

    层 关键入口 已核对行为
    参数 _get_params() 以 0x321 区分 Gen2;本快照设雷达不可用
    编码 riviancan.py create_lka_steering()、create_wheel_touch()、create_longitudinal()、create_adas_status() 复用原车字段并重算校验
    雷达 RadarInterface.update() 有目标解析器,但参数层总设不可用,通常走基础接口;只保留 LRR 或 SRR+LRR 模式有效目标
    安全 RX rivian_rx_hook() 从 ESP 与 VDM 双车速检查失配;CRC/质量位函数对特定地址独立验证
    安全 TX rivian_tx_hook() 转向扭矩设速度相关上限、驾驶员抵消及变化率;纵向加速度另设限值
    模式选择 rivian_init() 原车 ACC 与自控纵向使用不同 TX 白名单;纵向选择仅在 ALLOW_DEBUG 编译块读取

    对硬件与插件化重构的四条约束

    1. 车型/固件识别是执行前提。Tesla 的 steering-control 字段代际、Rivian Gen1/Gen2、雷达物理接入和发布/调试固件选择必须进入受审计的能力清单;不能把 alphaLongitudinalAvailable 直接映射为「允许发送」。
    2. 原车系统仲裁不能外包给普通插件。Tesla 的 Autopark、原车 LKAS/AEB 转发优先级和 Rivian 的 ACC 状态取消时序应与板级安全门、主机状态机共同验证,在不明状态下不放行控制。
    3. 传感器数据必须携带来源与有效性。Tesla/Rivian 的双车速、CAN 质量位和雷达模式不是普通数值字段;新硬件要保留总线、时间戳、计数器/校验及故障标志,防止旧值伪装成实时观测。
    4. 发布固件单独验收。这两个模式本身可在注册表中找到,但纵向参数读取受 ALLOW_DEBUG;不能用调试固件跑通的纵向测试宣称发布版可用。

    把两条链路放在一起看,可以发现共性:软件层「能构造帧」与硬件层「允许发送」始终是两件事,而原车辅助系统(Autopark、LKAS、AEB、ACC)的在场状态会随时改变这条边界。跨品牌流程可参考车型协议总览,板级执行可参考 panda 专题。

    常见问题

    Tesla 与 Rivian 都是电动车,为什么不能共用一套插件?

    因为两者控制类型不同:Tesla 采用转角控制并选择性转发原车报文,Rivian 采用动态扭矩并依赖原车 ACC 状态取消。它们的总线拓扑、原车辅助系统与安全策略都不同,源文档明确「不能共用一套电动车插件」,必须按平台分别声明能力。

    为什么代码里有纵向控制,实车却不一定能启用?

    因为两品牌的纵向参数读取都受 ALLOW_DEBUG 限制:Tesla 的 tesla_longitudinal 仅在 ALLOW_DEBUG 读取安全参数,Rivian 的纵向选择也只在 ALLOW_DEBUG 编译块读取。因此不能用调试固件跑通的纵向测试宣称发布版可用。

    Tesla 雷达支持能否只凭车型名判断?

    不能。源文档指出雷达支持需要特定 Continental 雷达、DBC 与物理接入 bus 1,不能只凭车型名推断;Rivian 侧参数层在本快照总设 radarUnavailable=True,即使代码有目标解析器,也不会据此宣称实车雷达链已启用。

    Rivian 的原车纵向取消为什么要先发 available 再发 unavailable?

    源文档说明这是为了让 ACM 接受切换:取消时先发送数帧 available 再发 unavailable 状态。这属于原车系统仲裁时序,应与板级安全门、主机状态机共同验证,而不是当作普通的功能开关。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/TESLA_RIVIAN_DEEP_DIVE.md(取用了 Tesla/Rivian 的参数、输入、输出、安全 RX/TX、转发与初始化行为,以及四条插件化约束)。
    • 项目 02 —— openpilot-research/CAR_PROTOCOL_IMPLEMENTATIONS.md(仅用于确认两品牌目录与跨品牌安全主链路的总体位置)。

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

    更新记录

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

  • Honda 车辆输入:四轮混速与 Bosch/Nidec 巡航分支

    结论:Honda 的 CarState 不直接使用基类的四轮均速 helper:它把四轮 WHEEL_SPEEDS 求均并换算为 m/s,再用 1 到 6 m/s 的插值权重,把低速更依赖的 ENGINE_DATA.XMISSION_SPEED 与轮速混合后送入 KF1D 得到 vEgo/aEgo;standstill 另按变速箱速度小于 1e-5 单独判断。刹车开关信号按源码注释可能含单次噪声,非 BOSCH_ALT_BRAKE 车型要求当前与前值连续两样本同为真。本文为固定源码快照 c8fb906 的静态阅读,未做车型回放、CAN FD 台架或实车验证。

    解析器与总线边界

    动力和相机解析器的物理总线由 CanBus(CP) 指定,不能把 Bus.pt 恒等为 0。有雷达的 Bosch 平台把动力放在偏移加 1、雷达放在偏移;无雷达或 CAN FD 的 Bosch 与 Nidec 采用另一组映射。HAS_BSM 仅由特定 CR-V 指纹分支动态设置,状态层为此另建 Bus.body 解析器,物理输入使用 CanBus(CP).radar,源码注释说明 B-CAN 要先经 panda 转发——缺少这条物理路径时,软件支持 BSM 字段不等于盲区信息可用。

    三个解析器创建时都没有显式报文列表,cp.vl[...] 第一次读取才动态注册报文。由于 CarInterfaceBase.update() 已先调用 parser 更新,新注册报文在第一轮的值与时间戳初始为零,本轮此前到达的帧不会被它解析;此后 vl 保存最近成功值,不保证本轮有新样本。接口对已注册报文做 can_valid 全路聚合、对总线空窗做 bus_timeout 聚合,但若新硬件要以刹车、车速或盲区作为安全条件,仍需逐信号检查来源、时间戳与采样年龄。

    入口 作用
    CarState.__init__() 按变速箱/平台选择报文与 DBC 枚举,初始化刹车开关、仪表速度与巡航缓存
    CarState.update() 把动力、相机与条件性车身总线信号归一化为 CarState
    CarState.get_can_parsers() 建动力/相机解析器,BSM 车型额外建 body 解析器
    CanBus.__init__() 区分有雷达 Bosch 的动力/雷达交换、纵向模式下的 LKAS 路由与其他平台总线
    update_speed_kf() 对 Honda 已混合的 vEgoRaw 做 KF1D 速度/加速度估计
    create_button_events() 按本轮/上轮按钮值构造按下与释放事件

    速度混合与仪表值锁存

    Honda 先把四轮 WHEEL_SPEEDS 平均并换成 m/s,再用 1 到 6 m/s 的插值权重把变速箱速度与轮速混合;CP.wheelSpeedFactor 在这段代码里只乘变速箱速度项,高车速主要由轮速项决定。混合值进入 KF1D 形成 vEgo/aEgo,而 standstill 单独按变速箱速度小于 1e-5 判断,不能简单用 vEgo 是否为零替代。轮速与变速箱速度矛盾时的物理可信度尚需目标车型回放验证。

    CAR_SPEED.ROUGH_CAR_SPEED_2 首次大于阈值后 dash_speed_seen 永久锁存,之后每轮都把当前解析器缓存经 kph/mph 转换写入 vEgoCluster;在锁存前,基类接口用 vEgo 回退显示速度。台湾 Odyssey 特例不走该字段。状态侧没有单独的仪表速度年龄判据,若报文停发,解析器健康与显示缓存需要分别解读。

    车身、转向与刹车

    车门可来自四门 DOORS_STATUS、SCM_BUTTONS 驾驶门位或 SCM_FEEDBACK 驾驶门位,安全带用警告灯或未锁扣任一条件。自动/CVT 档位通过 DBC 枚举,手动挡只由倒车灯在倒车与前进之间二选一,无法在这里识别空挡或所有换挡中间态。方向灯调用拨杆型 250 次调用保持器;驾驶员转向直接以 STEER_TORQUE_SENSOR 绝对值对车型阈值比较,不使用基类去抖。STEER_STATUS 的 DBC 数值先转字符串,未知值映射 UNKNOWN,永久与临时故障有独立映射:BOSCH_ALT_RADAR 对预期低速锁定给例外,MDX 4G 的 TJA_LOW_SPEED_LOCKOUT 还单独清除故障位。

    有 BOSCH_ALT_BRAKE 时刹车直接读 BRAKE_MODULE.BRAKE_PRESSED;其他车型把 POWERTRAIN_DATA.BRAKE_PRESSED 与刹车开关并联,只有本轮 vl_all 有开关样本时才更新开关状态,单轮有多样本则用倒数第二条作为前值,要求当前和前值都为真。若没有新开关帧,brake_switch_active 保持上次状态;解析器健康是另一层,不应把保留布尔值当成新鲜采样。

    巡航、原车 ADAS 与下游

    POWERTRAIN_DATA.ACC_STATUS 和选定 SCM 报文主开关决定巡航 enabled/available。Bosch 原车纵向的设定速度读 ACC_HUD.CRUISE_SPEED,遇大于 160 的特殊值就沿用 v_cruise_pcm_prev;源码说明设置时可能脉冲到 254/255,252 又表示 standstill。带动态单位的 Bosch 平台才在英制时使用 mph 转换,其他情况按 kph;Nidec 则读 CRUISE.CRUISE_SPEED_PCM。这些特殊值是软件解码规则,不是一般范围检查。

    原车 AEB/FCW 来源也分 Bosch 与 Nidec:Bosch 有雷达且未启用自控纵向时才从动力 ACC_CONTROL 判定原车 AEB;Nidec 从相机 BRAKE_COMMAND 判 AEB/FCW,并保留 ACC_HUD 与刹车字段供控制器构帧;Bosch 无雷达车型另保存相机 LKAS_HUD。这些字段是 parser 的最近缓存引用,CarState 未给每个字段附单独采样年龄,因此不能把有 HUD 字典当作原车 AEB 仍可用的证明。按钮事件来自 SCM_BUTTONS 的巡航/设置字段当前与上次值,下游再由车辆事件层决定取消、启用和故障告警。

    信号类别 来源与处理 版本分支
    车速 四轮均速 + 变速箱速度按 1~6 m/s 插值权重混合,随后 KF1D 台湾 Odyssey 不锁存仪表速度字段
    刹车 直接读 BRAKE_MODULE,或与刹车开关并联并要求连续两样本 BOSCH_ALT_BRAKE 走直读分支
    巡航设定速度 Bosch 读 ACC_HUD.CRUISE_SPEED,Nidec 读 CRUISE.CRUISE_SPEED_PCM 大于 160 特殊值沿用上轮;252 表示 standstill
    原车 AEB Bosch 读 ACC_CONTROL,Nidec 读相机 BRAKE_COMMAND Bosch 无雷达保存 LKAS_HUD
    盲区 BSM 车型经 B-CAN 转发后由 body 解析器读取 HAS_BSM 由特定 CR-V 指纹动态设置

    硬件插件验收

    车型输入插件应为轮速四路、变速箱速度、刹车开关与压下、EPS 故障、巡航、原车 AEB 与 BSM 分别声明物理总线、DBC、采样时戳、年龄、校验/计数器结果和缺测状态。速度融合参数、仪表速度锁存和刹车连续样本门应按车型版本冻结并在回放中量化。无车测试至少覆盖第一轮动态注册、单轮异常、速度来源矛盾、刹车短脉冲与断流、ACC 特殊速度码、未知 EPS 状态、BSM 路由缺失及 Bosch/Nidec 分支,再与安全钩子的独立 RX 判据对照。

    常见问题

    Honda 的车速为什么不是简单的四轮平均?

    因为低速时四轮均速不够可靠。CarState 先求四轮均速并换算为 m/s,再用 1 到 6 m/s 的插值权重把变速箱速度 XMISSION_SPEED 与轮速混合,混合值才进入 KF1D 形成 vEgo/aEgo;CP.wheelSpeedFactor 只乘变速箱速度项,高车速主要由轮速项决定。

    为什么刹车开关需要连续两个样本?

    源码注释指出开关信号可能有单次噪声,且驻车挡未必有效。非 BOSCH_ALT_BRAKE 车型把 POWERTRAIN_DATA.BRAKE_PRESSED 与刹车开关并联,只有本轮有开关样本时才更新状态,单轮多样本用倒数第二条作为前值,并要求当前和前值都为真。

    巡航设定速度出现 254/255/252 是什么意思?

    这是软件解码规则而非一般范围检查。Bosch 原车纵向读 ACC_HUD.CRUISE_SPEED,遇大于 160 的特殊值就沿用 v_cruise_pcm_prev;源码说明设置时可能脉冲到 254/255,而 252 又表示 standstill,因此不能按普通数值范围直接采信。

    解析器聚合通过就代表每个信号都新鲜吗?

    不代表。接口只对已注册报文做 can_valid 聚合、对总线空窗做 bus_timeout 聚合,而 vl 保存的是最近成功值,不保证本轮有新样本;CarState 也未给原车 HUD 等字段附单独采样年龄,因此仍要逐信号核对来源与年龄。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/HONDA_CARSTATE_SPEED_BRAKE_CRUISE_INPUT_DEEP_DIVE.md(取用了 Honda 速度混合、刹车去噪、Bosch/Nidec 巡航分支、BSM 路由与插件验收清单)。
    • 项目 02 —— openpilot-research/CAN_PARSER_HEALTH_DEEP_DIVE.md(仅用于确认解析器健康聚合与总线空窗的语义位置)。

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

    更新记录

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

  • openpilot 感知到控制:四条数据流与 100 Hz 闭环

    结论:openpilot 的感知到执行不是一个同步函数调用栈,而是 modeld、radard、plannerd、controlsd 四条进程经消息松耦合的数据流:modeld 随图像帧运行,radard 与 plannerd 以 modelV2 为轮询节拍,controlsd 按 100 Hz 的 Ratekeeper 循环并最多等待 15 ms。同一轮控制里各输入可能来自不同采样时刻,因此不能按顺序函数调用推断一致性。本文为固定源码快照 c8fb906 的静态阅读,未加载模型、未连接车辆。

    闭环与进程时序:四条数据流而不是一条调用栈

    把整条链路理解成「摄像头 → 模型 → 规划 → 控制」的一次顺序调用,是最容易出错的心智模型。源码里 modeld 随图像帧运行,radard 和 plannerd 的 SubMaster 以 modelV2 为轮询主题,controlsd 则按 100 Hz 的 Ratekeeper 循环,以 selfdriveState 为轮询主题并有最多 15 ms 的更新等待。三条速率彼此不同,服务频率的定义见 services.py。

    进程 驱动节拍 主要输入 主要输出
    modeld 随图像帧 VisionIPC 相机流、CarParams、标定、车辆状态、驾驶员监测 modelV2、drivingModelData、cameraOdometry
    radard 轮询 modelV2 carState、radarTracks、modelV2.leadsV3 radarState
    plannerd 轮询 modelV2 modelV2、radarState、carState、CarParams longitudinalPlan、driverAssistance
    controlsd 100 Hz carState、selfdriveState、modelV2、计划 controlsState、carControl

    四者之间没有共享的同步函数栈,消息来自不同采样时刻是常态而非异常。这一点是后面所有边界讨论的前提:讨论「某一帧模型输出是否导致本轮转向」时,必须先确定这些消息是否属于同一次采样。

    感知:modeld 从图像到结构化预测

    main() 等待并连接 VisionIPC 相机流,读取 CarParams、标定、车辆状态与驾驶员监测信息,同步主相机与广角帧,计算图像变换和动作延迟,最后发布 modelV2、drivingModelData、cameraOdometry,并跟踪丢帧比例与相机时间戳。真正的模型装载在 ModelState.__init__():加载 tinygrad 序列化模型与 warp JIT,建立输入输出缓冲和递归状态对;模型文件、设备或编译任一步失败,初始化便无法继续。

    函数或类 读入与处理 输出与边界
    ModelState.pack_inputs() 将变换矩阵、动作条件、图像帧和状态输入装入连续缓冲 为外接 GPU 路径减少上传次数;缓冲布局本身是模型契约的一部分
    ModelState.run() 复制 NV12 帧,按上升沿形成 desire 脉冲,上传输入,执行 warp 与模型,按切片解析输出 大模型路径显式拒绝非有限输出;可选保留原始预测
    Parser.parse_outputs() 对轨迹、车道线、道路边缘、前车、意图和元信息执行 MDN / sigmoid / softmax 解码 结果供消息填充,不是最终车辆执行命令
    get_action_from_model() 从 action 头或 plan 回退提取目标曲率/加速度,按速度与延迟平滑 生成 ModelDataV2.Action:desiredCurvature、desiredAcceleration、shouldStop

    大模型选择逻辑在 main():检测到 Chestnut 后异步加载大模型,等待最多 60 秒,失败或超时改用小模型;大模型运行时异常会记录并切回小模型,小模型异常则继续抛出,交由进程管理层处理。这个回退是运行时选择,不能解释为对任意模型插件的兼容承诺。此外 extrinsics_calibration_seen 随标定数据更新并随模型消息发布,接入新相机时必须保留时间戳、图像几何和有效性语义。

    前车融合与纵向规划:radard 与 plannerd

    radard 不是「只转发雷达」的组件。即使没有雷达匹配,它也可能根据视觉预测产生 radar=False 的前车;反过来,低速场景可能用较近的雷达轨迹覆盖第一前车。Track.update() 按雷达 track ID 更新相对距离、横向位置、相对速度和滤波后的前车速度/加速度;match_vision_to_track() 用距离、横向位置与速度的概率分数选择轨迹;get_lead() 优先视觉与雷达匹配,无匹配但视觉概率足够时使用视觉前车。RadarD.update() 对齐自车速度与雷达延迟,结合 modelV2.leadsV3 形成 leadOne / leadTwo,RadarD.publish() 发布 radarState,有效位取自输入主题检查结果。

    plannerd.main() 等待 CarParams,以 modelV2 为节拍更新纵向计划与车道偏离警告,分别发布 longitudinalPlan 与 driverAssistance。LongitudinalMpc.update() 依据两辆前车和驾驶风格设定跟车时距、障碍距离及约束,运行 acados MPC;run() 调求解器保存速度、加速度与 jerk 轨迹,非零求解状态会记录告警并 reset(),此时该轨迹不能当成成功解。LongitudinalPlanner.update() 从 MPC、巡航及实验模式的端到端动作候选中选择最小加速度作为目标,shouldStop 对各候选取逻辑或。

    值得注意的是,所选加速度与 shouldStop 不一定来自同一候选,这种「最小加速度」合成是偏保守的候选合成,但不等于独立安全证明;求解器失败、输入无效、车型加速度能力和实际制动执行仍分别由其他层处理。车道偏离警告由 LaneDepartureWarning.update() 在高车速、近 5 秒未打转向灯且横向控制不活动时,结合车道线置信度、距离与变道意图触发,它不是转向控制命令。

    controlsd:从计划到 carControl

    Controls.__init__() 等待 CarParams,实例化车型接口、LongControl 与车辆动力学模型,并依转向控制类型或车型调参选择横向控制器;Controls.run() 以 100 Hz 调用 update()、state_control()、publish()。state_control() 计算使能、横向与纵向活动条件,执行纵向 PID 与横向控制,对目标曲率限幅,并将非有限执行器字段清零。

    横向实现 算法要点 边界
    角度接口 LatControlAngle.update() 用车辆模型把目标曲率转为目标转角,加入学习角偏置 执行器变化率仍由车型控制器与 panda 策略约束
    曲率接口 LatControlCurvature.update() 从当前转角估计曲率,配置 PID 时做误差闭环,否则前馈 输出限到 MAX_CURVATURE 的 PID 范围,不能当扭矩
    转角 PID LatControlPID.update() 以目标/当前转角误差闭环 安全限幅、驾驶员施力或低速时冻结积分
    扭矩接口 LatControlTorque.update() 在横向加速度域闭环,补偿摩擦、坡度与响应延迟 使用 1 秒目标加速度环形缓冲与 jerk 滤波

    state_control() 在 lateralManeuverPlan 有效时优先取其目标曲率,否则用 modelV2.action;停止横向活动时把目标重置为当前曲率以避免使能瞬间跳变。纵向 LongControl.update() 有 off / stopping / pid 三种状态,根据制动、巡航静止与 shouldStop 切换。需要特别注意的是,carControl 与 controlsState 的有效位当前仅赋值为 CS.canValid,源码注释指出若改为 sm.all_checks() 会形成循环依赖——因此消息有效位不能被误读为所有模型、计划与姿态输入都有效,下游状态机和 panda 安全门仍必须独立检查。

    插件化时的接口边界

    如果要按插件方式替换上述任一层,至少要保留该层的输入契约与有效性语义,而不是只替换一个数值输出。

    可替换层 至少保留的契约 不应开放的能力
    感知模型 VisionIPC 帧时间戳、相机标定、输出 schema、有效性/丢帧/延迟、启动预热与回退 直接发送车辆 CAN
    雷达融合 radarTracks 与 modelV2 对齐、leadOne/leadTwo 来源标志、错误与时延 绕过状态机强制使能
    规划器 longitudinalPlan 的轨迹、aTarget、停止/油门标志、求解状态和超时 把非成功解当成正常计划
    横纵向控制器 CarParams 能力、活动条件、饱和反馈、非有限数处理、控制频率 修改 panda 安全模式或白名单

    可迁移的路径是先建立消息契约与进程回放基线,再逐层替换算法实现。实车输出仍经 card 的车型接口和独立 panda 固件安全钩子,因此插件层的正确性并不等于执行链的完整性。

    常见问题

    为什么不能把 modelV2 到 carControl 当成一次函数调用?

    因为各进程速率不同:modeld 随图像帧运行,radard 与 plannerd 以 modelV2 为轮询主题,controlsd 按 100 Hz 循环。消息可能来自不同采样时刻,不存在保证它们同属一次采样的同步栈。

    radard 是雷达数据的转发器吗?

    不是。即使没有雷达匹配,也可能根据视觉预测产生 radar=False 的前车;低速场景还可能用较近的雷达轨迹覆盖第一前车。插件化时必须保留 track ID、延迟、误差和 present/radar 语义。

    carControl.valid 为真就代表所有输入都有效吗?

    不代表。当前 carControl 与 controlsState 的有效位仅取 CS.canValid;源码注释指出改成 sm.all_checks() 会形成循环依赖。因此它不能替代对模型、计划与姿态输入的独立检查。

    大模型加载失败会怎样?

    检测到 Chestnut 后异步加载大模型并等待最多 60 秒,失败或超时改用小模型;大模型运行时异常会记录并切回小模型,小模型异常则继续抛出,由进程管理层处理。这是运行时选择,不是对任意模型插件的兼容承诺。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/PERCEPTION_CONTROL_DEEP_DIVE.md(取用了四条进程数据流、modeld 感知链、radard 融合、纵向 MPC 与 controlsd 输出,以及插件化接口边界)。
    • 项目 02 —— openpilot-research/CONTROL_ALGORITHMS_DEEP_DIVE.md(仅用于确认四种横向控制器与纵向执行状态的分层位置)。

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

    更新记录

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

  • 8 个扭矩桶、4000 点门槛:横向扭矩在线估计与三层「可用」

    结论:横向扭矩响应的在线估计把样本分进八个扭矩桶,用全最小二乘拟合「扭矩—横向加速度」的斜率与偏置,并以残差离散度近似摩擦。但它对外暴露的「可用」其实是三层不同的门:顶层消息有效位来自输入通信检查,内层参数有效位要求各桶达到 [100, 300, 500, 500, 500, 500, 300, 100] 的最小点数且总数达到 4000,「是否使用参数」则只是品牌白名单与调校类型门。三者互不推导,控制侧甚至在未检查内层有效位的情况下就可能使用估计值。

    采样门:从健康位到扭矩桶

    采样横向加速度按「车速 × 校准偏航率 − sin(滚转) × 重力」计算。进入桶之前要逐层过关:上游各健康位全真、扭矩历史缓冲填满、横向控制已连续激活并稳定 2 秒且驾驶员未覆盖、车速大于 15 m/s、扭矩绝对值大于 0.02、横向加速度绝对值不超过 1。入桶时扭矩值还要落在八个开右端点的 [−0.5, 0.5) 区间之一,区间外的点会被静默忽略。

    门 条件 备注
    健康位 / 历史缓冲 全真且历史填满 前置门,不满足则不入桶
    激活稳定性 连续激活 2 秒、无驾驶员覆盖 —
    车速 >15 m/s 严格大于
    扭矩绝对值 >0.02 严格大于
    横向加速度 ≤1 m/s² 超限不入正常桶

    有个容易被忽略的边界:给车控、执行输出与车况的时间戳加的是「当时」的延迟,而设备观测用的是消息内部时间戳。若延迟被置成负值,用于插值的时间窗可能为空;由于对空序列的判断特性,即便激活历史全假、驾驶员覆盖全真,样本也可能被放行。正常延迟名义上是正的,这只是一个「函数本身没有拒绝异常延迟」的说明,并不代表生产环境会收到负延迟。

    八个桶与 4000 点门槛

    「能算」和「算得可信」是两个不同的门:只要每个桶里至少有一个点,就可以先输出原始拟合;但要标记为有效,各桶必须达到各自的最小点数,且总点数不少于 4000。进度百分比取「总量完成率」与「最低桶完成率」的平均后取整——这意味着某些桶偏少时,整体进度会被拉低。

    桶(扭矩区间,左闭右开) 最小点数
    第 1 桶 100
    第 2 桶 300
    第 3 ~ 6 桶 各 500
    第 7 桶 300
    第 8 桶 100
    合计 ≥4000

    全最小二乘与负斜率的坑

    拟合在「扭矩、常数 1、横向加速度」组成的点阵上做全最小二乘(SVD),把样本随机抽到最多 2000 点,只捕获线性代数异常。斜率与摩擦分别被约束在离线调校的 ±30% 与 ±50% 之内,偏置没有同类限幅。合成验证给出一个值得记住的结论:当斜率为负时(例如拟合 y = −2x + 0.5),斜率本身能被正确恢复,但摩擦却显著非零——原因是把斜率转成旋转角时用了平方根,结果恒为非负,负斜率下的残差没有沿直线方向被消去。这是合成负斜率分支的源码/数值结论,并不证明正常车型会出现负扭矩因子。

    数值健壮性还有一处边界:发布与滤波前只用「空值 / 非数」判断拟合结果,没有显式检查正负无穷;在合成输入下,偏置为无穷仍可能带着内层有效位进入滤波。这提示序列化与滤波层都需要独立的有限值校验。

    三层「可用」不是同一门

    层 来源 实际含义
    顶层消息有效 输入主题的通信/频率/有效性检查 不表示拟合已经成熟
    内层参数有效 桶分布与数量达标、拟合非空非非数 估计是否成熟
    是否使用参数 品牌白名单 + 调校类型 与样本量、内层有效位无关

    关键消费事实是:控制侧在扭矩车型上只要求「主题通信健康」与「是否使用参数」为真,就调用参数更新,没有检查内层有效位。因此未完成新标定时,也可能使用初始离线值或历史滤波值——不能把「内层无效」误读为「控制器必然停用」。滤波本身按一阶方式更新,衰减时间从 50 持续增大到最多 250;一次更新里会先用旧系数更新滤波值,再用增大后的衰减重算系数。整个过程最终仍要经过车辆接口与 panda 安全策略,估计插件不得直接发送 CAN。

    持久化与恢复的边界

    缓存键是持久参数。恢复时先核对车型指纹与恢复键:匹配且内层有效时恢复三个滤波值与缓存点;兼容但内层无效时,仍会加载缓存点并恢复衰减值。恢复不是事务式提交——一旦解析中途异常,内存中已经装载的部分状态可能保留,而已写入的缓存键会被删除,形成一个「条件性部分恢复」的风险;车型指纹或版本不匹配时则跳过缓存但不删除键。

    插件化边界

    做插件化时,扭矩估计器宜先作为只读影子实例,输出独立候选主题,控制端只允许单一权威发布者。插件契约需要显式携带车型/调校版本、延迟时间语义、坐标系、采样覆盖率、顶层通信有效位、内层拟合有效位与输出年龄。切换为权威源前必须证明缓存恢复、缺失或异常输入、横向激活与驾驶员覆盖门以及回放等价性,且不得绕开既有安全许可。

    常见问题

    每个扭矩桶里有点,就等于参数可用了吗?

    不等于。「能算」只要求每个桶非空,可以先输出原始拟合;「可信」还要求各桶达到 100/300/500 的最小点数且总数达到 4000,才会把内层参数有效位标为真并更新滤波值。

    从缓存恢复的估计点一定是本次标定的吗?

    不一定。兼容时无论内层有效位真假,都会加载缓存点并恢复衰减值;缓存恢复不是事务式提交,解析中途异常可能留下部分内存状态,因此只能视为条件性部分恢复。

    为什么负斜率的摩擦估计会明显偏大?

    因为把斜率转成旋转角时使用了平方根,结果恒为非负,负斜率下的残差没有沿直线方向被消去,于是垂直残差偏大、摩擦被高估。这是合成负斜率分支的结论,不证明正常车型会出现负扭矩因子。

    内层参数标记为无效,控制器会不会停用估计值?

    不会仅凭这一点停用。控制侧只检查主题通信健康与「是否使用参数」,不检查内层有效位,因此未完成新标定时仍可能使用初始离线值或历史滤波值;最终输出还要经过车辆接口与安全策略。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/TORQUED_LATERAL_TORQUE_LEARNING_DEEP_DIVE.md(采样门、八个扭矩桶与 4000 点门槛、全最小二乘与滤波、三层有效位、持久化与插件边界)

    说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有桶阈值、点数门槛与配置均出自源文档,标注为未验证的结论保留其原有边界;合成点拟合结论不代表实车扭矩响应。

    更新记录

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

  • Hyundai 与 Honda 车型协议:为什么品牌目录不等于通用协议

    结论:品牌目录不是通用协议。Hyundai 的经典 CAN 可选 hyundai/hyundaiLegacy、CAN FD 用 hyundaiCanfd;Honda 分 Nidec 与 Bosch,Bosch 还要再分雷达版/无雷达版及 CAN FD。它们是不同的能力组合,必须声明精确平台、固件范围与总线拓扑。CarController 的软件限幅不替代 panda 安全钩子,CarState 的解析结果也不替代固件对驾驶员扭矩、制动和巡航状态的独立采样。

    Hyundai:经典 CAN 与 CAN FD

    Hyundai 按 CANFD 分支:用相机和 E-CAN 指纹判别 LKA/LFA、按钮、档位及 SCC 来源,LONG、能源类型和限制参数写入 safetyParam。这里要记住:alphaLongitudinalAvailable 与 openpilotLongitudinalControl 分开,前者不能保证后者启用。

    层 关键入口 已核对的实现
    ECU 生命周期 init()、deinit() 自控纵向且非相机 SCC 时发送 UDS 通信控制,目标地址依平台/转向架构改变;退出尝试恢复。
    输入 CarState.update()、update_canfd() 分别把经典 CAN/CAN FD 的速度、驾驶员输入、巡航、转向、车门等信号映射到统一 CarState。
    输出调度 CarController.update() 经典 CAN 与 CAN FD 分流至 create_can_msgs()/create_canfd_msgs()。
    构帧 hyundaican.py、hyundaicanfd.py 经典 CAN 的 LKAS11/CLU11/SCC 帧与 CAN FD 的转向、取消、ACC、LFA 抑制帧分别构造;CAN FD 的 CanBus 映射 E-CAN、A-CAN、相机总线,不能硬编码单一 bus。
    固件接收/发送 hyundai_rx_hook()、hyundai_tx_hook()、hyundai_canfd_tx_hook() 两条分支检查转向扭矩/请求间隔、加速度及按钮权限,诊断地址仅准许指定 tester-present 载荷。
    模式组装 hyundai_init()、hyundai_legacy_init()、hyundai_canfd_init() Legacy 因旧报文缺计数器/校验而采用不同接收检查,不能视作安全规则全等。

    Honda:Nidec 与 Bosch

    Honda 的 Bosch 用 hondaBosch、Nidec 用 hondaNidec;Bosch 继续区分雷达版、无雷达版及 CAN FD。Bosch CAN FD 在此快照中不开放实验纵向。

    层 关键入口 已核对的实现
    ECU 生命周期 init()、deinit() 有雷达的 Bosch 自控纵向会尝试禁用雷达 ECU 通信,退出时尝试恢复。源码明确警告此操作会禁用原车 AEB。
    输出调度 CarController.update()、compute_gas_brake() 区分 Bosch/Nidec;actuator_hysteresis() 与 brake_pump_hysteresis() 处理 Nidec 制动输出。
    构帧 hondacan.py 按平台总线构帧;CanBus 映射 PT、radar、camera 等总线。
    固件接收 honda_rx_hook() 备用刹车报文 0x1BE 避免把某些车型电脑制动误判为用户踩刹车;Nidec 的原车 AEB 制动与 openpilot 制动比较后决定转发。
    模式与转发 honda_nidec_init()、honda_bosch_init()、honda_nidec_fwd_hook() Nidec 对相机制动帧 0x1FA 进行动态转发;Bosch 实验纵向在固件中受 ALLOW_DEBUG 编译门限制,不能只看 Python 配置推断可用。

    另外两处细节也影响拆分:Hyundai 侧,经典 CAN 可选 hyundai/hyundaiLegacy,CAN FD 转向灯能力还可能涉及额外 ECU;Legacy 因旧报文缺计数器/校验而采用不同接收检查,测试入口覆盖相机 SCC、Legacy、能源类型、纵向、AEB 位、按钮和诊断场景,但不等于覆盖所有实车变种。

    Honda 侧,honda_tx_hook() 对 Nidec 检查制动/HUD,对 Bosch 检查加速度/油门,均阻止主机发送 AEB 执行位;无使能时转向只能为零,Bosch 按钮只能取消,诊断地址仅准许指定 tester-present。备用刹车报文 0x1BE 用于避免把某些车型电脑制动误判为用户踩刹车。

    Hyundai 的固件发送侧还要区分纵向状态:hyundai_canfd_tx_hook() 在非自控纵向时仅允许符合取消语义的 ACC 帧,经典 CAN 则另检查 FCA11/SCC12 的 AEB 执行位。Honda 侧的转发也不对称:honda_nidec_fwd_hook() 对相机制动帧 0x1FA 动态转发,而 Bosch 实验纵向在固件中受 ALLOW_DEBUG 编译门限制,不能只看 Python 配置推断可用。

    为什么必须按能力组合拆分

    同一品牌下,CAN/CAN FD 与 Nidec/Bosch 是不同能力组合,不能互相套用。Honda 的原车 AEB 保留与否依硬件架构和具体纵向方案而变:Nidec 用相机制动帧的动态转发,而 Bosch 雷达禁用方案是另一条路径,不能把 Nidec 转发逻辑套到 Bosch 雷达禁用方案。Hyundai 侧,hyundai_canfd_tx_hook() 中的转向限值还包含驾驶员扭矩抵消、速率与 EPS 转向请求切断容差;这些是固件拒绝候选帧的条件,不应在插件层开放为任意可改配置。

    换句话说,能力组合必须由「平台 + 固件范围 + 总线拓扑 + 传感器架构」共同定义,任何一项缺失都可能把另一套组合的结论错误套用过来;这也解释了为何同品牌不同年款需要各自的证据链。

    车载硬件与插件化含义

    1. 车型插件应声明精确平台、固件范围、总线拓扑、DBC、能力与安全模式参数;同一品牌的 CAN/CAN FD 或 Nidec/Bosch 必须是不同能力组合。不能让插件动态改写 panda 固件的 TX 白名单和物理限制。
    2. ECU 通信控制必须是受控生命周期操作,记录请求、响应、超时及恢复结果。尤其 Honda Bosch 雷达禁用涉及原车 AEB;目标硬件设计应先明确是否允许此工作模式,而不是默认启用。
    3. 硬件网关需要保持总线来源与时间戳、独立看门狗、断电/主机失联回退、板上 RX/TX/转发安全门;E-CAN/PT/camera 总线映射错误是安全风险,不是普通配置瑕疵。
    4. 每个目标车型建立指纹和固件识别、DBC 解析/打包、安全测试、日志回放、断线与错误帧注入、SIL/HIL、封闭场地验证的证据链。未知或模糊匹配保持被动,不以品牌名放行执行。

    常见问题

    为什么同一品牌要按经典 CAN 与 CAN FD 分开?

    因为两者有不同的输入映射、构帧函数与安全模式:Hyundai 的经典 CAN 可选 hyundai/hyundaiLegacy、CAN FD 用 hyundaiCanfd;Legacy 还因旧报文缺计数器/校验而采用不同接收检查。它们是不同能力组合,不能视作同一套安全规则。

    Honda 禁用雷达 ECU 通信有什么代价?

    源码明确警告:有雷达的 Bosch 自控纵向禁用雷达 ECU 通信的操作会禁用原车 AEB。因此不能把它当作无副作用的模式切换,目标硬件设计应先明确是否允许此工作模式。

    Bosch 的实验纵向能在固件里开放吗?

    在固件中受 ALLOW_DEBUG 编译门限制,不能只看 Python 配置推断可用;此外 Bosch CAN FD 在此快照中不开放实验纵向。

    Nidec 的相机制动帧转发能套用到 Bosch 吗?

    不能。Honda 的原车 AEB 保留与否依硬件架构和具体纵向方案而变;Nidec 对相机制动帧 0x1FA 的动态转发与 Bosch 雷达禁用方案是不同路径,不能互相套用。

    延伸阅读

    参考资料

    • 项目 02 —— openpilot-research/HYUNDAI_HONDA_DEEP_DIVE.md(取用了 Hyundai 经典/CAN FD 链路、Honda Nidec/Bosch 链路,以及车载硬件与插件化含义)。
    • 项目 02 —— openpilot-research/HYUNDAI_CANFD_PANDA_SAFETY_DEEP_DIVE.md(仅用于确认 CAN FD 逐模式 RX/TX 总线矩阵与纵向构建门的引用位置)。

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

    更新记录

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