做控制的兄弟,尤其是搞过非线性模型预测控制(MPC)的,应该都有过这种体验:模型稍微复杂一点,比如倒立摆、机械臂、磁悬浮,控制器设计就从“愉快地调权重”变成了“痛苦地调求解器”。用fmincon在线求解非线性优化问题,一个周期跑几十毫秒甚至上百毫秒都很正常,实时性要求稍微高一点,基本只能望洋兴叹。我也被这个问题折磨了很久,直到最近把Koopman算子这套理论从论文里搬到Matlab代码里,才算找到一个既能得到“全局线性预测模型”、又能直接对接成熟线性MPC框架的路径。
Koopman模型预测控制(Koopman MPC)的核心思路并不玄乎:通过一组观测函数,把非线性系统的状态“提升”到高维空间,再用EDMD算法从数据中辨识出一个线性预测器,然后在这个高维线性模型上直接套用标准线性MPC。整个过程用Matlab实现,不需要在线求解非线性优化问题,而是把每个控制周期变成一个标准的二次规划(QP)问题,用quadprog几毫秒就能算完。这篇文章我会把完整流程——从训练数据生成、EDMD建模、模型验证,到quadprog求解MPC、闭环仿真,以及带噪声情况下的Koopman状态估计——全部用可运行的代码串起来,并把实际操作中踩过的坑一并写出来。如果你手头正好在纠结非线性MPC的实时性问题,或者想试试Koopman到底怎么落地,这篇应该能帮你少走不少弯路。
1. 为什么要用Koopman模型做MPC
1.1 非线性MPC的真实痛点
先聊一个很现实的问题:非线性MPC到底难在哪儿?
模型预测控制本身的框架并不复杂,每个控制周期都在线求解一个有限时域优化问题,把第一个控制量施加给系统,下一周期滚动重复。难点在于预测模型是非线性时,优化问题就变成了非凸非线性规划(NLP)。这种问题有几个让人头疼的特性:
第一,求解慢。非线性规划没有统一的快速算法,fmincon这类工具内部要多次计算梯度、Hessian,还要做线搜索和可行性恢复,迭代次数和计算量都远大于线性QP问题。第二,全局最优没有保证。非线性优化的目标函数可能是非凸的,求解器找到的往往只是局部最优解,控制效果自然打折扣。第三,实时性难以保证。NLP求解时间波动很大,最坏情况可能比平均情况慢一个数量级,这在嵌入式实时控制里是很致命的。
所以工程上常见的做法是:在工作点附近做线性化,得到一个局部线性模型,然后在这个模型上跑线性MPC。这样做实时性解决了,但线性化模型只在一个很小的邻域内有效,一旦系统状态偏离工作点,预测就严重失真,控制性能大幅下降。对于强非线性系统,比如软弹簧、倒立摆大摆角、化学反应器,这种“局部线性化+MPC”方案往往根本压不住。
那有没有一种方法,能在全局范围内得到一个线性模型?这就是Koopman算子理论最有吸引力的地方。
1.2 Koopman升维:用“换坐标系”换出线性
Koopman算子的核心思想,用大白话讲就是:一个有限维的非线性系统,可以被看作某个无限维线性系统在低维流形上的投影。既然低维非线性,那我就换一个视角,把它“嵌入”到更高维的空间里,在高维空间里,动力学反而展现出线性特征。
这个思想有点像看一座山。你在山脚走盘山路,觉得路又陡又弯;但如果你坐直升机升到高空往下看,整个山脉的等高线其实是很有规律的。Koopman做的就是一件事:找一组观测函数,把这些观测函数当作新的坐标轴,把系统状态映射过去。在新的坐标空间里,非线性动力学变成了一个线性矩阵作用在观测向量上——也就是变成了一个线性预测器。
形式化一点:对非线性离散系统
x(k+1) = f(x(k), u(k))
假设我们能找到一组观测函数 ψ(x) = [ψ₁(x), ψ₂(x), ..., ψ_N(x)]ᵀ,使得存在矩阵A和B满足
ψ(x(k+1)) ≈ A ψ(x(k)) + B u(k)
那么原系统的演化,在高维观测空间里就被“拍扁”成线性了。定义 z = ψ(x),就有了
z(k+1) = A z(k) + B u(k)
y(k) = C z(k)
其中C矩阵负责把高维状态z映射回我们关心的原始状态x。如果我们把原始状态x直接放在观测函数的最前面几个分量里,那么C就是简单的前几行单位阵。
这样一来,非线性MPC就变成了:
- 每个控制周期,把当前量测到的x lift成高维状态z;
- 用线性模型 z(k+1)=Az(k)+Bu(k) 做预测;
- 求解一个线性二次型QP问题,得到控制量u(k)。
整个过程完全没有非线性优化,实时性和稳定性都有保证。
1.3 这个方法在什么场景下真正有用
我在项目里测试下来,Koopman MPC比较适合两类场景:
一类是强非线性但自由度不高的系统,比如单级倒立摆、磁悬浮小球、非线性弹簧阻尼系统。这些系统非线性明显,局部线性化模型几乎没法用,但系统维度不高,即使lift到几十维的高维空间,计算量也完全可接受。
另一类是“模型无法精确建模但数据容易获取”的系统。Koopman方法本质上是数据驱动建模,只要你能采集到足够的输入输出数据,不需要精确知道系统的物理方程,就能学习出一个线性预测器。这一点在工程现场特别实用——很多时候我们根本写不出精确的机理模型,但数据采集非常容易。
当然,Koopman也不是万能的。对于状态空间特别大、动态特别复杂的系统(比如大规模流体控制),lift后的维度可能高到不现实,这时就需要用深度Koopman或者谱方法做进一步压缩。这个后面在常见问题部分再展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从非线性数据到Koopman线性模型:EDMD辨识
2.1 观测函数(lift函数)怎么选
使用Koopman方法前,第一件要决定的事就是:观测函数集合 ψ(x) 到底选什么。
这个选择直接决定了高维线性模型的拟合能力。我的建议是分两步走。
第一步,一定要把原始状态分量原封不动地放进观测函数里。也就是说 ψ(x) 的前几个分量就是 x₁, x₂, ..., xₙ。这样做有几个好处:高维状态的前几维保留了原始物理含义,状态恢复矩阵C非常简单,而且后续设计MPC代价函数时,可以直接惩罚原始状态,不用写复杂的映射。这一步虽然简单,但我见过很多新手忽略,导致后续C矩阵死活对不上。
第二步,增加非线性基函数。对不同类型的非线性,选不同的基函数:
- 多项式非线性(比如x³、x²y):用多项式型基函数,如
