4、开路电压法:OCV-SOC曲线标定、OCV-SOC关系拟合、静置时间对OCV的影响、温度对OCV的影响
开路电压法,说白了就是利用电池静置后的端电压来反推SOC。这个方法在BMS里属于“基本功”,但想把它做准了,还真得下点功夫。我个人习惯把OCV法当作多算法融合里的“定海神针”——平时不怎么用它,但关键时刻能纠偏。
4.1 OCV-SOC曲线标定:从实验到数据
标定OCV-SOC曲线,核心就一句话:让电池在不同SOC点充分静置,记录对应的开路电压。听起来简单,但做起来全是坑。
我常用的标定流程是这样的:
- 满充:以0.5C恒流恒压充到4.2V,截止电流0.05C。这一步确保起点一致。
- 静置:满充后静置2小时以上,让极化彻底消失。记录此时的OCV作为100%SOC点。
- 放电:以0.2C放出5%或10%的容量(比如50Ah电池,放2.5Ah或5Ah)。
- 再静置:每次放电后静置1小时(或更久),记录OCV。
- 重复:直到电压降到放电截止电压(比如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曲线标准了,后面的工作才能站得住脚。