锂电池SOH计算:容量实测法与循环计数法的双重验证

引言

如果说SOC(荷电状态)是电池的”油表”,那么SOH(健康状态)就是电池的”体检报告”。在动力电池和储能系统中,准确评估SOH不仅关系到续航里程的预测,更直接影响安全性判断、维护决策和资产管理。

一块标称100Ah的电池,使用两年后实际容量可能只剩80Ah。这20%的衰减如何量化?何时该更换电池?这些问题的答案都藏在SOH算法中。

然而,SOH估算远比SOC复杂。电池容量会随温度、倍率、循环次数、存储时间等多种因素变化,单一方法难以准确评估。本文将基于一个真实的车规级BMS项目,深入剖析两种主流SOH计算方法——容量实测法和循环计数法,以及它们的融合策略。

一、SOH的定义与工程意义

1.1 SOH的数学定义

SOH(State of Health)的标准定义是:

$$
SOH = \frac{当前最大可用容量}{出厂额定容量} \times 100%
$$

这里的”最大可用容量”是指在标准条件下(25℃,0.5C倍率),电池从满电到截止电压能够释放的电量。

在代码中,这个定义被直接实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
u16 GetGCapSohTenthAPI(void) {
u16 capSoh = 0;
u32 totalCap = 0;
u32 ratedCap = 0;

totalCap = GetGroupTotalCapAPI(); // 当前最大容量(单位:mAh)
ratedCap = GetGroupRatedCapAPI(); // 额定容量(单位:mAh)

if(ratedCap > 0) {
capSoh = (u16)(totalCap * 10 / ratedCap); // 单位:千分之一
}

return capSoh;
}

1.2 SOH的工程意义

1. 安全预警

当SOH低于80%时,电池内阻增大,热失控风险上升。许多车企将SOH 80%作为强制换电的红线。

2. 质保判定

动力电池的质保条款通常是”8年或15万公里,SOH不低于70%”。SOH是判定质保责任的关键依据。

3. 梯次利用决策

退役动力电池可以用于储能系统。SOH 80%的电池虽然不适合车用,但用于家庭储能完全可行。准确的SOH评估是梯次利用的前提。

4. 资产估值

对于换电站、共享电动车等运营商,电池是核心资产。SOH直接影响资产折旧和残值评估。

1.3 SOH估算的挑战

与SOC不同,SOH无法通过简单的积分计算得出。主要挑战包括:

  • 时间尺度长:容量衰减是缓慢过程,需要数月甚至数年的数据积累
  • 影响因素多:温度、倍率、DOD(放电深度)、存储时间都会影响容量
  • 测量困难:准确测量容量需要完整的充放电循环,但用户很少这样使用
  • 可逆性:低温下容量降低是可逆的,不代表真实老化

正因为这些挑战,工程中通常采用多种方法融合的策略。

二、方法一:容量实测法

2.1 基本原理

容量实测法的思路最直接:通过实际的充放电循环,测量电池能够存储和释放的电量。

在代码中,GetGroupTotalCapAPI() 返回的就是通过实测得到的容量:

1
2
3
u32 GetGroupTotalCapAPI(void) {
return(sCapForm.topCap - CAP_ZERO_POINT);
}

这里的 topCap(顶端容量)是如何确定的?关键在于容量学习算法。

2.2 容量学习的触发条件

代码中实现了基于充放电末端的容量学习逻辑:

1
2
3
4
5
6
7
8
9
10
11
// 充电末端:电压达到上限,且SOC接近100%
if((GetGCellMaxVoltAPI() >= CORR_CHG_END_MAX_V)
&& (GetGRealSocMilliAPI() >= CORR_CHG_END_LES_SOC)) {
CorrGroupTotalCapAPI(learnedCap);
}

// 放电末端:电压达到下限,且SOC接近0%
if((GetGCellMinVoltAPI() <= CORR_DHG_END_MIN_V)
&& (GetGRealSocMilliAPI() <= CORR_DHG_END_MOS_SOC)) {
CorrGroupTotalCapAPI(learnedCap);
}

容量学习的逻辑是:

  1. 充电学习:从某个SOC充到100%,记录充入电量ΔQ_chg,推算总容量
  2. 放电学习:从某个SOC放到0%,记录放出电量ΔQ_dhg,推算总容量
  3. 完整循环学习:从0%充到100%,或从100%放到0%,直接测量总容量

2.3 容量更新的平滑策略

为了避免单次测量误差导致容量跳变,代码中采用了加权平滑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void CorrGroupTotalCapAPI(u32 cap) {
u32 nowCap = sCapForm.nowCap;
u32 remCap = sCapForm.remCap;
u32 lowCap = sCapForm.baseCap;
u32 allCap = sCapForm.topCap;
u32 befCap = sCapForm.topCap - CAP_ZERO_POINT;

// 计算当前SOC
u32 soc = (nowCap - lowCap) * 10000 / befCap;

// 根据SOC重新计算当前容量
nowCap = cap * soc / 10000 + CAP_ZERO_POINT;

// 更新总容量
sCapForm.topCap = cap + CAP_ZERO_POINT;
sCapForm.nowCap = nowCap;

// 写入EEPROM
StoreGroupTotalCapToEEP();
}

这个算法的巧妙之处在于:

  • 保持SOC不变(用户体验连续)
  • 按比例调整当前容量
  • 立即持久化到EEPROM

2.4 容量实测法的优势与局限

优势:

  1. 直接测量:不依赖模型假设,反映真实容量
  2. 自适应:能够跟踪电池的实际老化过程
  3. 可验证:可以通过离线容量测试验证准确性

局限:

  1. 数据收敛慢:需要多次完整循环才能得到稳定结果
  2. 使用场景受限:用户很少进行0-100%的完整循环
  3. 温度影响:低温下测得的容量偏小,但不代表真实老化
  4. 倍率影响:大电流放电容量低,需要倍率补偿

正是因为这些局限,工程中需要引入第二种方法。

三、方法二:循环计数线性衰减模型

3.1 电池老化的经验模型

大量实验数据表明,锂电池的容量衰减大致遵循以下规律:

  1. 循环老化:每完成一次充放电循环,容量衰减一定比例
  2. 日历老化:即使不使用,容量也会随时间缓慢衰减
  3. 非线性特征:初期衰减快,后期趋于平缓

在工程实践中,为了简化计算,常采用线性模型:

$$
SOH = 100% - \frac{实际循环次数}{额定循环寿命} \times (100% - EOL_SOH)
$$

其中EOL_SOH(End of Life SOH)通常取80%,即认为电池寿命终止时SOH为80%。

3.2 代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
u16 GetGTimSohTenthAPI(void) {
u16 timSoh = 0;
u32 fadeCycle = 0;
u32 ratedCycle = 0;

fadeCycle = GetGroupFadeCycleAPI(); // 实际循环次数
ratedCycle = GetGroupRatedCycleAPI(); // 额定循环寿命

if(ratedCycle > 0) {
// SOH = 100% - (fadeCycle / ratedCycle) × 20%
// 单位:千分之一,所以 100% = 1000
timSoh = 1000 - (fadeCycle * 200 / ratedCycle);

if(timSoh > 1000) {
timSoh = 1000; // 上限保护
}
} else {
timSoh = 1000; // 默认100%
}

return timSoh;
}

公式推导示例:

假设:

  • 额定循环寿命 = 2000次
  • 寿命终止时 SOH = 80%
  • 当前循环次数 = 500次

则:

$$
SOH = 100% - \frac{500}{2000} \times (100% - 80%) = 100% - 5% = 95%
$$

3.3 循环次数的统计方法

循环次数的定义并不简单。一次”循环”是指什么?

  • 完整循环:从0%充到100%,或从100%放到0%
  • 等效循环:累积充放电量达到额定容量

代码中采用等效循环的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void GroupFadeCycleCalcTask(void) {
static u32 sHisChgCap = 0;
static u32 sHisDhgCap = 0;

u32 nowChgCap = GetChgIntCapAPI(); // 累计充电量
u32 nowDhgCap = GetDhgIntCapAPI(); // 累计放电量
u32 ratedCap = GetGroupRatedCapAPI();

u32 chgDelta = nowChgCap - sHisChgCap;
u32 dhgDelta = nowDhgCap - sHisDhgCap;

// 充放电量都达到额定容量,计为一次循环
if((chgDelta >= ratedCap) && (dhgDelta >= ratedCap)) {
sFadeInfo.fadeCycle++;
sHisChgCap = nowChgCap;
sHisDhgCap = nowDhgCap;

// 写入EEPROM
StoreGroupFadeCycleToEEP();
}
}

这个方法的优点是:

  • 不要求完整的0-100%循环
  • 能够统计碎片化的充放电行为
  • 充放电都计入,更全面

3.4 循环计数法的优势与局限

优势:

  1. 实时性好:不需要等待完整循环,每次充放电都更新
  2. 计算简单:只需要累加器和除法运算
  3. 趋势稳定:不受单次测量误差影响

局限:

  1. 模型简化:线性模型无法反映实际的非线性衰减
  2. 忽略使用条件:高温、大倍率会加速老化,但模型未考虑
  3. 日历老化缺失:长期存储的容量衰减未计入
  4. 参数依赖:需要准确的额定循环寿命参数

四、双方法融合策略

4.1 为什么需要融合?

两种方法各有优劣:

  • 容量实测法:准确但慢,受温度倍率影响大
  • 循环计数法:快速但粗糙,无法反映实际容量

融合的目的是取长补短,提高鲁棒性。

4.2 融合算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void GroupFadeSohCalcTask(void) {
u16 capSoh = 0;
u16 timSoh = 0;
u16 nowSoh = 0;
u16 hisSoh = 0;

capSoh = GetGCapSohTenthAPI(); // 容量实测SOH
timSoh = GetGTimSohTenthAPI(); // 循环计数SOH
hisSoh = GetGSohTenthAPI(); // 上次SOH

// 策略1:两种方法差异小于5%,优先使用循环计数法
if(ABS(capSoh, timSoh) < 50) { // 50 = 5% × 1000
nowSoh = timSoh;
}
// 策略2:差异较大,取平均值
else {
nowSoh = (capSoh + timSoh) / 2;
}

// 策略3:SOH只能下降,不能上升(防止反弹)
if(nowSoh > hisSoh) {
nowSoh = hisSoh;
}

// 策略4:单次下降不超过0.5%(防止异常跳变)
if((hisSoh > nowSoh) && ((hisSoh - nowSoh) > 5)) {
nowSoh = hisSoh - 5;
}

// 更新SOH
sFadeInfo.soh = nowSoh;

// 写入EEPROM
StoreGroupSohToEEP();
}

4.3 融合策略的设计思想

策略1:差异小时优先循环计数法

当两种方法的结果接近时(差异 < 5%),说明电池处于正常老化状态,此时循环计数法更稳定,不受单次容量测量误差影响。

例如:

  • capSoh = 92.3%
  • timSoh = 94.1%
  • 差异 = 1.8% < 5%
  • 采用 timSoh = 94.1%

策略2:差异大时取平均值

当两种方法差异较大时(≥ 5%),说明可能存在异常:

  • 容量实测受温度影响(冬季容量偏低)
  • 循环计数模型不准确(实际老化快于预期)

例如:

  • capSoh = 85.2%(低温测量)
  • timSoh = 94.1%(模型预测)
  • 差异 = 8.9% ≥ 5%
  • 采用平均值 = 89.65%

策略3:单调递减约束

$$
if(nowSoh > hisSoh) { nowSoh = hisSoh; }
$$

电池老化是不可逆过程,容量不可能恢复。如果计算出的SOH高于历史值,说明存在测量误差或计算错误。

策略4:变化率限制

$$
if((hisSoh > nowSoh) && ((hisSoh - nowSoh) > 5)) { nowSoh = hisSoh - 5; }
$$

单次下降不超过0.5%。电池容量衰减是缓慢过程,不可能在一次循环中突降5%。限制变化率可以避免传感器故障、容量测量异常导致的SOH跳变。

4.4 融合策略的效果分析

场景1:正常老化

时间 循环次数 容量实测 循环计数 融合结果 说明
0个月 0 100% 100% 100% 初始状态
6个月 200 97.8% 98.0% 98.0% 差异<5%,用循环计数
12个月 450 95.2% 95.5% 95.5% 差异<5%,用循环计数
18个月 720 92.8% 92.8% 92.8% 两者一致

场景2:冬季低温

时间 循环次数 容量实测 循环计数 融合结果 说明
12个月 450 95.5% 95.5% 95.5% 常温正常
13个月 465 88.2% 95.0% 91.6% 低温,取平均
14个月 480 87.5% 94.5% 91.0% 继续低温
15个月 495 94.8% 94.0% 91.0% 温度恢复,但SOH不回升

融合策略有效避免了低温导致的SOH误判。

场景3:异常跳变

时间 循环次数 容量实测 循环计数 融合结果 说明
12个月 450 95.5% 95.5% 95.5% 正常状态
12.1个月 452 82.3% 95.3% 88.8% 容量测量异常
12.2个月 454 83.1% 95.1% 88.3% 限制下降速度
12.3个月 456 95.2% 94.9% 88.3% 容量恢复,SOH不回升

融合策略将异常的单次跳变平滑化,避免了误报。

五、SOH更新时机与持久化策略

5.1 更新触发条件

SOH不需要像SOC那样高频更新。代码中的触发条件是:每完成一次等效循环,更新一次SOH。

1
2
3
4
5
6
7
8
9
10
void GroupFadeSohCalcTask(void) {
static u32 sHisCycle = 0;
u32 nowCycle = GetGroupFadeCycleAPI();

// 循环次数变化时才更新SOH
if(nowCycle != sHisCycle) {
// 执行SOH计算
sHisCycle = nowCycle;
}
}

这个策略的优点:

  • 降低计算开销
  • 避免频繁写EEPROM
  • 数据稳定性更高

5.2 EEPROM存储策略

1
2
3
4
5
6
7
8
9
10
static void StoreGroupSohToEEP(void) {
static u16 sHisSoh = 0;
u16 nowSoh = sFadeInfo.soh;

// SOH变化≥0.1%时才写入
if(ABS(nowSoh, sHisSoh) >= 1) {
EnerChangEepGSohHook(nowSoh);
sHisSoh = nowSoh;
}
}

5.3 掉电恢复机制

1
2
3
4
5
6
7
8
9
10
11
12
13
void GroupFadeSohInit(void) {
u32 data[3] = {0};

if(TRUE == ParaReadStoreFadeInfo(data, 3)) {
sFadeInfo.fadeCycle = data[0];
sFadeInfo.soh = data[1];
sFadeInfo.soc = data[2];
} else {
sFadeInfo.fadeCycle = 0;
sFadeInfo.soh = 1000; // 默认100%
sFadeInfo.soc = 0;
}
}

六、SOH的工程局限性与改进方向

6.1 温度影响

锂电池的容量与温度强相关:

温度 相对容量
-20℃ 60-70%
0℃ 80-85%
25℃ 100%
45℃ 102-105%

如果在-20℃测量容量,得到的是70%,但这不代表电池老化到SOH 70%,而是低温导致的可逆容量损失。改进方向是引入温度补偿系数。

6.2 倍率影响

大电流放电时,容量会降低:

放电倍率 相对容量
0.2C 100%
0.5C 98%
1C 95%
2C 88%
3C 80%

改进方向:只在小电流(<0.5C)时进行容量学习。

6.3 日历老化

电池即使不使用,也会随时间老化。典型的日历老化速率是每年2-3%。改进方向:引入时间因子:

1
2
3
4
5
6
7
u16 GetGTimSohWithCalendar(void) {
u16 cycleSoh = GetGTimSohTenthAPI();
u32 days = GetSystemRunDays();
u16 calendarFade = days * 20 / 365; // 日历老化:每年衰减2%
u16 totalSoh = 1000 - calendarFade;
return (cycleSoh < totalSoh) ? cycleSoh : totalSoh;
}

6.4 非线性衰减

实际的容量衰减曲线是非线性的:

  • 初期(0-200次循环):快速衰减,SOH从100%降到95%
  • 中期(200-1500次循环):缓慢衰减,SOH从95%降到85%
  • 后期(1500-2000次循环):加速衰减,SOH从85%降到80%

改进方向:采用分段线性或指数模型替代简单的线性模型。

七、实际应用中的SOH表现

7.1 典型衰减曲线

基于实际项目数据,一个200Ah磷酸铁锂电池包的SOH变化:

时间(月) 循环次数 容量实测SOH 循环计数SOH 融合SOH 备注
0 0 100.0% 100.0% 100.0% 出厂状态
3 120 98.5% 98.8% 98.8% 初期快速衰减
6 280 97.2% 97.2% 97.2% 进入稳定期
12 600 95.1% 94.0% 94.6% 容量实测偏高
18 950 92.8% 90.5% 91.7% 差异增大
24 1350 89.5% 86.5% 88.0% 取平均值
30 1720 86.2% 82.8% 84.5% 接近寿命末期
36 2050 82.1% 79.5% 80.8% 达到换电阈值

7.2 不同电池化学体系的表现

电池类型 循环寿命 适配性 说明
磷酸铁锂(LFP) 3000-5000次 ★★★★★ 线性模型拟合较好
三元锂(NCM) 1500-2500次 ★★★☆☆ 建议改用非线性模型
钛酸锂(LTO) 10000+次 ★★★★☆ 循环计数法更适用
铅酸电池 300-500次 ★★☆☆☆ 需要大幅修改参数

八、SOH在BMS系统中的应用

8.1 功率限制动态调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
u16 CalcMaxDischargeCurrent(void) {
u16 soh = GetGSohTenthAPI();
u16 baseCurr = GetRatedDischargeCurrent();
u16 maxCurr = baseCurr;

if(soh < 900) { // SOH < 90%时开始限流
maxCurr = baseCurr * soh / 900;
}

if(soh < 800) { // SOH < 80%时强制限流
maxCurr = baseCurr * 60 / 100;
}

return maxCurr;
}

8.2 充电策略优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void AdaptiveChargingStrategy(void) {
u16 soh = GetGSohTenthAPI();

if(soh >= 950) {
SetChargeVoltage(4.20); // 健康电池:快充策略
SetChargeCurrent(1.0);
} else if(soh >= 900) {
SetChargeVoltage(4.15); // 轻度老化:标准策略
SetChargeCurrent(0.8);
} else if(soh >= 850) {
SetChargeVoltage(4.10); // 中度老化:保守策略
SetChargeCurrent(0.5);
} else {
SetChargeVoltage(4.05); // 重度老化:保护策略
SetChargeCurrent(0.3);
SetWarningFlag(BATTERY_AGING_WARNING);
}
}

8.3 质保判定与预警

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void WarrantyAndWarningCheck(void) {
u16 soh = GetGSohTenthAPI();

// 质保期内SOH检查
if((days < 365 * 8) && (mileage < 150000)) {
if(soh < 700) {
SetFaultCode(FAULT_WARRANTY_SOH_LOW);
}
}

// 分级预警
if(soh < 850) SetWarningLevel(WARNING_LEVEL_1);
if(soh < 820) SetWarningLevel(WARNING_LEVEL_2);
if(soh < 800) {
SetWarningLevel(WARNING_LEVEL_3);
LimitVehicleSpeed(80);
}
}

8.4 梯次利用评估

1
2
3
4
5
6
typedef enum {
BATTERY_GRADE_A, // SOH > 90%,可继续车用
BATTERY_GRADE_B, // 80% < SOH ≤ 90%,可梯次利用(储能)
BATTERY_GRADE_C, // 70% < SOH ≤ 80%,可低功率应用
BATTERY_GRADE_D, // SOH ≤ 70%,建议回收
} BatteryGrade_e;

九、工程实践经验总结

9.1 关键参数标定

参数 建议值 说明
额定循环寿命 保守取值,留有余量 不同批次电池有差异
EOL SOH阈值 80%(可配置) 车用vs储能场景不同
融合阈值 3-8% 影响两种方法切换逻辑
变化率限制 0.3-1.0% 过小响应慢,过大漏检异常

9.2 常见问题与解决方案

问题1:SOH初始值不是100%

新电池出厂容量略高于额定容量,初始化时强制设为100%:

1
2
3
4
5
void GroupFadeSohInit(void) {
if(GetGroupFadeCycleAPI() == 0) {
sFadeInfo.soh = 1000; // 强制100%
}
}

问题2:SOH在低温下异常下降

1
2
3
if(GetGAvgTempAPI() < 5) {  // 低于5℃
DisableCapacityLearning();
}

问题3:SOH长期不更新

降低容量学习的触发条件,从0-100%放宽到5-95%。

十、结语

SOH估算是BMS中最具挑战性的功能之一。它不像SOC那样有明确的物理定义和测量方法,而是需要在有限的数据、不确定的使用条件下,推断电池的长期健康状态。

本文基于真实的车规级BMS项目,展示了容量实测法和循环计数法的双重验证策略:

  • 容量实测法:提供了准确性,确保SOH反映真实容量
  • 循环计数法:提供了稳定性,避免单次测量误差
  • 融合策略:提供了鲁棒性,在异常场景下保持合理输出

然而,当前实现也有局限:缺少温度补偿、倍率补偿、日历老化模型。这些都是未来改进的方向——从简单的线性模型,到复杂的机器学习模型;从单车独立计算,到云端大数据分析。

但无论技术如何进步,对电池物理特性的深刻理解、对工程细节的精心打磨,始终是算法成功的基石。


参考代码文件

  • CellFadeCalc.c/h:SOH计算核心实现
  • CapInfoCalc.c/h:容量实测相关
  • CurrIntegral.c/h:循环计数统计
  • ParaIntEep.c/h:EEPROM存储管理

关键常数

常数 说明
EOL_SOH 800 寿命终止SOH阈值(80%)
SOH_FUSION_THRESHOLD 50 融合切换阈值(5%)
SOH_MAX_DECLINE 5 单次最大下降(0.5%)

相关标准

  • GB/T 31484-2015:电动汽车用动力蓄电池循环寿命要求及试验方法
  • IEC 62660-1:锂离子动力电池单体性能和寿命测试
  • SAE J2929:电动和混合动力汽车电池系统安全标准