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

作者:

在

结论:横向的四种控制器不共享相同单位——角度、曲率与归一化扭矩只能在各自的 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

评论

发表回复

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