ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

四旋翼无人机动力学、控制与路径规划:MATLAB工程实践解析

四旋翼无人机动力学、控制与路径规划:MATLAB工程实践解析 简介本资源面向控制工程、机器人学及无人机方向的本科生与研究生聚焦四旋翼飞行器建模、控制律设计与自主路径规划三大核心问题提供一套完整可运行的MATLAB仿真学习方案。压缩包共52个文件含45个.m主程序与函数脚本涵盖动力学建模、PID/非线性控制器实现、势场法路径规划、B样条轨迹生成、运动剖面优化等模块、6张结果可视化PNG图含姿态响应、轨迹跟踪、障碍规避效果等以及1份详细说明README.md文档整体仅276KB轻量易部署。已有120人学习下载代码结构清晰分层startup.m统一初始化main.m驱动全流程potential_field子目录封装势场算法trajectory_generation与path_planning等文件夹体现功能模块化设计便于读者按需调试、对比不同控制策略或路径规划方法的性能差异。 我自己也记不清到底解压过多少个“四旋翼动力学、控制、路径规划附matlab代码”这种名字的压缩包了。老实说这类代码包在网上一搜一大把但绝大多数人拿到的第一反应是先跑demo看到几个彩色曲线和无人机动画在MATLAB里转起来新鲜感一过就关掉了。至于里面的动力学方程为什么长那样、控制器为什么分内外环、路径规划算法到底是怎么把一条几何路径变成无人机真正能飞的轨迹很少有人能讲清楚。这篇文章就是来把这条线讲透的。我会按四旋翼技术栈里最核心的三块——动力学建模、控制策略、路径规划——逐一拆开再结合我实际用MATLAB调仿真、改代码、往实机上猜参数的经验给你一条能真正复现、能自己改着玩的技术主线。内容适合两类人一类是刚入门、手里攥着某个四旋翼代码包却不知道怎么下手的同学另一类是已经跑通过demo、想更进一步理解每个模块为什么这样设计、遇到问题知道去哪里排查的研究者或工程师。1. 动力学建模先把坐标系、方程和常见的坑说清楚1.1 坐标系和旋转关系是第一个分水岭四旋翼动力学里第一个让很多人懵掉的就是坐标系。这玩意儿不搞清楚后面你无论写控制还是看代码都会觉得变量满天飞、不知道谁在哪个系里表达。常用的惯性坐标系是NED北东地或者ENU东北天我见过的很多MATLAB四旋翼示例用NED因为航空界习惯把地面系定义为“北东地”这跟飞行器仪表的方向定义天然一致。机体坐标系固定于无人机中心x轴通常指向机头方向y轴指向右翼z轴朝下NED约定下。这样定义的好处是当无人机水平悬停时姿态角的初始值都是0很多公式写起来清爽。从惯性系到机体系的旋转可以用欧拉角滚转φ、俯仰θ、偏航ψ、旋转矩阵或者四元数来表达。MATLAB代码包里我看到的大多数做法是状态量里用四元数做积分避免万向锁观测/控制输出时再转成欧拉角人看着直观。这是个很务实的工程习惯——如果你在纯欧拉角下做全姿态仿真当俯仰角跑到±90度附近时旋转矩阵会出现奇异性表现为某个角速度分量突然溢出轨迹瞬间飞掉。很多人跑仿真遇到“无人机突然翻滚爆炸”源头往往是这里而不是控制器写错了。在MATLAB里可以用自带的quaternion类q quaternion([psi theta phi], eul, ZYX, frame);不过要注意quaternion默认的欧拉角顺序、旋转约定可能跟你代码包里的模型不一致。如果代码里手写了旋转矩阵我强烈建议你打开那几行自己推一遍并注释清楚哪个矩阵是“惯性到机体”哪个是“机体到惯性”。这两个方向反了控制量会因为一个转置的错误而完全反相实际上等于把负反馈变成了正反馈仿真里飞机会直接发散。1.2 牛顿-欧拉方程每一项的物理含义都不一样四旋翼的刚体动力学本质上就是牛顿方程和欧拉方程联立。线运动方程质量乘以加速度等于合力在惯性系下的投影。合力主要包括旋翼总推力沿机体轴方向、重力、空气阻力。角运动方程转动惯量矩阵乘以角加速度等于外力矩减去角速度叉乘转动惯量乘角速度这一项也就是陀螺耦合项。很多简化模型里只保留了线运动方程把四旋翼当成一个质点角运动则偷懒不写。这在小角度、慢速飞行下还能凑合但一旦做大机动、快速转弯或者偏航与俯仰同时作用忽略耦合就会导致控制效果明显变差。关于力与力矩的表达有一个从执行器到机体的映射非常关键。四个旋翼转动会产生四个沿机体z轴的推力F_i k_f * ω_i^2与此同时每个旋翼还有反扭矩M_i k_m * ω_i^2。机体的总推力是四个推力之和滚转力矩来自左右两侧旋翼推力差乘以力臂俯仰力矩来自前后两侧旋翼推力差乘以力臂偏航力矩则由对角旋翼的反扭矩差产生。这组映射关系在代码包中通常体现为一个“混控矩阵”mixing matrix把期望的[总推力, 滚转力矩, 俯仰力矩, 偏航力矩]转换到四个电机的期望转速平方。这里我要特别提醒一个容易被漏掉的项螺旋桨陀螺力矩。当无人机本体有角速度、四个螺旋桨本身又在高速旋转时会把螺旋桨的角动量通过陀螺效应耦合到机体上。公式里常见的形式是tau_gyro -J_rotor * (omega_rotor_total × omega_body)其中omega_rotor_total是四个旋翼转速的代数和考虑正反转方向。仿真中如果电机转速很高或者机体角速度很大这一项对偏航通道的影响非常显著。很多代码包不一定包含它这没关系但你要知道如果你后面要上真机或者做高速航线规划这个力矩是会实际存在的。1.3 建模时最常见的四个坑我逐个踩过第一把电机推力当成常数或线性函数。实际上推力与转速的平方成正比所以在代码里如果直接对电机控制量取线性映射飞行器响应会跟预期差很多。正确做法是控制量输出转速或转速平方再用平方关系给推力。第二忘记转动惯量不是标量。虽然四旋翼对称结构下可以把转动惯量矩阵近似成对角阵但三个通道的转动惯量数值并不相等。Ixx滚转轴通常比Iyy俯仰轴略大或略小取决于机架是“X型”还是“十字型”。而且飞行器带负载后转动惯量会变同一组控制参数在不同负载下的表现可能天差地别。第三电机动态被当成瞬时环节。真实电机从指令到转速变化有一个响应过程通常可以用一阶惯性环节近似时间常数在0.01到0.1秒之间。如果你的控制器工作频率是几百赫兹而电机时间常数很大控制器会觉得“发了指令没反应”于是越调越大最后震荡。在MATLAB仿真里把电机模型加上一阶惯性哪怕时间常数只设0.02秒整个控制器调参的行为都会更接近实机。第四量纲和符号习惯不统一。同样是力矩有的代码用“机体坐标系中的力矩”有的代码用“在惯性系中表达”如果你的控制器输出不小心跟混控矩阵的输入对不上结果必然炸机。我处理过一个案例代码包里姿态控制器的输出量纲其实是“归一化PWM”但后面混控矩阵却默认输入是“力矩”导致整个仿真在悬停下就疯狂抖。2. 控制策略内外环PID的带宽匹配与调参路径2.1 为什么四旋翼控制几乎都长成“内外环”的样子四旋翼是一个欠驱动系统你只有四个旋翼的推力却要控制六个自由度位置三个、姿态三个。但这不意味着没法控制因为通过倾斜机体推力在水平方向的分量就可以产生水平加速度。因此工程上最经典的做法是把控制拆成“位置外环”和“姿态内环”。位置外环的输出不是“期望推力角度”而是“期望加速度”。比如水平方向期望位置是某个目标点外环PID根据位置误差计算出期望的水平加速度然后结合重力加速度反算出机体应该倾斜多少、总推力应该多大。姿态环再把这个期望姿态滚转、俯仰、偏航作为输入通过内环PID计算出三个轴的力矩。这个分层结构的好处是分工明确外环只负责“往哪飞”内环只负责“把飞机姿态稳住”。代码上你也会看到非常清晰的分层控制器函数输入位置和速度状态输出期望姿态角和油门姿态控制器接受期望姿态角和当前姿态输出力矩最后混控矩阵把力矩转成电机量。2.2 带宽匹配为什么内环必须比外环快内外环控制里最核心的工程原则是内环带宽必须远大于外环带宽通常至少3到5倍。说白了内环把飞机姿态稳住的速度要比外环指挥飞机移动的速度快得多否则外环刚给一个姿态修正指令内环还没反应过来整个控制就会发散。具体到参数上我自己常用的经验范围假设仿真步长1ms电机时间常数0.02s控制环典型带宽说明角速度内环8 - 15 rad/s决定姿态稳定速度和抗风扰能力姿态环3 - 6 rad/s将期望姿态角映射为角速度指令位置外环0.5 - 2 rad/s决定位置跟踪的响应速度不能调太快调参的顺序也必须是“由内到外”先把角速度环调稳再调姿态环最后才是位置环。如果直接调位置环的参数一旦飞行器姿态本身不稳位置环再努力也是白搭。举一个具体例子我调试一套代码包里的悬停控制时位置环P值是0.8姿态环P是4.0角速度环P是0.15、D是0.02。位置环P敢设0.8的原因是因为当时内环响应够快——姿态环能在0.2秒内修正一个10度的偏差外环才有余地去“慢慢指挥”。如果内环响应慢外环P值必须调小否则一个小的位置误差就会转换成很大的期望角度姿态环跟不上去系统开始大幅震荡。2.3 我在调参中踩过的重要坑积分、饱和与噪声积分环节在四旋翼控制里是两个极端用好了能消除静差用不好就是震荡之源。悬停时因为重力、重心偏移或风扰可能存在恒定偏差需要积分来补偿。但积分项一旦饱和比如无人机被按住不能动位置误差一直存在积分项一直累积松开后飞机会猛地往一个方向窜。这就是经典的积分饱和问题。我在仿真里经常看到的现象是无人机能稳定悬停十几秒但稍微给一个外力扰动再恢复时就开始低频振荡。原因就是积分项没有限制。解决办法有两类一是对积分项做限幅比如把积分输出限制在最大控制量的20%以内二是做“积分分离”误差大于某个阈值时暂停积分误差收敛后再重新启用。两种策略在代码里实现都很简单但效果差异巨大我强烈建议你把代码里的积分处理单独拎出来看一遍再决定要不要加。另一个坑是微分项对噪声的放大。真机上的陀螺仪和加速度计都有噪声如果你直接对测量值求微分控制信号会变得毛刺十足电机会发出“滋滋”的高频声音电池消耗也会变快。工程上更常见的做法是在微分项前面加低通滤波或者在期望值端做平滑微分先行避免目标突变引起微分冲击。仿真里可能表现不明显但一旦和真实传感器模型搭配你就会发现输出曲线远没有理想PID那么光滑。还有执行器饱和问题。每个电机转速有上下限混控矩阵算出来的期望转速可能在飞行中大范围超出限幅。如果不做限幅处理控一算出来是一个很大的力矩实际执行器却给不了飞机会按实际饱和后的响应走跟控制器的预期完全脱节。我看到不少代码包的demo里压根没有电机转速限幅这种仿真只能在很小角度范围里自嗨。3. 路径规划全局搜索、轨迹优化和“路径怎么变成可飞指令”3.1 全局路径规划只解决“走哪条路”不解决“怎么走”讲到路径规划很多人在第一眼看到RRT、A*、PRM这些算法时容易被算法复杂度吓到。但放在四旋翼这个场景下你首先要分清楚这些算法输出的是“几何路径”是一堆离散的路径点不包含时间、速度和加速度信息。也就是说路径规划解决的是“从哪里走、避开哪些障碍”而飞行器如何沿这条路径运动是另一层问题——轨迹规划。在代码包里如果作者用了RRT快速扩展随机树来找一条避开障碍物的路线路径点往往比较粗糙折线很多。把这种路径直接丢给位置控制器无人机每个点之间都要先加速再减速飞行体验会非常差而且大角度转折会让姿态环压力巨大。因此我看到很多四旋翼工程代码都把“全局规划”和“轨迹平滑/优化”分开做先用RRT或A*在地图里找到一个可行通道再用多项式轨迹把路径点连成连续可导的曲线。3.2 从路径到轨迹时间参数化与minimum snap要让一条几何路径变成四旋翼能实际跟踪的轨迹最核心的一步是“时间参数化”。我们给每个路径点分配一个期望到达时间然后用多项式插值去拟合位置曲线。四旋翼微分平坦的特性让这件事变得很优雅只要位置和偏航角的时间轨迹足够光滑其他状态姿态角、角速度、力矩都可以由它们代数推导出来。实际中用的比较多的是minimum snap轨迹也就是最小化四阶导数snap的能量。为什么要最小化高阶导数因为四旋翼的期望加速度和期望姿态角是强相关的位置曲线的snap直接关系到角速度的变化率snap越小轨迹越平滑控制器的响应压力越小续航和安全性都更好。在MATLAB里自己实现一个简化版基本流程是把整条轨迹按时间分成N段每段用一个多项式表示常见的是5阶或7阶多项式。设定每段起止点的位置、速度可能还有加速度约束。构造代价函数最小化加加速度平方或snap平方。用二次规划求解多项式系数。如果代码包里已经封装好了轨迹生成函数我建议你先试一下“在悬停和目标点之间生成一条minimum snap轨迹”看输出曲线的平滑程度然后改一下总时间观察对姿态环的影响。这个实验能让你直观理解“轨迹平滑”的意义。3.3 动态环境中的重规划策略真实飞行里障碍物不一定是静止的这就绕不开“避障”和“重规划”。很多MATLAB demo只做静态地图上的规划但实际场景里需要对动态障碍物响应。常见的策略是分层全局规划计算一条到目标点的参考路径飞行过程中通过传感器/感知模块检测近距离的动态障碍做局部路径修正如果局部修正空间不足再触发全局重规划。这种策略的好处是实时性好——全局规划可以慢一点局部避障必须快坏处是如果两层的代价函数没有好好设计可能出现在全局路径和局部路径之间反复横跳的情况。如果你的代码包里只有静态规划想加动态障碍测试我建议先做“简化版”给障碍物设一个运动模型当无人机当前路径与障碍物的预测轨迹距离小于安全阈值时触发一条局部的临时绕飞轨迹绕过后再回到全局路径上。这个临时轨迹可以直接用二次插值生成不需要很复杂。关键是阈值和触发逻辑我一般把安全半径设为无人机半径加上障碍半径的两倍再留0.5米的余量。3.4 路径规划与控制的衔接细节最容易翻车的就是这里在仿真系统里路径规划和控制器之间的数据接口是一个高频翻车点。轨迹规划器输出的通常是离散的时间序列每个时间戳对应期望位置、期望速度如果有的话、期望加速度。控制器则需要一个“当前时刻的期望值”。如果两者频率不一致或者时间同步有问题无人机就会呈现出“抽搐式”的跟踪。我自己在调试代码包时踩过一个特别典型的坑轨迹生成频率是20Hz而控制器是50Hz。结果在每个轨迹点之间控制器拿到的是“旧期望值”位置误差始终存在外环控制器不断输出修正指令姿态环被逼得来回动追踪曲线看着就像爬台阶。后来我改成把轨迹表示成时间相关的多项式函数在每个控制周期内通过当前时间直接计算期望值问题立刻消失。这里有一个非常实用的经验无论你的轨迹规划算法是什么输出接口尽量设计成“根据当前时间t返回此刻的位置、速度、加速度”而不是“返回一系列点”。这样控制器和规划器之间的耦合度最低代码结构也最清晰。4. 从代码包到自己的项目该怎么改、怎么调、怎么验4.1 先花30分钟弄清代码包的整体结构再动手跑拿到一份四旋翼MATLAB代码包我建议你先别急着运行主脚本。花30分钟把文件夹结构过一遍主脚本是哪个模型函数在哪控制器在哪有没有单独的轨迹生成函数有没有可视化脚本。常见的四旋翼MATLAB代码包分两种风格一种是纯脚本/函数链表所有状态更新写在主循环里这种适合读懂逻辑、方便打印调试另一种是基于Simulink的模型文件把控制器和无人机模型用框图表示这种更适合做自动代码生成或者与硬件在环结合。两种风格各有优劣。纯脚本的好处是你能精确知道每个周期里发生了什么缺点是当系统变大后容易混乱Simulink的好处是模块清晰但模型如果封装太深改参数反而麻烦。我个人的原则是先用纯脚本的方式快速理解算法逻辑然后再决定是否迁移到Simulink。如果代码包里已经是Simulink那就一步一步点进去看模块尤其是那些“黄色常量块”往往藏着所有关键参数。4.2 拿到demo之后按这个顺序改代码很多同学拿到的demo都能跑通但一改成自己的场景就炸。问题通常出在他们一上来就大刀阔斧地改。我建议按以下顺序循序渐进第一步改飞行器物理参数。把质量、转动惯量、力臂长度、推力系数和反扭矩系数换成你手头无人机的真实值。如果不知道真实值就用代码包默认值先练习但要清楚这些系数是高度耦合的只改质量而不改推力系数悬停油门就会变。第二步改控制参数。用上一章的“内环到外环”顺序先在目标点附近试小扰动确认每个环的响应正常。我习惯先在仿真里把位置环去掉单独测试姿态环——给一个固定的期望姿态角看飞机能不能稳定在那里角速度曲线是否平滑。姿态环稳了再接上位置环。第三步换地图和障碍物。把障碍物坐标写到一个数组里让规划算法去搜索路径。此时最容易暴露的问题是路径点和地图单位不一致。别笑我真的见过有人写障碍物时用了米但地图生成函数里用的是厘米。第四步加传感器噪声和延迟。很多demo是理想情况不加噪声。当你加上高斯噪声后你会发现原有PID参数可能开始抖这时就需要调低微分增益、增加滤波。下表是我常用的一个参数修改验证清单修改步骤检查指标正常范围改物理参数悬停状态推力输出约占最大推力40%-60%调姿态环阶跃响应超调量小于20%无持续振荡调位置环位置跟踪误差稳态误差趋近0动态误差在可接受范围内加噪声角速度控制量高频毛刺减弱主体信号保持加障碍物路径是否平滑转折点处速度连续无突变4.3 验证代码正确性不只是看动画“飞起来了”判断一套代码是好是坏不能只看无人机在动画里有没有从A飞到B。我通常用三个标准来评估零输入下能否稳定悬停把所有期望值设为零无人机应该能在原点附近保持姿态平稳位置漂移不超过几厘米。如果悬停时姿态就出现等幅振荡说明控制器参数或者模型本身有问题。阶跃响应动态指标给定一个目标位置阶跃记录位置曲线。如果上升时间、超调量、调节时间有任意一项明显异常比如超调量超过30%控制器设计就需要调整。轨迹跟踪误差让无人机跟一条已知轨迹计算位置误差的均方根。如果误差持续发散说明规划和控制之间的衔接有bug如果误差有界但偏大需要通过增大控制器增益或改善轨迹平滑度来优化。我自己会在每个里程碑改完以后跑同一组测试脚本把数值记录下来。这样一旦后续改动引入新问题对比前面几组数据就能很快定位是哪一步造成的。4.4 我建议的调试习惯先局部后整体先仿真后实机最后分享一个我从多次翻车中总结出来的习惯永远不要在一开始就追求“完整版”。很多代码包把动力学、控制、路径规划都封装好了看起来很高端但这种全部耦合在一起的状态最不利于学习也最不利于排错。我推荐的做法是“断点式调试”先单独跑通无人机在无控制下的自由运动观察重力作用下下落是否正常再打开控制器让它悬停再给它一个固定目标点最后接入路径规划。每打开一层就要确认这一层引入的问题都被消灭干净了再进下一层。这样做虽然看起来慢实际上是最快的路径。否则代码一跑起来炸机你要同时怀疑动力学方程、控制器符号、规划接口、参数单位排查工作量会翻好几倍心态也容易崩。另外如果你准备在自己的项目里复用这套代码我强烈建议把里面所有硬编码参数提取到一个统一的参数结构体里。比如params.mass、params.Ixx、params.kf而不是散落在各个函数里的魔法数字。这套习惯在仿真里帮你节省的时间可能还不明显一旦你要做参数扫描、蒙特卡洛实验或者把代码移植到真机上结构化参数的好处立刻会体现出来。我踩过最惨的一次是改了20个参数后发现自己改了A脚本里的推力系数但实际控制循环用的是B脚本里的另一个同名常量整整浪费了一个下午。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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