一、引言
此方法主要参考电动汽车用磷酸铁锂电池SOC估算方法这篇论文
总结:
开路电压的测量需要将电池静止相当长的一段时间才能达到平衡状态进行测量。
安时积分法存在初始SOC的估算和累积的误差。
所以上述两种方法都存在一定的缺陷,因此下面主要讲解两种方法混合估算SOC的方法。
二、SOC估算流程图
、
三、SOC软件设计步骤
四、软件代码的书写
第一步:获取在不同温度时OCV-SOC的曲线
第二步:上电校准SOC
实际采样OCV和关机时保存的电压差超过200mV,那么此OCV可用于上电修正SOC,否则使用之前掉电保存的SOC
第三步:安时积分法
充电时:
//单节最高电压小于3600mV时,进行正常SOC积分
//单节最高电压大于3600mV时,调整SOC积分斜率(目的是充到保护电压时正好SOC=100%)
放电时:
//单节最低电压大于2900mV时,进行正常SOC积分
//单节最低电压小于2900mV时,调整SOC积分斜率(目的是放空时正好SOC=0%)
第四步:完整代码
#include "soc.h"
TLIFE tagLife;
unsigned char SBErrFlashCount;
const unsigned int OCV_SOC[3][38]=
{
{ //表0:电池温度为0℃时,OCV对应SOC
2787, //0%
2897,2992,3059,3107,3150, //1%-5%
3159,3166,3172,3178,3184, //6%-10%
3189,3195,3200,3205,3209, //11%-15%
3214,3219,3223,3227,3231, //16%-20%
3235,3238,3242,3246,3249, //21%-25%
3252,3255,3258,3261,3264, //26%-30%
3266,3269,3271,3274,3276, //31%-35%
//此区域都给50%
3334,3380, //99%-100%
},
{//表1:电池温度为20℃时,OCV对应SOC
2837, //0%
2947,3032,3095,3139,3178, //1%-5%
3184,3187,3191,3195,3198, //6%-10%
3202,3206,3210,3214,3218, //11%-15%
3223,3227,3231,3235,3239, //16%-20%
3243,3247,3251,3255,3258, //21%-25%
3262,3265,3269,3272,3275, //26%-30%
3277,3280,3282,3284,3286, //31%-35%
//此区域都给50%
3334,3380, //99%-100%
}, {//表2:电池温度为30℃时,OCV对应SOC
2840, //0%
2950,3034,3096,3140,3179, //1%-5%
3185,3188,3192,3195,3199, //6%-10%
3203,3207,3211,3215,3219, //11%-15%
3223,3227,3231,3235,3240, //16%-20%
3244,3248,3252,3256,3259, //21%-25%
3263,3267,3270,3273,3276, //26%-30%
3279,3282,3285,3287,3289, //31%-35%
//此区域都给50%
3333,3360, //99%-100%
}
};
/************************************函数功能描述************************************************
Function Name : PowerONAdjustSOC(void)
Description : 上电时修正SOC
Input : 无
Output : 无
*************************************************************************************************/
void PowerONAdjustSOC(void)
{
unsigned int TempMinVoltage,DtV;
if(FLASH.BeWriteFlag == ISNO) //FLASH数据无效时
{
tagLife.fRealTotalCapacity = tagLife.fNominalCapacity; //电池实际总容量为标称容量(mA.h)
tagLife.uiSOH_Percent = 100; //SOH为100%
tagLife.uiDisTimes = 0; //清0循环次数
tagLife.uiSOC_Percent = OCVtoSOC(tagBMS.uiMinVoltage); //通过OCV获得SOC
tagLife.fSOC = (tagLife.fNominalCapacity/100)*tagLife.uiSOC_Percent; //通过SOC计算出当前容量
}
else //FLASH数据有效时
{
/****实际额定容量不能超过标称容量或不能小于标称容量的80%,否则进行修正****/
if(tagLife.fRealTotalCapacity > tagLife.fNominalCapacity){ tagLife.fRealTotalCapacity = tagLife.fNominalCapacity; }
else if(tagLife.fRealTotalCapacity < (tagLife.fNominalCapacity*0.5f)){ tagLife.fRealTotalCapacity = tagLife.fNominalCapacity; }
TempMinVoltage = FLASH.ReadData[7]; //读取上次掉电前最低节电压
if(tagBMS.uiMinVoltage > TempMinVoltage) //如果上电后实际采样OCV和关机时保存的电压差超过200mV,那么此OCV可用于上电修正SOC,否则使用之前掉电保存的SOC
{
DtV = tagBMS.uiMinVoltage - TempMinVoltage;
}
else
{
DtV = TempMinVoltage - tagBMS.uiMinVoltage;
}
if( DtV >= 200 ) //压差过大,进行OCV修正
{
tagLife.uiSOC_Percent = OCVtoSOC(tagBMS.uiMinVoltage); //通过OCV重新获得SOC
tagLife.fSOC = (tagLife.fNominalCapacity/100) * tagLife.uiSOC_Percent; //通过SOC计算出当前电量
}
}
tagLife.ulWorkTime = 0;
}
/************************************函数功能描述************************************************
Function Name : OCVtoSOC(unsigned int ocv)
Description : 通过OCV获得SOC
Input : OCV 开路电压值
Output : SOC 电量值(%)
*************************************************************************************************/
unsigned int OCVtoSOC(unsigned int ocv)
{
unsigned char i=0,socarray=1,tempsoc;
获取电池温度,找对应数组
if(tagBMS.iNtcTemp[0] < 10)
{
socarray = 0; //电池温度小于10℃,选用表0
}
else if(tagBMS.iNtcTemp[0] < 25)
{
socarray = 1; //电池温度10-25℃,选用表1
}
else
{
socarray = 2; //电池温度大于25℃,选用表2
}
电压查表得SOC
while(ocv > OCV_SOC[socarray][i])
{
i++;
if( i >= 38 ){ break; }
}
switch(i)
{
case 36: tempsoc = 50; break; //平台区默认SOC=50%
case 37: tempsoc = 99; break;
case 38: tempsoc = 100; break;
default : tempsoc = i; break;
}
return tempsoc;
}
/************************************函数功能描述************************************************
Function Name : CurrentIntegral(void)
Description : 电流积分,10mS处理一次
Input : 无
Output : 无
*************************************************************************************************/
void CurrentIntegral(void)
{
tagLife.fAddCapacity += tagBMS.ulBusCurrent;//单位mA
}
/************************************函数功能描述************************************************
Function Name : PackSOCCalculate
Description : SOC计算,1S执行一次
Input : 无
Output : 无
*************************************************************************************************/
void PackSOCCalculate(void)
{
FP32 ChgAf = 0.93; //充电电流补偿因子(调整充电时电量上升斜率,使其尽量接近充满时的斜率)
FP32 DchgAf = 0.93; //放电电流补偿因子(调整放电时电量下降斜率,使其尽量接近放空时的斜率)
FP32 surtim; //当前到满充或满放的时间,即:变化斜率
static INT dtv; //单位时间(这里为1S)电压变化值
static UCHAR cof,dcof; //满充、满放标志
//放电时计算SOC
if(( tagSBS.bCell_DSGING_Status == ISYES )&&( tagSBS.bCell_CHGING_Status == ISNO )&&( dcof == ISNO ))//检测为放电状态,且不为满放
{
cof = ISNO; //清满充标志
if(tagBMS.uiMinVoltage > 2900)//单节最低电压大于2900mV时,进行正常SOC积分
{
tagLife.fAddCapacity = tagLife.fAddCapacity/360000; //加100次后求平均值,单位mA.S=>mA.H
if( tagLife.uiSOC_Percent <= 1 ){ tagLife.fAddCapacity = 0; } //防止最低节电压还未低于2900mV,电量就达到了0%
tagLife.iDchgLastVoltag = tagBMS.uiMinVoltage;
}
else//单节最低电压小于2900mV时,调整SOC积分斜率(目的是放空时正好SOC=0%)
{
dtv = tagLife.iDchgLastVoltag - tagBMS.uiMinVoltage; //求1S后的电压变化值
tagLife.iDchgLastVoltag = tagBMS.uiMinVoltage;
if( dtv > 0 )
{
surtim = (tagBMS.uiMinVoltage - SET_UV)/dtv; //求按照当前放电电流到放空剩余时间(S)
tagLife.fAddCapacity = tagLife.fSOC/surtim; //求每次减少的容量 = 当前容量/到放空的时间
}
else
{
tagLife.fAddCapacity = 0; //电压未变化,容量增量为0
//tagLife.fAddCapacity = tagLife.fAddCapacity/360000; //加100次后求平均值,单位mA.S=>mA.H
}
}
tagLife.fSOC = tagLife.fSOC - tagLife.fAddCapacity*DchgAf; //单位mA.H
if( tagLife.fSOC <= 0 )
{
tagLife.fSOC = 0; //限制tagLife.ulSOC不小于0
}
tagLife.fTotalDisCapacity += tagLife.fAddCapacity; //累加放电容量(用于计算循环次数)
if( tagLife.fTotalDisCapacity >= tagLife.fRealTotalCapacity )
{
tagLife.fTotalDisCapacity = 0;
tagLife.uiDisTimes++; //如果放电累加容量达到额定容量,循环次数加一
}
}
/ //充电时计算SOC
else if((tagSBS.bCell_CHGING_Status == ISYES)&&( tagSBS.bCell_DSGING_Status == ISNO) &&( cof == ISNO ))//检测为充电状态,且不为满充
{
dcof = ISNO; //清满放标志
if(tagBMS.uiMaxVoltage < 3600)//单节最高电压小于3600mV时,进行正常SOC积分
{
tagLife.fAddCapacity = tagLife.fAddCapacity/360000; //加100次后求平均值,单位mA.S=>mA.H
if( tagLife.uiSOC_Percent >= 99 ){ tagLife.fAddCapacity = 0; } //防止最高节电压还未超过3450mV,电量就达到了100%
tagLife.iChgLastVoltag = tagBMS.uiMaxVoltage;
}
else//单节最高电压大于3600mV时,调整SOC积分斜率(目的是充到保护电压时正好SOC=100%)
{
dtv = tagBMS.uiMaxVoltage - tagLife.iChgLastVoltag; //求每个时间周期后的电压变化值(1S)
tagLife.iChgLastVoltag = tagBMS.uiMaxVoltage;
if( dtv > 0 )
{
surtim = (SET_OV - tagBMS.uiMaxVoltage)/dtv; //求按照当前充电电流到充满剩余时间(S)
tagLife.fAddCapacity = (tagLife.fRealTotalCapacity - tagLife.fSOC)/surtim; //求每次增加的容量=(总容量-当前容量)/到充满的时间
}
else
{
tagLife.fAddCapacity = 0; //电压未变化,容量增量为0
//tagLife.fAddCapacity = tagLife.fAddCapacity/360000; //加100次后求平均值,单位mA.S=>mA.H
}
}
tagLife.fSOC = tagLife.fSOC + tagLife.fAddCapacity*ChgAf; //单位mA.H
if(tagLife.fSOC > tagLife.fRealTotalCapacity ) //充电时增加的总电量不能超过总容量
{
tagLife.fSOC = tagLife.fRealTotalCapacity;
}
}
tagLife.uiSOC_Percent =(tagLife.fSOC*100)/tagLife.fRealTotalCapacity; //计算SOC值
if( tagLife.uiSOC_Percent >= 100 ) //满充
{
tagLife.uiSOC_Percent = 100; //soc不能大于100%
cof = ISYES; //置满充标志
}
else if( tagLife.uiSOC_Percent <= 1 ) //满放
{
tagLife.uiSOC_Percent = 1; //soc不能小于1%
dcof = ISYES; //置满放标志
}
tagLife.fAddCapacity = 0; //将1S钟累加的容量清0,重新累加
tagLife.ulWorkTime++; //计算工作时间(S)
//满放—满充对tagLife.RealQ修正
if( tagSBS.bCell_UVS_Status == ISYES ) //AFE产生欠压保护(放电过程中)
{
tagLife.bCellEmptyFlag = ISYES; //电池放空标志
tagLife.fSOC = 0; //修正当前容量0mAH
}
if(( tagSBS.bCell_OVS_Status == ISYES )&&( tagBMS.ulBattV >= 5440 )) //AFE产生过压保护(充电过程中),且总电压大于54.4v
{
if( tagLife.bCellEmptyFlag == ISYES ) //如果电池放空过(说明电池进行了一次满放满充)
{
tagLife.bCellEmptyFlag = ISNO; //清除放空标志
tagLife.fRealTotalCapacity = tagLife.fSOC; //电池充满后变化值为电池实际额定容量
tagLife.uiSOH_Percent = (tagLife.fRealTotalCapacity*100)/tagLife.fNominalCapacity; //求电池SOH
}
}
}