4、开路电压法(OCV)详解:OCV-SOC曲线标定、OCV-SOC关系拟合、静置时间与动态修正
各位好,我是老张。今天咱们聊聊开路电压法,也就是OCV。这玩意儿在BMS里是个老面孔了,但说实话,真正把它用好的团队并不多。我见过太多项目,OCV曲线标定得马马虎虎,结果SOC一跑就偏。嗯,今天我就把压箱底的经验掏出来,跟大伙儿好好掰扯掰扯。
4.1 OCV-SOC曲线标定:这事儿急不得
OCV-SOC曲线,说白了就是电池在静置状态下,开路电压和剩余电量的对应关系。你想想看,电池内部电化学反应平衡了,端电压就稳定了,这时候测出来的电压才叫开路电压。
标定流程,我一般这么干:
- 满充静置:先把电池充到4.2V(或者厂家规定的截止电压),然后静置2小时以上。我习惯静置3小时,这样更稳妥。
- 阶梯放电:以0.05C的小电流放电,每放5% SOC就停一次。注意,是5%不是10%,分辨率太粗了后面拟合会出问题。
- 充分静置:每次放电后,静置至少1小时。锂离子扩散需要时间,电压会慢慢回升。我曾经偷懒只静置30分钟,结果标出来的曲线跟实际差了2% SOC,后来被测试部追着骂。
- 记录数据:记录每个静置结束时的端电压,这就是该SOC点对应的OCV。
- 重复步骤2-4:直到放电到截止电压(比如3.0V)。
重要提醒:标定温度要统一。我建议在25℃恒温箱里做,温度一变,OCV曲线会整体平移。低温下OCV偏高,高温下偏低,这个规律大家要记住。
4.2 OCV-SOC关系拟合:多项式还是分段线性?
拿到离散的OCV-SOC数据点后,我们需要一个数学表达式来描述它们。常用的方法有两种:多项式拟合和分段线性拟合。
4.2.1 多项式拟合
多项式拟合,就是用一条高次曲线去逼近数据点。阶数越高,拟合精度越高,但过拟合风险也越大。
我个人习惯用5-7次多项式。阶数太低,拟合残差大;阶数太高,曲线会乱抖,尤其是两端(0%和100% SOC附近)。
代码示例(Python):
import numpy as np
from numpy.polynomial import polynomial as P
# 假设 soc_data 和 ocv_data 是标定得到的数组
soc_data = np.array([0, 5, 10, 15, ..., 100])
ocv_data = np.array([3.0, 3.2, 3.35, 3.45, ..., 4.2])
# 6次多项式拟合
coeffs = P.polyfit(soc_data, ocv_data, 6)
# 生成拟合后的曲线
soc_fit = np.linspace(0, 100, 1000)
ocv_fit = P.polyval(soc_fit, coeffs)
# 使用时,根据OCV反查SOC
# 注意:反查需要解方程,可以用牛顿法或二分法
我的小技巧:拟合后一定要检查残差。如果某个SOC点残差超过5mV,说明那个区域数据有问题,或者阶数不够。我曾经在一个磷酸铁锂项目上,发现10%-20% SOC段残差特别大,后来重新标定才发现是静置时间不够。
4.2.2 分段线性拟合
分段线性拟合,就是把OCV-SOC曲线分成若干段,每段用一条直线近似。这种方法在嵌入式系统里很受欢迎,因为计算量小,查表就能搞定。
分段原则:在OCV变化平缓的区域(比如磷酸铁锂的中间平台区),分段可以粗一些;在OCV变化剧烈的区域(两端),分段要细一些。
我一般这样分段:
| SOC区间 (%) | 分段点数 | 说明 |
|---|---|---|
| 0-10 | 5个点(每2%一个) | 电压陡降区,需要高分辨率 |
| 10-90 | 8个点(每10%一个) | 平台区,可以粗一些 |
| 90-100 | 5个点(每2%一个) | 电压陡升区,同样需要高分辨率 |
代码示例(C语言风格伪代码):
// 分段线性查表
typedef struct {
float soc;
float ocv;
} OCV_Point;
OCV_Point ocv_table[] = {
{0, 3.00}, {2, 3.05}, {4, 3.10}, {6, 3.15}, {8, 3.20}, {10, 3.25},
{20, 3.30}, {30, 3.33}, {40, 3.35}, {50, 3.37}, {60, 3.39}, {70, 3.42}, {80, 3.45}, {90, 3.50},
{92, 3.60}, {94, 3.70}, {96, 3.85}, {98, 4.00}, {100, 4.20}
};
float get_soc_from_ocv(float ocv) {
// 二分查找找到 ocv 所在的区间
// 线性插值计算对应的 SOC
// 返回 soc 值
}
注意:分段线性拟合的精度取决于分段点的密度和位置。如果分段不合理,查表误差可能达到3-5% SOC。我建议在标定数据的基础上,用误差分析来优化分段点位置。
4.3 静置时间对OCV的影响:别急着读数
这个问题,很多新手容易忽略。电池放电或充电后,内部锂离子浓度分布不均匀,需要时间扩散到平衡状态。这个过程中,端电压会缓慢变化,直到稳定。
静置时间与电压变化的关系:
- 0-10分钟:电压变化最快,可能下降或上升几十毫伏。这时候读OCV,误差很大。
- 10-30分钟:变化趋缓,但仍有几个毫伏的漂移。
- 30-60分钟:基本稳定,变化小于1mV。
- 60分钟以上:完全稳定,可以认为达到了真正的OCV。
我在项目中遇到过一个问题:客户反馈SOC跳变严重。排查后发现,是静置时间设置得太短,只有15分钟。电池在快充后静置15分钟,电压还在下降,这时候读到的OCV偏高,查表得到的SOC也偏高。等车辆行驶一段时间后,电压回归真实值,SOC又突然往下掉。嗯,这就是典型的「静置时间不足综合症」。
我的建议:对于三元锂电池,静置时间至少45分钟;对于磷酸铁锂电池,由于平台区电压变化极小,静置时间建议延长到1小时以上。如果时间不允许,至少也要30分钟,然后对OCV做温度补偿修正。
4.4 动态工况下的OCV修正:别指望直接测量
车辆行驶时,电池有电流流过,端电压是OCV加上极化电压和欧姆压降。这时候直接测端电压,肯定不是OCV。那怎么办?
常用的动态OCV修正方法:
- 电流积分+模型补偿:用安时积分法估算SOC,然后用电池模型(比如一阶RC模型)估算极化电压,从端电压中减去极化电压和欧姆压降,得到近似的OCV。
- 查表修正:根据当前电流大小和方向,对端电压进行查表修正。这个表需要提前标定,不同电流下的电压偏移量。
- 卡尔曼滤波融合:把OCV作为状态量之一,用卡尔曼滤波实时估计。这种方法精度高,但计算量大。
我个人比较推荐第一种方法,简单实用。代码思路如下:
// 动态OCV修正(一阶RC模型)
float estimate_ocv_dynamic(float v_terminal, float current, float dt) {
// 1. 计算欧姆压降
float v_ohm = current * R0; // R0是欧姆内阻
// 2. 更新极化电压(一阶RC)
static float v_polar = 0;
float tau = R1 * C1; // 时间常数
v_polar = v_polar * exp(-dt / tau) + current * R1 * (1 - exp(-dt / tau));
// 3. 估算OCV
float ocv_est = v_terminal - v_ohm - v_polar;
return ocv_est;
}
避坑指南:我曾经在动态修正时忽略了一个细节——电流方向。放电时极化电压是正的,充电时是负的。如果符号搞反了,修正后的OCV会偏差很大,SOC也跟着跑偏。所以,写代码时一定要检查电流符号。
另外,动态修正的精度很大程度上取决于模型参数的准确性。R0、R1、C1这些参数会随温度、SOC、老化程度变化。我建议在BMS中维护一个多维参数表,根据当前工况实时查表更新模型参数。
好了,关于OCV的内容就聊到这儿。总结一下:标定要耐心,拟合要细心,静置要够时,动态要修正。下一章咱们聊聊卡尔曼滤波在SOC估计中的应用,那可是个硬骨头,但啃下来之后,你会觉得前面的OCV都是小儿科。