2. Simulink 建模基础回顾

MIL 与 SIL 验证能否顺利开展,很大程度上不取决于测试脚本写得多漂亮,而取决于被验证的模型本身是否具备“可测试性”。一个随手搭建、信号类型混乱、采样时间随意、层次结构扁平的模型,往往在 MIL 阶段就频繁报错,到了 SIL 阶段更会因为代码生成接口不匹配而寸步难行。本节围绕四个基础要素——常用模块库、信号与数据类型、采样时间、子系统与模型引用——梳理如何建立一个面向后续验证的模型基线。

常用模块库:先分清“连续”与“离散”

Simulink 的库浏览器中模块众多,但对控制类模型而言,真正高频使用的集中在几个库:Sources(Step、Sine Wave、Constant、Signal Builder)、Sinks(Scope、To Workspace、Display)、Continuous(Integrator、Transfer Fcn、State-Space)、Discrete(Unit Delay、Discrete Transfer Fcn、Zero-Order Hold)、Math Operations(Gain、Sum、Product)、Logic and Bit Operations、Signal Routing(Mux、Demux、Switch)以及 User-Defined Functions(MATLAB Function、Fcn)。

这里有一个容易被忽视但直接影响 SIL 的关键点:连续模块无法直接生成嵌入式 C 代码。Integrator、Transfer Fcn 这类模块在代码生成时会被要求离散化,或者必须配合定步长求解器。因此,如果模型最终要走向 SIL 甚至产品代码,从建模之初就应优先使用 Discrete 库中的模块,把控制器部分建成离散形式,而把被控对象(Plant)保留为连续形式,仅在 MIL 阶段用连续求解器验证算法正确性。这种“控制器离散、对象连续”的划分,是后续 MIL/SIL 对比能够对齐的前提。

信号与数据类型:验证对齐的隐形基础

Simulink 默认的信号类型是 double,但嵌入式目标通常使用 single、int16、uint8、boolean 等类型。如果模型内部全是 double,而生成代码时又要求定点或单精度,就会出现大量隐式类型转换,SIL 与 MIL 的结果差异往往就来自这些转换。

建议在建模阶段就显式指定信号类型。可以通过 Signal Attributes 中的 Data Type 设置,也可以在模型配置参数里开启 “信号解析”相关的诊断。对于需要严格对齐的验证场景,推荐使用 Simulink.Bus 定义总线对象,把一组相关信号打包,这样接口清晰,SIL 阶段的 C 结构体也能一一对应。

另一个实用做法是给关键信号命名并开启 信号记录(Signal Logging)。在 MIL 与 SIL 对比时,同名信号的记录数据可以直接做差值分析,而不需要手动去 Scope 里找曲线。命名规范建议采用“模块名_信号含义”的形式,例如 Ctrl_GainOut、Plant_Speed,避免出现 Out1、Signal3 这类无意义名称。

采样时间:决定模型能否被代码生成

采样时间是 Simulink 建模中最容易埋雷的地方。模型中如果存在多个不同采样率的模块,Simulink 会以最慢的采样率作为基础采样时间,并可能触发速率转换。对于 SIL 验证,多速率模型会显著增加代码生成的复杂度,因为每个采样率都需要独立的任务调度。

面向验证的建模建议是:尽量使用单一采样时间。例如控制器统一用 Ts = 0.001 s(1 kHz),被控对象若为连续则用连续求解器,若也要离散则同样取 0.001 s 或整数倍关系。设置方式是在模块参数中把 Sample time 显式写成 0.001,而不是留 -1(继承)。留 -1 虽然方便,但会让采样时间在模型各处传播,后期排查非常困难。

可以用 Ctrl+D 更新模型后,在 Diagnostics → Sample Time 中查看采样时间图例,确认没有意外的速率转换。如果确实需要多速率,应使用 Rate Transition 模块显式处理跨速率信号,而不是让 Simulink 自动插入。

子系统与模型引用:为验证划分边界

当模型规模增大,扁平结构会让 MIL 测试难以定位问题。此时应使用子系统(Subsystem)把功能相关的模块封装起来,例如把控制器封装为 Controller 子系统,把被控对象封装为 Plant 子系统。子系统不仅让模型层次清晰,更重要的是它定义了测试边界:MIL 阶段可以单独给 Controller 子系统输入激励,观察其输出;SIL 阶段则可以把 Controller 子系统单独生成代码,与 Plant 在 Simulink 中联合仿真。

如果多个模型需要共享同一个控制器,或者控制器要独立生成代码,则应使用模型引用(Model Reference)。模型引用把被引用模型当作独立单元编译和代码生成,支持增量编译,也支持 SIL/PIL 模式切换。典型做法是:顶层模型 Top 引用 Controller.slx 和 Plant.slx,在 Top 的配置中把 Controller 的 Model block 设置为 SIL 模式,即可在不改动顶层结构的情况下完成 MIL→SIL 的切换。

模型引用与子系统的选择原则:需要独立代码生成、独立测试、多人协作或增量编译时用模型引用;仅用于组织图形层次、不需要独立生成代码时用子系统。

可测试模型基线的检查要点

综合以上四点,一个面向 MIL/SIL 的模型基线应满足:控制器使用离散模块、采样时间显式且统一、关键信号有明确类型和名称、功能边界用子系统或模型引用划分。下面这张图把四个要素与验证阶段的对应关系整理在一起,便于建模时逐项核对。

面向 MIL/SIL 的可测试模型基线四要素 常用模块库 • 控制器:Discrete 库 • 对象:Continuous 库 • 避免连续模块直接 生成嵌入式代码 信号与数据类型 • 显式指定 Data Type • 用 Bus 打包接口 • 关键信号命名并 开启 Signal Logging 采样时间 • 尽量单一采样率 • 显式写 Ts,不留 -1 • 多速率用 Rate Transition 显式处理 子系统与模型引用 • 子系统划分测试边界 • 模型引用支持独立 代码生成与 SIL 切换 • 增量编译、多人协作 可测试模型基线 结构清晰 · 类型明确 · 采样统一 MIL 阶段 算法逻辑验证 · 连续求解 SIL 阶段 生成 C 代码 · 定点/单精度 结果对比 同名信号差值 · 容差判断 建模阶段每满足一项,后续验证的返工成本就降低一层 示例参数:控制器 Ts = 0.001 s,信号类型 single,接口用 Simulink.Bus 定义

需要强调的是,上述基线不是一次性配置完就万事大吉。随着模型迭代,采样时间可能被新加入的模块破坏,信号类型可能被隐式转换覆盖,模型引用的接口也可能因参数变更而失配。因此建议在每次 MIL 测试前,用 Simulink.BlockDiagram.getSampleTimes 或模型顾问(Model Advisor)中的“采样时间检查”快速扫描一遍,把问题挡在验证之前。

当模型满足这些基础条件后,MIL 阶段的测试用例才能稳定复现,SIL 阶段的代码生成才有明确的接口边界,后续的对比分析也才有可靠的数据来源。下一阶段的工作,就是在这个基线上搭建具体的测试框架与激励信号。