ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LTV MPC深度解析:从线性化建模到轨迹跟踪控制实践

LTV MPC深度解析:从线性化建模到轨迹跟踪控制实践

做运动控制这几年,MPC(模型预测控制)是同行问我最多的话题之一。早些年一提 MPC,大家都觉得是论文里的东西,落地难、算不动;现在反过来了,无人机、机器人、精密运动平台、车辆运动控制,几乎到处都是 MPC 的身影。尤其是 LTV MPC,也就是线性时变模型预测控制,在轨迹跟踪这类场景里几乎成了默认解法。这篇文章我就把一套完整的 LTV MPC 设计链路讲清楚:预测模型怎么搭、线性化怎么做、代价函数和约束怎么调、代码怎么写、遇到不可行和抖振怎么排查。适合已经有 PID、LQR 基础,想往预测控制方向深一步的工程师和研究生。

1. 为什么控制器选型最终绕到 MPC 和 LTV MPC

1.1 约束与预测:MPC 相比 PID/LQR 的本质区别

传统的 PID 和 LQR 不是不好用,而是存在两个结构性的短板。

第一个短板是不能显式处理约束。电机有力矩上限,转向机构有角度极限,执行器有速率限制,这些物理约束无时无刻不在。PID 和 LQR 的应对方式是"不问未来,事后限幅"——计算出一个指令,如果超限就剪掉。剪掉之后,系统实际执行的变量和控制器认为的变量不一致,就会产生积分饱和、抗饱和逻辑、控制性能退化等一系列连锁问题。我调试过一个移动机器人,用 LQR 做轨迹跟踪,速度指令超过电机上限后被限幅,机器人会在弯道处明显往外飘,调增益效果非常有限,因为问题压根不在增益上。

第二个短板是没有前瞻性。反馈控制只根据当前误差做决策,看不到未来两秒路况的变化。同样是过弯,一个能看到前方曲率增大的控制器,会在进弯前就减速;而纯反馈的 LQR 只有在真正进入弯道、误差显现之后才开始反应,必然慢半拍。

MPC 的核心思想就是在每个采样时刻,拿一个预测模型,把未来一段时间(预测时域 N)的行为算出来,在满足约束的前提下,优化出一段最优控制序列,然后只执行第一步,下一步重新来。这就是常说的滚动优化、滚动时域。因为这个优化问题里有预测、有约束、有代价函数,所以 MPC 既能提前看到未来,又能把约束当作一等公民来处理——约束不是事后剪的,而是在求解时就保证了。

1.2 线性时变模型:非线性轨迹跟踪的现实答案

线性 MPC 的问题也很直接:它只有一组固定的 A、B 矩阵,必须在某个工作点附近线性化。如果你跟踪的路径比较平缓、速度变化不大,一个工作点撑全场是可以的。可一旦轨迹曲率变化明显,或者速度范围很宽,单一线性模型就无法代表系统行为。

举一个很直观的例子。用运动学模型描述一个差速机器人:

ẋ = v·cosθ,ẏ = v·sinθ,θ̇ = ω

其中 v 是线速度,ω 是角速度,θ 是航向角。这个系统的输入矩阵 B 里含有 cosθ 和 sinθ。机器人在不同朝向时,同样的 v 作用在 x 方向和 y 方向的效果完全不同。你用一个固定的 B 矩阵去描述一个正在掉头转向的机器人,预测误差会很大,后面约束都成了笑话。

LTV MPC 的做法是:在每一个采样时刻,沿着参考轨迹或者当前的运行轨迹,逐点做线性化,得到一组随时间/沿轨迹变化的矩阵序列 A(k)、B(k),用它们搭一个时变预测模型。虽然每一步解出来的仍是一个凸的二次规划(QP),但模型本身抓住了系统在不同运行点上的非线性特征。这就是"线性时变"四个字的含义:问题是线性的,模型是变化的。

为什么不直接上非线性 MPC(NMPC)?因为在工程落地上,NMPC 是要在每个时刻解一个非线性规划(NLP),非凸、求解时间不可控、需要专门的求解器和初值策略,调试成本高好几个量级。而 LTV MPC 仍然是一个 QP,有成熟的求解器、有可靠的热启动策略、求解时间可以压得很稳。所以对绝大多数轨迹跟踪应用,LTV MPC 是性价比最高的中间路线:非线性信息够用,凸优化的便利也够用。

2. LTV MPC 的理论地基:预测模型、线性化与误差模型

2.1 从非线性运动学模型到逐点线性化

先定一个问题框架。被控对象是连续非线性系统:

ẋ = f(x, u)

给定一条参考轨迹,包含状态轨迹 x_ref(k) 和输入轨迹 u_ref(k),k = 0, 1, …, N。在轨迹上的每个点,做一阶泰勒展开:

f(x, u) ≈ f(x_ref(k), u_ref(k)) + A_c(k)(x − x_ref(k)) + B_c(k)(u − u_ref(k))

其中 A_c(k) 和 B_c(k) 是 f 对状态和输入在参考点处的雅可比矩阵。对差速机器人而言:

A_c(k) = [ ∂f/∂x ] = [0, 0, −v_r·sinθ_r] [0, 0, v_r·cosθ_r] [0, 0, 0]

B_c(k) = [ ∂f/∂u ] = [cosθ_r, 0] [sinθ_r, 0] [0, 1]

这里的下标 r 表示第 k 个参考点上的值。可以看到,B 矩阵确实随 θ_r 在变化,这正是 LTV 模型比固定线性模型更贴近真实系统的地方。

有了连续时间的雅可比矩阵之后要做离散化。最朴素的离散化是前向欧拉:A = I + A_c·dt,B = B_c·dt。在采样周期相对于系统动态足够小的时候,欧拉法的精度够用。如果采样周期偏大、系统动态偏快,建议用更精确的离散化方法,比如对增广矩阵做矩阵指数运算。但实际项目里,我通常先用欧拉法搭起框架,再根据仿真和实测精度决定是否升级。

2.2 误差状态与参考残差项的处理

定义误差状态 e(k) = x(k) − x_ref(k),输入偏差 δu(k) = u(k) − u_ref(k)。那么预测方程可以写成:

e(k+1) ≈ A(k)·e(k) + B(k)·δu(k) + d(k)

这里有个很容易被忽略的项 d(k),它是参考轨迹和模型之间的残差:

d(k) = f(x_ref(k), u_ref(k)) − x_ref(k+1)

如果参考轨迹是用同一个离散模型生成的,d(k) 理论上为零,可以省略。很多教材例子都默认这种情况。但实际工程里,参考轨迹往往来自路径规划层,它可能只考虑了运动学或者纯几何关系,并没有严格满足你用于控制的动力学模型。这种情况下如果不算 d(k),预测就会出现系统性偏差,体现为跟踪的稳态误差。

我踩过这个坑。有一次把规划层给的轨迹直接接到 LTV MPC 上,仿真结果里机器人和参考轨迹之间总有一个固定偏差,调大 Q 权重也没用。后来检查发现,参考轨迹那些点上,用模型推算出来的下一时刻状态和给的参考状态对不上,差了常数项。把 d(k) 补进预测方程之后,偏差立刻消失。所以判断是否要 d(k) 的标准很简单:参考轨迹是否由控制模型自身生成。是,则不需要;不是,则必须补。

使用误差状态而不是绝对状态还有一个实际好处:数值尺度更合理。如果参考状态本身就很大,比如车辆位置坐标上千,而误差只有零点几,用绝对状态组装代价函数时,数值上很容易出现病态。用误差状态,代价函数里每一项都围绕"偏差"展开,尺度自然得多。

2.3 增量式写法:隐式积分与抗模型失配

上面的表达式用 δu 作为决策变量,代价函数惩罚的是输入相对参考的偏差。这种写法在模型完全匹配时很好用,但一旦存在未建模动态、参数漂移或者外部扰动,系统容易出现静态偏差。原因很简单:代价函数里没有对误差的积分作用,MPC 本质上是一个带约束的"比例+前馈"控制器。

一个非常实用的改进是把决策变量换成控制增量 Δu(k) = u(k) − u(k−1),并把上一拍的实际输入 u(k−1) 扩充进状态。这样预测模型变成增广形式:

[x(k+1); u(k)] = [A(k), B(k); 0, I] · [x(k); u(k−1)] + [B(k); I] · Δu(k)

对 Δu 加代价,等价于给系统加入了隐式的积分作用。模型失配时,误差会持续反映在 Δu 上,推动实际输入不断修正,直到误差被消除。代价是状态维度增加、QP 规模略大,但对 3 到 6 维状态的小系统来说,这点开销可以忽略。

我个人的习惯是:做轨迹跟踪的 LTV MPC,默认就用增量式写法。它带来的鲁棒性提升非常明显,尤其是当你没有做扰动观测器、没有在线参数估计的时候,增量式是最便宜的抗静差手段。

3. 代价函数与约束的工程化设计

3.1 权重矩阵 Q、R、终端权的实际语义

典型的跟踪代价函数长这样:

J = Σ e(k)ᵀ·Q·e(k) + Σ δu(k)ᵀ·R·δu(k) + e(N)ᵀ·Q_N·e(N)

Q 是状态误差权重,R 是输入权重,Q_N 是终端代价。对角线权重 Q_ii 的含义可以理解为"这个方向允许多大误差"的倒数。一个很实用的起步套路是归一化:如果你希望位置误差控制在 0.1 米以内,那么 Q 对应位置的元素可以取 1/(0.1)² = 100;希望航向误差控制在 0.1 弧度以内,则航向权重取 1/(0.1)² = 100。这样设置之后,各项的代价量级大致均衡,后续微调也更有手感。

R 的语义类似:如果线速度允许的变化幅度是 0.5 m/s,R 对应元素取 1/(0.5)² = 4。注意 R 不是越小越好。R 太小会让控制动作过于激进,尤其在有测量噪声和模型误差的时候,会表现为控制量毛刺、抖动。R 太大则响应迟钝、跟踪滞后。一个好的起步点是用上面的归一化方法得到初始值,然后仿真观察,再按 2 倍、5 倍的步长调整 Q 和 R 的相对比例。

有一个重要但容易被忽略的结论:Q、R 同时乘以同一个常数,最优解路径不变(在无约束或约束不激活的意义下),只有相对比例才真正影响控制性能。但绝对数值会影响求解器的数值条件和收敛速度,所以不建议权重设得太离谱,比如上千万量级。

终端代价 Q_N 的作用是缓解有限时域带来的"近视"问题。没有终端代价时,预测时域末尾的状态误差不受惩罚,控制器会在最后几步出现"不管后果"的激进动作。实际调参时,如果发现末端轨迹有明显的回头趋势或者最后几步约束总是贴着边界,多半就是终端代价没设好。工程上两种做法:一是把 Q_N 取为 Q 的 5 到 10 倍,简单有效;二是求解参考工作点处的离散黎卡提方程,把 Q_N 设为无穷时域 LQR 的代价矩阵,理论味道更足,实现也不复杂。

3.2 约束的加法与软约束

约束分为几类。输入约束最常见,比如电机转速上下限、转向角度上下限,一般直接写成硬约束:u_min ≤ u ≤ u_max。状态约束,比如位置不能越过安全边界、速度不能超过上限,也能加,但要小心一个问题:状态约束必须和参考轨迹、模型能力一致。如果参考轨迹本身就要求机器人走出约束范围,那这个 QP 永远不可行。

对于状态约束,我强烈建议用软约束加惩罚的写法,而不是硬约束。做法是引入松弛变量 s,把状态约束写成:

g(e) ≤ s + ε,其中 s ≥ 0,并在代价函数里加上 M·‖s‖

这样一来,正常情况下约束严格满足,极端情况下系统可以牺牲一点约束来换取可行性,避免整个控制器崩溃。罚系数 M 要取一个比较大的值,比如比状态权重高两个数量级,但也不能无限大,否则数值上又回到硬约束的状态。

另一个经常出现在代码评审里的问题是:用 δu 作为决策变量时,约束必须加在实际输入 u = u_ref + δu 上,而不是直接对 δu 限幅。我有一次看到别人写的代码,设置了 −u_max ≤ δu ≤ u_max,以为在限制输入,但参考输入 u_ref 本身就有 1.0,加上 δu 之后实际输入到了 2.0,完全超限。约束写错位置,后面的性能分析都是白做。

3.3 采样周期、预测时域、控制时域的配套选型

这三个参数是配套的,不能单独拍脑袋。采样周期 dt 原则上要能分辨系统的主要动态,经验上取开环主导时间常数的 1/10 到 1/20。对移动机器人,20ms 到 100ms 都是常用区间;对更慢的过程对象,比如温度控制、楼宇系统,可以到秒级。

预测时域 N 乘以 dt 要覆盖从当前到"系统基本稳定"的时间跨度。如果期望闭环稳定时间在 2 秒左右,dt 取 0.1 秒,那么 N 至少取 20。很多人问是不是 N 越大越好。理论上不完全是。N 太大,计算量增加,更重要的是远处的线性化模型本身已经不可信了,LTV 模型的优势到远处会被线性化误差侵蚀。所以 N 应当覆盖目标时间,但没必要盲目翻倍。我在工程里通常先按 1 到 2 倍的期望稳定时间估算 N,再在仿真里对比 N+10 看性能是否明显提升,如果没有就维持原值。

控制时域 M 是另一个自由度。它表示从第 M 步开始,输入保持恒定。缩短 M 可以直接减少决策变量数量,显著降低计算量,代价是控制自由度减少。对嵌入式平台,M 取 N 的 1/3 到 1/2 是常见的折中。但 LTV MPC 因为在时域内模型本身在变,缩短 M 相当于假设后半段输入不变,这个假设在模型变化明显时略粗暴。所以如果计算预算允许,我倾向于不缩短 M,而是先把 N 缩到够用即可。

参数取值偏小的影响取值偏大的影响起步参考值
采样周期 dt模型失真、约束信息丢失计算开销大主导时间常数 / 10
预测时域 N前瞻不足、易失稳计算量大、远端模型失真覆盖 1~2 倍稳定时间
控制时域 M控制自由度不足计算量大N 的 1/3 ~ 1/2
权重 R控制猛、易抖响应慢、跟踪滞后按输入允许量归一化

4. LTV MPC 从公式到代码的完整实现

4.1 单步优化与滚动时域的算法主循环

不管用什么语言、什么求解器,LTV MPC 的主循环结构都是一样的。每个采样周期做这几件事:

  1. 拿到当前状态估计值 x_k(状态估计器输出的,不是理想状态)。
  2. 截取参考轨迹窗口:x_ref[0..N]、u_ref[0..N−1]。
  3. 对窗口内每个参考点做线性化和离散化,得到 A(k)、B(k),必要时计算残差 d(k)。
  4. 组装 QP 问题:决策变量是误差状态序列和控制偏差序列,代价函数包含 Q、R、Q_N,约束包含模型方程和输入/状态约束。
  5. 调用求解器求解。6. 取出最优序列的第一个控制偏差 δu(0),计算实际输入并下发执行。
  6. 等待下一个采样时刻,把预测窗口整体向前平移一步,重复上述过程。

第 7 步就是"滚动时域"的含义。每次只执行第一步,但每步都重新优化。这个反馈机制天然能抵抗一部分模型失配和扰动,因为误差每步都会被"看到"并修正。

4.2 Python + OSQP 的参考实现

下面给一套完整可跑的参考实现,用 Python 的 cvxpy 建模、OSQP 求解。对象是差速机器人,参考轨迹是圆形匀速运动。

import numpy as np import cvxpy as cp # 采样周期与预测时域 dt = 0.1 N = 25 nx, nu = 3, 2 # 状态[x,y,theta],输入[v,omega] # 权重矩阵 Q = np.diag([10.0, 10.0, 0.5]) R = np.diag([0.1, 0.1]) QN = np.diag([50.0, 50.0, 2.0]) # 输入约束 u_min = np.array([-0.5, -1.5]) u_max = np.array([1.5, 1.5]) def discrete_ltv(x_eq, u_eq): """在参考点 (x_eq, u_eq) 处线性化并欧拉离散化""" th = x_eq[2] v = u_eq[0] A = np.array([ [1.0, 0.0, -v * np.sin(th) * dt], [0.0, 1.0, v * np.cos(th) * dt], [0.0, 0.0, 1.0] ]) B = np.array([ [np.cos(th) * dt, 0.0], [np.sin(th) * dt, 0.0], [0.0, dt] ]) return A, B def nonlinear_step(x, u): """真实被控对象(仿真用),前向欧拉""" return x + np.array([ u[0] * np.cos(x[2]), u[0] * np.sin(x[2]), u[1] ]) * dt def mpc_step(x_current, x_ref, u_ref): """求解单步 LTV MPC,返回第一个控制偏差 du""" e = cp.Variable((nx, N + 1)) du = cp.Variable((nu, N)) cost = 0.0 cons = [e[:, 0] == x_current - x_ref[0]] for k in range(N): A, B = discrete_ltv(x_ref[k], u_ref[k]) # 预测模型:误差状态递推 cons += [e[:, k + 1] == A @ e[:, k] + B @ du[:, k]] # 实际输入必须落在约束内 u_actual = u_ref[k] + du[:, k] cons += [u_min <= u_actual, u_actual <= u_max] # 代价 cost += cp.quad_form(e[:, k], Q) + cp.quad_form(du[:, k], R) # 终端代价 cost += cp.quad_form(e[:, N], QN) prob = cp.Problem(cp.Minimize(cost), cons) prob.solve(solver=cp.OSQP, warm_start=True) if prob.status not in (cp.OPTIMAL, cp.OPTIMAL_INACCURATE): print("solver status:", prob.status) return None return du[:, 0].value # 参考轨迹:半径2.0的圆,角速度0.6 rad/s r, w = 2.0, 0.6 T_total = 20.0 ts = np.arange(0, T_total, dt) x_ref = np.array([[r * np.cos(w * t), r * np.sin(w * t), w * t + np.pi / 2] for t in ts]) u_ref = np.array([[w * r, w] for t in ts]) # 线速度=r*w,角速度=w # 初始状态故意给一个偏移 x = np.array([2.5, 0.0, 0.0]) for i in range(len(ts) - N - 1): xr = x_ref[i:i + N + 1] ur = u_ref[i:i + N] du = mpc_step(x, xr, ur) if du is None: print("第", i, "步求解失败") break u = ur[0] + du x = nonlinear_step(x, u) # 这里记录仿真轨迹,画图对比即可

这套代码的关键点有三个。第一,线性化和离散化是在每个参考点上单独做的,所以 A、B 序列里包含轨迹的形状信息。第二,约束加在总是实际输入上,不是加在 du 上。第三,OSQP 开了 warm_start,连续滚动求解时,上一个时刻的解作为下一个时刻的初值,收敛速度明显加快。

如果参考轨迹不是由模型自身生成的,记得在预测方程里补残差项,把上面代码里的递推约束改成:

d = nonlinear_step(x_ref[k], u_ref[k]) - x_ref[k + 1] cons += [e[:, k + 1] == A @ e[:, k] + B @ du[:, k] + d]

这就是前面说的 d(k) 项。补上它,跟踪精度才守得住。

4.3 求解器为什么选 OSQP,以及向嵌入式移植的路径

在原型验证阶段我推荐 OSQP,原因是它在小规模凸 QP 上收敛快、支持热启动,而且接口简单。cvxpy 里的 OSQP 默认参数对绝大多数 MPC 问题够用。如果遇到收敛慢,可以调整容差参数 eps_abs、eps_rel 到 1e-5 左右,或者限制最大迭代次数,换取稳定求解时间。

另一个选择是 qpOASES,它属于 active-set 方法,对中小规模密集 QP 很有优势,尤其在 QP 维数不超过几百的时候。ECOS、SCS 是内点法体系,鲁棒但偏慢,一般用于离线验证而不是在线滚动。

到嵌入式落地阶段,我会把 cvxpy 拿走,直接用求解器原生接口组装 QP。思路是:把预测时域固定下来,矩阵维度全部静态分配,A、B 序列每步只更新数值、不改变稀疏结构。OSQP 的原生接口支持这种"只更新数据、不重建立问题"的模式,速度能比 cvxpy 包装层快一个量级。如果目标平台资源更紧,还可以采用 condensing 技术,把状态变量消掉,只保留控制变量,把 QP 规模从 (N+1)nx + Nnu 压缩到 N*nu。这部分内容展开又是一篇长文,但对小状态系统,效果立竿见影。

5. 实操中的常见问题与排查细节

5.1 求解器报不可行

不可行是所有 MPC 落地时最先遇到的坎。问题本身不复杂:你给出的约束集合和模型预测在某个采样时刻没有交集。常见的触发原因有这么几类。

第一,参考轨迹本身不满足约束。比如输入约束是 −1.5 到 1.5,而参考轨迹要求的角速度是 2.0,那无论怎么优化,控制序列都会碰壁。检查方法是把参考输入轨迹拉出来,逐点对比约束上下限,一眼就能看出问题。

第二,硬的状态约束过于激进。比如要求机器人未来 2 秒内位置误差必须落在 0.01 米以内,而当前误差是 0.5 米,线性化模型又不可能瞬间把误差拉回来,于是不可行。解决办法是软约束,或者把约束分段放松。

第三,终端等式约束。有些人喜欢加 e(N) = 0,这在理论上能保证稳定,但在 LTV 框架下经常因为线性化误差导致不可行。我建议改用终端不等式约束或者干脆只有终端代价。

排查不可行问题的顺序我一般这样走:先去状态约束试跑,只保留输入约束;再检查参考输入是否在约束范围内;然后取消终端约束看能否可行;最后如果还不行,检查数值问题,比如权重矩阵尺度悬殊、状态变量单位不一致。绝大多数不可行问题在第三、第四步就能定位。

5.2 跟踪偏差、末端抖动与控制量毛刺

跟踪出现稳态偏差,先不要急着调大 Q。按我前面的经验,先确认两件事:参考轨迹到底满不满足模型,残差项 d(k) 有没有补上;模型失配是不是系统性原因。如果这两个都不是,再考虑把控制系统改成增量式写法,或者加一个简单的状态观测器来估计扰动项。

末端抖动表现在预测时域后段:参考轨迹已经接近目标,但控制序列在后半段剧烈摆动。这通常说明终端代价权重太小,或者 R 权重过小导致控制增量惩罚不足。我会先加大 Q_N,观察曲线是否平滑下来;如果还抖,再增加 R 或对 Δu 加惩罚。

控制量毛刺是另一个常见现象。注意区分真抖和数值噪声:真抖通常是权重配比的问题,R 太小;数值噪声则表现为相邻两个采样周期的控制指令毫无规律地跳动,这时优先检查状态估计是否过于敏感,比如直接用带噪声的测量值而没有滤波。解决方法:对测量做滤波,或者把代价函数改成对控制增量 Δu 惩罚,都会很有效。

5.3 实时性卡顿与计算瓶颈

实时性不够,先做量化:把每次求解时间打印出来,看是均值高还是尾部有个别尖峰。普遍问题出现在几个地方。

一是求解器容差设得太严。对于 MPC,控制精度并不需要解到机器精度。把 eps_abs、eps_rel 放到 1e-4 到 1e-5,大多数情况下性能损失完全可以忽略,但求解速度能提升好几倍。

二是没有热启动。OSQP 的 warm_start 用上一拍的解作为初值,在新问题与旧问题相似度高时,迭代次数会大幅下降。这在滚动时域里几乎是免费的性能红利,务必开启。

三是预测时域过长。这个不用多解释,决策变量数量直接决定 QP 规模。如果缩 N 之后性能可以接受,那就缩。

四是 QP 建模层的开销。cvxpy 这类库每次都会重建问题结构,对实时性要求高的场合是瓶颈。落到原生接口后,单步耗时往往能降低一个量级。

5.4 问题症状速查表

现象常见原因排查建议
求解器返回不可行参考输入超约束范围 / 硬状态约束过紧 / 终端约束太强先去掉状态约束和终端约束,逐步加回定位冲突源
跟踪有稳态偏差参考不满足模型、d(k) 缺失、模型失配补残差项,改用增量式写法
控制量高频抖动R 过小、噪声敏感、对 u 而非 Δu 惩罚增大 R、滤波测量值、惩罚 Δu
预测末端动作怪异终端代价不足增大 Q_N 或改为 LQR 终端矩阵
求解时间波动大容差过严、缺热启动放宽容差、开启 warm_start
某个方向响应慢权重相对比例失衡按允许误差归一化重新设 Q、R

5.5 关于线性化误差的一个提醒

最后单独说一句线性化误差。LTV MPC 的预测模型每步都是近似的,误差在预测时域内会累积。预测时域越长,误差越大。这就是为什么我前面反复强调"别过度拉长 N"。在实际项目中,我习惯在仿真里故意加 10% 到 20% 的参数扰动,跑多组随机场景,看控制器是否还能稳定。不要只盯着一条完美曲线就拍板,MPC 的鲁棒性测试比精确性测试重要得多。

6. 我的调参经验次序与后续扩展

6.1 从 LQR 起步的调参流程

我把调参这件事拆成四步,每一步都验证后再进下一步。

第一步,先不管约束,把 MPC 当成一个有限时域的 LQR 来调。设置一个足够大的 N,关掉约束,直接用 LQR 的整定思路确定 Q、R 的相对比例,观察闭环响应快慢和阻尼。

第二步,把输入约束加上。这时重点检查控制器在接近约束边界时是否平滑,是不是总在边界上摩挲。如果频繁撞边,说明 R 偏小,控制动作和约束产生了持续冲突。

第三步,加上状态约束和软约束。这个阶段重点验证可行性,尤其是在各种初始状态下,求解器都应该返回最优解而不是不可行。

第四步,处理实时性。仿真没问题之后,不断压缩求解时间,观察性能折损,找到求解时间和控制性能之间的平衡点。

这套流程我用了很多次,基本没有翻过车。直接一上来就把全套约束、权重、时域一次性配好,出问题的时候根本分不清是哪一环节引起的。

6.2 从 LTV MPC 再往上的路线

LTV MPC 做扎实之后,往上走的路径很清晰。如果模型非线性更强,线性化误差已经不可接受,下一步是 NMPC,但要做好求解器和初值管理的准备。如果问题主要出在模型失配和参数漂移,可以做自适应 MPC,把模型参数在线辨识和 MPC 控制器耦合起来。如果想让 MPC 在极端情况下也能体面降级,可以在代价函数里加入安全和性能的多目标处理,这也是目前业界落地关注的方向。

我个人最推荐的路线是先把 LTV MPC 的每个环节吃透,尤其是线性化点选取和残差处理这两处细节。因为这两处是 LTV MPC 区别于教科书线性 MPC 的核心,也是实际项目中最容易出问题的地方。把它们弄明白之后,无论是往 NMPC 走,还是往自适应方向走,底子都是通的。做控制这么多年,我最深的体会就是:算法本身不难,难的是知道每一步为什么这么做。LTV MPC 是一个不吃亏的选择,值得花时间。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进