简介:电动汽车BMS中SOH(健康状态)与SOP(功率状态)估算策略总结文档,面向电池管理系统工程师、整车动力电池研发与测试人员,系统梳理了SOH与SOP的核心估算方法及联合估算思路。文档从SOH定义出发,给出容量衰减至80%的更换判据,并分类讲解基于耐久性模型的开环方法、基于电池模型的闭环方法(RLS、KF)以及基于开路电压的SOH估算;SOP部分涵盖基于电池模型与基于内阻两类策略,提及复合脉冲法和HPPC测试法在实际应用中的特点;联合估算部分则介绍基于卡尔曼滤波双时间尺度和多时间尺度的SOH/SOP联合估算方法,可在不同时间尺度上完成SOC与SOH的联合估计,兼顾精度与计算量。文中还附有利用内阻与SOH线性关系建立估算模型的实测案例,包括详细的测试流程与计算步骤,便于工程落地时参考。资源为单个docx文档,压缩包大小787KB,内容紧凑、结构清晰。已有3661人浏览学习,适合需要快速掌握电池状态估算方法的工程师与研究者。
1. 电动汽车BMS里SOH和SOP估算,为什么总是做得像“黑匣子”
做BMS的人都有这种经历:SOC算得差不多能用,但一提到SOH和SOP,算法、标定和整车验证就像掉进黑匣子。本篇文章要拆的正是电动汽车BMS中SOH和SOP估算策略:SOH回答“电池还剩下多少寿命”,SOP回答“现在一秒、十秒、三十秒还能输出多少功率”。两者直接影响整车功率限制、续航估算和售后质保,却经常因为定义不统一、模型参数难标定、低温行为异常让开发卡壳。这篇笔记的目标读者是BMS软件工程师、三电系统集成工程师、电池测试标定工程师。下文不会只解释原理,而是把从定义、建模、在线计算、参数标定到高频踩坑点的完整落地路径讲清楚,方便你拿回去直接对照自己的方案。
需要先说破一个反直觉结论:SOC计算其实足够成熟,真正难的是SOH和SOP——一个需要长周期才能验证,另一个要求毫秒级别算准,偏偏SOP还要依赖SOH的估计结果。所以这两项往往被做成“查表+滤波+手工修正”的混合体,但混合得好不好,全看对底层策略的理解。我见过不少项目在台架上SOP标定数据漂亮,一上车低温启动就开始限功率,问题几乎都出在SOH、SOP和温度三者耦合关系没理清。接下来的内容,从状态定义开始逐步展开,尽可能把能直接用的公式、代码和表结构给出来。
第2章先讲SOH,因为它是SOP的上游输入。SOH估算策略看似简单,实际上一套量产BMS里往往同时运行着多条估算路径。只有先把“定义什么”和“用谁修正谁”想清楚,后续SOP的逻辑才不会在整车路上反复翻车。下面从定义出发,再到安时积分、模型法和数据驱动,一层层拆开。
2. 把SOH估算策略拆成三条技术路线:容量法、内阻法和数据驱动
2.1 先钉死SOH定义:容量保持率还是内阻增长率,不能混用
SOH最常见的定义是当前最大可用容量与额定容量的比值,即容量保持率。这个定义容易理解,也直接对应续航。但问题在于容量不能实时测量,只能通过完整充放电循环估算。内阻增长率则代表电池的功率能力衰减,和SOP直接相关。工程上最稳妥的做法是:以容量法为主定义SOH,用内阻法作为辅助状态,用于修正SOP模型中的电阻参数,而不是直接替代SOH数值。
下面的表格给出三种常用定义方式在实际工程里的使用场景,便于你先对号入座。
| SOH定义方式 | 计算基准 | 优点 | 缺点 | 典型用途 |
|---|---|---|---|---|
| 容量保持率 | 当前满充容量/额定容量 | 直观,对应续航里程 | 需要完整充放电或高精度在线辨识 | 仪表显示、质保判断 |
| 内阻增长率 | 当前直流内阻/初始内阻 | 反应功率能力,可在线估算 | 内阻测量受温度和SOC影响大 | SOP降额、充电策略修正 |
| 混合SOH | 容量+内阻加权 | 兼顾能量和功率视角 | 加权系数需要大量老化数据标定 | 高端平台或梯次利用评估 |
定义A和定义B可以同时存在于一套BMS里,一般我会用“容量SOH”对外显示,用“内阻SOH”对内控制。注意不要把内阻SOH直接暴露给用户,否则温度一变内阻就变,用户会认为健康度忽高忽低。
2.2 安时积分加满充修正:大多数BMS的兜底方案
安时积分的原理很简单:在电池完成一次从放电截止电压到满充电压的过程中,对电流进行时间积分,算出充入电量,再结合库仑效率折算成可用容量。这个思路成熟可靠,代码也少,但容易受电流传感器偏置和充放电截止点判断影响。实际工程中不会只做一次积分,而是用滑动窗口和满充满放触发条件更新。
下面给出一段可运行的核心逻辑,模拟一个基于充电电量的SOH更新模块。代码只保留主干,便于你直接套进自己的BMS仿真环境里观察行为。
class SoHEstimatorByCoulomb: def __init__(self, cap_rated_ah, init_soh=1.0, coulomb_efficiency=0.99): self.cap_rated = cap_rated_ah # 额定容量,单位Ah self.soh = init_soh # 初始SOH self.coulomb_eff = coulomb_efficiency # 库仑效率 self.charged_ah = 0.0 # 本轮累计充入电量 self.last_soc = None # 上次记录SOC,用于判断积分窗口 def integrate(self, current_a, soc, dt_s): """每次调用传入当前电流、SOC和步长""" if self.last_soc is None: self.last_soc = soc return # 只在充电且SOC上升时积分,避免把放电过程的电流计进去 if current_a > 0 and soc > self.last_soc: delta_ah = current_a * dt_s / 3600.0 * self.coulomb_eff # 如果SOC发生明显跳变,则重置窗口,防止误差累积 if soc < self.last_soc - 0.5: self.charged_ah = 0.0 self.charged_ah += delta_ah # 检测到从低SOC到满充的完整充电段,更新容量估计 if soc > 99.0 and self.last_soc < 80.0 and self.charged_ah > 0.5: est_cap = self.charged_ah + (100.0 - soc) / 100.0 * self.cap_rated self.soh = min(1.0, max(0.5, est_cap / self.cap_rated)) self.charged_ah = 0.0 self.last_soc = soc充电段的电流方向和SOC变化方向判断很关键。上面的写法避免把放电电流混入积分窗口;SOC跳变时重置窗口,是为了应对传感器噪声和信号中断。更新条件里加了“充电段SOC跨过80%”的限制,避免用一段很小的充电区间估算整体容量,否则每次插上充电枪都会扰动SOH。容量的下限和上限需要根据电池化学体系设定,不同电芯差异很大。
这套“安时积分+满充触发”方案在量产BMS里通常是兜底策略,后台运行频率低,只在充电完结点更新SOH。它的坑在于温度:冬天充满电的电量比夏天少很多,如果不加温度补偿,SOH会在换季时出现季节性跳变。所以一定要在更新逻辑里加入“平均温度”条件,例如只在电池温度处于15到35摄氏度之间时允许更新。
2.3 模型法与数据驱动作辅助:什么时候值得上
如果在SOH更新频率和可用性上有更高要求,单纯靠完整充满条件是不够的。很多运营车辆长期不会深度放电,安时积分法几乎没有机会更新SOH。此时可以用等效电路模型加卡尔曼滤波来做在线容量辨识,核心思路是把容量作为状态变量,通过电压预测残差不断修正。优点是只要有电流电压激励就能持续估计,缺点是模型误差会被“硬塞”进容量里,一旦电阻参数失配,SOH会出现虚假波动。
数据驱动方法这两年逐渐进入BMS预研,比如用高斯过程回归或长短时记忆网络从历史工况片段预测容量衰减。它对付复杂工况很有优势,但车规级嵌入式平台部署难度高,且需要大量带标签的老化数据。我的观点是,量产项目优先保证安时积分+内阻辅助法,只有在平台具备足够算力且数据积累完整的条件下,才考虑模型辨识或数据驱动作为A/B对比。
下面这张表可以帮助你评估三类SOH技术路线的实际工程代价。
| 技术路线 | 在线实时性 | 标定工作量 | 嵌入式资源开销 | 适用阶段 |
|---|---|---|---|---|
| 安时积分+满充修正 | 低频更新 | 低,只需温度补偿 | 极低 | 所有量产项目的兜底方案 |
| 等效电路模型+卡尔曼 | 每次上电在线估计 | 中,需要OCV和RC参数表 | 中等 | 有差异化需求的中高端平台 |
| 数据驱动 | 取决于模型大小 | 高,需要大量老化数据 | 高 | 预研或云端健康度服务 |
3. SOP估算策略:把功率边界算成一条随时间和状态变化的曲线
3.1 SOP的时间窗口与整车需求:10秒、30秒和持续功率分别干什么
SOP不能用一个固定数值表示,它本质上是“当前电池能支撑的最大输出/输入功率”随时间变化的一条曲线。整车控制器通常关注三类窗口:短时峰值功率,时间窗口在10秒左右,用于急加速和扭矩补偿;中等窗口在30秒左右,用于爬坡或连续超车;长时功率没有固定窗口,但需要满足热平衡约束,用于持续高速巡航。下表给出典型工程配置。
| 时间窗口 | 典型用途 | 约束条件 | 计算频率 |
|---|---|---|---|
| 10秒 | 急加速、扭矩补偿 | 电压下限、电流上限 | 100ms或更快 |
| 30秒 | 爬坡、连续超车 | 温升限制、SOC限制 | 1秒 |
| 持续功率 | 高速巡航 | 散热能力、热平衡 | 10秒级刷新 |
实际代码里,SOP模块通常按窗口分别计算,最后取最小值作为整车使用的限制值。因为10秒窗口算出来的电流一定大于30秒窗口,但30秒窗口的温度约束更紧,不取小值就会把电池“烧”到过热保护。
3.2 基于一阶RC模型的峰值功率计算:从端电压约束反推电流
最常用的SOP实时计算方法是基于一阶RC等效电路模型,在当前SOC、温度下已知开路电压OCV、欧姆内阻R0、极化电阻R1和极化电容C。对于给定时间窗,先根据电流阶跃响应推导出端电压表达式,再搜索能满足电压下限的最大电流,进而得到功率限值。很多量产BMS实际使用的是查表法,但查表法无法应对老化修正和动态SOC变化,所以下面这段代码演示的迭代反推思路更有参考价值。
import numpy as np def calc_sop_discharge(ocv, r0, r1, tau, soc, temp, t_window, v_min, i_max=600): """ 基于一阶RC模型计算放电SOP(峰值功率) :param ocv: 当前开路电压,单位V :param r0: 欧姆内阻,单位Ω :param r1: 极化内阻,单位Ω :param tau: 时间常数,tau = R1 * C :param soc: 当前SOC,0~100 :param temp: 温度,摄氏度,用于温度修正 :param t_window: 功率持续时间,秒 :param v_min: 放电截止电压下限,V :param i_max: 最大允许电流,A :return: 最大持续放电功率,W """ # 温度修正系数:一般用查表,这里模拟低温电阻升高 temp_factor = 1.0 if temp > 25 else 1.0 + (25 - temp) * 0.02 # 迭代计算最大电流,目标是在t_window末时刻端电压等于v_min i_current = i_max for _ in range(10): v_t = ocv - i_current * r0 * temp_factor \ - i_current * r1 * temp_factor * (1 - np.exp(-t_window / tau)) \ - (i_current * r1 * temp_factor * t_window / tau) * 0 # 忽略二阶动态 if v_t < v_min: i_current *= 0.95 else: i_current *= 1.05 # 最后再进行一次端电压校验,防止超调 v_t = ocv - i_current * r0 * temp_factor - i_current * r1 * temp_factor * (1 - np.exp(-t_window / tau)) if v_t < v_min: i_current = (ocv - v_min) / (r0 * temp_factor + r1 * temp_factor * (1 - np.exp(-t_window / tau))) return i_current * v_min这段代码里最关键的是把端电压到达下限作为约束,而不是直接用恒功率公式。用迭代逼近的原因是电流与极化电压的关系不是线性的,直接解析求解复杂,迭代三次后精度已经足够。需要重点说明的是温度因子:上面简单用线性系数模拟,实际工程中应该用至少五个温度点插值得到的电阻修正系数,低温下R0和R1增长很不均匀。还请注意SOC变化对OCV的影响:如果计算10秒窗口,SOC下降量小可以忽略,但如果算30秒以上,就要考虑SOC修正。
3.3 温度与DOD修正:同一块电池,冷车和热车SOP相差一倍
温度对SOP的影响排在所有因素前面。单体温度从25摄氏度降到零下10摄氏度时,内阻可能变成原来的两倍以上。SOP的MAP表通常以SOC和温度为主索引,以时间窗口为内层维度,输出最大允许功率。SOC越低,允许功率越小,这是为了避免深度放电把电压拖垮。下表是一个典型的放电SOP MAP表片段,功率单位采用相对值,便于你理解趋势。
| SOC | 温度 -20°C | 温度 0°C | 温度 25°C |
|---|---|---|---|
| 90% | 45% | 70% | 100% |
| 50% | 30% | 55% | 85% |
| 20% | 15% | 30% | 60% |
查表后的功率值还要经过SOH降额,这背后已经是状态估计层和功率调度层的耦合关系。如果直接拿新电池的MAP给老电池用,高SOC段可能没问题,但中低SOC段电压跌落速度会明显超预期。下一章展开讲这个耦合策略,以及如何在软件架构上避免两类状态互相打架。
4. SOH与SOP的耦合:老化降额与状态估计分层
4.1 降额不是简单乘一个系数:内阻老化应该作用在模型里面
很多BMS一开始做SOP降额,只是拿容量SOH当成系数乘到功率MAP上。比如新电池功率200kW,SOH显示80%,就直接输出160kW。这样做的后果是:如果老化主要体现为内阻增大而不是容量衰减,电压会被瞬间拉低,触及保护后整车动力反而经常被“一刀切”。正确思路是让内阻维度代表的老化趋势改写SOP计算用的电阻值,而不是粗暴地缩放最终功率。
我习惯把容量SOH设为SoH_C,内阻SOH设为SoH_R。SOP计算中使用的R0_est和R1_est表示为:
R0_est = R0_table * (a0 + b0 * SoH_R) R1_est = R1_table * (a1 + b1 * SoH_R)其中a0和b0可以通过老化试验标定,比如在25摄氏度下定期测量内阻,拟合内阻与SoH_R的关系。这样做的好处是,如果某个电池内阻增长快但容量保持尚可,SOP会自动降低,避免电压跌破下限;如果容量衰减主导,SOP降幅相对平缓。只有把老化的影响放进模型内部,SOP在生命周期末尾才能真正可靠,而不只是“看起来还有余量”。
4.2 分层架构示例:从SOH到SOP的完整调用链
BMS软件的软件架构应将状态估计和功率限制解耦。SOH属于慢变量,几十秒甚至几分钟更新一次;SOP属于快变量,在下电前需要按控制器周期刷新。先更新SOH,再更新SOP,能防止SOP因为SOH瞬时抖动而频繁变化。下面给一个简化的类视图,用于说明调用顺序和接口划分。
class BMSStateEstimator: def __init__(self, battery_data): self.soh = 100.0 self.soh_r = 100.0 self.ocv_table = battery_data["ocv_table"] self.r0_table = battery_data["r0_table"] self.r1_table = battery_data["r1_table"] def update_soh(self, measured_cap_ah, rated_cap_ah, dcir_new, dcir_now): """慢循环更新SOH""" self.soh = min(100.0, measured_cap_ah / rated_cap_ah * 100.0) self.soh_r = min(100.0, dcir_new / max(dcir_now, 1e-6) * 100.0) def calc_sop(self, soc, temp, t_window): """快循环计算SOP""" r0_base = self.r0_table[soc][temp] r1_base = self.r1_table[soc][temp] # 用内阻SOH修正模型参数,而不是直接修正最终功率 r0_est = r0_base * (0.3 + 0.7 * self.soh_r / 100.0) r1_est = r1_base * (0.4 + 0.6 * self.soh_r / 100.0) ocv = self.ocv_table[soc][temp] # 调用第三章的SOP计算函数 sop = calc_sop_discharge(ocv, r0_est, r1_est, temp=temp, t_window=t_window, v_min=2.5, i_max=300) return sop这个设计把“估计什么”和“输出什么”分开。SOH更新频次低,即使波动大,也不会直接引发SOP跳动。SOP每次计算时读取最新内阻SOH,但增加了平滑处理的空间,比如对SoH_R做一阶低通滤波。调用关系可以描述为:电芯电压电流温度进入状态估计模块,输出SOC和SOH;SOP模块再按需读取SOC、SOH和温度,生成功率限值。
需要反复强调的是,SOH对SOP的影响远不止乘系数那么简单。极化内阻R1的老化规律和欧姆内阻R0不一样,前者更受电解液和负极界面状态影响,后者与极耳和集流体关系更大。所以分别修正R0和R1是必要的,如果它们共用一个降额因子,在低温大倍率工况下会再次翻车。
5. 避坑指南:SOH与SOP的五个翻车现场与修复方法
5.1 现象:冷车起步一脚加速,BMS立刻限制功率,整车像踩了刹车
整车在零下环境下测试,动力响应明显迟滞,仪表同时弹功率限制提示。不少工程师第一反应是SOC低或电池故障,但实际上是因为SOP模块没有使用低温下真实的R0和R1,导致计算的峰值功率过于乐观,触发电压下限保护。
原因:温度修正表覆盖不全,或者SOP计算只修正了R0而忽略了R1。低温下极化内阻增长比欧姆内阻更明显,RC模型的时间常数也会变长。解决:在SOP计算前加入温度查表,至少覆盖-20、-10、0、10、25度五个点。并让R1的低温修正因子独立于R0标定,不要共用一张表。
5.2 现象:SOH在两次充电循环之间从100%跳到95%又跳回99%
某运营车队反馈,车辆SOH数值经常在一周内跳变,用户怀疑电池有问题。查看数据发现每次充电完成后的更新结果都不稳定,尤其是插枪时间短、起始SOC高时,SOH被错误拉低。
原因:安时积分窗口没有限制完整充电段,短时充电片段也被用来计算容量。充电过程中温度波动大,库仑效率变化会直接影响累计电量。解决:SOH更新必须满足三个条件才能触发:起始SOC低于某个阈值,充电结束SOC高于99%,充电过程平均温度在15到35度之间。同时在更新前对多次充电结果做滑动平均,比如取最近五次的中间值。
5.3 现象:SOP预测能输出200kW,实际输出到180kW时电压已经跌破下限
整车大功率测试中,SOP模块给出的允许功率明显超过了电池实际能承受的功率,动力电池电压瞬间跌破截止值,BMS不得不紧急切断输出。
原因:SOP计算只用了单时间常数的一阶RC模型,忽略了电池内部的浓差极化过程。实际在大电流脉冲下,极化电压会持续增长,一阶模型严重低估了压降。解决:改用双时间常数模型,或者为短时脉冲增加一个电压跌落安全裕量。工程上常用做法是给算出的最大电流乘0.9到0.95的安全系数,再输出给整车控制器。
5.4 现象:SOH显示85%,用户实测续航只有标称的60%
容量法SOH更新正常,但实际用车体验差异极大,主要集中在冬季和长期大功率充放电的车辆上。
原因:容量SOH代表可用能量保持率,没有反映功率能力下降。电池经过高频大倍率充放电后,内阻增加而容量衰减不明显,用户感受到的可输出功率和可用能量同时下降。解决:在整车能量管理策略中加入内阻SOH,用它降低仪表显示的“预计续航”,同时在SOP输出端限制冬季大功率输出。不要只盯容量法SOH一种指标。
5.5 现象:HPPC台架测试标定出来的SOP参数,装车后误差增大
实验室里用标准HPPC工况标定得到的内阻模型,在实车动态工况下预测电压偏差达到了30mV以上,导致SOP估算偏差明显。
原因:实车电流变化频率高,模型参数在不同电流方向切换时存在迟滞,而台架测试使用的是固定电流脉冲序列。解决:在标定完成后,用实车采集的随机电流工况回放一遍,对RC参数做在线辨识校正,或者增加电流方向切换时的动态修正项。
6. 进阶验证:用离线HPPC测试数据把SOP参数标到“敢输出”
SOP估算最怕的是标定数据好看、实际输出不可靠。上章讲了几个坑,这一章补一个具体的标定验证流程,能够在项目量产前提前发现模型误差。我常用的做法不是直接信任HPPC表格,而是用HPPC结果做初始值,再用模拟实际工况的电流序列做一次闭环验证。
第一步是把HPPC测试数据整理成模型参数表。每一行记录SOC点、温度点、R0、R1和C值。关键是在10秒脉冲段提取R0,在脉冲结束后按指数拟合得到R1和C,这个步骤至少需要三个SOC点交叉验证。第二步是写一个离线回放脚本,用实际采集的车辆工况电流作为输入,预测端电压,并与实际端电压对比,统计均方根误差。下面是回放脚本的核心片段。
def replay_battery_model(voltage_meas, current_meas, dt, r0, r1, tau, ocv_soc_table, soc_init): v_pred = [] v_rc = 0.0 soc = soc_init for i in range(len(current_meas)): # 一阶RC等效电路预测端电压 v_rc = v_rc * np.exp(-dt / tau) + current_meas[i] * r1 * (1 - np.exp(-dt / tau)) v_t = ocv_soc_table(np.clip(soc, 0, 100)) - current_meas[i] * r0 + v_rc v_pred.append(v_t) # 简化SOC更新,实际用安时积分 soc -= current_meas[i] * dt / 3600.0 / 50.0 * 100.0 return np.array(v_pred)这个回放脚本跑完后,看电压预测误差曲线。如果误差在脉冲上升沿最大,说明R0偏小,需要调大;如果误差在脉冲结束后的恢复段最大,说明R1或tau不合适。这个习惯我坚持了很多项目,每次都能抓出至少一个因为温度插值不够细造成的标定问题。
做完离线回放,再给SOP增加一个闭环修正系数也不迟。修正系数的计算方式是让模型在标定功率点上的预测电压与实际电压之差小于5mV,这样整车上输出的SOP才敢真正作为扭矩控制依据。我自己的教训是,早期几次项目只依赖HPPC表格,没有做回放验证,导致冬天交付之后不断收到限功率投诉,后来才把“离线回放”写入开发流程。希望这套流程也能帮你少走一些弯路。
本文还有配套的精品资源,点击获取