产品实际发布的源码工程数量比较大,我这个人在拿到“含Matlab源码 15055期”的工程时,其实一开始是有点懵的。因为无人机系统制导与导航控制这个题目,听起来是三个方向,但真做起来是一整条链路——传感器数据进来,先解决“我在哪”,再解决“我往哪走”,最后解决“怎么走过去”。这篇文档就把我复现这套源码时积累的经验写出来,从模型搭建、算法落地到调试排坑,全都摊开讲,适合正在用MATLAB做飞控仿真、毕设导航制导、或者从零搭四旋翼控制系统的朋友。
1. 我为什么建议先搞懂“导航、制导、控制”这三件事,再开MATLAB
很多人拿到舵机、电机、飞控板就直接开干,结果往往卡在同一个地方:飞机飞了,但不知道飞去哪;代码改了,但不知道改的是哪一层。我自己的体会是,制导、导航、控制虽然互相耦合,但在逻辑上必须分得清清楚楚,否则连报错都看不懂。
1.1 导航:先在三维空间里把自己“定位”明白
导航层的任务只有一个:估计无人机当前的状态。这个状态包括位置、速度、姿态角、角速度,有时候还有加速度计偏置、陀螺仪漂移这些“隐藏状态”。在MATLAB里,这层对应的是状态估计器,比如扩展卡尔曼滤波(EKF)、互补滤波、或者基于惯性导航+GPS的松耦合组合导航。
举个实际场景:四旋翼在空中悬停,GPS飘了半米,气压计又飘了半米,那到底以谁为准?导航层不是简单取平均值,而是根据传感器噪声模型给不同数据分配权重。我在源码工程里看到,它用了误差状态卡尔曼滤波(ESKF),把姿态误差、速度误差、位置误差当作状态量,这种方式在机载处理器上计算量可控,而且数值稳定性比标准EKF好不少。如果你只是做仿真,用MATLAB自带的惯性传感器融合函数也能跑通,但要真移植到嵌入式平台,建议还是按这套误差状态的思路来写。
1.2 制导:决定该往哪里飞、怎么走才是合理的
制导层的输入是导航层给出的当前位置、速度,输出是期望的加速度或期望航向。它不直接管电机转速,只管“怎么飞过去更合理”。常见的研究点包括:航点序列生成、Dubins曲线平滑、B样条轨迹、纯追踪制导律、L1引导律,以及带避障的RRT路径规划。
我在这套源码里重点复现的是L1引导律。它跟传统的纯追踪不一样的地方在于:纯追踪是盯着目标点,横向加速度直接和视距误差成正比;而L1引导律是在飞行轨迹前方选一个参考点,像开车时看前方某个固定距离的点一样,通过控制横向加速度让这个点和期望路径之间的几何关系收敛。这样在风速突变或者路径拐弯时,跟踪不会出现那种“抽筋式”的剧烈修正。
1.3 控制:最后一步把指令变成舵面和油门响应
控制层接收制导层给出的期望姿态或期望加速度,再解算到电机转速或舵面偏转。对四旋翼来说,这层一般是一个串级PID:外环控制位置,输出期望姿态角;内环控制姿态角,输出期望力矩;最后通过混控器分配四个电机的PWM。
我在调试中最大的心得是:不要把内环和外环混在一起整定。内环是快回路,周期2kHz都有做;外环是慢回路,50Hz到100Hz就够。如果你把内外环放在同一个频率下一股脑调,参数会互相“打架”,外环觉得响应慢了,内环觉得振荡了,最后只能调出一个两边将就的平庸结果。
2. 从空模型到可仿真:环境准备与四旋翼模型搭建
在开始写算法前,得先有一个能“骗过”控制器的被控对象——也就是无人机本体模型。很多初学者会直接找一个Simulink示例跑起来,然后替换自己的参数,结果换了质量、惯量之后,飞机直接翻跟头,却找不到原因。这是因为对模型结构没底。我建议按下面的顺序从零搭一遍。
2.1 开发环境配置:MATLAB工具箱和Aerospace Blockset怎么选
MATLAB做无人机仿真,需要用到的工具箱其实没想象中多,核心就几样:Control System Toolbox、Aerospace Toolbox、Sensor Fusion and Tracking Toolbox、Simulink。如果做视觉相关的任务,还要加Computer Vision Toolbox;如果做精细动力学模型,可以上Simscape Multibody。
很多人在这一步会纠结:Aerospace Blockset里的六自由度模型,和我自己写微分方程,逼真度差多少?我的看法是:前期算法验证阶段,直接用Aerospace Blockset的点质量模型或六自由度模型就能满足需求;中期做控制律验证,再考虑加入旋翼动态和风场扰动;后期要做硬件在环,就必须自己写被控对象或把真实飞行数据喂进去。
15055期源码工程里,主体用的是Matlab脚本搭建的六自由度运动学模型,配合Simulink做控制环,没有依赖非常重的Simscape库,这样好处是运行快、方便单步调试,坏处是气动耦合、电机动态这些都没建模。对于制导导航控制算法的验证来说,这个简化程度是够用的。
2.2 机体系下的运动学与动力学方程
不管用哪个工具箱,四旋翼的运动学方程逃不过这三组:位置微分方程、姿态微分方程、角速度动力学方程。我这里给出一个常用的模型框架,参数用中型的四旋翼示意值,方便对照代入。
% 四旋翼物理参数(单位均为国际单位制) m = 1.25; % 质量 1.25 kg g = 9.81; % 重力加速度 Ixx = 0.0085; % x轴转动惯量 Iyy = 0.0085; % y轴转动惯量 Izz = 0.0161; % z轴转动惯量 d = 0.225; % 电机到质心距离位置微分相对简单:机体系速度经过旋转矩阵转到地面系,再积分得到位置。姿态微分这里要特别提醒,很多同学会直接用欧拉角求导公式,但那个公式在俯仰角接近90度时会出现奇异。工程上更稳妥的做法是用四元数来更新姿态:
% 四元数姿态更新(简化示意) % q为四元数向量 [q0 q1 q2 q3],omega为机体角速度 function q_dot = quat_derivative(q, omega) w = omega; Omega = [0 -w(1) -w(2) -w(3); w(1) 0 w(3) -w(2); w(2) -w(3) 0 w(1); w(3) w(2) -w(1) 0]; q_dot = 0.5 * Omega * q; end动力学方程部分,力和力矩的来源要理清楚:总升力沿机体z轴方向,重力沿地面系z轴负方向,力矩由四个电机的转速差产生。把这些写完后,状态输出给制导控制环,模型就算闭环了。
2.3 Simulink里的模块化建模思路
我不会把整个模型都塞进一个Simulink窗口中。更合理的做法是分成四个模块:传感器模块、导航模块、制导模块、控制模块。每个模块用单独的子系统封装,接口明确后,调试的时候可以单独看某个子系统的信号流。
15055期源码里有一个细节让我挺欣赏:传感器模块里的噪声模型,用的是随机游走加高斯白噪声,而不是简单加一个常数偏移。这更接近真实IMU的特性,因为陀螺仪和加速度计的偏置其实是在缓慢变动的。如果你的传感器噪声只加一个固定偏置,那后面写的EKF滤波基本是在“陪太子读书”,根本考验不到滤波器的跟踪能力。
3. 导弹式制导律转到无人机航点跟踪:核心算法落地
制导导航控制链路里,制导算法是最容易“看起来懂了、写起来懵”的部分。这里用航点跟踪作为场景,把从航点生成到L1引导律实现再到串级控制的完整过程拆开讲。
3.1 航点生成与路径平滑
任务层给了一串航点之后,不能直接拿去做引导。因为航点是离散的、折线式的,直接用折线去引导飞机会在拐角处产生很大的横向过载,姿态控制环很容易饱和。我在这套源码里看到它用了三次样条插值对航点进行平滑,插值后得到一条连续可微的期望路径。
平滑路径之后,还要解决“路径参数化”问题。所谓参数化,就是在这条连续路径上定义一个标量,比如累计弧长,引导律通过跟踪这个标量来找到参考点。MATLAB里做参数化可以用内置的cscvn或者csape函数,计算每个航点之间的弧长,再按弧长比例分配,这样即使航点间距不均,路径跟踪也不会卡顿。
3.2 L1引导律在水平通道的实现
L1引导律的核心思路并不复杂:在目标轨迹上取一个前方距离为L1的点,计算飞行器到这个参考点的侧向偏差,然后输出一个横向加速度指令,让飞行器朝这个参考点“拉近距离”。
function lat_accel = L1_guidance(pos, vel, path_points, L1_distance) % 找到路径上距离当前位置约为L1的参考点 ref = find_reference_point(pos, path_points, L1_distance); % 计算位置误差向量和参考点方向 eta = atan2(ref(2) - pos(2), ref(1) - pos(1)); chi = atan2(vel(2), vel(1)); % 视线角误差和横向加速度指令 dPsi = wrapToPi(eta - chi); lat_accel = 2 * vel(1)^2 / L1_distance * sin(dPsi); end这个算法里有两个变量很关键:L1距离和飞行速度。L1太短,跟踪会比较激进,容易振荡;L1太长,跟导性变差,切弯时会明显“抄近道”。我在仿真里试过,速度3m/s时,L1取8到12米比较舒服。如果是变速度飞行的无人机,L1还要随速度自适应调整,否则会出现在高速段绕大圈的问题。
3.3 内环姿态控制器与外环位置控制器的串级设计
制导层输出的横向加速度和期望高度,需要转换成姿态指令再进内环。外环位置控制的公式可以写成:
% 外环位置控制:位置偏差 -> 期望姿态角 phi_cmd = atan2(ay_des, g); % 期望滚转角 theta_cmd = atan2(ax_des, g); % 期望俯仰角内环姿态控制器,我建议用四元数形式的串级PID,避免欧拉角在机体系下更新时出现奇异。姿态误差用四元数乘法计算,而不是直接相减,这对于大角度机动尤其重要。15055期源码里,内环控制器还加了一个前馈项:期望角速度前馈到角速度环,这样姿态跟踪响应会快不少,且超调更小。
这里有一个容易被忽视的细节:混控器。姿态控制器输出的是期望力矩,但无人机执行机构是四个电机,力矩和升力之间需要通过混控矩阵分配。如果你直接跳过混控器把PID输出当成电机指令,那就等于把飞机的轴间耦合全丢掉了,飞起来一定会出现“推油门就偏航”等奇怪现象。
4. 仿真中容易翻车的五个位置,附排查链路
我复现15055期源码时并不是一帆风顺的,中间碰到过好几个非常隐蔽的问题,每一个都浪费了我不少时间。这里把最具代表性的五个坑列出来,每一个都附上排查链路,方便你们对照自己的工程。
4.1 坐标系的坑:NED、ENU、机体系混用
这个坑出现的概率极高。有些算法模块内部用的是北东地(NED),有些用的是东北天(ENU),如果两个坐标系没有明确标注接口,一旦螺旋桨转起来,飞机会以一种“看似合理但方向完全错误”的方式飞出去。排查方法是:在模型里给一个只沿x轴飞行的指令,分别在NED和ENU下打印位置曲线,观察趋势方向。一旦发现某个通道方向相反,就需要在旋转矩阵或者坐标变换处加负号。
4.2 仿真步长与控制周期的失配
MATLAB仿真默认情况下,Simulink的连续动力学模块用的是变步长求解器,而控制器代码常常是按照固定周期写的。如果两种步长不匹配,会出现“控制器在一个周期里重复执行了两次”或“动力学推进了两次但控制器没跟上”的情况。最明显的表现是:姿态曲线看起来在快速振荡,但振荡频率似乎和控制器采样时间没有固定关系。
我的建议是:在Simulink里把控制器子系统设置为离散采样时间,显式指定周期,比如0.01秒;被控对象动力学可以继续用连续求解器,但最大步长也别太大,否则数值积分误差会直接毁掉仿真结果。
4.3 传感器噪声没建模,算法一到真机就“虚”
这是制导导航控制项目最典型的问题:仿真里滤波器收敛看起来完美,一上真机立刻发散。原因往往是仿真里只用了理想真值,或者只加了一个很小的白噪声。15055期源码里的传感器模型做得比较细,但我发现很多人拿这个源码之后会为了“跑得快”把噪声模块删掉,然后发现系统真的仿真得很好——那其实是在骗自己。我建议至少保留量测噪声,哪怕幅度比实际情况稍大一点都没关系,这样才能逼着导航滤波器和控制律去适应噪声。
4.4 PID参数在仿真里很好,换一架无人机就崩
参数跟模型强相关,这一步不算玄学,但很多人会忽略:PID增益的量纲和物理量纲是要对应的。同一个PID参数,在一架0.5kg的小四旋翼上很稳,换到2.5kg的六轴上就会失稳,这不仅仅因为质量变了,还因为力矩、转动惯量、甚至电机响应速度都变了。排查方法很直接:记录换飞机前后同一指令下的实测响应带宽,按带宽比例缩放PID增益。仿真里做参数扫描时,可以用sim脚本配合parfor批量跑,很快就知道哪些参数组合在物理上是可解释的。
4.5 结果显示:轨迹图画出来不对时怎么检查
模拟完成后,我常常习惯先画三张图:三维轨迹图、高度随时间变化图、以及制导指令随时间变化图。三维轨迹图能最快看出整体趋势,但细节问题,比如某个轴方向的周期性振荡,在三维图上是看不出来的。所以调试时我基本先看指令-响应的对比图,再看轨迹图。如果二者结论矛盾,优先怀疑坐标变换和单位换算,而不是先改PID参数。
5. 把15055期源码工程跑通后,我的实操体会
整套源码工程跑下来,我最大的收获并不是某个算法有多么先进,而是它把“从模型到算法再到仿真验证”的完整链路打通了。下面这两点是我觉得最值得二次利用的地方。
5.1 源码里值得复用的部分
第一个是导航滤波框架。源码里的误差状态卡尔曼滤波结构非常清晰,状态预测、量测更新、误差注入是分开写的,这种模块化让后续接入新的传感器变得非常简单。比如你要加一个摄像头视觉定位,只需要新增一个量测函数,不用动整个滤波器。
第二个是制导接口设计。制导模块的输出被统一抽象为“期望横向加速度、期望纵向速度、期望高度变化率”,而不是直接输出期望姿态角。这个设计的好处是制导算法和具体机型解耦:你换一架固定翼飞机,控制内环变了,但制导算法几乎不用动。这是很多自写代码里很难做到的一点。
第三个我比较欣赏的是参数集管理方式。源码用了多个结构体来存物理参数、控制参数、导航参数,而不是散落在一堆全局变量里。你在仿真里做参数扫描时,只需要修改结构体字段,脚本层面完全不用动,这是非常省心的习惯。
5.2 从仿真往实物迁移前要做的事
如果你打算把这套仿真算法迁移到真机,我有几条亲测经验:
- 先把仿真里的传感器噪声调到实测噪声水平以上20%,再重新整定一次导航滤波器参数,记录下这时候的估计协方差,这是后续调真机的重要参考。
- 控制器频率在仿真里可以随便设,但真机上必须与姿态估计器的输出频率对齐。建议把姿态控制频率设为IMU输出频率的二分之一以内,避免控制环路里出现多个采样率混叠。
- 真机飞行前,用MATLAB里的
flight log analyzer之类工具,把仿真输出和飞控日志放到同一张图上对比。只要曲线形状接近,实物调试就会快很多;如果差异大,问题多半在模型参数,而不是控制算法。
我后来在这个源码基础上扩展了避障航点重规划和风场扰动补偿,花了不到一周就调通了。底子打得好,后续做扩展真的会轻松很多。