MPC 学习路径
从 LQR 基础到约束优化和控制,逐步掌握 MPC 的核心原理与实践技能。本路径采用四层递进法,适合从入门到进阶的系统学习。
💡 0. 核心理念
为什么很多人学不透 MPC?
常见的错误学习方式:
- 一上来就读 Rawlings 的理论大部头,被稳定性证明劝退
- 只用 Matlab MPC Toolbox 调参,不理解底层 QP 是怎么构造的
- 跳过 LQR 直接学 MPC,不理解代价函数和 Riccati 方程的来龙去脉
四层递进法
1 第一层:基础原理(2-3 周)
在接触 MPC 之前,必须先掌握以下三个基础工具。MPC 本质上是 LQR 的在线滚动版本,没有 LQR 基础就去学 MPC,代价函数的由来永远搞不清楚。
1.1 状态空间模型
MPC 使用状态空间模型进行预测,这是最核心的数学工具。
必须掌握:
- 从微分方程/传递函数到状态空间的转换
- 能控性 (Controllability) 与能观性 (Observability)
- 状态空间模型的物理含义(每个状态变量代表什么)
1.2 LQR 最优控制
LQR 是 MPC 代价函数的直接前身。理解 LQR = 理解 MPC 代价函数的灵魂。
LQR 代价函数:
LQR 解:
其中 \(P\) 满足代数 Riccati 方程 (ARE):
必须理解:
- Q, R 矩阵的物理含义(权重越大,对应的量越小)
- Riccati 方程的求解(`dlqr` / `care` 函数背后做了什么)
- LQR 是无限时域、无约束的;MPC 是有限时域、有约束的
- MPC 的终端代价 P 就是 LQR 的 Riccati 解
1.3 离散化方法
物理系统大多是连续的,但 MPC 在计算机上实现必须是离散的。
| 方法 | 公式 | 精度 |
|---|---|---|
| 零阶保持 (ZOH) | \(A_d = e^{AT_s},\; B_d = \int_0^{T_s} e^{A\tau} d\tau \cdot B\) | 一阶(精确匹配阶跃响应) |
| 欧拉前向 | \(A_d = I + AT_s,\; B_d = B T_s\) | 一阶(\(T_s\) 需很小) |
| RK4 | 四步龙格-库塔积分 | 四阶(适用于非线性) |
1.4 状态估计基础
实际 MPC 中状态往往不能直接测量,需要观测器。
- Luenberger 观测器(确定性系统)
- Kalman 滤波器(噪声系统)
- 理解观测器与 MPC 的分离原理
1.5 自查清单
进入第二层之前,确认你能:
- ✅ 写出任意线性系统的状态空间表示
- ✅ 手解离散 LQR 的 Riccati 方程(NumPy 实现)
- ✅ 用 ZOH 离散化一个连续系统
- ✅ 在 Simulink 或 Python 中仿真 LQR 闭环
- ✅ 描述 LQR 和 MPC 的核心区别
2 第二层:MPC 核心(3-4 周)
这是最关键的阶段。目标:手推 MPC 的 QP 形式 + 手写一个能跑的 MPC 控制器。
2.1 预测模型推导
给定系统 \(x_{k+1} = Ax_k + Bu_k\),在时刻 \(k\) 已知 \(x_k = x_0\):
写成矩阵形式:
其中:
这是 MPC 最重要的一组公式。必须亲手推一遍,理解每个矩阵块的来龙去脉。
2.2 QP 矩阵组装
把代价函数写成标准 QP 形式,这是 MPC 工程的精髓。
标准代价函数:
步骤1:定义块对角权重矩阵
步骤2:代入预测方程 \(\mathbf{x} = \mathcal{A}x_0 + \mathcal{B}\mathbf{u}\)
步骤3:得到标准 QP:
常数项 \(x_0^T \mathcal{A}^T \bar{Q} \mathcal{A} x_0\) 与优化变量无关,直接省略。
2.3 约束处理
这是 MPC 相对于 LQR 的核心优势。
输入约束:
状态约束(通过预测矩阵转化):
变化率约束:
软约束:当硬约束导致 QP 无解时,引入松弛变量:
2.4 从零手写 MPC
这是最重要的环节。用 Python + OSQP 或 cvxpy 写一个能跑的 MPC。
import numpy as np
import cvxpy as cp
# 系统:双积分器 x = [p, v], u = a
dt = 0.1
A = np.array([[1, dt], [0, 1]])
B = np.array([[0.5*dt**2], [dt]])
# MPC 参数
N = 10 # 预测时域
Q = np.diag([10, 1]) # 状态权重
R = np.array([[0.1]]) # 控制权重
P = np.diag([100, 10]) # 终端权重
# 约束
u_max, u_min = 1, -1
x_max, x_min = np.array([5, 5]), np.array([-5, -5])
def solve_mpc(x0):
x = cp.Variable((2, N+1))
u = cp.Variable((1, N))
cost = 0
constraints = [x[:, 0] == x0]
for i in range(N):
cost += cp.quad_form(x[:, i], Q) + cp.quad_form(u[:, i], R)
constraints += [x[:, i+1] == A @ x[:, i] + B @ u[:, i]]
constraints += [u_min <= u[:, i], u[:, i] <= u_max]
if i > 0:
constraints += [x_min <= x[:, i], x[:, i] <= x_max]
cost += cp.quad_form(x[:, N], P)
constraints += [x_min <= x[:, N], x[:, N] <= x_max]
prob = cp.Problem(cp.Minimize(cost), constraints)
prob.solve(solver=cp.OSQP, warm_start=True)
return u.value[:, 0], x.value, u.value
1. 去掉 cvxpy,直接用 `scipy.optimize.minimize` 或手写 QP 矩阵调用 OSQP
2. 加入参考轨迹跟踪(不再是调节到原点)
3. 加入干扰项,观察 MPC 的鲁棒性
4. 对比不同 N(预测时域)对控制效果的影响
2.5 自查清单
- ✅ 手写出 \(\mathcal{A}\) 和 \(\mathcal{B}\) 预测矩阵
- ✅ 手算出 H 矩阵和 f 向量(从 Q, R, A, B, N 出发)
- ✅ 用 cvxpy / OSQP 实现无约束和有约束 MPC
- ✅ 能解释 N 太大/太小对系统的影响
- ✅ 能调 Q, R 达到期望的控制效果
- ✅ 理解软约束何时用、怎么用
3 第三层:NMPC 进阶(2-3 周)
当系统有强非线性时(倒立摆大角度摆动、机械臂、飞行器),线性 MPC 不够用,需要 NMPC。
3.1 非线性模型处理
NMPC 问题:
由于 \(f\) 是非线性的,QP 不再适用。需要求解非线性规划(NLP)。
三种主流方法:
直接配点法 (Direct Collocation)
同时离散化状态和控制轨迹,用分段多项式逼近状态轨迹
多重打靶法 (Multiple Shooting)
将轨迹分段,每段的起点作为决策变量,加连续性约束
实时迭代 (RTI)
每步只做一次 SQP 迭代,牺牲精度换实时性
3.2 NLP 求解算法
NMPC 的核心求解器:
| 算法 | 原理 | 优缺点 |
|---|---|---|
| SQP(序列二次规划) | 每步将 NLP 近似为 QP,迭代求解 QP 序列 | 收敛快,适合中等规模;需精确梯度 |
| 内点法 (IPOPT) | 引入障碍函数,在可行域内部搜索 | 大规模问题效果好;热启动不如 SQP |
| Real-Time Iteration | 每步只做一次 SQP,用热启动加速 | 极快,适合实时 NMPC;精度略低 |
3.3 从零手写 NMPC
import casadi as ca
import numpy as np
# 倒立摆非线性模型 (x = [theta, omega])
def pendulum_dynamics(x, u):
g, L, m, b = 9.81, 1.0, 1.0, 0.1
theta, omega = x[0], x[1]
dtheta = omega
domega = (g/L)*ca.sin(theta) + (1/(m*L**2))*u[0] - (b/m)*omega
return ca.vertcat(dtheta, domega)
# RK4 离散化
def rk4_step(x, u, dt):
k1 = pendulum_dynamics(x, u)
k2 = pendulum_dynamics(x + dt/2*k1, u)
k3 = pendulum_dynamics(x + dt/2*k2, u)
k4 = pendulum_dynamics(x + dt*k3, u)
return x + dt/6*(k1 + 2*k2 + 2*k3 + k4)
# 构建 NMPC
N, dt = 10, 0.05
x_sym = ca.MX.sym('x', 2) # 符号变量
u_sym = ca.MX.sym('u', 1)
F = ca.Function('F', [x_sym, u_sym], [rk4_step(x_sym, u_sym, dt)])
opti = ca.Opti() # NLP 问题
X = opti.variable(2, N+1)
U = opti.variable(1, N)
# 代价
Q, R = ca.diag([10, 1]), ca.diag([0.1])
cost = 0
for i in range(N):
cost += X[:, i].T @ Q @ X[:, i] + U[:, i].T @ R @ U[:, i]
cost += X[:, N].T @ ca.diag([100, 10]) @ X[:, N]
opti.minimize(cost)
# 约束
opti.subject_to(X[:, 0] == [0.5, 0]) # 初始角度 0.5 rad
for i in range(N):
opti.subject_to(X[:, i+1] == F(X[:, i], U[:, i]))
opti.subject_to(U[:, i] <= 2)
opti.subject_to(U[:, i] >= -2)
opti.solver('ipopt')
sol = opti.solve()
u_opt = sol.value(U[:, 0])
3.4 MPC vs NMPC 对比实验
同一个倒立摆系统,分别用线性 MPC(在平衡点线性化)和 NMPC 控制,对比效果:
3.5 自查清单
- ✅ 能用 CasADi 或 ACADOS 构建 NMPC
- ✅ 理解多重打靶法和直接配点法的区别
- ✅ 对比同一系统上的 MPC 和 NMPC 效果差异
- ✅ 了解 SQP 和内点法的基本迭代步骤
- ✅ 知道 RTI 为什么比标准 NMPC 快
4 第四层:深入理论(可选)
第四层是学术研究方向的进阶内容。如果你只是为了工程应用,第三层已经足够。
4.1 稳定性证明
MPC 的理论核心:证明闭环系统渐近稳定。
关键定理:如果满足以下条件,MPC 闭环渐近稳定:
- 阶段代价 \(\ell(x,u) \geq \alpha(\|x\|)\),正定(只在原点为零)
- 终端代价 \(V_f\) 是终端局部控制器 \(\kappa_f(x)\) 的 Lyapunov 函数:
$$V_f(f(x, \kappa_f(x))) - V_f(x) \leq -\ell(x, \kappa_f(x)), \quad \forall x \in \mathcal{X}_f$$
- 终端约束集 \(\mathcal{X}_f\) 是 \(\kappa_f\) 的控制不变集
- 初始时刻优化问题可行
证明思路:以最优代价 \(V_N^*(x_k)\) 作为 Lyapunov 候选函数,证明沿闭环轨迹单调下降:
这保证 \(x_k \to 0\)(渐近稳定)。
4.2 鲁棒 MPC
当系统存在模型不确定性或外部扰动时:
| 方法 | 思想 | 计算量 |
|---|---|---|
| Min-Max MPC | 在最坏扰动下优化 | 极高(双层优化) |
| Tube MPC | 使用名义系统 + 反馈管(不变集) | 中等(离线算管,在线优化名义系统) |
| 随机 MPC | 概率约束代替确定性约束 | 中等 |
4.3 其他高级专题
显式 MPC (Explicit MPC)
离线计算参数化 QP 的解分区,在线查表。适合嵌入式系统。
经济 MPC (Economic MPC)
代价函数直接使用经济指标(利润、能耗),而非跟踪误差。
学习 MPC (Learning MPC)
结合机器学习在线更新模型(Gaussian Process, LSTM, Koopman)
分布式 MPC (DMPC)
大规模系统分解为多个子系统,各自运行 MPC 并通信协调。
📅 5. 学习计划与资源
5.1 建议时间线
5.2 推荐书目(按优先级)
| 书名 | 作者 | 难度 | 推荐理由 |
|---|---|---|---|
| Model Predictive Control | Kouvaritakis & Cannon | 入门 | 最友好的 MPC 入门书,篇幅适中,有 Matlab 代码 |
| Predictive Control with MATLAB | 王谨 | 入门 | MATLAB 实现为主,适合中国读者,实用性强 |
| Model Predictive Control: Theory and Design | Rawlings, Mayne & Diehl | 进阶 | MPC 圣经,理论完整但难度大。作为参考书而非入门书 |
| Nonlinear Model Predictive Control | Grüne & Pannek | 进阶 | NMPC 的全面教材,配 Python 代码 |
| Optimal Control | Kirk | 基础 | 最优控制经典教材,帮你理解变分法/Pontryagin 基础 |
5.3 代码资源
| 资源 | 语言 | 说明 |
|---|---|---|
| do-mpc | Python | 完整的 MPC/NMPC 工具箱,包含仿真器和求解器 |
| CasADi | Python/MATLAB | 符号 NLP 建模 + IPOPT/ACADOS 求解 |
| OSQP | Python/C | 嵌入式友好的 QP 求解器 |
| ACADOS | C/Python/MATLAB | 专为 NMPC 设计的高效求解器,支持 RTI |
| MPC.jl | Julia | Julia 生态的 MPC 库 |
5.4 学习工具对比
Matlab/Simulink MPC Toolbox
- 自带 `mpc` 函数,一行命令建 MPC
- 优点:上手快,可视化好
- 缺点:黑箱 —— 你不知道底层 QP 怎么构造的
- 建议:用来验证,别用来学习核心原理
Python (cvxpy/OSQP/CasADi)
- 手写每个矩阵,完全透明
- 优点:真正理解每行公式
- 缺点:需要自己处理所有细节
- 建议:学习阶段用 Python,工作后用专用工具箱
1. 学习阶段:Python + cvxpy 手写(理解原理)
2. 验证阶段:Matlab MPC Toolbox(对照验证自己的实现)
3. 部署阶段:C++ / ACADOS / qpOASES(工业级性能)
❓ 6. 常见问题
必须先学 LQR。MPC 的代价函数就是 LQR 的截断版本,终端代价 P 来自 Riccati 方程。没有 LQR 基础,你无法理解为什么 Q,R,P 要满足那些矩阵要求,也无法理解为什么加了终端代价就能保证稳定性。
常见原因:
1. 预测时域 N 太小(无法"看到"稳定趋势)
2. 终端代价 P 没有正确设定(用 ARE 求解)
3. 离散化步长太大
4. 约束过于激进导致 QP 无解(引入软约束)
5. 模型与实际系统严重不匹配
经验法则:
- N 应覆盖系统主导时间常数的 2-3 倍
- 对振荡系统,N 应覆盖至少半个振荡周期
- 从 N=10 开始调试,观察闭环响应
- N 越大越稳定,但计算越慢
线性 MPC 够用:系统工作点变化小、非线性弱(温度控制、液位控制、小角度运动)
必须 NMPC:系统大范围运动(机械臂全范围运动、飞行器姿态大机动)、强非线性(化学反应、生物过程)
折中方案:LTV MPC(在线线性化),精度介于两者之间,计算量接近线性 MPC。
最低要求:线性代数(矩阵运算、特征值)、基本控制理论(状态空间、能控性)、基本优化(凸优化、QP)
深入需要:Lyapunov 稳定性理论、凸分析、数值优化(SQP、内点法)
实用建议:不需要等数学完美再开始。先跑通代码,遇到看不懂的数学再回头补。工程是数学最好的老师。
嵌入式 MPC 的关键限制是计算资源和内存:
1. 使用高效的 QP 求解器(qpoases 的嵌入式版本、OSQP 的 C 版本)
2. 减小预测时域(N=3-5)
3. 使用显式 MPC(EFRAIM B 算法,离线分区在线查表)
4. 代码生成:ACADOS 和 FORCES Pro 支持将 MPC 编译为 C 代码
5. FPGA 实现的 NMPC 已有多篇论文验证
没有标准答案,但有经验法:
1. Bryson's Rule:\(Q_{ii} = 1 / x_{i,\max}^2,\; R_{jj} = 1 / u_{j,\max}^2\)
2. 先只调对角元,对角占优就是够
3. Q/R 比值决定了"控制性能"和"控制能量"的权衡
4. 增加 Q 中的某个元素 → 对应的状态跟踪更紧
5. 增加 R → 控制动作更柔和(更省力)
6. 如果系统振荡,减少 Q 或增加 R
根据你的方向:
- 机器人/自动驾驶:结合非线性优化 + 运动规划(Trajectory Optimization)
- 过程控制:学习经济 MPC + 实时优化(RTO)
- 航空/航天:学习鲁棒 MPC + 制导控制一体化
- AI/ML 方向:学习 Learning-based MPC(Koopman MPC, GP-MPC)
- 学术研究:读 Rawlings + Mayne,关注每年 CDC/ACC/ECC 论文