结论:纵向规划器每帧把 MPC 目标加速度、巡航候选和实验模式的 e2e 候选放在一起,取最小加速度作为 aTarget,但 shouldStop 是对全部候选求或——所以两者可能来自不同候选,不能按单一来源重构。最终还要裁到车型接口常量 ACCEL_MIN=-3.5、ACCEL_MAX=2.0,再交给 LongControl。本文为固定源码快照 c8fb906 的静态分析,数学建模说明不等于数值收敛或车辆级认证。
进程与消息链
manager 进程表在非纵向机动实验的行车模式运行 plannerd。main() 等待 CarParams,以 modelV2 轮询;只有本轮模型主题更新才调用纵向规划并发布 longitudinalPlan,然后运行车道偏离检测、发布 driverAssistance。两个输出各自写 sm.all_checks() 作为顶层有效位,规划计算本身没有被该检查提前短路。longitudinalPlan 名义频率为 20 Hz,真实时延受输入消息年龄和求解耗时影响。
输入包括 modelV2 模型动作与油门概率、radarState 两个前车、carState 车速/巡航速度/转角、controlsState 纵向状态与强制减速、vehicleParameters 转向零偏,以及 selfdriveState 模式与驾驶风格。MPC 本体把自车建模为位置、速度、加速度三维状态,以 jerk 为控制量。
候选是怎么合成的
LongitudinalPlanner.update() 同步车况、MPC、巡航和可选 e2e 候选,选择目标并推进期望车速滤波。它在 controlsState.forceDecel 时把巡航目标速度置零;纵向控制关闭、巡航速度未初始化或非自控纵向且尚未使能时重置内部目标。carControl.orientationNED 可用时以俯仰估计滑行加速度,否则以 ACCEL_MAX 作缺省值;vehicleParameters.angleOffsetDeg 从转角中扣除,用于非实验巡航候选的横向加速度预算。
get_cruise_accel() 的巡航候选按车速限制正加速度,并在非实验模式下用 sqrt(max(a_total²-a_y²,0)) 限制弯中纵向余量;若 allow_throttle=False,还把正加速度上限向坡度估得的滑行加速度收紧。allow_throttle 来自模型 gasPressProbs[1] > 0.4 或车速 <= 2.5 m/s;若数组不足两项,代码回退概率 1.0。它的计算顺序是先将速度误差裁到巡航/弯道/滑行候选范围,再按上一帧 a_cruise 和 j_cruise * dt 限变化率。
四项原函数 NumPy 探针确认了这种「先裁范围、后限变化率」的瞬态:10 m/s、上一帧 2.0 m/s² 时返回 1.94 m/s²,高于该速度的 1.2 m/s² 巡航上限;20 m/s 且弯道预算降为零时仍返回约 0.453 m/s²;禁油门滑行上限为 -0.3 m/s² 时可暂时返回 -0.07 m/s²。这是平滑历史带来的候选瞬态,不等于最终 aTarget 或车辆执行值必然越界。
最终选择上,MPC 轨迹经 get_accel_from_plan() 按车型纵向延迟插值得到当前目标;再与巡航候选比较,实验模式增加 modelV2.action.desiredAcceleration 候选。代码取最小加速度,再裁到车型接口常量 ACCEL_MIN=-3.5、ACCEL_MAX=2.0;shouldStop 却对全部候选求或。因此 aTarget 与 shouldStop 可来自不同候选。controlsd controlsd 将两者交给 LongControl.update(),后者按 off/stopping/pid 状态与车型 PID 限值给执行器目标;不能把 aTarget 当作直接 CAN 帧。
| 字段 | 来源 | 相位与边界 |
|---|---|---|
aTarget |
MPC、巡航与实验模式 e2e 候选取最小加速度后再裁到车型常量。 | 不是直接 CAN 帧;需经 LongControl。 |
shouldStop |
对全部候选求或。 | 可与 aTarget 来自不同候选。 |
allowThrottle |
模型 gasPressProbs[1] > 0.4 或车速 <= 2.5 m/s。 |
只提示与巡航候选约束;controlsd 未把它当独立布尔门。 |
消息 valid |
publish() 时写 sm.all_checks()。 |
与候选计算相分离,计算本身不被它短路。 |
| 函数/组件 | 作用 | 关键边界 |
|---|---|---|
get_cruise_accel() |
按巡航速度差给目标加速度,非实验模式考虑弯道横向与滑行/油门约束,再做 jerk 限幅。 | 后置 jerk 限幅可使返回值暂时高于前面算出的候选上限。 |
LongitudinalPlanner.update() |
同步车况、MPC、巡航与可选 e2e 候选,选择目标并推进期望车速滤波。 | 允许在输入检查失败时计算;valid 只在发布时反映 sm.all_checks()。 |
LongitudinalPlanner.publish() |
发布速度/加速度/jerk 轨迹、aTarget、shouldStop、FCW、来源与油门提示。 |
hasLead 取 leadOne.present,不表达雷达消息有效性。 |
get_T_FOLLOW() |
选择跟车时距:宽松 1.75 s、标准 1.45 s、激进 1.25 s。 | 个性化只改变优化参数,不改变独立安全网关策略。 |
MPC 时域与跟车时距
gen_long_model() 在构建期由 CasADi/acados 生成求解器,运行时不是 Python 手工积分器。gen_long_ocp() 建立 12 段、10 秒非均匀预测时域,设置距离/jerk 成本与速度、加速度、危险距离软约束;约束与权重在运行时更新,不能只看静态模型推断车辆一定在物理极限内。get_T_FOLLOW() 按驾驶风格选择 1.75 s、1.45 s 或 1.25 s 的跟车时距。
process_lead() 处理前车状态、裁剪异常值并外推轨迹;无前车时注入虚拟高速前车。六项原 MPC 前车方法隔离探针确认:无目标时首点为前方 50 m、车速比自车高 10 m/s 的虚拟前车;近目标距离会被可刹距离公式抬高,输入加速度被裁到 [-10,5] m/s²。LongitudinalMpc.update() 把两个前车换算成等效静止障碍,逐节点选择更近约束,运行求解并累计 FCW 近碰撞帧;source 取第零节点最近障碍,后续节点可换前车,FCW 只用 leadOne 且要求 modelProb>0.9。探针确认即使后续节点的逐时刻最小障碍已切换到另一前车,source 只由第零节点决定。set_cur_state() 与上次速度差严格大于 2 m/s 时重设所有 solver 节点状态,等于 2 m/s 不重设。
求解失败与 FCW 边界
LongitudinalMpc.run() 在求解状态非零时先读取本次解,再按单调时钟判断是否记录警告并调用 reset();LongitudinalPlanner.update() 随后仍插值当前(已清零的)速度/加速度/jerk 轨迹并参与候选选择,没有专门的「求解失败」消息位。reset() 同时将 solve_time、solution_status、crash_cnt 与 last_cloudlog_t 置零;因此告警时间戳不跨失败保存,当单调时钟已超过 5 秒时,连续失败可每次记录,而非真正跨失败限频。发布的求解耗时为清零后的值,原始失败状态没有发布。
FCW 侧,LongitudinalMpc.update() 在 5 秒预测窗口内存在距离小于 0.25 m 且首个前车模型概率 >0.9 时累加 crash_cnt;规划器要求计数 >2 且车辆未静止才发布 FCW。selfdrived 还会结合另一条模型硬制动预测生成 FCW 事件,两者不能混为单一碰撞检测器。探针还确认:第一前车模型概率从 0.91 降到严格边界 0.9 时计数清零;dRel=NaN 可继续形成 NaN 位置轨迹,当前纯输入处理层没有有限性校验。
常见问题
为什么 aTarget 和 shouldStop 可能来自不同的候选?
因为最终 aTarget 是「取最小加速度」,而 shouldStop 是对全部候选「求或」。探针确认:MPC 的 -0.5 m/s² 目标获选时,较大的 e2e 候选仍可提供 shouldStop=True。
跟车距离有哪几档?
get_T_FOLLOW() 提供宽松 1.75 s、标准 1.45 s、激进 1.25 s 三档;个性化只改变 MPC 的优化参数,不改变独立的安全网关策略。
MPC 求解失败会怎样?
run() 遇非零求解状态会调用 reset(),清零轨迹、计数和状态;规划器随后仍插值已清零的轨迹参与候选选择,且没有专门的「求解失败」消息位,发布的求解耗时也是清零后的值。
allowThrottle=False 是不是就等于禁止加速?
不完全是。allowThrottle 被发布供 UI 调整显示;本快照 controlsd.state_control() 只显式消费 aTarget/shouldStop,未把 allowThrottle 作为独立布尔门传给 LongControl。它对正常巡航候选的限幅有作用,但不能据此断言所有候选都被禁止正加速度。
延伸阅读
- openpilot 的 100 Hz 控制回路:开源 ADAS 能给车载设备什么 —— 控制回路的节拍与纵向规划的 20 Hz 输出。
- 雷达前车融合:两个前车怎么变成 MPC 的等效障碍 —— 前车数据如何进入纵向候选。
- 横向目标如何落到执行器:从轨迹到转向命令的一段链 —— 与纵向并列的横向执行路径。
- 从使能到车轮:执行链上的八道门,以及它们各自不保证什么 —— 规划输出之后还有哪些门。
参考资料
- 项目 02 ——
openpilot-research/LONGITUDINAL_PLANNER_MPC_DEEP_DIVE.md(plannerd 消息链、候选合成、MPC 时域与求解失败边界)。 - 项目 02 —— 同目录探针记录:四项巡航数值、四项规划器候选、六项 MPC 前车、四项求解器生命周期隔离探针结论。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11