BMS 软件单元测试与集成测试策略:从函数级用例到硬件在环

BMS 软件单元测试与集成测试策略:从函数级用例到硬件在环

电池管理系统(BMS)的软件出了问题,代价往往不是一句日志里的报错,而是一次热失控或者一次整车召回。很多团队在功能开发上投入了几个月,却在测试环节只”跑通主流程就算完”。本文从 BMS 软件的实际结构出发,梳理单元测试与集成测试的分层策略,重点讨论几个工程上最容易做空、却最致命的地方:采样滤波、SOC 估算、保护阈值逻辑,以及软件看门狗与硬件保护路径之间的缝隙。


1. 先明确一件事:BMS 软件的”单元”是什么

通用软件测试教材里的”单元”,通常指一个函数或者一个类。但放在 BMS 上,这个定义要重新校准。BMS 主控固件通常跑在实时操作系统之上,逻辑被切分成:

  • 采样任务:ADC 采集、NTC 温度换算、多路复用器切换;
  • 状态估计算法:SOC / SOH / SOP,往往带滤波器或扩展卡尔曼滤波;
  • 保护与诊断逻辑:过压/欠压/过温判断、继电器控制、绝缘监测;
  • 通信栈:CAN、菊花链(daisy chain)AFE 协议、报文解析与 CRC 校验;
  • 看门狗与安全状态机:任务调度监视、故障降级流程。

因此,一个合理的”单元”,应该是一个可独立编译、可注入输入、可断言输出的最小功能块——它可以是一个纯函数(如 OCV 查表),也可以是封装好的模块(如均衡控制状态机)。关键在于把那些带硬件依赖、带时序依赖的代码,和被测试的逻辑分离开。这是后面所有分层能成立的前提,做不到这一点,单测就是空谈。

工程上的第一步,不是急着写测试,而是做一次可测性改造(design for testability)。把 ADC 原始值换算和滤波逻辑拆出来,把 CAN 报文的编解码从发送接口里拆出来,把继电器动作封装成”命令接口 + 硬件驱动”两层。否则你的测试用例只能测”调用不崩溃”,测不出对错。


2. 单元测试:先堵住会算错的逻辑

2.1 纯算法优先

单测的投入产出比最高的地方,永远是纯计算逻辑。这类代码没有硬件依赖,用例可以做到穷举边界,收益立竿见影。BMS 里最典型的纯函数包括:

  • 电压/温度的线性插值、标定查表;
  • 电池单体电压的合理性检查(上下限、变化率、与相邻单体偏差);
  • CRC 校验、报文长度与格式校验;
  • SOC 的安时积分与 OCV 查表混合。

下面是一个被拆出来的单体电压合理性检查函数,以及它对应的单测思路:

// 纯函数:单体电压合理性检查(可单元测试)
bool is_cell_voltage_plausible(float v,
                               float prev_v,
                               float dt,
                               float neighbor_avg) {
    // 1. 硬门限:超出电化学平台的物理极限
    if (v < V_CELL_MIN || v > V_CELL_MAX) return false;
    // 2. 斜率门限:电压变化率不可能超过物理上限
    if (dt > 0.0f && fabsf(v - prev_v) / dt > V_SLEW_MAX) return false;
    // 3. 一致性门限:与相邻单体偏差过大
    if (fabsf(v - neighbor_avg) > V_DEVIATION_MAX) return false;
    return true;
}

这个函数的测试用例不应该只测”正常值返回 true”,而要覆盖每一层门限:

用例 输入构造 期望
正常电压 v=3.65V, 变化 1mV, 偏差 5mV true
电压超上限 v=4.5V(超出 LFP 平台) false(触发硬门限)
斜率异常 10ms 内下降 800mV(疑似内短路骤降) false(触发斜率门限)
单体离群 与邻居偏差 120mV false(触发一致性门限)

关键在于:合理性检查不做平滑修正。如果电压突然跳变,正确做法是判定该通道不可信、转而用冗余信息,而不是滤波掩盖。滤波器会把真实的内短路早期骤降磨平,反而把报警窗口抹掉——这是一个在纯逻辑层面就能验证、却常被忽略的语义细节。

2.2 表格驱动与边界穷举

标定表(如 SOC-OCV 曲线、温度-内阻曲线)的查表插值,是另一个”静态扫描就能抓 bug”的典型。用表格驱动测试(table-driven test),把边界点、区间端点、反序查询、NaN 输入等情况列成一张表,循环断言。这类逻辑一旦插值公式写错(比如端点外推斜率取反),往往要到真实电池包标定阶段才暴露,代价极大。

对于状态机——比如均衡开关控制、充电流程迁移——单测的重点是遍历所有合法与非法迁移。用一张状态转移表驱动测试,确保不存在”从任意状态跳到非法状态”的路径。很多 BMS 现场问题,本质是某个异常条件下状态机落到了一个没人定义的分支,导致保护逻辑被绕过。

2.3 引入桩(Stub)与模拟(Mock)

对于那些依赖硬件的逻辑,用桩替换硬件抽象层(HAL)。比如测试”过压保护 → 断开主继电器”这条链路时,不必真的有一个继电器,而是注入一个伪 HAL,断言”断开命令被调用了一次,且参数正确”。桩的关键纪律是:桩的返回值要可控、可被断言,而不是简单地返回 0。要能在测试里人为制造”继电器粘连””ADC 通道失效”这种故障返回,否则故障分支永远是死代码。

一个常见坑:测试里桩把 read_cell_voltage() 永远返回理想值,导致保护阈值分支从来没被执行过。用覆盖率工具盯住分支覆盖,而不是只看行覆盖。BMS 这种安全关键软件,行覆盖 100% 但分支没覆盖,等于没测。


3. 集成测试:测的是”组合起来之后”的行为

单元测试把函数”单独”验证了,但 BMS 的危险往往来自模块交互。集成测试按范围从小到大,通常分三层。

3.1 模块级集成

把采样、滤波、SOC 估算串起来,喂一组仿真电流/电压曲线,断言 SOC 收敛在合理区间。这一层测的是数据流:采样产生的延时、滤波引入的相位滞后,会不会让 SOC 估算产生系统性偏差。比如一个典型问题——电流采样的偏置没校准,安时积分会随时间线性漂移,SOC 越跑越偏。这在单测里看不出来,只有把”直流偏置 + 积分”串起来才能暴露。

3.2 SIL / HIL:软件在环与硬件在环

软件在环(Software-in-the-Loop, SIL) 是把整个控制代码编译到 PC 上,配合电池模型(电化学模型或等效电路模型)跑闭环。它的价值在于大规模、可重复地跑工况:NEDC、WLTC、狂暴充放电、随机工况,成本几乎为零。

硬件在环(Hardware-in-the-Loop, HIL) 则是把真实控制器挂到实时仿真机上,由仿真机输出模拟的电池电压/温度/电流,控制器回读并执行动作,仿真机再根据继电器的开断改变工况。SIL 测算法,HIL 测的是真实硬件上的时序、I/O、通信。两者的关系不是”二选一”,而是分级漏斗:

层级 环境 主要验证目标 典型规模
单元测试 主机编译 算法正确性、边界 数千用例
模块集成 主机/PC 数据流、模块交互 数百场景
SIL PC + 电池模型 闭环策略、SOC/SOH 收敛 大量工况
HIL 实时仿真机 + 真实控制器 时序、I/O、通信、故障注入 关键场景
台架/实测 真实电芯/电池包 标定、环境、寿命 少量

3.3 故障注入:集成测试的灵魂

普通集成测试只测”正常工况下能不能跑通”,但 BMS 的生死就在故障边缘。成熟的集成环境一定有故障注入(fault injection)能力,主动制造这些场景:

  • 某一路单体采样失效 / 漂移;
  • 温度传感器断线;
  • CAN 报文丢失、CRC 错误、总线拥塞;
  • 继电器反馈信号与命令不一致(粘连/未闭合);
  • 复位、掉电、时钟漂移。

这些用例对应的验收标准不是”不出错”,而是“在规定时间内进入规定安全状态”——这正是功能安全(ISO 26262)里安全机制验证的核心思路。一次采样失效,BMS 应当降级到冗余采样或安全保护,而不是让错误电压值流进 SOC 估算。


4. 软件测试与硬件保护的边界:最容易漏的一环

BMS 的顶级安全不能只靠软件。无论软件测试做得多好,固件本身可能跑飞、可能被看门狗复位、可能出栈溢出。所以安全设计中,软件路径之外必须有一条独立的硬件保护路径。测试策略要专门覆盖这个边界:

  • 软件看门狗:喂狗逻辑被主循环以外的任务误喂?看门狗超时后能否正确复位并进入安全态?
  • 独立硬件比较器:过压/欠压由独立芯片(或独立比较电路)直接断开,不经过 MCU —— 这条路径要单独测试,且不能用软件测试代替。
  • 软件 → 硬件命令的一致性:MCU 判断过压后发出断开命令,和硬件比较器直接断开,两者会不会冲突、会不会争抢继电器?

这里常见的一个认识误区是:以为软件单测/集成测过了,硬件保护就不用单独验证了。实际上两者是正交的两条线,各有各的失效模式。测试计划里必须明确列出”哪些保护依赖软件、哪些保护独立于软件”,并对后者做专门的注入测试。


5. 落地建议:让测试真正跑起来

说了这么多,最后落到实操层面,几点体会:

第一,可测性改造是测试的前提。 先拆接口、拆 HAL、拆纯函数,再谈覆盖率。否则测试写得再多,也只是在给一坨耦合代码打补丁。

第二,覆盖率要盯分支,不盯行。 安全关键软件,保护阈值、故障迁移这些分支必须被真实触发,而不是”代码被执行过”。

第三,自动化是测试存在感的来源。 测试用例写一次跑一次,等于没写。把它挂进持续集成(CI),每次提交自动跑单测 + SIL,失败即阻断合并。BMS 软件迭代快,没有 CI 的测试会很快腐烂。

第四,故障注入不是可有可无的加分项。 对 BMS 而言,正常工况测试只能证明”系统能工作”,只有故障注入才能证明”系统能安全工作”。

第五,记录测试与需求的追溯关系。 每一条保护需求,都要能对应到具体的测试用例和验证记录。这在功能安全审计里是硬要求,也是团队自己排查问题时的地图。

测试不是开发的尾巴,而是 BMS 软件能不能在车上安心跑起来的底气。从一次单体采样异常到一次完整的热失控场景,每一层测试都在为同一个问题兜底:当电池真的走向失控时,软件能不能在最后的毫秒里,把该断的断掉。


本文面向 BMS 软件与测试工程师,聚焦单元测试与集成测试的分层策略。文中涉及的采样合理性检查、表格驱动测试、故障注入等概念,均在 ISO 26262 功能安全开发流程的框架内讨论。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Navigation

About

Writing on the Wall is a newsletter for freelance writers seeking inspiration, advice, and support on their creative journey.