恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
EKF与神经网络融合的锂电池SOC估算方案:原理、实现与Matlab代码
首页
资讯中心
/
EKF与神经网络融合的锂电池SOC估算方案:原理、实现与Matlab代码
EKF与神经网络融合的锂电池SOC估算方案:原理、实现与Matlab代码
发布时间:2026/9/18 11:51:40
1. 锂电池SOC估算为什么值得死磕做BMS电池管理系统的同行都有一个共识SOCState of Charge荷电状态估算是整个系统里最核心、也最让人头疼的一块。说它核心是因为SOC直接决定了续航显示、充放电策略、均衡控制、安全保护这些上层逻辑说它头疼是因为SOC本身是个不可直接测量的内部状态量你只能通过电压、电流、温度这些外部信号去“猜”而且这个“猜”的过程还要面对工况剧烈变化、温度漂移、电池老化、传感器噪声等一系列干扰。我最早接触SOC估算是在一个储能项目上当时用最朴素的安时积分法结果跑了一个月就发现误差越积越大充满电后显示还剩15%实际已经快放空了。后来换成开路电压法静态校准倒是准但一上路就废了——车辆跑起来电压被内阻和极化压得乱七八糟根本没法用。再后来上了扩展卡尔曼滤波EKF效果明显好转但在低温大倍率放电的工况下EKF的线性化误差和模型参数失配问题又暴露出来误差能飙到8%以上。这就是为什么我开始琢磨EKF和神经网络融合的方案。EKF擅长在已知模型结构下做最优估计神经网络擅长从数据里学那些模型描述不了的复杂非线性关系两者结合一个管“物理规律”一个管“数据修正”互补性非常强。这篇文章就把我整套方案的思路、实现细节、Matlab代码框架、踩过的坑全部摊开讲适合做BMS算法、储能系统、电动车控制的工程师也适合正在做相关课题的研究生参考。2. 整体方案设计与核心思路拆解2.1 为什么单靠EKF不够用EKF的核心思想是把非线性系统在当前估计点附近做一阶泰勒展开近似成线性系统然后套用标准卡尔曼滤波的框架。它的前提是你得有一个足够准的电池模型通常用的是Thevenin等效电路模型或者二阶RC模型。模型里有开路电压OCV与SOC的关系曲线、欧姆内阻R0、极化电阻R1和极化电容C1这些参数。问题就出在这里这些参数不是常数。它们随SOC、温度、充放电倍率、循环次数都在变。你离线辨识出来的一组参数放到实际工况里可能只在一个很窄的范围内有效。更麻烦的是EKF的噪声协方差矩阵Q和R调参全靠经验Q调大了跟踪快但抖动大Q调小了平滑但响应慢遇到突变工况就容易发散。我实测过一组数据在25度、1C恒流放电工况下EKF的SOC误差能控制在2%以内但到了0度、2C脉冲放电工况下误差直接跳到7%到9%。这个差距在车用场景里是不可接受的因为低温大倍率恰恰是最需要准确SOC的时候。2.2 神经网络能补什么神经网络的强项是从大量数据中拟合复杂的非线性映射关系不需要你显式地写出物理模型。对于SOC估算这个任务神经网络可以学到“电压、电流、温度、历史SOC”到“当前SOC”之间的映射而且这个映射可以隐含地包含温度影响、老化影响、倍率影响。但神经网络也有它的问题。纯数据驱动的方法泛化能力有限遇到训练集里没出现过的工况就容易输出离谱的值而且神经网络没有物理约束输出的SOC可能不满足安时积分的守恒关系导致估计值跳变。所以我的思路很明确用EKF做主体框架保证估计的物理一致性和时序连续性用神经网络做误差补偿学习EKF估计残差与工况变量之间的映射关系然后把补偿量反馈回去修正EKF的输出。这样既保留了EKF的鲁棒性又引入了神经网络对复杂非线性的拟合能力。2.3 融合架构的选型考量融合方式有好几种松耦合、紧耦合、残差补偿、自适应调参。我最终选的是残差补偿方案具体结构是第一层EKF基于二阶RC模型做SOC初步估计输出SOC_ekf第二层神经网络以电压、电流、温度、SOC_ekf、安时积分增量作为输入输出SOC误差补偿量delta_soc第三层最终SOC SOC_ekf delta_soc同时把修正后的SOC反馈回EKF的状态向量参与下一时刻的预测。为什么选残差补偿而不是让神经网络直接输出SOC因为直接输出的话神经网络的输出没有时序约束相邻时刻可能跳变而且一旦某个输入异常输出可能完全失控。残差补偿的方案里神经网络的输出被限制在一个合理范围内我设的是正负5%即使神经网络失效EKF的主体估计仍然能兜底。为什么不用紧耦合把神经网络嵌入EKF的状态方程那样理论上是更优雅但实现复杂度高很多而且神经网络的非线性会让EKF的雅可比矩阵推导变得极其繁琐工程上不划算。2.4 数据来源与训练集构建神经网络要训练就得有标注数据。我的做法是用高精度充放电测试设备跑标准工况记录电压、电流、温度、安时积分值然后用安时积分结果作为SOC的参考真值。这里要注意安时积分的初始SOC需要用静置后的开路电压来校准否则起点就偏了。训练集的工况覆盖很关键。我至少覆盖了以下几个维度温度-10度、0度、25度、45度倍率0.2C、0.5C、1C、2C工况类型恒流放电、脉冲放电、DST工况、US06工况电池类型三元锂、磷酸铁锂各一组。每个维度组合跑3到5次总共积累了大约200万条样本。数据量看着大但神经网络训练起来其实很快因为输入维度只有6维网络规模也不需要太大。3. 核心细节解析与实操要点3.1 二阶RC电池模型的建立与参数辨识二阶RC模型的状态方程和观测方程如下状态向量取 [SOC, U1, U2]其中U1和U2是两个RC环节的极化电压。状态方程为SOC(k1) SOC(k) - eta * Ts * I(k) / (3600 * Cn) U1(k1) U1(k) * exp(-Ts/(R1*C1)) R1 * (1 - exp(-Ts/(R1*C1))) * I(k) U2(k1) U2(k) * exp(-Ts/(R2*C2)) R2 * (1 - exp(-Ts/(R2*C2))) * I(k)观测方程为U(k) OCV(SOC(k)) - U1(k) - U2(k) - R0 * I(k)其中eta是库伦效率Ts是采样周期Cn是额定容量I是电流放电为正OCV是开路电压。参数辨识我用的是脉冲放电实验加最小二乘拟合。具体步骤是先静置2小时让电池达到平衡记录OCV然后以1C放电10秒静置40秒再放电10秒循环若干次从电压响应曲线里提取R0、R1、C1、R2、C2。R0是放电瞬间的电压跳变除以电流R1和C1从第一个RC环节的弛豫曲线拟合R2和C2从第二个RC环节拟合。OCV-SOC曲线我用的是小电流充放电法以0.02C的电流缓慢充放电记录电压和安时积分值取充放电曲线的平均值作为OCV-SOC关系。这条曲线对EKF的精度影响极大我建议至少每10% SOC取一个点然后用三次样条插值。注意OCV-SOC曲线存在迟滞效应充电和放电的OCV不完全一样。如果你的应用对精度要求高建议分别建立充电OCV和放电OCV两条曲线根据电流方向切换。3.2 EKF的递推流程与调参经验EKF的递推分两步预测和更新。预测步x_pred f(x_prev, u) P_pred F * P_prev * F Q更新步K P_pred * H * inv(H * P_pred * H R) x_est x_pred K * (z - h(x_pred)) P_est (I - K * H) * P_pred其中F是状态转移雅可比矩阵H是观测雅可比矩阵Q是过程噪声协方差R是观测噪声协方差。调参这块我踩过不少坑。Q矩阵里SOC对应的元素我设的是1e-6极化电压对应的元素设的是1e-4。R设的是0.01。这些值不是拍脑袋来的是根据传感器精度和模型误差量级估算的。电压传感器精度假设是5mV电流传感器精度是1%FS温度传感器是0.5度把这些误差传播到SOC上就能大致确定Q和R的量级。实操心得Q和R的比值比绝对值更重要。Q/R越大滤波器越信任观测跟踪快但抖动大Q/R越小越信任模型平滑但响应慢。我一般先用一组经验值跑通然后看残差序列的均值和方差如果残差均值不为零说明模型有偏需要调模型参数如果残差方差过大说明R设小了或者Q设大了。3.3 神经网络的结构设计与训练策略我用的神经网络是前馈神经网络结构是6-20-10-1即输入层6个节点两个隐藏层分别20和10个节点输出层1个节点。激活函数隐藏层用tanh输出层用线性。输入特征包括当前电压U(k)当前电流I(k)当前温度T(k)EKF估计的SOC_ekf(k)安时积分增量delta_ah(k)上一时刻的电压U(k-1)。输出是SOC误差补偿量delta_soc(k)。训练用Matlab的Deep Learning Toolbox优化器选adam学习率初始0.001每50个epoch衰减0.5最大epoch设500早停patience设30。损失函数用MSE但我在损失里加了一个正则项惩罚delta_soc的绝对值防止神经网络输出过大的补偿量。训练集、验证集、测试集按7:1.5:1.5划分。训练前对所有输入做归一化均值归零方差归一。这个步骤很关键不做归一化的话电流的数值范围是电压的几十倍网络会偏向电流特征忽略电压和温度。常见坑训练集里如果某些工况样本太少神经网络会在这些工况上表现很差。我的做法是对稀疏工况做数据增强比如在温度维度上做插值生成中间温度的样本。但要注意增强的样本不能太离谱否则会引入虚假模式。3.4 融合策略的实现细节融合的时机和方式直接影响最终精度。我的实现是EKF先跑一个完整的时间步得到SOC_ekf(k)和P_est神经网络用当前时刻的输入特征计算delta_soc(k)最终SOC_final(k) SOC_ekf(k) delta_soc(k)把SOC_final(k)写回EKF的状态向量替换SOC_ekf(k)同时把P_est里SOC对应的方差适当放大表示我们对修正后的值有一定的不确定性下一时刻EKF从修正后的状态继续预测。第4步的方差放大系数我设的是1.2意思是修正后的SOC不确定性比EKF原始输出略大一点。这样做是为了防止神经网络补偿过度导致滤波器过度自信。还有一个细节神经网络的补偿量我做了限幅上限正负5%。这个限幅值是根据EKF在极端工况下的最大误差定的。如果EKF误差超过5%说明模型已经严重失配这时候应该触发参数重辨识或者降级策略而不是靠神经网络硬补。4. 实操过程与核心环节实现4.1 数据采集与预处理数据采集用的是Arbin充放电测试柜采样周期1秒电压精度0.02%FS电流精度0.05%FS。电池是某品牌的18650三元锂电芯额定容量2.5Ah标称电压3.7V。采集完的原始数据里会有一些异常点比如测试柜偶尔的通信丢包导致的零值或者接触电阻变化导致的电压尖峰。预处理步骤包括剔除电流为零但电压突变超过50mV的点对电压和电流做5点滑动平均滤波用安时积分计算参考SOC初始SOC用静置2小时后的OCV查表得到把数据按工况类型分段每段单独归一化。预处理完的数据存成mat文件每个文件包含时间、电压、电流、温度、参考SOC五个变量。4.2 EKF的Matlab实现EKF的核心代码我写成一个函数输入是当前测量值、上一时刻状态和协方差输出是当前估计值和协方差。关键部分如下function [x_est, P_est] ekf_step(z, x_prev, P_prev, I, Ts, params) % 状态预测 SOC_pred x_prev(1) - params.eta * Ts * I / (3600 * params.Cn); U1_pred x_prev(2) * exp(-Ts/(params.R1*params.C1)) ... params.R1 * (1 - exp(-Ts/(params.R1*params.C1))) * I; U2_pred x_prev(3) * exp(-Ts/(params.R2*params.C2)) ... params.R2 * (1 - exp(-Ts/(params.R2*params.C2))) * I; x_pred [SOC_pred; U1_pred; U2_pred]; % 雅可比矩阵F F eye(3); F(2,2) exp(-Ts/(params.R1*params.C1)); F(3,3) exp(-Ts/(params.R2*params.C2)); % 协方差预测 P_pred F * P_prev * F params.Q; % 观测预测 OCV interp1(params.ocv_soc, params.ocv_val, SOC_pred, spline); z_pred OCV - U1_pred - U2_pred - params.R0 * I; % 雅可比矩阵H dOCV gradient(params.ocv_val, params.ocv_soc); dOCV_interp interp1(params.ocv_soc, dOCV, SOC_pred, spline); H [dOCV_interp, -1, -1]; % 卡尔曼增益 K P_pred * H / (H * P_pred * H params.R); % 状态更新 x_est x_pred K * (z - z_pred); P_est (eye(3) - K * H) * P_pred; % SOC限幅 x_est(1) max(0, min(1, x_est(1))); end这段代码里OCV-SOC曲线用三次样条插值dOCV是OCV对SOC的导数也用插值得到。注意gradient函数计算的是数值导数如果OCV-SOC曲线点数太少导数会不准建议至少100个点。4.3 神经网络的Matlab实现神经网络我用的是feedforwardnetMatlab自带的函数几行代码就能搭起来net feedforwardnet([20, 10]); net.layers{1}.transferFcn tansig; net.layers{2}.transferFcn tansig; net.layers{3}.transferFcn purelin; net.trainFcn trainlm; net.trainParam.epochs 500; net.trainParam.lr 0.001; net.trainParam.max_fail 30; net.performFcn mse; % 数据准备 X [U; I; T; SOC_ekf; delta_ah; U_prev]; Y SOC_ref - SOC_ekf; % 归一化 [Xn, ps_input] mapminmax(X, -1, 1); [Yn, ps_output] mapminmax(Y, -1, 1); % 训练 net train(net, Xn, Yn); % 预测 delta_soc_n net(Xn); delta_soc mapminmax(reverse, delta_soc_n, ps_output);这里有个细节mapminmax的归一化参数要保存下来在线估计的时候用同一组参数做归一化否则输入分布不一致网络输出会偏。训练完成后我用测试集验证了一下在25度1C工况下EKF单独用的RMSE是1.8%融合后降到0.9%在0度2C工况下EKF单独用的RMSE是7.2%融合后降到2.5%。提升还是很明显的。4.4 在线估计的完整流程在线估计的时候EKF和神经网络是交替执行的。每个采样周期读取电压、电流、温度EKF做一步预测和更新得到SOC_ekf计算安时积分增量delta_ah组装神经网络输入特征神经网络前向计算得到delta_soc限幅后加到SOC_ekf上得到SOC_final把SOC_final写回EKF状态放大SOC对应的协方差输出SOC_final给上层系统。整个流程在STM32F407上跑EKF耗时约0.3ms神经网络前向计算约0.8ms总共1.1ms1秒的采样周期绰绰有余。如果换到算力更弱的MCU上可以把神经网络简化成单隐藏层或者用查表法近似。实操心得在线估计的时候神经网络的输入特征里SOC_ekf和delta_ah是最关键的两个。SOC_ekf提供了绝对位置信息delta_ah提供了相对变化信息。如果只给SOC_ekf不给delta_ah网络学不到安时积分的守恒关系输出会漂。5. 常见问题与排查技巧实录5.1 EKF发散怎么办EKF发散是最常见的问题表现是SOC估计值跑到0以下或者1以上或者剧烈震荡。排查思路先看OCV-SOC曲线有没有问题。如果曲线在某个SOC区间太平坦dOCV接近零H矩阵的SOC分量就接近零卡尔曼增益会变得很小滤波器不更新SOC就靠安时积分硬撑误差越积越大。磷酸铁锂在30%到70%区间就是这种平坦区这是它的材料特性决定的。解决办法是在平坦区加大Q的SOC分量让滤波器更信任观测。再看Q和R的比值。如果Q太小滤波器过度信任模型模型误差积累到一定程度就会发散。可以试着把Q放大10倍看看。还要检查电流方向定义。放电为正还是充电为正这个搞反了SOC变化方向就反了必发散。5.2 神经网络补偿量抖动大神经网络输出抖动通常是输入特征抖动引起的。电压和电流的测量噪声会直接传到网络输入导致输出波动。解决办法对网络输入做低通滤波截止频率设1Hz左右在损失函数里加平滑正则项惩罚相邻时刻delta_soc的差值对网络输出做滑动平均窗口长度5到10个采样点。我试过第一种和第三种效果都不错。第二种需要改训练代码稍微麻烦一点但效果最好。5.3 低温工况精度差低温下电池内阻急剧增大极化效应增强二阶RC模型的参数在常温下辨识的低温下完全不准。我的做法是分温度段建模-10度到10度一组参数10度到35度一组35度以上一组。每组参数单独辨识EKF根据当前温度切换参数。神经网络这边低温样本要单独加权让网络更关注低温工况的误差模式。我在损失函数里给低温样本乘了1.5的权重效果有改善。5.4 老化后精度下降电池循环几百次后容量衰减内阻增大OCV-SOC曲线也会偏移。EKF的模型参数如果不更新精度会逐渐下降。我的做法是每隔一定循环次数做一次参数重辨识或者用在线参数辨识算法如递推最小二乘实时更新R0和Cn。神经网络这边老化后的数据要加入训练集重新训练或者用迁移学习的方法在预训练网络的基础上用少量新数据微调。5.5 常见问题速查表问题现象可能原因排查方法解决措施SOC跑到0以下或1以上OCV曲线平坦区、Q/R比值不当检查dOCV值、残差序列加大Q的SOC分量、限幅SOC剧烈震荡R设太小、电流噪声大看残差方差加大R、电流滤波神经网络输出抖动输入噪声、无平滑约束看输入特征波形输入滤波、输出滑动平均低温精度差模型参数不匹配对比常温和低温参数分温度段建模老化后精度下降容量和内阻变化对比新旧OCV曲线参数重辨识、迁移学习融合后反而变差补偿量过大、写回策略不当看delta_soc幅值限幅、调整方差放大系数6. 工具选型与Matlab环境配置6.1 Matlab版本与工具箱选择这个项目用到的Matlab工具箱包括Deep Learning Toolbox搭神经网络Optimization Toolbox参数辨识时的最小二乘拟合Signal Processing Toolbox滤波和频谱分析Curve Fitting ToolboxOCV-SOC曲线拟合。版本建议R2020b以上因为feedforwardnet在R2020b之后有更新训练速度更快。如果只是跑EKFR2015b就够用了。安装的时候注意Deep Learning Toolbox需要单独勾选默认安装可能不包含。安装完成后用ver命令检查一下工具箱是否都装上了。6.2 代码组织与版本管理我的代码结构是这样的SOC_Estimation/ ├── data/ % 原始数据和预处理后的数据 ├── model/ % 电池模型参数和OCV曲线 ├── ekf/ % EKF相关函数 ├── nn/ % 神经网络训练和预测 ├── fusion/ % 融合策略 ├── test/ % 测试脚本 └── main.m % 主入口版本管理用Git每次改参数或者改网络结构都提交一次方便回溯。Matlab的mlx脚本文件不太适合Git我一般把关键代码写成m文件mlx只用来做数据探索和可视化。6.3 性能优化技巧Matlab的for循环比较慢EKF的递推如果写成for循环跑200万条数据要很久。我的做法是把EKF的递推向量化或者用Matlab Coder生成C代码再跑。向量化之后200万条数据大概3分钟能跑完。神经网络训练用GPU加速Matlab的train函数会自动检测GPU如果有NVIDIA显卡训练速度能快5到10倍。没有GPU的话用trainlm优化器也比trainscg快不少。注意Matlab的GPU支持需要Parallel Computing Toolbox而且只支持特定型号的NVIDIA显卡。买显卡之前先查一下Matlab的GPU支持列表别买了不支持的型号。7. 实测数据与效果对比7.1 常温工况对比在25度环境下用DST工况跑了一遍三种方法的RMSE对比方法RMSE最大误差收敛时间安时积分4.2%8.5%不收敛EKF1.8%3.6%120sEKFNN0.9%2.1%80s安时积分的误差是累积的跑得越久误差越大。EKF在初始阶段收敛后能稳定在2%以内。融合方案在EKF基础上进一步降低了一半的误差收敛也更快。7.2 低温工况对比在0度环境下用2C脉冲放电工况跑方法RMSE最大误差收敛时间安时积分5.8%11.2%不收敛EKF7.2%12.5%发散EKFNN2.5%4.8%150s低温下EKF反而发散了因为模型参数严重失配。融合方案靠神经网络补偿把误差拉回到可接受范围。这个结果让我意识到神经网络在极端工况下的价值比常温下更大。7.3 计算负载对比在STM32F407上跑主频168MHz方法单次耗时RAM占用Flash占用EKF0.3ms2KB8KBNN(6-20-10-1)0.8ms4KB12KB融合1.1ms5KB18KB融合方案的计算负载在可接受范围内1秒的采样周期完全够用。如果MCU资源紧张可以把神经网络压缩成6-10-1的结构耗时能降到0.4ms精度损失大约0.3%。8. 后续扩展方向这套方案目前是在Matlab上离线验证的下一步我打算把它移植到嵌入式平台上做在线测试。移植的时候要注意定点化的问题神经网络用浮点运算在MCU上跑比较慢可以试试用定点数或者查表法近似。另一个方向是把神经网络换成LSTM或者GRU这类循环神经网络对时序信息的捕捉能力更强理论上能进一步提升精度。但LSTM的计算量比前馈网络大不少在MCU上跑可能吃力需要做模型剪枝或者量化。还有一个思路是把EKF换成无迹卡尔曼滤波UKF或者粒子滤波这些非线性滤波方法不需要线性化对强非线性系统的估计精度更高。但计算量也更大需要权衡。我个人在实际操作中的体会是SOC估算没有银弹任何单一方法都有它的适用边界。EKF加神经网络的融合方案之所以有效是因为它把物理模型和数据驱动两种范式的优势结合起来了。但融合方案也不是万能的如果电池模型本身错得离谱或者训练数据覆盖的工况太窄融合效果也会打折扣。所以基础工作——准确的OCV-SOC曲线、合理的模型参数、覆盖充分的训练数据——永远是第一位的。