ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

扩展卡尔曼滤波EKF实现锂离子电池SOC估计:模型、原理与Matlab代码

2026/10/1 12:06:28 拓冰建站 浏览量
扩展卡尔曼滤波EKF实现锂离子电池SOC估计:模型、原理与Matlab代码 在电池管理系统BMS的日常开发里SOCState of Charge电荷状态估计一直是个既基础又让人头疼的问题。它不像测电压电流那样直接读个传感器就行而是一个典型的“隐状态”问题——你永远没法拿万用表量出“还剩多少电”只能通过可测的电压、电流、温度去反推。我这些年做BMS相关的算法和仿真用过的估计方法从简单的安时积分、开路电压查表到卡尔曼滤波家族的各种变体都有涉及这篇就围绕基于扩展卡尔曼滤波器EKF的SOC估计方法把从模型建立、算法原理到Matlab代码实现的完整链路捋一遍代码部分会给出可以直接跑通的参考实现适合正在做课程设计、毕业设计或者刚入门电池管理算法想找个靠谱起点的朋友。先说清楚一个容易踩的认知误区EKF并不是什么神秘的高深算法它的核心思想其实就一句话——把非线性系统在当前工作点附近做泰勒展开只保留一阶项然后套用标准卡尔曼滤波的递推框架。电池的SOC估计之所以要用它是因为电池模型里那个开路电压OCV与SOC之间的关系曲线是强非线性的如果无视这个非线性直接上线性卡尔曼滤波结果会偏得离谱。EKF所做的本质上就是在这个非线性曲线上不断找切线用切线替代曲线来完成状态预测和更新。需要说明的是这篇文章涉及的Matlab代码是基于常见的二阶RC等效电路模型也有用一阶的差别在于极化环节的精度和对计算量的要求采用EKF进行SOC递推估计。文中的代码结构和参数均来自我在类似项目中的常用实践你拿到的原始工程可能模型参数不同但整个框架是通用的改一改矩阵维度和参数表就能适配。1. 项目整体设计与思路拆解1.1 为什么SOC估计非得绕开“直接测量”这条思路想理解EKF在这个场景里的价值得先从“为什么SOC不能直接测”说起。电池内部电化学反应的状态宏观上表现为端电压、充放电电流、温度、内阻等电气特征但这些特征和SOC之间不是一一对应的关系。同一个端电压值在大电流放电和静置状态下对应的SOC可能差出十几个百分点这就是所谓的“极化电压”在捣乱——电池在电流激励下内部浓差极化和电化学极化会让端电压偏离真实的平衡电势。安时积分法最简单直接对电流做时间积分再除以容量但它的两个致命缺陷是初值必须已知、积分误差会随时间和电流测量偏差不断累积。开路电压法倒是能绕过积分误差但要求电池必须静置足够久让端电压回落到平衡状态这在实际车载或储能场景下根本做不到。所以工程上真正可落地的思路只有一条找一个能同时利用电压、电流测量信息又能内部修正误差的状态观测算法卡尔曼滤波家族就是干这个的。1.2 EKF与标准KF、安时积分法、查表法的取舍逻辑做方案选型时很多人会纠结一个问题既然标准卡尔曼滤波计算量小、理论成熟为什么不直接用原因是标准KF的适用前提是系统状态方程和观测方程都是线性的。如果强行把OCV-SOC关系曲线线性化只在某个固定点展开那么当SOC运行到曲线的其他区段时线性化误差就会迅速增大滤波结果直接发散。EKF做的事情是每步都在当前估计点重新线性化相当于一条曲线被切成无数小段直线段每步都用最贴合当前位置的那一段来做递推精度和鲁棒性就都有了保障。和安时积分法相比EKF的关键优势在于它是一个闭环估计器每步都用实测端电压与模型预测端电压的残差来修正状态所以即使初值给得不准、电流传感器有偏置系统也会在若干步内把SOC估计值拉回真实值附近。和单纯的查表法相比EKF天然融合了电流对SOC的动态驱动作用能把静置和动态工况统一在一个框架里处理。1.3 适用场景与局限性说明EKF-SOC估计的适用范围非常宽动力电池、储能电池、消费电子电池都可以用前提是你能拿到足够好的OCV-SOC标定曲线和模型参数。它也有局限性我简要列出OCV曲线平台区磷酸铁锂中段SOC 30%-70%斜率很平此时电压对SOC的观测信息弱EKF的修正能力会减弱估计误差会变大。EKF对模型参数误差敏感如果电池老化导致容量和内阻漂移而模型参数没更新估计精度会下降。计算量虽然不大但在极低成本的MCU上跑递推矩阵运算时仍需注意浮点性能和存储空间的余量。2. 电池模型与参数辨识EKF估计的地基工程2.1 等效电路模型选型一阶RC还是二阶RCEKF的观测方程需要描述“SOC和端电压之间怎么映射”这就要靠等效电路模型。最常用的是戴维南Thevenin模型一个理想电压源OCV串联欧姆内阻再串联若干个RC并联网络来模拟极化效应。一阶RC模型结构最简单只有一个极化环节参数少、辨识容易但在动态电流工况下对电压响应的拟合精度稍差二阶RC模型多了一个RC环节把浓差极化和电化学极化分开描述精度更高但参数从3个变成5个辨识难度和计算量也上去了。我的建议是如果你的OCV曲线斜率较大比如三元锂一阶RC基本够用如果是磷酸铁锂或者你做的工况里大倍率脉冲频繁出现果断上二阶RC。下面这套代码按二阶RC模型展开但如果你用一阶把第二个RC环节的参数置零、状态维度从2降到1就行改动不大。2.2 状态方程与观测方程的建立连续时间下二阶RC模型的状态方程是这样的z_dot -eta * i / Cn Vp1_dot -Vp1 / (R1 * C1) i / C1 Vp2_dot -Vp2 / (R2 * C2) i / C2端电压观测方程Vt OCV(z) - i * R0 - Vp1 - Vp2这里z就是SOCeta是库仑效率充电时通常取1放电时约等于1具体按电池手册Cn是额定容量Ahi是电流放电为正R0是欧姆内阻R1/C1和R2/C2是两个极化环节的电阻和电容。把状态向量取为x [z, Vp1, Vp2]输入为u i输出为y Vt就得到了标准的非线性状态空间模型。2.3 离散化与参数辨识实操要点计算机实现必须离散化。设采样周期为Ts一阶欧拉离散化够用也可以用双线性变换但SOC估计的采样率通常1Hz左右欧拉法的精度已经足够。离散后的状态转移矩阵和输入矩阵如下以二阶RC为例z(k1) z(k) - (eta * Ts / Cn) * i(k) Vp1(k1) exp(-Ts / (R1C1)) * Vp1(k) R1 * (1 - exp(-Ts / (R1C1))) * i(k) Vp2(k1) exp(-Ts / (R2C2)) * Vp2(k) R2 * (1 - exp(-Ts / (R2C2))) * i(k)参数辨识这件事代码本身不会替你完成需要靠实验数据。常规流程是做HPPC混合脉冲功率特性测试得到不同SOC点的脉冲响应。从脉冲的瞬间压降读R0从恢复段的指数衰减曲线拟合R1/C1和R2/C2。用满充-静置-小倍率放电-静置的循环做OCV-SOC标定曲线。如果你手上没有实测数据先用文献里的典型锂离子电池参数也能把算法跑通后面再拿实测数据替换。下面是这段代码里我用的参考参数参数值说明Cn2.0 Ah额定容量R00.02 Ω欧姆内阻R10.03 Ω电化学极化电阻C12000 F电化学极化电容R20.05 Ω浓差极化电阻C25000 F浓差极化电容Ts1 s采样周期这些参数不是万能的不同电芯差异很大必须重新辨识。切记。3. 扩展卡尔曼滤波算法原理拆解3.1 从标准卡尔曼到EKF一句话概括的核心逻辑标准卡尔曼滤波解决的是线性系统的最优状态估计问题它的五个核心公式构成了完整的递推闭环状态预测、协方差预测、卡尔曼增益计算、状态更新、协方差更新。EKF的唯一区别在于系统是非线性的所以我们在每一步计算雅可比矩阵Jacobian把非线性函数在当前估计点线性化再代入标准卡尔曼的框架。用生活化的类比来说你在山路上开车标准KF相当于拿着一张直线道路的地图在导航遇到弯道就偏离EKF相当于每隔几米重新画一条当前弯道的切线来导航虽然每条切线只对附近一小段有效但不断更新切线整条山路就能跟住。电池的OCV-SOC曲线就是那条山路EKF就是那个不断画切线的导航员。3.2 EKF五个公式在SOC估计中的具体形态状态预测x_pred f(x_est, u)其中f就是2.3节里的离散状态方程把当前状态估计x_est和当前电流输入u代进去求出下一步的状态预测值x_pred。误差协方差预测P_pred A * P_est * A Q这里的A是状态方程对状态向量的雅可比矩阵。因为z的递推不依赖Vp1、Vp2Vp1和Vp2的递推是线性且互不耦合的所以A矩阵非常简单A [1, 0, 0; 0, exp(-Ts/(R1C1)), 0; 0, 0, exp(-Ts/(R2C2))]注意A是对角阵且SOC状态那一行与极化电压无关这背后是“SOC只受电流驱动与极化电压无关”的物理事实理解了这个矩阵就不会写错。卡尔曼增益计算K P_pred * H * (H * P_pred * H R)^(-1)这里的H是观测方程对状态向量的雅可比矩阵即端电压Vt对z、Vp1、Vp2的偏导数H [dOCV/dz, -1, -1]其中dOCV/dz是OCV-SOC曲线在当前SOC预测值处的斜率。这个斜率非常重要它决定了当前工作点下电压观测对SOC修正的“可信度”——平台区斜率小增益自动调低避免把电压噪声误当成SOC变化陡峭区斜率大增益自动调高修正力度更强。状态更新x_est x_pred K * (Vt_meas - Vt_pred)其中Vt_meas是实测端电压Vt_pred是用x_pred代入观测方程算出的预测端电压。两者的差值就是残差innovationK决定把多少残差分配到SOC和两个极化电压上。误差协方差更新P_est (I - K * H) * P_pred3.3 为什么这个流程能抑制安时积分的误差累积安时积分法的问题是纯开环的电流积分误差、初值误差都会原封不动地保留在结果里。EKF引入了“电压反馈闭环”这个关键机制如果SOC估计偏高模型预测的OCV就会偏高进而预测端电压Vt_pred偏高与实测值之间出现正残差卡尔曼增益K会把这个残差按权重折算成对SOC的负向修正把估计值拉回来。这一正一负的负反馈回路就是EKF能长期保持估计精度的根本原因。4. Matlab实现与核心代码解析4.1 代码整体结构与数据流我提供的参考代码按功能分为四个部分参数定义与初始化、OCV-SOC曲线插值函数、EKF递推主循环、结果可视化。数据流如下读取电流序列和端电压测量序列→初始化状态和协方差→逐时刻计算状态预测和观测预测→用实测电压残差修正→记录SOC估计值→与实际SOC对比评估误差。整个过程完全按3.2节的递推公式展开没有使用任何Matlab工具箱函数所以只要装了基础Matlab就能跑通。4.2 完整参考代码可直接复制运行%% 基于EKF的锂离子电池SOC估计 % 模型二阶RC等效电路模型 % 状态x [SOC; Vp1; Vp2] % 输入电流i放电为正端电压Vt测量 % 作者实践配置Matlab R2020a及以上版本均可运行 clear; clc; close all; %% 1. 电池模型参数 % 电芯基本参数 Cn 2.0; % 额定容量Ah eta 1.0; % 库仑效率 Ts 1; % 采样时间s % 二阶RC模型参数参考值需按实际电芯辨识 R0 0.02; % 欧姆内阻Ohm R1 0.03; % 电化学极化电阻Ohm C1 2000; % 电化学极化电容F R2 0.05; % 浓差极化电阻Ohm C2 5000; % 浓差极化电容F%% 2. OCV-SOC曲线数据查表用典型三元锂风格 SOC_points [0; 0.1; 0.2; 0.3; 0.4; 0.5; 0.6; 0.7; 0.8; 0.9; 1.0]; OCV_points [3.00; 3.35; 3.52; 3.65; 3.74; 3.82; 3.92; 4.03; 4.14; 4.23; 4.30]; % 用分段线性插值构造OCV-SOC函数及其导数 OCV_fun (soc) interp1(SOC_points, OCV_points, soc, linear, extrap); % 导数dOCV/dSOC用差分近似 dOCV_dSOC (soc) (OCV_fun(soc0.005) - OCV_fun(soc-0.005)) / 0.01;%% 3. EKF初始化 x_est [0.8; 0; 0]; % 状态初值[SOC; Vp1; Vp2]故意设得不准 P_est diag([0.1^2, 0.05^2, 0.05^2]); % 初始误差协方差矩阵 Q diag([1e-5, 1e-4, 1e-4]); % 过程噪声协方差调参重点 R 1e-3; % 观测噪声协方差电压测量噪声方差 % 预先分配存储数组 N 3600; % 仿真时长1小时 SOC_est zeros(N, 1); SOC_true zeros(N, 1); Vt_meas zeros(N, 1); Vt_pred_arr zeros(N, 1); Vp1_arr zeros(N, 1); Vp2_arr zeros(N, 1);%% 4. 仿真工况生成动态电流放电实测电压模拟 % 这里用真实系统会直接读取电流/电压传感器数据 % 为了验证算法先构建一个带噪声的仿真环境 t (0:N-1) * Ts; % 电流序列混合工况包含恒流、脉冲、静置 I zeros(N, 1); I(1:600) -1.0; % 1C放电 I(601:700) 0; % 静置 I(701:1000) -0.5; % 0.5C放电 I(1001:1100) 0; % 静置 I(1101:1500) -2.0; % 2C脉冲放电 I(1501:1700) 0; % 静置 I(1701:2200) -0.8; % 0.8C放电 I(2201:2300) 0; % 静置 I(2301:2600) -1.5; % 1.5C放电 I(2601:2800) 0; % 静置 I(2801:3600) -0.3; % 小电流放电 % 用状态方程递推“真实SOC”和“真实极化电压” SOC_true(1) 0.9; Vp1_true 0; Vp2_true 0; Vt_meas(1) OCV_fun(SOC_true(1)) - I(1)*R0 - Vp1_true - Vp2_true sqrt(R)*randn(); for k 2:N SOC_true(k) SOC_true(k-1) - eta*Ts/Cn * I(k-1); Vp1_true exp(-Ts/(R1*C1))*Vp1_true R1*(1-exp(-Ts/(R1*C1)))*I(k-1); Vp2_true exp(-Ts/(R2*C2))*Vp2_true R2*(1-exp(-Ts/(R2*C2)))*I(k-1); Vt_meas(k) OCV_fun(SOC_true(k)) - I(k)*R0 - Vp1_true - Vp2_true sqrt(R)*randn(); end%% 5. EKF主递推循环 for k 1:N % --- 状态预测 --- SOC_pred x_est(1) - eta*Ts/Cn * I(k); Vp1_pred exp(-Ts/(R1*C1))*x_est(2) R1*(1-exp(-Ts/(R1*C1)))*I(k); Vp2_pred exp(-Ts/(R2*C2))*x_est(3) R2*(1-exp(-Ts/(R2*C2)))*I(k); x_pred [SOC_pred; Vp1_pred; Vp2_pred]; % --- 雅可比矩阵 --- A [1, 0, 0; 0, exp(-Ts/(R1*C1)), 0; 0, 0, exp(-Ts/(R2*C2))]; % --- 协方差预测 --- P_pred A * P_est * A Q; % --- 观测预测 --- Vt_pred OCV_fun(SOC_pred) - I(k)*R0 - Vp1_pred - Vp2_pred; % --- 观测雅可比矩阵 --- H [dOCV_dSOC(SOC_pred), -1, -1]; % --- 卡尔曼增益 --- K P_pred * H / (H * P_pred * H R); % --- 状态更新 --- innovation Vt_meas(k) - Vt_pred; x_est x_pred K * innovation; % --- 协方差更新 --- P_est (eye(3) - K * H) * P_pred; % --- 记录 --- SOC_est(k) x_est(1); Vp1_arr(k) x_est(2); Vp2_arr(k) x_est(3); Vt_pred_arr(k) Vt_pred; end%% 6. 结果可视化 figure(Name, SOC估计结果, Color, w); subplot(3,1,1); plot(t/60, SOC_true*100, k-, LineWidth, 1.5); hold on; plot(t/60, SOC_est*100, r--, LineWidth, 1.5); xlabel(时间 (min)); ylabel(SOC (%)); legend(真实SOC, EKF估计, Location, best); title(EKF SOC估计与真实值对比); grid on; subplot(3,1,2); plot(t/60, (SOC_est - SOC_true)*100, b-, LineWidth, 1.2); xlabel(时间 (min)); ylabel(SOC误差 (%)); title(SOC估计误差); grid on; subplot(3,1,3); plot(t/60, I, g-, LineWidth, 1.0); xlabel(时间 (min)); ylabel(电流 (A)); title(电流工况); grid on;4.3 代码里的几个关键细节这段代码里最容易被忽略的地方是初始化时的状态初值设置。我在第3节里故意把SOC初值设为0.8而真实值是0.9这模拟的是“不知道电池当前剩余电量”的场景。EKF能不能在几十秒内从0.8拉回到0.9附近直接检验的就是卡尔曼增益设计得合不合理、观测模型可不可靠。如果你把初值设成刚好等于真实值算法当然也能跑但就失去了验证闭环修正能力的机会。另一个关键是Q矩阵和R矩阵的量级关系。它们俩的比值相当于在“信任模型递推”和“信任电压测量”之间做权衡。Q设得大相当于告诉滤波器“模型不可靠多听电压的”K会变大修正快但容易把电压噪声的毛刺也当成状态变化Q设得小相当于认为模型很准K变小估计轨迹平滑但响应慢初值误差要很久才能消除。我在代码里给的Q和R是经过调试的相对合理起点但换电芯、换工况后你还是得自己调。具体怎么调下一节详细说。5. 常见问题与排查技巧实录5.1 滤波发散表现、原因与处理如果你跑代码发现SOC估计值直接飞掉或者在一段剧烈波动后出现负值或超过100%十有八九是发了散。常见的几种触发原因H矩阵算错了。dOCV/dSOC的差分计算如果是在插值区间之外或者插值函数没做边界处理斜率会突然失真。我的建议是先用平滑曲线拟合OCV数据比如用多项式或样条拟合再解析求导比数值差分稳定得多。Q/R设置失衡。Q太小而R太大滤波器就几乎不修正误差会越积越大最终完全偏离。遇到这种情况先把R调小一个数量级试试看残差是否有反应。初值协方差P_est给得太小。P_est代表的是对初值的置信度给得越小说明你越相信初值。如果初值其实错得离谱又给了很小的P_est系统前期的修正量会被严重压制。工程经验是P_est初值宁可给大一个数量级让滤波器先快速收敛再慢慢收紧。5.2 SOC初值误差消除得慢怎么解决很多时候你会发现EKF确实在收敛但收敛速度慢得让人着急几百秒才从0.8爬到0.85。这通常是两个原因叠加一是当前SOC落在OCV平台区dOCV/dSOC接近0H矩阵里对应SOC的那一项没多大权重电压残差传递不到SOC修正上二是Q里SOC过程噪声设得太小限制了每步状态修正的步伐。解决思路有两个方向。方向一把Q(1,1)适当调大允许SOC每步可以有更大的修正幅度。方向二增加观测信息比如把温度、端电压变化率等辅助量加入观测方程或者改用双扩展卡尔曼滤波同时估计SOC和容量。我在实际项目中惯用的一个折中方案是启动阶段用较大的Q(1,1)等SOC误差收敛到2%以内再切换成较小的稳态值效果比全程恒定Q好很多。5.3 平台期SOC估计误差偏大的工程对策磷酸铁锂电芯的OCV曲线在中段非常平坦这会给所有基于电压观测的SOC估计方法带来一个先天性的难题电压上0.1%的变化对应SOC可能有好几个百分点。EKF在这种情况下会自动压低增益避免把电压噪声放大成SOC的大幅跳动但代价是估计误差确实比曲线陡峭区大。工程上的常用对策放这里集中列一下在平台期增加安时积分的权重靠电流积分跨过模糊区等SOC进入曲线陡峭区再用电压修正“校准”。引入电池温度修正因为平台区的OCV-SOC关系受温度影响更明显把温度项纳入观测模型能部分恢复可观测性。用多模型融合估计比如在EKF之外同时运行一个纯安时积分器按SOC区域动态加权融合两者的输出平台区信安时积分陡峭区信EKF。这种方式我在实车BMS项目里验证过效果比单一算法稳定不少。5.4 表格速查常见问题、可能原因与处理建议问题现象可能原因处理建议SOC估计发散数值异常H矩阵计算错误、Q/R比例失衡、A矩阵写错核对雅可比矩阵调大Q/R比P初值放大初值误差消除慢SOC处于OCV平台区、Q(1,1)过小启动阶段加大Q(1,1)或融合安时积分估计结果噪声大、抖动明显R设得太小、电压测量噪声大适当增大R或对电压测量做低通预滤波恒流工况下SOC偏移稳定但回不去模型容量Cn不准在线辨识容量或定期更新容量参数放电结束时SOC跳变OCV曲线在末端斜率突变、插值点稀疏增加SOC末端标定数据点密度增强平滑关于表格最后一条“放电结束时SOC跳变”我补充说一句OCV曲线的两端SOC接近0和接近1通常斜率很大插值点如果太稀疏差分会剧烈变化导致卡尔曼增益突变。解决方案是把OCV标定数据在两端加密集点同时考虑用平滑样条而不是线性插值来构造曲线能显著减少末端的毛刺。6. 个人实操体会与扩展建议做SOC估计这几年我的一个最深体会是算法本身的下限不低但真正决定上限的往往是模型参数的准确度。EKF给再好的框架模型参数不准输出也只能在错误的地基上跳舞。所以如果你打算把这套代码用到自己手头的电芯上第一优先级永远是花时间把HPPC实验做扎实、把OCV曲线标定清洗干净这比调Q和R的收益大得多。这套框架的扩展方向也很清晰往上走可以加自适应噪声协方差调节比如用新息序列的实际方差在线调整Q和R加双卡尔曼滤波同时估计SOC和SOH往前沿走可以对比无迹卡尔曼滤波UKF和粒子滤波PF它们在强非线性场景下比如极端低温、大倍率脉冲往往比EKF有更好的表现当然计算量也更大。我个人建议新手还是先把EKF吃透因为它的线性化思想是所有后续进阶算法的共同底层逻辑搞懂了EKF再去看UKF和PF就不会有认知障碍。最后提醒一句所有仿真算法移植到实车或实际BMS控制器时都要重新做一轮硬件在环测试和数据验证代码里的参数只能作为起点。祝你顺利跑通遇到问题欢迎回来对照这篇的排查表逐项检查。