4、开路电压法:OCV-SOC曲线标定、OCV-SOC关系拟合、静置时间对OCV的影响、温度对OCV的影响

开路电压法,说白了就是利用电池静置后的端电压来反推SOC。这个方法在BMS里属于“基本功”,但想把它做准了,还真得下点功夫。我个人习惯把OCV法当作多算法融合里的“定海神针”——平时不怎么用它,但关键时刻能纠偏。

4.1 OCV-SOC曲线标定:从实验到数据

标定OCV-SOC曲线,核心就一句话:让电池在不同SOC点充分静置,记录对应的开路电压。听起来简单,但做起来全是坑。

我常用的标定流程是这样的:

  1. 满充:以0.5C恒流恒压充到4.2V,截止电流0.05C。这一步确保起点一致。
  2. 静置:满充后静置2小时以上,让极化彻底消失。记录此时的OCV作为100%SOC点。
  3. 放电:以0.2C放出5%或10%的容量(比如50Ah电池,放2.5Ah或5Ah)。
  4. 再静置:每次放电后静置1小时(或更久),记录OCV。
  5. 重复:直到电压降到放电截止电压(比如3.0V)。

嗯,这里要注意:静置时间不能偷懒。我在项目中遇到过,为了赶进度把静置时间从1小时压缩到20分钟,结果标出来的曲线在低SOC段偏差超过2%。后来返工重做,反而更耽误时间。

关键点:标定用的电流越小越好(0.05C~0.2C),静置时间越长越好(至少1小时)。如果条件允许,用0.05C放电+2小时静置,数据最干净。

4.2 OCV-SOC关系拟合:从离散点到连续曲线

标定完得到的是离散点——比如10%、20%、30%...对应的OCV。但实际使用中,我们需要任意SOC下的OCV值。这就得做拟合。

常用的拟合方法有几种:

  • 多项式拟合:最简单,但高阶多项式容易过拟合。我一般用5~7阶多项式。
  • 分段线性插值:把相邻两个标定点之间用直线连起来。精度够用,计算量小。
  • 样条插值:平滑性好,但计算稍复杂。

我个人习惯用分段线性插值+低阶多项式平滑。举个例子:

// 伪代码:分段线性插值查表
float getOCVfromSOC(float soc, float* soc_table, float* ocv_table, int table_size) {
    // 边界处理
    if (soc <= soc_table[0]) return ocv_table[0];
    if (soc >= soc_table[table_size-1]) return ocv_table[table_size-1];
    
    // 查找区间
    int idx = 0;
    for (int i = 0; i < table_size-1; i++) {
        if (soc >= soc_table[i] && soc <= soc_table[i+1]) {
            idx = i;
            break;
        }
    }
    
    // 线性插值
    float ratio = (soc - soc_table[idx]) / (soc_table[idx+1] - soc_table[idx]);
    return ocv_table[idx] + ratio * (ocv_table[idx+1] - ocv_table[idx]);
}

你想想看,为什么不用纯多项式?因为磷酸铁锂的OCV曲线在中间段(20%~80%SOC)非常平坦,多项式拟合容易在两端出现“龙摆尾”现象。我曾经用10阶多项式拟合LFP电池,结果在0%SOC附近算出来的OCV比实际低了50mV——这误差足以让SOC估算偏5%。

我的经验:标定点建议在两端(0~20%和80~100%)加密,中间段可以稀疏一些。LFP电池尤其要注意低SOC段的标定,因为那里电压变化最剧烈。

4.3 静置时间对OCV的影响:别急着读数

电池刚停止充放电时,端电压不是真正的OCV。因为内部还有极化效应——浓差极化、电化学极化、欧姆极化。这些极化会随时间逐渐消失。

我做过一组对比实验:

静置时间 OCV读数(3.6V电池) 与真实OCV偏差
0分钟(刚停止) 3.72V +120mV
10分钟 3.65V +50mV
30分钟 3.62V +20mV
60分钟 3.605V +5mV
120分钟 3.600V 0mV(基准)

看到了吧?刚停止时偏差120mV,换算成SOC误差可能高达8%~10%。所以BMS里用OCV法时,必须判断电池是否充分静置。

实际工程中,我们不可能每次都等2小时。我的做法是:

  • 短静置(<30分钟):不用OCV法,只用安时积分。
  • 中静置(30分钟~2小时):用修正后的OCV,加一个极化补偿系数。
  • 长静置(>2小时):直接用OCV查表,认为极化基本消失。

避坑指南:我曾经在项目里遇到一个bug——车辆熄火后只停了15分钟就重新上电,BMS直接用OCV查表,结果SOC从60%跳到了68%。后来加了静置时间判断逻辑,才解决这个问题。

4.4 温度对OCV的影响:别忽视这个“隐形杀手”

温度对OCV的影响,很多人觉得不大。但实际数据会告诉你:温度每变化10°C,OCV可能偏移5~15mV。对于LFP电池,这个偏移在低SOC段尤其明显。

我整理过一组典型数据(以25°C为基准):

温度 OCV偏移(50%SOC) OCV偏移(10%SOC)
-20°C -12mV -25mV
0°C -5mV -10mV
25°C 0mV 0mV
45°C +3mV +8mV
60°C +6mV +15mV

为什么会这样?因为温度影响电池的化学平衡电位。低温下内阻增大,极化更严重,OCV会偏低;高温下活性增强,OCV会偏高。

我的处理方法是:建立温度-OCV修正表。标定时不只标25°C一条曲线,而是标-20°C、0°C、25°C、45°C、60°C五条曲线。实际使用时,根据当前温度做线性插值。

// 伪代码:温度修正OCV
float getOCVwithTemp(float soc, float temp, float* temp_table, float*** ocv_3d_table) {
    // 找到温度区间
    int t_idx = findTempIndex(temp, temp_table);
    float t_ratio = (temp - temp_table[t_idx]) / (temp_table[t_idx+1] - temp_table[t_idx]);
    
    // 查两个温度下的OCV
    float ocv_low = getOCVfromSOC(soc, soc_table, ocv_3d_table[t_idx], table_size);
    float ocv_high = getOCVfromSOC(soc, soc_table, ocv_3d_table[t_idx+1], table_size);
    
    // 温度线性插值
    return ocv_low + t_ratio * (ocv_high - ocv_low);
}

核心结论:OCV法不是简单的查表,而是静置时间判断+温度修正+极化补偿的综合过程。忽略任何一个环节,SOC估算都会跑偏。

嗯,最后说一句。OCV法虽然基础,但它是多算法融合的“锚点”。安时积分漂移了,卡尔曼滤波发散时,最终都要靠OCV法来拉回来。所以,把OCV-SOC曲线标准了,后面的工作才能站得住脚。