1. 做SOC均衡前,先搞清楚SOC为什么会偏
1.1 单体不一致是均衡存在的唯一理由
去年我做一块16串磷酸铁锂电池组的BMS样机时,遇到了一个很典型的毛病:整组充电器已经转灯,但拆下来一量,某几串电芯电压只有3.35V,另外几串却已经顶到3.65V。当时保护板没有均衡功能,SOC显示还有8%,结果一拉载电压直接塌到保护值。说白了,这就是电芯不一致导致的容量利用率问题。
电芯不一致来自三个方面:制造工艺的离散性、使用过程中的温度差异、以及循环老化速度不同。哪怕是同一批次出厂的电芯,内阻、自放电率、容量也不可能完全一致。随着充放电循环次数增加,这种差异会被逐步放大。一组电池的可用容量,不是由平均值决定的,而是由最差的那一串决定的。均衡的目的,就是让各串电芯在充放电过程中尽量保持同步,避免某一串提前到达截止电压,从而浪费整组容量。
1.2 SOC误差的三个来源:采样、积分、模型
做SOC估算时,我习惯把误差来源分成三层来排查。
第一层是采样误差。电压、电流、温度的测量精度直接决定SOC估算的上限。STM32F4内部的12位ADC理论上能分辨3.3V/4096,约0.8mV,但在实际板上,电源纹波、PCB走线压降、参考电压漂移都会把有效精度拉低。我实测一块板子,ADC采样值在静止状态下有±5mV的跳变,这对SOC估算来说是不可接受的噪声。
第二层是积分误差。安时积分法说白了就是把电流对时间做累加,积分误差会随时间累积。假设电流采样偏置有10mA,对一块100Ah的电池来说,10小时就累积0.1Ah,折合SOC约0.1%。看起来不大,但如果偏置达到100mA,10小时就是1%的SOC偏差,而且这个误差只增不减,没有收敛机制。
第三层是模型误差。无论是开路电压查表还是等效电路模型,本质上都是对电芯特性的近似。磷酸铁锂的OCV-SOC曲线在中段(20%到90%)非常平缓,电压变化只有几十毫伏,这个时候靠电压反推SOC,误差相当可观。模型误差不像积分误差那样随时间累积,但在特定SOC区间会被放大。
1.3 为什么单独靠电压法撑不住
很多人会问,SOC直接查开路电压曲线不就行了吗?实测中问题很多。
电池从工作状态切换到静置状态,极化电压需要相当长时间才能消除,磷酸铁锂可能需要2小时以上。也就是说,你必须等电池真正"休息"够,测得端电压才对应OCV曲线。行驶中、充电中、刚停机时的电压,都叠加了极化内阻和欧姆内阻上的压降,直接查表必然不准。
我在样机上做过一个对比测试:同一串电芯,静态放置3小时后测得电压3.32V,查表SOC约55%;而在1C放电过程中同样测到3.32V,实际SOC已经到了40%左右。两者相差15个百分点。这就是我坚持在方案中采用"OCV查表 + 安时积分 + 卡尔曼滤波"三者融合的原因——任何一个单独方案都有硬伤,组合起来才能互相弥补。
2. 采样链路设计:电压、电流、温度这三路数据怎么喂给STM32F4
2.1 电压采样:从模拟前端到ADC的完整链路
对串数较多的电池组,优先选择专用模拟前端芯片来做电压采样,比如常见的16串AFE方案,通过SPI或I2C与MCU通信,同时提供均衡MOS驱动、诊断功能和温度采集接口。它的优点是采样精度高、通道间一致性有保障、电气隔离也天然解决。STM32F4在这里的角色是负责策略判断、数据融合和总线上位机通信,而不是直接去量每一串电压。
如果只做几串的低压保护板试验,当然可以用STM32F4的ADC直接采。此时必须注意输入通道的共模电压范围,以及前端电阻分压网络的精度温漂。分压电阻用1%还是0.1%的合金电阻,直接决定整条采样链路的精度天花板。我建议至少用0.5%精度、温度系数小于100ppm的电阻,并且在ADC引脚对地并联一个0.1uF电容,配合几十到几百欧姆的串联电阻构成低通滤波,滤除高频噪声。
关于采样触发,我强烈建议用定时器触发ADC,而不是在软件里用延时循环去读。STM32F4的ADC支持外部触发注入,配合DMA可以做到固定采样率、零CPU干预。我一般配置定时器每100ms触发一次16路电压采样,每路做64次累加后取平均,等效采样率足够滤掉大部分工频干扰。CPU只在DMA传输完成中断里做数据搬运,耗时几乎可以忽略。
2.2 电流采样:两个方向,两种做法
电流采样有两条路线:分流电阻法和霍尔传感器法。
分流电阻法在BMS里更常见,尤其是需要控制BOM成本的场合。用毫欧级的锰铜或合金电阻串联在主回路中,通过高精度运放把微弱压降放大到ADC可测范围,或者直接用带放大功能的专用电流检测芯片。STM32F4的ADC采集放大后的信号,换算成实际电流。这种方法线性度好、温漂可控,但需要注意分流电阻的功率损耗:100A电流流过2mΩ电阻,功耗20W,散热设计不能马虎。
霍尔传感器的优势是隔离和几乎零插入损耗,但零漂和温漂相对更明显,小电流下精度不如分流电阻。我做均衡电流检测时用了开环霍尔,静态输出有±10mA的波动,对SOC积分来说需要做零点校准。方案里我用了一个低成本的零点校准策略:电池静置超过一定时间后,把霍尔输出读数的平均值记录为零偏量,实时计算时扣除。
无论哪种方式,电流采样值最终都要进安时积分器,所以偏置误差必须严肃对待。我调试时发现,运放的输入偏置电流和参考电压误差会造成实测零点偏移,这一步不做校准,后面SOC积分会越漂越远。
2.3 温度采样与SOC温度修正
温度对SOC估算的影响常被低估。电池在不同温度下的可用容量不同:低温时放电容量明显衰减,内阻增大、极化加剧,同一SOC下端电压也不同。如果SOC算法不引入温度修正,冬季低温场景会出现SOC虚高、电量跳变的现象。
我在这块BMS上用了两种温度采集方式混合:每串电芯附近的AFE芯片自带NTC输入,采集电芯表面温度;同时在电池组两侧布置两路独立NTC,通过STM32F4的ADC直接采集,作为环境温度参考。所有温度数据进入SOC算法后参与两个修正:一是容量修正系数,二是OCV查表曲线的温度插值。
温度采样同样要用DMA配合定时器,只不过NTC信号本身变化慢,采样频率可以降到每1秒一次。另外,NTC的串联分压电阻需要低漂移类型,否则温度变化引起的分压变化会被错误当成NTC阻值变化,产生几度的偏差。我在实际样板调试时遇到过NTC引脚被干扰的问题,解决方法是在ADC引脚加100nF电容并做软件滤波。
3. 核心算法落地:查表、安时积分与卡尔曼滤波的组合拳
3.1 OCV-SOC查表与滞回处理
在STM32F4上做SOC估算,大多数人会先建立一张"电压-SOC"查找表。这张表怎么来?最好不要直接拿厂家手册的典型曲线,而应该用你实际批次电芯做静态标定:充满后静置,按5%或10%的SOC步进放电,每次静置足够长时间后记录端电压,再把数据拟合、插值成至少几十个点的数组。
实测过程中最值得注意的是滞回现象。充电平衡状态下和放电平衡状态下,相同SOC对应的OCV并不一致,差距可以到20mV以上。磷酸铁锂这方面尤其明显。我在算法里维护了充放电两套OCV表,根据最近的电流方向和充电累计电量切换查表。同时增加了一个滞回过渡逻辑:当电流方向改变时,估算的OCV在两条曲线之间渐进过渡,而不是瞬时跳变,避免SOC输出明显突变。
具体数据结构很简单:一个const数组存SOC点对应的OCV,一个const数组存OCV点对应的SOC,二分查找配合线性插值。在STM32F4上,用浮点计算和二分法没有任何性能压力,查找一次耗时是微秒级,完全可以在1秒周期的估算任务里随意调用。
3.2 安时积分:采样率、漂移与校零
安时积分的基础公式就是SOC(t) = SOC(0) - (∫I dt) / Q_nom。工程上要做的首先是把电流采样值转换为Ah增量,并按固定周期积分。我设定估算周期为1秒,电流采样周期为100ms,每1秒对近10个电流样本做算术平均,再乘时间差得到Ah增量,累加到当前SOC上。
积分有两个关键敌人。第一个是零点漂移,上面提到过,霍尔和运放都会有零偏。我在设备进入静置状态且电流小于一定阈值时,把当前读数的平均值记录为零偏补偿量,参与实时计算。需要注意,只有在电量稳定的状态才适合校准,否则会把真实电流当成零偏消掉。
第二个是容量老化。Q_nom不是一个常量,它随循环次数增加而下降。严谨的做法是定期做一次满充满放标定,把积分得到的实际容量更新到算法里。我的方案支持通过上位机写入实际容量值,同时在每次充满判据成立时,把安时积分结果强制归一到100%,消除累积误差。
3.3 卡尔曼滤波在STM32F4上的工程化
卡尔曼滤波的原理大家都清楚,但落地到MCU上有几个实际工程点需要处理。我采用的是扩展卡尔曼滤波(EKF),状态量是SOC和极化电压,输入量是电流,观测量是端电压。
状态方程离散化后,SOC的预测就是上一时刻SOC减去电流积分与容量之比;极化电压预测项包含一个由RC参数决定的时间常数衰减项。测量方程就是端电压 = OCV(SOC) + 极化电压 + 欧姆内阻×电流。
核心难点是测量方程的雅可比矩阵,需要计算dOCV/dSOC。我把OCV-SOC数组的导数也预先算好,存在另一个const数组里,插值得到当前梯度。这样在STM32F4上就避免了运行时求解偏导数的复杂运算。
STM32F4带有FPU,单精度浮点运算足够快。EKF里的矩阵运算维度很小,我用C语言手写,没有引入通用矩阵库,反正就是2x2和1x2的运算量。整个EKF一步迭代在168MHz主频下运行时间不到几十微秒,1秒的周期里非常充裕。
这里要提醒一点:卡尔曼滤波的Q和R参数不是随便设的。Q设得小,滤波增益低,SOC变化迟缓,但抗扰能力强;Q设得大,跟踪速度快,但容易被噪声带偏。我测试中Q设太小时,从静置切到放电状态,SOC要几分钟才能跟上真实值,表现就是"显示值掉得慢、突然跳一下"。最后我是在不同工况下反复试出来的折中值,并且让Q随电流大小动态调整,大电流工况下信任安时积分,小电流/静置工况下信任电压测量。
4. 均衡策略的工程实现:什么时候均衡才不算瞎折腾
4.1 被动均衡电路与MOS控制细节
SOC均衡和SOC估算完全不同,它是要真正干预电池状态的。我的方案选择被动均衡,理由很简单:电路简单、控制可靠、成本低,对中小容量电池组完全够用。主动均衡效率高,但电感或变压器的能量转移控制逻辑复杂,在紧凑的板子上也不易布局。
被动均衡的电路拓扑是每串电池并联一组"均衡电阻 + 均衡MOS"。MOS管由STM32F4的GPIO控制,或者由AFE芯片内部的均衡开关控制。均衡电阻阻值决定均衡电流,我用的典型值是51Ω,对应单串3.2V电压下约63mA的均衡电流。也有人用10Ω电阻做到300mA级别,但散热和功耗考验比较大,16串同时均衡时,电阻网络总功耗可能超过10W,板子会明显发热。
我使用的是AFE芯片内置均衡MOS,只需通过I2C/SPI写入均衡控制寄存器即可开启对应通道,省去了外置MOS驱动电路。实际测试中,内置MOS的导通电阻和额定电流需要确认,部分AFE的均衡电流上限只有几十毫安,不适合大容量电芯。大容量电芯做均衡时,可以考虑外置MOS并联多组电阻的方案。
4.2 均衡判据:压差、SOC差、温度、状态机
均衡不能不分时机地一直开着。均衡开启时机不对,不仅浪费能量,还会让SOC本来偏高的串被强行拉低到错误值。
我设计的均衡状态机分成三个阶段:充电均衡、静置均衡、放电不均衡。充电阶段是均衡的最佳窗口,因为充电末期电压被充电机抬升到较高水平,最高单串电压已经接近充电截止电压,此时把偏高电芯的能量通过电阻放掉,能减缓它到达截止电压的速度,让其他电芯继续充电。静置阶段也会做小幅均衡,目的是修正静置电压差异,但这个阶段均衡电流不能太大,避免把电芯压到均衡曲线以下。
开启均衡的条件我在代码里做成同时满足多项:最高单体电压高于某个平台值(比如3.40V)、最高与最低单体压差大于30mV、被均衡串的温度低于设定阈值(比如45℃)、电池处于非放电状态且电流小于设定值。只有这些条件全部满足,均衡状态机才允许进入均衡流程。
为什么要在充电阶段才均衡?如果静置时某串电压偏高就拼命均衡,很容易造成"虚均衡":这串电芯的电压偏高可能只是静置时间不够、极化未消除,而不是真的SOC偏高,强行均衡会把它拉到不健康的低SOC状态。
4.3 均衡结束条件与异常保护
均衡不能无休止地进行。我设置了几个结束条件,任何一个满足就停止整组均衡或关闭该串通道。
- 单体压差收敛到阈值以下,例如10mV以内,说明不一致已经修正
- 充电过程结束,均衡必须同步停止,防止把高SOC电芯放过头
- 被均衡串电压降到与最低串电压一致,此时继续均衡意义不大,甚至会引入反向差异
- 温度超限,均衡电阻发热严重时立即关闭,转化为告警事件上报
- 均衡连续运行超过预设时长(例如2小时),即使压差还没收敛,也要强制停一段时间,避免电阻长期发热
我把这些条件全部挂在1秒的周期任务里检查,每次均衡开关操作后更新AFE寄存器,并在内存中保留均衡记录,方便通过上位机查看哪一串被均衡过、均衡了多久。这个记录对后续定位电芯老化非常有用——经常被均衡的串,大概率容量偏低,需要重点关注。
5. 参数整定与实测调试记录
5.1 从仿真到真电池的差距
我先在PC端的仿真模型里用理想数据跑SOC估算逻辑,把EKF的增益参数和OCV表调到自洽,但移植到STM32F4真机上后,问题马上就来了。
最大的差距是真实电压数据里带着噪声和极化干扰。仿真里我给OCV查表输入的是一个精确值,真机上输入的是经过64次平均的采样值,仍然有大约2mV的毛刺。这个毛刺对端电压观测方程的影响不大,但在计算dOCV/dSOC时会放大——曲线平坦区段的导数很小,微小电压误差会换算成较大的SOC修正量。
解决思路是让EKF在静置和动态工况下使用不同的R参数。静置时端电压可信,R设小一些,让滤波器更多信任电压观测;动态时极化模型不完美,R设大一些,更多信任安时积分。同时我限制了单次EKF修正SOC的最大幅度,比如单次不超过3%,防止个别异常采样点把SOC打飞。
5.2 典型调试问题:跳变、振荡、均衡失效
调试阶段我遇到过一个很典型的SOC跳变问题:电池从放电切到静置,显示SOC会在几秒内从52%跳变到58%再弹回54%。排查下来,原因是我在滞回过渡和EKF修正两部分同时对OCV做了调整,两个修正量叠加导致过冲。后来我把滞回过渡改成纯曲线插值,不再直接修正SOC,只影响测量方程的OCV值;SOC的修正完全交给EKF的状态更新逻辑,问题就消失了。
均衡振荡的案例也值得分享。最初我的均衡开启条件里压差阈值是30mV,关闭条件是10mV。实际运行时,某串电芯被均衡到与最低串只差8mV,均衡关闭;但因为它自放电率稍高,电压慢慢回落,半小时后压差又到28mV,均衡重新开启。这样的振荡导致均衡电流频繁通断,对MOS寿命不利。我加了时间延迟:均衡关闭后至少要等待10分钟才能重新开启,并且关闭阈值提高到15mV,让均衡留出一点余量。
还有一个均衡失效的情况:在充电阶段开启均衡后,均衡电流会叠加到充电电流里,导致被均衡电芯所在回路的充电电流反而略高,出现"越均衡越慢"的现象。后来我把充电阶段均衡的判定策略做了调整,只在接近充电截止时开启均衡,配合充电机降流阶段,才能保证均衡效果可观测。
5.3 参数整定的实践清单
最后分享一份我在调试过程中沉淀下来的参数整定清单,直接按这个顺序过,能少走不少弯路。
- 先校零后调增益:任何电流采样校零不完成,后面所有积分逻辑都不可信
- SOC初始值用静置OCV对表,不要用上次下电保存值直接续着用,除非确认电池没有明显自放电
- OCV-SOC表的标定必须用同一批次电芯,不同厂家的电芯曲线差异足以改变整个SOC行为
- EKF的Q和R参数用日志记录法来调:跑实际工况,记录电压残差,残差偏大说明R偏小或模型不准,SOC过冲说明Q偏大
- 均衡开启后要在日志里记录均衡通道和时长,隔段时间对比一下,长期被均衡的串大概率容量衰减,后续维护时优先排查
- 电池组静置下的压差正常应在10mV之内,超过20mV要检查连接片压降、采样线虚接和电芯本身差异,别一上来就调均衡参数
整套系统在STM32F4上运行稳定后,我做了一个小实验:把均衡功能关闭,充满后静置24小时,记录各串压差;再把均衡打开跑一轮完整充电,同样静置24小时。压差从关闭时的37mV收敛到12mV,满充容量利用率提高了约6个百分点。这个数据侧面说明了SOC估算和均衡策略配合的价值。
目前在琢磨的是把主动均衡方案做一个简化版样机,利用STM32F4的定时器输出可控PWM驱动电感式能量转移电路,想看看效率能把被动均衡的发热损耗优化到什么程度。这个坑估计又要踩几个月,后面有结论了再来跟大家细聊。