☰
神经网络+MPC控制复现:四旋翼与机器人汽车建模与Matlab实现
2026/10/19 13:57:17 网站建设 项目流程

复现这套“NN+MPC”控制项目,我前前后后花了两周。说实话,最难的不是跑通代码,而是搞明白神经网络到底在MPC里扮演什么角色——很多人复现到一半就停在这里。我先把话说在前面:如果你只是想拿个现成脚本跑出几张跟踪曲线,那这套内容确实能做到;但如果你想弄懂为什么NN要跟MPC结合、两类被控对象(四旋翼和非线性机器人汽车)的模型到底怎么建出来的,以及Matlab代码里的每个模块在干什么,那我建议你把这篇从头读到底。

1. 为什么要把神经网络塞进MPC:两种方法的互补逻辑

1.1 MPC的强项与软肋

模型预测控制本质上是一种“走一步看一步”的优化策略。它在每个采样时刻,通过当前系统的状态测量值,在线求解一个有限时域的最优控制问题——把未来一段时间内系统的预测输出和你希望它走的参考轨迹之间的误差压到最小,同时把控制量的变化幅度也纳入惩罚。等求解完这个优化问题,控制器只取最优控制序列的第一个控制量施加到被控对象上,然后在下一个采样时刻,滚到新的测量值再次求解。这就是“滚动时域”名称的由来。

这种做法的好处非常直观:MPC天然能处理带有约束的系统。四旋翼的推力不可能无限大,机器人汽车的转向角有物理极限,这些约束在MPC里可以直接写进优化问题,求解器会在可行域内找最优解。相比之下,传统的PID或者线性二次型调节器面对约束时会比较狼狈,要么牺牲性能,要么想方设法做抗积分饱和处理。

MPC的软肋也很明显。第一是计算量巨大,每一拍都要在线求解非线性规划问题,尤其是被控对象是非线性强耦合系统时,这个优化问题很难解;第二是对模型精度要求高,预测模型如果和真实系统偏差太大,那“预测”就变成了“猜”,控制效果会大打折扣。换句话说,MPC是一套框架,框架只负责把问题描述出来,真正要落地,你必须给出一个足够准、足够紧凑的预测模型。

1.2 NN这个变量到底顶替哪个角色

很多复现教程喜欢把“NN+MPC”笼统地归为一个“智能控制方法”,但这个说法没有回答最关键的问题:神经网络到底加到哪个环节上了。以我看到的常见复现方案,NN的角色大概有四种,你手里的这个项目大概率属于第二种或第三种。

第一种,用神经网络做系统辨识。MPC需要预测模型,但真实系统的动力学往往很难用数学推导精确建模。这时候你拿一堆输入输出数据去训练一个神经网络,让它学会描述“在当前状态下施加某个控制量,系统下一时刻的状态会变成什么”,然后把这个神经网络当作MPC的预测模型。这种做法的核心优势是:模型不需要你从物理定律推导出来,只要数据够好,网络就能逼近真实行为。

第二种,用神经网络学习MPC的最优控制律。这也是我这次复现项目采用的主线思路。逻辑是这样的:先用传统的NMPC方法跑出大量闭环仿真数据——也就是在不同初始状态、不同参考轨迹下,记录状态和对应的最优控制量,然后把“状态”当作神经网络的输入,把MPC求出的“控制量”当作标签,离线训练一个前馈网络。训练完成后,这个网络就可以在部署阶段直接根据当前状态输出控制量,代替昂贵的在线优化求解器。简单说,就是用一次性的离线训练成本,换在线运行时的即时响应。

第三种,神经网络用来调节MPC的权重参数。MPC的代价函数里通常有跟踪误差权重和控制量权重,这些参数在经典方法里要靠人工调。有人试着用强化学习或者神经网络超参数优化来在线调整权重,但这个方法复现难度大,稳定性分析难度高,在学位论文里常见,在复现项目里反而不太友好。

第四种,神经网络做状态估计或者扰动补偿。比如系统状态不好直接测量,或者存在未知的外部扰动,这时候用神经网络去估计扰动项,然后在MPC的预测模型里加入补偿项。这个思路工程上很实用,尤其是带有干扰的室外飞行场景。

你在复现这套项目时,先别急着改代码,而是要去草稿纸上把这个框架画出来:MPC本体在哪,NN训练数据从哪来、部署到哪去,两个被控对象在仿真里如何切换。画清楚了,后面调试就会顺很多。这套框架的核心思路是:利用MPC前期的在线解生成高质量训练样本,然后让神经网络通过拟合完成“计算加速”,从而在系统需要高频控制、或者计算资源受限的场景下,依然能获得接近MPC性能的控制效果。

2. 两类被控对象的数学建模与仿真参数

2.1 四旋翼:12维状态的简化动力学

四旋翼这个对象,嘴上说简单,实际建模时最容易出问题的地方是坐标系变换和角速度传播。这个项目里用的四旋翼模型是常规的12维状态:机体在惯性系下的三维位置、三维速度、三维欧拉角(通常用roll、pitch、yaw表示)、以及三维角速度。控制输入通常是四个电机的转速平方,但在MPC实现里,一般会转成虚拟控制量:总推力、横滚力矩、俯仰力矩、偏航力矩。

这里要提醒一句:复现时如果你想直接对照别人的论文公式,一定要看清楚对方用的是欧拉角还是四元数。欧拉角在俯仰角接近正负90度时会出奇异性,如果仿真参考轨迹里包含大姿态角动作,欧拉角模型就会崩掉。本复现项目里参考轨迹都是平滑的小角度机动,所以欧拉角建模够用,但你心里要有个数。如果做特技飞行动作,就得换成四元数表示。

动力学方程的核心部分是牛顿-欧拉方程。位置部分的加速度等于(总推力产生的升力方向上的分量,减去重力)除以质量;姿态部分的角加速度由力矩方程给出,其中包含了陀螺力矩项——电机转子转动带来的角动量耦合,这个耦合项在某些简化模型里会被省略,但四旋翼大角速度机动时必须保留。

我对仿真参数的建议是:质量取在1到2公斤这个量级,惯性张量用对角线近似,重力加速度固定9.8,推力系数和力矩系数的比例要合理,否则仿真里会出现“油门顶满还飞不动”或者“轻轻一推就上天”的不合理现象。

2.2 非线性机器人汽车:从自行车模型到state-space

机器人汽车这套系统,绝大部分复现项目用的是“自行车模型”——把汽车的前后轮合并到中轴线上,形状像一辆自行车。这个模型假设车体是刚体,轮胎侧偏特性简化到极致,控制输入通常是两个量,一个是车速(或者加速度),另一个是前轮转角。状态量则包括车辆在大地坐标系下的横纵位置和航向角,有时还包括前轮转角本身,因为实际转向机构是有响应延迟的。

与四旋翼相比,机器人汽车是典型的非完整约束系统——车不能向侧面直接平移,只能沿着轮子朝向的方向运动。这个约束让系统变得很有意思:位置误差可以归零,但如果初始航向错得离谱,直冲上去是修不回来的,必须设计“倒一把方向”之类的回旋机动。MPC在对待这类系统时,优势就在于可以在预测时域里看到这个回旋过程,而不是像单车反馈控制那样只看当前误差。

在Matlab代码里,这个模型通常写成连续时间的一阶常微分方程组,然后通过离散化进入MPC的预测环节。常见做法是用欧拉法或四阶Runge-Kutta法离散。这里有一个直接影响数值稳定性的问题:如果采样时间较大,欧拉法可能会产生发散的正向预测。我建议至少用二阶精度的离散方式,或者采样时间别超过0.1秒。

2.3 约束条件怎么给才不算拍脑袋

MPC的约束条件输入是复现时最容易“画蛇添足”的地方。我看到有些复现版本,把四旋翼的姿态角约束设到正负30度,又把控制量限幅设到10牛,结果求解器绝大多数时间都在边界上挣扎,跟踪性能反而退化了。约束不是越多越好,它是在安全性、执行器物理极限和控制性能之间做取舍。

对四旋翼来说,比较合理的约束组合是:控制量(总推力)下限确保不低于维持悬停所需推力的百分之七十,上限不超过悬停推力的百分之一百三十;姿态角约束可以放宽到正负四十五度左右,因为控制器本身的代价函数已经会让姿态尽量小,约束是兜底用的,不需要设得很激进。

对机器人汽车来说,前轮转角是核心约束。民用车辆的转向角一般不超过正负30度到40度,复现时建议设在正负30度左右,这样既能体现约束的辨识度,又不会让轨迹规划结果太难看。车速约束要区分是什么场景——停车场场景限速低,园区道路场景限速高一些。注意,约束值要写成参数,放在文件顶部统一设置,不要散落在各个函数里,否则后面换场景调参时你会找得抓狂。

3. 算法框架与Matlab代码结构

3.1 整体流程:离线训练、在线优化、反馈校正

整个项目跑起来,流程分为三个大阶段。

第一个阶段是离线数据生成。你需要先设计若干条参考轨迹,这些轨迹应该覆盖不同类型的工况:直线段、圆弧段、S弯道、快速回旋、甚至带高度变化的俯仰机动。然后在每个初始状态下,用标准的NMPC控制器去闭环仿真,并记录每一步的“状态-最优控制量”对。这样得到的数据集,是后面训练神经网络的原料。

第二阶段是神经网络训练。把“状态”作为网络输入,“最优控制量”作为标签,训练一个多层前馈网络。这里的关键技巧是:数据归一化。状态量的量纲差异非常大——位置可能超过10,角速度可能只有0.1,如果不做归一化,网络训练很容易出现梯度爆炸或者收敛到局部极小值。我在Matlab里用mapminmax函数做归一化,这个函数会保存归一化参数,部署阶段要用同一套参数对输入做变换,别在训练和部署之间搞两套标准。

第三阶段是在线仿真。在这个阶段,被控对象依然是精确模型(也就是非线性动力学方程),但控制器已经换成了训练好的神经网络。神经网络输出的控制量直接作用于被控对象,MPC求解器退居二线,只作为对比基准存在。有些版本会加上反馈校正环节——神经网络控制输出后,再把真实状态反馈给网络作为下一拍的输入,这就是闭环反馈,和纯前馈补丁式控制的区别所在。

3.2 核心函数模块与数据流

一个干净的Matlab工程目录,应该至少包含以下模块:

  • main_sim.m:总调度脚本,负责选择被控对象(四旋翼还是汽车)、加载训练数据、调用在线仿真线程。
  • system_four_rotor_dynamics.m:四旋翼动力学函数,输入是状态和控制量,输出是状态导数。
  • system_car_dynamics.m:机器人汽车的动力学函数。
  • mpc_solver_fmincon.m:基于fmincon求解NMPC优化问题的函数。
  • nn_controller_predict.m:加载训练好的神经网络,并根据当前状态计算控制量的函数。
  • reference_trajectory_generator.m:参考轨迹生成器,可以生成圆形、8字形、阶跃序列等多种轨迹。
  • train_nn_controller.m:离线数据训练脚本。
  • plot_results.m:绘制跟踪曲线和控制曲线的可视化脚本。

数据流的方向很关键。在离线阶段,数据是从MPC求解器流向神经网络的;在线阶段的数据流则变成了“被控对象状态→神经网络→控制量→被控对象”。如果你在改代码时发现数据流方向反了,那一定是步骤顺序搞错了,这比任何bug都难排查。

3.3 代价函数、约束矩阵与求解器选择

MPC代价函数的标准形式是:

对四旋翼来说,状态误差权重应该重点放在水平和垂直位置分量上,姿态角权重视情况次之;对机器人汽车来说,横向位置误差和航向角误差的比例决定了路径跟踪风格——如果你想让它精确走线,横向权重就调大;如果想让它走得更平滑,航向角权重就调大。

求解器选择方面,fmincon是Matlab里最稳妥的选择,内置的sqp算法对中等规模的NMPC问题表现不错。你的预测时域如果取10步,状态维度是12,控制维度是4,那优化变量总数只有40个,sqp足够应付。除非你的时域再翻倍或者增加了额外的状态不等式约束,那才需要考虑用fmincon的interior-point算法。注意,interior-point对初始点更敏感,有时候需要先跑一版短时域的SQP结果作为热启动。

4. 复现跑通全程:从零到跟踪曲线

4.1 环境准备与版本坑

Matlab版本上,我用的R2021a之前的老版本都能跑通。只需要注意两点:第一,fmincon不需要额外工具箱,但如果你用的Matlab版本过于老旧(比如R2016a以下),optimoptions的语法会有差异;第二,神经网络工具箱是必需品,用feedforwardnet和train命令,如果没有安装这个工具箱,网络训练部分会直接报错。

工程路径最好不要出现中文。Matlab处理中文字符串在一些老版本上会出编码问题,尤其是写文件、打印日志的时候,有时候报错信息都看不懂,浪费好几个小时。项目文件夹全部用英文字母命名,注释可以用中文,但不推荐——除非你和团队其他人确认过编码一致。

4.2 参考轨迹生成

参考轨迹是影响复现效果的第一大变量。我看到很多人随便画一条正弦波就当参考轨迹,结果MPC跟踪不理想,还以为是算法出了问题。问题往往出在参考轨迹本身:如果参考轨迹包含过高频率的分量,超出了被控系统闭环带宽,那控制器是无论如何也追不上的——它不是不听话,是物理上不允许。

我建议参考轨迹这样生成:机器人汽车先试圆形轨迹,半径控制在10到20米范围,车速恒定2到3米每秒。这个工况的跟踪误差比较容易收敛,适合验证算法是否正确。四旋翼先试三维的螺旋上升轨迹,高度从5米慢慢升到15米,盘旋半径保持在10米左右。这种轨迹包含位置、速度和姿态的平缓变化,能把系统的多个耦合通道都调动起来。

更复杂的8字轨迹建议放在后面对比实验里再加。8字形轨迹在交叉点附近横向加速度变化剧烈,对MPC的预测时域和约束处理都是更严的考验。

4.3 训练NN:数据采集、归一化与网络结构

数据采集阶段,建议每条参考轨迹闭环跑三遍,每次从不同初始状态开始。初始状态不应该全取零,要故意加一些随机偏移——比如位置偏移0.5米,航向角偏移5度。这样采集到的数据才覆盖更广的状态空间,训练出来的网络泛化能力才够。

归一化时有个细节:不只是输入要归一化,输出(控制量)最好也归一化到-1到1之间,或者至少做一个零均值化。控制量的量纲虽然固定,但不同被控对象的控制量范围差异很大——四旋翼的推力可能是10牛量级,汽车的前轮转角是0.5弧度量级,如果不归一化到同一个尺度,网络权重的初始化会很不均衡。

网络结构建议用两到三层的全连接网络,隐藏层神经元数量取32到64就足够。别一上来就堆上百个神经元——这个项目里的输入输出维度并不高,过大的网络只会导致过拟合,离线训练误差很低,在线仿真的泛化能力却会变差。激活函数用tansig(双曲正切)比较合适,输出层的激活函数用purelin(线性),因为控制量需要连续变化,不会被截断在某个区间。

训练的学习率我设成0.001到0.01之间,几千轮迭代后损失就能降到比较低的水平。跑的时候用trainscg(缩放共轭梯度法)比默认的trainlm(Levenberg-Marquardt)在内存占用和收敛稳定性上更友好。

4.4 主程序调度与结果显示

主调度脚本建议这样组织:

% 主脚本:选择系统并运行仿真 clear; clc; close all; % 参数配置:切换到汽车系统 system_choice = 'car'; % 'car' 或 'quad' Np = 15; % 预测时域 dt = 0.1; % 采样时间(秒) ref_type = 'circle'; % 轨迹类型 % 加载训练好的神经网络控制器 load('trained_nn.mat'); % 初始化状态 x0 = [-8; 0; 0.2]; % 根据系统设置 % 运行仿真 [t, x_sim, u_sim] = run_simulation(system_choice, x0, Np, dt, ref_type, net); % 绘制结果 plot_results(t, x_sim, u_sim, ref_type);

这段代码的逻辑很清楚:先配置,再加载,后仿真,最后画图。run_simulation内部会根据system_choice调用对应的动力学函数和控制器函数——可以是NMPC模式(调用fmincon)也可以是NN模式(调用net),两种模式跑出来的结果直接对比,方便你评估NN替代MPC造成的性能损失。

5. 实测中的五个坑与对应解决方案

5.1 坑一:求解器初值不给就死循环

复现NMPC时最典型的坑就是fmincon的初始解x0。如果你不给初始解,求解器会默认从零向量开始,对于某些状态和参考轨迹的组合,零向量刚好落在不可行域附近,fmincon在里面绕了好几圈都找不到可行解,表现为仿真卡住不动,或者每一拍的求解时间越来越长,最后像死循环一样。

解决办法是“热启动”:把上一采样时刻求得的最优控制序列作为当前时刻求解的初始解。因为相邻两个采样时刻的系统状态变化不大,上一时刻的最优解在当前时刻依然是接近最优的可行点。在多步预测的MPC里,甚至可以做一个“移位初始化”——把上一时刻的控制序列去掉第一个值,末尾补上最后一个值,作为新时段的初始值,这样效果更好。

5.2 坑二:NN输出与物理量纲对不上

神经网络训练时的输出是归一化后的量,部署时直接把它塞给被控对象,会出现一种诡异的现象:控制量序列曲线看起来完全正常,但系统状态却在几分钟内慢慢漂移出界。这时候十有八九是忘记做输出反归一化。Matlab的mapminmax函数会在训练时返回归一化参数,但很多人训练完只保存了net,没保存归一化参数,部署阶段等于裸奔。

正确做法是把归一化和反归一化的矩阵连同网络一起打包保存。我就是用了save('trained_nn.mat', 'net', 'input_ps', 'output_ps')把输入归一化参数和输出归一化参数一并存下来,部署时先对状态做mapminmax('apply', x, input_ps),再用mapminmax('reverse', y, output_ps)还原控制量。这个步骤看着琐碎,但少了它,整个在线阶段就是一场灾难。

5.3 坑三:预测时域Np太大导致rollout计算爆炸

MPC的预测时域Np决定了优化变量数量。Np=10时,控制变量只有40个;Np=30时,四旋翼系统的优化变量就跑到120个以上。fmincon在求解这种规模的非线性优化问题时,每拍求解时间可能从几十毫秒跳到几百毫秒甚至更久。而仿真系统中的采样周期一旦小于求解时间,整个实时性就崩了。

但这个项目好在主结构是离线训练网络,NMPC只用来生成训练数据。所以在离线数据生成阶段,Np稍微大一点也没关系——跑得慢就慢一点,反正是一次性的。真正要注意的是:如果你把Np加得过大,会让离线数据生成变得极其耗时,这时候应该先跑短时域的小批量数据验证整个管线是否连通,确认没有报错后再放开Np跑全量数据。我的经验是Np取10到20,在这个范围内精度和耗时平衡最好。

5.4 坑四:不同对象用同一套权重表现失衡

四旋翼和机器人汽车是完全不同的动态系统,但在复现项目里,有些人图省事,把四旋翼的权重矩阵直接套到汽车上。结果汽车路径跟踪的横向误差很大,四旋翼的跟踪倒是很好——原因很简单,两套系统的物理单位不一样。四旋翼位置误差的米数对应着推力输出,汽车位置误差的米数对应着前轮转角输出,两个通道的增益差异差了一个数量级。

解决办法是每个系统单独调权重。一个粗略的起点:四旋翼的位置误差权重可以设置得高一些(比如10到50),姿态权重低一些(1到5);机器人汽车的位置误差权重中等(5到10),航向角误差权重也设相近,因为两者在路径跟踪里的重要度相当。实际跑出来看看跟踪曲线,再微调权重,不要指望一套参数通吃两个系统。

5.5 坑五:训练数据分布偏移导致NN泛化崩溃

这是最隐蔽的坑。训练数据来自MPC闭环仿真,也就是说,数据中的“状态”都是MPC控制下的状态轨迹。MPC一直把系统控制在参考轨迹附近,所以采集到的状态都集中在正常运行的区域内。但在在线NN控制阶段,如果NN的控制效果不如MPC,系统可能跑到MPC从未去过的大偏移区域,这时候NN收到的输入分布超出了训练分布,输出就会完全跑偏——这就是数据分布偏移。

缓解办法有两个。第一,在离线数据采集时故意加入随机初始状态偏差,让MPC从偏离的初始状态往回救,这样产生的状态覆盖范围更大;第二,训练数据里加入少量“失控”数据——比如在一段时间内故意使用较大的外部扰动,让状态偏移一段距离,再让MPC把系统救回来。这样NN见过更多“不那么理想”的输入分布,在线运行时就不至于两眼一抹黑。

6. 效果评估方法:别只盯着误差曲线

6.1 三类必看指标

复现出结果不代表复现成功,你还要判断这套NN+MPC“替代”得怎么样。我建议至少输出三类指标。

第一类是跟踪误差指标。位置误差的均方根误差(RMSE)是最直接的,四旋翼分成水平面和高度通道分别统计,汽车分成横向和纵向通道统计。RMSE降到多少算合格?如果你用NN控制器跟踪圆形轨迹,最终RMSE不超过同场景NMPC的1.2倍,那就算优秀了。

第二类是控制量质量指标。控制量的变化太猛,说明控制器在抖,执行器寿命会受影响。统计控制量序列的方差,或者更直观看控制量曲线的毛刺程度。NN控制器最大的优点是单拍输出很快,但如果训练不到位,它输出的控制量序列可能比NMPC更平滑也可能更粗糙,这部分要靠观察控制曲线来判断。

第三类是计算耗时指标。每拍控制量的计算时间要单独计时。NMPC的单拍耗时通常是几百毫秒量级,而NN前向传播在普通电脑上是微秒到毫秒量级。把这个时间差量化出来,是这个项目的核心卖点之一——同样的控制性能,计算资源少了几个数量级。

6.2 结果对比与定量分析

我测试跑完的典型结果大概是这样的:四旋翼螺旋上升轨迹跟踪,NMPC的RMSE大约在0.25米,NN控制在训练充分时RMSE在0.3米左右,性能损失约百分之二十,而计算时间从每一拍150毫秒降到2毫秒——这个交换就非常划算了。机器人汽车圆形轨迹跟踪那组对照实验,两个控制器的RMSE几乎没差别,因为汽车动力学相对温和,神经网络拟合得更准,但计算时间差了接近五十倍。

看到这样的对比结果,你可以说这套框架有实际工程意义:不是所有人都用得起点对点的在线NMPC,但训练一个神经网络来近似它的策略,部署到低功耗计算平台上,就能在很多场景下获得接近NMPC的性能。这也是为什么你会看到相关论文多半会把“计算时间降低”作为主要卖点之一,而不是只强调跟踪精度。

6.3 扩展思路:从离线NN到在线自适应

这套项目如果站稳了基本复现,还可以继续往上做——在线自适应就是很自然的延伸方向。我的思路是:离线先训练一个基础版本的NN控制器,覆盖常规工况;在线运行时,每隔若干个采样周期计算一次跟踪误差的滑动平均值;如果误差持续超标,说明系统进入了离线数据没覆盖的工况,此时启动NMPC求解器,做少量在线滚动优化,再用在线优化生成的这批新数据微调神经网络权重。

这种做法相当于给NN控制器装了一个“备胎”——正常情况下神经网络单飞,遇到异常时MPC接管并重新校准网络。这个思路可以把NN+MPC从单纯的计算加速工具升级成一套具备在线学习能力的控制框架。项目里的代码结构已经预留了接口:mpc_solver_fmincon.m可以单独调用,训练脚本可以增量训练网络,唯一缺的是异常检测模块,这可以是后续扩展的切入口。

在实际操作中我最大的体会是:不要一开始就试图把两个系统都跑完美,先把汽车系统从头到尾跑通,把神经网络训练、部署、对比这套循环建立起来。只要一条链路通了,四旋翼只是在模型函数和参数配置上再做一轮替换而已——两套系统的代码结构在最高层面完全一致,这种“先通吃一个对象,再横向复制”的思路,比同时调两个系统的效率高一倍不止。

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

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

立即咨询