尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

软件在环仿真SIL:模型代码生成前必须掌握的验证方法

软件在环仿真SIL:模型代码生成前必须掌握的验证方法 做控制算法的人很多都卡在一个问题上模型里跑得好好的生成C代码烧到硬件上却不对。排查半天发现问题不在算法本身而是模型到代码之间缺了一层验证。这次我们要梳理的就是这一层验证软件在环仿真也就是常说的SILSoftware-in-the-Loop。SIL的价值一句话可以概括在不用真实硬件的前提下把控制器模型生成C代码并编译成可执行程序再和被控对象模型联合仿真用于确认“代码”和“模型”在逻辑上是否等价。它处于MIL模型在环和PIL处理器在环之间属于自动代码生成流程里成本最低、最容易提前发现问题的一环。本文将用一个典型控制模型的SIL测试流程演示从模型准备、仿真模式切换、代码生成到结果对比的全过程并整理一批常见的坑和排查方法。如果你正在做Simulink建模、VCU控制策略、车辆运动学模型或者开始接触Simulink代码生成、MIL测试、Carsim联合仿真这篇文章可以直接收藏。理解SIL之后再去看PIL和HIL你会非常清楚它们各自在解决什么问题。1. 核心能力速览SIL并不是一个独立工具而是MATLAB/Simulink代码生成链路中的一种仿真模式。下面先把关键信息列出来。能力项说明项目定位模型生成C代码后在主机上执行代码并与模型仿真对比核心目的验证模型与生成代码行为是否一致提前发现代码生成配置问题依赖工具箱Simulink Coder必装使用覆盖率分析时还需要Simulink Coverage使用测试管理器批量回归时推荐Simulink Test是否支持脚本支持MATLAB命令行可完成模式切换、代码生成、批量仿真是否支持批量任务支持结合Simulink Test或sim函数循环可以批量跑用例是否支持接口API支持仿真模式可通过SimulationMode参数或SimulationInput对象控制是否需要目标硬件不需要SIL在主机上运行是否需要C编译器必须Windows常用MinGW-w64或MSVCLinux下常用gcc典型适用场景代码生成配置变更后回归、硬件未到位时的算法验证、代码覆盖率分析与PIL区别SIL跑在主机CPU上PIL跑在目标处理器上PIL能暴露目标平台相关问题有一点要提前说明SIL仿真速度不一定比模型仿真快因为代码要编译仿真过程中还要在模型和生成代码之间交换数据。资源占用和耗时取决于模型规模、采样周期、编译优化选项和电脑CPU具体数字一定要在你自己机器上实测。2. 适用场景与使用边界SIL适合谁最直接的一类人是做MBD开发、嵌入式代码生成的工程师。你在Simulink里搭好了控制算法接下来准备用Embedded Coder生成C代码在写入目标芯片之前先在电脑上验证一下“生成的代码是否忠实于模型”SIL就是这个环节的标准做法。具体能解决这些问题模型在Normal仿真下正确但代码生成后逻辑不一致SIL可以发现多数这类问题。更换了编译器、代码生成选项、或者从GRT目标切换到ERT目标后行为和之前是否一致。硬件还没到位但需要提前跑控制算法的回归测试。需要统计代码覆盖率又暂时接不了真实硬件。SIL不适合什么场景不适合验证时序相关的实时性问题因为SIL跑在主机上和真实目标处理器执行时序不同。不适合发现芯片底层问题比如位宽、外设驱动、中断优先级。这类问题需要PIL或HIL。不适合做整机环境验证。被控对象模型和被控对象实物之间有差距SIL只能验证控制器代码逻辑验证不了传感器、执行器接口。还有一条边界必须提醒使用MATLAB/Simulink进行模型开发需要确认你的许可证和工具箱覆盖了所用功能。SIL本身通过Simulink Coder实现生成代码后如果要部署到商业产品或嵌入式设备要检查代码生成许可证的适用范围不要在未授权环境下做商用发布。3. 环境准备与前置条件SIL不需要高性能显卡也不依赖GPU但需要一个能跑得动Simulink和编译器的CPU以及足够内存。模型规模不大时普通办公电脑也能跑。检查清单如下MATLAB/Simulink版本。建议使用R2019b之后的版本SIL配置入口在较新版本中更直观版本越老细节差异越大。工具箱。Simulink Coder必装Embedded Coder在需要精细配置代码风格、目标平台时使用。如果要用代码覆盖率分析需要Simulink Coverage。C/C编译器。Windows下常见的两个选择是MinGW-w64和Microsoft Visual CMSVC。安装后要在MATLAB里运行mex -setup完成工具链识别。模型命名规范。模型文件名不要用中文、不要带空格不要以数字开头。生成代码阶段的文件名和宏定义会直接使用模型名特殊字符容易引发编译失败。磁盘空间。生成代码和编译中间文件会占用一定空间常规模型预留1GB以上基本够用。路径管理。建议把模型、生成代码、测试脚本分目录管理避免模型文件放在MATLAB安装目录或系统盘临时目录。编译器配置是SIL最容易卡住的环境问题之一。打开MATLAB后先在命令行窗口确认编译器状态。mex -setup如果系统里没有可用编译器MATLAB会给出提示。此时先安装MinGW-w64MATLAB支持版本以官方文档为准也可以安装Visual Studio Build Tools然后在mex -setup窗口里选择对应编译器。4. 模型准备与基线仿真在做SIL之前先做一次完整的Normal仿真把结果保存下来作为“模型基线”。这一步很关键没有基线的SIL对比就是空中楼阁。这里以一个典型的一阶低通滤波/控制对象模型为例说明流程。假设模型名称为sil_demo.slx模型内部结构是输入阶跃信号经过一个PID控制器输出到一阶惯性环节再用Scope或To Workspace模块保存结果。第一步打开模型。open_system(sil_demo)第二步在Normal模式下运行仿真并把仿真输出保存到工作区。simOut sim(sil_demo, StopTime, 10);第三步在模型里添加To Workspace模块或者在调用sim时设置保存参数把关键信号记录下来。注意区分信号保存方式和输出日志方式。如果模型里已经有Scope可以直接用Simulink.sdi.view打开Simulation Data Inspector查看正常仿真曲线。基线仿真跑通之后标记一下关键指标比如超调量、稳定时间、稳态误差。后面的SIL结果要和这组数据做对比。5. SIL仿真模式配置SIL模式本身不需要写额外代码Simulink会在你切换仿真模式后自动完成代码生成、编译和运行。但在切换之前要先把模型配置调整好。5.1 检查代码生成配置打开模型的“设置”模型窗口CtrlE进入“代码生成”页签。需要关注两个地方系统目标文件。如果只做SIL验证通常用ert.tlcEmbedded Coder或grt.tlcGeneric Real-Time。使用Embedded Coder时默认目标通常就是ert.tlc。如果是纯Simulink Coder环境常见的默认目标是grt.tlc。工具链。确保这里选择的编译器和mex -setup中配置的一致。配置项的位置在不同版本里略有差异但关键词是“系统目标文件”“工具链”“代码生成”。如果找不到具体菜单直接在模型设置右上角搜索“工具链”。5.2 使用GUI切换SIL模式在模型编辑器窗口顶部找到“仿真模式”下拉框。这个下拉框通常在工具栏的仿真按钮旁边默认显示“Normal”。下拉菜单里可以看到NormalAcceleratorRapid AcceleratorSoftware-in-the-Loop (SIL)Processor-in-the-Loop (PIL)选择“Software-in-the-Loop (SIL)”之后点击运行“Run”。Simulink会弹出对话框提示“该操作将生成并编译代码”确认后等待构建完成。首次编译通常需要几十秒到几分钟取决于模型大小。编译完成后仿真会自动执行。此时不要着急看结果先在命令行窗口观察输出确认是否有“Build process completed successfully”之类的信息。5.3 使用命令行切换SIL模式GUI适合人机交互命令行模式更适合批量验证和自动化回归。代码如下simIn Simulink.SimulationInput(sil_demo); simIn simIn.setModelParameter(SimulationMode, Software-in-the-loop); simIn simIn.setModelParameter(StopTime, 10); simOutSIL sim(simIn);执行这段代码后MATLAB会先构建代码再运行SIL仿真。第一次运行时间较长第二次如果模型没有变更部分编译缓存会被复用。如果只想先生成代码不立即运行仿真可以使用slbuild(sil_demo)slbuild之后在模型目录下可以看到生成的C源文件、头文件、构建日志等。生成的子文件夹名称通常和模型名、目标类型相关。6. SIL功能测试与效果验证SIL跑完不代表测试完成要确认SIL结果和Normal仿真结果一致。下面是一套完整的验证步骤。6.1 用Simulation Data Inspector对比波形运行完Normal和SIL两组仿真后打开Simulation Data InspectorSimulink.sdi.view在SDI界面里选择两次运行的信号叠加对比。判断标准不要求波形一模一样但在同样的输入和采样配置下控制输出曲线应当基本重合。如果曲线有明显偏移、振荡甚至发散就要进入问题定位。6.2 用数据计算误差肉眼对比不够严谨建议写一段小脚本计算两组结果的最大误差和均方根误差。% 假设两组运行结果中信号名称都是y yModel simOut.get(y); ySIL simOutSIL.get(y); maxErr max(abs(yModel - ySIL)); rmsErr sqrt(mean((yModel - ySIL).^2)); fprintf(Max error: %e\n, maxErr); fprintf(RMS error: %e\n, rmsErr);判断标准如下误差在数值极小范围内如1e-6以下可以认为模型和代码在逻辑上等价。误差在可接受范围内但明显存在可能来自浮点运算顺序或编译器优化差异。误差大且波形形态不同优先检查数据初始化、采样时间设置、数据类型配置和代码生成优化选项。6.3 等价性判断的常见视角SIL和Normal不一致最常见的原因并不是算法错误而是数据类型不匹配。比如Normal仿真中默认使用double代码生成后配置成了single或者模型中某个信号被设定为整数类型导致除法结果截断。此时应该先检查模型里所有信号的数据类型再检查代码生成配置里的“默认数据类型”。另一个常见原因是被控对象模型和控制器模型使用了不同的采样时间。SIL模式对采样时间的处理更严格如果模型中存在连续采样和离散采样混用Normal模式下可能隐藏问题到了SIL模式就会暴露出来。6.4 在SIL模式下使用覆盖率分析如果安装了Simulink Coverage可以分析生成代码的语句覆盖率、分支覆盖率、条件覆盖率以及汽车电子领域经常要求的MCDC覆盖率。这里注意概念区分模型覆盖率分析的是Simulink模型里的模块执行情况代码覆盖率分析的是生成的C代码中各条语句的执行情况。SIL模式下做代码覆盖率是验证测试用例充分性的重要手段。操作方式是打开“覆盖率分析”面板选择“代码覆盖率”运行SIL仿真后查看覆盖率报告。覆盖率偏低意味着测试用例充分性不足需要补充更丰富的工况。7. 自动化批量SIL测试与脚本化回归SIL一旦能跑通下一步就是把它接入自动化测试流程。实际项目中一个控制器模型有几十个测试用例不可能每次都手动切换仿真模式。7.1 用Simulink Test批量执行Simulink Test可以创建测试用例集每个测试用例绑定对应的输入信号、参数和仿真模式。在测试管理器里新建Test Case把仿真模式设置为SIL然后一键批量运行。优势很明显Simulink Test会把每次都把结果和验收标准比较输出Pass/Fail状态并生成测试报告。遇到SIL代码变更后回归直接选择全部用例运行即可。7.2 用脚本循环实现参数扫描如果没有Simulink Test可以用脚本循环完成批量参数扫描。需要注意SIL模式首次建立模型时编译代码如果循环中切换了模型参数有些参数修改会触发重新生成代码耗时较长。% 批量测试不同Kp参数下的SIL表现 KpList [1.0, 1.5, 2.0, 2.5]; results cell(length(KpList), 1); for i 1:length(KpList) simIn Simulink.SimulationInput(sil_demo); simIn simIn.setModelParameter(SimulationMode, Software-in-the-loop); simIn simIn.setVariable(Kp, KpList(i)); results{i} sim(simIn); end循环执行时要注意每次sim调用之间留出足够的间隔及时清理不再使用的输出数据避免内存持续增长。7.3 更面向工程的自动化模板若是产线项目建议补充日志和失败保存逻辑。% 批量运行SIL并记录结果 testCases {... struct(caseId, TC001, Kp, 1.0, Ki, 0.1), ... struct(caseId, TC002, Kp, 2.0, Ki, 0.2)}; logFile sil_run_log.txt; for idx 1:length(testCases) tc testCases{idx}; try simIn Simulink.SimulationInput(sil_demo); simIn simIn.setModelParameter(SimulationMode, Software-in-the-loop); simIn simIn.setVariable(Kp, tc.Kp); simIn simIn.setVariable(Ki, tc.Ki); out sim(simIn); fprintf(logFile, %s PASS\n, tc.caseId); catch ME fprintf(logFile, %s FAIL: %s\n, tc.caseId, ME.message); end end这种脚本化的方式同样适用于MIL、PIL模式。把仿真模式做成变量一套脚本就能跑多种验证形式。8. 资源占用与性能观察方法SIL模式的资源消耗点主要在编译阶段和仿真执行阶段和显卡无关重点看CPU、内存和磁盘I/O。编译阶段CPU占用会明显升高风扇可能开始转编译过程通常持续几十秒到几分钟。模型越大、代码生成选项越复杂耗时越长。执行阶段SIL仿真速度比Normal慢因为模型每次时间步都要和生成代码模块交互数据。如果遇到SIL仿真长时间卡住不动优先检查是不是模型内部有高频采样率或长时间仿真配置。比如StopTime设成10000秒而采样周期是0.0001秒仿真步数达到1亿步这中间等待是合理的。内存占用Simulink本身、生成的代码和SDI保存的数据都会占内存。跑大批量用例时内存增长会很快。降低资源占用的一些方法仿真时间先缩短到能覆盖动态响应的最小范围。把不关心的信号在信号日志里关掉减少SDI存储量。若批量跑SIL每次仿真结束后调用clear或让输出对象及时失引用减少内存堆积。在模型配置里开启代码生成缓存避免重复编译。频繁变更代码生成选项或清理工作区可能导致缓存失效。如果模型规模非常大可以把控制器模型和被控对象模型拆开单独对控制器模型跑SIL。9. 常见问题与排查方法下面把SIL使用中最常遇到的问题整理成排查表。问题现象可能原因排查方式解决方案切换SIL后报错找不到编译器未安装C编译器或mex -setup未配置在命令行运行mex -setup安装MinGW-w64或MSVC后重新配置SIL构建过程中提示文件路径错误模型文件名为中文、包含空格检查模型文件名改为英文和下划线命名构建成功但仿真报错模型中包含不支持代码生成的模块检查报错模块改用支持代码生成的替代模块SIL结果与Normal明显不一致数据类型不一致或采样时间设置问题对比信号数据类型和数据曲线统一数据类型检查采样时间多次运行后内存占用持续增长仿真输出对象未及时清理检查命令行窗口变量批量循环中及时清理变量模型设置了SIL但无法选择未安装Simulink Coder检查许可证和工具箱安装Simulink Coder覆盖率报告没有代码覆盖率数据未启用代码覆盖率或未安装Simulink Coverage检查覆盖率设置安装Simulink Coverage并开启代码覆盖率批量SIL测试中途卡住单个用例仿真时间过长或内存不足查看任务管理器CPU/内存占用缩短仿真时间分批运行生成的C代码在目标硬件上能跑但SIL失败编译器优化选项与目标工具链差异检查代码生成优化设置调整工具链和优化选项其中“SIL结果与Normal不一致”是最需要花时间的。定位顺序建议是先对比输出波形是全局偏差还是局部突变再检查数据初始化、采样时间、数据类型最后检查代码生成优化选项。不要一上来就怀疑编译器多数问题出在模型本身。10. 最佳实践与使用建议从工程落地的角度下面几条建议能在使用SIL时少走弯路。第一第一次跑SIL一定要控制模型规模。先用最小可运行模型跑通全流程再把自己的控制器模型放进去。这样能区分问题是出在SIL流程没配置好还是出在控制器模型本身。第二把基线数据保存下来。Normal仿真结果、SIL仿真结果、测试用例、生成代码、日志文件建议按日期和版本分目录管理。以后模型更新了直接回归对比。第三批量测试必须有日志和失败重试机制。SIL模式下如果某个用例因为一次编译问题挂了后续用例也会受影响。加日志后能快速定位是哪一轮、哪个参数触发的失败。第四SIL未通过时不要急着烧硬件。很多嵌入式端问题可以在SIL阶段提前暴露特别是数据饱和、整数溢出、死代码路径这些典型问题。第五涉及人脸、声音、多媒体素材或其他版权内容时不要因为是在仿真环境里就放松授权要求。SIL只是验证逻辑不豁免合规风险。第六对外发布测试报告时注明MATLAB版本、工具箱列表、编译器版本、模型版本、仿真步长和StopTime。缺少这些环境信息测试结果很难复现。11. 总结与下一步SIL是自动代码生成流程中性价比很高的一环。它不要求目标硬件却能把模型和代码之间的大部分逻辑差异暴露出来。先跑通Normal仿真作为基线再切换到SIL模式对比波形计算误差最后通过脚本或Simulink Test把测试用例批量跑起来这就是一套可复用的SIL验证流程。做完了SIL下一步通常就是PIL。SIL验证的是代码逻辑PIL验证的是代码在真实处理器上的运行行为。理解SIL以后你拿到PIL配置时会发现大部分概念是相通的差别只是多了一块目标硬件和对应的工具链。建议你在自己的模型上先做一个最小截图随便搭一个PID闭环记录Normal曲线切到SIL对比结果。整个流程跑通之后再去碰VCU整车模型、Carsim联合仿真或者更复杂的策略模型。熟练之后你会发现SIL其实是代码生成流程中最安全的一步——它花的时间不多但能挡住大部分低级错误。
返回列表