第194篇 模型预测控制MPC原理——预测+优化+滚动执行

📅 2026/8/18 14:18:57
第194篇 模型预测控制MPC原理——预测+优化+滚动执行
上篇聊了LQG卡尔曼滤波加LQR有噪声情况下的最优方案。但LQR和LQG都有一个共同的硬伤处理不了约束。电机最大力矩是50Nm你不能输出100Nm。关节角度限位是正负120度你不能让它转到180度。飞行器的最大倾斜角是30度超过了就翻机。这些都是硬约束LQR/LQG完全不管这些——它只管算出最优的控制量至于这个控制量超没超限它不关心。实际工程中怎么办最朴素的做法是饱和——算出来100Nm执行器只能给50Nm那就截断到50Nm。讲真这种截断式处理经常让系统性能急剧下降甚至不稳定。因为你把最优控制律给破坏了。模型预测控制MPC从根上解决了这个问题。它不是算完了再截断而是算的时候就考虑约束。一、MPC的核心思想三步走MPC的逻辑其实很直觉就三步第一步预测。基于当前状态和系统模型预测未来N步的系统行为。N叫预测时域prediction horizon。第二步优化。在未来N步的控制序列中找一组让代价函数最小的控制序列。优化过程中显式考虑所有约束。第三步执行。只执行第一步的控制量然后回到第一步用新的测量值重新预测、重新优化。这就是滚动时域receding horizon。你想想这和人类开车其实很像。你看到前方500米有个弯道你会提前规划一条减速转向的轨迹预测优化但你只会先执行当前这一秒的动作滚动执行然后不断根据新情况调整。MPC本质上就是把这个预测-优化-滚动的过程数学化了。二、数学形式MPC在优化什么离散时间系统x(k1) A_d * x(k) B_d * u(k)MPC在每个时刻k求解以下优化问题min sum_{i0}^{N-1} [x(ki|k)^T Q x(ki|k) u(ki|k)^T R u(ki|k)] x(kN|k)^T P x(kN|k) u(0),...,u(N-1) s.t. x(ki1|k) A_d x(ki|k) B_d u(ki|k) # 系统动态约束 u_min u(ki|k) u_max # 输入约束 x_min x(ki|k) x_max # 状态约束 x(k|k) x_current # 初始条件这里x(ki|k)表示在时刻k预测的时刻ki的状态。Q和R还是代价函数的权重矩阵和LQR里的一样。P是终端代价矩阵保证闭环稳定性。关键区别在于MPC的优化问题是一个带约束的优化问题。LQR没有约束解Riccati方程就行。MPC有约束需要解一个二次规划QP问题。QP问题的求解在控制领域已经非常成熟。常用的求解器有OSQP、qpOASES、CasADi等。对于线性系统二次代价线性约束的情况QP是凸优化问题保证能找到全局最优解。三、滚动时域为什么只看第一步MPC算出来的是未来N步的最优控制序列[u*(0), u*(1), ..., u*(N-1)]。但MPC只执行u*(0)然后扔掉剩下的下一步重新算。为什么要这样直接执行整个序列不行吗原因很简单模型不完美实际系统会有偏差。如果你执行了整个序列第二步、第三步的控制量是基于模型预测的状态算的而不是基于真实状态算的。模型越不准后面的控制量越不靠谱。滚动时域的好处是每一步都基于最新测量值重新计算。即使模型有误差你也能通过不断纠正来保持性能。从控制理论角度看滚动时域本质上是开环优化闭环反馈的组合——优化基于模型预测开环但滚动执行引入了实时反馈闭环。四、Python代码实战来看一个简单的MPC例子。还是用倒立摆加上输入约束。import numpy as np from scipy.linalg import block_diag import cvxpy as cp # 凸优化库 # 系统参数离散化后的倒立摆 dt 0.05 A_d np.array([[1,dt,0,0],[0,1,-dt,0],[0,0,1,dt],[0,0,14.7*dt,1]]) B_d np.array([[0],[dt],[0],[-dt]]) C_d np.eye(4) # MPC参数 N 20 # 预测时域 Q np.diag([10, 1, 100, 1]) R np.array([[0.1]]) u_max 5.0 # 输入约束最大力5N # 终端代价用LQR的P矩阵作为终端权重 from scipy.linalg import solve_discrete_are P solve_discrete_are(A_d, B_d, Q, R) def mpc_step(x_current, N, Q, R, P, u_max): 求解一步MPC优化问题 nx 4 # 状态维度 nu 1 # 输入维度 # 定义优化变量 X cp.Variable((nx, N1)) # 预测状态序列 U cp.Variable((nu, N)) # 预测控制序列 cost 0 constraints [] for i in range(N): cost cp.quad_form(X[:, i], Q) cp.quad_form(U[:, i], R) # 系统动态约束 constraints.append(X[:, i1] A_d X[:, i] B_d U[:, i]) # 输入约束 constraints.append(cp.abs(U[:, i]) u_max) # 终端代价 cost cp.quad_form(X[:, N], P) # 初始条件 constraints.append(X[:, 0] x_current) # 求解QP prob cp.Problem(cp.Minimize(cost), constraints) prob.solve(solvercvxpy.OSQP) return U[:, 0].value # 只返回第一步的控制量 # 仿真 x np.array([0, 0, 0.2, 0]) # 初始摆角0.2rad for k in range(200): u mpc_step(x, N, Q, R, P, u_max) x A_d x B_d.flatten() * u if k % 40 0: print(fStep {k}: angle{x[2]:.4f}, u{u[0]:.2f})这段代码用cvxpy定义了一个标准的MPC优化问题。注意几个关键点预测时域N20步对应1秒的预测窗口输入约束|u|5N在优化中被显式处理终端代价用LQR的P矩阵保证稳定性只返回U[:,0]即第一步的控制量五、MPC的优势和代价MPC最大的优势就是能处理约束。输入约束、状态约束、输出约束都能在优化框架里统一处理。但MPC的代价是计算量。每一步都要解一个QP问题预测时域N越大QP变量越多求解越慢。对于快动态系统比如四旋翼控制频率500Hz每步只有2msQP必须在2ms内解完。讲真MPC在工业界用得非常多。化工过程控制、自动驾驶、无人机轨迹跟踪MPC几乎是标配。原因很简单这些场景约束多控制频率不算特别高10-100HzMPC的计算量扛得住。六、面试实战QMPC和LQR的本质区别是什么ALQR是无约束最优控制解Riccati方程得到固定增益K。MPC是有约束最优控制每步解一个QP问题得到时变增益。LQR是一劳永逸MPC是步步为营。Q预测时域N怎么选AN太小MPC看不到足够远的未来约束处理不好稳定性也成问题。N太大计算量爆炸而且远端的预测本来就不准。经验法则是N取到系统 settling time的1.5到2倍。QMPC怎么保证稳定性A三种常见方法1终端代价terminal cost用LQR的P矩阵作为终端权重2终端约束terminal constraint要求预测末端状态进入某个不变集3足够长的预测时域让终端影响可以忽略。面试的时候把预测优化滚动这三个关键词说清楚再能讲出约束处理和稳定性保证的思路基本就过关了。小结今天聊了MPC的核心原理。三个关键词预测基于模型推演未来、优化在约束下找最优控制序列、滚动执行只执行第一步不断重新规划。MPC解决了LQR/LQG处理不了约束的根本问题代价是每步都要解一个优化问题。计算效率是MPC落地的关键瓶颈这个下篇专门聊。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第193篇 LQG控制——有噪声情况下的最优估计最优控制下一篇预告第195篇 MPC约束处理——输入/状态/输出约束的工程实现有任何问题欢迎评论区留言我会尽量回复。