4、测试环境搭建(MIL阶段):Simulink/Stateflow模型配置、测试用例注入方式、信号观测与记录

好,咱们进入MIL阶段最实操的一环——搭环境。

说实话,很多工程师觉得MIL就是跑个仿真,随便拉几个信号看看波形就完事了。但我在项目里吃过亏,有一次模型跑出来结果对不上,查了两天才发现是信号采样率没对齐。嗯,从那以后,我对环境搭建这件事就特别较真。

4.1 Simulink/Stateflow模型配置要点

先说说模型配置。你打开Simulink,按Ctrl+E进入配置参数面板,这里有几个关键项我每次都会检查:

配置项 推荐设置 我的备注
Solver类型 Fixed-step MIL阶段用定步长,方便和后续SIL/HIL对齐
步长 与目标控制器任务周期一致 比如10ms或1ms,别乱设
离散求解器 auto(自动) 除非你有连续状态,否则别碰连续求解器
信号存储 勾选“Signal logging” 方便后续分析,我习惯把输出信号全打出来
状态记录 勾选“Stateflow状态记录” 调试Stateflow时这个太重要了

核心原则:MIL阶段的配置要尽量贴近最终代码运行环境。步长、求解器类型这些,一旦定下来就别轻易改,否则你后面SIL阶段对不上数据,哭都来不及。

我个人习惯在模型里加一个“Config”子系统,专门放这些全局参数。这样换项目时,直接复制这个子系统,改几个参数就行,省得每次从头配。

4.2 测试用例注入方式

测试用例怎么喂进模型?说白了就三种路子。我按实战经验排个序:

  1. 直接信号源注入——最粗暴,也最常用
  2. Test Harness方式——官方推荐,隔离性好
  3. MATLAB脚本驱动——适合批量自动化

4.2.1 直接信号源注入

就是在模型输入端接上Signal Builder、Signal Editor或者Step、Ramp这些基础模块。适合快速验证单个场景。

举个例子,你要测一个油门踏板的急踩工况:

// 在Signal Editor里设置时间序列
Time: [0, 0.5, 0.6, 1.0]
Value: [0, 0, 100, 100]  // 0.5秒时突然踩到100%

这种方式的优点是快,缺点是你得手动调。我一般只在调试阶段用,正式回归测试不会这么干。

4.2.2 Test Harness方式

这是Simulink Test工具箱提供的功能。你右键模型,选“Test Harness → Create”。它会自动生成一个独立的测试环境,把被测模型包在里面。

好处是什么?

  • 测试用例和模型分离,不会污染原模型
  • 可以同时跑多个Harness,对比不同输入下的表现
  • 支持导入Excel/CSV格式的测试数据

我在项目中遇到过一个问题:用Harness跑测试时,模型内部的全局变量访问不到。后来发现需要在Harness里手动添加“Data Store Memory”模块。这个坑我踩过,你们注意一下。

4.2.3 MATLAB脚本驱动

这个才是工业级玩法。写个脚本,循环调用sim()函数,批量注入测试用例。

% 批量测试示例
testCases = [10, 20, 50, 80, 100];  % 油门开度(%)
for i = 1:length(testCases)
    set_param('myModel/Throttle', 'Value', num2str(testCases(i)));
    simOut = sim('myModel');
    results(i) = simOut.logsout;
end

你想想看,几百个测试用例,手动点得点到什么时候?脚本一跑,喝杯咖啡回来结果全有了。

我的小技巧:在脚本里加个进度条,用waitbar函数。跑长仿真时,看着进度条走心里踏实。

4.3 信号观测与记录

信号怎么看?怎么存?这里头门道不少。

4.3.1 实时观测

仿真过程中,我习惯用Scope模块。但注意,Scope多了会拖慢仿真速度。我的做法是:

  • 关键信号(比如输出、状态机跳转条件)挂Scope
  • 次要信号用To Workspace模块存到MATLAB工作区
  • 仿真结束后统一用plot()画图

另外,Stateflow的活性状态观测也很重要。你可以在Stateflow编辑器里右键,选“Debug → Chart Animation”,仿真时能看到状态是怎么跳的。这个功能我调试逻辑错误时必开。

4.3.2 数据记录策略

记录数据,我推荐用logsout方式。在模型配置里勾选“Signal logging”,然后给要记录的信号打上勾选标记。

仿真结束后,数据存在simOut.logsout里。你可以这样提取:

% 提取记录信号
logs = simOut.logsout;
signal1 = logs.getElement('Speed');
time = signal1.Values.Time;
data = signal1.Values.Data;

% 画图
plot(time, data);
grid on;
xlabel('时间 (s)');
ylabel('速度 (km/h)');
记录方式 优点 缺点 我推荐场景
To Workspace 简单直接 数据量大时占内存 少量信号调试
Signal Logging 结构化存储,方便后处理 配置稍复杂 正式回归测试
To File (.mat) 持久化存储,不占内存 读写速度慢 超长仿真(比如24小时)
Scope 实时可视化 拖慢仿真,不可回放 快速验证波形

注意:千万别把所有信号都打上记录标记。我曾经在一个大模型上这么干过,结果仿真跑了半小时,生成的日志文件有2个G。后来我学乖了,只记录接口信号和关键内部状态。

4.4 实战检查清单

每次搭建MIL测试环境,我都会过一遍这个清单:

  1. 求解器步长是否和目标控制器一致?
  2. 测试用例注入方式是否支持自动化回归?
  3. 关键信号是否都打了记录标记?
  4. Stateflow的活性状态观测是否开启?
  5. 日志文件大小是否可控?
  6. 有没有做基线仿真(Baseline)用于对比?

最后一条特别重要。我第一次做MIL时没留基线,后来模型改了一版,想对比前后差异,结果发现没有参考数据。嗯,从那以后,每次环境搭好,我先跑一个标准工况,把结果存成.mat文件,这就是你的“黄金参考”。

好了,MIL阶段的环境搭建就聊到这儿。下一章咱们进入SIL阶段,看看怎么把模型生成的代码跑起来,以及MIL和SIL的结果怎么对标。