3. SOC估算方法(下):卡尔曼滤波入门、扩展卡尔曼滤波(EKF)在SOC估算中的应用
各位工程师朋友,咱们接着聊SOC估算。上一节讲了安时积分和开路电压法,这两种方法各有各的毛病。安时积分会漂,开路电压法又不能实时用。那有没有一种方法,能把两者的优点结合起来?
有,就是卡尔曼滤波。说实话,我第一次接触这东西时也觉得头大,满屏的矩阵和协方差。但后来在项目中硬啃下来,发现它其实是个很优雅的“数据融合器”。今天我就带大家把它拆开揉碎了讲。
3.1 卡尔曼滤波:一个“会学习”的滤波器
卡尔曼滤波是什么?说白了,它就是个状态观测器。你给它一个带噪声的测量值,它就能给你一个更靠谱的估计值。
我习惯用一个比喻来理解它:
- 你有个朋友在开车,你坐在副驾。你闭着眼睛,根据上一秒的速度,猜他现在开到了哪里——这叫预测。
- 然后你睁眼看了一眼路边的里程碑——这叫测量。
- 你发现你猜的位置和里程碑差了50米。你会完全相信里程碑吗?不一定,因为里程碑可能被撞歪了。你会完全相信自己的猜测吗?也不一定,因为你刚才可能记错了速度。
- 于是你做了一个加权平均——这叫更新。
卡尔曼滤波干的就是这件事。它用两个核心公式来迭代:
# 预测步
x_pred = A * x_est + B * u
P_pred = A * P_est * A^T + Q
# 更新步
K = P_pred * H^T * (H * P_pred * H^T + R)^(-1)
x_est = x_pred + K * (z - H * x_pred)
P_est = (I - K * H) * P_pred
这里面的K叫做卡尔曼增益。它决定了你是更相信预测,还是更相信测量。Q是过程噪声协方差,R是测量噪声协方差。这两个参数怎么调?嗯,我在项目里吃过不少亏,后面会讲。
核心思想:卡尔曼滤波不是简单地取平均,而是根据当前的不确定性动态调整权重。不确定性越大,权重越小。
3.2 为什么SOC估算要用卡尔曼滤波?
你想想看,SOC估算本质上就是个状态估计问题。电池模型是非线性的,测量有噪声,初始SOC还不一定准。卡尔曼滤波正好能解决这些问题:
- 融合安时积分和电压测量:安时积分提供短期预测,电压测量提供长期修正。
- 自动校正初始误差:就算初始SOC设错了,滤波器也会慢慢收敛回来。
- 提供不确定性估计:不仅告诉你SOC是多少,还告诉你这个值有多可信。
我记得有一次做BMS项目,客户要求SOC误差在3%以内。用纯安时积分,跑两小时就漂到5%了。换成卡尔曼滤波后,误差稳稳控制在1.5%以内。效果立竿见影。
3.3 扩展卡尔曼滤波(EKF):对付非线性系统
标准的卡尔曼滤波要求系统是线性的。但电池模型呢?开路电压与SOC的关系是非线性的,RC网络的响应也是非线性的。怎么办?
EKF的思路很简单:把非线性函数在当前估计点做泰勒展开,只取一阶项。说白了,就是用切线来近似曲线。
具体来说,原来线性卡尔曼里的矩阵A和H,在EKF里变成了雅可比矩阵:
# 状态转移矩阵 A 的雅可比
A_k = ∂f/∂x | x = x_est
# 观测矩阵 H 的雅可比
H_k = ∂h/∂x | x = x_pred
这里f是状态转移函数,h是观测函数。对于电池模型,通常选择二阶RC模型:
# 状态向量: [SOC, V1, V2]^T
# V1, V2 是两个RC网络的电压
# 状态方程:
SOC_k+1 = SOC_k - (η * Δt / Q_n) * I_k
V1_k+1 = V1_k * exp(-Δt / τ1) + R1 * (1 - exp(-Δt / τ1)) * I_k
V2_k+1 = V2_k * exp(-Δt / τ2) + R2 * (1 - exp(-Δt / τ2)) * I_k
# 观测方程:
V_t = OCV(SOC_k) - V1_k - V2_k - R0 * I_k
这里面OCV(SOC)是非线性函数,需要求导。我一般用查表加线性插值,然后直接数值求导,省得推导解析表达式。
我的经验:EKF的雅可比矩阵不一定非要解析推导。数值求导(有限差分法)在大多数情况下够用,而且不容易出错。但要注意步长的选择,步长太大精度不够,步长太小容易受浮点误差影响。
3.4 EKF-SOC估算的完整流程
咱们把整个流程串起来。假设你已经在MATLAB或Python里实现了电池模型:
import numpy as np
class EKF_SOC:
def __init__(self, dt, Q_n, R0, R1, R2, tau1, tau2):
self.dt = dt
self.Q_n = Q_n # 标称容量
self.R0, self.R1, self.R2 = R0, R1, R2
self.tau1, self.tau2 = tau1, tau2
# 初始状态
self.x = np.array([0.5, 0.0, 0.0]) # [SOC, V1, V2]
self.P = np.eye(3) * 0.1 # 初始协方差
# 噪声协方差(需要调参)
self.Q = np.diag([1e-5, 1e-6, 1e-6])
self.R = np.array([[1e-3]])
def predict(self, I):
# 状态预测
SOC, V1, V2 = self.x
SOC_pred = SOC - (self.dt / self.Q_n) * I
V1_pred = V1 * np.exp(-self.dt / self.tau1) + self.R1 * (1 - np.exp(-self.dt / self.tau1)) * I
V2_pred = V2 * np.exp(-self.dt / self.tau2) + self.R2 * (1 - np.exp(-self.dt / self.tau2)) * I
self.x_pred = np.array([SOC_pred, V1_pred, V2_pred])
# 雅可比矩阵 A
A = np.array([
[1, 0, 0],
[0, np.exp(-self.dt / self.tau1), 0],
[0, 0, np.exp(-self.dt / self.tau2)]
])
# 协方差预测
self.P_pred = A @ self.P @ A.T + self.Q
def update(self, V_meas, I):
# 观测预测
SOC_pred = self.x_pred[0]
OCV = self.ocv_lookup(SOC_pred) # 查表函数
V_pred = OCV - self.x_pred[1] - self.x_pred[2] - self.R0 * I
# 雅可比矩阵 H(对SOC求导)
dOCV_dSOC = (self.ocv_lookup(SOC_pred + 0.01) - self.ocv_lookup(SOC_pred - 0.01)) / 0.02
H = np.array([[dOCV_dSOC, -1, -1]])
# 卡尔曼增益
S = H @ self.P_pred @ H.T + self.R
K = self.P_pred @ H.T @ np.linalg.inv(S)
# 状态更新
innovation = V_meas - V_pred
self.x = self.x_pred + K.flatten() * innovation
# 协方差更新
self.P = (np.eye(3) - K @ H) @ self.P_pred
return self.x[0] # 返回SOC估计值
注意:EKF对初始值敏感。如果初始SOC偏差太大(比如超过20%),滤波器可能收敛很慢甚至发散。我建议在系统上电时先用开路电压法粗估一个SOC,再交给EKF去微调。
3.5 调参经验与避坑指南
EKF最难的不是公式推导,而是调参。Q和R的取值直接影响滤波效果。我曾经在一个项目中,Q设得太小,滤波器几乎不相信测量值,结果SOC一直跟着安时积分漂。后来把Q调大了一个数量级,效果立刻好了。
这里分享几个经验:
| 参数 | 物理含义 | 调参建议 |
|---|---|---|
| Q(过程噪声) | 模型的不确定性 | 模型越不准,Q越大。一般从1e-5开始试 |
| R(测量噪声) | 传感器的噪声水平 | 根据电压采样精度定。12位ADC大概1e-3量级 |
| P0(初始协方差) | 对初始状态的信任度 | 初始SOC越不确定,P0越大。一般设0.1~1 |
还有一个坑:电池老化后参数会变。R0、R1、R2都会随着循环次数增加而变化。如果你用固定参数跑EKF,时间长了误差会越来越大。我建议每隔一段时间做一次参数辨识,或者用自适应EKF(AEKF)在线更新参数。
一句话总结:EKF把安时积分的“短期记忆”和开路电压法的“长期校准”结合在了一起。它不完美,但在工程实践中,是SOC估算最主流、最可靠的方法之一。
3.6 本章知识体系
下面这张图展示了EKF-SOC估算的核心逻辑,从模型建立到滤波迭代,再到参数调优,每一步都环环相扣:
好了,EKF在SOC估算中的应用就讲到这里。这套方法我在多个项目中验证过,只要参数调得好,精度完全能满足工程需求。下一节咱们聊聊更高级的无迹卡尔曼滤波(UKF)和粒子滤波,看看它们又是怎么解决非线性问题的。
公众号:蓝海资料掘金营,微信deep3321