第三节:模型预测控制(MPC)——让机器人“预见未来”
各位好,欢迎来到复杂轨迹跟踪的第三节。
前两节我们聊了PID和LQR,它们各有千秋。但说实话,当你面对一个带有各种约束(比如电机速度上限、关节角度极限)的系统时,PID和LQR往往就有点力不从心了。这时候,就该我们的“预言家”——模型预测控制(MPC)登场了。
3.1 MPC基本原理:它凭什么能“预测”?
MPC的核心思想,说白了就是四个字:滚动优化。
你想想看,我们开车时,眼睛会看前方几百米的路况,然后提前打方向盘、踩刹车。MPC也是这个道理。它利用系统的数学模型,预测未来一段时间(我们叫它“预测时域”)内系统的状态,然后找到一个最优的控制序列,让系统沿着我们期望的轨迹走。
我刚开始接触MPC时,总觉得它很玄乎。后来我把它拆解成三步,就好理解了:
- 预测模型:用数学公式描述系统“下一步”会怎样。比如,你给电机一个电压,它下一秒会转多快。
- 滚动优化:每走一步,都重新算一遍未来几步的最优控制量。不是算一次就完事,而是边走边算。
- 反馈校正:实际结果和预测有偏差?没关系,把偏差反馈回去,下一轮优化时修正。
核心公式(离散时间MPC):
min J = Σ (x_k - x_ref)^T Q (x_k - x_ref) + Σ u_k^T R u_k
s.t. x_{k+1} = A x_k + B u_k
u_min ≤ u_k ≤ u_max
x_min ≤ x_k ≤ x_max
这个公式看着复杂,其实就三件事:让状态接近目标(第一项)、让控制量别太大(第二项)、满足物理约束(第三项)。
3.2 约束处理:这才是MPC的“杀手锏”
为什么说MPC比LQR强?因为它能硬生生地处理约束。
我在项目中遇到过这样一个场景:一个六轴机械臂,需要快速抓取工件。如果用LQR,算出来的关节速度可能瞬间超过电机极限,导致电机过流报警。而MPC可以在优化时,直接把“关节速度 ≤ 额定值”写进约束条件里。
约束处理通常有两种方式:
- 硬约束:必须满足,比如“电压不能超过48V”。一旦违反,优化问题无解。
- 软约束:尽量满足,但允许轻微违反。比如“位置误差小于1mm”,实在不行放宽到2mm也行。
避坑指南: 我曾经在一个移动机器人项目里,把电机电流约束设成了硬约束。结果遇到一个陡坡,MPC算出来无解,机器人直接原地罢工。后来改成软约束,加了一个惩罚项,问题就解决了。所以,实际工程中,我建议优先用软约束,给优化器留点余地。
3.3 滚动优化:为什么不是“一步到位”?
你可能会问:既然能预测未来,为什么不一次性算完所有控制量,然后照着执行就行了?
嗯,这里要注意。模型总是不完美的,还有外部干扰。你算完10秒的控制序列,结果第2秒就刮来一阵风,后面的8秒全白算了。
滚动优化的精髓就是:只执行第一个控制量,然后重新预测、重新优化。就像下棋,你只走当前这一步,然后根据对手的回应,再想下一步。
具体流程是这样的:
- 在时刻k,测量当前状态x_k。
- 求解优化问题,得到未来N步的控制序列 [u_k, u_{k+1}, ..., u_{k+N-1}]。
- 只把u_k施加给系统。
- 等到时刻k+1,重复步骤1-3。
这样做的好处是:实时修正误差,鲁棒性极强。
3.4 在轨迹跟踪中的应用:一个完整的例子
我们来看一个实际的轨迹跟踪案例。假设我们要控制一个差速轮小车,跟踪一个“8”字形轨迹。
小车的运动模型(简化版):
x_{k+1} = x_k + v_k * cos(θ_k) * dt
y_{k+1} = y_k + v_k * sin(θ_k) * dt
θ_{k+1} = θ_k + ω_k * dt
其中,v是线速度,ω是角速度,是我们的控制量。
约束条件:
- 线速度:0 ≤ v ≤ 1.0 m/s
- 角速度:-1.0 ≤ ω ≤ 1.0 rad/s
- 位置误差:|x - x_ref| ≤ 0.1 m
MPC的优化目标就是:在满足上述约束的前提下,让小车的位置(x, y)尽可能接近参考轨迹(x_ref, y_ref)。
我个人的习惯是,先用Python的cvxopt或casadi库快速搭建一个MPC原型,验证算法可行性。下面是一个简化版的代码框架:
import casadi as ca
# 定义优化变量
v = ca.MX.sym('v', N) # 线速度序列
ω = ca.MX.sym('ω', N) # 角速度序列
# 构建目标函数和约束
J = 0
g = []
for k in range(N):
# 预测状态(简化)
x_pred = ... # 根据模型递推
y_pred = ...
# 跟踪误差
J += (x_pred - x_ref[k])**2 + (y_pred - y_ref[k])**2
# 约束
g.append(v[k] - 1.0) # v ≤ 1.0
g.append(-v[k]) # v ≥ 0
# 求解
solver = ca.qpsol('solver', 'qpoases', ...)
sol = solver(...)
重要提醒: 实际部署时,MPC的计算耗时是个大问题。我曾经在一个嵌入式平台上跑MPC,优化求解花了50ms,而控制周期只有20ms。结果就是:算完的时候,系统已经跑偏了。解决办法有两个:一是简化模型(比如用线性MPC代替非线性MPC),二是用更高效的求解器(比如ACADO、qpOASES)。
3.5 知识体系图:MPC的核心逻辑
为了让你更直观地理解MPC的运作流程,我画了一张图:
3.6 总结与个人体会
MPC不是万能的,但它确实是处理约束轨迹跟踪问题的利器。我个人觉得,它的最大价值在于:把“控制”和“规划”融合在了一起。传统的做法是先规划一条路径,再用控制器去跟踪。而MPC在优化时,同时考虑了路径跟踪和约束满足,效果自然更好。
当然,代价就是计算量大、调试复杂。我建议初学者先从线性MPC入手,用简单的模型(比如一阶惯性系统)跑通流程,再逐步增加复杂度。记住:能跑起来的简单MPC,比算不出来的复杂MPC强一百倍。
核心要点回顾:
- MPC = 预测模型 + 滚动优化 + 反馈校正
- 约束处理是MPC的核心优势,建议优先使用软约束
- 滚动优化只执行第一个控制量,保证实时性
- 实际部署时,计算耗时是最大瓶颈,需要权衡模型精度和求解速度
好了,这一节就到这里。MPC的内容其实很深,我们只是开了个头。下一节我们会聊自适应控制,看看当系统参数变化时,控制器该如何“自我调整”。