简介:本资源是一套面向高校自动化、航空航天及控制工程专业师生的四旋翼飞行器MATLAB/Simulink控制系统仿真学习包,聚焦于无人机动力学建模、姿态控制算法实现与闭环仿真验证。压缩包共17个文件(42KB),含15个.m脚本文件(如控制器设计alt_control.m、动力学求解dinamica.m、传感器滤波sam5_filter.m、三维可视化draw_results_3D.m等)和2个.mdl Simulink模型文件(systema2.mdl与untitled.mdl),完整覆盖物理建模、PID/反向旋转控制策略、姿态解算、数据可视化及仿真结果分析全流程。已有1790人学习下载,用户可直接运行仿真系统观察滚转、俯仰、偏航响应曲线,调试控制器参数,理解多输入多输出非线性系统的建模难点与实时控制逻辑,是开展课程设计、毕业设计或科研入门的理想实践素材。 一个.rar压缩包,里面装着一整套四旋翼飞行器的 MATLAB 仿真程序,包含 Simulink 仿真系统和配套脚本——这就是我这次要聊的主角。做无人机控制、写飞控算法、做毕业设计,或者单纯想看看别人怎么把四旋翼动力学放进 Simulink 里跑起来的人,都应该会需要这份内容。
我先坦白一下:我自己拿到的这份程序并不是什么“论文级别的豪华货”,但它胜在完整——模型、控制器、初始化脚本、结果绘图都在同一个文件包里。打开就能跑,跑完能出曲线,曲线能帮你理解四旋翼是怎么飞起来的。这比你自己从零开始在 Simulink 里搭一个模型省下太多时间,但也正因为它是别人整理好的,拿到手之后第一件事不是兴奋地点运行,而是先把文件结构和依赖关系摸清楚。
1. 拿到压缩包以后:先花十分钟摸清家底
1.1 压缩包里“大概率”会出现哪些文件
很多刚接触 MATLAB 仿真的人都有一个共同习惯:双击.slx文件,看到模块图之后直接点那个绿色的小三角。结果十有八九是先报错,报错之后又一脸懵。所以我建议你拿到这个压缩包之后,先别急着解压到桌面然后双击,先看一下文件清单。
按我见过的大多数“四旋翼飞行器 MATLAB 仿真程序”类资源,解压之后通常会是这样一套结构:
quadrotor_sim/ ├── readme.txt ├── init_quadrotor.m ├── quadrotor_param.m ├── quadrotor_main.slx ├── attitude_control.slx ├── position_control.slx ├── plot_results.m └── data/ └── results.mat这个结构不是绝对标准,但八九不离十。readme 文件里通常写的是“怎么跑、需要什么工具箱、注意事项”;.m文件负责把模型里所有变量加载到工作区;.slx文件是真正能看到的仿真模型;plot_results.m则是把仿真结果画成图的脚本。
你可以先用文本编辑器打开 readme 看看,再看一下各个.m文件头部注释。整个过程花不了太久,但能给你省下后面大量排查时间。
1.2 解压和路径处理:中文路径是最大的坑
解压这一步听起来没什么技术含量,但坑恰恰在这里。如果你把压缩包解压到一个带中文的路径下,比如C:\Users\张三\桌面\四旋翼仿真\,后面 Simulink 模型里那些init_quadrotor.m的调用、数据文件读写、甚至 MATLAB Function 模块里的临时文件名,都有可能踩到编码兼容问题。
尤其要注意的是:Simulink 的模型文件在加载时会自动去找工作区变量。如果路径中有中文,一个很常见的现象是脚本能运行,但模型里的 Gain、Constant 等模块显示不出数值,或者运行几秒后报出文件读写类错误。
所以我的建议是统一放到英文字符路径下,比如D:\quadrotor_sim\。另外,有些 rar 压缩包在 Windows 下用右键自带解压是没问题的,但如果压缩包文件名带特殊符号或者解压软件版本太老,可能导致文件损坏。解压完成后最好对照一下压缩包里的文件大小,至少确认那几个关键文件不是 0 字节。
2. 仿真程序的核心:四旋翼模型是怎么进 Simulink 的
2.1 先把动力学方程捋一遍
这个仿真包做得再怎么花哨,核心一定是四旋翼动力学模型。如果你只是想拿来跑个曲线,不关心模型也能出图;但如果后面要改控制器、调参数、甚至自己重新搭一遍,那动力学方程这部分就必须看懂。
四旋翼是一个典型的六自由度刚体系统,但只有四个输入,所以它是个欠驱动系统。简化建模时通常把四个电机产生的升力叠加成总升力u1,把电机差速产生的滚转、俯仰、偏航力矩分别记作u2、u3、u4。
忽略空气阻力、忽略电机动态延迟的简化模型可以写成下面这个样子:
x_ddot = (cosφ sinθ cosψ + sinφ sinψ) * u1 / m y_ddot = (cosφ sinθ sinψ - sinφ cosψ) * u1 / m z_ddot = cosφ cosθ * u1 / m - g φ_ddot = (u2 + (Iy - Iz) * q * r) / Ix θ_ddot = (u3 + (Iz - Ix) * p * r) / Iy ψ_ddot = (u4 + (Ix - Iy) * p * q) / Iz这里的x, y, z是机体在地面坐标系下的位置,φ, θ, ψ是滚转、俯仰、偏航角,p, q, r是机体角速度。看到这个方程你会发现,四旋翼平移运动主要靠倾斜姿态来改变水平方向的推力分量,所以“位置控制”本质上是通过“控制姿态”实现的,这也是后面控制器要分成内环和外环的根本原因。
2.2 从方程到 Simulink 模块的映射
很多朋友看到上面一串公式就有点头大,但落到 Simulink 里其实并不复杂。一般仿真包里的模型结构会是这样:
reference trajectory → position controller → attitude setpoint → attitude controller → motor mixing → quadrotor dynamics → 12 state outputs模型里每一条通道基本都能在模块图上找到对应关系。积分器用来累积加速度得到速度、累积速度得到位置;Fcn 模块或者 MATLAB Function 模块用来算那个比较长的三角函数表达式;Bus Creator 作用是把所有状态收拢成一根总线,方便后续控制器引用。
如果你打开quadrotor_main.slx发现里面看不到这种干净的分层结构,而是密密麻麻一堆模块叠在一起,那大概率是原作者把所有子系统都展开到了顶层。这种情况不用慌,你只需要找到几个关键点:输入端在哪里、输出端在哪里、中间那个和飞行器动力学相关的子系统在哪。
定位到动力学子系统后,我的经验是“只看端口不看内部”,先看它输入几个量、输出几个量。输入通常是四个控制量[u1, u2, u3, u4],输出是 12 个状态量。如果你要验证模型对不对,可以给一个固定不变的[mg, 0, 0, 0]输入,看 z 轴会不会保持着高度不变。如果能稳住,说明模型的基本力和重力关系是对的。
2.3 为什么控制器一定要分内外环
四旋翼只有四个电机,飞起来靠的是倾转机体方向产生水平加速度。想从 A 点飞到 B 点,你不能直接说“往东飞一点”,只能告诉姿态系统“把机头朝某个方向倾斜一个角度”。这个逻辑决定了控制器天然要分成位置环和姿态环两层。
内环是姿态环,它负责让当前姿态快速逼近期望姿态,响应速度要快,通常 20Hz 甚至 50Hz 以上才有比较好的效果。外环是位置环,它负责根据当前位置和期望位置,计算出“需要多大的水平加速度”,再换算成期望俯仰角和滚转角。
这类程序一般在position_control.slx或attitude_control.slx里做了集成。一个典型的做法是:外环用 PD 控制器,把位置误差映射成期望姿态角;内环用 PID 控制器,把姿态角误差映射成力矩指令。如果你拿到手的是已经封装好的模型,别急着删掉外面的黑色子系统边框,先把里面的串级结构找出来,理解了“位置环在前、姿态环在后”之后,你后面调参才不会像无头苍蝇一样乱试。
3. 控制回路怎么搭:从 PID 到完整闭环
3.1 内环姿态控制器
姿态环是整个控制系统的核心,它直接决定飞机的稳定能力。Simulink 里姿态环通常长这样:输入是期望姿态角[phi_d, theta_d, psi_d]和当前姿态角[phi, theta, psi],误差进 PID,输出是三个力矩指令。
姿态环的难点在于大多数 PID 算法对“角速度阻尼”有依赖。很多资料喜欢直接在角度误差后面接一个 PID,但实际调试时会发现,光有角度环不够,容易造成机体来回振荡。更常见的做法是角度环加角速度环的双环结构,类似于一个串级 PID:外环角度误差生成期望角速度,内环角速度误差生成力矩。
在这个仿真包里,可能会用 Transfer Fcn 或 Discrete PID Controller 模块。模块参数一般放在 init 脚本里,比如Kp_phi、Ki_phi、Kd_phi这类变量名。用变量而不是常量有一个好处:你不需要每次改完参数都去模型里双击模块再点确认,直接在 MATLAB 工作区改一次,重新运行初始化脚本即可。
这里我强烈建议你先看清楚模块里填的是变量名还是具体数字。如果看到模块里填的是Kp_phi而不是20,说明变量来自初始化脚本,运行模型前必须先跑一遍那个.m文件,否则模型会报“未定义变量”的错。
3.2 外环位置控制器
位置环相对姿态环要慢一个档次,它关注的不是“飞机现在平不平”,而是“飞机在空间里的位置对不对”。
典型位置环的做法是:把期望轨迹和当前位置的差值送入 PD 控制器,得到期望加速度。水平方向再将期望加速度除以重力加速度再取一个三角函数关系,换算成期望的滚转角和俯仰角。垂直方向则直接输出总升力指令,用于抵消重力和修正高度误差。
在这个程序包中,如果你看到“position controller”子系统里有一个写着atan2或asin的 MATLAB Function,说明作者已经做了角度换算,而不是直接把 x 方向控制量当作 φ 角指令。这是一个好习惯,因为大角度情况下线性关系“加速度 = 重力 × 角度”会失真,用三角函数换算会更贴近真实模型。
3.3 一个可以直接用的 PID 初始化脚本
不管程序包里原作者的脚本是什么样的,我给一个常见的初始化脚本示例,你拿到别人模型的时候也能套用:
% init_quadrotor.m m = 1.2; % 飞行器质量 kg g = 9.81; % 重力加速度 m/s^2 Ix = 0.015; % 滚转转动惯量 kg*m^2 Iy = 0.015; % 俯仰转动惯量 kg*m^2 Iz = 0.025; % 偏航转动惯量 kg*m^2 % 内环姿态 PID Kp_phi = 18; Ki_phi = 0.1; Kd_phi = 2.5; Kp_theta = 18; Ki_theta = 0.1; Kd_theta = 2.5; Kp_psi = 12; Ki_psi = 0.05; Kd_psi = 1.5; % 外环位置 PD Kp_x = 2.0; Kd_x = 1.2; Kp_y = 2.0; Kd_y = 1.2; Kp_z = 8.0; Kd_z = 3.0;这里只是初始参考值,具体数值要结合你模型里的质量和转动惯量重新调。把脚本保存成.m文件后,直接在 MATLAB 命令窗口运行一次,让这些变量进入工作区,再回到 Simulink 界面点开始仿真,基本就能正常跑起来。
4. 参数整定与仿真结果分析
4.1 仿真求解器设置:固定步长还是变步长
很多人拿到仿真包后,仿真跑起来慢得像幻灯片,于是怀疑是电脑不行。其实很多时候是 Solver 设置的问题。Simulink 默认通常用变步长求解器,比如ode45,遇到快速变化的动力学响应会自动缩小步长,导致仿真时间很长。
如果你的模型是纯连续模型、没有需要严格等步长采样的算法,用变步长ode45是可以的。但如果你想做后续代码生成、或者想和真实飞控一样按固定时间片执行,那就需要改成固定步长求解器,比如选择od4(四阶龙格-库塔)或odt(梯形规则)。
改法很简单:菜单栏选择 Simulation 模型配置参数,找到 Solver 选项,把 Solver options 里的 Type 改成Fixed-step,Fixed-step size 填0.001或0.005。步长选0.001仿真更精确但耗时更长,选0.005速度更快但误差会明显一些。
对于四旋翼这种有高频姿态动态的系统,我个人建议固定步长不要大于0.005。你拿到的程序如果已经在初始化脚本里写好了t_sim和dt,那你就先按照脚本设置来跑一遍,再根据自己的需求调整。
4.2 用脚本批量扫参,别在 Simulink 里手动点
调 PID 最忌讳的就是一遍遍在 Simulink 里点运行、看曲线、停掉、改参数、再点运行。这种方法一次只能试一组参数,而且你对“上一组为什么发散”的记忆很快会模糊。
正确姿势是用一个 MATLAB 脚本批量跑参数,把每次仿真结果保存下来再统一画图。基本框架如下:
% batch_test.m kp_list = [10, 15, 20, 25]; results = cell(length(kp_list), 2); for i = 1:length(kp_list) Kp_phi = kp_list(i); sim('quadrotor_main.slx'); results{i, 1} = Kp_phi; results{i, 2} = phi_theta_psi.signals.values(:, 1); % 滚转角曲线 end figure; for i = 1:length(kp_list) plot(tout, results{i, 2}, 'LineWidth', 1.5); hold on; end legend(cellstr(num2str(kp_list')));注意sim()这个命令会直接启动模型仿真,运行前需要保证模型未处于调试状态、初始化脚本已执行过。跑完后再用plot把不同 Kp 对应的滚转角响应画在一起,你就能直观看到增益太小导致响应慢、增益太大导致振荡的变化过程。
4.3 三种典型仿真场景下的输出判读
拿这类四旋翼仿真程序,最常跑的三种场景就是悬停、阶跃响应、轨迹跟踪。它们本质上是在验证三个层面的东西。
悬停仿真一般把期望位置设成[0, 0, 1],期望偏航角设成 0。跑完重点看 z 轴高度曲线能不能稳定在 1 米附近,俯仰和滚转角是否在 0 附近小幅波动。如果高度曲线是平的但姿态角抖动很剧烈,说明姿态阻尼不足或仿真步长过大。
阶跃响应常用在俯仰角或高度通道上。给一个阶跃指令后,看响应曲线有没有超调、有多少次振荡、稳态误差有多大。正常的一组 PID 参数应该是响应快、超调小、不振荡,但这三个指标本质上是矛盾的,所以实际调参时要根据设计任务取舍。
轨迹跟踪则更接近应用,常见的有圆形轨迹、8 字轨迹或螺旋轨迹。跑完后除了看姿态曲线,还要看三维位置曲线。程序包里的plot_results.m如果自带轨迹绘图功能,你会看到一条期望轨迹和一条实际轨迹,两者贴合程度就是控制器性能最直观的体现。
5. 常见问题与排查技巧实录
5.1 打开模型就报“变量未定义”怎么办
我远程帮别人看 Simulink 模型时,遇到过最多的报错就是:
Undefined function or variable 'Kp_phi'.这个报错文本本身已经说得很清楚了:模型里某个模块用了Kp_phi这个变量,但 MATLAB 工作区里没有这个变量。解决方案不是去模型里把Kp_phi改成数字,而是把定义这些变量的初始化脚本先跑一遍。
如果你拿到的程序包里有init_quadrotor.m或quadrotor_param.m,在命令窗口执行:
run('init_quadrotor.m');然后去工作区检查一下变量是否存在:
who如果变量列表里出现了Kp_phi、Ix这些名字,再回 Simulink 点运行。另外,某些模型会在Model Properties > Callbacks > InitFcn里自动调用初始化脚本,打开这个页面看一眼,能帮你判断是不是每次运行模型前都会自动加载变量。
5.2 仿真一发散,先查这三个地方
仿真曲线直接冲向Inf或者剧烈振荡,这是四旋翼仿真里最劝退人的问题。排查时不要一股脑怀疑 PID 参数,先从三个地方入手。
第一是看推力方向有没有接反。很多初始化脚本里总升力u1直接以牛顿值作为输入,但动力学模块里接收的是“四个电机推力之和”。如果你输入的是总升力却乘了个电机数量,高度就会失控。第二是看 PID 符号有没有接反。最典型的例子是角度误差e = phi_d - phi被写成了e = phi - phi_d,相当于一个负反馈变成了正反馈。第三是看积分项是否饱和,尤其是姿态环 Ki 设置太大时,积分项会在几秒内迅速累积导致大幅超调。
我调试的时候习惯先关闭积分项,只用 PD 把系统稳住,再加入 Ki 消除稳态误差。如果只加 PD 就发散,那通常不是积分问题,而是符号或者模型结构问题。
5.3 输出结果和论文对不上的排查思路
还有一个非常常见的情况:你明明照着某个教程或论文里的参数填进去了,结果跑出来的曲线跟论文里的完全不一样。这不一定是教程写错了,更可能是“初始化条件不一致”或者“坐标系定义不一致”。
坐标系的坑尤其隐蔽。有的程序里 z 轴沿重力方向向下,有的程序里 z 轴向上;有的偏航角在转换矩阵里用的旋转顺序是 ZYX,有的是 XYZ。你用同一个 PID 参数,在不同坐标系定义下跑出来的姿态趋势可能完全相同,但符号有正负差异。
排查思路是给一个特殊输入验证符号。比如给u1 = mg, u2 = 0, u3 = 0,模型应该保持悬停。如果此时位置输出依然上下乱跳,那大概率是整流罩坐标系和重力方向处理反了。再比如给一个正的俯仰力矩,看机体绕 y 轴转动的方向是否符合右手定则,不符合就把力矩或者角度符号取反。
我建议你在分析结果之前,先把初始化脚本里的坐标系定义、状态向量顺序、单位这三样东西全部确认一遍。这个确认过程虽然朴实,但能直接避免因为“单位是克还是千克”这种低级错误导致整套仿真白跑。
5.4 一个常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 模型打不开,提示缺工具箱 | 没有安装 Aerospace 或 Simulink Control Design | 在附加功能中安装对应工具箱 |
| 点运行后报变量未定义 | 初始化脚本未执行 | 运行init_quadrotor.m或检查 InitFcn |
| 高度曲线下降后冲出 NaN | 重力单位、质量单位不一致 | 检查m、g的数值量纲 |
| 姿态角快速振荡 | PID 增益过大或符号反了 | 先改小 Kp,再检查误差定义 |
| 运行非常慢 | 变步长求解器步长过小 | 改固定步长,步长设为 0.001~0.005 |
| 3D 轨迹图没有显示 | 输出变量名与绘图脚本不一致 | 查看plot_results.m里引用的信号名 |
| 打开模型后显示版本较新 | MATLAB 版本低于模型保存版本 | 使用模型导出为旧版本或升级 MATLAB |
6. 这个仿真程序还能怎么接着玩
6.1 把 PID 换成滑模控制或 ADRC
如果你已经跑通了原程序里的 PID 控制,下一步很自然的想法就是换控制器。四旋翼是强耦合、欠驱动的非线性系统,PID 在悬停和小角度机动下效果不错,但遇到大角度机动或外界扰动时,鲁棒性就不太够了。
滑模控制是很多论文里的常见替换方案。它的核心思想是设计一个滑模面,比如s = e_dot + lambda * e,然后通过切换控制项把状态“压住”在滑模面上。在 Simulink 里实现时,你只需要把姿态控制器子系统里的 PID 模块替换成一个 MATLAB Function,输入仍然是角度误差和角速度误差,输出换成力矩指令。
我试过在一套现成的四旋翼模型里做这个替换,做法其实不复杂:
function tau = att_smc(e, e_dot, lambda, eta) s = e_dot + lambda * e; tau = -lambda * e_dot - eta * sign(s); end当然这只是示意,真正的滑模控制还要考虑模型项补偿。你可以先保持原模型的动力学模块不动,只换控制器,然后对比同样的圆形轨迹跟踪效果。这样你就能直观看到,滑动模态控制在大角度情况下到底比 PID 强在哪里。
换成 ADRC(自抗扰控制)也是类似逻辑。把模型内部参数不确定性和外部扰动统一当成“总扰动”,用扩张状态观测器观测并补偿。四旋翼模型里每个通道的扰动项都可以用一个 ESO 模块去估计,然后把估计值前馈到控制量里。
6.2 从 Simulink 直接生成 C 代码
很多做飞控的人拿这个仿真程序,不只是为了看曲线,而是想把控制算法部署到真实飞控硬件上。Simulink 的 C 代码生成功能这时候就能派上用场。
要生成代码,你的模型必须满足几个条件:求解器要改成固定步长,模型里的连续积分器要改成离散模块,输入输出要明确标成 Inport 和 Outport,最好把控制器单独拉出来做成一个独立子系统。生成代码时选择菜单栏里的 C 代码生成,勾选嵌入式实时目标,Simulink 会自动帮你生成.c、.h文件。
我实际操作中的经验是:不要指望生成的代码能直接刷进飞控板。更合理的做法是,把姿态控制子系统单独抽出来,接口定义成“期望姿态角、当前姿态角、当前角速度”作为输入,“四个电机 PWM 占空比”作为输出,然后再在飞控主循环里读取传感器数据、调用这个控制函数。
代码生成不是点击就完事,它涉及很多工程细节,比如数据类型的定标、代码生成模板的选择、中断服务函数的集成方式。一开始用这个仿真包练习时,先保证代码能成功编译、没有未定义变量,就是很大的一步了。
6.3 向硬件在环和真机移植延伸
继续往前走一步,你可以把这套 Simulink 仿真接到硬件在环测试里面。基本思路是:真实的飞控板运行控制器代码,Simulink 里的四旋翼动力学模型代替真实飞机,通过串口或者以太网把传感器数据发送给飞控,再把飞控输出的 PWM 信号读回 Simulink。
这样做的最大价值在于,你可以不用冒摔机的风险,就把真实飞控板上的参数、故障保护逻辑、电机响应延迟都验证一遍。很多基于 Simulink 的四旋翼程序包已经预留了这种接口,比如在模型里有一个 Serial Receive 模块或 UDP Receive 模块,你只需要把控制器输出和模型输出接到这些模块上。
如果你暂时用不到硬件,也可以做“模型一致性验证”:把同一个 PID 控制算法分别用连续模块和离散模块实现,对比它们在相同轨迹下的输出差,看看离散化步长对控制性能的影响。我做这种对比的时候,固定控制周期从 0.001 秒放宽到 0.01 秒,能明显看到姿态跟踪误差变大,这对理解“飞控为什么不能写得太太太慢”很有帮助。
我个人在实际操作中的体会是,这类“四旋翼飞行器 MATLAB 仿真程序,包括 simulink 仿真系统和程序”的压缩包,真正的价值不在于你能立刻跑出几条漂亮的曲线,而在于它给了你一个完整的“模型—控制器—可视化”闭环。你可以从这个闭环出发,替换模型、换控制器、调整坐标系、接硬件接口,每一步都能学到东西。拿到程序后先老老实实跑通原版,再开始拆,这个节奏是最稳的。
本文还有配套的精品资源,点击获取