结论:VehicleModel 是主机上的纯计算模型,不是独立的安全许可,它的两个数字最容易被误用。第一是速度下限 0.1:steady_state_sol() 在 u > 0.1 时选动态解,否则回退到运动学解,而动力学矩阵 A 含 1/u,不能把零速送入动态分支。第二是 1e-6:roll_compensation() 在 abs(sf) < 1e-6 时直接给零,否则按 g*roll/(1/sf - u²) 计算。此外 panda 固件的曲率因子不包含 Python 侧的 chi 或横滚补偿,两端不能宣称逐点数学等价。以下基于固定快照 c8fb906 的静态源码分析,未做实车动力学辨识或 CAN 发送验证。
输入、状态与公式
VehicleModel.__init__() 从 CarParams 保存质量 m、转动惯量 j、轴距 l、质心到前后轴距离 aF/aR、后轮转向比 chi 以及前后原始侧偏刚度。update_params() 仅把原始刚度乘以在线 stiffness_factor,并替换方向盘转向比 sR,不会累乘上次结果。
三条核心公式:calc_slip_factor() 算 sf = m(cF*aF − cR*aR) / (l²*cF*cR);curvature_factor() 给出 k = (1 − chi) / (l * (1 − sf*u²));calc_curvature() 返回 k*sa/sR + roll_compensation,其中 sa 为方向盘弧度、u 为 m/s,输出单位为 1/m。get_steer_from_curvature() 先扣除横滚补偿再除以 k,不是简单以轴距乘曲率。
| 函数 | 作用 | 输入与边界 |
|---|---|---|
steady_state_sol() |
速度 u>0.1 时选动态解,否则选运动学解 |
返回 [侧向速度, 横摆角速度];低速分支不使用 roll。 |
kin_ss_sol() |
低速按轴距、后轴到质心距离、转向比得线性解 | 即使 u=0 也返回零;不显式使用 chi。 |
create_dyn_state_matrices() |
构造状态 [侧向速度, 横摆角速度]、输入 [方向盘角, 横滚角] 的 2×2 A/B |
A 含 1/u,不能把零速送入动态分支。 |
dyn_ss_sol() |
用 −solve(A, B) @ [sa, roll] 求稳态 |
solve 可能因参数退化或矩阵奇异失败,函数本身不做回退。 |
get_steer_from_yaw_rate() |
先以 yaw_rate/u 求目标曲率,再反算方向盘角 |
u=0 无保护;与低速运动学回退是两条不同 API。 |
低速回退与奇异点约束
模型里有两个需要调用方自己守住的数值边界。其一是低速回退:u=0 时动态分支不可用,必须走运动学近似,且该近似即使 u=0 也返回零。其二是横滚补偿的奇异点:roll_compensation() 在 abs(sf) < 1e-6 时直接给零,避开分母 1/sf − u² 的退化。
| 边界量 | 取值 | 作用 |
|---|---|---|
| 动态/运动学切换 | u>0.1 |
决定用动态解还是低速运动学解。 |
| 侧偏因子近零门 | abs(sf)<1e-6 |
横滚补偿直接给零,避开奇异分母。 |
| 在线更新下限 | 0.1 | controlsd 更新模型时取的下限,仅防非正值。 |
| 角度限幅最小速度 | 1 m/s | apply_steer_angle_limits_vm() 里原始速度至少取 1 m/s。 |
需要注意的是,controlsd 从 vehicleParameters 读取估计值并各自以 0.1 为下限更新模型,这个下限仅防止非正值,不能证明估计值物理可信。上游 paramsd 发布时另带有效性字段,但该调用点没有以这些字段阻止更新。
运行调用链与安全边界
paramsd 发布估计的转向比、刚度、横滚和转角偏移,controlsd 每周期更新模型,再用转角减偏移估算实际曲率(此处曲率加负号以匹配控制层符号约定)。LatControlCurvature.update() 用模型曲率构成跟踪误差;LatControlTorque.update() 将其乘 vEgo² 作为横向加速度测量,并用模型把方向盘死区换算为加速度死区。模型误差会传播到控制反馈,但模型本身不决定 latActive。
apply_steer_angle_limits_vm() 用反算方向盘角把横向 jerk/加速度上限转换为主机角度限幅;Tesla 与 BYD 的控制器各用固定安全车型参数实例化独立模型,避免把 controlsd 在线估计参数直接当作固件许可条件。
主机与固件的差异
panda 的 steer_angle_cmd_checks_vm() 在固件内独立计算角度/加速度/jerk 门;其 get_curvature_factor() 与 Python 基式相近,但采用固件给定 slip_factor/轴距/转向比,不包含 Python 的 chi 或 roll_compensation,还使用固件车速与额外容差。因此不能声称两端逐点数学等价。
| 项 | Python 主机模型 | panda 固件 |
|---|---|---|
后轮转向比 chi |
参与 curvature_factor() |
不包含 |
| 横滚补偿 | roll_compensation() 参与 |
不包含 |
| 侧偏因子/轴距/转向比 | 来自 CarParams 或在线估计 |
固件给定值 |
| 车速来源与容差 | 主机估计 | 固件车速加额外容差 |
joystickd_thread() 也会用模型估算测试转角幅度,这不代表绕过车端许可。车辆模型可作为纯计算插件提供曲率、横摆角速度及反算接口,但应在边界明确参数来源、单位、版本、速度有效域和非有限值处理;在线估计值应视为观测数据而非授权数据,主机控制器与 panda 固件必须绑定同一车辆身份和经校准的安全参数集,版本失配时禁用执行。
现有验证与缺口
现有 test_vehicle_model.py 以 Honda Civic 参数验证 1–30 m/s 下的横摆角速度往返、动态稳态解与直接公式一致,以及一秒离散仿真逼近稳态;它没有覆盖 u=0 反算、参数退化、在线更新突变、固件与 Python 跨实现一致性或实车传感器误差。本专题只做静态源码核查,未运行测试或实车验证。下一阶段宜先在隔离仿真中加入零速/奇异点、参数边界、符号一致性和主机-固件限幅差异矩阵,再讨论车端实验。
常见问题
为什么 0.1 这个速度下限会出现在模型里?
因为动态状态矩阵 A 含 1/u,steady_state_sol() 在 u > 0.1 时才选动态解、否则回退运动学解;controlsd 更新模型时也取 0.1 为下限,但那只是防止非正值,不能证明估计值物理可信。
roll_compensation() 在什么情况下直接返回零?
当 abs(sf) < 1e-6 时直接给零,否则按 g*roll/(1/sf − u²) 计算。这个门槛是为了避开分母退化,属于奇异点约束。
主机模型和 panda 固件算的曲率一样吗?
不一样,也不能宣称逐点等价。固件的 get_curvature_factor() 与 Python 基式相近,但采用固件给定的 slip_factor/轴距/转向比,不包含 Python 的 chi 或 roll_compensation,还使用固件车速与额外容差。
VehicleModel 是一个转向安全许可吗?
不是。它是主机上的纯计算模型,不决定 latActive;候选转角经宿主限幅后仍须由不可被普通插件替换的独立固件安全门检查。在线估计值应视为观测数据而非授权数据。
延伸阅读
- 横向目标与执行 —— 承接模型曲率如何进入横向执行链。
- paramsd 车辆参数学习 —— 在线估计转向比、刚度与横滚的来源与有效性字段。
- torqued 横向扭矩学习 —— 对照扭矩控制链对模型量的依赖。
- lagd 横向延迟估计 —— 同类在隔离仿真中先建差异矩阵的验证思路。
- 从使能到车轮:执行链上的八道门 —— 承接宿主限幅与固件安全门相互独立的设计。
参考资料
- 项目 02 ——
openpilot-research/VEHICLE_MODEL_DYNAMICS_CONTROL_SAFETY_CONTRACT_DEEP_DIVE.md(取用了输入与公式、关键函数索引、低速回退与 1e-6 奇异点、运行调用链、主机与固件差异以及现有测试缺口)。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复