做自动驾驶路径跟踪的人,基本都会遇到一个尴尬场景:同一套MPC参数,在低速园区绕个圆道又稳又准,车速一旦提到80甚至120,过连续弯道就明显开始抖,要么横向误差变大,要么方向盘来回修正。这个问题的根源一般不是MPC本身不行,而是你给控制器定了一套“固定性格”,让它在高速、大曲率的工况里还沿用低速时偏好的激进权重。
这篇内容要聊的,正是把“二自由度横摆动力学”作为被控对象模型、用模型预测控制做任意路径跟踪,并在此基础上加一层自适应参数调节,让控制器根据当前车速、路径曲率和跟踪误差实时调整权重与约束。直白说,就是让MPC从“一个参数走天下”变成“随机应变”。适合正在做路径跟踪控制、车辆横摆稳定控制,或者刚接触自动驾驶横向控制的工程师和研究生参考,读完你能搞清楚模型怎么建、MPC怎么搭、自适应参数到底在调什么,以及真正落地时会踩到哪些坑。
1. 为什么是“二自由度横摆动力学 + MPC”
很多人一听到车辆动力学模型,脑子里先想到七自由度、十四自由度那种复杂仿真模型。但做控制跟做仿真不一样,控制器内部的模型讲究“够用但不冗余”。这里选二自由度横摆动力学,不是因为高阶模型不好,而是因为路径跟踪这个问题本身,主要关心的是车辆“往哪儿走”和“车头朝向怎么变”,也就是侧向运动和横摆运动。二自由度模型刚好抓住这两个关键运动,计算量又小,非常适合实时控制。
1.1 二自由度模型到底简化了什么
二自由度模型的经典简化思路是这样的:把车辆看作一个刚性自行车模型,前轮和后轮各自合并成一个虚拟车轮。忽略垂向跳动、侧倾、俯仰,也不管空气阻力,只保留车辆平面内的横向运动和绕质心的横摆运动。这就像你骑自行车时俯视看路况,注意力全部集中在“车身相对道路中心线的偏移”和“车头方向与道路切线的夹角”上。
在这个模型里,轮胎侧偏特性被简化成线性关系:侧偏力等于侧偏刚度乘以侧偏角。这个线性区假设在小侧偏角范围内是成立的,一般侧偏角不超过几度时精度足够。路径跟踪控制本身就是保持车辆在正常行驶工况下工作,不会频繁去逼轮胎进入饱和区,所以这个假设在绝大多数场景下真够用。
二自由度模型最直接的好处,是能推导出一个结构清晰的状态空间方程:
[ m v_x (\dot{\beta} + r) = -C_f(\beta + \frac{a r}{v_x} - \delta) - C_r(\beta - \frac{b r}{v_x}) ]
[ I_z \dot{r} = -a C_f(\beta + \frac{a r}{v_x} - \delta) + b C_r(\beta - \frac{b r}{v_x}) ]
这里的符号解释一下:(m)是整车质量,(v_x)是纵向车速,(\beta)是质心侧偏角,(r)是横摆角速度,(a)和(b)分别代表质心到前、后轴的距离,(I_z)是整车绕垂直轴的转动惯量,(C_f)、(C_r)是前后轮等效侧偏刚度,(\delta)是前轮转角,也就是我们最终要控制的东西。
1.2 为什么路径跟踪要用MPC而不是纯PID
路径跟踪本质上是一个带约束的优化问题。你希望车辆沿着参考路径走,但又要保证方向盘转角不超过机械极限、转角变化率不能太猛,否则乘客会难受。PID虽然简单,但它没有一个明确的方式去“提前预测”未来会发生什么,面对前方曲率突变时永远是已经偏差了才开始反应。
MPC解决这个问题的思路很像下棋:每走一步之前,先基于当前车辆状态和车辆模型,预测未来若干步的跟踪误差和横摆响应,然后在满足约束的前提下,求解一串最优控制序列,只执行第一步,下一周期重新滚动计算。
这种滚动优化的方式天然适合路径跟踪。参考路径信息本身就可以转化为未来的参考序列,MPC可以从容面对弯道前提前减速或提前调整转角。不过MPC挑模型,模型不准,预测等于白给。二自由度横摆动力学在这里就是一个性价比很高的选择,它既保留了车辆横向响应的主要特征,又不会给优化器带来过大的非线性负担。
1.3 自适应参数调节解决的是什么
固定参数的MPC,本质上是在“多个互相矛盾的目标之间”提前固定了一个妥协点:
- 横向误差权重调大,跟踪更紧,但控制量容易振荡,高速下尤其明显;
- 控制增量权重调大,方向盘更平滑,但过弯时误差会被放大;
- 预测时域调长,控制器更有远见,但计算量增大,动态响应变慢;
- 预测时域调短,响应快,但面对连续弯道容易“近视”。
这些矛盾随工况变化还在变。低速挪车时你希望横向偏差权重高、动作干脆;高速巡航时你更关心方向盘平顺性和车辆稳定性;急弯里你又需要提前预判,不能太激进。自适应参数调节就是把“妥协点”变成“动态点”,让MPC在不同工况下切换自己的行为倾向。
实际工程里最常见的实现方式,是基于车速、路径曲率和实时跟踪误差做增益调度。这个思路不算新,但把它跟二自由度横摆模型的MPC结合起来,效果非常直接:低速小区间测试时跟踪精度好,高速连续变道时也不会像固定权重那样左右摇摆。这个方案的本质,不是在优化算法里塞多高大上的东西,而是把控制器的“性格管理”做扎实。
2. 把车辆模型写成MPC看得懂的数学形式
2.1 从物理方程到状态空间
上面的二自由度模型物理方程,第一眼看起来有点乱,但整理成状态空间之后非常规整。这里有个习惯:状态量一般取(\beta)和(r),控制量取前轮转角(\delta),写成矩阵形式:
[ \begin{bmatrix} \dot{\beta} \ \dot{r} \end{bmatrix} = \begin{bmatrix} -\frac{C_f+C_r}{m v_x} & -\frac{a C_f - b C_r}{m v_x^2} - 1 \ -\frac{a C_f - b C_r}{I_z} & -\frac{a^2 C_f + b^2 C_r}{I_z v_x} \end{bmatrix} \begin{bmatrix} \beta \ r \end{bmatrix} + \begin{bmatrix} \frac{C_f}{m v_x} \ \frac{a C_f}{I_z} \end{bmatrix} \delta ]
细看就能发现,矩阵里几乎每一项都和纵向车速(v_x)有关。这也解释了为什么同一套权重在不同车速下表现差异很大:系统的动态响应特性本身就随速度显著变化。在MPC里使用这个状态方程时,需要把连续时间系统离散化。常用的方法是一阶欧拉离散或零阶保持离散,控制周期一般取20ms到50ms,欧拉法在这个步长下已经能保证足够的预测精度。
2.2 路径跟踪误差状态空间
不过直接用(\beta)和(r)做MPC状态,路径跟踪问题还得单独处理横向误差。更常见的做法是建立以路径误差为状态量的模型。参考路径可以表达为每个点上的位置、航向和曲率(\kappa),车辆相对参考路径的偏差包括横向位置误差(e_y)和航向误差(e_{\psi})。
一个常用的误差状态向量是:
[ x_e = [e_y, e_{\psi}, \dot{e}y, \dot{e}{\psi}]^T ]
对应的状态空间表达式里,包含参考曲率(\kappa)作为扰动项。这个过程就是把“世界上的一条路”转换成“车辆坐标系下的一系列偏差”,MPC的核心任务就是把误差序列压到零。
误差模型里有一个很有意思的耦合关系:航向误差会顺着车速直接影响横向误差变化率,也就是说车辆开得越快,同一个方向盘转角造成的横向偏移变化就越剧烈。这一点在自适应参数设计里非常关键,因为高速时哪怕很小的航向误差也会很快累积成大的横向偏移,控制器必须在航向误差权重和控制量平滑权重之间做更强的取舍。
2.3 MPC优化命题的构造
有了误差状态空间,MPC命题就可以正式写出来。目标函数一般长这样:
[ J = \sum_{k=1}^{N_p} \left( q_e e_y(k)^2 + q_{\psi} e_{\psi}(k)^2 \right)
- \sum_{k=0}^{N_c-1} \left( r_u u(k)^2 + r_{\Delta u} (\Delta u(k))^2 \right) ]
其中:
- (N_p)是预测时域,(N_c)是控制时域;
- (q_e)是横向误差权重,决定跟踪精度偏好;
- (q_{\psi})是航向误差权重,决定车头方向与道路切线的一致性偏好;
- (r_u)是控制量权重,抑制过大转角;
- (r_{\Delta u})是控制增量权重,抑制转向抖动。
约束条件包括前轮转角限幅:
[ \delta_{min} \le u(k) \le \delta_{max} ]
以及转角变化率限幅:
[ \Delta \delta_{min} \le \Delta u(k) \le \Delta \delta_{max} ]
这个优化问题在每个控制周期内都要在线求解一次。它的求解过程可理解为:基于当前状态,带入车辆模型,预测未来(N_p)步的误差走向,寻找一组控制序列使得目标函数最小。求解完之后只执行序列里的第一步,下一个周期重复。这也是“滚动优化”名字的由来。
2.4 自适应参数机制的设计思路
自适应参数调节并不是随意抖动权重或者让权重随误差“疯狂反馈”,那样容易引发控制器自身的不稳定。比较稳妥的做法,是设计一个基于工况前馈加误差反馈的权重调度器。
核心思路可写成这样:
[ q_e(t) = q_{e0} \cdot f_v(v_x) \cdot f_{\kappa}(\kappa) \cdot f_{err}(e_y) ]
每一项的含义很直接:
- (f_v(v_x)):车速高时适当降低横向误差权重的相对比重,同时提高航向误差和控制平滑权重,否则方向盘容易发飘;
- (f_{\kappa}(\kappa)):曲率大时提高跟踪权重和预测时域,保证急弯里贴着弯心走;
- (f_{err}(e_y)):误差偏大时临时提高横向误差权重,加快拉回路径;误差小时回归正常偏好,避免持续修正。
具体函数形式可以用分段线性、指数或者简单的查表实现。工程上我反而推荐一开始用分段线性,因为每段物理意义清楚,标定的时候好调试。直接上神经网络或者复杂的自适应律,出了抖动问题很难定位是哪个环节引起的。
用代码表达这个调度器大概是这样:
def adaptive_weights(vx, kappa, e_y, q_base): qe0, qpsi0, rdu0 = q_base f_v = 1.0 / (1.0 + 0.02 * max(0.0, vx - 30.0)) f_kappa = 1.0 + 10.0 * abs(kappa) f_err = 1.0 + 0.3 * min(1.0, abs(e_y) / 0.2) qe = qe0 * f_v * f_kappa * f_err qpsi = qpsi0 * (1.0 + 0.5 * (vx - 30.0) / 50.0) rdu = rdu0 * (1.0 + 0.8 * abs(kappa) / 0.02) return qe, qpsi, rdu这套方法的好处是保留了MPC原有的凸优化性质,权重无论怎么变,优化命题依然是二次规划,求解器不需要改。自适应的逻辑完全落在外层,调试时可以单独验证调度器行为,也可以单独验证MPC本身,问题隔离非常容易。
3. 从仿真到实车:自适应MPC的完整落地路径
3.1 仿真环境搭建与工况设计
落地之前先在仿真里验证是必须的。我习惯用的环境是Python搭动力学模型加MPC,或者Matlab/Simulink联合仿真。Python的好处是灵活,状态方程、优化器、调度器都自己掌控,调试时每一步都有迹可循。如果只负责控制算法,用Python足够;如果还需要验证底盘执行器响应,建议上更细的车辆动力学软件配合联合仿真。
仿真工况别一上来就整复杂路径。先把基础工况跑透:
- 直线路径,验证稳态跟踪是否稳定;
- 圆弧路径,验证恒定曲率下的跟踪精度;
- 双移线路径,模拟高速换道这种突发工况;
- 回旋线路径,验证曲率连续变化下自适应调参是否平滑;
- 连续S弯,模拟最容易被固定权重打败的工况。
路径生成可以直接用坐标点序列模拟,不需要依赖真实地图数据。核心是要让参考路径每个点都附带曲率信息,因为调度器要用曲率做前馈。计算曲率可以用相邻三点拟合圆弧,也可以用路径解析式直接算。
3.2 MPC求解的代码骨架
整个MPC循环的核心其实就几步:获取状态、更新参考序列、计算自适应权重、构建二次规划、求解、执行第一步。二次规划这一步可以直接用成熟的求解器,比如OSQP,它支持稀疏矩阵,适合嵌入式环境;也可以用qpOASES,轻量且约束处理能力强;研究阶段最省事的是用cvxpy或者直接用某些线性代数库自己构造QP。
一个最简单的控制循环骨架可以写成这样:
import osqp import numpy as np from scipy import sparse def run_mpc_cycle(x0, ref_x, weights, A_d, B_d, Np, Nc, umax, dumax): # 根据状态和参考路径构造预测矩阵 Sx, Su = build_prediction_matrices(A_d, B_d, Np, Nc, x0, ref_x) # 构建QP目标 Q = blk_diag(weights.qe, weights.qpsi, Np) R = blk_diag(weights.rdu, Nc) H = Su.T @ Q @ Su + R + 1e-8 * np.eye(Nc) g = Su.T @ Q @ Sx # 约束:控制量和控制增量 ... result = osqp.solve(H_sparse, g_sparse, A_constraints, l_bounds, u_bounds) return result.x[0]在实际代码里,注意把离散化的状态矩阵提前算好,预测矩阵可以复用,不需要每个周期重新构建。矩阵维度由状态数、预测时域、控制时域决定,例如状态4维、(N_p=20)、(N_c=10),预测矩阵规模并不大,求解通常能在毫秒级完成。这里多提一句,预测时域长度和控制器“反应速度”不是严格正相关,预测时域过长反而会让控制器响应迟钝,因为最优解把更多精力放在远期误差上。自适应调度器里可以考虑在高速时适当缩短有效预测时域,跟车速匹配。
3.3 自适应参数的标定顺序
新手最常见的错误,是一上来就同时调节所有权重和时域,结果完全不知道哪个参数导致了振荡。我的建议是把自适应过程拆成三个层级,逐级放开。
第一层级只做车速前馈。先把曲率和误差反馈关掉,只看车速变化时权重如何切换。高速时加大控制增量权重,低速时靠跟踪权重保持灵敏。这一层搞定后,车辆在直线-圆弧混合工况下的速度适应性已经有明显提升。
第二层级加入曲率前馈。曲率变大时提高横向误差权重,保证入弯之后不往里侧切。这里需要观察不同曲率下的稳态误差,确定(f_{\kappa})的强度。曲率前馈太强会导致出弯时方向盘回正过快,所以曲率调度的变化斜率要平缓,最好加上一阶低通滤波。
第三层级才引入误差反馈。误差反馈主要是应对瞬态扰动,比如侧风、路面起伏,或者参考路径出现局部突变。误差权重增益不要调太大,通常让误差大时权重增加到基础值的1.5到2倍就够,再大就容易出现极限环式的振荡。
每放开一个层级,都要重新跑一遍之前的全部工况做回归测试。别偷懒,自适应参数的特性和MPC本身的动态耦合起来,很容易出现低速好了高速又差了的跷跷板问题。用表格把每个工况下固定参数和自适应参数的横向误差峰值记录下来,是很有价值的调参档案。
3.4 嵌入式部署的实时性考虑
从仿真到实车部署,最大的变化是算力从跑步机降到了正常人体力。仿真里你可以用100毫秒的步长跑完一整个弯道,但实车控制周期是硬指标,一般要求横向控制算法在20到50毫秒内给出转角指令。
实际部署时我推荐把整个模块拆成三层任务:
- 状态估计层:车速、横摆角速度、质心侧偏角这些状态量来自传感器或状态观测器,估计周期可以短一些;
- 自适应调度层:权重计算不需要每个控制周期都做,可以10到20个周期更新一次,因为车速和曲率的变化相对缓慢;
- MPC计算层:每个控制周期执行一次预测和求解。
为了避免理论预测和实际延迟不匹配的问题,MPC状态输入最好采用“执行器延迟补偿”,也就是把当前转角指令到前轮实际转到位的纯延迟时间计入模型。这个细节在实车里经常是被低估的元凶,固定参数MPC表现还行,自适应参数加上之后延迟影响会被放大,反应表现就是弯道里一会儿推头一会儿甩尾的错觉。
4. 高速换道、急转弯实战结果与典型问题排查
4.1 固定参数与自适应参数的效果对比
这里分享一个很有代表性的仿真结果。工况是高速双移线,车速恒定80公里每小时,参考路径曲率峰值大约0.02每米,也就是急弯程度接近半径50米的弯道。固定参数MPC用的是低速标定的一组权重,自适应参数MPC用的是叠加了车速前馈、曲率前馈和误差反馈的调度方案。
结果对比很明显:
| 项目 | 固定权重MPC | 自适应参数MPC |
|---|---|---|
| 最大横向误差 | 0.38米 | 0.14米 |
| 平均横向误差 | 0.12米 | 0.05米 |
| 控制量峰值 | 8.2度 | 7.6度 |
| 控制增量振荡幅度 | 明显 | 明显收敛 |
| 收敛时间 | 1.8秒 | 1.1秒 |
固定参数的MPC也不是完全不能用,它的问题是误差大时才靠反馈硬拉回来,方向盘修正频繁;自适应参数MPC因为提前根据车速和曲率调整了偏好,在入弯前就开始温和地增大转向权重,出弯后又能及时平复控制动作,整体手感好很多。
4.2 自适应参数的工程落地注意事项清单
从我自己的实践经验看,有几个问题几乎每个做这个方向的人都会遇到,统一整理成一张给后来人速查的对照表:
| 常见现象 | 可能原因 | 处理思路 |
|---|---|---|
| 高速弯道方向盘来回振荡 | 控制增量权重太低,或者车速前馈权重切换太快 | 提高(r_{\Delta u}),对权重调度的输出加低通滤波 |
| 入弯时横向误差先大一下才收回来 | 曲率前馈不够,控制器反应偏慢 | 增大(f_{\kappa})强度,适当延长预测时域 |
| 低速时跟踪精度反而不如固定参数 | 车速前馈把跟踪权重压低过多 | 限制车速前馈的下限,低速时权重不要低于基线的80% |
| 出弯时方向盘回正过猛 | 曲率调度滞后,出弯后仍然保持高跟踪权重 | 对曲率前馈增加滞后释放逻辑,或使用曲率微分信息提前降权重 |
| 自适应参数下偶发QP不可解 | 约束太紧或参考路径曲率突变导致可行域空 | 增加软约束,或检测到不可解时回退上一周期控制量 |
| 实车测试横向误差比仿真大一个量级 | 执行器延迟没补偿,模型参数与实车差异大 | 加入延迟补偿,用实车数据重新辨识侧偏刚度 |
这些问题的共性根源,其实是模型和调度器之间存在“互相刺激”。仿真里所有信号都是理想同步的,实车上反馈延迟、执行器滞后、参数偏差混在一起,自适应把响应提灵敏了,缺陷也被放大了。所以我的习惯是:调自适应参数时先保守后激进,确认每个工况没有振荡再逐步提高增益。
4.3 模型失配与非线性边界
二自由度模型虽然在大部分路径跟踪场景里够用,但别忽略它的边界。当侧向加速度超过约0.4g,轮胎逐渐进入非线性区,线性侧偏刚度假设开始失效。此时控制的载体已经偏离了模型描述的“理想自行车”,再强的自适应权重也无法弥补模型失配。
工程上有两个处理方向。第一是让调度器限制工况:当估计的侧向加速度超过安全阈值时,自适应权重往更保守的方向走,增加控制平滑权重,减少激进跟踪权重,相当于“告诉”控制器当前轮胎状态不允许极限操作。第二是模型参数在线修正:根据横摆角速度响应与模型预测的偏差,实时修正等效侧偏刚度,让MPC的预测尽量贴近真实车辆。第二种方式效果更好,但复杂度会增加不少,前期不推荐一上来就做。
这里分享一个个人意见:MPC的自适应参数调节,真正值钱的部分不是权重公式写得多么花哨,而是你对工况的理解能通过调度器准确地表达出来。每写一个前馈项,都要能回答一个问题:这个工况下控制器应该更看重什么、为什么。
实际测试中还发现一个容易被忽略的点:参考路径的曲率必须是平滑的。如果直接拿原始地图采样点计算曲率,噪声非常大,调度器会跟着曲率噪声频繁调整权重,造成无意义的控制波动。解决方法是先对路径点做平滑滤波,或者用多段多项式拟合后再求曲率。这个预处理不做好,后面的自适应调度和MPC都会被带偏。
这套从二自由度横摆动力学建模、MPC滚动优化到自适应权重调度的完整思路,足够支撑起一个能跑的路径跟踪Demo。回头再看看我自己的代码,最深的体会是:一开始总想把模型建得越精确越好,把调度逻辑设计得越复杂越好,但真正在仿真里来回磨的时候才发现,稳定性的关键往往在那些不起眼的细节——曲率预处理、权重滤波器、延迟补偿、不可解时的回退策略。如果你准备做这个方向,我的建议是先把固定权重的MPC在直线、圆弧和双移线三种工况下完全跑通,记录下它的不足,再逐步把自适应调度加进去。每一步增加一个变量,出了问题你才知道去哪里定位。