☰
NMPC非线性模型预测控制:从原理到避障实战与调参
2026/10/22 7:55:47 网站建设 项目流程

做控制或者机器人的朋友,大概率都遇到过这种尴尬:PID 调得顺顺当当的工况,一旦目标轨迹突然拉开幅度,或者系统本身是强耦合的非线性对象,那套线性近似就立刻开始“撒谎”;好不容易换到线性 MPC,又发现它在工作点附近很好用,但稍微偏离一点,预测轨迹就开始和真实轨迹分道扬镳。于是你就绕不开一个词:NMPC 非线性模型预测控制。

这篇文章我想把 NMPC 非线性模型预测控制讲透一点,重点放在三件事:它和线性 MPC 的本质区别是什么、每个采样周期里优化器到底在算什么、以及一份能跑通的最小代码和配套调参经验。适合刚接触 NMPC 的开发者,也适合已经跑过简单仿真但总在实时性和参数上调不明白的人。我会按照自己做项目的思路来讲,不堆公式,但必要的数学骨架会放在最显眼的位置。

1. 从线性MPC到NMPC:非线性这词到底改变了什么

1.1 线性的舒适区在哪里,又在哪里失效

先统一一下共识。模型预测控制的核心动作是:每个控制周期,基于当前测量到的状态,在预测时域内求解一个有限时域最优控制问题,得到未来 N 步的最优控制序列,然后只执行第一步,下一个周期重新测量、重新求解。这个“滚动优化”的框架,线性 MPC 和非线性 MPC 是完全一样的。差别只在一个地方:预测用的动力学 (x_{k+1}=f(x_k,u_k)) 是线性的,还是非线性的。

线性 MPC 通常把系统建模成状态空间方程 (x_{k+1}=Ax_k+Bu_k),或者在每个工作点把非线性模型做一阶泰勒展开。这样的好处非常大——问题变成二次规划(QP),凸、可快速求解、理论成熟。但代价也明显:线性化只在展开点附近有效。你把这套模型用到预测时域之外,误差就开始放大,尤其当系统本身是强耦合、大范围运动的对象时,线性模型从第一步预测起就已经失真。

我见过不少开发者把线性 MPC 用在倒立摆的摆动上升(swing-up)上,结果很挣扎。原因不是 MPC 不好,而是线性模型描述不了“从底部甩到顶部”这样一个大范围动态过程。你在底部附近线性化,模型告诉你的未来状态全是失真的;你在顶部附近线性化,又根本看不到底部初始状态该怎么演化。线性 MPC 能把顶部小扰动维持得不错,但让它完成大幅度状态转移,等于让一张局部照片去推导全球地图。

1.2 三处非线性:模型、约束与目标

很多人以为 NMPC 的“非线性”只体现在动力学方程里,其实真正让问题变难的,是三个地方同时出现非线性。

第一是模型非线性。差速机器人的运动学 (v\cos\theta, v\sin\theta)、机械臂的关节耦合、四旋翼的旋转动力学、车辆高速工况下的轮胎力模型,这些都是典型的非线性。只要预测时域内的工作范围大到不能忽略这些非线性项,线性近似就会成为主要误差来源。

第二是约束非线性。这是 NMPC 在实际工程里最有价值的地方之一。比如避障约束 ((x-x_{obs})^2+(y-y_{obs})^2\ge r^2),这是一个非凸约束。线性 MPC 想处理它,通常得把它线性化成半平面约束,结果就是保守——把圆形障碍物近似成一个多边形,安全距离被迫放大,能走的路径也窄了。NMPC 可以直接吃原始的非线性约束,让控制器贴着障碍物边缘规划,路径质量明显提高。

第三是目标函数的非线性。有的任务是让机械臂末端沿一条特定曲线运动,误差项可能不是简单的二次型;有的任务涉及终端惩罚是到目标点的距离,甚至在机器人导航里代价函数会和环境的距离场耦合。这些都不是把 Q、R 矩阵塞进去就能表达的。

所以 NMPC 不是“MPC 加了一点非线性”,而是把每个采样周期里要解的凸 QP 换成了非凸非线性规划(NLP)。这就带来三个连锁反应:求解变慢、存在局部最优、对初始猜测敏感。后面的章节基本都是在处理这三个连锁反应。

1.3 什么时候才值得上NMPC

NMPC 不是银弹,我自己的判断标准很简单:先看工作范围,再看约束复杂度。如果系统长期在小扰动、固定工作点附近运行,线性 MPC 甚至 LQR 就够了,上 NMPC 纯属给自己找麻烦。如果任务涉及大范围机动、强耦合、非凸约束,或者必须贴着物理边界工作,那才值得付出额外算力。

应用场景推荐方案判断依据
小型稳定系统微纠偏(液位、温度)PID / LQR工作范围小,模型基本线性
常规轨迹跟踪,工作点固定线性 MPC约束简单,预测时域内线性近似够用
机器人避障导航、大幅机动NMPC非线性动力学 + 非凸避障约束
机械臂大范围摆动、无人机特技NMPC强耦合、大范围状态转移
极高频率嵌入式控制(电流环等)专用线性控制器算力有限,NLP 求解很难达到频率要求

一句话总结:NMPC 的收益来自“预测模型的真实性”,代价是“求解难度”。值不值得,取决于你的预测模型在目标工况下失真到什么程度。

2. 每个采样周期里,优化器在做一道什么样的题目

2.1 一个标准的离散时间最优控制问题

把 NMPC 的每个周期拆开看,它要解的是这样一个问题:给定初始状态 (x_0),寻找状态序列 (x_1,\dots,x_N) 和控制序列 (u_0,\dots,u_{N-1}),让代价函数最小,同时满足动力学方程和所有约束。

用纯文本写出来就是:

minimize J = sum_{k=0}^{N-1} L(x_k, u_k) + V_f(x_N) subject to x_{k+1} = f_d(x_k, u_k) # 离散动力学 x_0 = x_current # 初始状态 g(x_k, u_k) <= 0 # 路径约束/避障约束 u_min <= u_k <= u_max # 执行器限制

这里 (L(x_k,u_k)) 是阶段代价,常见的是 ((x_k-x_{ref})^T Q (x_k-x_{ref}) + u_k^T R u_k);(V_f(x_N)) 是终端代价,用来把最终状态引导到目标附近。(f_d) 是连续动力学经过数值积分离散化之后得到的差分方程。整个决策变量包括所有时刻的状态和控制,数量大约是 ((n_x+n_u)\times N)。

这个问题的本质是一个带等式约束和不等式约束的非线性优化问题。注意它一般不是凸的——这在实践中意味着求解器不能保证找到全局最优,只能找到满足 KKT 条件的局部最优。NMPC 的工程艺术,很大程度上就是让这个局部最优解尽量接近你想要的全局最优行为。

2.2 打靶法:怎么把微分方程变成优化约束

有了问题数学形式,下一步是决定“哪些量是决策变量”。早期的方法叫单步打靶:只把控制序列 (u_0,\dots,u_{N-1}) 当作决策变量,状态轨迹完全由初始状态递推仿真得到。这样变量少,但问题也很明显——约束是嵌套在仿真函数内部的隐函数,梯度计算麻烦,误差沿预测时域累积,而且很难对中间某个状态点直接加约束。

现在几乎所有的 NMPC 实现都用多重打靶:状态序列 (x_1,\dots,x_N) 和控制序列同时作为决策变量,动力学方程变成相邻节点之间的等式约束:

x_{k+1} = f_d(x_k, u_k), k = 0, 1, ..., N-1

你可以把状态轨迹想成一串珍珠,控制量是想拉动的绳结。优化器要做的是同时找到珍珠的位置和绳结的拉力,并保证相邻两颗珍珠之间的距离关系恰好满足动力学方程。多重打靶让问题变得更大,但约束结构非常稀疏,现代 NLP 求解器对这种稀疏结构有专门优化,实际求解效率反而更高。更重要的是,你现在可以对任意中间节点的状态直接加约束,比如第 k 步的机器人位置必须避开障碍物,这就是上一章说的非线性约束能力的来源。

离散化本身也有讲究。我一般不用显式欧拉,而是用 RK4。显式欧拉实现简单,但精度低,在预测时域较长时误差累积会明显扭曲“预测模型”和“真实模型”的一致性,最终导致控制器以为自己在走一条安全路径,实际早就蹭到障碍物了。RK4 每一步需要调用四次动力学函数,计算量略高,但对大多数工程模型来说,精度足够且数值稳定,是 NMPC 默认的性价比选择。

离散化方式精度计算量适用情况
显式欧拉低低教学、步长极小的简单系统
RK4高中工程常用,覆盖绝大多数场景
高阶变步长积分很高高刚体动力学、模型高度病态

2.3 SQP与内点法:两种主流求解器的取舍

有了打靶法,NLP 的结构就定了。接下来是谁来解的问题。主流有两类求解思路:SQP 和内点法。

SQP(序列二次规划)的思路是:在当前迭代点附近,把非线性目标函数和约束都用二阶/一阶近似展开,构造一个二次规划子问题,然后求解这个 QP 得到搜索方向,再沿这个方向做线搜索或置信域更新,重复迭代直到收敛。你可以理解成“走一步看一步”——每一步都在当前点建一个局部二次模型,沿着这个模型找到下一步,再重新建模型。

内点法走的是另一条路:把不等式约束变成障碍项塞进目标函数,比如对 (g(x)\le0) 加一个 (- \mu \sum \log(-g(x))),其中 (\mu) 是逐渐缩小到零的障碍参数。求解器像是在约束边界上涂了一层很软很厚的墙,一开始墙很软,解可以稍微越界;随着迭代,墙越来越硬,最后逼近真正的约束边界。

在实际工程里,我第一版基本都用 IPOPT(内点法),因为它对中等规模的 NLP 非常稳健,对初值的要求也不算极端。如果以后要做实时性更强的部署,再考虑 SQP 系列,因为 SQP 天然支持实时迭代模式——每拍只做有限次迭代,不必完整收敛。Casadi 里两种都能直接用,关键是理解它们各自的脾气。

3. 一个跑得通的NMPC最小例子:差速机器人避障导航

3.1 工具选型:怎么从零快速上手

写 NMPC 代码,最怕的是把时间花在“表达一个优化问题”的繁琐细节上。我推荐先从 Casadi 这类符号计算型框架入手。它的好处是所有动力学、代价、约束都可以用符号变量写出来,框架自动帮你求导和生成数值求解接口,省掉了手写雅可比和黑塞矩阵的痛苦。你只需要关心自己的模型和问题表达,求解器选好参数就行。

安装很直接:

pip install casadi

装完后可以检查一下内置求解器情况:

import casadi as ca print(ca.has_solver("ipopt"))

如果你看到 True,就可以直接用 IPOPT 了。Casadi 社区版把 IPOPT 接口集成在主包里,对学习和小型项目足够用。

3.2 建模与离散化:核心代码怎么写

下面用差速驱动机器人做例子。状态是 ([p_x, p_y, \psi]^T),分别是平面位置和航向角;控制是 ([v, \omega]^T),线速度和角速度。动力学是典型的非线性运动学模型:

import casadi as ca import numpy as np dt = 0.2 # 控制周期/离散步长 N = 40 # 预测步数 v_max, w_max = 1.0, 2.0 goal = np.array([5.0, 5.0]) obs = np.array([2.5, 2.5]) obs_r = 0.8 safe_r = 0.15 px = ca.MX.sym('px') py = ca.MX.sym('py') psi = ca.MX.sym('psi') v = ca.MX.sym('v') w = ca.MX.sym('w') state = ca.vertcat(px, py, psi) ctrl = ca.vertcat(v, w) # 连续动力学 xdot = ca.vertcat( v * ca.cos(psi), v * ca.sin(psi), w ) f_cont = ca.Function('f_cont', [state, ctrl], [xdot]) # RK4 离散 def rk4_step(x, u): k1 = f_cont(x, u) k2 = f_cont(x + dt/2 * k1, u) k3 = f_cont(x + dt/2 * k2, u) k4 = f_cont(x + dt * k3, u) return x + dt/6 * (k1 + 2*k2 + 2*k3 + k4)

解释一下这段代码为什么要这么写。先用 MX 符号变量把状态和控制定义出来,再用 Casadi Function 封装连续动力学。RK4 离散时,k1 到 k4 是同一个函数在不同中间点的斜率估计,最终加权组合得到一步状态更新。这个rk4_step返回的是符号表达式,后面可以直接塞进优化问题。

接下来构建优化问题。用 Casadi 的 Opti 栈写起来很顺手:

opti = ca.Opti() X = opti.variable(3, N+1) # 状态变量,每一步所有状态 U = opti.variable(2, N) # 控制变量 P = opti.parameter(3) # 当前测量状态 # 目标函数 cost = 0 for k in range(N): dx = X[0, k] - goal[0] dy = X[1, k] - goal[1] cost += dx*dx + dy*dy cost += 0.1 * U[0, k]**2 + 0.05 * U[1, k]**2 # 终端加权,让终点尽量靠近目标 cost += 3.0 * ((X[0, N] - goal[0])**2 + (X[1, N] - goal[1])**2) opti.minimize(cost) # 动力学等式约束 for k in range(N): opti.subject_to(X[:, k+1] == rk4_step(X[:, k], U[:, k])) # 初始状态 opti.subject_to(X[:, 0] == P) # 控制约束 opti.subject_to(opti.bounded(-v_max, U[0, :], v_max)) opti.subject_to(opti.bounded(-w_max, U[1, :], w_max)) # 圆形障碍物避障约束 obs_r_sq = (obs_r + safe_r)**2 for k in range(N+1): dist_sq = (X[0, k] - obs[0])**2 + (X[1, k] - obs[1])**2 opti.subject_to(dist_sq >= obs_r_sq) # 求解器选择 opti.solver('ipopt', {'print_time': False}, {'print_level': 0})

注意几个细节。目标函数里我用的是 “误差平方之和 + 控制量平方之和”,权重都是标量,这样便于初调。避障约束我直接用了圆形不等式,表达的是“预测轨迹上的每一个状态点都必须离障碍物中心足够远”。这种约束在多重打靶下非常自然,你在线性 MPC 里想表达一个非凸的圆形避障区域就很麻烦。

这个例子故意没有惩罚航向角,因为航向角有 (2\pi) 的周期性,直接用“误差平方”会在角度跳变处制造虚假大误差。如果你确实要约束航向,更稳妥的做法是用 (\cos\psi) 和 (\sin\psi) 的误差项,而不是直接惩罚角度差。

3.3 闭环主循环与初始猜测(warm start)

优化问题建好之后,真正的闭环仿真是一个循环:测量/获取当前状态 → 把状态赋给参数 P → 求解 NLP → 取出当前控制量 → 应用到被控系统积分一步 → 反复。代码骨架如下:

def rk4_numpy(x, u, dt): x = ca.DM(x) u = ca.DM(u) k1 = np.array(f_cont(x, u)).flatten() k2 = np.array(f_cont(x + dt/2 * k1, u)).flatten() k3 = np.array(f_cont(x + dt/2 * k2, u)).flatten() k4 = np.array(f_cont(x + dt * k3, u)).flatten() return np.array(x).flatten() + dt/6 * (k1 + 2*k2 + 2*k3 + k4) x_current = np.array([0.0, 0.0, 0.0]) traj = [x_current.copy()] last_u = np.array([0.0, 0.0]) for step in range(80): opti.set_value(P, x_current) try: sol = opti.solve() last_u = np.array(sol.value(U[:, 0])).flatten() # warm start:把上一次解作为下一次迭代初值 opti.set_initial(X, sol.value(X)) opti.set_initial(U, sol.value(U)) except Exception as e: # 求解失败时的兜底:沿用上一次控制量 pass x_current = rk4_numpy(x_current, last_u, dt) traj.append(x_current.copy())

warm start这一步可以说是 NMPC 工程实践里最重要的一环。因为每个采样周期目标参数只是小幅变化,上一次的解通常离下一次的解很近,把它作为初始猜测能让 IPOPT 少走很多弯路,求解时间可以从几百毫秒降到几十毫秒。我第一次跑 NMPC 时没做 warm start,每个周期都要重新冷启动求解,结果不仅慢,还经常收敛到奇怪的局部解。做了 warm start 之后,一个 40 步的差速小车问题在普通笔记本上单次求解大约在 30-80ms 量级,已经可以支撑 5Hz 左右的控制频率。

仿真跑完之后,把traj画出来,你会看到一条从原点出发、绕过障碍物、最终停在目标点附近的平滑曲线。第一次自己跑通这个闭环时,那种“非线性优化真的在实时扮演控制器”的感觉,比看任何论文都直观。

4. 调参实战:预测时域、权重矩阵与求解失败的处理

4.1 预测时域N的选择:先算物理时间,再谈步数

很多新手上来就问 N 取多少,其实这个问题要拆成两步。第一步先决定物理上的预测时域长度 (T_p = N \times dt),第二步再决定离散步长 (dt)。

我的经验法则是:预测时域要覆盖到系统主导动态过程的 3 到 5 倍。差速小车的例子中,起点到目标点最短路径大约 7 米,最大线速度 1 m/s,不考虑障碍也要 7 秒的通行时间,所以我把预测时域定在 8 秒左右。如果预测时域只有 4 秒,相当于让控制器只能看到路径的前半段,它规划的路径会非常“近视”,容易为了眼前的小障碍绕远路,整体轨迹质量明显变差。反过来,预测时域太长也有代价——决策变量更多,非线性程度可能更复杂,求解变慢,局部最优的风险也变大。

离散步长 (dt) 我默认取控制周期本身。差速小车控制周期 0.2 秒,N=40,总预测时域 8 秒,这个配置是一个很平衡的起点。如果控制周期很短比如 20ms,那么 N 通常取 20-50 就够,不要为了“看得远”把 N 拉到一两百,算力吃不消。

4.2 权重矩阵的经验调法:从量纲归一化开始

权重的调法我最推荐这样做:先把所有量纲归一化,把位置误差放在米量级、控制量放在执行器最大值的比例量级,然后设置初始权重。不要一上来就用矩阵操作,先用标量权重跑通,再逐步细化。

以代码里的例子来说,位置误差通常在 0 到 7 米之间,控制量 v 在 0 到 1 m/s,w 在 0 到 2 rad/s。我初始设置:

  • 位置误差权重:1.0
  • 线速度惩罚:0.1
  • 角速度惩罚:0.05
  • 终端误差权重:3.0

调参顺序是固定位置权重,从较大的控制惩罚开始往下扫。控制惩罚大的时候,轨迹平滑但反应迟钝,路径会绕远;逐渐减小控制惩罚,轨迹会变得更短更激进,但执行器摆动也变大。如果减得太狠,你会看到速度在目标点附近来回震荡,甚至出现高频抖振。遇到抖振不要急着加滤波器,先检查是不是控制权重太小导致的。

一个小技巧是给控制量加变化率约束,比如限制每拍 v 的变化不超过 0.2 m/s。这会牺牲一点快速性,但对执行器和整条轨迹的平滑度帮助很大。注意加了变化率约束之后,变量之间的耦合更强,求解器更容易出现不可行,需要配合下一节的松弛策略使用。

4.3 求解失败、超时与约束冲突的兜底思路

NMPC 开发中一定会遇到的问题就是:某个采样周期求解器报 infeasible,或者迭代超时。我的处理思路是分层的。

第一种情况是不可行。最直接的原因通常是约束太紧,尤其是避障约束这类非凸约束。当预测时域里存在一段无论如何都满足不了避障约束的路径时,硬约束会让整个 NLP 找不到可行解。工程上常见的做法是软约束化:把避障约束改写成带松弛变量的形式,比如 (dist_sq + s \ge r^2),其中 (s\ge0) 是一个非负的松弛变量,同时在代价函数里加一项高权重的惩罚项 (1000 \cdot s^2)。这样就算某个节点真的暂时违反了避障,求解器也能给出一个可行解,再由权重慢慢把轨迹拉回安全区。硬约束适合理论上总走得出安全路径的场景,软约束适合实际系统存在干扰、状态可能被推出安全区域的场景。

第二种情况是求解超时。对策按优先级排列:减小预测时域 N、放宽求解器容差、减少约束节点数量、换用实时迭代策略。有时候一个看似普通的 NLP 会因为初值太差多迭代几十次,这时候重新检查 warm start 的合理性,比盲目调 N 更有效。

第三类是轨迹抖振。多数情况下是权重失衡或控制变化率约束缺失,我在 4.2 里已经提到过。还有一个容易被忽略的原因是模型里数值尺度差太大,比如位置是公里级、角度是弧度级,代价函数里两个误差随便一加,量纲小的项直接被淹没,这时候再怎么调权重都是白费。

5. 从仿真到部署:实时性是绕不开的最后一道坎

5.1 算一次的时间到底花在了哪里

跑通仿真之后,下一步必然面对一个现实:计算耗时。上面那个例子单次求解在笔记本上可能只要几十毫秒,听起来很快,但对于控制周期只有 1ms 到 10ms 的嵌入式系统,这个数字根本不够看。要想优化,得先知道时间花在哪里。

一个 NLP 的求解时间由几个因素决定:决策变量维度、约束数量、非线性程度、初值质量、求解器容差。差速小车例子是 3 个状态 2 个控制,N=40,决策变量约 200 个,对 IPOPT 来说属于小问题。但如果你做机械臂,状态可能 10 维以上,N 又是 50,决策变量上千,单次求解时间会指数级上涨。这时你需要先 profile,算清楚每一拍的真实耗时,再决定用什么优化策略。

我做项目时的经验是先定硬指标:控制器必须在多少个毫秒内返回控制量。然后在这个硬指标下倒推模型维度和 N 的可行范围。不要先写一个复杂度很高的模型,再去想怎么压到实时——顺序反了会非常痛苦。

5.2 从在线完整求解到实时迭代(RTI)

如果完整求解无法满足实时性,工程上的下一步是改用实时迭代思想。核心逻辑是:不再要求每个控制周期都把 NLP 解到完全收敛,而是每拍只做一次或少数几次 SQP 迭代,让解沿着“离线最优轨迹”逐步逼近。看起来有点偷懒,但从预测控制的角度完全可以接受——反正下一拍还要重新测量、重新规划,与其花 100ms 把这一拍的解算完美,不如 10ms 算个八九不离十,然后下一拍继续修正。

这种模式在 SQP 框架里实现得非常自然,也是所谓实时迭代(RTI)的基本思想。Casadi 里也能通过 sqpmethod 设置迭代次数来模拟这个效果,不过更专业的方案是上专门的性能导向框架,它们会把符号维度、内存规划、代码生成都按嵌入式实时场景重构。

5.3 代码生成与嵌入式部署的一些经验

到了部署阶段,我会把“原型框架”和“生产框架”分开看。原型阶段用 Casadi 的 Python 接口完全没问题,模型、权重、求解器都可以快速迭代。但要落到低算力板卡上,至少要考虑两个方案。

一是 Casadi 的代码生成功能。它可以把动力学表达式、代价函数、甚至整个 NLP 相关函数导出成自包含的 C 代码,这样你可以在嵌入式环境里调用,省去 Python 解释器开销。适合算力稍弱的单板计算机。

二是直接用面向嵌入式 NMPC 的性能导向框架,比如 Acados。它从设计之初就为多层打靶、RTI、代码生成做了深度优化,内存管理更精细,时间开销更可控。代价是学习和配置成本更高。我的建议是:先在你的目标硬件上做一个算力 benchmark,跑一个最简单的 NMPC 循环看真实耗时,再决定值不值得迁移到更底层的框架。

还有一点要提醒:预测模型不是越精细越好。嵌入式实时系统里,你往往需要刻意简化模型,比如忽略次要耦合、把执行器动态近似成一阶惯性环节。模型的简化会带来失配,需要额外设计鲁棒性兜底,但在工业应用里,把“足够好的模型”跑在 10ms 周期内,通常优于“完美模型”只能跑 100ms。

6. 几个让NMPC翻车的小细节(个人踩坑汇总)

6.1 单位制与量纲:最不起眼却最致命的坑

我第一次在仿真里引入避障约束时,障碍物坐标用的是厘米,而位置状态用的是米,结果代价函数里两个距离误差的量级差了 100 倍,控制器完全无视障碍约束,直接穿墙而过。后来检查代码才发现是单位制没统一。NMPC 里所有状态、控制、约束都必须在同一套单位制下,一般建议全部转成国际单位(米、弧度、秒)。角度尤其容易出问题——如果你把角速度写成度/秒,而动力学里 (\cos\psi) 还是按弧度算,那模型本身就是错的,求解再好也没用。

6.2 硬约束和软约束的选择

避障这类约束我最初都写成硬约束,逻辑很简单:安全要求不能违反。但实际跑起来就发现,系统一旦受到扰动,真实状态稍微越过约束边界,下一拍优化问题立刻 infeasible,控制量直接中断。这个问题在只做仿真时经常暴露不出来,因为仿真模型和预测模型一致,永远不会超出安全区域。一旦接上真实系统,状态噪声、延迟、模型失配都来了,硬约束就变得非常脆弱。所以我现在默认把避障类约束做成带惩罚项的软约束,只对执行器限幅这类物理硬约束保留硬形式。

6.3 第一次跑通时的操作顺序建议

最后分享一个我个人的流程,希望对第一次上 NMPC 的人有帮助。先写状态方程,用数值差分和解析雅可比互相验证;再做量纲归一化;然后不急着闭环,先把一个开环最优控制问题解出来,看看轨迹是否符合直觉;确认没问题后再接闭环主循环;闭环稳定后,再注入噪声和扰动,测试求解失败时的兜底逻辑;最后才考虑实时框架和部署。

如果你按照这个顺序走,大部分“调参调不出来”的问题都能提前暴露在更简单的环节,而不是堆在最后一步一起爆发。我自己最惨的一次就是因为跳过了开环验证,直接写闭环,结果 NMPC 一直在绕圈,我调了三天权重,最后才发现是动力学方程里一个符号写反了。

NMPC 非线性模型预测控制最大的魅力,是它真的能让控制器在强非线性、强约束场景下拿出接近人类水平的规划行为。但它也足够坦诚——你留给它的模型越长,它对你的要求就越多。先把模型写对,把量纲理顺,把预测时域和权重按物理意义初定一版,跑通最简单的闭环,后面那些实时性、鲁棒性的问题才谈得上慢慢解决。

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

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

立即咨询