2、线性MPC理论:线性状态空间模型、预测方程推导、代价函数设计、约束处理

好,咱们进入正题。线性MPC,说白了就是模型预测控制里最基础、最成熟的那一套。你想想看,很多工业场景下,系统本身虽然不是严格线性的,但在工作点附近用线性模型去近似,效果已经足够好了。我个人习惯,做项目第一步就是先把模型线性化,省得后面自找麻烦。

2.1 线性状态空间模型

咱们先从最熟悉的离散时间状态空间模型说起。为什么用离散的?因为控制器是数字的,采样周期固定,天然就是离散的。

标准形式长这样:

x(k+1) = A x(k) + B u(k)
y(k)   = C x(k) + D u(k)

这里:

  • x(k) 是状态向量,维度 n×1。比如电机的位置、速度。
  • u(k) 是控制输入,维度 m×1。比如电压、电流。
  • y(k) 是系统输出,维度 p×1。比如你关心的角度。
  • A 是系统矩阵,n×n。描述状态怎么自演化。
  • B 是输入矩阵,n×m。描述控制量怎么影响状态。
  • C 是输出矩阵,p×n。描述哪些状态被观测到。
  • D 是前馈矩阵,p×m。大多数情况下是零矩阵。

我在项目中遇到过一个问题:有人直接把连续系统模型拿过来用,采样周期一长,预测全乱套了。记住,这里的A和B必须是离散化之后的。常用的方法有零阶保持器法(ZOH),或者直接用欧拉法近似:

A_d = I + A_c * Ts
B_d = B_c * Ts

嗯,这里要注意,欧拉法只在采样周期足够小时才准。如果Ts大了,建议用c2d函数做精确离散化。

2.2 预测方程推导

有了模型,下一步就是预测未来。说白了,就是根据当前状态x(k),和未来一系列控制输入u(k), u(k+1), ...,算出未来的状态x(k+1), x(k+2), ...。

咱们定义预测时域为N。那么从k时刻开始,未来N步的状态可以写成:

x(k+1) = A x(k) + B u(k)
x(k+2) = A x(k+1) + B u(k+1) = A^2 x(k) + A B u(k) + B u(k+1)
x(k+3) = A^3 x(k) + A^2 B u(k) + A B u(k+1) + B u(k+2)
...
x(k+N) = A^N x(k) + A^(N-1) B u(k) + ... + B u(k+N-1)

写成紧凑的矩阵形式,就是所谓的预测方程:

X = F x(k) + G U

其中:

  • X = [x(k+1); x(k+2); ...; x(k+N)],维度 Nn×1
  • U = [u(k); u(k+1); ...; u(k+N-1)],维度 Nm×1
  • F = [A; A^2; ...; A^N],维度 Nn×n
  • G 是一个下三角块矩阵,维度 Nn×Nm

这个G矩阵长什么样?我写个伪代码你感受一下:

G = [B, 0, 0, ..., 0;
     A B, B, 0, ..., 0;
     A^2 B, A B, B, ..., 0;
     ...
     A^(N-1) B, A^(N-2) B, ..., B]

为什么是下三角?因为未来的控制输入不能影响过去的状态。这个因果关系一定要搞清楚。

我的小技巧: 实际写代码时,别手算这个G矩阵。用循环或者矩阵运算库生成,又快又不容易出错。我曾经手算过一个5步预测的G矩阵,结果少写了一项,调试了一整天。

2.3 代价函数设计

预测做好了,接下来就是「怎么才算好」。代价函数,就是量化控制性能的尺子。

标准的二次型代价函数长这样:

J = sum_{i=1}^{N} [ x(k+i)^T Q x(k+i) + u(k+i-1)^T R u(k+i-1) ]

这里:

  • Q 是状态权重矩阵,半正定。越大,系统越「着急」让状态回到零。
  • R 是输入权重矩阵,正定。越大,控制动作越「温柔」。

你想想看,如果Q很大R很小,控制器会拼命用大电压去调节,响应快但可能震荡。反过来,R很大Q很小,系统就慢吞吞的,像个老爷车。

在实际项目中,我一般这样调参:

  1. 先把R固定,比如设为单位矩阵。
  2. Q用对角线矩阵,先设成单位阵,然后根据物理量纲调整。比如位置误差权重给10,速度误差权重给1。
  3. 跑仿真,看响应。如果震荡,增大R;如果太慢,增大Q。

把预测方程代入代价函数,可以写成关于U的二次型:

J = U^T H U + 2 x(k)^T f U + constant

其中:

  • H = G^T Q_bar G + R_bar
  • f = G^T Q_bar F
  • Q_bar和R_bar是块对角矩阵,由Q和R重复N次组成。

这个形式,就是标准的二次规划(QP)问题。后面求解就交给求解器了。

2.4 约束处理

线性MPC最大的优势之一,就是能显式处理约束。没有约束的MPC,其实和LQR差不多。有了约束,才是真正的MPC。

常见的约束有三类:

约束类型 数学表达 物理含义
输入约束 u_min ≤ u(k) ≤ u_max 电机最大电压、阀门最大开度
输出约束 y_min ≤ y(k) ≤ y_max 位置不能超限、温度不能过高
状态约束 x_min ≤ x(k) ≤ x_max 速度限制、角度限制

把这些约束写成关于U的线性不等式:

A_ineq U ≤ b_ineq

举个例子,输入约束:

u_min ≤ u(k) ≤ u_max
u_min ≤ u(k+1) ≤ u_max
...
u_min ≤ u(k+N-1) ≤ u_max

写成矩阵形式就是:

[ I; -I ] U ≤ [ u_max * ones(N,1); -u_min * ones(N,1) ]

输出约束稍微复杂一点,因为输出是状态的线性组合:

y(k+i) = C x(k+i) = C ( A^i x(k) + ... )

最终也能写成关于U的线性不等式。

避坑指南: 我曾经在一个项目中,输出约束设得太紧,导致QP问题无解。控制器直接崩溃了。后来加了软约束(松弛变量),才解决了这个问题。建议你在实际应用中,对约束留一点余量,或者使用软约束。

2.5 完整的MPC优化问题

把上面所有东西拼起来,线性MPC在每个采样时刻要解决的优化问题就是:

min_U   U^T H U + 2 x(k)^T f U
s.t.    A_ineq U ≤ b_ineq

这是一个标准的凸二次规划问题。求解之后,取U的第一个元素u(k)作为实际控制量施加到系统上。下一时刻,重新测量状态,再求解一次。这就是滚动时域控制的核心思想。

嗯,说到这里,我想起刚入行时的一个误区:以为MPC是一次性算完所有控制量然后一股脑全用上。其实不是的,每次只取第一步,剩下的都扔掉。为什么?因为模型有误差,预测不准,必须不断用新测量值修正。

核心要点回顾:

  • 线性状态空间模型是基础,记得离散化
  • 预测方程把未来状态写成当前状态和未来控制输入的线性组合
  • 代价函数用Q和R调性能,二次型形式方便求解
  • 约束写成线性不等式,加入QP问题中
  • 滚动时域:只取第一步,不断重复

下一章,咱们会聊怎么实际求解这个QP问题,以及代码实现上的细节。到时候我会分享一些我在嵌入式平台上跑MPC的实战经验,包括怎么选求解器、怎么处理计算延时等等。敬请期待。