简介:面向汽车工程专业学生、自动驾驶横向控制研发人员及控制系统工程师的MPC学习资料,聚焦二自由度车辆动力学建模、轨迹跟踪与车道保持的Simulink实现与优化。内容涵盖质心侧偏角、横摆角速度状态方程及线性化处理,预测时域、控制时域与Q/R权重矩阵设置,执行器饱和约束,以及单移线工况下的跟踪偏差、计算开销和参数折中分析。资源包共1个PDF文件,约114KB,结构集中便于按主题查阅,已有63人学习。读者可从中获得车辆模型搭建、MPC控制器参数整定、仿真结果解读与常见问题排查的参考,文档还结合调参经验强调理论推导与仿真验证的结合,适合课程设计、算法入门及横向控制调参时辅助使用。
1. 从一把方向盘到一条预测时域:MPC 轨迹跟踪到底在控什么
把车道保持做成只会打方向盘的 PID,直道上很稳,一进弯就画龙;换成基于 MPC 的二自由度车辆轨迹跟踪与车道保持 Simulink 模型,真正要解决的不是控制器高不高级,而是预测模型、约束和执行器延迟能不能对上。这个标题拆开看有三层:二自由度车辆模型负责把前轮转角和车辆横摆、横向速度连起来;MPC 负责在预测时域里滚动求解一串未来控制量;Simulink 负责把参考路径、控制器、车辆和执行器延迟串成闭环。它适合已经会搭基础车辆模型、但一上弯道就出现横向误差超调或方向盘抖动的从业者,也适合想把车道保持从“能跑”推到“能复现”的工程人员。下面按模型、控制器、车道保持、避坑和验证五段推进。
2. 二自由度车辆模型怎么搭:从轮胎侧偏刚度到状态空间矩阵
二自由度不是偷懒,而是把 MPC 最需要的横向和横摆先抓住。车辆模型自由度越多,预测模型越接近真实,但 QP 求解也越重,参数辨识也更难。常见做法是用横向位置误差、航向误差、横向速度、横摆角速度四个状态,配一个前轮转角输入,先把中低速到中高速的轨迹跟踪跑通,再决定要不要加纵向、垂向或载荷转移。这个边界要先说清楚:二自由度线性模型在轮胎线性区、曲率不过分大、速度 5 到 25 m/s 的区间里够用;到了极限工况,它不是“不准”,而是“错得很有规律”,需要靠增益调度或更高自由度模型兜底。
2.1 为什么二自由度够用:横向+横摆的取舍
二自由度模型通常保留横向速度 (v_y) 和横摆角速度 (r),把纵向速度 (v_x) 当作慢变量或给定值。轮胎力用线性侧偏刚度近似,前轮侧偏角 (\alpha_f=\delta-(v_y+l_f r)/v_x),后轮侧偏角 (\alpha_r=-(v_y-l_r r)/v_x)。这个近似在侧偏角小于 3 到 5 度时比较可信,方向盘打得越猛、路面越滑,偏差越明显。MPC 的预测模型不需要百分百复现真实车辆,它需要的是“趋势对、相位对、稳态增益对”,否则控制器会在错误的预测上反复修正,表现就是方向盘抖或横向误差回摆。
状态取 (x=[e_y,e_\psi,v_y,r]^T),其中 (e_y) 是相对参考路径的横向误差,(e_\psi) 是航向误差,(r) 是横摆角速度。输出取 (y=[e_y,e_\psi]^T),因为车道保持最关心的就是这两个量。前轮转角 (\delta) 是控制量,道路曲率 (\kappa) 作为可测扰动进入航向误差导数。这个模型没有纵向动力学,所以速度跟踪要另做一层,或者把 (v_x) 做成随时间变化的参数。别小看这个取舍,很多 Simulink 模型跑不起来,不是 MPC 写错,而是把纵向和横向耦合在一个预测模型里,约束维度一下子上去了。
参数上,常见乘用车量级可以取 (m=1500) kg、(I_z=2500) kg·m²、(l_f=1.2) m、(l_r=1.6) m、(C_f=C_r=80000) N/rad。前后侧偏刚度相等会让车辆趋于中性转向,便于先调通控制器;真实车辆通常前轮侧偏刚度略小,不足转向梯度更明显。把这些参数放在一个结构体里,后面做速度调度和批量仿真会省很多事。需要提醒的是,侧偏刚度不是越高越好,取值过高会让预测模型对转角过于敏感,QP 解出来的前轮转角很小,但实际车辆响应跟不上,闭环反而更容易振荡。
2.2 从轮胎侧偏力到状态空间矩阵:四个状态怎么摆
把轮胎力代入横向和横摆方程,得到连续状态空间。横向误差导数 (\dot e_y=v_y+v_x e_\psi),航向误差导数 (\dot e_\psi=r-v_x\kappa)。横向速度导数里包含 (-v_x r) 这一项,很多人手推矩阵时会漏掉,结果离散化后模型在高速下相位差一大截。横摆角速度导数由前后轮侧偏力对质心的力矩得到。写成矩阵后,(A) 的第 3 行第 4 列是 (-(l_f C_f-l_r C_r)/(m v_x)-v_x),第 4 行第 3 列是 (-(l_f C_f-l_r C_r)/(I_z v_x)),这两个元素是高速行为的关键。
下面这段 MATLAB 代码把连续矩阵、输出矩阵和 ZOH 离散化一次做完。输入是一个参数结构体,输出是离散后的 (A_d,B_d,C_d,D_d)。代码里没有把曲率项写进 (A),因为曲率是外部扰动,放在预测里做前馈补偿更灵活。
function [Ad, Bd, Cd, Dd] = build_2dof_model(p) % p: 结构体,字段 m, Iz, lf, lr, Cf, Cr, vx, Ts m = p.m; Iz = p.Iz; lf = p.lf; lr = p.lr; Cf = p.Cf; Cr = p.Cr; vx = p.vx; A = zeros(4,4); A(1,2) = vx; A(1,3) = 1; A(2,4) = 1; A(3,3) = -(Cf + Cr) / (m * vx); A(3,4) = -(lf * Cf - lr * Cr) / (m * vx) - vx; A(4,3) = -(lf * Cf - lr * Cr) / (Iz * vx); A(4,4) = -(lf^2 * Cf + lr^2 * Cr) / (Iz * vx); B = zeros(4,1); B(3) = Cf / m; B(4) = lf * Cf / Iz; C = [1 0 0 0; 0 1 0 0]; D = zeros(2,1); sys = ss(A, B, C, D); sysd = c2d(sys, p.Ts, 'zoh'); Ad = sysd.A; Bd = sysd.B; Cd = sysd.C; Dd = sysd.D; end这段代码的逻辑很直接:先把连续状态空间拼出来,再用零阶保持器离散化。参数上,(T_s=0.02) s 对应 50 Hz,和常见相机、CAN 信号周期能对上;如果控制器跑在 10 ms,把 (T_s) 改成 0.01,预测矩阵也要跟着重建。注意c2d对 (v_x) 很敏感,速度变化超过 5 m/s 时最好重新算一遍 (A_d,B_d),不要拿一个 15 m/s 的模型去覆盖 25 m/s 的工况。常见做法是按 5 m/s 一档做增益调度,档与档之间线性插值,比在线辨识省事得多。
2.3 离散化与预测矩阵:把连续模型变成 QP 能吃的形式
MPC 每一步要预测未来 (N_p) 拍的输出,预测矩阵写成 (Y=\Phi x+\Theta U)。(\Phi) 描述初始状态自由响应,(\Theta) 描述未来控制量对输出的影响。这个矩阵在初始化时算一次就够,不要每个仿真步都重算,否则 Simulink 里看起来控制器没超时,实际每次都在做重复矩阵乘法。(N_p) 取 20、(N_c) 取 10 是常见起点,速度低时 (N_p) 可以短一点,速度高时预测时域要覆盖车辆走过 1 到 2 秒的距离。
function [Phi, Theta] = build_prediction(Ad, Bd, Cd, Np, Nc) n = size(Ad,1); m = size(Bd,2); ny = size(Cd,1); Phi = zeros(ny*Np, n); Theta = zeros(ny*Np, m*Nc); Ak = eye(n); for k = 1:Np Ak = Ad * Ak; Phi((k-1)*ny+1:k*ny, :) = Cd * Ak; end for k = 1:Np for j = 1:min(k, Nc) Akj = eye(n); for t = 1:k-j Akj = Ad * Akj; end Theta((k-1)*ny+1:k*ny, (j-1)*m+1:j*m) = Cd * Akj * Bd; end end end这段代码里,Phi的第 (k) 块是 (C_d A_d^k),Theta的第 (k) 行第 (j) 列块是 (C_d A_d^{k-j}B_d)。当 (N_c<N_p) 时,代码只填到第 (N_c) 个控制量,后面控制量保持的处理要在 QP 里补,否则预测后半段会缺输入。参数上,Np=20、Nc=10时Theta是 40 行 10 列,规模很小,普通笔记本就能实时跑。若把Np拉到 40,矩阵翻倍,QP 求解时间也会明显上去,先确认你的控制器步长扛得住再改。
3. 把 MPC 塞进 Simulink:预测模型、QP 求解与滚动优化落地
Simulink 模型最容易犯的错,是把所有逻辑塞进一个大子系统,信号线横穿整个画布,出了问题只能靠猜。比较稳的做法是按“参考路径—MPC 控制器—车辆模型—执行器延迟—记录”分层,每层只暴露必要的输入输出。模型用固定步长离散求解,控制器和车辆模型都按同一个 (T_s) 更新,避免变步长求解器在控制器输出上做插值,导致同一组参数两次仿真结果对不上。下面先给顶层结构,再落到 MATLAB Function 里的 QP 骨架,最后把约束和延迟塞进去。
3.1 Simulink 顶层怎么分层:参考路径、MPC、车辆、可视化
顶层至少放五个部分。参考路径生成负责输出 (e_y^{ref},e_\psi^{ref},\kappa),双移线工况可以用 MATLAB Function 按时间写,车道保持工况可以从三次多项式系数算。MPC 控制器用 MATLAB Function 块,输入当前状态、上一拍前轮转角和参考量,输出前轮转角指令和求解标志。车辆模型用离散状态空间块,输入实际前轮转角,输出四个状态。执行器延迟用 Unit Delay 或一阶惯性环节,别把控制器输出直接灌进车辆模型。记录部分把 (e_y,e_\psi,\delta,\delta_{cmd},solve_flag) 存到工作区,后面做频谱和峰值统计。
搭建顺序建议先开环、再闭环。开环时给车辆模型一个正弦前轮转角,看 (e_y) 和 (r) 的相位、稳态增益是否和手推传递函数一致;这一步不做,后面闭环一抖,你根本分不清是模型错还是权重错。闭环时先把参考曲率置零,只给初始横向误差,看控制器能不能把 (e_y) 拉回零;再把曲率前馈加上,看弯道稳态误差有没有降下来。信号命名要统一,e_y不要一会儿叫lat_err一会儿叫y_error,否则 To Workspace 里导出后自己都认不出。
固定步长取 0.02 s,求解器选离散。控制器内部如果有连续积分或低通滤波,离散化后放在控制器里,不要依赖 Simulink 变步长去补。可视化不用做太花,XY Graph 看轨迹,Scope 看 (e_y) 和 (\delta) 就够。真正有用的是把solve_flag记录下来,QP 求解失败时控制器会保持上一拍转角,车上表现为“突然不转向”,只看轨迹图很难发现,必须看求解标志。
注意:控制器输出和车辆状态之间如果有直接反馈,Simulink 可能报代数环。加 Unit Delay 或把车辆模型离散化,比调求解器参数更可靠。
3.2 用 MATLAB Function 写滚动优化:输入输出与 QP 调用
下面这段是位置形式的简化骨架,决策变量是未来 (N_p) 拍的前轮转角本身,不是增量。这样做代码短、容易读,适合先把闭环跑通;增量形式和累积约束在 3.3 展开。mpc.Q是 2×2 输出权重,mpc.R是 1×1 控制量权重,mpc.delta_max是前轮转角上限。Free是初始状态自由响应,曲率扰动按 ( -v_x T_s \kappa k) 累加到航向误差预测上。
function [delta_cmd, exitflag] = mpc_step(x, delta_prev, ref, mpc) % x = [e_y; e_psi; vy; r] % ref = [e_y_ref; e_psi_ref; kappa] % delta_prev 仅用于求解失败时保持 persistent H Phi Theta Qbar Rbar if isempty(H) [Phi, Theta] = build_prediction(mpc.Ad, mpc.Bd, mpc.Cd, mpc.Np, mpc.Np); Qbar = kron(eye(mpc.Np), mpc.Q); Rbar = kron(eye(mpc.Np), mpc.R); H = 2 * (Theta' * Qbar * Theta + Rbar); H = (H + H') / 2; end Yref = repmat(ref(1:2), mpc.Np, 1); Free = Phi * x; for k = 1:mpc.Np Free(2*k) = Free(2*k) - mpc.vx * mpc.Ts * ref(3) * k; end f = 2 * Theta' * Qbar * (Free - Yref); lb = -mpc.delta_max * ones(mpc.Np, 1); ub = mpc.delta_max * ones(mpc.Np, 1); [U, ~, exitflag] = quadprog(H, f, [], [], [], [], lb, ub, [], mpc.qp_opts); if exitflag <= 0 delta_cmd = delta_prev; else delta_cmd = U(1); end end这段代码的逻辑分四步:用持久变量缓存 (H) 和预测矩阵,避免每步重算;把参考输出堆成 (2N_p) 维;把初始状态和曲率扰动合成自由响应;调用quadprog解带上下限的 QP。参数上,mpc.Q=diag([10,5])表示横向误差比航向误差更被看重,mpc.R=1表示对前轮转角幅值有惩罚。delta_max取 0.5 rad 左右,对应约 28 度前轮转角,实际乘用车更小,先按执行器真实上限设。qp_opts里把显示关掉、最大迭代设一个有限值,避免某一步卡死拖慢整个仿真。
这段代码没有增量约束,也没有软约束,所以它只适合做第一版闭环。你会看到方向盘在参考曲率突变时有点抖,这是正常的,因为控制量本身没有平滑惩罚。下一步把决策变量改成 (\Delta\delta),把 (\delta(k)=\delta(k-1)+\Delta\delta(k)) 写进预测模型,再对 (\Delta\delta) 加权重,抖动会明显下来。别一上来就追求完整版,先把 (e_y) 能收敛这件事确认了,再逐步加约束。
3.3 约束、松弛因子与执行器延迟怎么塞进去
位置形式换成增量形式,状态增广成 (x_a=[x;\delta_{prev}]),控制量 (u=\Delta\delta)。增广矩阵写成 (A_a=[A_d,B_d;0,1]),(B_a=[B_d;1]),(C_a=[C_d,0])。这样约束可以直接写 (| \delta(k) | \le \delta_{max}) 和 (| \Delta\delta(k) | \le \Delta\delta_{max})。前者防止前轮转角超过执行器上限,后者抑制方向盘高频抖动。常见取值是 (\delta_{max}=0.5) rad、(\Delta\delta_{max}=0.05) rad/步,具体要看执行器速率和控制器周期。
横向误差软约束用松弛因子 (\varepsilon),目标函数加 (\rho \varepsilon^2)。约束写成 (|e_y(k)| \le e_{y,max}+\varepsilon),(\varepsilon \ge 0)。这样参考路径突然跳变时 QP 不会直接不可行,而是用一点松弛换可行解。(\rho) 取 1000 到 10000 是常见起点,太小会让软约束形同虚设,太大又会让求解器在边界上反复试探。求解失败时不要直接把 (U(1)) 丢掉,保持上一拍转角并记录exitflag,比输出零转角安全得多。
执行器延迟有两种塞法。简单做法是在控制器输出后加 Unit Delay,闭环里多一拍延迟,MPC 预测模型不用改,但高速时相位滞后会体现出来。更稳的做法是把延迟增广进预测模型,用一阶惯性 (\delta_{act}(k+1)=a\delta_{act}(k)+(1-a)\delta_{cmd}(k)),(a) 根据执行器时间常数和 (T_s) 算。预测模型里多一个状态,QP 规模增加很小,但控制器会提前考虑延迟,弯道响应更干净。两种方法都试过之后,我一般会先加
本文还有配套的精品资源,点击获取