前阵子接手一个项目,车辆在高速匝道连续变曲率路段总是压线,横向控制和纵向控制分开调了很久,单看每一项指标都正常,合在一起就是不对。折腾一圈之后,我把目光放到了Apollo开源的MPC控制器上,花了两周时间把横纵向耦合控制这条线认真啃了一遍,今天整理成这份学习笔记,希望能帮到正在研究自动驾驶控制算法的朋友。
先说结论:横纵向耦合控制不是把横向控制和纵向控制两个模块拼在一起,而是在同一个预测模型里同时考虑转向和加减速对车辆状态的影响,用模型预测控制(Model Predictive Control,MPC)统一求解。这样做最大的收益是:在极限工况下,车辆不会因为横向纵向分别决策而出现“油门踩了但方向盘转不过来”的割裂感,控制指令天然协调。你不需要自己是控制理论专家也能上手,但如果完全不懂MPC的原理,只看代码会非常痛苦,所以这篇笔记我会从模型讲起,一步步带你拆完整个Apollo MPC控制器,最后再分享我在仿真和实车上踩过的坑。
1. 横纵向耦合控制到底在解决什么问题
1.1 传统解耦控制的局限性
早期自动驾驶控制方案基本都是解耦设计,横向用一个控制器管方向盘,纵向用一个控制器管油门刹车,两个模块独立运行,各自输出指令。这种方案在低速、道路平缓、曲率变化小的场景下完全够用,市面上一大票L2级辅助驾驶产品就是这么做的,调参也相对简单。
但到了高速连续弯道、紧急变道、匝道大曲率路段,问题就暴露出来了。纵向控制器根据前方障碍物决定减速,横向控制器根据道路曲率决定转角,两边各自觉得自己算得没问题,但车辆实际动力学中,纵向加速度会改变前后轴载荷转移,进而改变轮胎的侧偏刚度,最终影响横摆响应。简单说就是——车辆一边刹车一边转向,轮胎的侧向力上限是下降的,横向控制器如果不知道纵向在刹车,算出来的转角很可能超出轮胎物理极限,结果就是推头或者侧滑。
还有一个更隐蔽的问题。横向控制器的参考路径通常是基于车辆后轴中心规划的,但车辆实际跟踪曲率和纵向速度强相关,同样的转角,低速和高速下车辆的横摆角速度差很多。解耦设计下,横向控制器如果只用当前速度做前馈补偿,遇到纵向加减速剧烈的场景,补偿往往跟不上,误差就出来了。
1.2 Apollo为什么选择MPC做耦合
Apollo在规划模块和部分控制方案中之所以倾向于MPC,是因为MPC天然支持多变量、多约束的统一优化求解。横向状态量(横向误差、航向误差、横摆角速度)和纵向状态量(速度误差、位移误差)可以被放进同一个状态向量里,用同一个车辆动力学模型做预测,控制量(前轮转角、加速度)也放在同一个优化变量里一起解。
这个设计和解耦控制最大的区别在于,耦合方案在预测模型内部就已经包含了横向状态和纵向状态之间的相互影响关系。比如刹车导致的速度降低,在预测模型里会同步体现为横摆角速度响应的变化,所以MPC算出来的转角指令,本身就已经考虑到了当前刹车减速对转向能力的削弱。这是解耦方案做不到的。
不过要说明一下,Apollo的MPC控制器用的是线性时变模型,也就是在每个控制周期把非线性车辆模型在当前工作点做线性化处理,然后用线性二次规划求解。它不是直接把完整的非线性模型丢给优化器去解非线性规划,后者计算代价太大了,实车跑不起来。这个线性化的细节对理解整个控制器非常关键,后面会展开讲。
2. MPC控制器的核心原理与代码实现思路
2.1 预测模型与状态空间表达
Apollo MPC控制器里使用的车辆模型是经典的自行车模型(Bicycle Model),把车辆简化为前后两个轮子,忽略左右轮的差异。模型考虑了车辆的横向、横摆和纵向三个自由度,状态量包括车辆在大地坐标系下的位置、航向角、横摆角速度、纵向速度等。
但这里有一个关键的建模技巧:Apollo的MPC不是直接对车辆绝对位置建模,而是对相对于参考路径的误差建模。状态向量里包含横向误差、横向误差变化率、航向角误差、航向角误差变化率、纵向速度误差等,这样做的好处是参考点在目标轨迹附近,误差量级小,线性化误差可控,数值稳定性也更好。
具体到状态方程,横向部分的核心是车辆横摆动力学方程,包含前轮侧偏刚度和后轮侧偏刚度,这两个参数直接决定车辆的转向响应特性。纵向部分则简化为一个一阶惯性模型或者加速度直接作为控制量。横纵向之间的耦合项主要体现在纵向速度vx出现在横向动力学的系数矩阵中——vx越大,前轮转角对横摆角速度的影响系数就越大,这就是耦合的数学来源。
2.2 预测时域内的滚动优化
MPC的核心机制是在每个控制周期做一次未来N步的预测和优化。假设控制周期是100ms,预测时域是20步,那么控制器会基于当前状态,预测未来2秒内车辆的运动轨迹,通过调节未来20步的转向和加速指令,让预测轨迹尽量贴合参考轨迹,同时满足各种约束。
这里要特别说清楚两个容易混淆的概念:预测时域(Prediction Horizon)和控制时域(Control Horizon)。预测时域是你要预测多远未来的状态,控制时域是你打算优化多少个控制量。在Apollo MPC里,这两个数值不一定相等,通常控制时域小于等于预测时域。比如预测20步,但只优化前10步的控制量,后10步的控制量保持为第10步的值不变。这样做的目的是降低优化变量的维度,减少计算量,因为每一步控制量都作为独立优化变量的话,变量数会爆炸。
优化目标函数通常由三部分组成:状态偏差惩罚、控制量变化幅度惩罚、控制量变化率惩罚。状态偏差惩罚用权重矩阵Q控制,让预测状态尽量接近参考状态;控制量惩罚用权重矩阵R控制,让控制量不要太大;变化率惩罚则是为了让控制量变化平缓,避免方向盘来回猛打。
优化问题以二次规划(Quadratic Programming,QP)的形式求解,Apollo使用的是OSQP求解器。整个优化过程在100ms内完成,包括模型更新、矩阵构造、QP求解和指令输出。
2.3 约束条件的建模与处理
约束是MPC相比LQR的一大优势。LQR可以算出最优控制率,但无法直接处理约束;MPC可以把约束显式地写进优化问题里。Apollo MPC中的约束主要分三类:
第一类是控制量约束,比如前轮转角不能超过方向盘物理限位(比如±38度),加速度不能超过车辆动力上限(比如最大3m/s²),刹车减速度不能超过轮胎附着力上限。这些约束直接体现为优化变量的上下界。
第二类是控制量变化率约束,也就是每一步控制量和上一步之间的差值不能太大。这个约束的作用是避免控制指令跳变,防止车辆突然猛打方向或者猛踩刹车,实际调参时这个约束对乘坐舒适性的影响非常大。
第三类是状态量约束,比如横向误差不能超过车道边界。这类约束Apollo默认一般是放宽处理的,因为状态约束如果设置得太紧,优化问题很容易无解。实际工程中更常见的做法是不设置硬状态约束,而是在目标函数里把状态偏差权重调大,让状态偏差尽量小。
2.4 QP求解与数值稳定性问题
Apollo MPC最终求解的是一个标准的凸二次规划问题,目标函数是二次的,约束是线性的。OSQP是一种基于交替方向乘子法的求解器,在中等规模的凸QP问题上表现不错,而且支持热启动,即用上一时刻的解作为当前时刻的初始解,能显著加速收敛。
我在实际调试中遇到过一个问题:在高动态场景下,线性化点变化剧烈,QP求解偶尔会出现数值不稳定的情况,表现为输出的控制指令突然跳变。排查下来发现是权重矩阵病态导致的。解决办法是把Q矩阵和R矩阵的对角元素量级调整到接近,避免出现某个状态量权重比其他状态量大几个数量级的情况,数值稳定性会好很多。
3. 横纵向耦合关系的模型表达与关键参数解析
3.1 轮胎力学中的横纵向耦合
理解横纵向耦合控制,必须回到轮胎力学的基本面。轮胎与地面的接触区域内,纵向力(驱动力/制动力)和侧向力(侧偏力)不是独立存在的,它们共享同一个摩擦圆。当纵向力增大时,侧向力裕量就减小,反之亦然。这就是为什么高速过弯时踩刹车到轮胎极限,车辆会突然失去转向响应。
在Apollo MPC的线性模型中,这种耦合是通过速度vx对模型系数的影响来近似表达的,而不是直接建模轮胎的摩擦圆。这是一种简化处理,因为在摩擦圆约束本身是非线性的,加进QP问题会破坏凸性,求解难度会大幅上升。所以Apollo的做法是通过速度和加速度约束来间接限制工作点,让它不要进入轮胎的强非线性区。
这意味着什么?意味着MPC算出来的轨迹在大多数工况下是安全的,但在极其接近轮胎极限的工况下,线性模型的预测会偏乐观。所以在实际应用中,横纵向耦合控制不能完全依赖MPC的模型预测,下层还需要一套底盘安全冗余,实时监控轮胎滑移率,必要时直接介入限制扭矩。
3.2 纵向速度对横向动力学的影响
横向车辆动力学模型中的关键耦合参数是纵向速度vx。在自行车模型中,横摆角速度对前轮转角的传递函数增益与车速vx成正比,同时横摆角速度的自然频率和阻尼比也都受vx影响。这意味着同一套横向控制器参数,低速时可能响应钝,高速时可能不稳定。
解耦控制面对这个问题的常规做法是查表——把车速分成若干区间,每个区间对应一套横向控制器参数,车速变化时切换参数。这种做法的问题是切换点附近容易跳变,而且车速变化剧烈时参数切换跟不上。MPC则没有这个烦恼,因为vx是状态量,本身就参与预测模型的构建。车速变了,模型的系数自动跟着变,相当于每个控制周期都在做参数自适应。
我在仿真里做过一个对比实验:同样的变道工况,起始速度分别是60km/h和120km/h,解耦PID在高速工况下横向误差峰值接近0.6m,而MPC两个工况的误差峰值都在0.3m以内。这个对比很直观地说明了把速度建模进预测模型的价值。
3.3 参考轨迹中的曲率前馈
Apollo MPC的目标函数里,除了状态跟踪误差项之外,还有一个前馈项用于补偿道路曲率的影响。当车辆跟踪一个弯道时,即使横向误差为零,车辆也需要一个稳态转角来维持转弯。这个稳态转角等于曲率乘以轴距加上一个与车速相关的项。
这个前馈项可以放在两个地方:一种是在目标函数的参考值里直接补偿,另一种是单独计算一个前馈转角,叠加在MPC输出的转角上。Apollo采用的是在预测模型的参考状态中注入曲率信息,让预测模型知道“这个弯道需要多少横摆角速度”。
这个设计细节对跟踪精度影响很大。如果没有曲率前馈,MPC在进入弯道的瞬间会有一段明显的滞后,表现为弯道内侧切线的轨迹超调。加上前馈之后,车辆在弯道入口就能提前建立合适的横摆角速度,跟踪误差会大幅下降。
4. 实车调试中的参数调优与问题排查
4.1 权重矩阵Q/R的调试顺序
Apollo MPC的调试核心就是调Q矩阵和R矩阵的相对关系。Q矩阵对角元素对应各个状态量的惩罚权重,R矩阵对应控制量的惩罚权重。我的调试经验是从简到繁,先调纵向,再调横向,最后调耦合项,每一步都用仿真验证清楚再做下一步。
纵向控制的Q权重里,速度误差的权重一般给得最大,因为纵向控制是跟着规划的速度走的,速度误差直接反映跟踪性能。加速度控制量的R权重不用给太大,只要有足够的变化率约束,纵向指令一般不会很激进。
横向控制的Q权重里,横向误差和航向误差是两个最主要的惩罚项。横向误差权重太大,车辆会为了减小横向误差而猛打方向,容易画龙;横向误差权重太小,车辆在弯道里会贴外圈行驶,稳态误差大。实际操作中我会把横向误差权重从0.5起步,每次加一倍,跑仿真看横向误差峰值和转向角速度的变化,找到拐点之后再微调。
4.2 预测时域的标定与计算耗时平衡
预测时域的长度对控制效果和计算耗时影响很大。预测时域太短,控制器只看得到眼前几步,预见性不足,遇到曲率突变路段会反应不及;预测时域太长,优化变量多,单步求解耗时暴增,而且远期预测的精度受模型误差影响大,意义不大。
我测试下来的经验值:控制周期100ms、预测时域15到20步、控制时域5到8步是兼顾性能和计算量的甜点区间。APOLLO默认参数大致在这个范围,但对于不同的车型,需要根据车辆模型的精度和计算平台的性能做微调。比如我在低算力的MCU上跑过,将预测时域压缩到了10步,控制时域压缩到了3步,跟踪性能下降约12%,但单步求解耗时从40ms降到了12ms。
这里有个常用的优化技巧:利用OSQP的热启动特性。每个控制周期所用的初始解计算,不必全零开始。用上一个控制周期的解作为当前周期的初值,QP求解平均迭代次数能下降40%以上,而且解的变化更平滑。
4.3 常见问题排查速查表
调试过程中我整理了一份常见问题排查表,直接贴出来供参考:
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 车辆画龙、横向震荡 | 横向误差Q权重过大或控制变化率约束过松 | 观察转向角速度是否有高频抖动 | 降低Q中横向误差权重,收紧控制量变化率约束 |
| 弯道稳态误差大 | 曲率前馈缺失或模型参数不准 | 检查参考状态是否包含曲率补偿项 | 确认预测模型中的前轮侧偏刚度标定准确 |
| 高速时控制不稳定 | 模型线性化误差累积 | 检查车辆在高速时的轮胎侧偏刚度变化 | 优化求解器,必要时缩短控制周期或预测时域 |
| 纵向频繁加减速 | 速度误差Q权重过大或R权重过小 | 观察加速度指令是否频繁在正负间跳变 | 适当降低速度误差权重,增大加速度变化率惩罚 |
| MPC输出跳变 | QP数值不稳定或热启动失效 | 检查权重矩阵量级差异是否过大 | 归一化状态量,调整权重矩阵条件数 |
| 极限工况下横向误差突然增大 | 线性模型在强非线性区预测失真 | 对比实际横摆角速度与模型预测值 | 增加下层安全冗余,设置轮胎滑移率保护 |
4.4 实车与仿真调试的差异点
仿真和实车调试的差异主要体现在两个方面:一是模型参数的真值问题,二是执行器的延迟问题。
模型参数方面,仿真里的轮胎侧偏刚度直接用的模型设定值,但实车上轮胎的侧偏刚度受胎压、磨损、路面附着系数影响很大。我第一次做实车测试时,直接沿用仿真参数,结果弯道里车辆响应明显钝于预期,横向误差比仿真大了将近一倍。后来重新做了轮胎参数辨识,把前轮侧偏刚度从原来的数值修正为辨识结果,效果立刻好转。
执行器延迟方面,仿真里通常不会建模转向系统的响应延迟和刹车系统的建压时间。实车上方向盘从发出指令到实际达到目标转角,通常有几十毫秒的滞后。这个滞后在MPC模型里如果不补偿,会导致预测状态领先于实际状态,控制效果打折扣。Apollo的解决方案是在模型中加入执行器的一阶惯性环节,但实际延时往往不是标准一阶,所以我在线做了纯延迟补偿,把延迟时间折算成预测步数偏移,效果很明显。
5. 从MPC到更多控制方案的扩展思考
5.1 MPC与LQR、PID的选型对比
很多人在学习MPC时会问:为什么不直接用LQR?LQR也是基于模型的最优控制,计算量还小得多。答案是LQR无法处理约束。LQR的推导前提是无限时域无约束优化,实际工程中控制量限幅只能在求解后做饱和处理,但饱和处理后的控制量就不再是最优的了。MPC的约束处理能力是本质性的优势,约束是优化问题的一部分,这一点在实车控制上非常关键。
PID的适用场景是模型不确定性大、精度要求不高的场合。PID没有预测能力,对时延系统天生有缺陷,典型的做法是加史密斯预估器补偿时延,但遇到强耦合的MIMO系统,PID的整定复杂度会指数级上升。横纵向耦合控制不是多回路PID拼装能解决的问题,这一点我吃过亏,所以后来才转投MPC。
5.2 非线性MPC为什么还上不了车
既然线性时变MPC在极限工况下有预测失真的问题,那能不能直接用非线性MPC(NMPC)?技术上可以,工程上很难。NMPC每一步要解非线性优化问题,实时性很难保证,而且非凸优化不保证收敛到全局最优,解释性也差。以当前工业界车载计算平台的算力,跑NMPC的可靠性还达不到量产要求。
折中的方案是线性参变MPC(LPV-MPC),根据工况在线调整线性化点,比如按不同的车速区间维护多组模型参数,这在工程上是更务实的路线。Apollo的方案虽然叫MPC,但严格说是线性时变MPC,这个定位很清晰,就是保证实时性和稳定性的前提下尽量逼近非线性性能。
5.3 控制算法学习路线的一点建议
如果你想系统地掌握这套东西,我的建议是先手推一遍自行车模型和误差动力学方程,不要只看代码。代码只告诉你实现,模型推导才能让你真正理解为什么Q矩阵这样调会有那样的效果。然后找一个开源QP求解器,自己写一个最简单的线性MPC,比如双积分器的例子,跑通之后再慢慢加约束、加模型复杂度,最后再上手Apollo MPC的代码。
Apollo的代码结构其实很清晰,主要控制逻辑在modules/control/controller/mpc_controller目录下,模型部分在conf文件中配置参数。先把代码流程读懂,再把模型手推一遍,最后在仿真环境里调一遍参数,整个方法论就内化了。
6. 最后想说的几句心里话
我在啃这个模块的过程中最深的体会是:控制算法不是调参调出来的,是推导推出来的。一开始我拿着一堆调好的参数觉得很厉害,后来发现换个工况全废。直到把自行车模型的每一个系数、MPC目标函数的每一项来源都搞清楚之后,我才有了真正的parameter tuning手感——每个参数改下去,我都知道它在模型里影响的是哪个状态量的哪个通道。
再说一个很多人忽略的细节:MPC的实际效果很大程度上取决于参考轨迹的质量。规划模块给的轨迹如果本身不平滑,曲率跳变严重,MPC的跟踪误差再小,车辆实际轨迹也会很难受。所以如果你发现车辆乘坐体验差,别急着调控制参数,先检查一下上游规划的轨迹平滑度。
这个笔记写到这里,后续我打算把Apollo纵向控制里的ACC跟车场景也用MPC重写一版,看看与现有方案的差异有多大。等跑完仿真有结果了,再回来把新踩的坑更新进来,希望对你有帮助。