4. 实时仿真模型开发环境搭建

ECU 硬件在环(HIL)测试的核心,是让被控对象模型以“实时”的方式运行在目标机上,与真实 ECU 通过 I/O 板卡交换信号。所谓实时,并不是“跑得快”,而是每个仿真步长都能在确定的截止时间内完成计算与 I/O 刷新,否则 ECU 收到的信号时序就会失真,测试结论也就失去意义。因此,本节要搭建的不是一台普通 PC 上的仿真环境,而是一条从建模到实时执行的完整工具链:MATLAB/Simulink 负责建模与代码生成,实时操作系统负责确定性调度,编译工具链负责把模型翻译成目标机可执行的二进制,目标机配置与部署流程则把这一切串成可复现的工程动作。

一、开发环境的分层结构

先建立整体认识。一套典型的 HIL 实时仿真开发环境可分为四层:建模层、代码生成层、编译与链接层、实时执行层。建模层运行在宿主机(Host PC)上,使用 MATLAB/Simulink 搭建被控对象模型、I/O 驱动模型和测试激励模型;代码生成层由 Simulink Coder / Embedded Coder 把模型转换为 C 代码;编译与链接层调用目标机对应的交叉编译器,把 C 代码编译成目标机 CPU 架构的可执行文件;实时执行层则是目标机上的实时操作系统(RTOS)或实时内核,负责加载可执行文件、按固定步长调度任务、驱动 I/O 板卡。

这四层之间通过明确的接口衔接:模型步长决定实时任务的周期,I/O 驱动模型决定硬件访问方式,编译器决定目标机可执行格式,RTOS 决定调度与同步机制。任何一层配置错误,都会在部署或运行阶段以“超时”“丢帧”“信号跳变”等形式暴露出来。

宿主机 Host PC MATLAB / Simulink 被控对象模型 · I/O 模型 Simulink Coder 模型 → C 代码 交叉编译工具链 gcc / clang · 目标架构 · 链接脚本 目标机 Target 实时操作系统 RTOS / 实时内核 I/O 板卡驱动 DIO · AI · AO · CAN 实时任务调度器 固定步长 · 优先级 · 同步 可执行文件 部署 / 下载 真实 ECU(被测对象) 传感器信号 ← 执行器信号 → 总线报文 通过 I/O 板卡与目标机闭环连接 激励输出 响应采集 关键配置参数: 模型步长 Ts = 1 ms(典型值) · 实时任务周期 = Ts · 编译器目标 = x86_64 / ARM · 通信接口 = Ethernet / PCIe 部署方式 = 宿主机编译 → 目标机加载 · 同步机制 = 硬件定时器中断 · 监控 = Simulink Real-Time / 第三方实时平台

二、MATLAB/Simulink 建模与代码生成配置

在 Simulink 中搭建被控对象模型时,首先要确定求解器类型。HIL 仿真必须使用固定步长(Fixed-step)求解器,因为实时系统无法预知变步长求解器的下一步计算量。常用选择是 ode4(四阶龙格-库塔)或 ode1(欧拉法),步长根据模型动态特性设定。例如发动机扭矩模型动态较快,步长常取 0.1 ms~1 ms;车身动力学模型可放宽到 1 ms~10 ms。步长一旦确定,就决定了实时任务的周期,也决定了 CPU 的负载上限。

代码生成配置集中在 Configuration Parameters 中。关键项包括:System target file 选择目标机对应的 TLC(Target Language Compiler)文件,例如 ert.tlc 用于嵌入式实时目标;Language 选择 C;Generate code only 视部署方式决定是否勾选;Code Generation > Interface 中设置 Code replacement library 以匹配目标机数学库。若使用 Simulink Real-Time,则选择 slrt.tlc,它会自动生成目标机可加载的 .dlm 文件。

模型内部还需要处理 I/O 驱动。通常做法是把 I/O 板卡的读写封装成 Simulink 的 S-Function 或 MATLAB Function 模块,放在模型的最外层。例如一个模拟量输出通道可以写成:读取模型内部信号 → 调用板卡驱动 API → 写入硬件寄存器。这部分代码在代码生成时会被保留,因此需要确保驱动库已正确链接。

三、实时操作系统与编译工具链

目标机上的实时操作系统决定了任务调度方式。常见方案有两类:一类是通用操作系统加实时补丁(如 Linux + PREEMPT_RT),另一类是专用实时内核(如 Simulink Real-Time 的 Speedgoat 内核、VxWorks、QNX)。前者成本低、生态好,但需要仔细配置 CPU 隔离、中断亲和性和内存锁定;后者确定性更强,但工具链封闭、授权成本高。

无论哪种方案,核心机制都是:硬件定时器产生周期中断 → 中断服务程序触发实时任务 → 任务执行模型计算和 I/O 读写 → 任务在下一个中断前完成并进入等待。如果任务执行时间超过步长,就会发生超时(Overrun),表现为输出信号更新延迟或丢帧。因此,部署前必须测量最坏执行时间(WCET),并留出至少 30% 的 CPU 余量。

编译工具链方面,宿主机上需要安装与目标机 CPU 架构匹配的交叉编译器。例如目标机是 x86_64 架构,则使用 gcc 的 x86_64 版本;若是 ARM 架构,则使用 arm-linux-gnueabihf-gcc。编译过程通常由 Simulink 自动调用,但需要提前在 MATLAB 中配置编译器路径:

% 在 MATLAB 命令窗口中设置交叉编译器
setenv('TARGET_CC', '/opt/arm-gcc/bin/arm-linux-gnueabihf-gcc');
setenv('TARGET_CXX', '/opt/arm-gcc/bin/arm-linux-gnueabihf-g++');
% 验证编译器可用
system('arm-linux-gnueabihf-gcc --version');

编译产物通常包括:目标文件(.o)、可执行文件(.elf 或 .out)、以及部署描述文件(.mk 或 .xml)。如果编译报错,优先检查头文件路径是否包含目标机 BSP(板级支持包)中的 include 目录,以及链接脚本是否指定了正确的内存布局。

四、目标机配置与模型部署流程

目标机配置包括网络、存储和启动方式。以 Simulink Real-Time 为例,目标机通常通过以太网与宿主机通信,需要设置目标机 IP 与宿主机在同一网段,并在宿主机上配置 slrt 目标对象。部署流程可归纳为以下可复现步骤:

  1. 建立目标机连接:在 MATLAB 中创建目标对象 tg = slrt('192.168.1.100'),测试 tg.isConnected 返回 true。
  2. 编译模型:按 Ctrl+B 或执行 slbuild('myPlantModel'),生成目标机可执行文件。
  3. 下载到目标机:执行 tg.load('myPlantModel'),将可执行文件传输到目标机内存或存储。
  4. 启动实时运行:执行 tg.start,目标机开始按步长周期执行模型。
  5. 监控与调参:通过 tg.addSignal 添加观测信号,使用 tg.Scope 或 Simulink Real-Time Explorer 查看实时波形;通过 tg.setparam 在线修改模型参数。
  6. 停止与卸载:测试结束后执行 tg.stop,必要时 tg.unload 释放目标机资源。

部署过程中最常见的异常是“目标机连接失败”和“模型加载后立即停止”。前者多因 IP 不在同一网段或防火墙拦截;后者通常由步长过小导致 CPU 超载,或 I/O 驱动初始化失败引起。排查时先查看目标机日志(tg.getLog),再检查模型中最耗时的模块,必要时提高步长或优化模型。

实践建议:首次搭建环境时,先用一个最简单的“正弦波 → 模拟量输出 → 模拟量输入 → 示波器”闭环模型验证整条链路。确认信号能正确输出和回采后,再逐步加入复杂的被控对象模型和 I/O 通道。这样可以把“工具链问题”和“模型问题”分开定位,避免一开始就陷入多因素耦合的排障困境。

最后需要强调,实时仿真环境的搭建不是一次性的配置工作,而是需要版本管理的工程资产。模型文件、代码生成配置、编译器版本、目标机 BSP 版本、RTOS 内核版本都应记录在案。当测试结果出现异常时,能够快速回溯到“哪一版模型、哪一版工具链、哪一版目标机配置”下产生的数据,是 HIL 测试可信度的基础。