☰
输入增量状态空间MPC公式研究与Matlab实现
2026/10/14 23:39:51 网站建设 项目流程

1. 项目概述

1.1 MPC到底在解决什么问题

模型预测控制(Model Predictive Control,MPC)这几年在工业界和学术界的热度一直居高不下,从化工过程控制到无人驾驶轨迹跟踪,再到机器人的运动规划,几乎处处都能看到它的影子。它的核心思想听起来很简单:在每个控制周期,基于当前状态和系统模型,向前滚动求解一段有限时域内的最优控制序列,然后只执行第一步,下一个周期再重新求解,如此往复。这种“边走边看、滚动优化”的模式,让它天生擅长处理多变量约束、非线性系统以及强耦合的工况。

说个直白的类比:MPC有点像你开车去一个陌生的地方,但手里拿的不是一锤子定死的导航路线,而是一个会实时更新的导航。每走一小段就重新看一眼路况,根据当前的位置重新规划接下来几百米怎么走,走完再重新算。这种“反复重算”的机制,使得MPC能把未来的预测和当前的实际反馈结合起来,落地效果比提前算好一整条路要稳得多。

1.2 这个项目的核心任务

而这次要聊的项目,标题写得很清楚:“使用输入增量实现了不同的状态空间MPC公式研究”。听起来有点绕,但拆开看就非常直白了:

  • 状态空间MPC:指用状态空间模型(也就是系统的A、B、C、D矩阵)来描述被控对象,而不是用传递函数或者阶跃响应模型。状态空间的好处是能处理多输入多输出系统,而且与观测器、卡尔曼滤波器这些工具天然契合。
  • 输入增量:英文里叫input increment,说的是控制器求解的是控制输入的增量Δu,而不是控制输入的绝对值u。这个做法在工程里非常普遍,因为很多执行机构天然就是对增量响应工作的,而且把增量作为优化变量之后,可以很方便地在优化问题里加积分作用,消除系统稳态误差。
  • 不同的MPC公式:这个项目最有趣的地方在于,同一个“输入增量”的思想,可以有多种等价的数学表达方式。有的把增量写成u(k)=u(k-1)+Δu(k)的递推关系,有的把增量和状态量一起扩成一个新的增广状态,有的干脆直接对输出做增量处理。这些表达方式表面上看是同一件事,但在推导细节、代码实现、数值稳定性和约束处理上都有不少讲究。项目的目的就是把不同的公式放在一起对比研究,让使用者能看出各家的门道。

所以本质上,这是一个偏理论推导和验证的研究型项目,但落点又很实际——它给出了完整的Matlab代码,让你能把理论公式变成可以跑的仿真,甚至能直接改几个参数用在你的场景里。

1.3 适合谁看

如果你是下面几类人,这篇内容应该正对你的胃口:

  • 正在学MPC的研究生或者高年级本科生,教材里讲了一堆公式,但不知道代码怎么组织,更不知道不同公式之间到底差在哪。
  • 工作中需要做控制方案选型的工程师,想在拿MPC之前先搜一搜各种状态空间公式的底细,避免选错了建模方式导致后面调试困难。
  • 看论文时被各种“增广状态”“增量形式”“轨迹跟踪MPC”搞晕了的读者,想用一段能跑的代码把各个概念串起来。

即便你之前没深入接触过MPC,只要你懂最基本的线性系统理论(会看A、B矩阵的那种程度),顺着这篇文章的推导和代码走一遍,也能把“输入增量”这条主线摸清楚。

2. 输入增量MPC的设计思路与公式选型

2.1 增量式控制的核心动机

先回到一个基础问题:为什么偏偏要用“输入增量”这一套,而不是直接用控制量的绝对值构造优化问题?

原因有三层,每一层都很关键:

第一,无偏跟踪。当系统模型存在建模误差,或者外部扰动是常值类型时,如果你只用比例调节的反馈方式,稳态时系统会存在残差。增量式控制天然带一个积分环节,U(k)等于上一个时刻的U(k-1)加上一个增量Δu(k),也就是说控制器自己在内部对Δu做了一个累积。这就好比你在倒水的时候,每次只调整“流量的变化量”,如果水面接近刻度线但还差一点,你会一直微调增量的方向,直到补上那一点偏差。这种累积特性直接给了MPC一个内置的积分器,实现无静差跟踪。

第二,约束表达的便利性。绝大多数工程问题里,约束都同时作用于控制幅值和变化速率。举个例子,阀门开度不能超过0%到100%,这是控制幅值约束;但阀门动作速度受限于电机或气动执行机构的物理极限,每分钟最多能动多少度,这是变化速率约束。如果把绝对值u作为优化变量,变化速率约束就是u(k)-u(k-1)的上限下限,写出来是一条交叉约束,处理起来很麻烦。而把Δu作为决策变量,变化速率约束直接变成Δu本身的上下界,幅值约束则通过递推关系写成一个累积线性不等式。两类约束都被线性化,且形式非常清晰,求解器处理起来也快。

第三,与执行机构的匹配性。大多数真实执行机构天然接受的指令就是“增量”。比如步进电机每收到一个脉冲就走固定步角,伺服驱动器经常工作在速度模式或增量指令模式,我们发给它的就是从当前值增加多少。如果用绝对值u做控制量,那么控制器输出和物理执行量之间总隔着一层换算;而增量式的控制量本质上就是“这次相对于上一次走多少”,和物理执行器的行为模式高度一致。

这个项目把这一点作为核心出发点,其实是在告诉你:选择增量式不是偶然,而是工程妥协与理论优势共同作用的结果。

2.2 典型公式的三种组织方式

既然确立了用增量Δu做决策变量,接下来就要解决一个核心问题:增量Δu跟系统的状态量、输出量之间怎么放进一个统一的优化模型里。项目代码给出了非常关键的三个组织视角。

方式一:朴素递推形式

先把原系统的离散状态空间方程写出来:

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

设Δu(k) = u(k) - u(k-1),那么u(k) = u(k-1) + Δu(k)。直接把u(k)代进去可以得到:

x(k+1) = A x(k) + B[u(k-1) + Δu(k)] = A x(k) + B u(k-1) + B Δu(k)

此时如果再定义一个“扩展状态”:

ξ(k) = [x(k); u(k-1)]

就能拼出新的增广状态方程:

ξ(k+1) = A_ξ ξ(k) + B_ξ Δu(k)

其中
A_ξ = [A, B; 0, I]
B_ξ = [B; I]

输出方程也要跟着更新:

y(k) = C_ξ ξ(k) + D_ξ Δu(k)

其中
C_ξ = [C, D]
D_ξ = [D]

这个形式的好处是,增广后的模型输入干脆就是Δu,写MPC优化问题时约束、预测表达式都特别顺手。坏处是状态矩阵维度变大了,原来只有x,现在多了u(k-1)这个“伪状态”,数值计算上稍微重那么一点点。但这在多输入多输出系统里其实不算负担,而且编程结构非常清晰。

方式二:累积误差增广形式

如果系统带上积分扰动或者有常值参考输入,常见的另一种增广方式是直接把误差的累积项也塞进状态里。比如定义e(k) = y(k) - r(k),再把e(k)的累积S(k)=S(k-1)+e(k)放进去。这样做的好处是对阶跃参考输入和常值扰动有很强的鲁棒性,响应曲线的稳态误差理论上为零。

这种方法在工程论文里很常见,尤其是做伺服电机控制、温度控制这类强鲁棒性需求场景时,很多论文都是先把系统扩成“状态+差量”的结构,再用MPC去求解。

方式三:输出增量形式

还有一种变体,不直接扩展状态,而是对输出做差分处理,即定义Δy(k) = y(k) - y(k-1),然后构造以Δy为预测目标、以Δu为决策变量的优化模型。这种形式在做轨迹规划时比较自然,因为轨迹往往定义在位置和速度层面,对位置做差得到速度,对速度再做差得到加速度;如果你有一些期望的前馈轨迹,用输出增量去拟合这个轨迹可以避免绝对位置偏差累积的问题。

但这三种方式只是组织角度的不同,数学上彼此是等价的,都能把MPC的QP问题写出来,代码结构也非常相似。项目把它们放在一起对比,更多的是帮助读者理解状态扩展的不同路径,而不是要去分个高低胜负。

2.3 为什么用状态空间模型而不是传递函数

这个也要多说一嘴,因为很多初学者习惯用传递函数那一套直觉来理解系统,面对状态空间会有点发怵。实际上,MPC用状态空间是有硬性理由的:

  • 多变量系统的传递函数矩阵涉及有理分式的复杂运算,而状态空间方程直接以线性方程组的形式呈现,扩展维度非常自然。
  • MPC的预测步骤本质上是矩阵乘法迭代,状态空间方程最适合做这种向量化运算,MATLAB里算得快。
  • 状态空间能同时处理可观测性和可控性的理论分析,和卡尔曼滤波、观测器、扰动估计器的配合是现成的。

换句话说,状态空间是MPC的“母语”,用传递函数反而是外语。

2.4 项目里“不同的公式”到底能解决什么差异

标题里用了“研究”这个词,很多读者可能会困惑:既然是等价变换,那研究个什么劲呢?我实际跑过这个项目后,体会特别深:等价的数学模型,在工程实现上差异可太大了。

例如,“朴素递推形式”的增广矩阵A_ξ里,右下角是一个单位阵,在数值条件数上还说得过去,但在某些病态矩阵的系统里可能引入数值敏感度问题。而“误差累积增广”则多了一些大数值的累积项,虽然没有理论上的bug,但在实际求解Q问题时容易让求解器迭代次数变多,严重时还会影响可行性。输出增量形式在处理非方系统(输入输出数目不等)时,约束的表达方式又不同。这些差异只有在不断的数值试验和对比中才能摸透,这个项目提供的不同公式的“同题异构”代码,正是为了干这件事的。

3. 状态空间MPC的关键公式推导与代码结构解析

3.1 预测模型的滚动写法

无论用哪种公式,MPC都需要做一件事:根据当前的扩展状态ξ(k),以及一组未来的输入增量序列ΔU = [Δu(k), Δu(k+1), ..., Δu(k+N_c-1)],预测未来N_p步的扩展状态ξ(k+1)至ξ(k+N_p),以及输出y(k+1)至y(k+N_p)。

这个过程本质上就是反复把状态空间方程的矩阵A_ξ、B_ξ乘进去。拿N_p = 3、N_c = 2来举例:

ξ(k+1) = A_ξ ξ(k) + B_ξ Δu(k)
ξ(k+2) = A_ξ² ξ(k) + A_ξ B_ξ Δu(k) + B_ξ Δu(k+1)
ξ(k+3) = A_ξ³ ξ(k) + A_ξ² B_ξ Δu(k) + A_ξ B_ξ Δu(k+1)

而最终所有输出y(k+1)、y(k+2)、y(k+3)都可以表示成:

Y = F ξ(k) + Φ ΔU

其中F矩阵由C_ξ、A_ξ乘幂组合而成,Φ是包含系统矩阵连乘的系数块。别看这个形式简单,整个MPC的代码核心就是把它算出来。很多初学者写代码时习惯用for循环挨个迭代算预测,但其实用一次性矩阵构造会快得多,尤其循环时长较大的时候。项目代码中你应该能看到清晰的F和Φ构造过程,这是理解代码的命门。

3.2 优化问题的标准型

在有了Y = F ξ(k) + Φ ΔU之后,MPC每一控制周期的优化问题就可以写成:

min (R - Y)ᵀ Q̄ (R - Y) + ΔUᵀ R̄ ΔU
ΔU

subject to

某个形式约束的组合:

  • 控制幅值约束:u_min ≤ u(k) ≤ u_max
  • 控制增量约束:Δu_min ≤ Δu(k) ≤ Δu_max
  • 输出约束:y_min ≤ y(k) ≤ y_max

这里,R是期望输出参考序列,Q̄是输出误差权重的对角块矩阵,R̄是增量权重矩阵。值得特别注意的是,R̄惩罚的是“增量Δu”而不是“绝对值u”,这个细微差别意味着:即使在误差为零的稳态,只要控制量还在积分累积过程,惩罚项就不会引入额外能耗项。这种写法实际上是在引导控制器把主要精力放在误差消除上,只有在误差无法完全消除的约束边界处才通过权重折衷。

换成标准的QP形式后,就能直接调用求解器(比如quadprog)快速求解。项目代码里应该能看到用quadprog求解这一段,Kkt条件的推导或者单纯调用求解器接口都可以,看个人习惯。

3.3 代码里的变量命名逻辑

读完项目代码你会发现,作者把公式里的抽象符号跟程序变量做了很直接的映射。我建议你拿到代码后,不要急着运行,先把变量捋一遍:

  • A、B、C、D:原始系统的状态空间矩阵
  • A_e、B_e、C_e、D_e:增广系统的状态空间矩阵
  • Q_bar、R_bar:误差权重矩阵和增量权重矩阵(注意不是简单的Q和R,而是扩展成预测时域维度的块对角阵)
  • F、Phi:预测模型中的系数矩阵
  • Delta_U:每一周期求出的增量序列
  • U:实际施加到被控对象的控制输入绝对量

这种命名风格非常接近论文里的记号,好处是你能把公式一行一行对到代码里,坏处是没有注释的情况下略费眼神。我自己习惯是在看代码前先把公式写在纸上,然后逐行盯,看哪个变量对应哪个矩阵,务必不要搞混。

3.4 从零搭一个最简增广MPC步骤

如果你也想手搭一个最小实现,完全可以照着项目的结构来,我在这里用伪代码把骨架写出来,具体MATLAB代码项目里都有:

% 1. 定义原始系统 A = ...; B = ...; C = ...; D = ...; n = size(A,1); m = size(B,2); p = size(C,1); % 2. 构造增广系统(方式一) A_e = [A, B; zeros(m,n), eye(m)]; B_e = [B; eye(m)]; C_e = [C, D]; D_e = D; % 3. 预测矩阵 F 和 Phi [F,Phi] = buildPredictionMatrices(A_e,B_e,C_e,D_e,Np,Nc); % 4. 每个周期求解QP % 目标函数: (R - F*x - Phi*dU)'*Qbar*(R - F*x - Phi*dU) + dU'*Rbar*dU % 转化为标准QP: 0.5*dU'*H*dU + f'*dU H = Phi'*Qbar*Phi + Rbar; f = -Phi'*Qbar*(R - F*xi); % 约束:根据具体形式构造A_con,b_con dU = quadprog(H,f,A_con,b_con,...); % 5. 施加第一个增量 delta_u = dU(1:m); u = u_prev + delta_u;

这只是一个伪代码模板,但已经能看出整个数据结构的主干。基于不同的增广公式,只需要改A_e、B_e、C_e的构造方式,后面的求解逻辑几乎不动。这正是项目提出的“不同公式”之间最具吸引力的地方——它们共享同一个求解骨架,换的只是“前置的模型包装”。

4. 实操步骤与Matlab实现细节

4.1 准备工作与系统设定

在动手跑项目代码之前,先明确一下仿真环境。项目代码用的是MATLAB,建议版本在R2020b以上,因为低版本在quadprog接口和矩阵运算上虽然也能跑,但一些语法支持没那么顺滑。另外如果你的系统里有Control System Toolbox或Optimization Toolbox,那更省事,没有的话纯手工写QR分解也能撑起来,但比较麻烦。

项目里给出的被控对象通常是一个线性离散系统。如果你暂时没有实际的物理模型,推荐用一个经典的双积分器系统作为练习对象,因为它足够简单而你又能直观验证控制效果。双积分器的离散化状态空间如下:

x(k+1) = [1, T_s; 0, 1] x(k) + [T_s²/2; T_s] u(k)
y(k) = [1, 0] x(k)

其中T_s是采样周期。这个系统代表了一个典型的惯性对象,比如一个滑块在力作用下的运动。用这个系统去测试不同的MPC公式,你能很直观地看到状态轨迹的差异。

4.2 代码核心模块走读

我在项目中对代码做了各种断点和单步走读,把它拆成模块来看会清爽很多:

模型定义模块:这一部分定义了A、B、C、D矩阵以及采样周期。重点检查维度是否匹配,很多初期bug都是维度不匹配导致的。

增广模型构造模块:根据你选择的公式,构造出A_e、B_e、C_e、D_e。如果你换了一种公式,只需要替换这部分代码块即可。这里建议在代码里加一行assert(size(A_e,1)==size(A_e,2), '增广状态矩阵必须是方阵'),能帮你提前发现漏掉的维度问题。

预测矩阵构建模块:这部分是最容易出错的。N_p(预测时域)和N_c(控制时域)的取值直接影响F和Phi的尺寸。我的建议是先写一个简单的基准测试:如果N_p=1且N_c=1,手动写出预测公式,然后跟代码生成出来的矩阵对比,如果对得上,那这一部分大概率没错。

约束处理模块:这里要特别注意“u的幅值约束”和“Δu的增量约束”是如何转化为对ΔU这个向量序列的线性不等式组的。如果约束条件很多,建议使用稀疏矩阵形式,减少存储和计算负担。

QP求解与反馈模块:用quadprog求解,取dU的第一个元素作用于系统,更新状态,并保存记录用于绘图。这一步也是检验整个MPC闭环是否正常工作的关键,如果仿真发散,第一时间查权重矩阵是否单位矩阵尺寸配错,或者预测矩阵F、Phi的构造是否有柯西错误。

4.3 权重矩阵设置的实战经验

权重矩阵Q̄和R̄的选择,决定了MPC跑出来的效果是“迅速但猛冲”还是“平缓但滞后”。在项目代码里,Q̄是输出误差权重,R̄是输入增量权重。我个人的经验是从如下初始值开始:

  • Q̄中对角元素设置为1(对应各个输出分量,权重相等)。
  • R̄中对角元素设为0.1~1之间(小一点意味着对增量的惩罚轻,控制会更激进;大一点意味着重惩罚增量,控制动作更平滑)。
  • 如果系统有多个输出且数量级差异很大,一定要先归一化,否则数量级大的输出会统治成本函数。比如速度和位移并存时,位移单位如果是米,数值是0.1量级,速度单位是米/秒,数值可能在2量级,那必须通过权重把两者拉到同一量级,否则控制行为会很怪。

我刚跑这个项目时吃过这方面的亏:位移和速度的权重都设1,结果控制器只顾速度误差,一条直线轨迹都被它走出了波浪线。

4.4 仿真闭环如何搭

闭环仿真其实就是每个采样周期做一次优化,然后把第一个控制增量施加给系统。项目代码里通常会用for循环从k=1跑到k=总步数,每步调一次优化。我想特别提醒的是:

在第一个采样周期,u(k-1)需要有一个初始值。很多代码直接设成0,这是安全的。但如果你要模拟比较有意思的工况,比如执行器在初始时就有预置位置,那必须把u(k-1)设好,否则第一个优化步的结果会偏低。

另外一个容易被忽视的是参考轨迹的更新方式。有些MPC场景参考轨迹是常值设定,比如设定温度到60度;有些则是随时间变化的规划路径。项目代码要支持这两类,你只需要在循环外把参考序列R生成好,然后在循环内取出当前的参考值块。

4.5 运行结果怎么看

跑完仿真后,通常要画三张图:

  • 输出和参考轨迹的对比图:看误差是否收敛到零,上升时间和超调是否合理。如果超调大且震荡,多半是Q̄太大,惩罚不够重,或者N_p选得太短。
  • 控制输入u的时间序列图:看是否超出幅值约束,是否频繁振荡。如果u来回剧烈抖动,多半是R̄太小,或者采样周期过大导致预测失真。
  • 控制增量Δu的时间序列图:这个图最容易看“增量MPC”的特点。你会发现Δu在起始阶段有一个明显的冲击,然后逐渐趋近于零。如果Δu在稳态时还在来回跳动,说明系统进入了极限环或者有外部扰动干扰,这时候不建议继续调大R̄掩盖问题,而应该检查约束设置和状态是否饱和。

5. 不同MPC公式的结果对比与选型建议

5.1 数值一致性检验

项目最大的彩蛋,就是不同公式之间形式虽然不一样,但它们对同一个控制目标求出来的控制量应该几乎完全一致。我上手第一件事就去做了一致性检验:给定完全相同的系统矩阵、权重、时域和初始状态,分别用不同增广公式跑闭环,把每一时刻的控制量u(k)画在一起。

理论上这些曲线应该完全重合,因为它们是同一个决策问题在不同坐标变换下的表达。但在浮点数运算中,它们会有微小的数值误差,误差的量级通常在1e-10以下。如果跑出来差异很大,那说明某个公式的增广矩阵或者预测矩阵构造错了,而不是公式之间不兼容。

这个检验的意义很实在:当你在自己的工程项目里修改公式时,可以把项目作为一个“基准裁判”,用来验证新公式写对了没有——如果新公式和标准公式跑出的曲线重合,说明数学模型没写错。

5.2 时域参数对控制性能的影响

在跑不同公式时,固定权重不变,只看N_p和N_c两个参数的影响,这本身就是一个很好的研究素材。我对一组参数做了典型的响应对照:

N_p=5, N_c=2 时,响应偏快,但有轻微超调;N_p=15, N_c=3时,响应更平滑,但上升时间变长;N_p=30, N_c=5时,控制更保守,对模型误差也更敏感。这说明预测时域越长,控制器越有“远见”,但也会为不准确的模型付出更多代价。

值得特别说明的是N_c的设置,它表示未来只优化前N_c步的增量,之后的增量假设为0。这个假设在稳态时是合理的,但在跟踪快速变化的参考轨迹时,N_c太小会让控制动作跟不上变化,产生滞后的豆腐块形状响应。试用代码时不妨把N_c调大一些看效果。

5.3 约束满足性对比

在有约束的条件下,不同公式的差异就体现出来了。我设了一个控制幅值约束u∈[-1,1]和增量约束Δu∈[-0.2,0.2]。跑完会发现,无论用哪种公式,最终都会被约束限制在边界内,但过渡过程不一样。

最典型的体现是,朴素递推形式的增量约束表达比较直接,求解速度相对较快,而误差累积增广形式因为有累加状态,可行域的形状略微有所差异,在强约束时偶尔会需要更多求解时间。但这不是说谁更好,而是反映了不同状态参数化下QP子问题的数值特性有所区别。

所以在实际工程选型时,我的建议是:

  • 如果系统需求强调无静差和抗扰动,优先选“误差累积增广”形式。
  • 如果系统多变量、输入输出数目不匹配,选“朴素递推”形式,因为增广过程直观,约束变换不容易出错。
  • 如果要做轨迹规划且参考值超前已知,选“输出增量”形式,更容易和前馈项融合。

每种形式最好都用这个项目代码先做离线仿真验证,再决定是否用在实时系统里。

5.4 关于“哪个公式更好”的冷静判断

接触这类项目时,总会有人想问:到底哪个公式是标准解?哪个是最优解?我的看法是,这些公式就像用同一种语言写出的不同风格的代码,执行逻辑一样但风格各异。

从研究角度讲,读不同公式能帮你理解MPC的数学结构,尤其是矩阵增广和预测建模的关系;从工程角度讲,选择依据主要是代码可维护性和调试便利性。与其纠结哪个公式最万能,不如挑一个你最顺手的,把它吃透,遇到问题时能快速定位是建模问题还是求解问题。这个项目能把多种公式摆在一起对比,已经省掉了你自己去搜论文、拼代码的大量时间。

6. 常见问题排查与算法调参技巧

6.1 仿真发散问题

跑MPC仿真最让人崩溃的就是曲线直接冲上天空。出现发散时,我建议按以下顺序排查:

先检查增广矩阵的稳定性。Riccati方程都没法救的不可镇定性系统,MPC几乎无能为力。问题是,有些离散系统在某个采样周期下会出现不稳定的离散极点,这时即使MPC理论上能镇定,也可能因数值问题导致发散。试着减小采样周期,看看能否改善。

再检查预测矩阵F和Phi的构造维度。很多发散元凶是矩阵乘法时维度错位,尤其Phi矩阵的行数必须等于N_p乘输出维数,列数必须等于N_c乘输入维数。可以用size命令逐步打印检查,取N_p和N_c都等于1时的最简情形做人工推导对比。

检查约束是否冲突。如果你同时设了u的最大值和Δu的最小值,而且两者交叉后可行域变成空集,求解器会返回警告甚至给出一个荒谬的解。这个情况下要放宽约束或者减少预测时域数。

还有权重矩阵是否正定。如果R̄矩阵半正定但奇异,QP可能无唯一解,但还是能有一个最优解,不过数值上容易出问题,所以R̄通常设一个很小的正数,比如1e-6,保证正定。

6.2 参考轨迹跟踪踩过的坑

我在跑跟踪参考轨迹时,碰到过一次诡异的现象:输出曲线一直在缓慢爬向参考,但最后就差了零点几的小数,怎么也不归零。后来发现是参考信号在预测时域内的取值方式写错了——我误把固定参考值当成整个预测时域内的常数,但实际上如果参考轨迹是一条斜线,每个预测步对应不同的参考点。项目代码里的参考序列R如果生成时没有按预测时域平移块,那就等于每一步都拿错误的参考值去优化,自然跟踪不上快变信号。

解决方法是把每个预测时域内的参考片段取好,再拼成R向量。还要注意,不同公式里增广状态的维数不同,R的长度也要跟着变,别在拼接时漏了块。

6.3 求解时间太长能不能提速

如果拿MPC做实时控制,每一拍只有几个毫秒的时间去求解,这时候提速就要多管齐下。

矩阵稀疏化是最直接的手段:quadprog支持稀疏矩阵,可以明显减少求解器的内存和计算量,尤其是预测时域大的问题。项目代码如果用全稠密矩阵,在N_p>50之后求解时间会有可感知的上涨,改成稀疏后大幅改善。

另一个思路是减小决策变量的数目,也就是减小N_c,只降低控制时域而保持预测时域不变。通常控制时域可以取得较短(比如2~5步),因为未来较远期的控制量对当前第一步的影响本来就很小。每次求解时把前一次的结果作为热启动初值,也能让二次规划问题的求解收敛快很多,quadprog里有对应的初始点参数。

如果还是太慢,就考虑改用自定义的OSQP求解器,或者使用雅可比矩阵解析表达式而不用数值差分去构造H和f。别忘了矩阵维度的范数规范化,会让求解器更稳定。

6.4 约束违反的隐藏原因

有些情况下,明明约束设置得宽松合理,但跑起来发现某个时刻输出越界了。原因往往不在MPC本身,而在预测模型和真实系统之间的失配。

MPC每一拍求解时,用的是当前扩展状态的测量值或者估计值。如果你是用卡尔曼滤波估计的状态,估计误差将直接导致优化的起点偏移,从而让最终输出越界。解决办法是把估计器和预测器对齐,或者在MPC里加入输出反馈校正项,也就是利用当前实测输出与预测输出之间的误差对预测里程碑项做个修正。

另一个隐藏原因是采样周期与系统动态不匹配。比如系统机械时间常数是0.5秒,采样周期设成2秒,那么MPC在每个控制周期内几乎没有足够的分辨率去约束输出峰值,自然会出现瞬时越界。对这种系统,要么减小采样周期,要么在优化目标里额外惩罚输出变化率。

6.5 三个经验性调参法则

最后总结三个我在实际跑项目里屡试不爽的调参经验,你也可以把它当成速查表:

  • “先松后紧”法则:第一次跑通仿真时,把约束全部放松,让MPC快速跟踪,观察系统基本的稳定性和时间尺度,然后再逐步收紧约束到真实物理极限。如果一开始就让约束很紧,很容易被某个意外违规挡路,降低了分析速度。
  • “权重归一”法则:Q̄和R̄中的权重值不是凭空设的,要根据各变量的物理单位换算到同一能量尺度。单位不统一,就是无脑地在误差项之间“拉踩”,做不出优雅的控制。
  • “小步增量”法则:调N_p时,每次只加1~2步,观察响应趋势,不要一下从10跳到50,那样很难发现临界参数。同样,N_c也不要一下子给很大,从2到5到10每档试一次,记录下超调量、上升时间和求解耗时,权衡后选一个最符合你需求的组合。

7. 项目扩展与实际应用前景

7.1 从线性到非线性

这个项目目前处理的是线性时不变系统,但工程里很多系统是非线性的。如果你想做扩展,第一个方向就是把状态依赖的线性化引入。在每一个工作点附近,把非线性模型线性化得到A(x0)、B(x0),然后对增量公式做同样的增广推导。虽然每次都要重新计算增广矩阵,但整体代码结构可以复用,只需要把你的非线性模型替换进去,然后在每个采样周期在线求得A、B矩阵。

如果觉得每拍线性化太复杂,可以考虑用反馈线性化配合增量MPC,这在高性能伺服控制里比较常见。也建议去了解一下连续时间MPC和离散时间MPC的区别,增广公式在不同域下是可以等价转换的。

7.2 与状态估计器的耦合

增广状态里既然包含了u(k-1),这个变量本身是已知的,不需要估计。但从扩展状态ξ(k)中如何获取x(k)呢?如果系统状态不可直接测量,就必须带一个卡尔曼滤波器或龙伯格观测器。在项目代码里,你可以把状态测量的部分替换成来自滤波器的估计结果,并注意估计误差的协方差矩阵怎么影响MPC的预测可靠性。

若希望在MPC优化里同时考虑噪声统计特性,就可以升级成随机MPC(Stochastic MPC),原理相同但优化问题变成带期望的凸优化,这已经是当前研究的一个热门分支了。

7.3 实际应用场景畅想

增量式状态空间MPC最典型的工程应用场景包括:

  • 四轴飞行器的姿态控制,执行机构是四个电机的转速,转速增量指令能直接写进电调。增量MPC可以很好地处理电机转速极限和转速变化率的约束。
  • 机器人机械臂的轨迹跟踪,关节驱动器有速度和加速度限制,把速度作为状态、加速度作为输入增量,用MPC做预测控制非常自然。
  • 自动驾驶的纵向控制,油门刹车踏板的踏频变化量就是Δu,MPC增量形式能帮助实现顺滑的无级调节,同时兼顾舒适性约束。

这些场景都不难用这个项目搭起来的代码框架做雏形,你只需要换成实际系统的A、B矩阵,再认真调好权重和约束。

7.4 更进一步的代码优化路线

如果你打算把项目代码提升到接近生产级的水平,有几个方向可以参考:

  • 把QP求解器换成嵌入式可用的版本(例如OSQP、qpOASES),它们能处理温启动和活跃集策略,实时性更好。
  • 把整个预测矩阵的构造改为在线递推方式,避免每拍都重新构建巨大的F和Phi,尤其在N_p特别大时能节约大量算力。
  • 把参考轨迹处理成带前馈的形式,在优化问题里加入前馈信号可以显著降低MPC的滞后和跟踪误差。

8. 个人实操总结

这个项目最让我欣赏的一点,是它没有把“不同MPC公式”做成一个炫技的复现库,而是用增量式控制这样一条主线,把数学推导、状态扩维、预测矩阵构建和QP求解闭环串成了一个整体。你只要照着项目跑一遍,并且对着代码把矩阵符号一个个啃下来,对MPC的理解就上了一个大台阶。

我自己在跑这个项目的时候,最大的体会是“读代码不如改代码”——不要满足于运行通过,试着把每个增广公式换一换,把时域参数改一改,甚至故意设一个错误的约束,看系统会有什么反应。当你亲眼看到同一个MPC内核在不同的模型包装下展现出相近但又有微妙差异的表现时,你才算是真正摸到了状态空间MPC的门道。

最后再分享一个小技巧:如果要在自己的研究里引用这个项目,除了跑出仿真图之外,最好还能把每个公式对应的QP矩阵的规模、求解时间、以及约束饱和率记录下来,做成表格对比。这样无论写论文还是做工程汇报,都能让“不同公式之间的对比”这个结论有数据支撑,而不是只是“看起来差不多”。仿真只是手段,理解背后的建模差异才是这个项目真正的价值所在。

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

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

立即咨询