资讯动态

Simulink软件在环仿真(SIL)实战:从原理到配置与测试

发布时间:2026/8/31 11:20:46 来源:尧图企业网站定制
1. 软件在环仿真SIL是什么为什么需要它在基于模型的设计MBD开发流程中从算法模型到产品代码中间有一个关键过渡点你写的 Simulink 模型最终要变成 C 代码烧录到控制器里。这个过渡往往最怕两件事一是模型能跑但代码行为不对二是生成代码与模型仿真结果不一致。软件在环仿真Software-in-the-LoopSIL就是专门用来验证“模型到代码”这个环节可靠性的手段。通俗理解SIL 就是把“控制器算法”从 Simulink 模型形态替换成“已经编译好的软件代码形态”然后在普通电脑上继续跑仿真。原来的被控对象模型可以保留在 Simulink 中不需要真实的硬件。通过这种方式我们可以在没有 ECU、没有单片机的情况下提前验证目标代码的行为是否符合算法设计。与大家熟悉的 MILModel-in-the-Loop模型在环相比SIL 最大的变化是被测对象发生了变化。MIL 阶段测的是模型本身算法是否正确SIL 阶段测的是代码本身代码是否忠实地实现了模型逻辑。如果只做 MIL 不做 SIL很多代码生成时引入的问题要到 PIL 或者 HIL 阶段才能暴露那时候排错代价会明显增加。SIL 的常见应用场景包括验证自动生成的代码与原始模型行为是否一致。在没有目标硬件的情况下提前开展集成测试和回归测试。验证手写代码与模型代码混合集成后的正确性。进行大批量自动化测试例如参数扫描、随机输入测试、边界条件测试。在 V 型开发流程中作为 MIL 与 PIL/HIL 之间的过渡验证环节。在汽车电子、航空航天、轨道交通等长周期控制器开发项目中SIL 几乎是“标配”。因为控制器软件复杂度很高如果直接跳到硬件测试很多软件逻辑问题不好定位。而 SIL 可以在普通电脑上快速跑完大量测试用例帮助开发者在进入硬件在环测试之前先把软件级问题清干净。要注意的是SIL 不能完全替代 HIL。SIL 只验证软件代码逻辑不验证目标芯片时钟、外设、中断优先级、驱动接口等硬件相关问题。它更像是“软件层面的第一道关口”后续还需要通过 PIL 或 HIL 解决硬件相关风险。2. MIL、SIL、PIL、HIL 的区别与联系很多刚开始学习软件在环仿真的人容易把 SIL、PIL、HIL 混为一谈。这里用一个表格帮大家梳理清楚。仿真模式控制器算法形态被控对象运行环境控制器运行平台主要验证目标MIL模型在环Simulink 模型Simulink 模型开发电脑算法逻辑正确性SIL软件在环编译后的软件代码Simulink 模型或外部模型开发电脑通用处理器代码与模型一致性、软件逻辑PIL处理器在环编译后的软件代码Simulink 模型或外部模型目标处理器/开发板代码在目标处理器上的正确性HIL硬件在环真实控制器ECU/MCU实时仿真机真实控制器控制器软硬件整体行为、时序、故障注入从中可以看出SIL 和 PIL 的区别不在于代码而在于代码运行在哪里SIL 的代码运行在开发电脑的处理器上速度较快方便调试。PIL 的代码运行在目标处理器上虽然平台更接近实际但编译下载和部署成本更高。所以在流程上多数团队会先做 SIL 再做 PIL。SIL 能提前发现算法实现问题PIL 再确认目标平台兼容性问题HIL 最后做闭环系统级验证。这里也顺带解释一个容易混淆的概念SIL 中的“软件”并不一定专指 Simulink 自动生成的代码。如果你的项目有手写 C 代码同样可以把手写代码编译后接入 Simulink 仿真环境通过 SIL 模式验证它与模型算法、被控对象模型之间的协同是否正确。本文后面主要以 Simulink 自动生成代码的场景为例展开。3. SIL 仿真的核心工作流程与原理在深入实操之前先了解 SIL 在 Simulink 内部是怎么工作的这样遇到问题时更容易定位。3.1 SIL 的基本流程Simulink 中做软件在环仿真本质上分为三个步骤根据模型生成 C/C 代码。将生成的代码编译成可执行模块通常以 S-Function 或共享库的形式加载到 Simulink 中。运行仿真。此时模型中原来的控制器算法被替换成这段编译好的代码被控对象模型仍由 Simulink 正常计算。从用户角度看仿真还是那个仿真示波器还能看波形数据还能导出到工作区。但真正参与计算的核心控制器部分已经从“模型解释执行”变成了“目标代码执行”。3.2 SIL 与普通仿真的差异普通仿真模式下Simulink 以解释执行的方式运行模型重点在于验证算法逻辑并不会生成可供嵌入式控制器使用的代码。加速器模式和快速加速器模式虽然也会生成代码但它们的目的是“加速计算”生成代码的路径和产品级代码不是一回事。例如加速器生成的代码不追求运行效率也不会按照目标芯片架构做优化。SIL 模式则不同。使用 SIL 时代码生成配置会走产品代码生成路径例如使用ert.tlc系统目标文件生成适合嵌入式系统的代码。然后这段代码以软件在环的形式参与仿真。正因为代码生成路径与真实产品一致SIL 的验证结果才更有说服力。3.3 SIL 的三种常见接入方式在实际项目中SIL 可以通过三种方式接入接入方式操作位置适用场景顶层模型 SIL模型设置 → 代码生成 → 验证 → 仿真模式对整个模型做验证适合快速验证完整算法模型引用块 SIL被引用模型对应的 Model 块 → 块参数 → 仿真模式对控制器子系统做 SIL被控对象仍保持模型仿真外部代码 SIL将手写 C 代码通过 S-Function 或其他接口集成验证手写代码与模型的协同逻辑其中第二种“模型引用块 SIL”是最符合实际工程习惯的。因为一个完整系统里面往往包含控制器、被控对象、传感器、执行器等模块。真正需要生成代码的是控制器部分被控对象在仿真中通常只是一个被模拟的环境。因此把控制器单独建到一个模型里再以模型引用的方式接入顶层系统然后在模型引用块上打开 SIL是最清晰的验证结构。4. 软件在环仿真环境准备做 SIL 仿真前需要先确认环境和工具箱是否齐全。4.1 必需工具箱Simulink建模和仿真主环境。Simulink Coder负责从模型生成 C/C 代码。Embedded Coder生成适合嵌入式控制器的优化代码属于进阶选项。如果只是学习 SIL有 Simulink Coder 基本够用如果目标是做产品级代码生成建议安装 Embedded Coder。MATLAB Coder如果你的算法通过 MATLAB Function 块编写可能需要用到它。部分 SIL 测试还会用到 Simulink Test、Simulink Coverage前者用于自动化测试管理后者用于代码覆盖率分析。初学者第一阶段不强制安装后面做项目规范化时可以再补充。4.2 C/C 编译器SIL 模式需要把生成的 C 代码编译成可执行模块所以电脑上必须安装可用的 C/C 编译器。在 Windows 环境中常用的编译器是 Microsoft Visual C 或者 MinGW-w64。建议尽量安装官方支持的编译器版本。打开 MATLAB在命令行窗口执行以下命令确认编译器可用mex -setup如果返回结果列出了可用的编译器说明 C 编译器已经配置好如果提示找不到编译器需要先安装对应版本的 Visual Studio 或者 MinGW-w64。4.3 版本说明不同 MATLAB 版本的菜单项和默认配置可能略有差异。本文示例以较新的 MATLAB 版本为主例如 R2021b 及以后版本。如果你使用的是旧版本菜单名称可能稍有不同但核心逻辑一致找到“代码生成”配置页将“仿真模式”设置为“软件在环”再生成代码运行仿真即可。5. 完整实战从模型搭建到 SIL 仿真下面用一个简单但完整的闭环控制系统演示如何从零开始完成软件在环仿真。这个例子包含一个离散 PID 控制器和一个一阶被控对象在工程中非常有代表性。5.1 搭建系统模型假设我们的控制目标是一个简单被控对象传递函数为G(s) 1 / (s 1)控制器采用离散 PID 算法采样时间为 0.01 秒。打开 Simulink新建一个模型命名为sil_demo_top。在模型中添加以下模块模块模块库说明StepSimulink/Sources输入阶跃信号例如幅值为 1SumSimulink/Math Operations计算误差 e r - yDiscrete PID ControllerSimulink/Continuous 或按名称搜索控制器采样时间设为 0.01Transfer FcnSimulink/Continuous被控对象设为 1/(s1)ScopeSimulink/Sinks看输出波形To WorkspaceSimulink/Sinks导出仿真数据到工作区用于 MIL/SIL 对比为了更像工程结构建议把控制器单独封装成子系统。操作方式选中 Discrete PID Controller 模块右键 → Create Subsystem from Selection。这样生成一个名为Subsystem的子系统后续可以把它单独抽出来作为控制器模型。模型连接关系如下Step ──► Sum() ──► PID Controller ──► Transfer Fcn ──► Scope ▲ │ └─────────────────────────────────┘在 To Workspace 模块中设置变量名为yout保存格式选择Timeseries这样后续方便用脚本分析。5.2 设置求解器由于控制器是离散的而被控对象是连续系统仿真需要采用固定步长求解器。打开“模型设置”进入“求解器”页面配置如下类型固定步长 求解器ode4Runge-Kutta 固定步长0.01 停止时间10为什么必须用固定步长因为代码生成之后目标代码的采样周期必须是确定的。如果使用变步长求解器Simulink 会按误差自动调整仿真步长生成代码后这种行为无法在目标控制器上复现导致 SIL 结果失去参考意义。5.3 将控制器封装成模型引用为了让 SIL 验证更符合工程习惯我们需要把控制器独立成一个模型然后在顶层模型中通过“模型引用”方式调用它。步骤一右键控制器子系统选择“Cut”。然后在新建模型中粘贴并给这个新建模型添加两个端口Inport 表示误差输入。Outport 表示控制量输出。将新建模型保存为sil_demo_controller.slx。步骤二回到sil_demo_top顶层模型把原来的子系统删除从模块库中拖入一个 Model 块Simulink / Ports Subsystems / Model双击 Model 块选择sil_demo_controller。此时顶层模型会根据模型引用的端口自动生成输入输出连接。如果连接不对重新连线即可。最后模型结构如下sim_demo_top ├── Model: sil_demo_controller └── Plant: Transfer Fcn Step Scope To Workspace5.4 配置控制器模型的代码生成在生成 SIL 之前需要给控制器模型配置代码生成环境。打开sil_demo_controller.slx进入“模型设置”找到“代码生成”页面代码生成 └─ 系统目标文件ert.tlc这里选择ert.tlc是因为我们要模拟嵌入式控制器代码。如果只是验证功能grt.tlc也可以但嵌入式目标更贴近真实产品。然后进入“代码生成 → 验证”页面将“仿真模式”设置为“软件在环”代码生成 └─ 验证 └─ 仿真模式Software-in-the-loop部分版本中还需要勾选“生成代码报告”这样代码生成完成后会自动打开 HTML 报告方便查看代码结构和变量映射。5.5 在模型引用块上启用 SIL回到sil_demo_top.slx右键点击控制器对应的 Model 块选择“块参数”找到“仿真模式”下拉框仿真模式软件在环Software-in-the-loop如果没有这个选项说明代码生成配置尚未生效请先回到第 5.4 步确认系统目标文件和验证选项已经设置。这里也给出命令行方式便于后续脚本化自动化测试% 切换控制器模型引用块为 MIL 模式 set_param(sil_demo_top/Controller, SimulationMode, normal); % 切换控制器模型引用块为 SIL 模式 set_param(sil_demo_top/Controller, SimulationMode, software-in-the-loop);需要注意sil_demo_top/Controller是模型引用块在顶层模型中的路径实际名称以你的模型为准。5.6 运行 MIL 仿真并保存结果为了对比先运行一次 MIL 仿真也就是让控制器以普通模型模式运行。设置好模型引用块为normal后在命令行执行simOut_mil sim(sil_demo_top, StopTime, 10); y_mil simOut_mil.yout{1}.Values.Data; t_mil simOut_mil.tout;仿真结束后把 MIL 结果保存下来save(mil_result.mat, t_mil, y_mil);此时可以打开 Scope 查看波形正常的闭环阶跃响应应该先上升再稳定到 1超调量不大。5.7 运行 SIL 仿真并对比结果接下来把控制器模型引用块切换为 SIL 模式set_param(sil_demo_top/Controller, SimulationMode, software-in-the-loop);然后再次运行仿真simOut_sil sim(sil_demo_top, StopTime, 10); y_sil simOut_sil.yout{1}.Values.Data; t_sil simOut_sil.tout;首次运行 SIL 时Simulink 会先生成代码再调用编译器编译。命令行会输出类似下面的构建信息### Starting build procedure for model: sil_demo_controller ### Successful completion of build procedure for model: sil_demo_controller构建完成后仿真会自动继续。SIL 仿真通常比普通仿真慢一些因为每个采样步长都要通过 S-Function 调用生成的代码。对比两种模式的结果figure; plot(t_mil, y_mil, b-, LineWidth, 1.5); hold on; plot(t_sil, y_sil, r--, LineWidth, 1.5); grid on; legend(MIL, SIL); xlabel(时间 t/s); ylabel(输出 y); title(MIL 与 SIL 仿真结果对比);正常情况下两条曲线会基本重合。如果两条曲线有明显差异说明控制器代码生成过程中存在数据精度、采样周期或初始化问题需要进一步排查。5.8 在构建日志中定位生成代码SIL 运行完成后工作目录下会生成slprj文件夹里面存放代码生成中间产物。如果项目配置了代码生成报告也可以直接在报告里查看控制器代码。对于简单的 PID 控制器代码量不大可以重点看以下几个文件sil_demo_controller.c sil_demo_controller.h sil_demo_controller_private.h对比生成代码里的 PID 系数和 Simulink 模型中的离散 PID 参数确认系数是否一致。这一步是 SIL 验证中非常实用的细节很多项目都是在这里发现参数定标或变量名映射问题。6. 常见问题与排查思路SIL 仿真在第一次使用时容易遇到一些配置和编译问题。这里整理最常见的几种情况。问题现象常见原因解决思路模型引用块仿真模式里没有“软件在环”选项控制器模型未正确配置代码生成目标打开控制器模型设置系统目标文件ert.tlc保存后再回到顶层模型构建时报错找不到 System Target File未安装 Simulink Coder 或 Embedded Coder检查工具箱列表确认两个工具箱已安装且许可有效编译报错找不到 C 编译器未安装编译器或 MATLAB 未识别运行mex -setup选择正确的编译环境SIL 仿真结果与 MIL 差异较大数据类型不一致、求解器步长不合适、PID 初始化状态不一致检查模型数据类型设置相同固定步长对比初始化条件仿真运行非常慢SIL 每次调用都要经过代码生成、编译和外部接口调用避免在模型内部频繁使用不必要的数据转换大批量测试用脚本批处理生成代码文件特别多找不到关键文件项目目录未整理代码生成中间文件混杂使用 Simulink 项目管理将代码生成路径独立到slprj或指定工作文件夹切换 SIL 后模型报 “Simulation mode cannot be changed”模型正在运行或代码生成未完成先停止仿真清理生成代码重新构建信号日志丢失无法获取输出信号没有标记保存或优化将信号优化掉了使用 Outport 导出信号或者在模型设置中开启信号日志如果你遇到的是比较模糊的编译错误建议优先清理以下内容% 关闭当前模型 bdclose(sil_demo_top); % 删除代码生成中间目录 if exist(slprj, dir) rmdir(slprj, s); end % 重新打开模型 open_system(sil_demo_top);这种“清缓存、重新生成代码、重新运行”的操作能解决大部分奇怪的构建问题。7. 软件在环仿真的最佳实践与工程建议SIL 不是一个简单的开关用好了能显著提升代码质量用不好会变成重复劳动。下面这些建议来自实际项目经验供参考。7.1 为不同验证阶段建立独立配置集在一个模型中可能同时存在 MIL、SIL、PIL 三种模式的配置需求。如果每次手动改求解器和代码生成参数很容易出错。建议使用 Simulink 的“配置引用”功能为不同仿真阶段创建独立配置集。在模型设置中Simulation → Model Configuration Parameters → Active configuration或者使用脚本% 创建配置引用 configRef Simulink.ConfigSetRef; configRef.Name SIL_Config;实际项目中通常会有MIL_Config、SIL_Config、PIL_Config三套配置。这样切换验证模式时只需激活对应配置集不需要逐项核对参数。7.2 用数据字典管理参数SIL 验证过程中经常需要修改控制器参数、采样时间、运行时长。如果这些参数分散在模型内部维护成本很高。推荐把参数集中到 Simulink 数据字典中例如controllerParams.sldd。在数据字典中定义Ts 0.01; Kp 1.2; Ki 0.5; Kd 0.1;模型中的 PID 控制器参数直接引用这些变量。这样在自动化测试时只需要修改字典中的数值就能批量跑不同参数组合下的 SIL 仿真。7.3 用脚本实现 SIL 自动化测试SIL 适合大批量测试但手工点击“运行”显然太低效。可以用脚本驱动仿真和结果对比。这里给出一个简单的自动化框架思路% 定义测试参数 paramList [1.0, 0.2, 0.05; 1.2, 0.5, 0.10; 0.8, 0.1, 0.01]; for i 1:size(paramList, 1) % 更新控制器参数 Kp paramList(i, 1); Ki paramList(i, 2); Kd paramList(i, 3); % 运行 MIL set_param(sil_demo_top/Controller, SimulationMode, normal); simOutMIL sim(sil_demo_top, StopTime, 10); % 运行 SIL set_param(sil_demo_top/Controller, SimulationMode, software-in-the-loop); simOutSIL sim(sil_demo_top, StopTime, 10); % 对比 yMIL simOutMIL.yout{1}.Values.Data; ySIL simOutSIL.yout{1}.Values.Data; diffMax max(abs(yMIL - ySIL)); fprintf(测试用例 %d最大误差 %.6f\n, i, diffMax); end这种方式虽然简单但已经具备仿真测试自动化雏形。如果项目规模更大建议使用 Simulink Test 管理测试用例、预期结果和覆盖率数据。7.4 数据类型与步长一致性是重点SIL 结果和 MIL 结果不一致最常见的根因就是数据类型和采样步长。确认控制器模型输入输出都是double如果使用定点数要检查定标方式。确认仿真求解器是固定步长且步长与控制器采样时间存在合理关系。确认模型中的单位延迟、PID 初始状态在两次仿真中一致。如果模型中存在 MATLAB Function 块建议用codegen提前检查类型推导结果。7.5 SIL 不是越早越好也不是越多越好SIL 验证是有成本的尤其是代码生成和编译阶段比较耗时。建议在以下时间点做 SIL算法模型稳定、MIL 测试通过之后。控制器接口定义完成之后。每次修改控制器代码生成相关配置时。每次修改控制器算法逻辑时。不建议在算法还在频繁调整的阶段就反复跑 SIL那样只会增加编译等待时间。建议先在 MIL 阶段把算法迭代成熟再进入 SIL 阶段做代码级验证。7.6 保留构建日志和测试记录在 V 型开发流程中SIL 测试结果往往需要作为交付物提交给质量团队或配合安全标准评审。因此建议每次 SIL 测试保留以下信息MATALB 和 Simulink 版本号。系统目标文件名称。求解器和步长设置。测试用例列表。MIL 与 SIL 对比误差。生成的代码报告。这些记录不仅方便追溯问题也是很多行业标准评审时的必要材料。8. 总结与下一步学习建议本文围绕 Simulink 软件在环仿真讲解了 SIL 的基本概念、与 MIL/PIL/HIL 的关系、环境准备、核心配置、完整实战流程以及常见问题排查方法。通过一个离散 PID 控制器的闭环系统演示了如何从普通模型仿真切换到 SIL 仿真并对比验证结果。掌握 SIL 之后下一步可以继续学习PIL 处理器在环仿真看看代码在目标处理器上的行为差异。HIL 硬件在环仿真结合实时仿真机验证真实控制器。Simulink Test 自动化测试把 SIL 变成可持续的回归测试体系。代码覆盖率分析了解测试用例对代码的覆盖情况。建议你先找一个小模型比如本文的 PID 闭环系统把 MIL 到 SIL 的流程完整跑一遍。第一次跑通可能花一些时间但跑通之后后续再做复杂控制器的软件验证就会顺很多。如果你在实际操作中遇到其他问题欢迎在评论区留言交流。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价