横向限幅四种算法:主机候选与 panda 判决为什么不是一件事

作者:

在

结论:横向限幅分成扭矩、转角、曲率三条路径,共享默认横向加速度约 3.6 m/s²、jerk 约 3.6 m/s³(含道路横滚容差)。它们只是主机侧的候选值计算,不能等同于 panda 的发送许可;同一场景下主机函数输出并不保证每个瞬时输入同时满足加速度与 jerk 两项物理限值。插件可以输出带单位的候选扭矩/角度/曲率,但固件必须保留独立最终许可。

三类配置与四条算法路径

AngleSteeringLimits、AngleSteeringLimitsVM、CurvatureSteeringLimits 分别对应速度表转角、车辆模型转角和曲率。它们的共同特点是:配置类不检查车型身份、传感器健康或 CAN 权限,具体值由车型提供。下面是四条主要路径:

入口 主机行为 主要消费者
apply_driver_steer_torque_limits() 据驾驶员力矩与 allowance/factor/multiplier 算上下限,再对上次请求作非对称上升/下降速率限制,最后取整 Hyundai、GM、Subaru、Mazda、MG、Rivian、Volkswagen 等扭矩车型
apply_dist_to_meas_limits() / apply_meas_steer_torque_limits() 先约束命令与实测量的误差带和绝对上限,再按上次请求限制每帧变化;误差带以 STEER_ERROR_MAX 给零附近保留范围 Toyota、Chrysler 等 EPS 电机力矩测量
apply_std_steer_angle_limits() 按请求是否同向增大绝对值,在两张车速-角速度表间选择;插值后限单步变化,停用横向时改用实测转角,再限绝对角度 Nissan、PSA、Toyota 角度路径
apply_steer_angle_limits_vm() 用 MAX_LATERAL_JERK/v² 反算每次可变转角,用 MAX_LATERAL_ACCEL/v² 反算绝对角;车速至少取 1 m/s Tesla、BYD 以固定安全车型参数建模

注意 apply_driver_steer_torque_limits() 里的 steer_max 可动态覆盖静态上限。VM 路径还会用 STEER_STEP*DT_CTRL 将角速度换算为每次发送增量——调用周期由车型控制器决定,不是函数自己假设的常量。

非扭矩路径的数值顺序

曲率路径 CurvatureSteeringLimits.apply_limits() 的裁剪顺序固定,且这个顺序会产生反直觉的结果:

步骤 动作
1 按 a_max/v² 限曲率
2 按 jerk_max/v² * steer_step*DT_CTRL 限相对上次请求的增量
3 停用时以调用方传入的当前曲率替换
4 最后限固定 CURVATURE_MAX

当上次命令在当前车速下已经超出新的加速度界限时,jerk 区间可能与加速度区间不相交;后一道 np.clip 会把先前的加速度限值「拉回」上次命令附近。源文档给了一个可复算的例子:用 Ford 量级的 CURVATURE_MAX=0.02 1/m、MAX_LATERAL_ACCEL=MAX_LATERAL_JERK=3、车速 20 m/s、上次与本次请求均为 0.02 1/m、steer_step=1,本地原函数输出 0.019925 1/m——它高于当前加速度导出的 3/20²=0.0075 1/m,但仍低于固定绝对曲率上限。

这说明了什么?主机此函数不保证每个瞬时输入同时满足两项物理限值。源文档很克制地补了一句:这并非证明真实 Ford 配置或 panda 固件会放行该帧;实际车型的速度、加速度配置、帧步长及固件独立检查需另核对。停用时的行为也值得记住:曲率和两种转角函数先前的动态限幅结果会被当前测量值替换,再只受最终绝对上限约束。

主机不等于固件:同一场景的 safety helper 对照

板端 steer_curvature_cmd_checks() 对启用的曲率命令分别 OR 累积固定绝对曲率、按板端车速推导的横向加速度、每帧 jerk 和实时发报频率等违例——加速度检查不会被后续 jerk 检查覆盖。这与主机的顺序差异,正是「主机候选」和「固件判决」不是同一件事的根源。

Ford FORD_STEERING_LIMITS 将 0.02 1/m 映射为 1000 CAN 单位,20 Hz 发报;主机 CarController 每五个 100 Hz 周期调用一次共享曲率限幅。跨层探针的对照结果是:车速 20 m/s、上次曲率 0.02 1/m 的候选值被拒;同速度下界内的 0.008 1/m 候选通过。源文档强调这是共享函数与独立板端 helper 的对照证据,不是实际整车拒帧率或驾驶性能证明。

辅助逻辑:故障规避与摩擦前馈

common_fault_avoidance() 只在连续 request && fault_condition 时累计;超过阈值暂时拉低请求位,到 max_above_limit_frames+max_mismatching_frames 重置计数。Hyundai 的大转角、Toyota/Subaru 的高转角速度等复用此逻辑。它有两条边界:返回的是新计数和请求位,不清除同帧扭矩,也不自行核验 MCU 对请求位不匹配的容忍度。

摩擦侧,apply_center_deadzone() 在死区内把误差归零,get_friction() 在 +/-FRICTION_THRESHOLD 之间线性插值摩擦前馈、区间外饱和。摩擦输出按 friction*latAccelFactor 缩放,不是原始扭矩命令——把它当作扭矩值会算错整整一个量纲。

插件与硬件建议

源文档给出的插件契约是:把横向算法插件限制为输出带单位的候选扭矩/角度/曲率;宿主负责车型身份和参数绑定、消息新鲜度、活动门及主机限幅,panda 固件保留独立最终许可。每个参数包应带车型、总线布局、固件安全模式、版本和发帧周期,拒绝跨车型或不完整配置;动态标定不能自动改变固件许可。

在验证缺口上,test_lateral_limits.py 只对非 mock、非 notCar 的扭矩平台做静态估算,且明确跳过非扭矩平台,不直接执行限幅函数,也不验证 CAN 帧或 panda 判决;CarController.update() 全路径、NaN、真实车辆 CAN、目标固件和实车反馈均尚未验证。硬件验证的顺序应是:离线回放/隔离台架比较主机候选值与固件判决,覆盖驾驶员抢方向、车速跳变、实测量失联、请求位切断以及停用瞬间,再讨论受控场地测试。

常见问题

主机限幅通过就等于车辆会执行吗?

不等于。主机函数只产生候选值;steer_torque_cmd_checks() 与 steer_curvature_cmd_checks() 在 panda 内另有状态化检查(固件车速、双车速差、曲率绝对值、加速度、jerk、实测误差等)。源文档明确主机函数不是这些状态化检查的替身。

为什么曲率限幅可能不满足加速度门?

因为 CurvatureSteeringLimits.apply_limits() 的顺序是加速度 → 相对上次命令的 jerk → 停用时实测曲率 → 固定绝对曲率。当上次命令已超出新的加速度界限时,后一道 np.clip 会把先前的加速度限值拉回上次命令附近,因此不保证每个瞬时输入同时满足两项限值。

3.6 m/s² 和 3.6 m/s³ 是通用限制吗?

它们是三类配置共享的默认量级(含道路横滚容差),但具体值由车型提供。例如 VM 路径用固定安全车型参数,Ford 量级例子用的是 CURVATURE_MAX=0.02 1/m 与 MAX_LATERAL_ACCEL=MAX_LATERAL_JERK=3。所以不能把它当成跨车型的通用上限。

摩擦前馈的输出是扭矩命令吗?

不是。get_friction() 的输出按 friction*latAccelFactor 缩放,由 LatControlTorque.update() 消费,不是原始扭矩命令。混淆两者的单位会得到错误结果。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/LATERAL_LIMITS_HOST_SAFETY_PLUGIN_CONTRACT_DEEP_DIVE.md(取用了四条算法路径、曲率裁剪顺序与数值例子、板端 helper 对照与插件建议)。
  • 项目 02 —— openpilot-research/CAR_PROTOCOL_IMPLEMENTATIONS.md(仅用于确认 safety_tx_hook 与 CANPacker 在横向候选之后的分工)。

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

更新记录

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

评论

发表回复

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