2. Simulink模型工程化基础

在把Simulink模型接入CI/CD之前,必须先解决一个更基础的问题:模型本身是不是一份“可被机器稳定构建和验证的资产”。如果模型依赖某个工程师桌面上的变量、某个未纳入版本管理的MAT文件、某个只在本地生效的配置参数,那么流水线即使跑起来,结果也不可复现。本节围绕模型文件组织、数据字典、配置集、回调函数以及项目与工程引用五个方面,说明如何把模型整理成CI/CD可管理的工程资产。

一、模型文件组织:让目录结构表达依赖关系

工程化的第一步是让文件系统结构反映模型之间的真实依赖。一个可被流水线稳定构建的Simulink工程,通常按“入口—组件—数据—配置—脚本—产物”分层组织,而不是把所有slx文件平铺在一个目录里。例如:

VehicleCtrl_Project/
├─ models/
│  ├─ top/VehicleCtrl_Top.slx        % 顶层集成模型
│  ├─ subsys/MotorCtrl.slx           % 子系统模型(模型引用)
│  └─ subsys/BrakeCtrl.slx
├─ data/
│  ├─ VehicleCtrl.sldd               % 数据字典
│  └─ calibration/veh_params.mat     % 标定数据(纳入版本管理)
├─ config/
│  ├─ cfg_mil.mat                    % MIL配置集
│  └─ cfg_sil.mat                    % SIL配置集
├─ scripts/
│  ├─ setup_project.m                % 工程初始化脚本
│  └─ run_regression.m               % 回归入口
└─ artifacts/                        % 流水线产物(不纳入版本管理)

这里的关键约定是:models/下只放模型,data/下只放数据,config/下只放配置集,artifacts/由流水线生成且加入.gitignore。目录结构一旦固定,流水线脚本就可以用相对路径定位资产,而不依赖任何绝对路径。模型引用(Model Reference)应优先于库链接(Library)用于需要独立编译和独立测试的组件,因为模型引用支持增量构建和独立仿真,这对回归测试的并行化至关重要。

二、数据字典:把变量从“工作区”搬进“受管资产”

Simulink模型中的参数、信号和总线对象如果定义在基础工作区(Base Workspace),就只存在于当前MATLAB会话中,CI代理上不会自动拥有这些变量。数据字典(.sldd)的作用是把这些定义持久化为可版本管理的文件。典型做法是:在数据字典中建立Design Data和Calibration两个分区,前者存放Simulink.Parameter和Simulink.Signal对象,后者存放标定用的数值。

以电机控制为例,数据字典中可定义:

% 在数据字典中定义参数对象
MotorCtrl.Kp = Simulink.Parameter(1.25);
MotorCtrl.Kp.DataType = 'single';
MotorCtrl.Kp.Min = 0;  MotorCtrl.Kp.Max = 10;
MotorCtrl.Kp.DocUnits = 'N*m/A';

MotorCtrl.SpeedRef = Simulink.Signal;
MotorCtrl.SpeedRef.DataType = 'single';
MotorCtrl.SpeedRef.Dimensions = 1;

模型通过Simulink.data.dictionary接口或直接在模型属性中绑定该字典。这样,CI代理检出代码后,只要加载数据字典,模型就能解析所有符号。需要特别注意的是,数据字典本身是二进制文件,Git无法做行级合并,因此团队应约定“同一时间只有一人修改字典”,或改用基于脚本的.m文件生成字典,以便在流水线中重建。

三、配置集:把仿真配置从模型里“抽出来”

配置集(Configuration Set)保存求解器、步长、代码生成目标、诊断级别等设置。如果这些设置直接写在模型里,MIL、SIL、代码生成三种场景就会互相干扰。正确做法是为每种场景建立独立的配置集,并在流水线中按需激活。例如:

配置集求解器步长主要用途
cfg_milode45变步长模型在环功能验证
cfg_silFixedStepDiscrete0.001 s软件在环、与C代码比对
cfg_codegenFixedStepDiscrete0.001 s嵌入式代码生成

在脚本中可通过setActiveConfigSet切换,或在SimulationInput对象中指定配置集,使每次回归运行都使用确定的配置,避免“上次手动改过求解器”这类不可复现问题。

四、回调函数:把初始化逻辑变成可执行契约

模型的PreLoadFcn、PostLoadFcn、InitFcn等回调函数,是模型加载和仿真时自动执行的钩子。工程化用法是把“加载数据字典、设置路径、检查依赖”等动作写进PostLoadFcn,而不是依赖工程师手动运行脚本。例如:

% PostLoadFcn 示例
if ~bdIsLoaded('VehicleCtrl_Top')
    load_system('VehicleCtrl_Top');
end
% 绑定数据字典
Simulink.data.dictionary.open('VehicleCtrl.sldd');
% 添加工程路径
addpath(genpath(fullfile(pwd,'scripts')));

但回调函数也有风险:如果其中包含交互式命令(如input、uiwait)或依赖图形界面,CI代理会挂起。因此,回调中只应保留无交互、幂等的初始化逻辑,并把复杂逻辑封装到scripts/下的函数中,由回调调用。

五、项目与工程引用:把资产打包成可复现单元

MATLAB Project(.prj)把上述模型、数据字典、配置集、脚本和路径设置打包成一个可引用的工程单元。工程引用(Project Reference)允许一个工程引用另一个工程,从而支持多团队并行开发。在CI中,推荐的做法是:顶层工程引用组件工程,流水线检出顶层工程后,通过openProject自动解析所有引用并设置路径。

Simulink工程资产组织与CI加载关系 VehicleCtrl_Project.prj 顶层工程(CI检出入口) MotorCtrl_Project.prj 组件工程(模型引用) MotorCtrl.slx + 数据字典 BrakeCtrl_Project.prj 组件工程(模型引用) BrakeCtrl.slx + 数据字典 VehicleCtrl.sldd Design Data / Calibration cfg_mil / cfg_sil / cfg_codegen 按场景激活的配置集 PostLoadFcn / InitFcn 无交互、幂等的初始化钩子 CI检出后:openProject → 解析引用 → 加载数据字典 → 激活配置集 → 执行回调 → 运行回归

图中展示了从顶层工程到组件工程、数据字典、配置集和回调函数的加载关系。CI流水线的典型入口是openProject('VehicleCtrl_Project.prj'),它会自动解析工程引用、设置路径、加载数据字典并触发回调。这样,流水线脚本只需要一条命令就能把整个工程恢复到可构建状态,而不需要逐项手动配置。

工程引用中的组件工程应保持“可独立打开、可独立仿真”的能力。如果组件工程必须依赖顶层工程才能运行,说明依赖关系被反向耦合了,应把共享数据下沉到独立的数据字典工程中。

六、为CI/CD准备可管理资产的检查要点

在把上述资产接入流水线之前,建议逐项确认:模型是否全部使用相对路径引用;数据字典是否纳入版本管理且不依赖基础工作区;配置集是否按场景分离且可脚本激活;回调函数是否无交互、无图形界面依赖;工程引用是否形成有向无环图,避免循环引用。这些检查可以直接写成scripts/check_assets.m,在流水线的“静态检查”阶段执行,失败即阻断后续构建。

完成本节整理后,模型不再是一堆散落的slx文件,而是一份结构清晰、依赖明确、可被脚本完整重建的工程资产。这是后续搭建持续集成流水线、实现自动化回归测试的前提。