最近刚全流程跟进了一个新能源甲醇增程船舶项目,整船全部正向开发,在联调的时候遇到了电池的问题,所以就顺道介绍下相关知识,欢迎大家共同探讨。
SOC(State of Charge,荷电状态)就是电池还剩多少电,定义成当前可用容量和额定容量的比值,数值落在 之间。BMS 里它几乎是"一等公民":续航显示、功率限制、过充过放保护全靠它。可麻烦在于,电池内部没有直接传感器能读出 SOC,你手上只有端子电压 、电流 和温度。SOC 只能从这几个量"反推"出来,是个典型的状态估计问题。
最朴素的反推是库仑积分:,把流进流出的电量累加。问题是电流传感器有零偏、有噪声,积分误差会随时间越滚越大,跑几千秒就能偏掉好几个百分点。要抑制这个漂移,就得把电压模型拉进来做校正——这正好是扩展卡尔曼滤波(EKF)的活:它一边用电流做库仑预测,一边用电压残差把预测值往真实值拽。
这篇文章不堆公式,也不靠 Simscape Battery 点几下鼠标出结果,而是用一个自写的二阶 RC 等效电路模型(ECM)造出带噪声的电压/电流"真值",再手搓一个 EKF 把 SOC 估回来。代码跑在 MATLAB 语法下,全部可复现,拷到任何一台机器上结果都一样。
一、二阶 RC 等效电路模型与参数
把电芯等效成"一个开路电压源 + 一串阻容网络"。一阶 RC 只够描述一种极化,二阶 RC 能分开快、慢两路极化,对脉冲、动态工况的拟合明显更好。本文用的电路是:
符号约定:表示放电。所以放电时极化压降和欧姆压降都从开路电压里减掉,电压随 SOC 下降而往下走。式中 是欧姆内阻上的瞬时压降; 是两个极化电压,分别对应快、慢两类电化学过程—— 来自电荷转移(电极/电解液界面的双电层),响应快、时间常数小; 来自固相扩散(锂离子在材料内部迁移),响应慢、时间常数大。把两路分开,脉冲放电后的电压"回弹"和长时弛豫就能同时被刻画。
两个 RC 支路各自是一个一阶低通。以支路 1 为例,连续域是 ,其中 。用零阶保持(ZOH)离散化到采样周期 后变成:
本文参数(额定容量 5 Ah,所以 1C = 5 A):
参数 | 真值(地面真值用) | EKF 用的(故意错一点) | 含义 |
|---|---|---|---|
5.0 Ah | 5.0 Ah | 容量 | |
0.020 Ω | 0.020 Ω | 欧姆内阻 | |
0.010 Ω / 1500 F | 0.010 Ω / 2200 F | 快极化,真值 s,滤波端 s | |
0.006 Ω / 150 F | 0.006 Ω / 110 F | 慢极化,真值 s,滤波端 s | |
1.0 s | 1.0 s | 采样周期 | |
0.05 A | — | 电流传感器噪声 | |
5 mV | — | 电压传感器噪声 |
注意一个细节:EKF 用的电阻和真值一致,但两个时间常数都偏了(一个 +47%,一个 −27%)。这模拟的是现实里最常见的情形——HPPC 脉冲辨识得到的参数本来就有误差,或者电芯已经轻度老化。电阻对了、时间常数错了,这种偏差在恒流下几乎看不出来,一到动态工况就露馅,正好用来拷问算法的鲁棒性。
开路电压 用一张单调查表表示(NMC 风格),SOC 越低电压越低。查表时Z必须升序,否则interp1会给出错误结果:
function v = ocv(z)
Z = [0 0.05 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.85 0.9 0.95 1];
V = [3.00 3.26 3.44 3.62 3.71 3.77 3.82 3.87 3.93 3.99 4.02 4.06 4.12 4.20];
v = interp1(Z, V, z, 'linear', 'extrap'); % Z 必须升序
end
造真值的 plant 就是按上面的差分方程一步步滚:
function [vt, z, V1, V2] = simECM(i, z0)
N = numel(i); z = zeros(N,1); V1 = z; V2 = z; vt = z;
zk = z0; x1 = 0; x2 = 0;
for k = 1:N
zk = zk - i(k)*1.0/(3600*5.0); % 库仑计数
x1 = A1*x1 + B1*i(k); % 快极化
x2 = A2*x2 + B2*i(k); % 慢极化
z(k)=zk; V1(k)=x1; V2(k)=x2;
vt(k) = ocv(zk) - 0.020*i(k) - x1 - x2;
end
end
二、造"地面真值":两种工况
真值不能等于干净曲线,否则看不出滤波器抗噪能力。真值电流要加 A 噪声,真值电压要加 mV 噪声——这就是"传感器看到的样子"。5 mV 看着小,但 OCV 曲线在 SOC 中段斜率只有约 0.6 V/单位 SOC,换算成 SOC 误差就是每 5 mV 对应约 0.8%,所以噪声直接决定了估计的下限。
工况 A:恒流 + 脉冲(CC-pulse)。一条 1C(5 A)恒流放电段,接一段静置让极化自然衰减,再叠 22 组"8 秒 2C 脉冲 + 12 秒休息",最后再来一段长恒流。总长 1100 秒。它的意义是专门把快/慢极化"激活"出来,看 EKF 能不能把 跟住:脉冲一来极化猛涨、电压下掉,脉冲一停极化慢慢泄掉、电压回弹。
工况 B:动态工况(DST / UDDS 风格)。用一段 36 步、每步 10 秒的电流模板反复平铺 12 遍,峰值 12 A(≈2.4C),还有 −5 A 的回馈制动(充电)。总长 4320 秒,电流一直在变向、变率。这才是整车实际跑出来的负载长相——市区走走停停、高速急加速、刹车回收。
构造思路(MATLAB 伪代码):
% 工况A:恒流+脉冲
cc = [repmat(5,150,1); repmat(0,70,1)];
for n = 1:22, cc = [cc; repmat(10,8,1); repmat(0,12,1)]; end
cc = [cc; repmat(0,60,1); repmat(5,300,1); repmat(0,80,1)];
% 工况B:DST风格模板平铺
DST = [0 0.8 0 -0.4 1.6 0 2.4 0 0.8 ... ]; % 单位 C,正负代表放/充
dyn = repmat(DST*5, 12, 1); % *5A 转成电流,平铺12圈
三、EKF 怎么估 SOC
状态向量取三个量:
是我们要的, 是两个极化电压,被一起估出来。预测步就是前面那套差分方程;更新步靠端电压残差把预测"拉回"测量值。
测量方程是
它对状态是非线性的(OCV 是查表),所以 EKF 在每一步用数值差分把斜率 线性化:
这个 是整套算法的命门:它告诉滤波器"电压偏差有多少是 SOC 引起的、有多少是极化引起的"。中段 SOC 斜率大,电压对 SOC 敏感,估计就准;两端 SOC 接近满/空时 OCV 变平、斜率趋零, 第一项塌掉,SOC 就几乎不可观测——这也是为什么满电和空电附近 SOC 最难估。
一个 EKF 步完整写成 MATLAB 函数:
function [x, P] = ekfStep(x, P, i, y, Q, R)
% 预测
xp = [ x(1) - i*1.0/(3600*5.0);
A1f*x(2) + B1f*i;
A2f*x(3) + B2f*i ];
F = diag([1, A1f, A2f]);
Pp = F*P*F' + Q;
% 线性化 OCV 斜率
s = (ocv(xp(1)+1e-4) - ocv(xp(1)-1e-4)) / 2e-4;
h = ocv(xp(1)) - R0f*i - xp(2) - xp(3);
H = [s, -1, -1];
% 更新
S = H*Pp*H' + R;
K = Pp*H'/S;
e = y - h;
x = xp + K*e;
x(1) = min(max(x(1),0),1); % SOC 限幅到 [0,1]
P = (eye(3) - K*H)*Pp;
end
注意预测步里的 是用滤波端参数算的,和真值不一致——这正是我们要测试的"模型不准"场景。卡尔曼增益 自动权衡"信模型还是信测量":模型协方差大、测量协方差小,增益就高,滤波器更听电压的话。
四、初始化与 Q/R 整定
EKF 启动最怕两件事:初值给错、噪声协方差给错。本文故意把初始 SOC 设成 ,而真值是 ,先白送它 0.3 的偏差,看滤波器多久能爬回来。这个设置很贴近真实上车:冷启动那一刻你根本不知道电池到底剩多少电。
协方差初值 要反映"我对初值有多不确定"。SOC 给 ±0.2、极化电压给 ±0.05/±0.04:
过程噪声 描述模型本身的不确定性。SOC 的库仑积分很准,所以 给极小;极化支路模型有误差,给大一点:
测量噪声 直接由电压传感器定: mV,所以
整定经验:先让 等于真实传感器方差,再调 。 太小滤波器过于自信、跟不上极化变化,动态工况下误差尖峰更高; 太大则把噪声当真、SOC 抖得厉害、稳态方差下不来。上面这组在两种工况下都能既快收敛又稳。
五、仿真结果:恒流很美,动态才见真章
先把工况 A 跑出来。初始 0.3 的 SOC 偏差,滤波器在大约91 秒内把估计值拉进 ±2% 带;进入稳态后,平均误差 0.21%,95 分位 0.43%,最大稳态误差不到 0.5%。把初值偏差吸收掉的那一下,瞬时峰值到过 4.4%。曲线整体很乖,误差带几乎贴着零线。
再看工况 B。同一套滤波器、同一个错初值、同一组噪声,稳态平均误差 0.10%,看起来比恒流还"干净"。但在吸收初始偏差的那段动态过程里,瞬时峰值冲到了8.1%——是恒流工况(4.4%)的将近两倍。而且在动态工况下,电流频繁变向变率,极化支路被反复拉扯,滤波器得一直和模型误差搏斗,误差分布明显更"毛",图上能看出一段段随电流跳变而甩出的尖刺。
读这两张图有个窍门:上半部分是 SOC 真值(虚线)和 EKF 估计(实线)几乎贴在一起,光看上半幅你甚至会觉得"两种工况都估得不错";真正的故事在下半幅的误差曲线里。CC-pulse 的误差很快压进 ±2% 且基本不反弹;动态工况的误差虽然均值也小,但会随每一次电流跳变甩出尖刺,这些尖刺才是算法在动态负载下"吃力"的证据。图里那条浅色带是滤波器自己给出的 置信区间,由协方差 开方得到——它代表滤波器"对自己有多不确定",区间越宽说明当前越难估。
为了把话说死,又补了一条"纯 5 A 恒流"的极端对照:它收敛后稳态误差只有 0.10%,曲线平得像尺子画的。这就是为什么很多人 Demo 里只用恒流——它永远漂亮,截图发出来谁看了都说好。
核心结论:恒流工况下 EKF 表现很好,但参考价值有限;动态工况才暴露算法的鲁棒性。具体数字摆在这:
纯恒流 5 A:稳态 ≤0.15%,曲线极其平稳;
CC-pulse:收敛 ~91 s,稳态均值 0.21%,峰值 4.4%;
动态 DST 风格:收敛 ~84 s,稳态均值 0.10%,但峰值8.1%(>2× 恒流)。
平均误差大家都小,可真实的 BMS 关心的是最坏情况:过放保护要是信了那 8% 的瞬时偏差,续航就会跳变、功率限制会误触发。恒流验证只给你一条好看的线,让你误以为算法"成了"。只有把动态工况甩上去,才能看见误差尖峰、看见滤波器在急加速和回馈制动间的手忙脚乱——那才是算法真实的鲁棒性。
六、为什么不用 Simscape Battery
Simscape Battery 当然能搭模型、出结果,但本文刻意不用,原因有三个。第一,可复现:Simscape 模型依赖工具箱版本和求解器设置,换台机器、换个版本结果就飘;自写 ECM + EKF 是纯数学,MATLAB 语法逐行对应 numpy 实现,任何人跑出来都是同一组数。第二,透明:每一个系数、每一步线性化你都看得见,调参时知道改的是哪根弦。第三,轻:一个.m文件、几百行,不占 license、不卡机器,适合嵌进 CI 做回归测试。教学和理解,手搓永远比拖模块来得实在。
七、温度与老化:参数会漂移
上面的"滤波端时间常数偏了"还不是最狠的。温度一降,电解液电导率掉、 全涨,OCV 曲线也整体挪动;循环老化后容量 缩水、内阻翻倍。这些漂移会让真值模型和滤波模型越离越远,动态工况下的误差尖峰会进一步抬高。实战里要定期用满充放空或脉冲辨识把参数"刷新"一遍,或者把温度、SOH 也并进状态向量一起估——那是下一篇的话题