当前位置: 首页 > news >正文

BMS电池管理系统学习笔记_SOC算法

一、SOC算法介绍

什么是SOC?SOC其实就是电池的电量。求SOC值就是求电池现在还剩百分之几的电量。例如 100% 表示满电,0% 表示电量耗尽。

SOC最常用的算法:开路电压法(OCV查表法)+安时积分法

这种算法的核心逻辑是 “用安时积分法保证动态实时性,用 OCV 查表法解决安时积分的累计误差”,通过两者互补实现 “低成本、高可靠性” 的 SOC 估算

特别提醒:我们所说的获取电池的SOC,一般指的是获取单节电池的SOC如果要求电池组的SOC,电池组的SOC只能由 “电量最少的那节电池” 决定,这是电池组计算的核心原则。(比如 5 节里有 1 节只剩 10%,组就只能按 10% 算,否则这节会过放损坏)

OCV查表法与安时积分法关系

当电池不充电、不放电(静置)时,电压很稳定,用通过查表法算 SOC,这就是 OCV 法。

当电池在充电 / 放电时,电压会波动(比如充电时电压突然升高),OCV 法不准,这时候用 “电量守恒” 算:剩余电量 = 之前剩余电量 + 充进去的电(放出去的电)

因此,静置状态用OCV查表法,非静置状态(充放电状态)用安时积分法

1.1 OCV查表法

// 1. OCV-SOC对应表(SOC:0%~100%,电压单位:mV)
static const uint16_t SocOcvTab[101] =
{3282, // 0%3309, 3334, 3357, 3378, 3398, 3417, 3434, 3449, 3464, 3477, // 1%~10%3489, 3500, 3510, 3520, 3528, 3536, 3543, 3549, 3555, 3561, // 11%~20%3566, 3571, 3575, 3579, 3583, 3586, 3590, 3593, 3596, 3599, // 21%~30%3602, 3605, 3608, 3611, 3615, 3618, 3621, 3624, 3628, 3632, // 31%~40%3636, 3640, 3644, 3648, 3653, 3658, 3663, 3668, 3674, 3679, // 41%~50%3685, 3691, 3698, 3704, 3711, 3718, 3725, 3733, 3741, 3748, // 51%~60%3756, 3765, 3773, 3782, 3791, 3800, 3809, 3818, 3827, 3837, // 61%~70%3847, 3857, 3867, 3877, 3887, 3897, 3908, 3919, 3929, 3940, // 71%~80%3951, 3962, 3973, 3985, 3996, 4008, 4019, 4031, 4043, 4055, // 81%~90%4067, 4080, 4092, 4105, 4118, 4131, 4145, 4158, 4172, 4185  // 91%~100%
};

通过查电压 - 电量表,中间值用 “比例分摊”;说白了就是用一个大数组,通过查电池的电压,得到电量SOC值。

这里面会用到查找算法(折半查找)和线性插值算法(在两个值之间用一次函数求电量)

1.2 安时积分法

该方法仅添加了温度补偿,商用级别的BMS肯定会考虑更多因素

SOC值

电量(Ah)= 电流(A) × 时间(h);

1Ah = 1A 电流充 / 放 1 小时的电量(比如 1A 充 2 小时,就是 2Ah 电量)

理想情况下:单节电池SOC = 剩余电量/额定电量(2.2Ah)x100%。

由于精度误差,实际情况下:

单节电池SOC = 剩余电量/实际电量x100%

实际容量

实际电量 = 额定电量(2.2Ah) × 电池容量系数(该例程只涉及温度补偿)(rate_temp);

剩余容量

剩余电量(Ah) = 之前剩余电量(Ah)+充进去的电量(+Ah) 或者 放出去的电量(-Ah)。

举例:

电池实际容量 = 2Ah,现在剩余电量 = 1.5Ah,放电:电流 2A,时间 0.1 小时

剩余电量 = 1.5Ah - (2A x 0.1h) = 1.3Ah

温度补偿

 线性温度补偿模型:实际容量 = 额定容量 × [1 + (当前温度-基准温度)×温度系数]
    real_cap = rated_cap * (1 + (temp - TEMP_BASE) * TEMP_COEFFICIENT);

二、SOC例程

例程中有非常详细的注释,直接看代码看注释就可以理解。

前提说明:

float V[5];//5节各电池电压
float V_Sort[5];//5节各电池电压排序,数据由大到小
float V_Small;
float V_Big;
float All_V;//电池组电压
float A;//电流
float T;//温度

我的程序已经定义了变量,并且已经声明为了全局变量

定义结构体变量和工具函数

// 宏定义:核心参数配置(集中管理关键参数,便于后期调整)
#define RATED_CAPACITY        4.3f    // 单节电池额定容量(Ah),电池出厂时的标准容量
#define SAMPLE_INTERVAL       1       // 采样间隔(s),即SOC计算函数的调用周期
#define TEMP_BASE             25.0f   // 基准温度(℃),电池额定容量对应的标准温度
#define TEMP_COEFFICIENT      0.001f  // 温度系数(每℃容量变化率),温度每偏离基准1℃,容量的变化比例// 1. OCV-SOC对应表(SOC:0%~100%,电压单位:mV)
static const uint16_t SocOcvTab[101] =
{3282, // 0%3309, 3334, 3357, 3378, 3398, 3417, 3434, 3449, 3464, 3477, // 1%~10%3489, 3500, 3510, 3520, 3528, 3536, 3543, 3549, 3555, 3561, // 11%~20%3566, 3571, 3575, 3579, 3583, 3586, 3590, 3593, 3596, 3599, // 21%~30%3602, 3605, 3608, 3611, 3615, 3618, 3621, 3624, 3628, 3632, // 31%~40%3636, 3640, 3644, 3648, 3653, 3658, 3663, 3668, 3674, 3679, // 41%~50%3685, 3691, 3698, 3704, 3711, 3718, 3725, 3733, 3741, 3748, // 51%~60%3756, 3765, 3773, 3782, 3791, 3800, 3809, 3818, 3827, 3837, // 61%~70%3847, 3857, 3867, 3877, 3887, 3897, 3908, 3919, 3929, 3940, // 71%~80%3951, 3962, 3973, 3985, 3996, 4008, 4019, 4031, 4043, 4055, // 81%~90%4067, 4080, 4092, 4105, 4118, 4131, 4145, 4158, 4172, 4185  // 91%~100%
};//单节电池SOC信息结构体(存储单节电池的关键参数和状态)
typedef struct
{float rated_cap;    // 额定容量(Ah),固定为RATED_CAPACITY,不随温度变化float real_cap;     // 温度修正后实际容量(Ah),=额定容量×温度补偿系数,随温度变化float remain_cap;   // 剩余容量(Ah),当前可使用的容量float soc;          // 荷电状态(0.0~1.0),=剩余容量/实际容量,反映电池当前电量比例float voltage;      // 单节电压(V),记录当前采样周期的电压值
} CellSOC_Info;// 全局变量:存储所有单节电池的SOC信息
static CellSOC_Info cell_info[5] = {0};
//工具函数
//二分法(折半查找)查找右边界索引(用于OCV查表)
//查找数组,数组低索引,数组高索引,目标内容
uint16_t right_bound(const uint16_t* arr, uint16_t low, uint16_t high, uint16_t target)
{while (low <= high){uint16_t mid = low + (high - low) / 2;if (arr[mid] > target){high = mid - 1;}else{low = mid + 1;}}return high; // 返回≤target的最大索引
}

温度补偿模块

/*** @brief  温度补偿计算:根据当前温度修正电池实际容量* @param  temp:当前电池组温度(℃)* @param  rated_cap:电池额定容量(Ah)* @return 温度修正后的实际容量(Ah)* @note   电池容量受温度影响:低温容量下降,高温容量略有上升,此处用线性模型近似*/
static float temp_compensate(float temp, float rated_cap)
{float real_cap;// 线性温度补偿模型:实际容量 = 额定容量 × [1 + (当前温度-基准温度)×温度系数]real_cap = rated_cap * (1 + (temp - TEMP_BASE) * TEMP_COEFFICIENT);// 限制容量范围(避免极端温度下计算值不合理,如超低温时容量不会无限下降)if (real_cap < rated_cap * 0.8f){real_cap = rated_cap * 0.8f;  // 最低为额定容量的80%}else if (real_cap > rated_cap * 1.2f){real_cap = rated_cap * 1.2f;  // 最高为额定容量的120%}return real_cap;
}

SOC初始化

/*** @brief  初始化单节电池参数(仅首次运行时执行)* @param  cell_idx:电池索引(0~4)* @note   初始化额定容量和初始剩余容量(默认为50%)*/
static void single_cell_Init(uint8_t cell_idx)
{// 仅当额定容量未初始化(为0)时执行初始化if (cell_info[cell_idx].rated_cap == 0.0f){cell_info[cell_idx].rated_cap = RATED_CAPACITY;  // 赋值额定容量4.3Ahcell_info[cell_idx].remain_cap = RATED_CAPACITY * 0.5f;// 初始剩余容量默认为50%}
}

开路电压法计算SOC值

开路电压法中,SOC值对应的就是数组的索引

/*** @brief  开路电压法计算单节SOC(静止模式下使用)* @param  cell_idx:电池索引(0~4)* @note   原理:静止时电池电压(OCV)与SOC有固定对应关系,通过电压查OCV表获取SOC*/
static void calc_soc_by_ocv(uint8_t cell_idx)
{uint16_t voltage_mV;  // 电压转换为mV(OCV表单位为mV)uint16_t ocv_index;   // 查找到的OCV表索引(对应SOC百分比)// 将电压从V转换为mV,并四舍五入(如3.282V → 3282mV)voltage_mV = (uint16_t)(cell_info[cell_idx].voltage * 1000.0f + 0.5f);// 调用二分法查找OCV表,获取对应SOC的索引ocv_index = right_bound(SocOcvTab, 0, 100, voltage_mV);// 索引边界保护(防止电压超出OCV表范围时索引异常)if (ocv_index > 100)ocv_index = 100;// 更新SOC(索引转换为0.0~1.0的比例)cell_info[cell_idx].soc = (float)ocv_index / 100.0f;// 更新剩余容量(SOC × 实际容量)cell_info[cell_idx].remain_cap = cell_info[cell_idx].soc * cell_info[cell_idx].real_cap;
}

安时积分法计算SOC值

/*** @brief  安时积分法计算单节SOC(非静止模式下使用)* @param  cell_idx:电池索引(0~4)* @note   原理:通过积分充放电电流计算容量变化,累计得到剩余容量*         公式:容量变化 = 电流(A) × 时间(s) / 3600(s/h) → 单位转换为Ah*         电流方向:充电时为正(容量增加),放电时为负(容量减少)*/
static void calc_soc_by_coulomb(uint8_t cell_idx)
{float cap_change;  // 本采样周期的容量变化量(Ah)// 计算容量变化:电流×采样时间(秒)/3600(转换为小时)// 充电时A为正 → cap_change为正 → 剩余容量增加(remain_cap += 正数)// 放电时A为负 → cap_change为负 → 剩余容量减少(remain_cap += 负数 → 等价于减)cap_change = A * SAMPLE_INTERVAL / 3600.0f;// 更新剩余容量(加上容量变化量)cell_info[cell_idx].remain_cap += cap_change;// 剩余容量边界保护(避免超出0~实际容量范围)if (cell_info[cell_idx].remain_cap < 0.0f){cell_info[cell_idx].remain_cap = 0.0f;  // 最低为0(过度放电保护逻辑)}else if (cell_info[cell_idx].remain_cap > cell_info[cell_idx].real_cap){cell_info[cell_idx].remain_cap = cell_info[cell_idx].real_cap;  // 最高为实际容量(过度充电保护逻辑)}// 计算当前SOC(剩余容量 / 实际容量)cell_info[cell_idx].soc = cell_info[cell_idx].remain_cap / cell_info[cell_idx].real_cap;
}

单节电池SOC值总计算(OCV查表法+安时积分法融合)

/*** @brief  单节电池SOC总计算(协调初始化、参数更新和方法选择)* @param  cell_idx:电池索引(0~4)* @note   按步骤完成单节SOC计算:初始化→更新电压→温度补偿→选择计算方法*/
static void update_single_cell_soc(uint8_t cell_idx)
{// 1. 初始化单节参数(首次运行时执行)single_cell_Init(cell_idx);// 2. 获取当前采样周期的单节电压cell_info[cell_idx].voltage = V[cell_idx];// 3. 计算温度修正后的实际容量cell_info[cell_idx].real_cap = temp_compensate(T, cell_info[cell_idx].rated_cap);  // 4. 根据当前能量模式选择SOC计算方法if (Energy_Mode == Energy_Static_Mode){calc_soc_by_ocv(cell_idx);  // 静止模式→开路电压法}else if(Energy_Mode == Energy_Charge_Mode || Energy_Mode == Energy_Discharge_Mode){calc_soc_by_coulomb(cell_idx);  // 非静止模式→安时积分法}
}

我在自己的工程里定义了枚举,当电流小于0.02A(大于-0.02A)时,电池属于静止状态Energy_Mode == Energy_Static_Mode;反之处于非静止模式(充放电模式)。

电池组SOC计算

/*** @brief  计算电池组SOC* @return 电池组SOC(0.0~1.0)* @note   串联电池组的SOC由电量最低的一节决定(短板效应),取所有单节SOC的最小值*/
static float get_pack_soc(void)
{float min_soc = 1.0f;  // 初始化最小值为最大可能值(100%)uint8_t i;// 遍历所有单节电池,找到最小的SOCfor (i = 0; i < 5; i++){if (cell_info[i].soc < min_soc){min_soc = cell_info[i].soc;}}return min_soc;  // 最小SOC即为电池组SOC
}

总入口函数

/*** @brief  SOC总更新函数(外部调用入口)* @return 电池组当前SOC(0.0~1.0,即0%~100%)* @note   调用流程:更新所有单节SOC→计算电池组SOC→返回结果*         建议按SAMPLE_INTERVAL(如1秒)的周期调用*/
float soc_total_update(void)//1s调用一次
{uint8_t i;// 1. 依次更新所有单节电池的SOCfor (i = 0; i < 5; i++){update_single_cell_soc(i);}// 2. 计算并返回电池组SOC(短板效应)return get_pack_soc();
}

使用方法

MyPrintf(USART1,"SOC:%.1f%%\r\n",soc_total_update()*100);
VTaskDelay(1000);

每1秒调用一次函数

 

http://www.dtcms.com/a/504266.html

相关文章:

  • 浅谈信创数据库改造重难点
  • 建设银行唐山分行网站上海专业网站建设服务
  • 算法沉淀第七天(AtCoder Beginner Contest 428 和 小训练赛)
  • 温州做网站定制车载互联系统网站建设
  • 迅当网络深圳外贸网站建设竞价网络推广
  • 【GESP】C++四级真题 luogu-B4006 [GESP202406 四级] 宝箱
  • 公司做网站找谁公司做网站找谁网站信息评估抽查
  • wordpress网站添加密码访问营销微网站建设公司
  • 吴恩达新课程:Agentic AI(笔记2)
  • 用dw怎么做网站留言板重庆重庆网站建设
  • 家具网站开发目的wordpress页脚太高
  • 西南交通建设集团股份有限公司网站带分期功能的网站建设
  • 姚期智京都奖( 2021)演讲:做研究最好的方法是提出深刻、大胆和关键性的问题
  • 商务网站创建经费预算wordpress打开文章响应慢
  • arthas简介
  • 企业网站源码交易国产wordpress主题
  • 做网站要学什么c语言西安网站建设求职简历
  • 用阿里云服务器做自己购物网站wordpress多人聊天室
  • sm2025 模拟赛16 (2025.10.11)
  • 国内网站建设流程淘宝店标logo在线制作免费
  • 长沙市做网站公司排名广州做网站好的公司
  • SQL Server数据查询语句
  • 给领导发网站建设可行性方案邮件怎么写wordpress 获取用户邮箱
  • SQL NULL 函数详解
  • Linux进程信号(壹)_产生信号
  • 关于茶文化网站建设的背景做全屏的网站 一屛多高
  • GIS与农业 考公考编面试 几个参考题
  • 百度网站的安全建设方案在wordpress中rss订阅的步骤是什么?
  • win2008sr怎么用iis做网站东乡族网站建设
  • 织梦新闻门户网站模板wordpress 4.7.0 漏洞