3、安时积分法:基本原理、累积误差分析、初始SOC确定、库仑效率校准
安时积分法,说白了就是「数着电流算电量」。
你想想看,电池的充放电过程,本质上就是锂离子在正负极之间来回搬家。电流大,搬得快;电流小,搬得慢。那我们只要把每一瞬间的电流都记录下来,再对时间做个积分,理论上就能知道电池里还剩多少「家当」——也就是SOC。
这个方法很直观,也是目前BMS里最常用的SOC估算手段之一。我在项目里见过不少同行,一上来就想着用卡尔曼滤波或者神经网络,结果连安时积分法都没调明白。我个人习惯是,先把安时积分法吃透,再考虑要不要上更复杂的算法。
3.1 基本原理
安时积分法的数学表达式很简单:
SOC(t) = SOC(0) - (1 / Q_n) * ∫ η * I(t) dt
其中:
- SOC(t):当前时刻的荷电状态
- SOC(0):初始荷电状态
- Q_n:电池的额定容量(单位:Ah)
- η:库仑效率
- I(t):t时刻的电流(放电为正,充电为负)
嗯,这里要注意:积分符号前面的负号,是因为我们定义放电电流为正。放电时电流为正,SOC下降,所以是减号。
在实际的BMS代码里,我们不可能真的去做连续积分。一般都是用离散累加的方式:
def soc_integration(soc_initial, current_array, dt, coulomb_efficiency, rated_capacity):
"""
安时积分法离散实现
:param soc_initial: 初始SOC (0~1)
:param current_array: 电流采样序列 (A),放电为正
:param dt: 采样间隔 (s)
:param coulomb_efficiency: 库仑效率 (0~1)
:param rated_capacity: 额定容量 (Ah)
:return: SOC序列
"""
soc = soc_initial
soc_list = []
# 将容量从Ah转换为As
capacity_as = rated_capacity * 3600
for i in current_array:
# 累积安时变化量
delta_soc = (coulomb_efficiency * i * dt) / capacity_as
soc -= delta_soc
# 限制SOC范围
soc = max(0.0, min(1.0, soc))
soc_list.append(soc)
return soc_list
这段代码我用了很多年,核心逻辑就这几行。但实际工程中,远没有这么简单。
3.2 累积误差分析
安时积分法最大的痛点是什么?
误差会累积,而且越积越大。
为什么会这样?因为积分是个「记忆」过程。你每一次的电流采样误差、时间基准误差、容量标定误差,都会被积分器记住,然后叠加到最终结果上。
我在项目中遇到过最典型的情况:一个电池包静置时SOC显示80%,客户说「怎么放了一晚上,SOC变成82%了?」——这就是积分误差在作祟。
误差来源主要有三个:
| 误差来源 | 典型量级 | 影响 |
|---|---|---|
| 电流传感器偏置误差 | ±5mA ~ ±50mA | 长期累积,影响最大 |
| 电流传感器增益误差 | ±0.5% ~ ±2% | 与电流大小成正比 |
| 时间基准误差 | ±10ppm ~ ±100ppm | 长期运行后显现 |
| 容量标定误差 | ±1% ~ ±5% | 影响满充/满放判断 |
我曾经做过一个测试:用50mA的偏置误差去积分一个100Ah的电池包,24小时后SOC误差就超过了1%。你想想看,如果这个误差不处理,一个月下来会偏到哪里去?
3.3 初始SOC确定
安时积分法需要一个初始值。这个初始值如果给错了,后面再怎么积分都是错的。
确定初始SOC,我常用的方法有三种:
- 开路电压法(OCV法):电池静置足够长时间后,端电压会趋近于开路电压。通过OCV-SOC曲线查表,就能得到初始SOC。这是最可靠的方法,但需要电池静置2小时以上。
- 上次下电记忆法:系统休眠前保存SOC值,下次上电时直接读取。这个方法简单,但有个坑——如果电池在休眠期间发生了自放电或者被外部负载消耗,读回来的值就不准了。
- 电压+温度联合查表法:对于不能长时间静置的场景(比如电动汽车),可以用电压和温度做一个粗略的SOC估计,作为积分起点。
我个人习惯是:能静置就静置,不能静置就保守估计。
举个例子,在储能BMS项目中,我要求系统上电后先判断静置时间。如果静置超过2小时,就用OCV法;如果不足2小时,就用上次记忆的SOC,但标记为「低置信度」,同时启动电压辅助校正。
def determine_initial_soc(ocv, rest_time, last_soc, ocv_soc_table):
"""
确定初始SOC
:param ocv: 当前开路电压 (V)
:param rest_time: 静置时间 (s)
:param last_soc: 上次保存的SOC
:param ocv_soc_table: OCV-SOC查表数据
:return: (初始SOC, 置信度)
"""
if rest_time > 7200: # 静置超过2小时
# 使用OCV法
soc = lookup_ocv_soc(ocv, ocv_soc_table)
confidence = 0.95
elif rest_time > 1800: # 静置超过30分钟
# OCV法,但置信度降低
soc = lookup_ocv_soc(ocv, ocv_soc_table)
confidence = 0.80
else:
# 使用上次记忆值
soc = last_soc
confidence = 0.60
# 如果电压明显异常,做限幅处理
voltage_based_soc = lookup_ocv_soc(ocv, ocv_soc_table)
if abs(soc - voltage_based_soc) > 0.15:
soc = voltage_based_soc
confidence = 0.50
return soc, confidence
3.4 库仑效率校准
库仑效率,就是放出的电量与充入的电量之比。理想情况下是1.0,但实际中总会小于1。
为什么?因为充进去的电量,有一部分会变成热量散失掉,还有一部分会消耗在副反应中(比如SEI膜的形成)。
库仑效率不是个固定值,它跟很多因素有关:
- 温度:低温下库仑效率明显下降,因为锂离子扩散变慢,副反应加剧
- 电流倍率:大电流充放电时,极化效应增强,库仑效率降低
- 电池老化:随着循环次数增加,内阻增大,库仑效率逐渐下降
- SOC区间:在低SOC和高SOC区间,库仑效率通常更低
我在项目中是怎么处理库仑效率的?
说白了,就是建一个二维查表:
# 库仑效率查表:行=温度,列=电流倍率
coulomb_efficiency_table = {
# 温度: { 电流倍率: 库仑效率 }
25: { 0.1: 0.998, 0.5: 0.995, 1.0: 0.990, 2.0: 0.980 },
10: { 0.1: 0.995, 0.5: 0.990, 1.0: 0.982, 2.0: 0.970 },
0: { 0.1: 0.990, 0.5: 0.980, 1.0: 0.970, 2.0: 0.955 },
-10: { 0.1: 0.980, 0.5: 0.965, 1.0: 0.950, 2.0: 0.930 },
}
def get_coulomb_efficiency(temperature, c_rate):
"""
根据温度和电流倍率获取库仑效率
"""
# 找到最接近的温度和倍率
temps = sorted(coulomb_efficiency_table.keys())
temp = min(temps, key=lambda x: abs(x - temperature))
rates = sorted(coulomb_efficiency_table[temp].keys())
rate = min(rates, key=lambda x: abs(x - c_rate))
return coulomb_efficiency_table[temp][rate]
嗯,这里要注意:库仑效率的校准不是一劳永逸的。我建议每半年或者每100次循环做一次在线校准。方法很简单——做一次完整的充放电循环,用实际放出的电量除以充入的电量,就能得到当前状态下的平均库仑效率。
核心要点总结:
- 安时积分法原理简单,但误差会累积,必须配合其他方法校正
- 初始SOC的确定,优先使用OCV法,静置时间不够时用记忆值+限幅
- 库仑效率不是常数,要建立温度-倍率二维查表
- 电流传感器的零点漂移是最大的误差源,务必做好温度补偿
最后说一句:安时积分法虽然「土」,但它是BMS的基石。我见过太多人一上来就搞什么深度学习、粒子滤波,结果连最基本的积分误差都控不住。先把安时积分法玩明白了,再谈别的。