2、开路电压法(OCV):OCV-SOC曲线标定、查表法实现、温度补偿、滞后效应
大家好,我是你们的老朋友。今天我们来聊聊开路电压法,也就是OCV法。
说实话,在BMS的各种SOC估算方法里,OCV法是最直观、最基础的一种。它的核心思想很简单:电池在静置足够长时间后,端电压会趋于稳定,这个电压和SOC之间存在一一对应的关系。说白了,就是测个电压,查个表,SOC就出来了。
但你要是真这么想,那就太天真了。我在实际项目中踩过的坑,十个手指头都数不过来。今天我就把这些经验掰开了揉碎了讲给你听。
2.1 OCV-SOC曲线标定
首先,你得有一条准确的OCV-SOC曲线。这条曲线怎么来的?不是拍脑袋想的,是实打实测出来的。
标定流程大致是这样的:
- 把电池充满电(SOC=100%),静置2小时以上。
- 以0.05C的小电流放电,每放出5%或10%的容量,就停下来。
- 每次停下来的静置时间,我建议至少1小时。为什么?因为极化效应需要时间消退。
- 记录静置结束时的端电压,这就是该SOC点对应的OCV。
- 重复步骤2-4,直到SOC=0%。
嗯,这里要注意:小电流放电是为了尽量减小动态影响。0.05C是我个人习惯用的,你也可以用0.02C,更准,但更耗时。
关键点:标定出来的OCV-SOC曲线,通常不是线性的。磷酸铁锂的曲线中间有一段特别平,三元锂的曲线相对陡一些。你想想看,如果曲线太平,电压稍微波动一点,SOC估算误差就会很大。这就是为什么磷酸铁锂用OCV法特别难搞的原因。
我在项目中遇到过一个问题:同一批电芯,标定出来的曲线居然有差异。后来发现是老化程度不同导致的。所以,我建议新电池和老电池要分别标定,或者定期更新曲线。
2.2 查表法实现
曲线有了,接下来就是怎么用。最常用的方法就是查表法。
查表法说白了就是:你测到一个OCV值,去表格里找对应的SOC。如果OCV正好落在两个标定点之间,那就插值。
线性插值是最简单的:
def ocv_to_soc(ocv, ocv_table, soc_table):
"""
ocv: 实测开路电压
ocv_table: 标定的OCV列表(从小到大)
soc_table: 对应的SOC列表(从0到100)
"""
# 边界检查
if ocv <= ocv_table[0]:
return soc_table[0]
if ocv >= ocv_table[-1]:
return soc_table[-1]
# 找到ocv在表中的位置
for i in range(len(ocv_table) - 1):
if ocv_table[i] <= ocv <= ocv_table[i+1]:
# 线性插值
ratio = (ocv - ocv_table[i]) / (ocv_table[i+1] - ocv_table[i])
soc = soc_table[i] + ratio * (soc_table[i+1] - soc_table[i])
return soc
# 理论上不会到这里
return None
小技巧:查表时,我建议用二分查找法,而不是顺序查找。特别是当表格有几十个点时,二分查找快得多。上面的代码为了清晰用了顺序查找,实际工程中请用二分法。
另外,表格的精度也很关键。标定点越多,插值误差越小,但占用内存也越大。我个人习惯是:对于三元锂,每5% SOC标一个点就够了;对于磷酸铁锂,建议每2%甚至1%标一个点,因为那段平缓区域太容易出错了。
2.3 温度补偿
温度对OCV的影响,很多人容易忽略。但我要告诉你,这个影响真的不小。
为什么?因为电池的化学反应受温度影响很大。低温下,内阻增大,极化效应更明显,同样的SOC对应的OCV会偏低。高温下则相反。
我常用的补偿方法是:
- 在不同温度下(比如-20°C、0°C、25°C、45°C)分别标定OCV-SOC曲线。
- 实际使用时,根据当前温度,选择最近的两条曲线进行插值。
举个例子:
def ocv_to_soc_with_temp(ocv, temp, ocv_tables, soc_table, temp_points):
"""
ocv: 实测开路电压
temp: 当前温度
ocv_tables: 字典,key为温度,value为该温度下的OCV表
soc_table: SOC表(所有温度共用)
temp_points: 标定的温度点列表
"""
# 找到温度区间
if temp <= temp_points[0]:
return ocv_to_soc(ocv, ocv_tables[temp_points[0]], soc_table)
if temp >= temp_points[-1]:
return ocv_to_soc(ocv, ocv_tables[temp_points[-1]], soc_table)
for i in range(len(temp_points) - 1):
if temp_points[i] <= temp <= temp_points[i+1]:
# 温度线性插值
ratio = (temp - temp_points[i]) / (temp_points[i+1] - temp_points[i])
# 分别查两个温度下的SOC
soc_low = ocv_to_soc(ocv, ocv_tables[temp_points[i]], soc_table)
soc_high = ocv_to_soc(ocv, ocv_tables[temp_points[i+1]], soc_table)
soc = soc_low + ratio * (soc_high - soc_low)
return soc
return None
警告:温度补偿不是万能的。在极端温度下(比如-30°C以下),OCV法的误差会急剧增大。我曾经在东北的冬天做过测试,-30°C时OCV法估算的SOC误差能达到15%以上。这时候,建议结合安时积分法或其他方法一起使用。
2.4 滞后效应
最后,我们来聊聊滞后效应。这是个很有意思的现象,也是很多新手容易忽略的。
什么是滞后效应?简单说就是:同样的SOC,充电后静置和放电后静置,测出来的OCV不一样。充电后的OCV偏高,放电后的OCV偏低。这两条曲线之间有一个差值,我们称之为滞后电压。
为什么会这样?嗯,这跟电池的材料特性和历史状态有关。说白了,电池是有记忆的。
我处理滞后效应的经验:
- 标定时,分别标定充电方向和放电方向的OCV-SOC曲线。
- 实际使用时,根据电池的最近状态(充电还是放电),选择对应的曲线。
- 如果电池静置时间足够长(比如4小时以上),滞后效应会逐渐消退,两条曲线会趋于一致。
避坑指南:我曾经在一个项目中,没有考虑滞后效应,结果SOC估算在充放电切换时出现了5%以上的跳变。后来加了滞后补偿,问题就解决了。所以,如果你发现SOC在充放电切换时突然跳变,先检查一下是不是滞后效应在作怪。
好了,关于开路电压法,我就讲这么多。总结一下:OCV法简单直观,但要做好温度补偿和滞后补偿,才能真正用好它。下一章,我们会讲卡尔曼滤波法,那是个更高级的玩法,敬请期待。