2、开路电压法(OCV):OCV-SOC曲线标定、查表法实现、温度补偿、滞后效应

大家好,我是你们的老朋友。今天我们来聊聊开路电压法,也就是OCV法。

说实话,在BMS的各种SOC估算方法里,OCV法是最直观、最基础的一种。它的核心思想很简单:电池在静置足够长时间后,端电压会趋于稳定,这个电压和SOC之间存在一一对应的关系。说白了,就是测个电压,查个表,SOC就出来了。

但你要是真这么想,那就太天真了。我在实际项目中踩过的坑,十个手指头都数不过来。今天我就把这些经验掰开了揉碎了讲给你听。

2.1 OCV-SOC曲线标定

首先,你得有一条准确的OCV-SOC曲线。这条曲线怎么来的?不是拍脑袋想的,是实打实测出来的。

标定流程大致是这样的:

  1. 把电池充满电(SOC=100%),静置2小时以上。
  2. 以0.05C的小电流放电,每放出5%或10%的容量,就停下来。
  3. 每次停下来的静置时间,我建议至少1小时。为什么?因为极化效应需要时间消退。
  4. 记录静置结束时的端电压,这就是该SOC点对应的OCV。
  5. 重复步骤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法简单直观,但要做好温度补偿和滞后补偿,才能真正用好它。下一章,我们会讲卡尔曼滤波法,那是个更高级的玩法,敬请期待。