3. 安全测试基础
好,咱们进入实操环节。SIL测试环境搭建,说白了就是让代码在PC上跑起来,模拟ECU的行为。我见过不少团队,一上来就怼硬件,结果问题一大堆。其实,先把SIL环境搭稳了,很多坑在PC上就能发现。
3.1 基于Simulink的SIL环境搭建
Simulink做SIL,核心就是模型在环。但我们要的是代码在环,所以得把模型生成代码,再跑回Simulink里验证。我个人习惯,先搭一个干净的测试平台。
核心思路: 将Simulink模型通过Embedded Coder生成C代码,再通过S-Function或SIL模块,将代码集成回Simulink仿真环境。这样,仿真时跑的就是目标代码,而不是模型本身。
具体步骤,我列个清单:
- 准备被测模型:确保模型是离散的,固定步长。连续模型在SIL里跑,容易出时序问题。
- 配置数据字典:定义好输入输出接口,数据类型要跟目标芯片一致。我曾经遇到过,模型里用double,芯片用float,结果SIL通过,HIL就崩了。
- 创建SIL测试框架:在Simulink中新建一个模型,用
SIL模块(或Model block配置为SIL模式)替代原模型。 - 连接测试激励:用Signal Builder或From Workspace模块,注入测试用例。
- 设置数据记录:用To Workspace或Scope,抓取输出,用于后续比对。
小技巧: 我习惯在SIL模型里加一个Assertion模块,用来做运行时检查。比如,信号超出范围,直接报错,省得事后翻数据。
3.2 代码生成配置
代码生成配置,是SIL环境搭建的关键中的关键。配置不对,生成的代码跟模型行为不一致,SIL就白做了。你想想看,代码生成器也是软件,它有自己的脾气。
我常用的配置项,整理成了一张表:
| 配置项 | 推荐设置 | 说明 |
|---|---|---|
| System target file | ert.tlc |
嵌入式实时目标,生成效率高,适合SIL |
| Language | C |
别选C++,除非你有特殊需求 |
| Generate code only | off |
让Simulink自动编译,省事 |
| Support floating-point numbers | 匹配目标芯片 | 如果芯片不支持浮点,选single或fixed-point |
| Default parameter behavior | Tunable 或 Inlined |
SIL测试建议用Inlined,减少变量 |
| Generate report | on |
生成代码报告,方便追溯 |
注意: 代码生成配置,一定要跟最终产品的配置保持一致。我见过一个项目,SIL用ert.tlc,产品用grt.tlc,结果SIL过了,产品上跑出bug。说白了,SIL环境要跟目标环境对齐,否则就是自欺欺人。
配置好之后,生成代码。我个人习惯,生成后先看一眼model.c和model.h,确认接口函数名、参数类型跟模型一致。这一步,花不了几分钟,但能避免很多低级错误。
3.3 测试脚本框架搭建
测试脚本框架,是SIL测试的骨架。没有框架,测试就是一团乱麻。我建议,用MATLAB脚本或Python,搭建一个可复用的测试框架。
为什么?因为SIL测试要跑很多轮,手动点仿真,效率太低。而且,故障注入需要自动化,手动操作容易遗漏。
我常用的框架结构,画了个图:
框架搭好了,写脚本就快了。我贴一段核心代码,展示主控脚本怎么调用各模块:
% run_tests.m - SIL测试主控脚本
% 作者:资深工程师老李
% 功能:自动化执行SIL测试用例
clear; clc; close all;
% 1. 加载配置
config = load_config('config.m');
fprintf('[INFO] 配置加载完成\n');
% 2. 获取测试用例列表
test_cases = get_test_cases(config.test_dir);
fprintf('[INFO] 共发现 %d 个测试用例\n', length(test_cases));
% 3. 循环执行
for i = 1:length(test_cases)
tc = test_cases{i};
fprintf('[INFO] 执行测试用例: %s\n', tc.name);
% 生成激励
stimulus = generate_stimulus(tc, config);
% 运行SIL仿真
results = run_sil_simulation(config.model_name, stimulus, config.sim_time);
% 采集数据
data = collect_results(results, config.signals);
% 比对结果
[pass_flag, details] = compare_results(data, tc.expected, config.tolerance);
% 记录结果
record_result(tc.name, pass_flag, details, config.report_dir);
fprintf('[INFO] 测试用例 %s: %s\n', tc.name, iif(pass_flag, 'PASS', 'FAIL'));
end
fprintf('[INFO] 所有测试用例执行完毕\n');
避坑指南: 我曾经在脚本里忘了加close_system,结果Simulink模型越开越多,内存爆了。建议每次仿真完,强制关闭模型,释放资源。
3.4 故障注入接口预留
SIL环境搭好了,别忘了预留故障注入接口。因为咱们这门课的核心就是故障注入。我一般会在模型里加一个故障注入层,通过一个全局结构体,控制故障的注入。
举个例子:
// 故障注入控制结构体
typedef struct {
uint8_t fault_id; // 故障ID
float fault_value; // 故障值
uint8_t fault_enable; // 使能标志
} FaultInjection_Type;
// 全局变量
FaultInjection_Type FaultCtrl;
在Simulink模型里,用S-Function或MATLAB Function模块,读取这个结构体,修改信号值。这样,测试脚本就能通过修改FaultCtrl,实现故障注入。
注意: 故障注入接口,一定要在SIL环境搭建时就设计好。等测试用例写完了再补,容易漏掉关键信号。我建议,把模型中所有可能出问题的信号,都列出来,逐一考虑是否需要注入故障。
嗯,到这里,SIL环境搭建的核心内容就讲完了。记住,环境搭得越扎实,后面的测试就越顺利。别急着跑用例,先把地基打牢。