ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RflyExpCode无人机仿真实验:MATLAB解析PX4日志与调参实战

2026/9/13 16:09:59 拓冰建站 浏览量
RflyExpCode无人机仿真实验:MATLAB解析PX4日志与调参实战 简介RflySim-RflyExpCode.zip是一套基于MATLAB/Simulink的无人机模拟与实验代码包面向无人机控制、导航与算法验证方向的研究者和开发者适用于飞行控制器设计、空气动力学建模、传感器数据处理等学习与二次开发场景。包内共409个文件涵盖m脚本、slx模型、mat数据、pdf文档及png图片等类型Simulink模型用于搭建飞行控制器与无人机动力学仿真环境MATLAB脚本负责实验数据读取、滤波分析和结果可视化pdf文档则提供原理与操作说明整体约149.91MB。目前已有146人学习下载。资源内容覆盖空气动力学建模中的升力、阻力与扭矩计算PID、状态反馈等飞行控制器设计以及GPS、加速度计、陀螺仪等多源传感器数据处理同时涉及路径规划、避障与自主导航等算法开发。通过对照模型与脚本读者可以系统理解从理论建模到实验数据分析的完整流程并基于提供的示例快速搭建自己的无人机实验平台。1. 从一卷 RflyExpCode 开始为什么 MATLAB 是无人机仿真的主战场拿到RflySim-RflyExpCode.zip先别急着解压后到处点开.m文件。这个包在 RflySim 生态里的位置相当于把「Pixhawk 硬件在环、PX4 固件参数、MATLAB/Simulink 控制器模型、飞行日志」揉成了一组可对照的工程样本里面有myownUAV.ac自定义机型气动配置文件、e4_ekf_A.binEKF 估计相关二进制、e_acc1_A.bin与e4_A.bin不同传感器/状态源的数据记录、datalog_A.bin飞行日志、pixhawk_A.bin硬件在环或实飞记录、e3_m_A.bin另一组电机/状态样本以及同名出现三次的px4_simulink_app_params.c——这个重复出现的文件名在 Windows 解压时会被自动改名但它恰恰说明 PX4 的 Simulink 应用层参数是这套实验的核心纽带。对正在做无人机课程设计、毕设或飞控算法验证的人来说这套代码的直接价值是它把「理论上的姿态控制」变成「能跑在 Pixhawk 硬件上的二进制实验」。用 MATLAB 的原因也不复杂——Simulink 的代码生成工具链可以一键把控制器模型编译成 PX4 的模块你在 Simulink 里拖出来的 PID 结构、状态观测器、导航逻辑最终会变成px4_simulink_app_params.c里可配置的参数表。本文不会去复述工具箱介绍而是顺着文件清单逐个拆解每个.bin是什么实验留下的、.ac文件怎么描述一架自定义无人机、参数文件里哪些字段在真实调参时会被反复改动。2. 文件清单里的实验脉络从 myownUAV.ac 到 px4_simulink_app_params.c2.1 myownUAV.ac自定义机型的空气动力学入口.ac后缀在 RflySim 中代表 Aircraft Configuration 文件它不是 MATLAB 脚本而是一个描述飞行器物理属性的文本配置。打开后常见字段包括机架类型四旋翼/固定翼、质量、转动惯量矩阵、电机位置、螺旋桨气动系数等。RflySim 的 Simulink 模块RflySim3D或UAV Dynamics会读取这个文件构建出你专属的被控对象模型。我一般会先检查三个关键参数转动惯量对角项Ixx/Iyy/Izz、电机时间常数MotorTimeConstant、螺旋桨推力系数ThrustCoeff。转动惯量直接决定角加速度对控制力矩的响应快慢如果实验机是 F450 机架Ixx通常在0.01-0.03量级但教学包里的myownUAV.ac可能已经针对某款自定义机架做了修改所以不要照抄论文里的惯量值而是用这个文件配合datalog做系统辨识。提示如果发现仿真中无人机高频振荡或响应迟缓先怀疑.ac里的惯量是否和实际螺旋桨尺寸匹配而不是急着调 PID。2.2 一串 .bin 文件的实验语义EKF、加速度计、电机与 Pixhawk 数据.bin文件在 MATLAB 中通常用fopenfread按字节读取但 RflyExpCode 里的.bin更可能是 PX4 的 ULog 或自定义二进制日志。e4_ekf_A.bin名字里的e4多半对应实验编号Experiment 4ekf指明记录的是扩展卡尔曼滤波器的状态输出e_acc1_A.bin保存的是加速度计通道原始数据或加计融合后的线加速度e3_m_A.bin是第三组实验的电机控制信号样本pixhawk_A.bin则是从 Pixhawk 硬件上导出的完整飞行记录可能包含姿态、位置、IMU 原始数据。要读懂它们先在 MATLAB 里写一个通用读取框架:function data read_rfly_bin(filename) fid fopen(filename, rb); if fid -1 error(无法打开文件: %s, filename); end % 读取头部 4 字节判定是否为 PX4 ULog 格式 (magic: 0x55 0x4C 0x6F 0x67) magic fread(fid, 4, uint8); fseek(fid, 0, bof); if isequal(magic, [0x55 0x4C 0x6F 0x67]) data parse_ulog(fid); % 进入 ULog 解析流程 else % 自定义二进制格式按固定结构体顺序读取 n_samples fread(fid, 1, uint32); data struct(timestamp, [], acc, [], gyro, []); for i 1:n_samples data.timestamp(i) fread(fid, 1, uint64); data.acc(i, 1:3) fread(fid, 3, double); data.gyro(i, 1:3) fread(fid, 3, double); end end fclose(fid); end这段代码的逻辑分两级先尝试验证文件头是否为 ULog 的特定魔数[0x55 0x4C 0x6F 0x67]对应 ASCII 字符 ULog如果是就进入官方解析流程否则回退到自定义结构体解析。fread的uint64时间戳是 PX4 的微秒计数换算成秒需要除以1e6。参数说明第一参数filename是路径第二参数rb表示二进制只读这是 Windows 平台下避免换行符被错误转换的关键。2.3 px4_simulink_app_params.c控制器参数落地的关键文件文件出现三次说明解压时的重名导致了px4_simulink_app_params.c、px4_simulink_app_params - 副本.c、px4_simulink_app_params - 副本(2).c这样的组合。这些文件本质是同一份参数源内容里是 PX4 自定义 Simulink 应用模块比如姿态控制器需要的参数结构体初始化代码。看它时我通常抓住两个宏定义PARAM_*开头的float数组和px4_simulink_app_params结构体。结构体成员对应 Simulink 模型里的Simulink.Parameter比如Kp_Roll、Ki_Pitch、Kd_Yaw。修改后要在 MATLAB 中重新生成代码命令是% 打开模型 open_system(my_attitude_controller.slx); % 修改参数 my_attitude_controller.Kp_Roll.Value 6.5; my_attitude_controller.Ki_Pitch.Value 0.05; % 生成代码到目标文件夹 slbuild(my_attitude_controller);slbuild生成的是可嵌入 PX4 的 C 代码而不是直接可执行文件。生成的*.c需要拷贝到 PX4 Firmware 的src/modules/simulink_app/目录下再通过 PX4 的make px4_fmu-v5编译链编入固件。参数说明open_system(my_attitude_controller.slx)第一个实参是模型名称必须与当前工作目录下的.slx文件同名slbuild默认生成ert_shrlib类型的共享库若用于硬件编译需要先配置 Embedded Coder 的目标硬件为 Pixhawk 系列。提示如果生成代码后报参数无法解析优先检查 Simulink 模型里参数对象是否选中了Tunable属性否则参数会被内联为常量px4_simulink_app_params.c中对应的变量就不会生成。3. MATLAB 中的数据回放与状态估计验证方法3.1 用 datalog_A.bin 还原飞行状态datalog_A.bin是这套实验里最有价值的调试素材它保存的通常是同步对齐后的 IMU 数据、控制器输出、参考输入。还原时第一步是把二进制指针移动到数据域起点第二步是按列映射数据。假设文件存储格式是「时间戳(8 字节 uint64) 三轴加速度(float32 的 3 列) 三轴角速度(float32 的 3 列) 四元数(float32 的 4 列)」则读取代码为function flight load_datalog(filename) fid fopen(filename, rb); fseek(fid, 0, eof); file_size ftell(fid); fseek(fid, 0, bof); % 每行 8 3*4 3*4 4*4 48 字节 row_bytes 8 12 12 16; n_rows floor(file_size / row_bytes); flight.t zeros(1, n_rows); flight.acc zeros(n_rows, 3); flight.gyro zeros(n_rows, 3); flight.quat zeros(n_rows, 4); for i 1:n_rows flight.t(i) fread(fid, 1, uint64) / 1e6; flight.acc(i, :) fread(fid, 3, float32); flight.gyro(i, :) fread(fid, 3, float32); flight.quat(i, :) fread(fid, 4, float32); end fclose(fid); flight.t flight.t - flight.t(1); % 时间对齐到零 endftell配合fseek算出文件总字节数再除以固定行宽得到行数这种「先定长扫描再整块读取」的方法避免了循环中频繁判断文件末尾解析速度更快。row_bytes 8 12 12 16需要严格与数据生成端结构体#pragma pack(1)对齐如果生成端有字节对齐实际宽度可能是56或64。遇到解析出乱码大概率是这里宽度算错——最稳妥的办法是先读一两行直接打印十六进制对照结构体偏移。3.2 EKF 估计结果与真实数据的对齐分析e4_ekf_A.bin中记录的 EKF 状态与datalog_A.bin里直接测量量之间天然有相位差因为 EKF 内部有预测-更新循环滤波器输出是平滑后的最优估计而加速度计样本是原始测量。把两组数据画在同一张图里时我习惯先计算互相关来估计延迟[c, lag] xcorr(data_ekf_roll, data_acc_roll, 50, normalized); [~, idx] max(abs(c)); time_delay lag(idx) * dt; % dt 为采样周期 % 对齐后绘制对比曲线 t_aligned data_ekf.time - time_delay; figure; plot(t_aligned, data_ekf_roll, b-, LineWidth, 1.5); hold on; plot(data_acc.time, data_acc_roll, r--, LineWidth, 1); legend(EKF 输出, 加速度计计算滚转角); xlabel(时间 (s)); ylabel(滚转角 (deg));xcorr的第三个参数50限制了最大滞后样本数采样率 250 Hz 时对应 0.2 秒的最大延迟搜索范围normalized选项让互相关值归一化到 [-1,1]避免振幅差异造成的假峰值。如果最大互相关值低于 0.6先检查dt是否估算正确再确认两个.bin文件的时间基准是否一致——RflySim 的仿真实验中硬件时间戳和 Simulink 仿真时间戳常有固定偏移需要先用第一段代码统一到秒。3.3 用e_acc1_A.bin检查传感器饱和与振动e_acc1_A.bin这类加速度计样本经常被忽略但它反映的是飞控振动环境。多旋翼在接近共振转速时加计读数会出现等幅振荡而不是随机噪声。快速检测方法是计算功率谱密度fs 250; % 采样率 [pxx, f] pwelch(data_acc, hann(256), 128, 1024, fs); figure; plot(f, 10*log10(pxx)); xlabel(频率 (Hz)); ylabel(功率谱密度 (dB/Hz)); title(加速度计功率谱分析);pwelch的hann(256)是 256 点的汉宁窗128是重叠样本数50% 重叠1024是 FFT 点数频率分辨率约250/1024 ≈ 0.244 Hz。在 80-120 Hz 频段看到明显尖峰说明机架振动传递到了 IMU此时在 Simulink 模型里加低通滤波器的截止频率应设在这些尖峰频率的 1/2 以下而不是随意选个 50 Hz。值得一提的是e_acc1_A.bin里的数据可能已包含滤波器后的信号若在低频段也看到整体抬升的底噪说明滤波器带宽过宽或陷波器参数失配。4. 从二进制日志到底层调参RflyExpCode 的实战推演4.1 参数辨识从e3_m_A.bin提取电机响应曲线e3_m_A.bin是电机控制输入与转速响应的记录。四旋翼模型中电机通常被建模为一阶惯性环节G(s) K / (τ s 1)其中K是油门到转速的静态增益τ是电机时间常数。读取文件后用tfest直接辨识传递函数data_e3 read_rfly_bin(e3_m_A.bin); u data_e3.control_cmd; % 控制输入归一化 0~1 y data_e3.motor_speed; % 电机转速RPM ts 0.004; % 采样周期 250Hz % 构造 iddata 对象输入输出单位时间 data_id iddata(y, u, ts); sys_motor tfest(data_id, 1, 0); % 一阶无零点模型 tau sys_motor.Denominator(2) / sys_motor.Denominator(1); K sys_motor.Numerator(1) / sys_motor.Denominator(1); fprintf(电机时间常数 tau %.4f s, 增益 K %.4f\n, tau, K);tfest(data_id, 1, 0)中的1表示极点数0表示零点数一阶系统传函K/(τs1)对应一个极点、无零点因此选(1,0)。如果实际电机有信号延迟尝试(1,1)模型加入一个零点来拟合相位。识别出的τ对控制频率设计很关键——如果 τ 超过 0.02 秒姿态控制环的带宽就要适当下调否则控制器输出的高频变化会被电机响应衰减掉导致实际力矩跟不上指令。4.2 把myownUAV.ac中的气动参数代入 Simulink 非线性模型RflyExpCode 的 Simulink 模型模型顶层通常是RflyUAV模块双击后可以关联myownUAV.ac。关联后Simulink 内部会通过Load UAV Parameters回调函数读取文件中的惯量、质量、力臂长度等生成一个总线结构体传给动力学模块。我建议在模型初始化回调里加一行显式加载避免手动点选遗漏% 在模型 Callback - InitFcn 中添加 UAV_PARAM load_ac_file(myownUAV.ac); assignin(base, UAV_PARAM, UAV_PARAM);load_ac_file是 RflySim 工具包提供的内置函数返回结构体数组assignin(base, ...)将结果放到 MATLAB 基础工作区确保信号线中的UAV_PARAM总线可见。加载后在 Simulink 的UAV Dynamics模块参数中确认Mass、Inertia已经变成myownUAV.ac中的值而不是默认的1.5 kg与diag([0.015 0.015 0.03])。如果模块显示还是默认值多半是回调函数名拼写不一致——打开Model Explorer查看InitFcn是否真正执行了该行必要时手动点击Edit - Update Diagram强制执行回调。4.3 联合仿真中的坑Pixhawk 在环实验的配置顺序把 Simulink 控制器编译进 Pixhawk 做硬件在环HITL时容易踩三个顺序问题。第一必须先运行 PX4 SITL 仿真让 QGroundControl 与 RflySim3D 建立连接再启动 Simulink 模型否则 Pixhawk 端的 MAVLink 无法正确接收仿真姿态。第二PX4 固件版本必须与e4_ekf_A.bin生成时的固件版本一致不同 PX4 版本中 EKF 的状态量排列不同直接拿旧.bin当参考会让 EKF 输出错位。第三参数写入顺序要用px4_simulink_app_params.c的PARAM_*宏配合 QGroundControl 的 Onboard Parameters 界面写入不要直接在 C 文件里改硬编码值那样每次编译都会静默覆盖。具体编译命令在 RflySim 的脚本框架里常见两种方式# 方式一利用 RflySim 的自动编译脚本 cd C:\PX4Firmware python Tools\setup\px4_simulink_generate.py my_controller.slx # 方式二手动编译并上传 make px4_fmu-v5_defaultpx4_simulink_generate.py的核心工作是解析.slx里 Embedded Coder 生成的报告找到model.c与model.h后自动复制到src/modules/simulink_app/同时更新 CMakeLists.txt。make px4_fmu-v5_default里的px4_fmu-v5是硬件型号对应 Pixhawk 4如果实际用的是 Pixhawk 6C要改成px4_fmu-v6c_default否则固件签名不匹配上传后飞控不启动。5. 实验学到的关键技巧把 .bin 变成可交互的验证平台5.1 用 MATLAB App Designer 做飞行数据快速查看器RflyExpCode 里的处理脚本大多是一次性分析但实际调试中反复读取不同.bin时每次改路径和时间窗口很烦。我写过一个小工具把读取、对齐、画图封装成一个交互面板。核心逻辑只有三步选择文件、选择通道、绘制对比。classdef FlightDataViewer matlab.apps.AppBase properties (Access public) UIFigure matlab.ui.Figure LoadButton matlab.ui.control.Button Ax1 matlab.ui.control.UIAxes Ax2 matlab.ui.control.UIAxes Data struct end methods (Access private) function loadData(app, ~) [fname, pname] uigetfile(*.bin, 选择飞行日志); if isequal(fname, 0); return; end app.Data read_rfly_bin(fullfile(pname, fname)); end function plotData(app, ~) t app.Data.t; plot(app.Ax1, t, app.Data.acc(:, 1), r); hold(app.Ax1, on); plot(app.Ax1, t, app.Data.acc(:, 2), g); plot(app.Ax1, t, app.Data.acc(:, 3), b); legend(app.Ax1, {Ax, Ay, Az}); end end end上面用了matlab.apps.AppBase作为基类app.Data保存在属性里供多个回调共享避免每次画图都重新读文件。uigetfile(*.bin)返回文件名与路径注意返回顺序是[filename, pathname]初学者常写反导致fullfile拼接错误。两个坐标轴Ax1/Ax2可以一个画三轴加速度一个画四元数在飞行实验中快速定位异常时间段。5.2 从 EKF 输出的协方差矩阵判断滤波发散e4_ekf_A.bin中不仅保留状态量通常还包括状态协方差对角的根号视觉上表现为ekf_cov曲线。滤波发散时协方差会呈指数上升然后估计值开始漂移。检测策略是在 MATLAB 中扫描协方差序列cov_data data_ekf.cov_diag; % Nx7 矩阵对应位置、姿态等 7 维状态 figure; semilogy(data_ekf.t, cov_data); xlabel(时间 (s)); ylabel(协方差 (log 尺度)); legend({x, y, z, q0, q1, q2, q3}); ylim([1e-6, 1e2]);semilogy使用对数坐标是为了同时看清正常工况下1e-6量级的协方差和发散时1e1以上的跳变。如果滚转角对应协方差在 5 秒内从1e-5涨到1e-1说明该轴量测噪声矩阵R设置过小——滤波器过于相信量测位置稍微跳变就认为速度也跳变。此时回 Simulink 中调EKF 模块的 R 参数把加速度计噪声从0.02调到0.08重新生成代码再做一组e4_ekf_A.bin对比。协方差跳变沿也可用于检查 IMU 与 EKF 的时间戳是否错位错位时协方差会先出现单点尖峰然后回落。5.3 用 Simulink Test 自动化对比不同参数的飞行表现当你在px4_simulink_app_params.c里改了Kp_Roll想验证不同取值的效果手工一次次起飞既费时又难以保证环境一致。RflyExpCode 这类工程包通常集成了 Simulink Test 的支持可以写一个直接跑批的脚本把参数数组串起来循环仿真并保存日志Kp_values [4.0, 5.0, 6.0, 7.0]; for k 1:length(Kp_values) set_param(my_controller/Kp_Roll, Value, num2str(Kp_values(k))); % 运行仿真 simOut sim(rfly_uav_hitl_model, StopTime, 20); roll_overshoot(k) compute_overshoot(simOut.logsout.get(roll).Values.Data); disp([Kp , num2str(Kp_values(k)), - 超调 , num2str(roll_overshoot(k))]); endset_param直接改模型内部 Gain 模块的Value适用于参数没有封装成 Simulink.Parameter 的情况如果是独立参数对象更推荐用my_controller.Kp_Roll.Value ...的方式。sim(rfly_uav_hitl_model, StopTime, 20)会在当前目录生成simOut其中的logsout包含模型里配置的标记信号。compute_overshoot是自己写的简单函数找到阶跃响应的最大值、减去终值、再除以终值变化量。跑完四组 Kp 后直接在命令行打印超调与时域响应时长然后回px4_simulink_app_params.c里修改你选中的那组参数重新部署硬件。最后落到一个具体技巧所有.bin日志的解析脚本前缀保持同一套read_rfly_bin函数。RflyExpCode 的实验数据格式虽然不统一但用统一的读取入口可以快速适配新增实验文件时只需要在parse_ulog分支里加一个if判断数据主题名不必重写整段逻辑。这样做的好处是当你拿到第二个版本的RflyExpCode时旧的解析工具不用推倒重来。本文还有配套的精品资源点击获取