ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

模型预测控制MPC:从PID到滚动优化与QP工程实践

模型预测控制MPC:从PID到滚动优化与QP工程实践 你要控制的东西带着硬约束而你还希望它跑得尽量快——PID那套误差大就加大输出的思路这时候往往就开始顶不住了。模型预测控制MPCModel Predictive Control解决的正是这一类问题执行器有上下限、变化速率有限制、多个变量互相耦合、还希望提前知道设定值要变。它不算新东西过程工业里用了几十年但最近几年在机器人、自动驾驶、储能、电力系统里被反复提起很多人第一反应是听起来很高级是不是很难落地。我把这套东西从直觉到代码完整走了一遍下面按我自己的理解顺序拆开讲。这篇内容适合三类人做过PID但对MPC只有模糊印象的控制工程师在项目里被约束和耦合逼着来找新方案的开发者以及想动手写一版MPC但被一堆矩阵公式劝退的学生。我会先讲清楚它到底在算什么再把预测模型、滚动优化、反馈校正这三块积木拆开然后落到代码最后把调参和踩坑的部分讲透。全程给可复现的参数和代码也标注哪些是我的经验判断、哪些是有标准的做法。1. PID调到头了还是不够MPC到底在解决什么问题1.1 一个反复出现的现场场景假设你在做一个位置伺服要求把一个滑块从0推到1米的位置。用PID你会发现一个很典型的两难Kp给小了到位慢、爬行Kp给大了冲过头再荡回来超调很难看。你开始加微分抑制超调但微分对测量噪声极其敏感传感器稍微有点毛刺输出就开始抖。接着你发现真正的问题不在PID参数上——执行器电机驱动器有最大加速度限制指令给太大直接进饱和饱和期间积分项还在累加退饱和的时候就出现大滞后和大幅超调。你加抗饱和、加限幅、加前馈参数越加越多每换一个工况就要重调一遍。更麻烦的是如果这个位置是一个更复杂系统的一部分比如一个两轴平台两个轴之间存在耦合单独调好的两个PID合在一起就开始互相打架。你开始考虑解耦做补偿器工程量一下子就上去了。这套流程几乎每个做控制的人都走过。PID本身没有问题它的局限在于它只看当前的误差不看未来。它不知道再往前半步就要撞到限位了也不知道接下来设定值要降下来现在没必要冲这么猛。误差出现了才反应本质上是一种滞后的策略。1.2 MPC和PID的根本差别在哪模型预测控制的思路完全不同它拿一个能描述系统未来行为的模型往前推演N步然后在这N步里找一个让总代价最小的控制序列同时保证每一步都满足约束。算完之后它并不把这一整串控制量都执行掉而是只执行第一个等到下一个采样时刻拿到新的状态重新算一遍。这个只执行第一步、然后重算的动作叫滚动优化receding horizon。它是MPC和普通最优控制最本质的分界线也是它自带反馈能力的来源——哪怕模型有偏差只要每一步都基于真实状态重新规划系统就不容易跑偏。把PID、LQR、MPC放在一张表里对比差别会更直观维度PIDLQRMPC是否依赖模型基本不需要需要状态空间模型需要模型可辨识获得硬约束处理靠限幅和抗饱和打补丁不直接处理原生支持写进优化问题设定值前瞻无无需额外前馈天然支持写进参考轨迹多变量耦合需要人工解耦天然处理天然处理在线计算量极低极低每个采样周期解一次优化主要调参对象3个增益Q、RQ、R、S 加两个时域可以看出MPC是用计算量换约束处理能力和前瞻能力。这也解释了它为什么在过程工业先火起来——那里的采样周期常常是秒级甚至分钟级算力不是瓶颈而约束是真实存在且不能违反的比如阀门只能开到100%、反应温度不能超上限、升温速率不能太快。提示如果你的被控对象是单回路、动态平缓、没有真正意义上的硬约束PID往往更划算。MPC的价值高度依赖约束和耦合这两件事的真实存在没有这两个前提上MPC多半是给自己找麻烦。早期的工程形态是动态矩阵控制DMC和模型算法控制MAC它们直接用阶跃响应或脉冲响应作为模型避开了复杂的机理建模这也是MPC能在炼油、化工这类模型难建但响应容易测的行业快速铺开的原因。2. 把MPC拆成三块积木预测模型、滚动优化、反馈校正不管后面写成什么形式MPC的内核永远是三件事一个能预测未来的模型、一个在有限时域上最小化代价的优化、以及一套让系统在模型偏差下仍然稳定的反馈机制。把这三块弄清楚剩下的都是技术细节。2.1 预测模型MPC的水晶球模型的形式很多常见的有状态空间模型、传递函数、阶跃响应模型、ARX/ARMAX这类输入输出差分模型以及近些年用得越来越多的神经网络和Koopman线性化模型。选哪一种主要看你在哪个行业、手头有什么数据。过程工业偏好阶跃响应模型因为现场做一次阶跃测试就能拿到不需要写一堆微分方程。机器人和自动驾驶偏好状态空间模型因为要处理多输入多输出还涉及速度、姿态这些物理量。电力系统的频率调节、储能调度这类场景则会用带约束的线性状态空间模型或者把非线性的部分在工作点附近线性化。这里有一个容易被误解的点预测模型并不需要全局精确。MPC关心的是未来N步内的趋势够不够准而不是它能不能复现系统的每一个瞬态。一个在主要工作点附近线性化得到的模型只要时域选得合理往往就能跑得很好。反过来如果你为了追求模型精度把阶跃响应测了二十遍取平均却在预测时域上拍脑袋选了个值效果可能反而更差。模型的来源大概三条路机理建模按物理定律写方程精度高但费时、系统辨识给激励信号、采数据、拟合参数工程上最常用、以及混合建模机理给出结构数据补参数。经验上用辨识方法配合合理的测试信号伪随机二进制序列PRBS、多级阶跃能在几小时内拿到一个可用的线性模型这是大多数项目的实际起点。2.2 滚动优化每一步只执行第一个控制量滚动优化的逻辑用一句话概括每一步都在重新规划整条路线但只走下一小段。导航软件就是这么干的——它会给你一条完整路径但你只按当前这一段走遇到堵车下次重新规划。这个设计解决的是一个非常实际的问题。如果优化一次就把整串控制量全部执行掉那就是开环最优控制模型一旦有偏差误差会不断累积最终发散。而每步重算等于把最新的实测状态作为新的起点模型误差不会持续积累系统具备自我修正的能力。代价函数通常包含三部分跟踪误差项让预测输出尽量贴近期望轨迹权重矩阵Q。控制量幅值项抑制过大的输入权重矩阵R。控制量增量项抑制输入的剧烈变化让执行器动作平滑权重矩阵S。这三项之间是在互相拉扯的。跟踪项希望动得越快越好控制量项和增量项希望动得越柔越好。调参的本质就是在这三者之间找平衡后面单独用一节讲。2.3 反馈校正为什么滚动本身就自带鲁棒性严格来说滚动优化已经把闭环反馈做进去了因为在每个采样时刻都用了实测状态作为初始条件。但工程上要真正实现无静差通常还需要额外两层第一层是状态估计。很多系统只能测到部分状态比如只能测位置测不到速度这就要用卡尔曼滤波或龙伯格观测器把缺失的状态估计出来。这里的模型精度直接影响估计质量观测器带宽一般要设得比控制器快两到五倍否则估计的滞后会拖累控制性能。第二层是扰动建模与目标计算。现实中的系统都有持续存在的扰动比如摩擦力、环境温度漂移、模型参数的小偏差。如果只用标称模型稳态时会留下一个固定偏差。解决办法是把扰动作为一个额外状态扩进模型里比如把输入扰动或输出扰动建模成常值或慢变状态用观测器在线估计它再在优化里减去它。这就是常说的无差MPCoffset-free MPC也是工业MPC的标准配置。所谓目标计算是指在每个采样时刻先解一个稳态优化算出在给定扰动下能真正达到的稳态工作点再把这个点作为动态优化的目标。这样即使由于约束限制无法达到原本的设定值控制器也能稳定停在可达的最优工作点上而不是一直拼命去够一个够不着的点。3. 把控制问题写成二次规划MPC的数学落地全流程理解了思路下一步是把它变成一个求解器能吃的形式。线性MPC最终会变成一个标准的二次规划QP问题这一步的推导值得自己动手推一遍因为后面调参和排错都依赖这个理解。3.1 从连续状态方程到离散预测方程设连续系统为线性时不变形式ẋ(t) A x(t) B u(t) y(t) C x(t)控制器在数字系统里运行必须先离散化。最常用的是零阶保持ZOH离散化它在采样周期内假设输入保持不变和实际执行器的行为最接近x(k1) Ad x(k) Bd u(k) Ad exp(A·Ts) Bd ∫(0→Ts) exp(A·τ) dτ · B当矩阵维度不大时可以直接用矩阵指数计算Python里用scipy.linalg.expm对增广矩阵做一次指数就同时得到Ad和Bd。简单地用前向欧拉Ad I A·Ts也常见但采样周期偏大时会引入明显的离散化误差尤其对振荡模态所以能用精确离散化就用精确的。把预测时域内的状态展开成矩阵形式X [x(k1); x(k2); ...; x(kN)] A_bar · x(k) B_bar · U其中A_bar是把Ad的各次幂纵向堆叠B_bar是一个分块下三角矩阵第i行第j列的分块是Ad的(i-j)次幂左乘Bd且j不超过i。这个结构的物理含义很清晰越靠后的状态受到越早的控制量影响越多通过多次状态转移而未来的控制量影响不到过去的状态。3.2 代价函数的三个惩罚项各自在管什么把三项目标写成二次型J Σ(预测输出 - 期望)ᵗ Q (预测输出 - 期望) Σ uᵗ R u Σ Δuᵗ S ΔuQ管跟踪速度。Q元素越大控制器越不能容忍偏差响应越激进副作用是容易超调和振荡。对于多输出系统Q通常是按输出分别给权重哪个量重要或者量纲小就给大一点。R管控制幅值。R增大控制器倾向于用更小的输入完成同样的事响应变慢但更省执行器。对于输入被约束得比较紧的系统R的存在能减少优化问题在约束边界附近反复横跳的概率。S管控制变化率。这是实际工程中最常被用来救场的项。很多系统的噪声不是来自测量而是来自控制量的高频抖动S增大能直接压住这种抖动。电机、阀门、舵机这类执行器对频繁往复动作很敏感S设得合理能显著延长寿命。注意这三项的数值大小只有相对意义。如果量纲不统一比如位置误差是米、输入是安培那Q和R的数值差好几个数量级也很正常此时调参就变成了盲猜。标准做法是先做归一化把Q、R、S各自设成允许误差、最大输入、最大变化率平方的倒数让三个权重在相近的数量级上之后再微调。3.3 约束怎么塞进QP这是MPC最核心的卖点约束是直接写进优化问题的而不是靠外部限幅。常见约束有三类约束类型数学表达典型场景输入幅值u_min ≤ u ≤ u_max阀门开度、电机电流上限输入变化率Δu_min ≤ Δu ≤ Δu_max执行器动作速度限制状态/输出x_min ≤ x ≤ x_max位置限位、温度安全上限把它们都写成关于决策变量U的线性不等式。输入幅值约束是U本身的范围变化率约束因为ΔU D·U - e·u_prevD是差分矩阵e是提取上一次输入的向量展开后边界要加上u_prev的贡献状态约束展开成x_min - A_bar·x(k) ≤ B_bar·U ≤ x_max - A_bar·x(k)也就是把已知的漂移项移到边界上。这三类约束叠起来就构成了QP的约束矩阵。整个问题可以写成标准二次规划形式min 0.5 · Uᵗ H U fᵗ U s.t. l ≤ A_con · U ≤ u其中H是对称半正定矩阵由预测矩阵和三个权重矩阵组合而成f是随当前状态、参考轨迹和上一次输入变化的线性项。这个结构非常适合用成熟的QP求解器不需要自己写优化算法。3.4 转化成QP标准形的推导与求解器选择把代价函数展开对U求导可以得到H 2 · (B_barᵗ Q_bar B_bar R_bar Dᵗ S_bar D) f 2 · (B_barᵗ Q_bar · (A_bar·x(k) - X_ref) - Dᵗ S_bar · e · u_prev)Q_bar、R_bar、S_bar是把单步权重沿时域块对角堆叠得到的X_ref是整个时域上的参考轨迹。这两个式子建议手推一遍理解f里状态项和上一次输入的来源后面排查为什么稳态有偏差时能省很多时间。求解器的选择上线性MPC常用这么几种求解器算法适用特点OSQPADMM稀疏大规模稀疏问题开源支持热启动qpOASES活动集中小规模稠密问题实时性好HPIPM内点法结构化专为MPC结构优化速度快acados实时迭代RTI非线性MPC实时部署Gurobi/CPLEX内点法/单纯形离线验证和基准对比选型的经验是如果问题规模在几十到几百个变量、矩阵稀疏OSQP基本够用如果要求单次求解在毫秒级、变量数又不多qpOASES或HPIPM更合适如果是非线性MPC上嵌入式平台acados配合CasADi是当前比较成熟的路线。还有一点容易被忽略——热启动。相邻两个采样时刻的QP问题非常接近用上一次的解作为初始点求解迭代次数能明显下降实时性提升很可观OSQP和qpOASES都支持。4. 手撸一版线性MPC双积分系统跟踪控制理论讲完了如果不动手写一遍很多细节永远体会不到。我用最经典的双积分系统做一个从零实现的例子被控对象是位置加加速度的二阶系统正好对应位置伺服的核心动态。4.1 被控对象与参数确定选双积分系统是因为它的物理含义直观状态是位置和速度输入是加速度而且位置和速度都能反过来对应到实际的位置环和速度环便于理解。系统矩阵如下A [[0, 1], [0, 0]] B [[0], [1]]参数选择上采样周期取0.1秒对应10Hz的控制器频率。这个值不是随手定的——位置环的带宽通常在1到3Hz量级按采样频率为带宽的5到10倍来估计10到30Hz比较合适取10Hz留出安全余量。预测时域取20步也就是覆盖2秒的未来窗口对调节时间在1秒左右的系统来说够用。约束上加速度限制在±2对应电机的最大加速度加速度增量限制在±0.5对应力矩变化率防止冲击位置约束松开只保留执行器约束。4.2 预测矩阵的构造代码先做离散化和预测矩阵构造import numpy as np import scipy.sparse as spa import osqp from scipy.linalg import expm # 连续模型 A np.array([[0.0, 1.0], [0.0, 0.0]]) B np.array([[0.0], [1.0]]) Ts 0.1 N 20 # 预测时域 控制时域 nx, nu 2, 1 # 零阶保持离散化 M np.zeros((nx nu, nx nu)) M[:nx, :nx] A M[:nx, nx:] B Md expm(M * Ts) Ad Md[:nx, :nx] Bd Md[:nx, nx:] # 预测矩阵 def build_prediction(Ad, Bd, N): nx, nu Bd.shape A_bar np.zeros((N * nx, nx)) B_bar np.zeros((N * nx, N * nu)) Apow np.eye(nx) for i in range(N): Apow Ad Apow # Ad^(i1) A_bar[i * nx:(i 1) * nx, :] Apow for j in range(i 1): B_bar[i * nx:(i 1) * nx, j * nu:(j 1) * nu] np.linalg.matrix_power(Ad, i - j) Bd return A_bar, B_bar A_bar, B_bar build_prediction(Ad, Bd, N)A_bar的第i个分块是Ad的(i1)次幂B_bar的分块结构体现的就是前面说的控制量通过多次状态转移影响后续状态。4.3 OSQP求解与滚动执行接着构造H矩阵、约束矩阵进入滚动循环# 权重已按量纲归一化 Q np.diag([10.0, 1.0]) # 位置权重 速度权重 R np.diag([0.1]) S np.diag([1.0]) # 增量权重抗抖关键 Q_bar np.kron(np.eye(N), Q) R_bar np.kron(np.eye(N), R) S_bar np.kron(np.eye(N), S) # 差分矩阵与提取向量 D np.eye(N) - np.eye(N, k-1) e1 np.zeros((N, 1)); e1[0, 0] 1.0 # 目标函数矩阵 H 2 * (B_bar.T Q_bar B_bar R_bar D.T S_bar D) H spa.csc_matrix((H H.T) / 2) # 强制对称避免数值问题 # 约束输入幅值、输入增量、状态 u_min, u_max -2.0, 2.0 du_min, du_max -0.5, 0.5 x_min np.array([-np.inf, -np.inf]) x_max np.array([np.inf, np.inf]) A_con spa.csc_matrix(np.vstack([np.eye(N), D, B_bar]))初始化求解器时先给一组初值然后在循环里用update刷新线性项和边界x np.array([0.0, 0.0]) # 初始状态 x_ref np.array([1.0, 0.0]) # 目标位置1速度0 u_prev 0.0 X_ref np.tile(x_ref, N) xr A_bar x - X_ref q 2 * (B_bar.T Q_bar xr - D.T S_bar e1 * u_prev).ravel() l np.concatenate([np.full(N, u_min), np.full(N, du_min) u_prev, np.tile(x_min, N) - A_bar x]) u np.concatenate([np.full(N, u_max), np.full(N, du_max) u_prev, np.tile(x_max, N) - A_bar x]) prob osqp.OSQP() prob.setup(PH, qq, AA_con, ll, uu, verboseFalse, warm_startTrue) log [] for k in range(150): xr A_bar x - X_ref q 2 * (B_bar.T Q_bar xr - D.T S_bar e1 * u_prev).ravel() l np.concatenate([np.full(N, u_min), np.full(N, du_min) u_prev, np.tile(x_min, N) - A_bar x]) u np.concatenate([np.full(N, u_max), np.full(N, du_max) u_prev, np.tile(x_max, N) - A_bar x]) prob.update(qq, ll, uu) res prob.solve() u0 float(res.x[0]) x Ad x Bd.ravel() * u0 # 真实对象此处与模型一致 u_prev u0 log.append([k * Ts, x[0], x[1], u0])这里的几个细节值得说xr那行的符号一定要确认清楚。代价里X - X_ref A_bar·x - X_ref B_bar·U所以线性项里带的状态部分是A_bar·x - X_ref正号。我见过有人抄成X_ref - A_bar·x结果控制器往反方向跑排查半天。x_min和x_max里的无穷要用np.infOSQP能正确处理无穷边界但它要求上下界数组里不能出现NaN如果状态维度多构造时很容易漏掉某个分量。H强制对称这一步别省。理论上B_barᵗ Q_bar B_bar是对称的但浮点累加会让它出现微小的不对称某些求解器对此非常敏感会报数值错误或者收敛极慢。4.4 跑出来之后怎么看曲线跑完150步你会看到位置从0平滑爬到1速度先升后降全程在2秒内到位而且加速度始终没有突破±2增量也没有突破±0.5。这正是MPC的典型表现——它会在快到目标的时候主动减速因为它看得见未来会超调于是提前收力。对比一下PID同样有±2的加速度限制PID要么超调后再拉回要么为了不超调把增益压得很低结果到位很慢。MPC的提前减速是它天然具备的能力不需要你去手工设计减速段。如果曲线出现明显高频抖动先把S调大如果到位太慢先加大Q里的位置权重如果超调明显别急着骂控制器先检查预测时域N是不是太小可能它看不到足够远的未来做不出正确的提前量。这个排查顺序我踩过好几次基本百试百灵。还有一点上面代码里真实对象用的也是同一个离散模型属于理想情况。真正验证时要换一个更细的模型或者干脆换成非线性模型比如加入库仑摩擦、加入死区看看控制器在这种模型失配下还能不能稳住。这一步才是检验MPC是不是真的能用的关键别跳过。5. 调参调到最后都是调权重和时域模型搭好、代码跑通之后真正的功夫在调参上。MPC的调参比PID多但逻辑其实很清晰关键是按顺序来不要同时动五六个参数。5.1 采样周期Ts的确定方法采样周期是所有参数的基准它定错了后面全是白费。两条原则第一条采样频率要远远高于系统的闭环带宽。经验值是采样频率至少是带宽的5到10倍。带宽不知道怎么办用开环调节时间估计如果系统的开环调节时间是T_s比如到63%所需时间那闭环带宽大致在1/T_s量级采样周期取T_s的1/20到1/10比较稳妥。第二条采样周期要尊重执行器的实际能力。如果执行器本身的响应时间就是50毫秒你把采样周期压到5毫秒没有任何意义反而让QP问题变得病态。同时采样周期越小预测时域需要的步数N就越多计算量线性甚至更快地增长实时性压力会传导到硬件上。我的经验是先按带宽定出一个值然后在此基础上上下一倍范围内试看哪个值下的控制效果和计算耗时平衡得最好。这一般要跑几轮仿真加一两次实机。5.2 预测时域与控制时域预测时域Np决定控制器看多远。太短控制器看不到远处的约束和动态表现接近一个高级PID超调和震荡控制不住太长计算量上升而且如果模型精度有限过长的预测反而会引入误差让控制器做出错误的提前动作。经验值是让Np乘以Ts覆盖系统开环调节时间的1.5到3倍。如果系统有明显的纯滞后比如管道的传输延迟时域最好能覆盖滞后时间加上主要动态时间否则控制器对滞后的处理会很吃力。控制时域Nc是可以独立设置的通常在预测时域里只对前Nc个控制量做优化后面的控制量要么保持不变要么按某种规律外推这叫移动块move blocking。这个小技巧非常实用它能在几乎不损失性能的前提下把决策变量从Np个降到Nc个从而显著减少QP规模。对大多数系统来说Nc取Np的10%到20%就够简单的二阶系统甚至取2到5个就够用。5.3 Q、R、S三个权重的手感这一步最依赖经验我把实际手感总结成几条Q决定有多急着追。加大Q响应变快但超过某个点之后开始超调、高频震荡。多输出系统里Q是对角阵不同输出分量可以给不同权重——你要保证哪个量精度高就给它更大的权重。要注意量纲位置误差和速度误差数值量级不同的话权重差异会很大。R决定愿意用多大的力气。R增大控制器更懒响应变慢但输入更小更省。R通常不需要大力调它主要用来避免输入长期贴着约束边界。S决定动作有多柔。这是现场最常用的调节旋钮。执行器噪音大、机械有异响、电机发热基本都是先加S。S调大到一定程度响应会明显变慢所以要在柔和快之间找点。我的经验顺序是先给S一个中等偏大的值把动作调顺再回头用Q提速度最后用R压边界附近的抖动。5.4 一个可复用的调参顺序把这套流程固化下来下次直接照着走先定Ts按带宽的5到10倍再和硬件算力、执行器能力对齐。再定Np覆盖调节时间的1.5到3倍有滞后的算上滞后。再定Nc从Np的10%起不够再加。归一化权重用允许误差、最大输入、最大变化率的倒数作为初值。先用S把动作调顺不看跟踪速度。再用Q提跟踪速度提一点看一点直到接近超调边界。最后用R压约束边界附近的抖动。换工况复验看参数在不同工作点是否仍成立不成立就考虑分段或自适应。这个顺序的好处是每一步都有明确的观察指标不会出现改了一个参数结果好几个现象一起变、不知道是哪个引起的这种情况。注意很多项目调参失败的真正原因是模型不准而不是权重没调好。如果你在权重的空间里怎么找都找不到一个好点先停下来回到模型用一段实测数据对比模型的预测输出和真值看偏差大不大。偏差大就去重新辨识或者重新标定别在权重上死磕。6. 工程落地最容易翻车的五个地方仿真跑通和现场跑稳之间隔着一条很深的沟。下面这五个问题几乎每个把MPC上到实际项目的人都会碰到至少两三个。6.1 约束不可行求解器直接报无解这是最常见也最致命的问题。如果边界约束太紧或者某个时刻的状态已经偏到了约束外QP会找不到同时满足所有约束的解求解器返回不可行状态控制器拿不到指令系统可能直接失控。比如位置已经超过限位了约束是位置不能超限位那这个问题确实无解。现实里这种情况完全可能发生比如被人为推了一把。解决办法是给约束加松弛变量soft constraint把硬约束改成x ≤ x_max ε其中ε ≥ 0并在代价里加一项大权重惩罚ρ·ε²。这样约束平时基本不被违反但真的越界时控制器还能给出合理的补救指令而不是直接罢工。工程上通常把执行器约束做成硬的物理限制确实存在不能违反把状态约束做成软的可以通过优化让它在极少数的过渡时刻被轻微突破。这个区分很关键。6.2 稳态误差和模型失配为什么老是差一点点前面提过只用标称模型和滚动优化在有持续扰动时会留下稳态偏差。现场常见的表现是位置总是差0.02米不动了或者温度总是低1.5度。排查顺序是先确认参考轨迹没问题再确认权重没有把积分作用压死然后重点看有没有做扰动建模。最直接的修法是加扰动状态把模型扩成x(k1) Ad x Bd u Bd·d其中d是待估计的常值输入扰动用卡尔曼滤波在线估计控制器里用估计值做前馈补偿。这一招基本能覆盖大多数稳态偏差问题。另一种情况是模型参数偏差引起的动态偏差比如实际系统比模型慢20%。这类问题通常表现为响应变缓、到位滞后但稳态还是能到位。解决思路是重新辨识或者引入在线参数自适应不过自适应的引入要小心它本身可能带来稳定性问题没有充分验证之前别乱加。6.3 计算时间超时为什么仿真很流畅实机就卡仿真里QP求解是瞬时的实机上如果单次求解时间超过了采样周期就会出现丢步控制周期变得不稳定性能直线下降。排查的第一步是先量出实际的求解时间分布不是平均值而是最大值和95分位值。有些QP问题大部分时刻几十微秒就解完偶尔遇到约束激活切换时会跳到几毫秒这个尖峰就足以破坏实时性。优化手段有几种按投入产出比排序热启动几乎零成本收益明显、移动块减少决策变量、缩短Nc、用结构化求解器如HPIPM、代码生成把求解器编译成C代码去掉解释开销、显式MPC离线算好控制律。通常前两个就够解决问题实在不行再往后走。6.4 执行器死区和速率限制模型没建模的部分MPC的模型里如果没写执行器的死区、回差、真实的速率限制控制器会给出一些执行器根本执行不了的指令导致实际响应和预测严重不符控制器越调越急最后震荡。最简单的办法是把真实特性写进约束。死区可以做逆补偿在控制器输出和实际输入之间加一层映射速率限制直接作为Δu约束回差如果不是很严重可以做成保守一点的有效范围。这些都属于把控制器的假设和现实对齐的工作看着琐碎但漏掉哪一项都会在现场炸出来。6.5 状态不可测估计器跟不上就全白搭很多系统只能测到部分状态。位置能测速度测不到温度能测但升温度测不到。这时候必须上观测器而观测器的性能直接决定MPC能跑多好。实践中的坑主要有两个。一是观测器带宽不够估计值总是滞后于真实值MPC用滞后的状态做预测提前量就做错了。一般观测器带宽要设为控制器带宽的两到五倍。二是观测器和控制器的采样时刻不匹配比如观测器用10Hz而控制器用20Hz需要做速率转换处理不好会引入额外的相位滞后。统一频率、把观测器放在控制器之前跑是最省心的做法。7. 非线性MPC、显式MPC与学习型MPC什么时候该换武器线性MPC能覆盖很大一部分场景但有些系统天生非线性或者对算力要求极端苛刻这时候就要考虑进阶方案。这一节讲清楚每种方案的代价和适用边界省得选型时被名字唬住。7.1 NMPC的实际代价非线性MPCNMPC直接用非线性模型做预测优化问题变成非线性规划。它的好处很直接——模型更真实能处理大范围工况和非线性约束。但代价也很直接每步要解一个NLP计算量通常是线性QP的十倍到百倍而且凸性没有保证可能陷入局部最优。要在实时系统上跑NMPC通常用实时迭代RTI策略每个采样周期只做一到几次牛顿迭代不做完整收敛靠滚动优化来修正。配合CasADi做自动微分和acados做代码生成小规模问题状态10维以内、时域20步以内能跑到毫秒级。超过这个规模要么换硬件要么考虑把非线性模型在工作点附近线性化退回到线性MPC加增益调度gain scheduling的路线。7.2 显式MPC的适用边界显式MPC是另一个思路把在线优化搬到离线。因为线性MPC的最优解是状态空间上的分段仿射函数可以离线把它算出来存成一个查找表在线时只做查表和简单的仿射计算速度能到微秒级。它的限制也很明确——查找表的大小随状态维数、约束数指数增长状态超过三到五维基本就存不下了嵌入式MCU的内存撑不住。所以显式MPC适合状态少、约束少、但采样率要求极高的场景比如电力电子变换器、小型电机的电流环。它对模型的精度要求极高因为离线算完就没有在线修正的余地了。7.3 学习增强MPC机遇和风险并存最近几年比较热的方向是用数据驱动的方法补足或替代模型比如用高斯过程、神经网络、Koopman算子来学系统动态再嵌入MPC框架。它的价值在于对那些机理难建、但数据丰富的系统能绕开繁琐的辨识。但它的问题也很实际。学习模型只在训练数据覆盖的范围内可信一旦系统跑出这个范围模型给出的预测可能就是完全错的而MPC会非常相信这个错误的预测做出危险的动作。另一个问题是安全验证。MPC的价值很大一部分来自可预测性和安全性而神经网络黑箱模型让这种保证变得困难。比较务实的用法是混合方案机理模型保底学习模型作为补偿项并且给补偿量设上限一旦超出范围就退回纯机理模型。这样既能享受数据带来的精度提升又保留了可预期的行为边界。7.4 什么时候老老实实用PID和LQR我一直觉得能不用MPC就不用MPC。听起来有点反直觉但这是我在项目里的真实体会。MPC引入的复杂度是实打实的需要模型、需要辨识、需要算力、需要调更多的参数、需要处理求解失败的兜底逻辑。这些成本要靠它带来的收益来覆盖如果收益不明显就是亏本买卖。具体来说以下几种情况我会选择不用MPC单回路、动态平缓、没有硬约束的流量、压力、液位控制PID足够了而且现场维护人员也熟悉出了故障能快速处理。无限时域、无约束、线性二次型的场景LQR的解析解更优雅、更省算力实在不行加个积分器做成LQI。采样频率极高几十kHz以上的电流环、开关电源环路线性MPC的求解时间都嫌长更别说NMPC这种场景用PI加前馈是主流做法。项目压根拿不到像样的模型又没有预算和时间去做辨识那先把PID调好把数据攒起来等有条件了再考虑MPC。反过来当系统有明确的硬约束、多个变量强耦合、需要前瞻设定值变化、或者对能耗和动作平滑度有严格要求时MPC的收益就非常明显了。储能系统的充放电调度、楼宇暖通的能耗优化、车辆轨迹跟踪、机械臂的力矩受限控制这些都是MPC真正发光的地方。电力系统里的频率调节和自动发电控制也是因为约束多、耦合强、还要考虑经济性才大量采用模型预测控制的框架。最后分享一个我自己踩过的体会。刚开始接触MPC时我总想着把模型建得越精确越好花了大量时间在机理建模上。后来发现在大多数工程场景里一个通过一两小时阶跃测试辨识出来的线性模型配合合理的时域和权重就能满足性能要求而那份完美模型因为没时间调参、没时间处理实时性反而迟迟上不了线。先把一个能跑的基础版本落地在真实运行数据上迭代模型和参数比在仿真里追求完美要靠谱得多。MPC真正难的地方从来不是那个优化问题而是怎么让它在有噪声、有延迟、有约束、有故障的现实世界里稳稳地活下去。
返回列表