1. 项目概述:为什么要同时盯着MPC、NMPC和强化学习
先把这个项目的核心说清楚:这是一个用Matlab搭建的无人机轨迹跟踪仿真框架,目标场景是让一架四旋翼无人机持续跟踪一个移动平台(可以理解成地面移动机器人、船只甲板或者处在运动状态下的着陆点)。项目里不只是用单一的控制器,而是把三种方案放在同一个框架下做横向对比——标准线性MPC、非线性NMPC,以及把强化学习和模型预测控制揉在一起的混合MPC-RL方案,最后统一用同一套性能指标去评估谁跟踪得更稳、更快、更省控制量。
适合谁来参考?如果你是正在做无人机控制、移动目标跟踪或者毕设/课题里涉及“轨迹跟踪控制算法对比”的人,这个项目的价值在于:它给你搭好了一个完整的对比环境,你不需要自己从零去分别实现三套控制器,而是可以把注意力放在“为什么不同控制方案表现差异这么大”“混合RL的方案到底赢在哪里”这类更本质的问题上。对于想入门模型预测控制的读者,它也是一条从线性MPC过渡到非线性MPC再进阶到学习型控制器的清晰路径。
我在实际跑这类仿真时最大的感触是:很多人一上来就追求“高级算法”,结果把NMPC调得发散、把RL训得不收敛,最后反过来怀疑算法本身。但问题往往出在建模和参数适配这种“土活儿”上。所以这篇内容我不会只罗列结果,而是把三套方案的建模差异、求解细节、调参过程、踩坑记录都摊开讲,让你能直接对着自己的场景复现和改动。
2. 三种控制方案的底层逻辑拆解
2.1 线性MPC:把预测问题变成标准二次规划
MPC的基本思路可以用一句话概括:在每个控制周期,根据当前状态,向前预测一段固定时域内的系统行为,然后求解一个带约束的优化问题,得到一串最优控制序列,但只执行第一步,下一周期重新滚动求解。
无人机模型本质上是非线性的,那为什么还有“线性MPC”这么一说?关键在于工作点。如果你把无人机约束在悬停或者小角度机动状态附近,那么推力和姿态之间的耦合可以近似为线性关系。这时候模型可以写成:
x(k+1) = A x(k) + B u(k)
其中状态量通常是位置、速度、姿态角和角速度,控制量是四旋翼的推力与三个方向的力矩指令。一旦模型线性化了,每步优化就变成一个标准的二次规划问题:
min Σ (x_ref - x)^T Q (x_ref - x) + u^T R u
subject to 状态和控制量约束
Matlab里直接调用quadprog甚至MPT工具箱都能解。线性MPC最大的优势就是实时性强、求解稳定,因为QP求解器非常成熟,几乎是“解不爆”的。但代价是,当无人机做大机动——比如从悬停状态突然加速去追一个高速移动平台时,线性化模型和真实模型偏差变大,跟踪精度会明显下降,这就是它的天花板。
2.2 NMPC:代价是求解难度,收益是模型保真度
NMPC把优化问题里的预测模型换成了完整的非线性动力学方程。四旋翼的常见模型是:
位置动力学:m * ddot(p) = R * T - m * g
姿态动力学涉及四元数或者欧拉角表示的旋转矩阵R,以及推力T与机体方向之间的关系。优化问题从QP变成了非线性规划,Matlab里可以用的求解器包括fmincon、casadi搭配ipopt,或者直接用自带优化工具箱的fmc求解。
这里有个关键点很多人第一次接触时容易忽视:NMPC的优化问题本身是非凸的,所以求解结果强烈依赖初始猜测。如果上一时刻的解没有正确传给下一时刻作为初值,求解器很可能在采样时间内无法收敛或者收敛到一个糟糕的局部解。我在项目里采用的热启动方式是:把上一周期解出的最优控制序列平移一步,当作当前周期的初值。这一步看着简单,但对求解稳定性的提升是巨大的,后面我会具体展开。
NMPC的优势是预测模型更贴近真实系统,大机动场景下跟踪精度明显优于线性MPC;劣势是每个控制周期的计算量陡增,采样周期稍微压到50ms以内,普通笔记本电脑上跑fmincon就会开始吃力。
2.3 强化学习:不是替代MPC,而是补上它的短板
强化学习在这类任务里的角色,很容易被误解成“用神经网络直接替代控制器”。但实际项目中更稳妥的做法是混合架构。纯RL端到端控制无人机跟踪移动目标在仿真里能跑,但约束满足能力差、可解释性弱、训练不稳定,落地风险很高。混合MPC-RL的思路是把RL用在MPC“不擅长”或者“算不动”的地方。
具体到本项目,我用的混合方式有两种,我建议你也照这个思路去理解:
一种叫“RL调参器”。MPC的核心权重矩阵Q和R直接决定控制性能,但手调Q和R非常耗时,而且Q和R在任务不同阶段可能应该有不同取值。比如目标在远处时更侧重快速趋近(加大位置权重),接近后更侧重平滑停靠(加大控制量惩罚)。这时可以让RL智能体根据当前相对距离、速度差等状态,每几十步输出一组Q和R的调整增量,MPC内部的优化结构和约束条件完全不动。
另一种叫“RL补偿器”。用RL网络学习MPC模型的未建模误差,比如气动阻力、执行器延迟、外部扰动。网络输出一个补偿项,叠加到MPC的预测模型或最终控制指令上。这样RL处理的是“残留误差”,而不是直接背锅控制主回路。
无论哪种混合方式,RL的训练环境都是MPC闭环下的仿真,奖励函数设计直接决定了最终性能。我踩过最深的坑就是奖励函数给得太“大锅饭”——把位置误差、速度误差、控制量都加在一起,结果智能体学到的是“和稀泥”,每一项都没做好。后来改成分段奖励,详细设计我放到第4节讲。
3. 无人机跟踪移动平台的场景建模与评估指标
3.1 被跟踪的移动平台应该怎么建模
这个项目里移动平台不是简单给一条参考轨迹让无人机去追,而是真的模拟了一个带有自身运动规律的目标。我采用的平台模型是二维平面上的运动目标(可以理解成小船或者地面车),运动模式分三种:
- 匀速直线运动
- 正弦曲线运动(模拟规避机动)
- 随机路点运动(模拟不可预测目标)
这三种模式分别测试控制器的“稳态跟踪能力”“动态响应能力”和“扰动适应能力”。平台的位置状态会直接喂给控制器做参考输入,而且在实际项目中,无人机只能通过机载传感器估计目标位置,不会是理想的全知状态。所以我还加了一个高斯噪声模块,模拟目标定位误差,这一下就把三套方案的差距拉大了——线性MPC在噪声下容易抖,NMPC因为预测窗口内有非线性补偿能稳住,混合RL方案则通过学习学会了主动过滤部分噪声。
3.2 无人机模型的核心参数
仿真用的四旋翼模型参数如下,都是通用参数,你可以直接套用:
| 参数 | 数值 | 说明 |
|---|---|---|
| 质量 m | 1.2 kg | 含电池和负载 |
| 重力加速度 g | 9.81 m/s² | |
| 最大推力 | 17 N | 约1.5倍重力 |
| 最大倾角 | 0.35 rad | 约20度 |
| 位置环采样周期 | 0.05 s | 20Hz |
| 姿态环采样周期 | 0.005 s | 200Hz |
注意这里有个分层控制的思想:位置环是外环,输出的是期望加速度和期望姿态角,然后传给内环姿态控制器去执行。MPC、NMPC和RL都在外环工作,内环用一个简单的PD控制器跟踪姿态指令。这么做的好处是让对比更干净——三套方案面对的是同一个内环,性能差异纯粹来自外环控制器的预测和规划能力。
3.3 性能评估指标怎么定
如果只比较“最终位置误差”,你很难说清楚一个控制器到底强在哪。我把评估指标拆成了四个维度:
- 平均跟踪误差(MAE):整个跟踪过程中无人机与目标位置的误差均值,反映整体跟踪精度
- 最大跟踪误差(Max Error):瞬态最大偏差,反映控制器在目标突然加速或者转向时的响应能力
- 控制能量:所有控制输入平方的累积和,反映控制器“费不费劲”,这个指标直接影响电池续航
- 稳态建立时间:从目标开始机动到误差收敛到0.1m以内所需的时间
项目里最后会输出一张对比雷达图或者柱状图,把三套方案在这四个维度上的表现画在一起。我在实际测试中发现一个很反直觉的现象:NMPC在MAE上并不是总能碾压线性MPC,在目标匀速直线运动时两者差距很小,但控制能量NMPC反而更大。原因在于NMPC每个周期都在优化非线性的完整模型,它会更“积极”地规划机动,控制量自然就大。而混合RL方案在控制能量这一项上通常表现最好,因为它学会了“该安静时安静”。
4. 混合MPC-RL方案的设计细节与训练要点
4.1 为何选择SAC而不是其他RL算法
先交代背景:我在这个项目里对比过DQN、PPO和SAC三种算法,最终选了SAC(Soft Actor-Critic)作为RL核心组件。原因是这个任务的动作空间是连续的——输出的是一个4维连续向量(期望加速度三轴+偏航角速度修正),DQN本质上处理离散动作空间,硬用的话还得把动作离散化,精度损失很大,直接排除。PPO虽然在连续控制里常用,但它对超参数敏感,特别是学习率和clip范围,在小样本场景下容易训练崩溃。SAC自带最大熵机制,天然鼓励探索,在无人机这类高动态任务里训练稳定性最好。
但SAC也有个明显缺点:训练时间是最长的,因为每次更新需要同时维护两个Q网络、一个策略网络和一个温度系数的更新。我在训练时使用了一台普通的办公电脑,跑完10万步仿真交互大约耗时一个晚上,属于可接受范围。
4.2 状态空间、动作空间和奖励函数的设计
状态空间的输入直接决定智能体能不能学到有用的映射关系。我最终选定的状态向量是:
[相对位置误差 ex, ey, ez, 相对速度误差 vex, vey, vez, 目标速度估计 vtx, vty, 上一时刻控制量 ux, uy, uz]
共11维。这里有两个我特别强调的工程决策:一是把“目标速度估计”放进状态空间,没有它,智能体面对运动中的目标时永远处于“看到偏差才反应”的被动状态,很难提前预判;二是把上一时刻控制量也放进去,这给了智能体一个“连续性感知”,避免它输出的控制序列来回跳变。
动作空间是3维的期望加速度增量,RL输出的增量会叠加到当前加速度指令上,而不是直接替代。这个设计保证了即使RL输出完全错误,叠加后的总指令也不会离谱到立刻让无人机发散。
奖励函数是我调试时间最长的一部分。第一次设计用的是单一综合奖励:
R = -w1 * ||position_error|| - w2 * ||velocity_error|| - w3 * ||control_input||
这个形式的问题在于,位置误差在一个较大值时,速度误差和控制惩罚基本被淹没,智能体只顾快速靠近目标,忽略了对控制量的约束,训练后期会出现高频抖动。后来我改成分段+事件触发的设计:
- 当||position_error|| > 2m时,只奖励位置误差的减少,速度和控制惩罚权重降为0,鼓励大胆接近目标
- 当||position_error|| <= 2m时,速度误差和控制惩罚权重逐步恢复,鼓励精细调节
- 每当||position_error||首次进入0.5m范围时,额外给+10的稀疏奖励,相当于给智能体一个“阶段性通关”信号
这个改动让训练收敛速度明显提升,最终策略也稳定得多。你可以把这个设计思路迁移到自己的任务里:关键词是“阶段性优先级”,别让所有目标均摊在同一个奖励函数里。
4.3 训练流程的工程细节
训练流程我整理成了五步,每一步都有值得注意的细节:
先用线性MPC以固定参数收集初始经验,写入经验回放池。这一步叫做“预热”,让回放池里一开始就有一部分有效数据,而不是全从随机探索开始,这能显著缩短冷启动时间。
在Simulink或者Matlab脚本中搭建RL环境接口,关键是写出一个符合RlToolbox接口定义的step函数,输入是动作,输出是下一时刻状态和奖励。注意step函数里要正确同步无人机动力学模型的离散时间步,否则训练环境的时间语义混乱,智能体永远学不会。
开始SAC训练,期间每个评估周期暂停训练,让当前策略去跑一次完整的跟踪任务,记录评价指标。这个“训练与评估分离”的操作非常重要,只看训练奖励曲线而忽略真实任务表现,你很容易被骗——训练奖励高可能只是智能体学会了刷奖励函数bug。
训练完成后把策略导出为Matlab函数或者保存为.mat文件,供仿真主程序调用。注意导出的网络需要在训练环境里做一次“冻结推理”测试,因为Matlab的RL智能体对象内部带了一些训练专用逻辑,直接拿去做闭环仿真有时会报维度错误。
最后做敏感性分析:把训练时固定的目标运动参数(如正弦运动频率)调大10%,看最终策略是否依然有效。这件事很多人不做,但实际项目中目标运动模式不可能和训练时完全一样,做一次敏感性分析能让你对混合方案的鲁棒性心里有底。
5. 仿真实现的关键代码逻辑与参数整定过程
5.1 整体代码结构怎么组织
这个项目我用的是纯脚本来组织仿真流程,没用Simulink。原因有两个:一是三套控制器的对比逻辑在脚本里写起来更直观,每套方案都是“初始化参数 -> 循环求解 -> 记录数据”三段式,结构清晰;二是混合RL方案的训练和推理涉及神经网络对象,在Simulink里嵌入反而麻烦。
主目录下建议按下面的层级组织:
UAV_Tracking_MPC_RL/ ├── core/ # 无人机动力学模型、目标运动模型 ├── controllers/ # MPC、NMPC、混合RL控制器 ├── evaluation/ # 性能指标计算、结果绘图 ├── rl_training/ # SAC训练脚本、环境接口、超参数配置 ├── main_compare.m # 三方案对比主脚本 └── config/ # 所有仿真参数集中配置把参数集中到config文件里是一个值得坚持的习惯。我见过太多人的仿真脚本里到处是魔法数字,改一个采样时间要找半天。集中管理之后,每次实验只需要改config.m里的值,结果的可复现性也大大提高。
5.2 MPC控制器核心代码解读
线性MPC的代码核心是预测矩阵的构建。假设预测时域为N,控制时域为M,那么从当前状态x0出发,系统的预测轨迹可以写成:
X_pred = A_aug * x0 + B_aug * U_seq
其中A_aug和B_aug是通过把状态转移矩阵A和输入矩阵B按预测时域“展开”得到的增广矩阵。这段代码我建议你手写一遍,哪怕只是20行的矩阵循环,也能让你彻底理解MPC的滚动优化本质:
function [A_aug, B_aug] = build_prediction_matrices(A, B, N, M) n = size(A, 1); p = size(B, 2); A_aug = zeros(n*N, n); B_aug = zeros(n*N, p*M); for i = 1:N A_aug((i-1)*n+1:i*n, :) = A^i; for j = 1:min(i, M) B_aug((i-1)*n+1:i*n, (j-1)*p+1:j*p) = A^(i-j) * B; end end end然后把优化目标写成标准二次型,调用quadprog求解。这里有一个新手很容易犯的错:在用quadprog时,目标函数里有一项是-2*x_ref'QX_pred,这里的x_ref需要在整个预测时域内复制成N份,很多人只用了当前时刻的参考值,导致预测时域后半段的参考全错,跟踪滞后感特别明显。
NMPC的代码框架和MPC类似,但优化目标不再是二次型,而是直接调用fmincon:
options = optimoptions('fmincon', 'Algorithm', 'sqp', 'MaxIterations', 50, ... 'OptimalityTolerance', 1e-4, 'SpecifyObjectiveGradient', false); [u_opt, ~] = fmincon(@(u) nmpc_cost(u, x0, x_ref_seq, dt), ... u_init, [], [], [], [], lb, ub, @(u) nmpc_constraints(u, x0, dt), options);注意MaxIterations设置成50而不是默认的400。这是我在实际调试中得出的经验,因为在20Hz的控制周期下,留给每个周期fmincon求解的时间只有50ms,限制迭代次数可以保证求解器在时限内退出,而不是卡在某个迭代里超时。当然代价是次优解,但工程上“准时的次优解”远好于“超时的最优解”。如果你用casadi搭配ipopt求解,可以把求解器换成ipopt并设置max_cpu_time参数,效果类似。
5.3 混合RL控制器的推理接口设计
训练好的SAC策略在仿真闭环中只是作为函数调用出现。Matlab的强化学习工具箱里,训练好的智能体可以直接用getAction函数得到动作。但我在实际集成时发现,getAction调用会带一些额外开销,每次调用大约5ms左右,在20Hz的控制周期里完全能接受。
更重要的集成细节是:混合RL控制器内部需要维护一个“MPC上次解出的控制序列”和“RL上次输出的补偿量”。两者叠加时要做限幅处理,防止补偿量把总控制指令推到执行器饱和区。我的实现逻辑是:
% MPC求解得到基础控制量 u_mpc = solve_mpc(x, x_ref, params); % RL根据当前状态输出补偿量 obs = build_observation(x, x_ref, u_mpc); u_rl_compen = getAction(rl_agent, obs); % 限幅叠加 u_total = u_mpc + 0.3 * u_rl_compen; % 经验系数0.3,防止RL主导控制 u_total = min(max(u_total, -u_max), u_max);这里0.3这个系数是经验值,我试过0.1和0.5,0.1的时候RL作用太微弱几乎没效果,0.5的时候训练初期的糟糕策略会对MPC造成明显干扰,甚至把系统带偏。0.3是一个折中的选择。你可以根据自己任务的动力学时间尺度去调整。
5.4 三套方案的参数选择与对比实验设计
为了对比公平,三套方案共享同一个无人机动力学模型、同一个目标运动轨迹、同一个控制周期和同一个内环姿态控制器。这意味着唯一变量就是外环控制器的算法本身。这听起来是理所当然的,但实际操作中我见过太多人给MPC调了很好的参数却给NMPC用默认参数,然后得出结论“NMPC不如MPC”。这种对比是没有意义的。
三套方案的关键参数我列在这里:
| 控制器 | 预测时域 | 控制时域 | Q权重 | R权重 |
|---|---|---|---|---|
| MPC | 20步(1s) | 10步(0.5s) | diag(10,10,10,5,5,5) | diag(0.1,...) |
| NMPC | 15步(0.75s) | 15步 | diag(15,15,15,8,8,8) | diag(0.05,...) |
| MPC-RL | 20步(1s) | 10步 | 基础同MPC,Q/R由RL在线微调 |
NMPC的预测时域为什么选15而不是更大?因为NMPC每一步的决策变量维度是4*控制时域,预测时域每增加5步,fmincon的求解时间几乎翻倍。在20Hz控制周期约束下,15步是一个平衡点。MPC因为是QP求解,速度快得多,所以可以放心用到20步甚至更长。
仿真时长我设为60秒,目标运动模式在0-20秒为匀速直线、20-40秒切换为正弦机动、40-60秒切换为随机路点。这样一个场景内就能覆盖三种特性。每个方案跑10次蒙特卡洛仿真(每次目标噪声种子不同),最后取平均指标对比,避免单次随机性掩盖真实差异。
6. 排查实录:仿真和训练中踩过的典型坑
6.1 fmincon求解失败的三大原因
NMPC在仿真中最常见的故障就是fmincon返回错误或者解出的控制量突然跳变。我总结出三大原因,按出现频率排序:
第一是初值猜测不合理。如果你是用的上一时刻的零向量或者常数向量作为初值,在目标突然改变运动方向时,fmincon大概率会在约束边界附近挣扎,求解出来的控制序列可能直接导致无人机向错误方向加速。解决办法就是我之前提到的热启动:把上一周期的最优控制序列去掉第一步,末尾补一个零,作为当前周期的初值。
第二个原因是约束过紧。如果你同时设置了最大倾角0.35rad、最大推力17N、最大角速度2rad/s,这些约束在NMPC的预测时域内某个中间步可能同时被激活,形成不可行域。fmincon遇到不可行约束时会返回一个很差的解。解决办法是把部分硬约束改成软约束,即在目标函数里惩罚约束违反量而不是直接禁止。我记得自己当时把倾角约束从硬约束改成软约束之后,求解成功率从70%直接提到了95%以上。
第三个原因是目标函数的梯度计算问题。fmincon默认用有限差分估计梯度,在无人机这种非线性较强、状态量数值尺度差异大的系统中(位置是米级、姿态角是弧度级、角速度是每秒弧度级),有限差分步长的选择稍不合适,梯度估计就会失真。一个简单粗暴的解决方案是对状态量做归一化:位置除以10、速度除以5、角速度除以2,让所有状态在优化问题中量级相当,效果立竿见影。
6.2 混合RL训练不收敛的排查思路
如果训练了十几万步loss还是震荡不下降,我建议按下面顺序排查:
先看状态观测的数值尺度。SAC算法的神经网络对输入尺度非常敏感,我的状态里位置误差在初始时可能达到10米量级,而速度误差可能在0.5米/秒量级,直接喂进去,网络层权重的梯度会被大数值特征主导。解决办法是在构建obs之前做归一化:
obs_norm = (obs_raw - obs_mean) ./ obs_std;obs_mean和obs_std可以从预热阶段采集的经验里统计得到。这一步做完,训练稳定性通常会有质的提升。
然后看奖励量级。SAC里的Q网络输出的是累积奖励的估计,如果奖励绝对值太大(比如几千),Q网络的loss很容易爆炸。把奖励整体除以一个缩放因子,让单步奖励的绝对值落在0到5之间是一个经验上安全的区间。
最后看经验回放池的容量和采样方式。SAC对回放池容量比较敏感,太小了容易过拟合近期经验,太大了训练收敛慢且占用内存。我在这个项目里用的是10万条经验、批次大小256。如果你发现训练后期策略表现很好但突然退化,可以检查回放池里旧经验的占比,适当调小容量或者增加随机采样的权重。
6.3 闭环仿真发散时的“背锅顺序”排查法
当混合RL闭环仿真发散时,很多人第一反应是怪RL策略不好。但我现在养成了一个固定的排查顺序,这个顺序帮我省了大量时间:
先断开RL,只跑纯MPC。如果纯MPC都发散,说明基础动力学建模、状态初始化或参考轨迹有问题,先把这个修好。
纯MPC正常后接上RL,但把RL补偿量设为零,验证接口和数据结构是否无误。
逐渐加大补偿量系数,从0.05开始,每跑一次仿真观察响应,直到找到发散临界值或者确定问题在奖励函数设计的哪个环节。
确认RL策略本身没问题后,再考虑是不是训练场景与推理场景不一致——比如训练时只用了正弦目标,推理时却用随机路点目标。
这个顺序听着慢,但实际排查速度最快,因为每层问题都是在前一层确认没问题之后才可能出现的,不会出现两个问题叠加在一起互相干扰的混乱局面。
7. 这套仿真框架后续可以怎么扩展
项目做到现在这个程度,基本已经形成了一个可以复用的“控制器横向对比+RL增强”实验平台。从我个人经验看,这个项目有几个明确的扩展方向:
第一个方向是加入风场扰动。现在模型里只有目标定位噪声,没有环境扰动。如果叠加一个空间变化的风场模型,让无人机在跟踪过程中穿越不同风速区域,三套方案的性能差距会进一步拉开,尤其是RL补偿器的优势会更明显——因为补偿器本质上就是在学“模型的未建模动态”。
第二个方向是把二维目标换成三维任意轨迹目标。现在移动平台被约束在半平面内,这是简化处理。如果你把目标扩展成三维空间任意机动,NMPC求解的计算压力会更大,这时候混合RL方案反而有了新的发挥空间——比如用RL学习在不同飞行阶段切换简化模型和全量模型,或者动态调整预测时域长度。
第三个方向是Simulink硬件在环测试。把Matlab脚本中训练好的RL策略导出为C代码,部署到实际的飞控硬件或至少是硬件在环仿真环境里。这一步的挑战在于把仿真中的理想状态接口换成真实的传感器数据流,需要处理延迟、噪声和丢包。我还没在这个项目里走到这一步,但如果你的课题进度允许,这会是一个非常有说服力的结果。
回到最初的问题:为什么要在同一个仿真框架里同时对比MPC、NMPC和混合MPC-RL?因为控制方案的选择从来不是“哪个算法的名字更高级”就能定下来的,它取决于你的任务对精度、实时性和鲁棒性的综合需求。这个项目兜了一大圈之后给我的结论是:线性MPC适合任务简单、讲究效率和稳定性的场景;NMPC在模型已知、算力足够时是精度上限最高的选择;而混合MPC-RL在模型误差大、任务动态变化、无法手动精细调参时,是实用性最均衡的方案。手里有这套仿真框架,下次再接到类似的控制任务,我就不会凭感觉拍脑袋选方案了。