一. 电池均衡软件算法介绍
1 、被动均衡方法一
以单体端电压为监督目标,当单体压差进入一定范围,均衡开启,开始发挥作用。设定均衡控制的触发阈值,比如极值与平均值的差值达到50mV起动均衡过程,5mV结束均衡。管理系统按照固定的采集周期采集每一串单体端电压,先计算平均值,再计算每只电芯电压与电压均值的差值,电芯编号按照差值大小排队。差值与设定阈值比较,若最大的差值在阈值范围内,触发均衡程序。后续策略与具体均衡实现形式有关。
2、 被动均衡方法二
常温状态下(20℃以上正常使用温度区间)当系统未进行充放电,电芯在电流 < 0.5A以下并保持2H(低温5H)以上时认为电池去极化效应完成,此时采集的电芯电压近似认为为开路电压(OCV),通过查SOC-OCV表得出当前电芯SOC,并计算出电量差异 ,例如5%(根据需求进行调整)时,则需要控制开启电芯电量高的均衡电路,通过放电调平电池间容量差异减小电芯间容量一致性差异。发生电芯欠压、电芯高低温故障、均衡故障、均衡电阻过温、均衡时间到达时,则关闭均衡控制。
3. 均衡算法规划
均衡规划:主被动均衡配合使用;电芯压差、SOC差异两种算法配合使用;
4. 均衡测试方法
《GBT34131-2022电力储能用电池管理系统》 中要求:
原文链接:BMS——电池均衡算法_吴二鸣的博客-CSDN博客
二、软件实现方式
1.被动均衡【包内均衡】
均衡启动条件 | 1、处于充电状态 2、无单体电压采集故障 3、当前设备最低单体电压大于均衡开启电压(3.0V) 4、当前设备压差大于均衡开启压差(30mV) 满足以上所有条件则开启均衡 备注:3、4开启阈值参考配置参数。 |
均衡关闭条件 | 不满足均衡启动条件中的任意一个,则关闭均衡 |
均衡电流大小 | TBD |
备注 | 最多同时均衡4路电芯,且不支持相邻两节同时开启均衡 |
typedef struct
{
U16 MaxVolt; //最大电压
U8 Max_Volt_Cell; //最大电压单体数
U16 MinVolt; //最小电压
U8 Min_Volt_Cell; //最小电压单体数
U8 MaxTemp; //最高温度
U8 MaxTemp_CellNmu; //最高温度单体数
U8 MinTemp; //最低温度
U8 MinTemp_CellNmu; //最低温度单体数
U16 AvgVolt; //平均电压
U32 SumVolt; //总电压
}_Bms_CalculateData;
程序设计思路:
- 读取均衡通道,查看通道的开启状态
- 读取均衡使能状态,使能信息由BMS发出
- 停止均衡,先停止全部均衡,开始新的均衡
- 计算均衡通道
void Balance_Passive_Task(void)
{
S32 s32BalEnable = 0;
if(u8OtpTestFg == TRUE)
{
return;
}
xSemaphoreTake(g_BQ76952_Comm_MutexSemaphore, portMAX_DELAY);
//读取均衡通道,查看通道的开启状态
Balance_Passive_Channel_Read();
//读取均衡使能状态,使能信息由BMS发出
(void)GetSigVal(MBMS_SAM_SIG_ID_BALANCE_ENABLE, &s32BalEnable);
//停止均衡,先停止全部均衡,开始新的均衡
Balance_Passive_Stop();
//不为充电状态则不进行均衡判断
if(TRUE != s32BalEnable
|| EVENT_HAPPEN == GetMsgVal(ALARM_ID_BMS_COMM_ERROR))
{
xSemaphoreGive(g_BQ76952_Comm_MutexSemaphore);
return;
}
//计算均衡通道
Balance_Passive_Cal_Cmd();
//开启均衡
Balance_Passive_Start();
xSemaphoreGive(g_BQ76952_Comm_MutexSemaphore);
}
采样获取Bms_CalculateData的各种数据
单体电压冒泡排序
单体电压与系统最小电压的压差大于均衡开启电压压差
计算该数组位 g_u32BalanceCmd 记录开始均衡的单体位置
不开启相邻节均衡
加一道防护,防止相邻节开启
根据 g_u32BalanceCmd 开启均衡
void Balance_Passive_Cal_Cmd(void)
{
S32 s32BalStaDV = 0;
U8 CellCnt = 0;
U16 celldiffvolt = 0;
U8 BalCellCnt = 0;
U8 GetSysBoardCellNum = 0;
CELL_BALANCE_DATA BALANCE_DATA_cell;
_Bms_CalculateData GetBmsData;
U8 u8CellNo = 0;
U8 u8Offset = 0;
U8 u8LastOffset = 0;
U8 u8AfeCnt = 0;
BOOL blBalanceSts = FALSE;
//单体个数
GetSysBoardCellNum = g_SysSampleData.SysSampleCellNum;
//读取BMS统计数据、电压、温度
GetBmsData = SampleGetCalData();
//读取均衡启动电压差
(void)GetSigVal(MBMS_SET_SIG_ID_BALANCE_START_DIFF_VOLT, &s32BalStaDV);
//当前设备最低单体电压大于均衡开启电压,比开启电压小也不开均衡
if(GetBmsData.MinVolt < GetMsgVal(MBMS_SET_SIG_ID_BALANCE_START_VOLT))
{
return;
}
//对电压进行冒泡排序 --升序
BALANCE_DATA_cell = bubble_sort();
//依次判断每个引脚采集的电压值
for(CellCnt = (GetSysBoardCellNum - 1); CellCnt > 0; CellCnt--)
{
//最多均衡路数--4路
if(BalCellCnt < SYS_BALANCE_MAX_CELL_NUM)
{
//求与系统最小电压的电压差--从排序开始算电压差
celldiffvolt = BALANCE_DATA_cell.CellVolt[CellCnt] - GetBmsData.MinVolt;
//均衡开启条件:单体电压与系统最小电压的压差大于均衡开启电压压差
if(celldiffvolt >= s32BalStaDV)//满足开启条件
{
u8CellNo = BALANCE_DATA_cell.Cellnum[CellCnt]-1; //从0开始计数
u8Offset = g_SysSampleData.u8PinNo[u8CellNo]; //记录电芯在全部采集引脚中的编号,从0开始计(用于配置均衡控制时使用)
if((u8LastOffset == (u8Offset + 1)) || (u8LastOffset == (u8Offset - 1))) //如果是相邻两个则开始下一次FOR循环
{
continue;
}
//g_u32BalanceCmd[0] == 0 初始化为0
//u8Offset 会从0递增到16 经过上面if语句处理后不会出现相邻为1的情况
g_u32BalanceCmd[g_SysSampleData.DevID[u8CellNo]] |= 1UL << u8Offset;//电压都是在一片AFE芯片上采集的
u8LastOffset = u8Offset;
BalCellCnt++;
}
}
else
{
break;
}
}
//防止相邻节开启均衡
g_u32BalanceCmd[u8AfeCnt]位10101010
1 << 0,结果为 00000001。
~(1UL << CellCnt) 的结果为 11111110。
g_u32BalanceCmd[u8AfeCnt] 与 11111110 进行按位与操作,结果为 10101010 & 11111110 = 10101010。
此操作将 g_u32BalanceCmd[u8AfeCnt] 的第0位(最低位)设置为0,其余位保持不变。
for(u8AfeCnt = 0; u8AfeCnt < g_SysSampleData.u8BQ76952Num; u8AfeCnt++)
{
for(CellCnt = 0; CellCnt <= SIGNAL_AFE_MAX_CELL_NUM; CellCnt++)
{
if(blBalanceSts == TRUE)
{
g_u32BalanceCmd[u8AfeCnt] &= ~(1UL << CellCnt);
}
blBalanceSts = (g_u32BalanceCmd[u8AfeCnt] >> CellCnt) & TRUE;
}
}
}
2.主动均衡
在均衡时可采用短时大电流,从而实现快速均衡;由此,主动均衡方式对电池的一致性要求相对不高,在性能上完胜被动均衡方式,即使其结构相对复杂、成本相对较高,仍可能成为未来BMS电量均衡的主流方式
【后续再做补充】