4. SOC估算扩展卡尔曼滤波
任务定义
本案例实现一个用于电池SOC(荷电状态)估算的扩展卡尔曼滤波器(EKF)。系统输入为电池端电压与负载电流的离散采样序列,输出为动态SOC估算值及其不确定性(协方差)。采用一阶RC等效电路模型描述电池动态特性,通过EKF在线融合电压电流数据,解决安时积分法因电流传感器偏置和初始SOC误差导致的累积漂移问题。
电池模型与状态方程
选用一阶RC等效电路模型,其连续域状态方程与观测方程如下:
状态变量: x = [SOC, V_RC]^T
输入: u = I (负载电流, 放电为正)
状态方程:
SOC_dot = -η·I / Q_max
V_RC_dot = -V_RC/(R_CT·C_DL) + I/C_DL
观测方程:
V_t = OCV(SOC) - V_RC - I·R_0
其中Q_max为最大可用容量(Ah),η为库仑效率,R_0为欧姆内阻,R_CT为电荷转移电阻,C_DL为双电层电容。OCV(SOC)为开路电压与SOC的非线性映射关系,采用查表插值实现。EKF通过一阶泰勒展开对非线性OCV曲线线性化,实现状态递推与增益更新。
扩展卡尔曼滤波器实现
以下代码完整实现EKF核心算法,包含状态预测、协方差预测、卡尔曼增益计算、状态更新与协方差更新五个步骤。OCV-SOC曲线采用分段线性插值,其导数通过数值差分获得,保证雅可比矩阵计算的准确性。
#include <stdio.h>
#include <math.h>
#define N_STATE 2
#define N_MEAS 1
/* 电池参数 */
#define Q_MAX 2.0f /* 最大容量 Ah */
#define ETA 1.0f /* 库仑效率 */
#define R0 0.05f /* 欧姆内阻 Ω */
#define RCT 0.08f /* 电荷转移电阻 Ω */
#define CDL 1000.0f /* 双电层电容 F */
#define DT 1.0f /* 采样周期 s */
/* 过程噪声与测量噪声协方差 */
#define Q_NOISE 1e-5f
#define R_NOISE 1e-3f
/* OCV-SOC 查找表 (SOC: 0.0~1.0) */
static const float soc_pts[11] = {0.0f,0.1f,0.2f,0.3f,0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f};
static const float ocv_pts[11] = {3.20f,3.35f,3.45f,3.55f,3.63f,3.70f,3.78f,3.88f,4.00f,4.12f,4.20f};
/* 状态: x[0]=SOC, x[1]=V_RC */
static float x[N_STATE] = {0.5f, 0.0f}; /* 初始SOC=50% */
static float P[N_STATE][N_STATE] = {{0.1f,0.0f},{0.0f,0.1f}};
/* 线性插值查表 */
static float interp1(const float* xs, const float* ys, int n, float xq) {
if (xq <= xs[0]) return ys[0];
if (xq >= xs[n-1]) return ys[n-1];
for (int i = 0; i < n-1; i++) {
if (xq >= xs[i] && xq <= xs[i+1]) {
float t = (xq - xs[i]) / (xs[i+1] - xs[i]);
return ys[i] + t * (ys[i+1] - ys[i]);
}
}
return ys[n-1];
}
/* OCV及其导数 */
static float ocv(float soc) { return interp1(soc_pts, ocv_pts, 11, soc); }
static float docv_dsoc(float soc) {
float eps = 0.001f;
float soc_lo = fmaxf(0.0f, soc - eps);
float soc_hi = fminf(1.0f, soc + eps);
return (ocv(soc_hi) - ocv(soc_lo)) / (soc_hi - soc_lo);
}
/* 扩展卡尔曼滤波单步 */
void ekf_step(float v_meas, float i_meas) {
float dt = DT;
float tau = RCT * CDL;
/* --- 1. 状态预测 (先验) --- */
float x_pred[N_STATE];
x_pred[0] = x[0] - (ETA * i_meas / Q_MAX) * dt;
x_pred[1] = x[1] * expf(-dt/tau) + i_meas * RCT * (1.0f - expf(-dt/tau));
/* --- 2. 协方差预测 --- */
/* 雅可比矩阵 F = df/dx */
float F[N_STATE][N_STATE] = {
{1.0f, 0.0f},
{0.0f, expf(-dt/tau)}
};
float Qm[N_STATE][N_STATE] = {{Q_NOISE,0.0f},{0.0f,Q_NOISE}};
float P_pred[N_STATE][N_STATE] = {{0.0f}};
float tmp[N_STATE][N_STATE] = {{0.0f}};
/* F * P */
for (int i = 0; i < N_STATE; i++)
for (int j = 0; j < N_STATE; j++)
for (int k = 0; k < N_STATE; k++)
tmp[i][j] += F[i][k] * P[k][j];
/* F*P*F^T */
for (int i = 0; i < N_STATE; i++)
for (int j = 0; j < N_STATE; j++) {
P_pred[i][j] = 0.0f;
for (int k = 0; k < N_STATE; k++)
P_pred[i][j] += tmp[i][k] * F[j][k];
P_pred[i][j] += Qm[i][j];
}
/* --- 3. 卡尔曼增益 --- */
/* 观测雅可比 H = dh/dx = [dOCV/dSOC, -1] */
float H[N_MEAS][N_STATE] = {{docv_dsoc(x_pred[0]), -1.0f}};
float S = 0.0f;
float S_inv;
float K[N_STATE][N_MEAS] = {{0.0f}};
/* S = H*P_pred*H^T + R */
float HP[N_MEAS][N_STATE] = {{0.0f}};
for (int j = 0; j < N_STATE; j++)
for (int k = 0; k < N_STATE; k++)
HP[0][j] += H[0][k] * P_pred[k][j];
for (int j = 0; j < N_STATE; j++)
S += HP[0][j] * H[0][j];
S += R_NOISE;
S_inv = 1.0f / S;
/* K = P_pred * H^T * S^-1 */
for (int i = 0; i < N_STATE; i++)
for (int j = 0; j < N_MEAS; j++) {
float sum = 0.0f;
for (int k = 0; k < N_STATE; k++)
sum += P_pred[i][k] * H[j][k];
K[i][j] = sum * S_inv;
}
/* --- 4. 状态更新 (后验) --- */
float v_pred = ocv(x_pred[0]) - x_pred[1] - i_meas * R0;
float innov = v_meas - v_pred; /* 新息 */
x[0] = x_pred[0] + K[0][0] * innov;
x[1] = x_pred[1] + K[1][0] * innov;
/* SOC限幅 */
if (x[0] < 0.0f) x[0] = 0.0f;
if (x[0] > 1.0f) x[0] = 1.0f;
/* --- 5. 协方差更新 --- */
float I_KH[N_STATE][N_STATE] = {{1.0f,0.0f},{0.0f,1.0f}};
for (int i = 0; i < N_STATE; i++)
for (int j = 0; j < N_STATE; j++)
for (int k = 0; k < N_MEAS; k++)
I_KH[i][j] -= K[i][k] * H[k][j];
for (int i = 0; i < N_STATE; i++)
for (int j = 0; j < N_STATE; j++) {
P[i][j] = 0.0f;
for (int k = 0; k < N_STATE; k++)
P[i][j] += I_KH[i][k] * P_pred[k][j];
}
}
/* 测试主函数 */
int main() {
/* 模拟真实SOC从50%开始,恒定0.5C放电 (1A) */
float i_load = 1.0f;
float v_meas;
float true_soc = 0.5f;
printf("step true_soc ekf_soc voltage\n");
for (int t = 0; t < 20; t++) {
/* 真实电压: 用真实SOC计算OCV并加测量噪声(此处用确定性值) */
true_soc -= (ETA * i_load / Q_MAX) * DT;
if (true_soc < 0.0f) true_soc = 0.0f;
v_meas = ocv(true_soc) - i_load * R0; /* 忽略RC动态以简化验证 */
ekf_step(v_meas, i_load);
printf("%4d %8.4f %8.4f %.4f\n", t, true_soc, x[0], v_meas);
}
return 0;
}
EKF数据流与状态递推结构
编译与运行
将上述代码保存为ekf_soc.c,使用任意C99兼容编译器编译运行:
gcc -o ekf_soc ekf_soc.c -lm
./ekf_soc
验证方法与判定标准
程序输出20个仿真步的SOC估算结果。判定标准如下:
- 收敛性验证:初始SOC设为50%,真实SOC从50%开始以0.5C速率下降。EKF估算SOC应跟随真实SOC变化,稳态误差小于2%(即|SOC_ekf − SOC_true| < 0.02)。
- 新息合理性:卡尔曼增益K应保持有界且非负,协方差P对角元素单调递减并趋于稳定值,表明滤波器收敛。
- 预期输出特征:由于代码中真实电压未加入RC动态和测量噪声,EKF应快速收敛至真实SOC轨迹。第20步时,估算SOC与真实SOC之差应小于0.005(理想情况下接近0)。
若需验证滤波器对初始误差的修正能力,可将x[0]初始值改为0.7(70%),观察EKF是否能在数步内将估算值拉回真实SOC附近,这体现了EKF融合电压测量修正SOC漂移的核心能力。