第2章:卡尔曼滤波入门:状态空间模型,线性卡尔曼滤波(KF)推导,SOC估计中的KF应用

各位同学,欢迎来到第二章。

上一章我们聊了SOC的基础概念。说实话,那只是开胃菜。真正让SOC估计从「猜」变成「算」的,就是卡尔曼滤波。我当年刚接触BMS时,看到卡尔曼滤波的公式,第一反应是——这玩意儿能用在单片机上?后来真做出来了,才发现它没那么神秘。

2.1 状态空间模型:把电池装进数学盒子

要理解卡尔曼滤波,先得搞懂状态空间模型。说白了,就是把电池系统用数学语言描述出来。

一个典型的状态空间模型包含两个方程:

  • 状态方程:描述系统内部状态怎么随时间变化
  • 观测方程:描述我们能测量到的数据跟状态的关系

拿电池SOC估计举例。状态变量通常选SOC本身,有时候还会加上极化电压。我习惯把SOC作为唯一状态变量,这样计算量小,适合嵌入式实现。

状态方程长这样:

x(k) = x(k-1) + (η·i(k)·Δt)/Q

其中:

  • x(k) 是k时刻的SOC
  • η 是库仑效率
  • i(k) 是电流(放电为正)
  • Δt 是采样周期
  • Q 是电池容量

观测方程呢?我们测的是端电压:

y(k) = OCV(x(k)) + R₀·i(k) + v(k)

OCV是开路电压,跟SOC有非线性关系。R₀是内阻。v(k)是测量噪声。

我的经验:OCV-SOC曲线一定要实测,别用厂家给的。我在一个项目中吃过亏,厂家给的曲线偏差3%,结果SOC估计误差直接翻倍。

2.2 线性卡尔曼滤波推导:一步步拆解

线性卡尔曼滤波,说白了就是五个公式来回跑。我刚开始学的时候,觉得这五个公式长得差不多,容易搞混。后来我总结了一个记忆方法——「预测-更新」两步走。

第一步:预测(时间更新)

  1. 状态预测:x̂(k|k-1) = A·x̂(k-1|k-1) + B·u(k)
  2. 误差协方差预测:P(k|k-1) = A·P(k-1|k-1)·Aᵀ + Q

第二步:更新(测量更新)

  1. 卡尔曼增益:K(k) = P(k|k-1)·Hᵀ·(H·P(k|k-1)·Hᵀ + R)⁻¹
  2. 状态更新:x̂(k|k) = x̂(k|k-1) + K(k)·(y(k) - H·x̂(k|k-1))
  3. 误差协方差更新:P(k|k) = (I - K(k)·H)·P(k|k-1)

你想想看,这五个公式其实就干了一件事——用测量值修正预测值。卡尔曼增益K(k)就是修正的权重。测量噪声大,K就小,我更相信预测;过程噪声大,K就大,我更相信测量。

关键点:卡尔曼增益K(k)不是拍脑袋定的,它是根据噪声统计特性自动算出来的。这就是卡尔曼滤波比低通滤波高明的地方。

2.3 SOC估计中的KF应用:从理论到代码

好了,理论讲完了。咱们看看怎么把卡尔曼滤波用到SOC估计上。

首先,得把电池模型线性化。前面说了,OCV-SOC是非线性的。但如果我们只在小范围内变化,可以近似看成线性。这就是扩展卡尔曼滤波(EKF)的思路,不过那是下一章的内容。这一章,我们先假设OCV-SOC是线性的。

线性化后的模型参数:

参数 含义 取值
A 状态转移矩阵 1(SOC自回归)
B 控制输入矩阵 η·Δt/Q
H 观测矩阵 ∂OCV/∂SOC(OCV曲线斜率)
Q 过程噪声协方差 通常取1e-5 ~ 1e-3
R 测量噪声协方差 电压传感器方差,约1e-4

下面是一个简化版的C代码实现。我在实际项目中就是这么写的:

// 卡尔曼滤波SOC估计 - 简化版
typedef struct {
    float x;  // SOC估计值
    float P;  // 误差协方差
    float Q;  // 过程噪声
    float R;  // 测量噪声
    float H;  // 观测矩阵(OCV斜率)
} KalmanFilter;

void Kalman_Init(KalmanFilter *kf, float init_soc) {
    kf->x = init_soc;
    kf->P = 0.1f;  // 初始不确定性
    kf->Q = 1e-4f;
    kf->R = 1e-4f;
    kf->H = 0.8f;  // OCV斜率,实际需查表
}

float Kalman_Update(KalmanFilter *kf, float current, float voltage, float dt) {
    // 1. 预测
    float x_pred = kf->x + (current * dt) / 3600.0f;  // 安时积分
    float P_pred = kf->P + kf->Q;
    
    // 2. 计算卡尔曼增益
    float K = P_pred * kf->H / (kf->H * P_pred * kf->H + kf->R);
    
    // 3. 更新
    float voltage_pred = kf->H * x_pred;  // 预测电压
    kf->x = x_pred + K * (voltage - voltage_pred);
    kf->P = (1.0f - K * kf->H) * P_pred;
    
    return kf->x;
}
避坑指南:我曾经在一个项目中,Q和R设得不对,导致SOC估计值剧烈震荡。后来花了三天才找到原因——Q设得太大了,滤波器太相信测量值。记住:Q和R的比值决定了滤波器的行为,不是绝对值。

实际应用中,有几个细节要注意:

  • 初始化:初始SOC不准没关系,卡尔曼滤波会收敛。但P(0)要设大一点,表示我不信任初始值。
  • 饱和处理:SOC必须在0~100%之间。我习惯在更新后加一个限幅。
  • 噪声自适应:电流传感器在低电流时噪声大,可以动态调整R值。

嗯,说到这里,我想起一个案例。有一次做低温测试,-20℃下卡尔曼滤波发散得厉害。后来发现是OCV斜率变了,H矩阵没更新。从那以后,我都在代码里加了一个温度查表模块,根据温度动态调整H值。

最后,给大家一个建议:先用仿真验证,再上真电池。我习惯在MATLAB里搭好模型,跑一遍典型工况数据,看看收敛速度和稳态误差。没问题了再移植到嵌入式平台。这样能省很多调试时间。

下一章,我们会讨论扩展卡尔曼滤波(EKF),解决OCV-SOC非线性问题。到时候你会发现,线性KF是基础,EKF才是实战利器。