4. 实时仿真模型开发基础
HIL(Hardware-in-the-Loop)验证的核心前提,是让被控对象模型以“真实时间”运行在实时硬件上。与桌面仿真不同,实时仿真对模型的架构、求解器、代码生成和硬件映射都有硬性约束:模型必须在每个固定时间步内完成全部计算,超时即意味着仿真失败。本章围绕MATLAB/Simulink,讲清从模型架构设计到代码部署到实时硬件的完整链路。
一、面向实时仿真的Simulink模型架构
桌面仿真模型往往追求功能正确,而实时模型必须同时满足可确定性执行和可代码生成两个条件。因此架构设计要遵循几条原则:
- 分层分区:按信号流向划分为输入接口层、被控对象层(Plant)、传感器/执行器模型层、输出接口层。Plant层内部再按物理子系统(电机、电池、逆变器、传动)分模块,便于单独调试和替换。
- 速率分离:不同物理过程的时间常数差异很大。电机电流环可能需要10 μs级步长,电池热模型用1 ms甚至10 ms即可。用多速率(Multi-rate)建模,让快慢子系统各自以合适步长运行,可显著降低实时硬件的计算负担。
- 避免不可生成代码的模块:连续状态求解器、代数环、变步长模块、部分MATLAB Function中的动态内存分配,都会阻碍代码生成或破坏确定性。实时模型应尽量使用离散状态、查表、固定点或单精度浮点运算。
- 接口显式化:所有与实时硬件I/O、CAN、PWM、ADC/DAC相关的信号,应集中在接口层,用明确的端口和数据类型标注,避免隐式全局变量。
一个典型的电机控制器HIL模型架构可以这样组织:顶层包含“Plant(PMSM+逆变器+电池)”“IO Interface”“Test Harness”三个子系统。Plant内部用离散状态空间或查表实现,IO Interface负责将实时硬件的模拟输出、数字输入、CAN报文与模型信号互相映射。
二、定步长求解器的选择
实时仿真必须使用定步长(Fixed-step)求解器,因为只有固定步长才能保证每个采样周期内计算量可预测。Simulink中常用的定步长求解器有:
| 求解器 | 适用场景 | 特点 |
|---|---|---|
| Fixed-step Discrete | 纯离散模型 | 无连续状态,最确定,推荐首选 |
| ode1 (Euler) | 含少量连续状态 | 一阶精度,计算量最小,稳定性一般 |
| ode4 (Runge-Kutta) | 连续状态较多、精度要求高 | 四阶精度,单步计算量大 |
| ode5 (Dormand-Prince) | 高精度连续系统 | 计算量最大,实时性压力高 |
选择依据是:模型是否含连续状态、实时硬件的单步计算余量、以及精度要求。对于三电控制器HIL,电机和逆变器模型若用离散化状态空间实现,可直接选Discrete;若保留连续积分器,则常用ode4,步长取10 μs~50 μs。步长选择要满足:单步计算时间 < 步长 × 安全系数(通常留30%~50%余量)。例如目标步长50 μs,实测单步计算35 μs,则余量30%,可接受;若达到48 μs,则必须减小模型复杂度或增大步长。
三、代码生成流程
Simulink模型不能直接在实时硬件上运行,必须通过Embedded Coder或Simulink Coder生成C代码,再编译为实时目标可执行文件。典型流程如下:
- 模型配置:在Configuration Parameters中设置Solver为Fixed-step、指定步长;设置Hardware Implementation为目标处理器(如x86、ARM、TI C2000);选择System target file(如ert.tlc用于嵌入式实时)。
- 接口配置:将需要与外部交互的信号标记为Root Inport/Outport,或使用C Caller、Legacy Code Tool集成手写驱动。数据类型统一为single或int16/int32,避免double带来的额外开销。
- 代码生成:使用slbuild或rtwbuild命令生成代码。生成结果包括模型C文件、头文件、makefile以及rtwtypes.h等。检查生成的代码中是否存在动态内存分配(malloc)和浮点异常处理,实时系统通常要求禁用。
- 编译与链接:将生成代码与实时OS(如RT-Linux、VxWorks、QNX)的I/O驱动、定时器驱动一起编译,生成可执行文件。
- 部署与运行:通过以太网或JTAG将可执行文件下载到实时目标机,启动实时任务。Simulink外部模式(External Mode)可用于在线调参和信号观测。
% 示例:配置并生成代码
set_param('MotorHIL','Solver','FixedStepDiscrete');
set_param('MotorHIL','FixedStep','50e-6');
set_param('MotorHIL','SystemTargetFile','ert.tlc');
set_param('MotorHIL','GenCodeOnly','on');
rtwbuild('MotorHIL');
生成后应检查代码中是否出现malloc、free、printf等非确定性调用。若存在,需在配置中关闭动态内存、关闭调试打印,或改用静态分配。
四、模型与实时硬件的部署映射
部署映射的核心是把模型中的信号和任务,对应到实时硬件的物理资源和调度周期上。主要映射关系包括:
- 任务映射:模型中的多速率子系统对应实时OS中的多个任务。快任务(如10 μs)优先级最高,慢任务(如1 ms)优先级较低。需确保快任务不被慢任务阻塞。
- I/O映射:模型中的模拟输出端口映射到DAC通道,数字输入映射到DI通道,CAN信号映射到CAN控制器邮箱。映射表应明确通道号、量程、偏移和数据类型。
- 内存映射:模型参数(如电机电感、电阻)映射到实时目标的全局变量或标定区,支持在线修改。Simulink的PARAM类型可生成可标定参数。
- 时间映射:模型步长与硬件定时器周期一致。例如步长50 μs,则定时器中断周期设为50 μs,每个中断触发一次模型步进。
下面这张图展示了从Simulink模型到实时硬件部署的完整信号链与映射关系。
图中左侧是Simulink模型的分层结构,中间是代码生成环节,右侧是实时目标机上的任务、I/O和物理接口。虚线反馈表示闭环:被测ECU的输出经实时硬件采集后送回模型,模型在下一个步长内响应,形成完整的HIL闭环。步长约束框提醒我们,实时性的底线是单步计算时间必须小于步长乘以安全系数。
五、常见问题与判断依据
部署后若出现仿真超时(Overrun),首先检查单步计算时间是否超过步长。可在实时目标机上用GPIO翻转或CPU计数器测量。若超时,优先降低模型复杂度:将连续求解器改为离散、减少查表维度、把慢过程移到低优先级任务。若出现信号跳变或数值发散,检查步长是否过大导致离散化误差累积,可尝试将步长减半观察是否改善。若CAN通信丢帧,检查CAN任务周期是否与模型步长匹配,以及邮箱配置是否正确。
判断模型是否满足实时性,最直接的依据是:连续运行10分钟以上,无Overrun计数,且关键信号(如电机电流、转速)与离线仿真结果的偏差在可接受范围内(通常电流偏差<5%,转速偏差<2%)。满足这两条,才认为模型可以进入HIL验证阶段。