第4章:测试环境搭建——Simulink/Stateflow测试环境配置、测试脚本与自动化框架介绍
好,咱们进入实战环节了。前面聊了那么多MIL的概念和流程,这一章咱们得把袖子撸起来,真正把测试环境搭起来。说实话,很多新手工程师在模型上花的时间不少,但一到搭测试环境就头疼。我当年刚入行时也踩过不少坑,今天就把这些经验掰开揉碎讲给你听。
4.1 Simulink/Stateflow测试环境配置
先说说Simulink测试环境。你想想看,一个复杂的控制模型,动辄几十个模块,几百个信号。如果没有一个规范的测试环境,那调试起来简直是一场噩梦。
我个人习惯是把测试环境分成三层:
- 测试平台层:负责信号注入、数据采集、结果比对
- 被测模型层:就是咱们的Simulink/Stateflow模型
- 测试用例层:存放各种测试场景和参数
这三层之间用接口模块连接,互不干扰。我在项目中遇到过好几次,因为测试环境和模型混在一起,最后改模型时把测试脚本也搞乱了。所以,一定要分开。
4.1.1 基础配置步骤
- 创建测试工程目录:建议用Git管理,目录结构要清晰。比如:
test/、model/、script/、data/ - 配置求解器:固定步长,离散求解器。我一般用
ode1或ode3,步长根据模型需求定,通常是0.01s或0.001s - 设置信号记录:把关键信号都打上
log标记,方便后续分析 - 配置Stateflow:注意设置
Enable debugging/animation,调试时很有用
重要提醒:Stateflow的初始化状态一定要明确。我曾经有个项目,因为Stateflow的默认状态没设对,导致仿真一开始就跑飞了。排查了两天才找到原因,教训深刻。
4.1.2 测试信号注入方式
测试信号怎么注入?说白了就三种方式:
| 方式 | 适用场景 | 优缺点 |
|---|---|---|
| 手动输入 | 快速验证、调试 | 灵活但不可重复 |
| From Workspace | 批量测试、回归测试 | 可重复,但需要提前准备数据 |
| Signal Builder | 复杂波形测试 | 可视化,但脚本化困难 |
我个人更推荐From Workspace方式。为什么呢?因为你可以用MATLAB脚本生成各种测试数据,然后一键注入。这在自动化测试中特别有用。
4.2 测试脚本编写
好,环境搭好了,接下来就是写测试脚本。很多新手觉得写脚本麻烦,其实不然。一个规范的测试脚本,能帮你节省80%的重复劳动。
4.2.1 基础测试脚本模板
我给大家一个我常用的脚本模板:
%% 测试脚本模板 - 第4章示例
% 作者:资深工程师
% 功能:MIL测试基础脚本
% 1. 清空环境
clear; clc; close all;
% 2. 加载模型
modelName = 'myController';
load_system(modelName);
% 3. 设置测试参数
testParam.stepTime = 0.01;
testParam.simTime = 10;
testParam.inputSignal = [0, 1, 0, -1, 0]; % 方波信号
% 4. 注入测试信号
assignin('base', 'testInput', testParam.inputSignal);
% 5. 运行仿真
simOut = sim(modelName, 'SimulationMode', 'normal', ...
'StopTime', num2str(testParam.simTime));
% 6. 提取结果
outputData = simOut.get('outputSignal');
timeData = simOut.get('tout');
% 7. 结果比对
expectedOutput = [0, 0.5, 0, -0.5, 0]; % 期望输出
if max(abs(outputData - expectedOutput)) < 0.01
disp('测试通过!');
else
disp('测试失败!');
end
% 8. 清理
close_system(modelName, 0);
小技巧:脚本里一定要加try-catch结构。我曾经有一次跑批量测试,中间一个用例报错,整个脚本就停了。加了异常处理之后,即使某个用例失败,也能继续跑后面的。
4.2.2 测试脚本的模块化设计
你想想看,如果每个测试用例都写一个完整的脚本,那维护起来得多累?所以,我建议把脚本拆成几个函数:
setupTest.m:初始化环境,加载模型runTestCase.m:执行单个测试用例checkResult.m:比对结果,生成报告teardownTest.m:清理环境
这样做的好处是,你只需要写一次基础框架,后面加新用例时,只需要修改参数文件就行了。
4.3 自动化测试框架介绍
说到自动化测试框架,很多公司都有自己的方案。但万变不离其宗,核心就三个字:可重复、可追溯、可报告。
4.3.1 轻量级框架:基于MATLAB脚本
对于小团队或者项目初期,我推荐用MATLAB自带的sltest工具箱。它提供了:
sltest.testmanager:管理测试用例sltest.testsequence:定义测试序列sltest.testharness:自动生成测试平台
举个例子,用sltest.testmanager创建一个测试套件:
%% 创建测试管理器
tf = sltest.testmanager.TestFile('myTestFile.mldatx');
ts = tf.createTestSuite('ControllerTestSuite');
% 添加测试用例
tc1 = ts.createTestCase('TestCase1');
tc1.setModel('myController');
tc1.setSimulationMode('Normal');
tc1.setStopTime('10');
% 设置信号
tc1.addSignal('inputSignal', 'FromWorkspace', 'testInput');
tc1.addSignal('outputSignal', 'Logging', 'on');
% 运行测试
resultObj = tf.run();
resultObj.displayResults();
注意:sltest工具箱需要额外的License。如果公司没买,可以用纯脚本实现。我早期在一个小公司,就是纯手写的自动化框架,虽然简陋,但够用。
4.3.2 企业级框架:基于Python+MATLAB Engine
对于大型项目,我建议用Python做调度,MATLAB做仿真引擎。为什么?因为Python的生态更丰富,报告生成、数据可视化、CI/CD集成都更方便。
基本架构是这样的:
- Python调度层:读取测试用例(Excel/JSON),调用MATLAB Engine
- MATLAB执行层:运行Simulink仿真,返回结果
- Python报告层:生成HTML/PDF报告,上传到服务器
我在一个ADAS项目中就用过这个方案。当时有2000多个测试用例,纯MATLAB跑要3个小时。换成Python调度后,利用多进程并行跑,时间缩短到40分钟。效果非常明显。
4.3.3 自动化框架的核心要素
不管用什么工具,一个成熟的自动化框架应该包含:
| 要素 | 说明 | 我的建议 |
|---|---|---|
| 用例管理 | 支持增删改查,版本控制 | 用Excel管理,简单直观 |
| 执行引擎 | 支持批量、并行、定时执行 | 优先考虑并行能力 |
| 结果比对 | 支持数值、波形、时序比对 | 设定合理的容差范围 |
| 报告生成 | 自动生成测试报告 | 包含通过率、覆盖率、失败原因 |
| 需求追溯 | 每个用例关联需求ID | 这是MIL测试的灵魂 |
4.4 实战避坑指南
最后,分享几个我踩过的坑:
坑1:仿真时间设置不当
我曾经有一个项目,模型里有个积分环节,仿真时间设得太短,积分还没稳定就结束了。结果测试一直失败,后来发现是仿真时间不够。建议:先跑一个长仿真,观察信号稳定时间,再确定合适的仿真时长。
坑2:信号别名冲突
Simulink里信号名字如果重复,会导致数据记录混乱。我建议在模型里给每个信号加前缀,比如IN_、OUT_、INT_,一眼就能看出信号类型。
坑3:忽略Stateflow的时序
Stateflow的状态切换是有时序的。如果你在同一个时间步里既改变输入又检查输出,可能会得到错误结果。建议:输入信号提前一个时间步注入,或者用delay模块做同步。
嗯,这一章的内容就到这里。测试环境搭建看似基础,但基础不牢,地动山摇。下一章我们会讲测试用例的设计方法,到时候会用到今天搭的环境。记得把代码跑一遍,有问题随时交流。