3. MIL环境搭建与模型规范

MIL(Model-in-the-Loop)阶段的核心产物是“可被后续 SIL/HIL 直接复用的模型资产”。如果 MIL 阶段只追求“能跑出曲线”,而工具链、求解器、命名、单位、版本各自为政,那么进入 SIL 时往往要重做接口,进入 HIL 时又要重做标定与代码生成配置,返工成本极高。因此本节的重点不是“把模型搭出来”,而是把模型搭成可移植、可追溯、可自动构建的工程资产。

一、建模工具链:先定“主干”,再定“插件”

工具链的第一原则是单一主干。建议以 MATLAB/Simulink 作为模型主干,Stateflow 承担状态逻辑,Simulink Test 承担 MIL 测试,Embedded Coder 承担后续 SIL/HIL 的代码生成。外围工具只做“插件”,例如用 Git 做版本管理,用 Jenkins 或 GitLab CI 做自动构建,用 Excel/JSON 做参数标定表。主干一旦确定,模型库、回调脚本、代码生成配置都围绕它组织,避免出现“一半模型在 Simulink、一半逻辑在 Python 脚本”的割裂状态。

工具链还要固定目录骨架。一个可直接复用的 MIL 工程建议采用如下结构:

project_root/
├─ model/            # .slx 模型与库
├─ data/             # 参数 .m / .mat / .json
├─ test/             # MIL 测试用例与基线
├─ script/           # 初始化、构建、检查脚本
├─ config/           # 代码生成与求解器配置
└─ doc/              # 接口表、单位表、变更记录

目录骨架的意义在于:SIL 阶段只需替换“被测对象”的编译产物,HIL 阶段只需替换 I/O 驱动,而模型、参数、测试用例的路径不变,脚本无需重写。

二、求解器设置:定步长是 SIL/HIL 复用的前提

MIL 阶段常有人用变步长求解器(如 ode45)追求速度,但变步长在 SIL/HIL 中会带来两个问题:一是代码生成后步长不确定,二是与 HIL 实时步长不匹配。因此,凡是要进入 SIL/HIL 的模型,MIL 阶段就应使用定步长离散求解器,推荐 Fixed-step discrete,步长与目标 ECU 任务周期一致。

以某电机控制器为例,电流环任务周期为 100 μs,则 MIL 模型求解器设置为:

Solver:        Fixed-step
Solver type:   Discrete (no continuous states)
Fixed-step:    1e-4   % 100 μs
Sample time:   1e-4   % 与任务周期一致

同时要检查模型中是否存在连续状态(如积分器未离散化)。若存在,应改用离散积分器或前向欧拉,否则代码生成会报“连续状态不支持定步长离散”的错误。求解器设置还应写入 config/ 下的配置脚本,由初始化脚本统一加载,避免每次手工点选。

三、信号命名与单位约定:接口表的“法律效力”

信号命名和单位约定是 SIL/HIL 复用中最容易被忽视、却最容易出错的环节。建议采用“方向_对象_物理量_单位”的命名规则,例如 In_MotorSpd_rpm、Out_IdqRef_A、In_TrqCmd_Nm。方向前缀区分输入输出,对象段说明信号归属,物理量段说明含义,单位后缀直接写在名字里,避免“看名字猜单位”。

单位约定要形成一张接口表,至少包含:信号名、方向、数据类型、单位、量程、默认值、采样周期。下表为示例片段:

信号名方向类型单位量程周期
In_MotorSpd_rpm输入singlerpm-12000~12000100 μs
In_TrqCmd_Nm输入singleNm-200~200100 μs
Out_IdqRef_A输出singleA-400~400100 μs
Out_PwmDuty_pu输出uint16pu0~1100 μs

接口表一旦冻结,SIL 的测试用例、HIL 的 I/O 映射都直接引用它,不再各自定义。若后续需要修改,必须走变更流程并同步更新接口表版本号。

四、模型版本管理:让“可复用”可追溯

模型版本管理不是简单地把 .slx 丢进 Git。二进制模型文件无法 diff,因此要配合模型导出为 XML 或使用 Simulink 的模型比较工具,并在提交时附带变更说明。建议每次提交包含:模型文件、接口表、参数文件、测试基线、变更记录。版本号采用 主版本.次版本.修订号,例如 v1.2.0,主版本对应接口变更,次版本对应功能变更,修订号对应参数或缺陷修复。

此外,模型应设置只读保护与签出机制,避免多人同时编辑同一模型。对于要进入 HIL 的模型,还应打上 release 标签,并记录对应的代码生成配置哈希,确保“模型版本—代码版本—标定版本”三者一致。

MIL 模型资产复用链:工具链 · 求解器 · 接口 · 版本 建模工具链主干 Simulink / Stateflow Simulink Test Embedded Coder Git / CI 插件 求解器设置 Fixed-step discrete 步长 = 任务周期 100 μs 示例 无连续状态 信号命名与单位 方向_对象_物理量_单位 In_MotorSpd_rpm Out_IdqRef_A 接口表冻结 版本管理 v1.2.0 模型+接口+参数 +测试基线 release 标签 MIL 模型资产(可复用) 模型 .slx + 接口表 + 参数集 + 测试基线 + 构建脚本 SIL 复用 替换被测对象为编译产物 接口表 / 测试用例不变 HIL 复用 替换 I/O 驱动与实时步长 模型 / 标定 / 版本一致 图:MIL 阶段四项规范共同构成 SIL/HIL 可复用的模型资产

五、把规范落到脚本与检查项

规范若只写在文档里,执行时必然走样。建议把上述约定固化为初始化脚本 + 模型检查脚本。初始化脚本负责加载求解器配置、参数文件和接口表;模型检查脚本在每次提交前自动运行,检查项包括:求解器是否为定步长离散、是否存在未离散的连续状态、信号名是否符合命名规则、单位后缀是否与接口表一致、模型版本号是否更新。

例如,一个简化的检查脚本可以这样组织:

% check_model.m
mdl = 'MotorCtrl_MIL';
load_system(mdl);
cs = getActiveConfigSet(mdl);
assert(strcmp(get_param(cs,'SolverType'),'Fixed-step'), '求解器必须为定步长');
assert(strcmp(get_param(cs,'Solver'),'FixedStepDiscrete'), '必须使用离散求解器');
assert(strcmp(get_param(cs,'FixedStep'),'1e-4'), '步长必须为 100 μs');
% 信号名检查
sigNames = find_system(mdl,'FindAll','on','Type','line');
for i = 1:numel(sigNames)
    nm = get_param(sigNames(i),'Name');
    assert(~isempty(regexp(nm,'^(In|Out)_[A-Za-z]+_[A-Za-z]+_(rpm|Nm|A|pu|V)$','once')), ...
        ['信号命名不合规: ' nm]);
end

这类脚本一旦接入 CI,任何不合规的模型都无法合入主干,SIL/HIL 阶段拿到的就是“干净”的模型资产。实际项目中,检查项可根据团队约定增减,但求解器、命名、单位、版本这四项建议作为强制项保留。

复用判断依据:当 MIL 模型满足“定步长离散求解器 + 接口表冻结 + 命名单位合规 + 版本标签可追溯”四项条件时,即可认为具备进入 SIL 的复用资格;若其中任一项缺失,应在 MIL 阶段补齐,而不是留到 SIL/HIL 阶段补救。

最后需要强调,MIL 环境搭建不是一次性任务,而是贯穿模型生命周期的约束。工具链主干决定模型能否被自动构建,求解器设置决定模型能否被代码生成,命名与单位决定接口能否被 SIL/HIL 直接映射,版本管理决定问题能否被追溯。把这四件事在 MIL 阶段做扎实,后续 SIL 和 HIL 的工作量会显著下降,模型资产才真正具备“一次建模、多阶段复用”的价值。