从阿克曼几何到 QP 求解器输入:自动驾驶横向 MPC 的完整数学链条(2)
9. 非线性误差模型如何线性化成 x˙=Ax+Bu+w\dot x = A x + B u + wx˙=Ax+Bu+w
9.1 定义状态和输入
设:
x=[eθeδ],u=[u]x= \begin{bmatrix} e\\ \theta_e\\ \delta \end{bmatrix}, \qquad u= \begin{bmatrix} u \end{bmatrix} x= eθeδ ,u=[u]
其中:
- eee:横向误差
- θe\theta_eθe:航向误差
- δ\deltaδ:实际转角
- uuu:转向命令
那么根据之前的推导,系统为:
e˙=vθe \dot e = v\theta_e e˙=vθe
θ˙e=vLtanδ−vκr \dot\theta_e = \frac{v}{L}\tan\delta - v\kappa_r θ˙e=Lvtanδ−vκr
δ˙=−1τδ+1τu \dot\delta = -\frac{1}{\tau}\delta + \frac{1}{\tau}u δ˙=−τ1δ+τ1u
这里唯一非线性项是:
tanδ \tan\delta tanδ
9.2 线性化点怎么选
对参考曲率 κr\kappa_rκr,理想前轮角是:
δr=arctan(Lκr) \delta_r = \arctan(L\kappa_r) δr=arctan(Lκr)
所以在这个工作点附近线性化最自然。
9.3 泰勒展开
设:
f(δ)=tanδ f(\delta)=\tan\delta f(δ)=tanδ
在 δr\delta_rδr 附近一阶展开:
tanδ≈tanδr+ddδtanδ∣δr(δ−δr) \tan\delta \approx \tan\delta_r + \frac{d}{d\delta}\tan\delta\Big|_{\delta_r}(\delta-\delta_r) tanδ≈tanδr+dδdtanδ δr(δ−δr)
而:
ddδtanδ=sec2δ=1cos2δ \frac{d}{d\delta}\tan\delta = \sec^2\delta = \frac{1}{\cos^2\delta} dδdtanδ=sec2δ=cos2δ1
所以:
tanδ≈tanδr+sec2δr(δ−δr) \tan\delta \approx \tan\delta_r + \sec^2\delta_r(\delta-\delta_r) tanδ≈tanδr+sec2δr(δ−δr)
展开:
tanδ≈tanδr−δrsec2δr+sec2δr δ \tan\delta \approx \tan\delta_r - \delta_r\sec^2\delta_r + \sec^2\delta_r \, \delta tanδ≈tanδr−δrsec2δr+sec2δrδ
9.4 代回 θ˙e\dot\theta_eθ˙e
原式:
θ˙e=vLtanδ−vκr \dot\theta_e = \frac{v}{L}\tan\delta - v\kappa_r θ˙e=Lvtanδ−vκr
代入线性化:
θ˙e=vLsec2δr,δ+[−vκr+vL(tanδr−δrsec2δr)] \dot\theta_e= \frac{v}{L}\sec^2\delta_r,\delta+ \left[ -v\kappa_r + \frac{v}{L}\left(\tan\delta_r-\delta_r\sec^2\delta_r\right) \right] θ˙e=Lvsec2δr,δ+[−vκr+Lv(tanδr−δrsec2δr)]
9.5 得到线性仿射系统
于是:
x˙=Ax+Bu+w \dot x = A x + B u + w x˙=Ax+Bu+w
其中:
A=[0v000vLsec2δr00−1τ] A= \begin{bmatrix} 0 & v & 0\\ 0 & 0 & \frac{v}{L}\sec^2\delta_r\\ 0 & 0 & -\frac{1}{\tau} \end{bmatrix} A= 000v000Lvsec2δr−τ1
B=[001τ] B= \begin{bmatrix} 0\\ 0\\ \frac{1}{\tau} \end{bmatrix} B= 00τ1
w=[0−vκr+vL(tanδr−δrsec2δr)0] w= \begin{bmatrix} 0\\ -v\kappa_r+\frac{v}{L}\left(\tan\delta_r-\delta_r\sec^2\delta_r\right)\\ 0 \end{bmatrix} w= 0−vκr+Lv(tanδr−δrsec2δr)0
这里的 www 是线性化偏置项,用来表达常数项,不是噪声。
10. 连续时间模型如何离散化成 xk+1=Adxk+Bduk+Wdx_{k+1}=A_d x_k+B_d u_k+W_dxk+1=Adxk+Bduk+Wd
10.1 连续系统
从:
x˙=Ax+Bu+w \dot x = A x + B u + w x˙=Ax+Bu+w
出发。
10.2 为什么要离散化
MPC 是数字控制器,只在离散时刻工作:
tk=k dt t_k = k\,dt tk=kdt
所以需要把连续系统变成一步一步更新的离散系统。
10.3 最简单欧拉法(先作为参考)
用:
x˙(tk)≈xk+1−xkdt \dot x(t_k)\approx \frac{x_{k+1}-x_k}{dt} x˙(tk)≈dtxk+1−xk
可得:
xk+1≈(I+dtA)xk+dtBuk+dtw x_{k+1}\approx (I+dtA)x_k + dtB u_k + dtw xk+1≈(I+dtA)xk+dtBuk+dtw
但工程里常用更稳定的 Tustin / 双线性离散化。
10.4 Tustin 推导
从积分形式:
xk+1−xk=∫tktk+1(Ax+Bu+w),dt x_{k+1}-x_k= \int_{t_k}^{t_{k+1}} (Ax + Bu + w),dt xk+1−xk=∫tktk+1(Ax+Bu+w),dt
用梯形法近似:
∫f(t) dt≈dt2(fk+fk+1) \int f(t) \, dt \approx \frac{dt}{2}(f_k + f_{k+1}) ∫f(t)dt≈2dt(fk+fk+1)
于是:
xk+1−xk≈dt2[(Axk+Buk+w)+(Axk+1+Buk+w)] x_{k+1}-x_k \approx \frac{dt}{2} \Big[ (Ax_k+Bu_k+w) + (Ax_{k+1}+Bu_k+w) \Big] xk+1−xk≈2dt[(Axk+Buk+w)+(Axk+1+Buk+w)]
整理:
(I−dt2A)xk+1=(I+dt2A)xk+dtBuk+dtw \left(I-\frac{dt}{2}A\right)x_{k+1}= \left(I+\frac{dt}{2}A\right)x_k+ dtBu_k+ dtw (I−2dtA)xk+1=(I+2dtA)xk+dtBuk+dtw
左乘逆矩阵:
xk+1=(I−dt2A)−1(I+dt2A)xk+(I−dt2A)−1B dt uk+(I−dt2A)−1w dt x_{k+1}= \left(I-\frac{dt}{2}A\right)^{-1}\left(I+\frac{dt}{2}A\right)x_k+ \left(I-\frac{dt}{2}A\right)^{-1}B \, dt \, u_k+ \left(I-\frac{dt}{2}A\right)^{-1}w \, dt xk+1=(I−2dtA)−1(I+2dtA)xk+(I−2dtA)−1Bdtuk+(I−2dtA)−1wdt
定义:
Ad=(I−dt2A)−1(I+dt2A) A_d=\left(I-\frac{dt}{2}A\right)^{-1}\left(I+\frac{dt}{2}A\right) Ad=(I−2dtA)−1(I+2dtA)
Bd=(I−dt2A)−1B dt B_d=\left(I-\frac{dt}{2}A\right)^{-1}B \, dt Bd=(I−2dtA)−1Bdt
Wd=(I−dt2A)−1w dt W_d=\left(I-\frac{dt}{2}A\right)^{-1}w \, dt Wd=(I−2dtA)−1wdt
所以:
xk+1=Adxk+Bduk+Wd x_{k+1}=A_d x_k + B_d u_k + W_d xk+1=Adxk+Bduk+Wd
11. 单步模型为什么还不够:MPC 为什么必须做未来时域展开
单步模型只告诉你:
如果现在给一个 uku_kuk,下一步状态会怎样。
但 MPC 要做的是:
同时优化未来 NNN 步的输入,让未来整个误差轨迹最好。
所以必须引入:
Uex=[u0u1⋮uN−1] U_{ex}= \begin{bmatrix} u_0\\ u_1\\ \vdots\\ u_{N-1} \end{bmatrix} Uex= u0u1⋮uN−1
并预测:
Xex=[x1x2⋮xN] X_{ex}= \begin{bmatrix} x_1\\ x_2\\ \vdots\\ x_N \end{bmatrix} Xex= x1x2⋮xN
12. 从单步离散模型到批量预测方程
12.1 单步模型
对每一步:
xk+1=Akxk+Bkuk+Wk x_{k+1}=A_kx_k+B_ku_k+W_k xk+1=Akxk+Bkuk+Wk
注意这里是时变系统,所以每一步矩阵可能不同。
12.2 展开前几步
第一步:
x1=A0x0+B0u0+W0 x_1 = A_0 x_0 + B_0 u_0 + W_0 x1=A0x0+B0u0+W0
第二步:
x2=A1x1+B1u1+W1 x_2 = A_1x_1 + B_1u_1 + W_1 x2=A1x1+B1u1+W1
代入 x1x_1x1:
x2=A1A0x0+A1B0u0+B1u1+A1W0+W1 x_2=A_1A_0x_0+ A_1B_0u_0+ B_1u_1+A_1W_0+W_1 x2=A1A0x0+A1B0u0+B1u1+A1W0+W1
第三步:
x3=A2x2+B2u2+W2 x_3 = A_2x_2 + B_2u_2 + W_2 x3=A2x2+B2u2+W2
代入 x2x_2x2:
x3=A2A1A0x0+A2A1B0u0+A2B1u1+B2u2+A2A1W0+A2W1+W2 x_3=A_2A_1A_0x_0+ A_2A_1B_0u_0+ A_2B_1u_1+ B_2u_2+ A_2A_1W_0 + A_2W_1 + W_2 x3=A2A1A0x0+A2A1B0u0+A2B1u1+B2u2+A2A1W0+A2W1+W2
12.3 堆叠形式
把所有状态堆起来:
Xex=[x1x2x3⋮xN] X_{ex}= \begin{bmatrix} x_1\\ x_2\\ x_3\\ \vdots\\ x_N \end{bmatrix} Xex= x1x2x3⋮xN
则可以写成:
Xex=Aexx0+BexUex+Wex X_{ex}=A_{ex}x_0+B_{ex}U_{ex}+W_{ex} Xex=Aexx0+BexUex+Wex
12.4 AexA_{ex}Aex
Aex=[A0A1A0A2A1A0⋮] A_{ex}= \begin{bmatrix} A_0\\ A_1A_0\\ A_2A_1A_0\\ \vdots \end{bmatrix} Aex= A0A1A0A2A1A0⋮
12.5 BexB_{ex}Bex
Bex=[B000⋯A1B0B10⋯A2A1B0A2B1B2⋯⋮⋮⋮⋱] B_{ex}= \begin{bmatrix} B_0 & 0 & 0 & \cdots\\ A_1B_0 & B_1 & 0 & \cdots\\ A_2A_1B_0 & A_2B_1 & B_2 & \cdots\\ \vdots & \vdots & \vdots & \ddots \end{bmatrix} Bex= B0A1B0A2A1B0⋮0B1A2B1⋮00B2⋮⋯⋯⋯⋱
这是一个块下三角矩阵。
12.6 WexW_{ex}Wex
Wex=[W0A1W0+W1A2A1W0+A2W1+W2⋮] W_{ex}= \begin{bmatrix} W_0\\ A_1W_0 + W_1\\ A_2A_1W_0 + A_2W_1 + W_2\\ \vdots \end{bmatrix} Wex= W0A1W0+W1A2A1W0+A2W1+W2⋮
12.7 输出展开
定义:
Yex=CexXex Y_{ex} = C_{ex}X_{ex} Yex=CexXex
yk=[ekθe,k] y_k= \begin{bmatrix} e_k\\ \theta_{e,k} \end{bmatrix} yk=[ekθe,k]
这里的YexY_{ex}Yex用于后续做约束,求解最小值。
其中:
Ck=[100010] C_k= \begin{bmatrix} 1&0&0\\ 0&1&0 \end{bmatrix} Ck=[100100]
Cex=diag(C0,C1,…,CN−1) C_{ex}=\operatorname{diag}(C_0,C_1,\dots,C_{N-1}) Cex=diag(C0,C1,…,CN−1)
13. 代价函数是怎么设计出来的,为什么是二次型
代价函数不是“物理定律推导出来的”,而是根据控制目标设计出来的。
13.1 目标 1:跟踪误差小
希望:
- 横向误差小
- 航向误差小
设每步输出为:
yk=Ckxk y_k = C_k x_k yk=Ckxk
单步惩罚:
ykTQkyk y_k^T Q_k y_k ykTQkyk
堆叠后:
Jstate=YexTQexYex J_{\text{state}} = Y_{ex}^T Q_{ex}Y_{ex} Jstate=YexTQexYex
其中:
Qex=diag(Q0,Q1,…,QN−1) Q_{ex}=\operatorname{diag}(Q_0,Q_1,\dots,Q_{N-1}) Qex=diag(Q0,Q1,…,QN−1)
其中 Qk⪰0Q_k \succeq 0Qk⪰0 是权重矩阵。
13.2 目标 2:输入不要过大,尽量接近轨迹角度
不希望控制器猛打方向,即输入不要太大。但更合理的是让输入靠近参考前馈输入:
uref,k=arctan(Lκk) u_{ref,k}=\arctan(L\kappa_k) uref,k=arctan(Lκk)
于是:
Jinput=(Uex−Uref,ex)TR1ex(Uex−Uref,ex) J_{\text{input}}= (U_{ex}-U_{ref,ex})^T R_{1ex}(U_{ex}-U_{ref,ex}) Jinput=(Uex−Uref,ex)TR1ex(Uex−Uref,ex)
13.3 目标 3:输入变化要平滑
为了避免转向抖动,希望:
uk−uk−1 u_k-u_{k-1} uk−uk−1
尽量小,惩罚:
(uk−uk−1)2 (u_k-u_{k-1})^2 (uk−uk−1)2
堆叠后可写成:
Jsmooth=UexTR2exUex J_{\text{smooth}} = U_{ex}^T R_{2ex} U_{ex} Jsmooth=UexTR2exUex
其中 R2exR_{2ex}R2ex 编码了输入差分的二次项。
13.4 最终总代价函数
J=YexTQexYex+(Uex−Uref,ex)TR1ex(Uex−Uref,ex)+UexTR2exUex J= Y_{ex}^T Q_{ex}Y_{ex}+ (U_{ex}-U_{ref,ex})^T R_{1ex}(U_{ex}-U_{ref,ex})+ U_{ex}^T R_{2ex} U_{ex} J=YexTQexYex+(Uex−Uref,ex)TR1ex(Uex−Uref,ex)+UexTR2exUex
再由于:
Yex=CexXex Y_{ex}=C_{ex}X_{ex} Yex=CexXex
也可写成:
J=XexTCexTQexCexXex+(Uex−Uref,ex)TR1ex(Uex−Uref,ex)+UexTR2exUex J= X_{ex}^T C_{ex}^T Q_{ex} C_{ex} X_{ex}+ (U_{ex}-U_{ref,ex})^T R_{1ex}(U_{ex}-U_{ref,ex})+ U_{ex}^T R_{2ex} U_{ex} J=XexTCexTQexCexXex+(Uex−Uref,ex)TR1ex(Uex−Uref,ex)+UexTR2exUex
14. 把预测方程代入代价函数:如何变成只关于输入的优化问题
现在关键来了。
已经有:
Xex=Aexx0+BexUex+Wex X_{ex}=A_{ex}x_0+B_{ex}U_{ex}+W_{ex} Xex=Aexx0+BexUex+Wex
Yex=CexXex Y_{ex}=C_{ex}X_{ex} Yex=CexXex
所以:
Yex=Cex(Aexx0+BexUex+Wex) Y_{ex}=C_{ex}(A_{ex}x_0+B_{ex}U_{ex}+W_{ex}) Yex=Cex(Aexx0+BexUex+Wex)
定义简写:
A:=Aex,B:=Bex,C:=Cex,Q:=Qex,R1:=R1ex,R2:=R2ex,U:=Uex,Ur:=Uref,ex,W:=Wex A:=A_{ex},\quad B:=B_{ex},\quad C:=C_{ex},\quad Q:=Q_{ex},\quad R_1:=R_{1ex},\quad R_2:=R_{2ex},\quad U:=U_{ex},\quad U_r:=U_{ref,ex},\quad W:=W_{ex} A:=Aex,B:=Bex,C:=Cex,Q:=Qex,R1:=R1ex,R2:=R2ex,U:=Uex,Ur:=Uref,ex,W:=Wex
则:
X=Ax0+BU+W X = Ax_0 + BU + W X=Ax0+BU+W
Y=C(Ax0+BU+W) Y = C(Ax_0 + BU + W) Y=C(Ax0+BU+W)
设:
d:=C(Ax0+W) d := C(Ax_0 + W) d:=C(Ax0+W)
则:
Y=d+CBU Y = d + CBU Y=d+CBU
于是状态项:
Jstate=(d+CBU)TQ(d+CBU) J_{\text{state}}= (d+CBU)^TQ(d+CBU) Jstate=(d+CBU)TQ(d+CBU)
展开:
Jstate=UTBTCTQCBU+2dTQCBU+dTQd J_{\text{state}}= U^T B^T C^T Q C B U+ 2d^TQCBU+ d^TQd Jstate=UTBTCTQCBU+2dTQCBU+dTQd
输入项:
(U−Ur)TR1(U−Ur)=UTR1U−2UTR1Ur+UrTR1Ur (U-U_r)^TR_1(U-U_r)= U^TR_1U - 2U^TR_1U_r + U_r^TR_1U_r (U−Ur)TR1(U−Ur)=UTR1U−2UTR1Ur+UrTR1Ur
平滑项:
UTR2U U^TR_2U UTR2U
合并后:
J=UT(BTCTQCB+R1+R2)U+2(dTQCB−UrTR1)U+const J= U^T(B^TC^TQCB+R_1+R_2)U+ 2(d^TQCB-U_r^TR_1)U+ \text{const} J=UT(BTCTQCB+R1+R2)U+2(dTQCB−UrTR1)U+const
其中常数项与 UUU 无关,不影响优化,可以忽略。
15. 输入约束和输入变化率约束如何写成 QP 形式
前面我们已经把代价函数整理成了只关于未来输入序列 UUU 的二次型。
但只有目标函数还不够,因为真实车辆永远存在物理限制:
- 转角不能无限大
- 转角变化不能无限快
因此,MPC 不是一个“只看目标函数”的无约束优化,而是一个带线性约束的二次优化问题。
也正因为如此,它最后自然落到 QP 这个数学形式上。
15.1 优化变量到底是什么
在这一层里,真正被优化的不是状态 xxx,而是未来一段时间内的控制输入序列。
把未来 NNN 步输入堆叠起来:
U=[u0u1⋮uN−1]∈RNu U = \begin{bmatrix} u_0\\ u_1\\ \vdots\\ u_{N-1} \end{bmatrix} \in \mathbb R^{N_u} U= u0u1⋮uN−1 ∈RNu
这里:
- 若每一步只有一个转向输入,则 Nu=NN_u=NNu=N
- 更一般地,若每一步输入维度为 DIMU\mathrm{DIM}_UDIMU,则 Nu=N⋅DIMUN_u=N\cdot \mathrm{DIM}_UNu=N⋅DIMU
这一步一定要想清楚,因为 QP 求解器根本不直接“优化轨迹”或“优化误差”,它优化的是:
未来一串控制量 UUU
而未来状态 XexX_{ex}Xex、未来输出 YexY_{ex}Yex 都只是这个输入序列通过预测模型推出来的结果。
15.2 输入幅值约束为什么是 box 约束
如果转向命令存在物理上限 δmax\delta_{\max}δmax,那么每一步都必须满足:
−δmax≤uk≤δmax -\delta_{\max} \le u_k \le \delta_{\max} −δmax≤uk≤δmax
把全部时刻堆叠起来后,就得到最简单的一类约束:
lb≤U≤ub lb \le U \le ub lb≤U≤ub
其中:
lb=−δmax1,ub=δmax1 lb = -\delta_{\max}\mathbf 1,\qquad ub = \delta_{\max}\mathbf 1 lb=−δmax1,ub=δmax1
这类约束叫 box 约束,本质上就是:
每个优化变量单独都有一个上下界
从几何上看,它在输入空间里对应一个超矩形区域。
QP 求解器只能在这个可行区域内寻找最优 UUU。
15.3 输入变化率约束从哪里来
真实车辆除了“能打多大方向”之外,还有“能多快打方向”的限制。
若连续时间下存在转角速度上限:
∣u˙(t)∣≤δ˙max(t) |\dot u(t)| \le \dot\delta_{\max}(t) ∣u˙(t)∣≤δ˙max(t)
那么离散化之后,相邻两步输入之间就不能差太多。
在第 kkk 步,可写成:
−δ˙max,kΔt≤uk−uk−1≤δ˙max,kΔt -\dot\delta_{\max,k}\Delta t \le u_k-u_{k-1} \le \dot\delta_{\max,k}\Delta t −δ˙max,kΔt≤uk−uk−1≤δ˙max,kΔt
这条式子的物理意义非常直接:
- 左边限制“不要往负方向变得太快”
- 右边限制“不要往正方向变得太快”
如果 δ˙max,k\dot\delta_{\max,k}δ˙max,k 随速度、曲率或工况变化,那么它就不再是一个常数,而是一组逐步变化的上限,这也是工程实现里常见的做法。
15.4 为什么差分约束可以写成矩阵形式
QP 喜欢的约束形式是仿射不等式:
lbA≤AineqU≤ubA lbA \le A_{\text{ineq}}U \le ubA lbA≤AineqU≤ubA
所以关键问题变成:
如何把“相邻输入之差”写成矩阵乘法
定义差分矩阵:
D=[100⋯−110⋯0−11⋯⋮⋮⋮⋱] D = \begin{bmatrix} 1 & 0 & 0 & \cdots\\ -1 & 1 & 0 & \cdots\\ 0 & -1 & 1 & \cdots\\ \vdots & \vdots & \vdots & \ddots \end{bmatrix} D= 1−10⋮01−1⋮001⋮⋯⋯⋯⋱
则:
DU=[u0u1−u0u2−u1⋮] DU = \begin{bmatrix} u_0\\ u_1-u_0\\ u_2-u_1\\ \vdots \end{bmatrix} DU= u0u1−u0u2−u1⋮
这条式子就是整个输入变化率约束写成 QP 形式的核心。
它告诉我们:
- 第一行取出的是 u0u_0u0
- 第二行取出的是 u1−u0u_1-u_0u1−u0
- 第三行取出的是 u2−u1u_2-u_1u2−u1
- 以此类推
因此,原本逐项写的差分约束,现在可以统一改写为:
lbA≤DU≤ubA lbA \le D U \le ubA lbA≤DU≤ubA
这一步的本质是“结构重写”:
没有改变任何物理含义,只是把一串标量不等式改写成了一个矩阵不等式
15.5 为什么第一项要单独处理
对 k≥1k\ge 1k≥1 的项,差分总是:
uk−uk−1 u_k-u_{k-1} uk−uk−1
但对第一步来说,并不存在预测时域内部的 u−1u_{-1}u−1。
第一个控制量真正要比较的对象,不是“预测时域中的前一个量”,而是:
上一个控制周期已经实际发出去的控制命令
记它为 uprevu_{\mathrm{prev}}uprev,控制周期为 TcT_cTc,则第一项约束应写成:
uprev−δ˙max,0Tc≤u0≤uprev+δ˙max,0Tc u_{\mathrm{prev}}-\dot\delta_{\max,0}T_c \le u_0 \le u_{\mathrm{prev}}+\dot\delta_{\max,0}T_c uprev−δ˙max,0Tc≤u0≤uprev+δ˙max,0Tc
这非常重要,因为它把当前优化问题和上一拍真实执行过的控制量接起来了。
没有这一步,优化器就可能在当前时刻给出一个相对上一拍跳变过大的新命令。
所以完整的差分约束实际是:
DU=[u0u1−u0u2−u1⋮] D U = \begin{bmatrix} u_0\\ u_1-u_0\\ u_2-u_1\\ \vdots \end{bmatrix} DU= u0u1−u0u2−u1⋮
并配套边界:
lbA=[uprev−δ˙max,0Tc−δ˙max,1Δt−δ˙max,2Δt⋮] lbA = \begin{bmatrix} u_{\mathrm{prev}}-\dot\delta_{\max,0}T_c\\ -\dot\delta_{\max,1}\Delta t\\ -\dot\delta_{\max,2}\Delta t\\ \vdots \end{bmatrix} lbA= uprev−δ˙max,0Tc−δ˙max,1Δt−δ˙max,2Δt⋮
ubA=[uprev+δ˙max,0Tcδ˙max,1Δtδ˙max,2Δt⋮] ubA = \begin{bmatrix} u_{\mathrm{prev}}+\dot\delta_{\max,0}T_c\\ \dot\delta_{\max,1}\Delta t\\ \dot\delta_{\max,2}\Delta t\\ \vdots \end{bmatrix} ubA= uprev+δ˙max,0Tcδ˙max,1Δtδ˙max,2Δt⋮
这样写以后,第 0 行和后续各行就被统一装进了同一个矩阵不等式里。
15.6 最终约束的统一写法
因此,这个 MPC 对输入的约束可以概括成两组:
幅值约束
lb≤U≤ub lb \le U \le ub lb≤U≤ub
变化率约束
lbA≤DU≤ubA lbA \le D U \le ubA lbA≤DU≤ubA
如果愿意,也可以进一步把二者堆成一个更大的线性约束系统:
[lblbA]≤[ID]U≤[ububA] \begin{bmatrix} lb\\ lbA \end{bmatrix} \le \begin{bmatrix} I\\ D \end{bmatrix}U \le \begin{bmatrix} ub\\ ubA \end{bmatrix} [lblbA]≤[ID]U≤[ububA]
这说明一个很本质的事实:
只要模型是线性的、约束是对输入的线性不等式、代价函数是二次型,问题就天然是 QP
16. QP 求解器到底在解什么数学问题
从原理上说,MPC 在这一层解的是:
一个关于未来输入序列 UUU 的凸二次优化问题
它之所以是“二次”的,来自二次代价函数;
它之所以是“带约束”的,来自输入幅值和输入变化率限制。
16.1 为什么 MPC 最后会落到 QP
回顾前面的链条:
- 预测模型是线性的:
Xex=Aexx0+BexUex+Wex X_{ex}=A_{ex}x_0+B_{ex}U_{ex}+W_{ex} Xex=Aexx0+BexUex+Wex - 输出也是线性的:
Yex=CexXex Y_{ex}=C_{ex}X_{ex} Yex=CexXex - 代价函数是二次型:
J=YexTQexYex+(Uex−Uref,ex)TR1ex(Uex−Uref,ex)+UexTR2exUex J= Y_{ex}^TQ_{ex}Y_{ex} +(U_{ex}-U_{ref,ex})^TR_{1ex}(U_{ex}-U_{ref,ex}) +U_{ex}^TR_{2ex}U_{ex} J=YexTQexYex+(Uex−Uref,ex)TR1ex(Uex−Uref,ex)+UexTR2exUex - 约束是线性不等式:
lb≤U≤ub,lbA≤DU≤ubA lb \le U \le ub,\qquad lbA \le D U \le ubA lb≤U≤ub,lbA≤DU≤ubA
这四点一组合,就已经把问题的类型完全定死了:
线性预测模型 + 二次代价函数 + 线性约束
就是标准的 Quadratic Programming
所以 QP 不是“额外选的求解工具”,而是这套建模自然导出的数学结果。
16.2 把输出写成关于 UUU 的仿射函数
从预测方程出发:
X=Ax0+BU+W X = Ax_0 + BU + W X=Ax0+BU+W
Y=CX=C(Ax0+BU+W) Y = CX = C(Ax_0 + BU + W) Y=CX=C(Ax0+BU+W)
把与 UUU 无关的部分记为:
d:=C(Ax0+W) d := C(Ax_0 + W) d:=C(Ax0+W)
把输入到输出的映射记为:
M:=CB M := CB M:=CB
于是:
Y=d+MU Y = d + MU Y=d+MU
这一步非常关键。它告诉我们:
- ddd 描述“在当前状态 x0x_0x0、当前参考轨迹和偏置项 WWW 已知时,不考虑优化变量也会自然产生的输出偏移”
- MUMUMU 描述“未来输入序列 UUU 会怎样改变整个预测输出”
因此,从优化角度看,问题已经被压缩成:
用输入序列 UUU 去修正一个已有偏移 ddd
16.3 从总代价函数推到二次型
把:
Y=d+MU Y=d+MU Y=d+MU
代回总代价函数:
J=YTQY+(U−Ur)TR1(U−Ur)+UTR2U J= Y^TQY+(U-U_r)^TR_1(U-U_r)+U^TR_2U J=YTQY+(U−Ur)TR1(U−Ur)+UTR2U
得到:
J=(d+MU)TQ(d+MU)+(U−Ur)TR1(U−Ur)+UTR2U J= (d+MU)^TQ(d+MU) + (U-U_r)^TR_1(U-U_r) + U^TR_2U J=(d+MU)TQ(d+MU)+(U−Ur)TR1(U−Ur)+UTR2U
下面分三部分展开。
第一部分:状态跟踪项
(d+MU)TQ(d+MU)=UTMTQMU+2dTQMU+dTQd (d+MU)^TQ(d+MU)= U^TM^TQMU + 2d^TQMU + d^TQd (d+MU)TQ(d+MU)=UTMTQMU+2dTQMU+dTQd
这里:
- UTMTQMUU^TM^TQMUUTMTQMU 是二次项,表示输入变化对跟踪误差带来的二阶影响
- 2dTQMU2d^TQMU2dTQMU 是一次项,表示当前偏差 ddd 会把最优解往某个方向“推过去”
- dTQdd^TQddTQd 是常数项,不依赖于 UUU
第二部分:输入偏离前馈项
(U−Ur)TR1(U−Ur)=UTR1U−2UrTR1U+UrTR1Ur (U-U_r)^TR_1(U-U_r)= U^TR_1U -2U_r^TR_1U +U_r^TR_1U_r (U−Ur)TR1(U−Ur)=UTR1U−2UrTR1U+UrTR1Ur
这里:
- UTR1UU^TR_1UUTR1U 惩罚输入本身过大
- −2UrTR1U-2U_r^TR_1U−2UrTR1U 体现“最优输入倾向于贴近参考前馈输入 UrU_rUr”
- UrTR1UrU_r^TR_1U_rUrTR1Ur 仍然是常数项
第三部分:输入平滑项
UTR2U U^TR_2U UTR2U
它本身已经是标准二次型,无需再展开。
16.4 合并得到标准二次目标函数
把三部分合并:
J=UT(MTQM+R1+R2)U+2(dTQM−UrTR1)U+const J=U^T(M^TQM+R_1+R_2)U+ 2(d^TQM-U_r^TR_1)U+ \text{const} J=UT(MTQM+R1+R2)U+2(dTQM−UrTR1)U+const
记:
G:=MTQM+R1+R2 G := M^TQM+R_1+R_2 G:=MTQM+R1+R2
c:=MTQd−R1Ur c := M^TQd-R_1U_r c:=MTQd−R1Ur
则:
J(U)=UTGU+2cTU+const J(U)=U^TGU+2c^TU+\text{const} J(U)=UTGU+2cTU+const
由于常数项不影响最优解,可以省略,于是优化问题只剩下:
minU UTGU+2cTU \min_U\; U^TGU+2c^TU UminUTGU+2cTU
这已经是一个非常标准的二次优化问题了。
16.5 为什么教材里常写成 12UTHU+gTU\frac12 U^T H U + g^T U21UTHU+gTU
数学上,二次规划最常见的标准形式是:
minU12UTHU+gTU \min_U \frac12 U^T H U + g^T U Umin21UTHU+gTU
它和上面的写法完全等价,只是记号不同。
若从:
J(U)=UTGU+2cTU J(U)=U^TGU+2c^TU J(U)=UTGU+2cTU
变成标准形式,只需令:
H=2G,g=2c H = 2G,\qquad g = 2c H=2G,g=2c
于是:
12UTHU+gTU=12UT(2G)U+(2c)TU=UTGU+2cTU \frac12 U^T H U + g^T U= \frac12 U^T(2G)U + (2c)^TU= U^TGU + 2c^TU 21UTHU+gTU=21UT(2G)U+(2c)TU=UTGU+2cTU
所以这里的 12\frac1221 没有任何新的物理意义,它只是一个为了让导数更简洁的记号约定:
∇ (12UTHU)=HU(当 H 对称时) \nabla\!\left(\frac12 U^T H U\right)=HU \qquad (\text{当 } H \text{ 对称时}) ∇(21UTHU)=HU(当 H 对称时)
也就是说:
标准 QP 形式中的 12\frac1221 是“记号规范”,不是“新的控制假设”
16.6 Hessian 为什么一定是对称的
由定义:
G=MTQM+R1+R2 G = M^TQM + R_1 + R_2 G=MTQM+R1+R2
若:
Q⪰0,R1⪰0,R2⪰0 Q \succeq 0,\qquad R_1 \succeq 0,\qquad R_2 \succeq 0 Q⪰0,R1⪰0,R2⪰0
则:
MTQM⪰0 M^TQM \succeq 0 MTQM⪰0
因此:
G⪰0 G \succeq 0 G⪰0
从而标准形式中的:
H=2G H = 2G H=2G
也是对称半正定的。
这意味着:
- 目标函数是凸的
- 只要约束集合非空,优化问题就是凸 QP
- 若再进一步有 R1+R2≻0R_1+R_2 \succ 0R1+R2≻0,通常还能保证解更稳定、甚至唯一
这就是为什么在线性 MPC 里我们特别喜欢二次型代价函数:
它既能表达跟踪误差、输入大小、输入平滑性,又能保持问题的凸性
16.7 一次项 ggg 的物理意义是什么
很多人第一次学 QP 时,只记住了 Hessian,却不太明白一次项为什么存在。
从上面的推导可知:
c=MTQd−R1Ur c = M^TQd - R_1U_r c=MTQd−R1Ur
所以一次项的来源有两部分:
来源 1:当前偏差 ddd
若当前状态已经偏离参考,那么即便未来输入全为零,输出也会带着偏移量:
d=C(Ax0+W) d = C(Ax_0 + W) d=C(Ax0+W)
这一项会把优化器“推向能消除当前误差的方向”。
来源 2:前馈输入 UrU_rUr
若参考轨迹本身是弯的,那么理想情况下系统本来就不该围绕零输入工作,而应围绕某个参考转向输入工作:
Ur U_r Ur
这一项会把优化器“推向靠近参考前馈输入的方向”。
所以可以把一次项理解成:
当前误差和参考曲率共同告诉优化器:最优解应当朝哪个方向偏移
而 Hessian 则告诉优化器:
往那个方向偏移时,会付出多大的二次代价
16.8 如果没有约束,最优解长什么样
为了建立直觉,先暂时去掉所有约束,只看:
minU12UTHU+gTU \min_U \frac12 U^T H U + g^T U Umin21UTHU+gTU
若 HHH 可逆,则一阶最优条件为:
∇J=HU+g=0 \nabla J = HU + g = 0 ∇J=HU+g=0
因此:
U⋆=−H−1g U^\star = -H^{-1}g U⋆=−H−1g
这条式子很有启发性,因为它说明:
- 若没有物理限制,最优解只是一个线性代数问题
- 真正让问题变成“需要 QP 求解器”的,不是二次代价本身,而是线性约束的加入
一旦加入:
lb≤U≤ub,lbA≤DU≤ubA lb \le U \le ub,\qquad lbA \le D U \le ubA lb≤U≤ub,lbA≤DU≤ubA
最优解就不再是简单的闭式解,而必须在可行域边界和目标函数之间做折中。
16.9 最终的完整 QP 数学形式
因此,基于前面的预测模型、代价函数和输入约束,最终得到的 QP 可以完整写成:
minU12UTHU+gTU \min_U \frac12 U^T H U + g^T U Umin21UTHU+gTU
其中:
H=2(BexTCexTQexCexBex+R1ex+R2ex) H = 2(B_{ex}^TC_{ex}^TQ_{ex}C_{ex}B_{ex}+R_{1ex}+R_{2ex}) H=2(BexTCexTQexCexBex+R1ex+R2ex)
g=2(BexTCexTQexCex(Aexx0+Wex)−R1exUref,ex) g = 2\Big( B_{ex}^TC_{ex}^TQ_{ex}C_{ex}(A_{ex}x_0+W_{ex}) -R_{1ex}U_{ref,ex} \Big) g=2(BexTCexTQexCex(Aexx0+Wex)−R1exUref,ex)
满足:
lb≤U≤ub lb \le U \le ub lb≤U≤ub
lbA≤DU≤ubA lbA \le D U \le ubA lbA≤DU≤ubA
其中:
lb=−δmax1,ub=δmax1 lb = -\delta_{\max}\mathbf 1,\qquad ub = \delta_{\max}\mathbf 1 lb=−δmax1,ub=δmax1
并且:
lbA=[uprev−δ˙max,0Tc−δ˙max,1Δt⋮],ubA=[uprev+δ˙max,0Tcδ˙max,1Δt⋮] lbA = \begin{bmatrix} u_{\mathrm{prev}}-\dot\delta_{\max,0}T_c\\ -\dot\delta_{\max,1}\Delta t\\ \vdots \end{bmatrix}, \qquad ubA = \begin{bmatrix} u_{\mathrm{prev}}+\dot\delta_{\max,0}T_c\\ \dot\delta_{\max,1}\Delta t\\ \vdots \end{bmatrix} lbA= uprev−δ˙max,0Tc−δ˙max,1Δt⋮ ,ubA= uprev+δ˙max,0Tcδ˙max,1Δt⋮
这就是求解器真正面对的数学问题。
如果某个实现把目标函数写成:
UTHU+2fTU U^THU + 2f^TU UTHU+2fTU
那只是符号约定不同,和上面的标准形式完全等价。
本质上,求解器解的始终是同一个问题:
在物理约束允许的范围内,寻找一串未来控制输入,使未来预测误差最小、输入不过大、输入变化又足够平滑
16.10 这个 QP 一般是怎么求解的
到这里为止,我们已经把 MPC 写成了一个标准凸二次规划:
minU12UTHU+gTU \min_U \frac12 U^T H U + g^T U Umin21UTHU+gTU
满足:
lb≤U≤ub,lbA≤DU≤ubA lb \le U \le ub,\qquad lbA \le D U \le ubA lb≤U≤ub,lbA≤DU≤ubA
先看没有约束时会怎样
如果暂时把所有约束都拿掉,那么问题退化为:
minU12UTHU+gTU \min_U \frac12 U^T H U + g^T U Umin21UTHU+gTU
对 UUU 求导并令梯度为零:
∇J=HU+g=0 \nabla J = HU + g = 0 ∇J=HU+g=0
因此,若 HHH 可逆,则:
U⋆=−H−1g U^\star = -H^{-1}g U⋆=−H−1g
这说明一件很重要的事:
- 二次目标函数本身并不难
- 真正让问题变成 QP 的,是约束
一旦加入转角幅值约束和转角变化率约束,最优解就不能再直接写成一个闭式公式,而必须在“目标最优”和“约束可行”之间寻找平衡。
统一写成单边不等式
为了更方便讨论求解原理,可以把双边约束统一改写成单边不等式:
GU≤h G U \le h GU≤h
例如可以写成:
G=[I−ID−D],h=[ub−lbubA−lbA] G = \begin{bmatrix} I\\ -I\\ D\\ -D \end{bmatrix}, \qquad h = \begin{bmatrix} ub\\ -lb\\ ubA\\ -lbA \end{bmatrix} G= I−ID−D ,h= ub−lbubA−lbA
于是整个问题变成:
minU12UTHU+gTU,GU≤h \min_U \frac12 U^T H U + g^T U,\qquad G U \le h Umin21UTHU+gTU,GU≤h
这就是教科书里最常见的凸 QP 形式。
最优解满足什么条件:KKT 条件
对这个带不等式约束的凸 QP,最优解满足 KKT 条件。
引入拉格朗日乘子:
λ≥0 \lambda \ge 0 λ≥0
构造拉格朗日函数:
L(U,λ)=12UTHU+gTU+λT(GU−h) \mathcal L(U,\lambda)= \frac12 U^T H U + g^T U + \lambda^T(GU-h) L(U,λ)=21UTHU+gTU+λT(GU−h)
最优解满足:
HU+g+GTλ=0 HU + g + G^T\lambda = 0 HU+g+GTλ=0
GU−h≤0 GU - h \le 0 GU−h≤0
λ≥0 \lambda \ge 0 λ≥0
λi(GU−h)i=0,∀i \lambda_i (GU-h)_i = 0,\qquad \forall i λi(GU−h)i=0,∀i
这四条分别表示:
- 驻点条件:目标函数梯度与约束反作用力平衡
- 原始可行性:解必须满足原约束
- 对偶可行性:拉格朗日乘子非负
- 互补松弛:只有真正“卡住”的约束才会对应非零乘子
所以从本质上说,所有 QP 求解器都在做同一件事:
找到一组满足 KKT 条件的 (U,λ)(U,\lambda)(U,λ)
常见求解方法 1:Active-Set
Active-Set 方法的核心想法是:
先猜哪些约束在最优点会正好卡住
把这些“活跃约束”记为:
GAU=hA G_A U = h_A GAU=hA
那么原问题暂时变成一个等式约束二次规划:
minU12UTHU+gTU,GAU=hA \min_U \frac12 U^T H U + g^T U,\qquad G_A U = h_A Umin21UTHU+gTU,GAU=hA
它对应的 KKT 线性系统为:
[HGATGA0][UλA]=[−ghA] \begin{bmatrix} H & G_A^T\\ G_A & 0 \end{bmatrix} \begin{bmatrix} U\\ \lambda_A \end{bmatrix}= \begin{bmatrix} -g\\ h_A \end{bmatrix} [HGAGAT0][UλA]=[−ghA]
解出之后,再检查:
- 有没有别的约束被违反
- 当前 active set 里的某些乘子是否变成负值
如果有,就调整活跃约束集合,再重复。
这个方法很适合 MPC,因为相邻时刻的问题往往很像,活跃约束集合也常常变化不大,所以 warm start 很有效。
常见求解方法 2:Interior-Point
Interior-Point 的思路不是去猜哪条约束活跃,而是始终待在可行域内部,然后逐渐逼近边界上的最优点。
做法通常是先引入松弛变量:
GU+s=h,s>0 GU + s = h,\qquad s>0 GU+s=h,s>0
再构造带对数障碍项的目标函数:
minU12UTHU+gTU−μ∑ilogsi \min_U \frac12 U^T H U + g^T U - \mu \sum_i \log s_i Umin21UTHU+gTU−μi∑logsi
其中 μ>0\mu>0μ>0 是 barrier 参数。
当 μ\muμ 较大时,解会远离约束边界;当 μ→0\mu \to 0μ→0 时,解会逐渐逼近原始 QP 的最优点。
这类方法的特点是:
- 数值上通常很稳健
- 对中大型问题很强
- 每一步往往需要解较大的牛顿线性系统
本质上,它是在“可行域内部”沿着一条中心路径逐步逼近最优解。
常见求解方法 3:Operator Splitting / ADMM
还有一类方法不直接一次性求解完整 KKT 系统,而是把问题拆成几个更容易的子问题交替求解。
典型思想是:
- 一步解一个带正则项的二次问题
- 一步把结果投影回约束集合
- 一步更新乘子或对偶变量
这类方法的代表就是 ADMM 风格的 QP 求解器。
它的优点通常是:
- 算法结构简单
- 很适合稀疏大规模问题
- 很适合嵌入式和实时优化
- warm start 也比较自然
它的缺点是:
- 相比高精度 interior-point,往往需要更多迭代
- 但对 MPC 这类“每拍都要很快给出一个够好的答案”的问题,往往已经非常合适
对 MPC 来说一般怎么选
从控制应用的角度看,常见选择大致是:
- 若问题规模中小、相邻时刻变化不大、希望充分利用 warm start,常用 Active-Set
- 若问题规模更大、追求稳健和高精度,常用 Interior-Point
- 若问题稀疏、强调实时性和工程实现便利,常用 ADMM / OSQP 这一类方法
17. 整套链条的统一大图景
总链条:
理想阿克曼几何
→ \tan\delta = L/R
→ 世界坐标自行车模型
→ Frenet坐标定义:e, θe
→ 精确非线性误差模型
→ 小误差近似
→ 执行器动态:τ
→ 连续时间线性仿射模型
→ Tustin离散化
→ 单步离散模型
→ N步批量预测方程
→ 代价函数设计(误差、输入、平滑)
→ 代入预测方程
→ QP标准形式
→ 输入与输入差分约束
→ QP求解器
→ 得到最优未来控制序列
→ 执行第一步输入并滚动优化
18. 常见误区与统一澄清
18.1 “代价函数是推导出来的吗?”
不是。
更准确地说:
- 动力学模型是推导出来的
- 代价函数是根据控制目标设计出来的
但一旦设计好,它就可以严格地代入模型并整理为 QP。
18.2 “τ\tauτ 是阿克曼几何里推出来的吗?”
不是。
τ\tauτ 是执行器动态参数,用来描述“转角不会瞬间到位”。
18.3 “为什么还要前馈输入 UrefU_{ref}Uref?”
因为沿参考曲率走本来就需要一个理想转角:
uref=arctan(Lκ) u_{ref}=\arctan(L\kappa) uref=arctan(Lκ)
MPC 只优化相对于这个前馈值的偏差,能让控制更自然,也减小误差负担。
18.4 “为什么平滑项会变成 UTR2UU^TR_2UUTR2U 而不是显式写 (ui−ui−1)2(u_i-u_{i-1})^2(ui−ui−1)2”
因为所有差分平方项展开后,本质上都能写成一个整体二次型:
UTR2U U^TR_2U UTR2U
这样求解器更容易处理。
18.5 “为什么最后只执行第一个输入?”
因为 MPC 是滚动优化。未来总会变化,所以:
- 先优化整个未来
- 但只执行眼前最优那一步
- 下一时刻重新测量、重新预测、重新优化
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)