☰
辅助李亚普设计:MPC稳定性证明与终端代价构造的工程指南
2026/10/22 16:24:29 网站建设 项目流程

最近在啃MPC稳定性相关的论文,发现一个反复出现的工具——辅助李亚普设计。简单说就是给模型预测控制找一个“兜底”的局部控制器,再配上它的李亚普函数,用来构造终端代价和终端约束,把MPC的稳定性证明从“很难下手”变成“顺理成章”。这篇笔记是第一篇,先把这套方法的前因后果、数学推导和我在仿真里踩过的坑捋清楚。适合正在读非线性MPC论文、写课程作业或者刚接手预测控制项目的朋友,不管是纯看理论还是准备复现代码,应该都能省点功夫。

1. 辅助李亚普设计到底在解决什么问题

1.1 为什么MPC的稳定性不是“天生的”

很多刚接触预测控制的人有个错觉:MPC每一拍都在求最优,闭环结果理所当然就是稳定的。实际跑几次双回路仿真就会发现,如果不做特殊处理,MPC完全可能闭环震荡甚至发散。原因在于,MPC每一拍解的是一个有限时域的开环最优问题,我们只保证了“从当前状态出发,未来N步内某条预测轨迹是好的”,根本没有保证“把控制器按滚动时域方式实现之后,闭环系统会收敛到原点”。

要理解这个坑,可以想象你在迷雾里开车,眼前的十米看得清清楚楚,但导航给你规划的是接下来一百米的路线。每走一步,导航重新规划一次。如果导航完全不考虑“车道尽头必须是能停下来的安全区域”,那它完全可能给你规划出一条“前面十米很顺、开到第十一米就是悬崖”的路线——因为每次重新规划时,悬崖已经在视野之外了。

MPC的有限时域优化就有这个毛病:预测末端的状态没有任何限制,优化器完全可以为了前期的性能指标,把末端状态带到某个毫无收敛希望的角落。下一拍从那个角落重新出发,问题就来了。为了解决这个问题,经典MPC稳定性理论里引入了三个东西:终端约束、终端代价、终端控制律。而这三个东西的核心,其实就是一个辅助李亚普设计。

提示:MPC稳定性问题,本质是“有限时域预测”和“无限时域闭环”之间的缝隙。辅助李亚普设计就是用来填这个缝隙的。

1.2 终端三件套其实是同一个东西

先看标准形式。假设系统是离散模型

x(k+1) = f(x(k), u(k))

我们每拍求解

min ∑_{i=0}^{N-1} ℓ(x(i), u(i)) + Vf(x(N))

s.t. x(0)=当前状态,x(i+1)=f(x(i),u(i)),x(i)∈X,u(i)∈U,x(N)∈Xf

这里ℓ是阶段代价,Vf是终端代价,Xf是终端区域。教科书告诉我们:要证明闭环稳定,需要设计Vf和Xf,让它们满足“终端区域不变、终端代价沿某个控制律下降”。但问题是,Vf和Xf到底怎么来?随便给一个二次型矩阵和椭球区域,往往怎么调都凑不出那条下降不等式。

辅助李亚普设计就是解决这个“怎么来”的问题。思路非常直接:先不管MPC,另外设计一个简单的辅助控制器u=κ(x),比如最典型的线性二次型调节器(LQR),然后为这个闭环系统找一个李亚普函数V(x),满足沿闭环轨迹下降:

V(f(x, κ(x))) - V(x) ≤ -ℓ(x, κ(x))

有了这个V,直接把三件套配齐:

  • 终端代价取Vf(x)=V(x),通常是x^T P x形式的二次型;
  • 终端区域取Xf={x | V(x) ≤ α},其中参数α需要满足输入约束等要求;
  • 终端控制律就用κ(x),它代表“当预测末端进入终端区域后,由辅助控制器接着把状态带回原点”。

换句话说,辅助李亚普设计就是“先做一个哪怕简单但能保证稳定的局部控制器,再把它的李亚普函数借给MPC当终端道具”。这也是“辅助”这个词的来历:它不是滚动优化里的主角,只在理论上负责兜底。

2. 从线性到非线性:辅助控制器到底怎么选

2.1 线性系统最舒服:LQR一次给出全部

如果是线性时不变系统

x(k+1) = A x(k) + B u(k)

最经典的辅助设计就是LQR。给定阶段代价权重Q和输入权重R,解离散代数黎卡提方程(DARE)

A^T P A - P - A^T P B (R + B^T P B)^{-1} B^T P A + Q = 0

得到对称正定矩阵P,最优反馈增益K=(R+B^T P B)^{-1} B^T P A,辅助控制律κ(x)=-Kx,李亚普函数V(x)=x^T P x。然后验证衰减条件:

x^T P x - (Ax - BKx)^T P (Ax - BKx) ≥ x^T Q_ℓ x

其中右侧随便取成一个正定的阶段代价,比如x^T (Q + K^T R K) x。因为有黎卡提方程垫底,这条不等式是自动满足的,不需要额外调参。

真正需要动手算的是终端区域参数α。Xf={x | x^T P x ≤ α}既要保证不变性,又要保证对区域内所有x,辅助控制律u=-Kx都满足输入约束|u|≤u_max。实际操作里,我一般用采样加内插的办法估α:在单位球面上撒点,对每个方向计算满足|Kx|≤u_max的最大长度,然后取所有方向上的最小值,再乘以一个保守系数,比如0.8。

这步很容易被忽略。如果α取得太大,Xf边缘处的点会被LQR拉到违反约束的地方,整个“终端控制律”的合法性就不成立;如果α取得太小,MPC的初始可行域被压得很窄,很多实际状态进不了可行域。

2.2 非线性系统就要动点脑子了

非线性系统没有“现成的LQR”,辅助李亚普设计的关键变成“如何找一个可验证的李亚普函数”。最理想的情况是系统本身存在控制李亚普函数(CLF),也就是存在一个正定函数V,使得对任意状态x,都能找到一个控制u让导数为负:

V(f(x,u)) - V(x) ≤ -ℓ(x,u)

有CLF之后再找一个具体的辅助控制律κ(x),让它满足上述不等式。构造CLF的常见路子有这么几条。

  • Sontag公式:针对仿射系统x'=g0(x)+g1(x)u,如果能找到李亚普函数V,可以用显式公式构造全局渐近镇定控制器,这是理论上的经典结果,代码实现也不难。
  • 反步法:对严格反馈级联系统,从最后一阶开始逐层反推,每一步都能构造出李亚普函数和控制律。
  • 在线线性化加局部验证:工程上更务实,直接在平衡点附近线性化系统,用LQR得到局部控制器和李亚普函数,再在某个邻域内做网格化数值验证,确认衰减不等式成立。

我在非线性案例里一般先试第三条路,因为快。比如平衡点附近线性化得到的P矩阵,往往在平衡点周围一个不算太小的邻域内都能满足衰减条件,只要阶段代价和约束不离谱。网格验证时要注意,状态空间维数不能太高,三维以内随便撒,四维以上就得想别的办法,比如用随机采样加CROWD理论那套思路。

提示:辅助控制器的核心价值不是“在闭环里真的经常用”,而是“在理论上替MPC完成最后一步的稳定性担保”。它设计得保守一点没关系,终端区域的形状难看也没关系,关键是数学验证要过关。

实际设计时,我还会做一次“辅助控制器闭环仿真”,故意从终端区域边缘出发,确认状态在辅助控制器作用下确实向原点收缩,而不是理论推导写完就完事。这一步看着多余,却能最早暴露离散化导致的不等式失效问题。

3. 稳定性证明的关键链条:递归可行性和代价衰减

3.1 两步走:这一拍行,下一拍还行,代价还要降

辅助李亚普设计装上之后,MPC闭环稳定性证明就变成一套非常清晰的流程。假设当前时刻k已经求出一个最优控制序列u*(0),u*(1),…,u*(N-1),相应的状态预测为x*(0),x*(1),…,x*(N)。由于终端约束强制x(N)∈Xf,而Xf是辅助闭环系统的不变集,接下来把控制序列整体向左平移一步,丢掉第一个控制量,把原序列尾部u*(1),…,u*(N-1)作为新控制序列的前N-1段,最后补上一拍辅助控制κ(x(N))作为新的末端控制。

这一步是最关键的操作,叫“平移尾段”。因为辅助控制器能够保证从x(N)出发沿着u=κ(x)前进的状态x(N+1)仍然留在Xf内,所以平移之后的新状态序列依然满足终端约束。也就是说,只要k时刻的问题是可行的,k+1时刻就一定能找到可行解。这就是递归可行性,它保证了整个闭环过程不会“走到一半无解”而崩溃。

第二步是代价衰减。记当前时刻最优代价为J*(x)。下一时刻用平移尾段构造一个可行解,把这个可行解的目标函数值记为J_tail(x+)。因为J*(x+)是最优值,它一定不大于J_tail(x+)。把J_tail(x+)展开,利用终端代价在辅助控制律下的下降不等式,可以经过代数整理得到

J*(x+) - J*(x) ≤ -ℓ(x, u*(0))

这条不等式说明,闭环下每一拍的真实代价都在严格下降,而且下降量不小于当前阶段代价。由V是正定函数,加上代价单调递减,就可以推出闭环渐近稳定。

3.2 为什么这个证明绕不开“辅助”

很多人会问:能不能直接把终端代价设成一个大二次型,不搞辅助控制器?答案是可以“形式上设”,但证明不了。因为终端代价必须满足函数的局部下降条件,没有辅助控制律做背书,你根本不知道终端区域内应该有多少衰减量。反过来,有了辅助李亚普函数,Vf和κ是配套的,证明里每一行都闭合。

更现代的做法里,很多论文开始取消硬性终端约束,只保留终端代价,通过充分长的预测时域配合局部可控性条件证明稳定性,这就是无终端约束MPC的思路。但仔细看它的证明,终端代价依然来自某个局部控制器的李亚普函数,只是把“硬约束保证终端状态在Xf内”换成了“长时域让尾部自然进入Vf的吸引域”。核心结构没变,辅助李亚普设计依然在暗处起作用。

所以我的经验是:读MPC稳定性论文时,哪怕作者说不要终端约束,也要自己在草稿纸上找一找“Vf是谁、它对应的辅助控制器是什么”。找到了,整篇证明的骨架就清楚了;找不到,这篇论文的稳定性结论就要打个问号。

4. 从公式到程序:仿真里的实现套路

4.1 一个能跑通的最小案例

理论讲完,必须落到代码上,不然看一百遍还是不会用。我拿一维非线性系统举例

x(k+1) = x(k) + Δt·(-x(k)^3 + u(k))

状态约束取|x|≤1.5,输入约束取|u|≤1,阶段代价ℓ=x^2+0.1u^2。这个系统在原点附近线性化,得到一个标量系统,可以直接用LQR配一个终端代价Vf=P x^2,终端区域取|x|≤α。

先算线性化:A=1,B=Δt,代入离散黎卡提方程直接手算,取Q=1,R=0.1,Δt=0.1,可以解出P。然后画一下闭环映射,检查在|x|≤α内,辅助控制器u=-Kx满足输入约束,并且衰减不等式成立。算好之后,MPC优化问题就是:

min ∑_{i=0}^{N-1} (x_i^2 + 0.1 u_i^2) + P x_N^2

s.t. x_{i+1} = x_i + 0.1·(-x_i^3 + u_i),|x_i|≤1.5,|u_i|≤1,|x_N|≤α

这个案例虽然小,所有关键元素都齐了:辅助控制器、终端代价、终端约束、平移尾段的可行性验证。

4.2 代码骨架和三个值得注意的细节

仿真主循环用Python写的话,核心结构大概是:

import casadi as ca # 系统定义、代价权重、终端P和alpha N = 20 x0 = np.array([1.0]) while True: opti = ca.Opti() x = opti.variable(N+1) u = opti.variable(N) opti.subject_to(x[0] == x0) for i in range(N): opti.subject_to(x[i+1] == x[i] + dt*(-x[i]**3 + u[i])) opti.subject_to(x[i] <= 1.5); opti.subject_to(x[i] >= -1.5) opti.subject_to(u[i] <= 1); opti.subject_to(u[i] >= -1) opti.subject_to(P * x[N]**2 <= alpha) cost = ca.sum1(x[:-1]**2) + 0.1 * ca.sum1(u**2) + P * x[N]**2 opti.minimize(cost) opti.solver('ipopt') sol = opti.solve() x0 = sol.value(x[1])

代码骨架本身不难,难点在三个工程细节。

第一个是终端约束的写法。直接把x[N]放进硬约束在优化器里会引入非凸约束,尤其是终端区域是椭球x^T P x≤α时,IPOPT这类求局部最优的内点法并不怕这个问题,但如果状态维数高、α又很小,初始点稍微差一点就可能收敛到不可行的伪解。我自己的做法是先用松弛版本跑:把终端约束从硬约束临时放开,加一个很大的惩罚项,得到初始可行解之后再收紧。

第二个是“平移尾段”的数值验证。很多人写完代码就只看轨迹有没有收敛,我强烈建议同时记录两个量:每一步的真实代价J(x(k)),以及真实代价的变化量J(x(k+1))-J(x(k))。如果变化量没有比-ℓ(x(k), u(k))更小,说明终端配置没生效或者数值有问题。

第三个是滚动中的“理论换挡”。MPC求出的最优控制序列中,最后一个控制量和辅助控制律之间往往不相等,这是正常的。因为我们没有强制“末端用辅助控制”,终端约束只是保证“末端之后可以使用辅助控制”。实际闭环中,辅助控制器甚至一次都不真正上场——它只需要在证明里存在就足够了。

5. 调参中的坑和实用避坑心得

5.1 终端区域太小,可行域缩成一团

这是最常见的失败模式。现象是MPC在实验或仿真里初期一切正常,但状态稍微远离原点一步,优化器就开始报不可行,轨迹直接中断。

原因基本都出在α选小了。α控制着终端区域Xf的大小,直接决定“预测末端必须落在哪里”。α小,代表末端被压得很紧,初始可行域随之变小。解决办法是回头检查辅助控制器:如果α小是因为输入约束吃紧,那就得扩大控制权限候选集,或者换一个更“凶”的辅助控制器;如果α小是因为辅助控制器本身在李亚普函数方向上不匹配,那就重新调LQR的Q和R权重。

我踩过最蠢的一次坑是,案子里辅助控制器明明是LQR,但我在验证阶段只用了一个固定方向的状态点,导致α估计偏大。仿真中恰好有个状态点跑出终端区域,MPC当场翻车。后来老实做网格采样,再没出过这类问题。

5.2 终端代价的P矩阵范数太大,数值病态

辅助李亚普设计的终端代价如果是二次型x^T P x,P的范数由Q和R决定。Q取很大、R取很小的时候,P的范数会非常大,优化器里终端代价的梯度会压过阶段代价的梯度,导致数值上目标函数像一块跷跷板,收敛精度很差。

一个实用的建议是给P做归一化,把目标函数里的终端代价写成(x_N^T P x_N)/λ,再根据透视把P换算到合理量级。或者更简单,Q和R的选择时就把终端代价的量级估算进去:先算一遍DARE,看P的谱半径,如果超过1e4,就把R适当调大或Q适当调小。

还有一个小技巧:如果P的条件数太高,可以在终端代价里加一个很小的单位矩阵干扰项,比如P+εI,稍微牺牲一点理论优雅性,换数值上稳定得多。理论上要重新验证衰减不等式,但这通常只损失一点点终端区域。

5.3 初始状态不在可行域,怎么找回

实际控制器不可能从天上掉下来的可行域里开机。上电时状态可能远远超出终端约束允许的范围,硬约束直接导致第一拍就无解。工程上的标准处理是软化终端约束:把x(N)∈Xf从硬约束变成惩罚项

F(x(N)) = max(0, x(N)^T P x(N) - α)

然后目标函数里加一个比阶段代价大得多的权重。这样状态远离原点时,终端约束会被暂时“买断”,MPC仍然能给出一个可行解,等状态逐渐被拉进终端区域之后,软约束惩罚自然就消失了。

注意这只是工程补救,不能用它骗自己说“辅助李亚普不需要终端约束”。真正的稳定性证明仍然需要硬约束版本,软约束版本只是作为启动和异常恢复时的安全网。

5.4 连续域设计直接离散化,衰减条件悄悄失效

做非线性系统时,我习惯先在连续时间下设计CLF和辅助控制器,然后再离散化仿真。但连续时间满足的导数条件,离散化之后不一定自动满足。打个比方,连续车开得很稳,不代表换成离散采样之后还稳——采样间隔够大,同样的控制律完全可能震荡。

所以所有验证都要落到离散时间不等式

V(f(x, κ(x))) - V(x) ≤ -ℓ(x, κ(x))

上面这条不等式的检查逻辑是:在终端区域内部采点,对每一点把辅助控制器作用一步,看李亚普函数的变化量是否真的小于等于-阶段代价。如果多点采样都通过,再跑闭环;如果边际吃紧,就把Δt减小,或者重新设计辅助控制器。这一步不能省,我在连续设计、离散验证这条路上栽过的跟头,比终端区域选错还多。

6. 以后读MPC稳定性论文,记住这三个问题就够了

被辅助李亚普设计这套思路训练过之后,我再看任何一篇MPC稳定性论文,都会带着三个问题去读。第一个问题是在论文里找终端代价的表达式,看它是不是某个李亚普函数;第二个问题是找终端控制器或者它的存在性假设,看作者是否真的构造出了那个“兜底”的控制律;第三个问题是找终端区域的描述,看它是不是由某个水平集直接定义的,以及参数怎么选。

把这三个问题定位完,论文的稳定性证明基本就是一条直线:递归可行性来自终端区域不变性,代价衰减来自终端代价沿辅助控制器的下降,闭环稳定是这两条的简单推论。不管论文包装成经济MPC、鲁棒MPC还是随机MPC,只要它的稳定装置用的是这套逻辑,辅助李亚普设计就都在那里等着你。

回到实践,我的个人体会是:与其费劲去找什么“万能终端代价”,不如老老实实为系统设计一个简单的辅助控制器,再花半小时把它的李亚普函数验证踏实。这半小时的成本,换来的是稳定性证明的可信度和调试时少掉一大半无解的烦恼。做MPC的人,迟早都会遇到终端约束和递归可行性这两个坎,辅助李亚普设计就是过去十几年学界替我们总结出来的最省力的一道桥。这篇笔记先写到这里,后面再继续整理不同变体论文之间的细节差异。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询