第四章 模型在环测试(MIL):基于MATLAB/Simulink的飞控模型开发
说实话,MIL(Model-in-the-Loop)是整个飞控测试链条里最容易被轻视的一环。很多人觉得「不就是跑个仿真嘛」,但我在实际项目中吃过不少亏——有一次模型里一个积分器初始值设错了,愣是到HIL阶段才抓出来,返工成本高得吓人。所以这一章,咱们把MIL的底细彻底聊透。
4.1 飞控模型开发:从算法到可执行模型
飞控模型开发,说白了就是把控制算法变成能在电脑上跑的东西。我习惯用MATLAB/Simulink来做这件事,原因很简单:它让你能边搭积木边验证。
一个典型的飞控模型包含这几块:
- 传感器模型——模拟IMU、GPS、气压计的输出,带噪声和延迟
- 执行器模型——模拟电机、舵机的响应特性
- 飞控算法——姿态解算、控制律、导航逻辑
- 环境模型——重力场、大气模型、风场扰动
嗯,这里要注意:千万别一上来就追求高保真。我见过有人花两周建了个六自由度气动模型,结果飞控算法还没写完。先跑通,再精修,这是铁律。
核心原则:MIL阶段的模型精度,够用就行。你的目标是验证控制逻辑,不是复现物理世界。
4.2 MIL测试环境搭建:你需要的不是工具,是流程
搭建MIL环境其实不复杂。你想想看,核心就三样东西:模型、测试脚本、数据记录器。但为什么很多人搭出来的环境不好用?因为缺少流程思维。
我建议按这个步骤来:
- 建立模型库——把传感器、执行器、环境模型做成模块化组件,方便复用
- 编写测试脚本——用MATLAB脚本控制仿真启停、注入故障、记录数据
- 设计测试用例——从需求文档里提取测试点,转化成可执行的仿真场景
- 自动化运行——用Simulink Test或自定义脚本实现批量跑测
- 结果分析——对比仿真输出与预期值,生成报告
我曾经在一个项目里踩过坑:测试环境搭好了,但每次跑完都要手动翻波形图找问题。后来花了一天时间写了个自动比对脚本,效率直接翻了三倍。所以自动化这件事,越早做越划算。
小技巧:在Simulink模型里加一个「测试注入点」子系统,专门用来注入故障信号。这样测试用例写起来会轻松很多。
4.3 模型覆盖率分析:别被数字骗了
模型覆盖率,很多人盯着这个数字看。但我要说一句:覆盖率100%不代表测试充分,覆盖率低也不代表测试不到位。
Simulink Design Verifier可以帮你做这些覆盖率分析:
| 覆盖率类型 | 含义 | 我的建议 |
|---|---|---|
| 语句覆盖率 | 每行代码是否被执行 | 目标100%,但别纠结死代码 |
| 分支覆盖率 | 每个判断的真假分支是否都走到 | 必须100%,逻辑漏洞常在这 |
| 条件覆盖率 | 每个布尔条件的真/假组合 | 安全关键功能要求100% |
| MC/DC覆盖率 | 每个条件独立影响结果 | DO-178C Level A要求,eVTOL建议做到 |
我个人习惯的做法是:先跑一轮功能测试,看看覆盖率基线。然后针对未覆盖的分支,手动补充测试用例。别指望一次跑完所有覆盖率——那不现实。
注意:覆盖率工具只能告诉你「哪些代码没跑到」,不能告诉你「哪些场景没测到」。真正的测试充分性,要靠需求覆盖来保证。
4.4 测试用例生成:从手动到自动
测试用例生成,这是MIL阶段最耗时的活。我早期做测试时,一个简单的姿态控制律就要写二三十个测试用例,还经常漏掉边界条件。
后来我改用Simulink Design Verifier的自动用例生成功能,配合手动补充,效率提升很明显。具体做法是:
- 用工具生成基础用例——覆盖所有分支和条件
- 手动补充场景用例——比如极端风况、传感器故障、执行器卡死
- 用随机测试做补充——在参数空间里撒点,抓意外行为
举个例子,这是用MATLAB脚本生成的一个简单测试用例:
% 测试用例:IMU故障注入
% 场景:飞行10秒后,陀螺仪输出漂移
% 设置仿真参数
simTime = 30;
faultTime = 10;
driftRate = 0.5; % deg/s
% 运行仿真
simOut = sim('flight_controller_model', ...
'SimulationMode', 'normal', ...
'StopTime', num2str(simTime));
% 注入故障
set_param('flight_controller_model/IMU/GyroDrift', ...
'Value', num2str(driftRate));
% 重新运行并记录
simOut = sim('flight_controller_model', ...
'SimulationMode', 'normal', ...
'StopTime', num2str(simTime));
% 检查姿态角是否在容忍范围内
rollData = simOut.logsout.get('roll_angle').Values.Data;
maxRoll = max(abs(rollData));
assert(maxRoll < 30, '姿态角超限!');
你看,代码其实不复杂。关键是要把测试用例组织好,方便复用和维护。我习惯把测试用例按功能模块分类,每个模块一个MATLAB脚本,统一入口调用。
经验之谈:测试用例的命名要有规律。比如「TC_ATT_001」表示姿态模块的第1个测试用例。这样后期追溯问题时会省很多时间。
4.5 本章知识体系总览
为了让你对整个MIL流程有个直观印象,我画了张图。这张图展示了从模型开发到测试完成的完整链路:
这张图里有个关键点我想强调一下:反馈回路。MIL测试不是一次性的活。你跑完一轮测试,发现覆盖率不够,那就补充用例再跑。发现模型有bug,修完再测。这个迭代过程,才是MIL测试的真正价值所在。
我的习惯:每次迭代都保留一份测试日志,记录「改了啥、为啥改、测了啥结果」。三个月后回头看,你会感谢自己这个习惯。
好了,MIL阶段的内容就这些。记住一句话:模型在环测试不是走过场,它是你发现早期设计缺陷的最后机会。等代码生成完、烧到硬件上再改,成本至少翻十倍。所以,认真对待每一次仿真,认真看每一条覆盖率数据。你的飞控系统,值得这份耐心。