SOC估算方法之(OCV-SOC+安时积分法)
一、引言
此方法主要参考电动汽车用磷酸铁锂电池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对应SOC2787, //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对应SOC2837, //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获得SOCtagLife.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重新获得SOCtagLife.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.Hif( tagLife.fSOC <= 0 ){tagLife.fSOC = 0; //限制tagLife.ulSOC不小于0 } tagLife.fTotalDisCapacity += tagLife.fAddCapacity; //累加放电容量(用于计算循环次数) if( tagLife.fTotalDisCapacity >= tagLife.fRealTotalCapacity ){tagLife.fTotalDisCapacity = 0; tagLife.uiDisTimes++; //如果放电累加容量达到额定容量,循环次数加一 } }/ //充电时计算SOCelse 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.Hif(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} }
}相关文章:
SOC估算方法之(OCV-SOC+安时积分法)
一、引言 此方法主要参考电动汽车用磷酸铁锂电池SOC估算方法这篇论文 总结: 开路电压的测量需要将电池静止相当长的一段时间才能达到平衡状态进行测量。 安时积分法存在初始SOC的估算和累积的误差。 所以上述两种方法都存在一定的缺陷,因此下面主要讲…...
指针(下)
文章目录 指针(下)野指针、空指针野指针空指针 二级指针**main**函数的原型说明 常量指针与指针常量常量指针指针常量常量指针常量 动态内存分配常用函数**malloc****calloc****realloc****free** **void**与**void***的区别扩展:形式参数和实际参数的对应关系 指针…...
C# 浅谈IEnumerable
一、IEnumerable 简介 IEnumerable 是一个接口,它定义了对集合进行迭代所需的方法。IEnumerable 接口主要用于允许开发者使用foreach循环来遍历集合中的元素。这个接口定义了一个名为 GetEnumerator 的方法,该方法返回一个实现了 IEnumerator 接口的对象…...
mmdebstrap:创建 Debian 系统 chroot 环境的利器 ️
文章目录 mmdebstrap 的一般性参数说明 📜mmdebstrap 的常见用法示例 🌈使用 mmdebstrap 的注意事项 ⚠️ 🌈你好呀!我是 山顶风景独好 🎈欢迎踏入我的博客世界,能与您在此邂逅,真是缘分使然&am…...
【Linux SQLite数据库】一、SQLite交叉编译与移植
SQLite 是一个用 C 语言编写的开源、轻量级、快速、独立且高可靠性的 SQL 数据库引擎,它提供了功能齐全的数据库解决方案。SQLite 几乎可以在所有的手机和计算机上运行,它被嵌入到无数人每天都在使用的众多应用程序中。此外,SQLite 还具有稳定…...
每天写两道(数组篇)移除元素、
27.移除元素 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作&#…...
Unity 使用 NewtonSoft Json插件报错
JsonReaderException: Unexpected character encountered while parsing value: . Path , line 0, position 0. 通过断点发现,头有一串ZWNBSP,这个是BOM格式的JSON。在文件下看不到。 解决方法:改编码格式,Remove BOM....
k8s 部署 Mysqld_exporter 以及添加告警规则
最近监控 mysql 数据库,用了 pmm-server、pmm-client 发现监控是真的不太好用,还是用回 prometheus 吧。 部署mysqld_exporter k8s 部署最新版本的 mysqld_exporter,支持的数据库版本 MySQL >5.6、MariaDB > 10.3。 先在数据库创建用…...
基于STM32开发的智能农业环境监测系统
目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码控制代码应用场景 农田环境监测温室环境控制常见问题及解决方案 常见问题解决方案结论 1. 引言 智能农业环境监测系统通过集成多种环境传感器,实时监测土壤湿度、温度…...
【SQL】平均售价
目录 题目 分析 代码 题目 表:Prices ------------------------ | Column Name | Type | ------------------------ | product_id | int | | start_date | date | | end_date | date | | price | int | ---------------…...
存储器与CPU的连接
1.单块存储芯片与CPU的连接 单独的一块独立的存储芯片提供的线有:地址总线,数据总线,读写控制线,片选线,如果该存储器只有八根数据总线用于输出数据,而cpu一次可以读64位的数据呢? 我们可以将八…...
unity--webgl 访问本地index.html
目录 1:使用本地服务器 1.1 使用 Python 的 SimpleHTTPServer 1.2 使用 Node.js 的 http-server 2:让其他人通过 IP 地址来访问你的 Unity WebGL 项目 2.1: 确保服务器可访问 2.2 获取公共 IP 地址 2.3 配置本地服务器 1.使用 Python 的 SimpleHTTPServer 2…...
慢慢欣赏DPDK RTE_MAX_ETHPORTS的定义
DPDK代码里面,RTE_MAX_ETHPORTS是一个常见的宏定义,但是在.c和.h文件找不到其定义,在全文件搜索条件下,在config/meson.build找到这么一个定义 dpdk_conf.set(RTE_MAX_ETHPORTS, get_option(max_ethports)) 该宏定义是根据构建输…...
Java Nacos与Gateway的使用
Java系列文章目录 IDEA使用指南 Java泛型总结(快速上手详解) Java Lambda表达式总结(快速上手详解) Java Optional容器总结(快速上手图解) Java 自定义注解笔记总结(油管) Jav…...
前端项目中的Server-sent Events(SSE)项目实践及其与websocket的区别
前端项目中的Server-sent Events(SSE)项目实践 前言 在前端开发中,实时数据更新是提升用户体验的重要因素之一。Server-SentEvents(SSE)是一种高效的技术,允许服务器通过单向连接将实时数据推送到客户端。下面将从SSE的基本改变,使用场景展…...
《老俞闲话|唯爱和热情不可辜负》读后感
《老俞闲话|唯爱和热情不可辜负》读后感 俞敏洪先生的这篇讲话充满了深情与智慧,他以自己丰富的人生经历和教育实践,向我们展现了一位教育家对于教育事业的热爱和对教师角色的深刻理解。 情感真挚,触动人心 俞敏洪先生的讲话中流…...
C语言 ——— 在杨氏矩阵中查找具体的某个数
目录 何为杨氏矩阵 题目要求 代码实现 何为杨氏矩阵 可以把杨氏矩阵理解为一个二维数组,这个二维数组中的每一行从左到右是递增的,每一列从上到下是递增的 题目要求 在杨氏矩阵中查找具体的某个数 要求:时间复杂度小于O(N) 代码实现…...
DAI-Net: 基于对偶自适应交互网络的药物推荐算法
引言 DAI-Net: Dual Adaptive Interaction Network for Coordinated Medication Recommendation 论文链接:https://ieeexplore.ieee.org/document/10614809 代码链接:GitHub - obananas/DAI-Net 在现代医疗保健中,如何利用电子健康记录&a…...
haproxy高级功能及配置
章节 一、haproxy 基础用法 二、haproxy 高级用法 三、haproxy之ACL的使用 目录 1 基于cookie的会话保持 1.1 cookie命名,并赋予其值 1.2 验证cookie信息 1.2.1 Windows浏览器验证 1.2.2 Linux下虚拟机验证 2 IP透传 2.1 四层与七层透传的区别 2.2 七层IP透传 2.2…...
【前端】NodeJS:记账本案例优化(MongoDB数据库)
文章目录 1 字符串转为时间对象——Moment2 记账本实例优化 1 字符串转为时间对象——Moment Moment.js中文网:https://momentjs.cn/docs/#/parsing/。 npm install moment // 安装moment var moment require(moment); // require moment().format(); 2 记账本实…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
React、Git、计网、发展趋势等内容——前端面试宝典(字节、小红书和美团)
React React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么,Fiber架构,面试向面试官介绍,详细解释 用户: React Hook实现架构、.Hook不能在循环嵌套语句中使用 , 为什么,Fiber架构,面试向面试官介绍&#x…...
Redis:常用数据结构 单线程模型
🌈 个人主页:Zfox_ 🔥 系列专栏:Redis 🔥 常用数据结构 🐳 Redis 当中常用的数据结构如下所示: Redis 在底层实现上述数据结构的过程中,会在源码的角度上对于上述的内容进行特定的…...
二叉树-226.翻转链表-力扣(LeetCode)
一、题目解析 翻转可以理解为树的左右子树交换,从根到叶子节点,但是这里交换的是链接的指针,而不是单纯的交换值,当出现nullptr时,也是可以交换链接的,交换值的话就不行了。 二、算法原理 依旧的递归&…...
