安时积分法在车规级 BMS 中的完整工程实现——基于 NXP 芯片的 SOC 估算实践
引言
在动力电池管理系统(BMS)中,SOC(State of Charge,荷电状态)估算是最核心的功能之一。它直接关系到电池的使用安全、续航里程预测以及充放电策略的制定。在众多SOC估算算法中,安时积分法(也称库仑计法)因其原理简单、实时性好而成为工程应用中的基础算法。
然而,看似简单的”电流乘以时间”背后,隐藏着大量工程实现细节:如何在没有浮点运算单元的MCU上高效计算?如何设计单位换算避免精度损失?如何处理累积误差?本文将基于一个真实的车规级BMS项目(基于NXP芯片),深入剖析安时积分法的完整工程实现。
一、安时积分法的理论基础
1.1 基本原理
安时积分法的核心思想源于电荷守恒定律。电池的SOC本质上是当前剩余电量与总容量的比值:
$$
SOC(t) = SOC(t0) + \frac{1}{C_{rated}} \times \int_{t0}^{t} \eta(I) \times I(\tau) d\tau
$$
其中:
- **SOC(t0)**:初始荷电状态
- C_rated:电池额定容量
- **I(τ)**:τ时刻的电流(充电为负,放电为正)
- **η(I)**:充放电效率(通常充电效率0.95-1.0,放电效率接近1.0)
在数字系统中,连续积分被离散化为累加:
$$
\Delta Cap = I \times \Delta t
$$
$$
SOC(k) = SOC(k-1) - \frac{\Delta Cap}{C_{rated}}
$$
1.2 算法优势与局限
优势:
- 实时性强:每个采样周期都能更新SOC
- 动态响应快:能准确跟踪大电流充放电过程
- 计算简单:适合资源受限的嵌入式系统
局限:
- 误差累积:传感器漂移、量化误差会不断叠加
- 依赖初值:需要准确的SOC初始值
- 无自校正能力:必须配合其他算法(如OCV法)定期修正
正因为这些局限,工程实现中需要精心设计数据结构、单位换算和误差控制策略。
二、工程实现的单位级联设计
2.1 为什么不直接用浮点数?
在这个项目中,采用的是 NXP 芯片,虽然它配备了FPU(浮点运算单元),但工程师选择了整数运算方案。原因有三:
- 确定性:整数运算的执行时间固定,不会因为数值大小产生波动,这对实时系统至关重要
- 资源分配:FPU资源可以留给更复杂的算法(如卡尔曼滤波、神经网络SOC估算)
- 精度可控:通过合理的单位设计,整数运算可以达到足够的精度
2.2 三级单位级联架构
代码中设计了一个巧妙的三级单位系统:
1 | typedef struct { |
第一级:10mA·ms(微观累积器)
电流采样值的单位是 10mA,采样周期是 1ms。每次采样后执行:
1 | void ChgCapIntTask(u32 curr, u8 cycle) { |
这里的关键常数是 360000,它的来历是:
1 | 1 mAh = 1 mA × 1 h = 1 mA × 3600 s = 3600 mA·s |
第二级:1mAh(中观容量单位)
当累积器达到 360000 时,进位到 mAh 级别。这个单位适合小型储能系统(如电动自行车、小型UPS)。
第三级:1Ah(宏观容量单位)
对于大型储能系统(如电动汽车、储能柜),代码还支持进一步进位到 Ah:
1 |
|
2.3 单位设计的精度分析
采用 10mA·ms 作为最小单位,理论精度是:
1 | 1个计数单位 = 10mA × 1ms = 0.01 mA·s = 0.01/3600 mAh ≈ 2.78 μAh |
对于一个100Ah的电池包,相对精度为:
1 | 2.78 μAh / 100000 mAh = 2.78 × 10^-8 ≈ 0.0000028% |
这个精度远超实际需求(通常SOC精度要求在1%以内),因此整数方案完全可行。
三、充放电方向处理与净变化量
3.1 双向累积的必要性
电池既可以充电也可以放电,代码中分别维护了两个独立的累积器:
1 | sCapInt.chgCap10ma1ms // 充电累积 |
为什么不用一个有符号变量?因为:
- 统计需求:需要分别记录累计充电量和累计放电量,用于电池健康度分析
- 效率计算:充放电效率不同,分开记录便于后续修正
- 溢出保护:无符号整数的溢出行为更可预测
3.2 净变化量的巧妙设计
代码中有一个关键变量 changCap1ma1h,它记录的是充放电的净变化:
1 | // 充电时(电流为负) |
这个变量在SOC计算中起到关键作用:
1 | static void CalcGroupNowCapHandle(void) { |
这里的差分计算(nowChangCap - sHisChangCap)是一个重要的工程技巧,它能够:
- 自动处理充放电切换
- 检测异常跳变(如传感器故障)
- 避免累积误差在短时间内爆发
四、能量积分的并行实现
4.1 为什么需要能量积分?
除了电量(Ah),BMS还需要统计能量(Wh),用于:
- 能量效率分析(充入能量 vs 放出能量)
- 电费计算(储能系统)
- 热管理(能量损耗 = 发热)
能量积分的公式是:
$$
E = \int V(t) \times I(t) dt
$$
4.2 四级单位级联
能量积分采用了更复杂的四级单位系统:
1 | typedef struct { |
代码实现:
1 | void ChgEnerIntTask(u32 curr, u16 volt, u8 cycle) { |
单位换算链:
1 | 1 Wh = 1 W × 1 h = 1 W × 3600 s = 3600 W·s = 3,600,000 W·ms |
4.3 电压采样的同步问题
注意代码中的一个细节:
1 | // 在CurrSample.c中 |
这里做了电压源的选择:
- 优先使用 AFE芯片采集的单体电压总和(
GetGCellSumVoltAPI()) - 降级使用 总压传感器的值(
GetGSampSumVoltAPI()),当总压差异过大时
这是因为单体电压求和的精度通常高于总压传感器,但在某些异常情况下(如单体采样失败),需要降级使用总压传感器。
五、积分触发时机与采样同步
5.1 采样完成标志的作用
在实时系统中,ADC采样是异步的。代码中通过标志位确保积分使用的是稳定的采样值:
1 | if((0 == SampGetCurrSampExpFlagAPI(eCCHAN_Num)) // 电流采样正常 |
SampGetCurrSampExpFlagAPI() 检查采样异常标志,只有在采样正常时才执行积分。这避免了使用脏数据导致的SOC跳变。
5.2 积分起始点的设定
代码中定义了积分起始点:
1 | gGHardPara_104[eGHardPara104_ChgIntPoint] // 充电积分起始点 |
这是为了避免小电流噪声的累积。例如,如果积分起始点设为 50(即500mA),那么小于500mA的电流不会被积分,这在静置状态下能有效抑制零漂误差。
六、累积误差来源与控制策略
6.1 误差来源分析
1. 电流传感器零漂
霍尔传感器的零点会随温度漂移,典型值为 ±50mA。假设零漂为 +50mA(实际电流为0,但传感器输出50mA),在24小时内累积的误差为:
1 | 误差 = 50mA × 24h = 1200 mAh = 1.2 Ah |
对于100Ah电池,这相当于 1.2% 的SOC误差。
2. ADC量化误差
假设ADC分辨率为12位,电流测量范围为 ±500A,则量化步长为:
1 | LSB = 1000A / 4096 ≈ 0.244 A = 244 mA |
每次采样的量化误差为 ±122mA,在随机分布假设下,N次采样后的累积误差为:
$$
\sigma_{累积} = \sigma_{单次} \times \sqrt{N}
$$
3. 采样周期不均匀
在RTOS环境下,任务调度会产生抖动。假设标称周期为1ms,实际周期在0.9-1.1ms之间波动,那么在1小时内:
1 | 理论采样次数 = 3,600,000 |
这会导致时间基准误差。
6.2 工程中的误差控制策略
策略1:异常值检测与丢弃
1 | if((realChangCap > (s32)sCapForm.standCap) || |
如果单次变化量超过额定容量,显然是异常数据(传感器故障或通信错误),直接丢弃。
策略2:边界限制
1 | if(sCapForm.nowCap > sCapForm.topCap) { |
防止SOC超出0-100%范围。
策略3:基于电压的定期校正
代码中实现了复杂的电压-SOC校正逻辑(在 SocDisplay.c 中):
1 | // 充电末端校正 |
这是利用电池的OCV特性:在充电末端(高电压)和放电末端(低电压),电压与SOC有较强的相关性,可以用来修正累积误差。
策略4:静置状态下的电压校正
1 | if(eCURR_IDLE == GetGChgDhgStateAPI()) { |
在静置状态下,电池电压会逐渐趋近于 OCV,此时可以根据电压-SOC查找表进行校正。
策略5:EEPROM定期存储
1 | static void StoreGroupNowCapToEEP(void) { |
定期将SOC存入EEPROM,防止掉电后丢失。存储策略很巧妙:
- 正常情况下,SOC变化0.1%就存储一次
- 充放电结束时立即存储(即使变化量小于0.1%)
这样既保证了数据安全,又避免了频繁写EEPROM导致寿命问题。
七、SOC平滑显示算法
7.1 为什么需要平滑?
安时积分法计算的SOC是”真实SOC”,它会随着电流波动而快速变化。但对于用户界面,频繁跳变的SOC会造成不良体验。因此需要一个平滑算法。
7.2 分段变速追踪策略
代码中实现了一个精妙的分段变速追踪算法:
1 | if(copySoc < aimSoc) { // 显示值低于真实值(充电中) |
这个算法的设计思想是:
| SOC区间 | 追踪速度 | 设计目的 |
|---|---|---|
| 90%-100% | 5-30秒 | 用户最关心”何时充满”,加快追踪 |
| 80%-90% | 60秒 | 适中追踪速度 |
| 0%-80% | 120秒 | 较慢追踪,避免频繁跳变引起焦虑 |
放电时采用对称的策略,在低SOC区域(0%-20%)加快追踪,提醒用户及时充电。
7.3 基于电量变化的追踪
除了时间因素,算法还考虑了实际电量变化:
1 | inteCap = GetChgDhgChangCapAPI(); // 获取净变化量 |
只有当电量变化足够大时,才更新显示SOC。这避免了小电流下的频繁跳变。
八、工程实践中的经验总结
8.1 单位设计的黄金法则
- 最小单位要足够小:确保精度满足需求
- 进位常数要合理:避免频繁进位导致的计算开销
- 中间单位要实用:匹配实际应用场景(mAh vs Ah)
8.2 误差控制的层次化策略
| 层级 | 措施 |
|---|---|
| 硬件层 | 选择高精度传感器,做好温度补偿 |
| 采样层 | 滤波、异常检测、同步控制 |
| 算法层 | 边界限制、差分计算、定期校正 |
| 系统层 | 多算法融合(安时积分 + OCV + 卡尔曼滤波) |
8.3 实时性与精度的平衡
- 快速路径:安时积分(1ms周期),用于实时响应
- 慢速路径:电压校正(1s周期),用于误差修正
- 超慢路径:容量学习(数小时),用于长期优化
九、结语
安时积分法看似简单,但要在资源受限的嵌入式系统中实现高精度、高可靠性的SOC估算,需要在单位设计、误差控制、用户体验等多个维度进行精心设计。
本文基于真实的车规级BMS项目,展示了从理论到工程实现的完整链条。希望这些经验能帮助读者理解:优秀的嵌入式软件不仅需要扎实的理论基础,更需要对硬件特性、实时约束、用户需求的深刻理解。
在实际项目中,安时积分法通常不会单独使用,而是与开路电压法、扩展卡尔曼滤波、神经网络等算法融合,形成多层次的SOC估算体系。但无论算法如何演进,安时积分法作为基础,其工程实现的质量直接决定了整个系统的下限。
关键常数汇总
| 常数 | 值 | 用途 |
|---|---|---|
MULT_MAH_TO_10MAMS |
360000 | mAh到10mA·ms的换算系数 |
MULT_WH_TO_WMS |
3600000 | Wh到W·ms的换算系数 |
SOC_CHANG_TO_WRITE_EEP |
1 | EEPROM存储阈值(0.1%) |