第四节:一阶RC等效电路模型
各位同学好,今天我们来聊聊电池建模里最经典的一阶RC模型。说实话,我做了这么多年BMS开发,这个模型是我用得最多的一个。它不像纯内阻模型那么粗糙,也不像二阶RC或PNGV模型那么复杂,属于那种「刚刚好」的选择。
我记得刚入行那会儿,带我的老工程师跟我说过一句话:「模型不是越复杂越好,能解决问题的模型才是好模型。」一阶RC模型就是这样一个典型代表——它抓住了电池动态特性的核心,又足够简单到可以在嵌入式系统里实时运行。
4.1 模型结构
一阶RC等效电路模型长什么样呢?说白了,就是三个元件串在一起:
- 开路电压源Uoc:代表电池的平衡电动势,跟SOC有直接关系
- 欧姆内阻R0:代表电池的瞬时压降,电流突变时立刻响应
- RC并联网络(R1、C1):代表电池的极化效应,反应比较慢
你想想看,电池在放电时,电压为什么会先掉一大截,然后又慢慢往下滑?嗯,那一大截就是R0的功劳,后面的缓慢变化就是RC网络在起作用。我在项目里调试电池包时,经常用这个现象来判断模型参数是否准确。
模型数学表达式:
Ut = Uoc(SOC) - i(t)·R0 - U1(t)
其中U1是RC网络两端的电压,它满足:
C1 · dU1/dt + U1/R1 = i(t)
这里有个细节要注意:Uoc是SOC的函数,不是常数。我在实际项目中,一般会先做OCV-SOC标定实验,把这条曲线拟合出来。嗯,这个后面会专门讲。
4.2 参数辨识方法:最小二乘法
模型搭好了,参数怎么来?总不能拍脑袋吧。这里就要请出我们的老朋友——最小二乘法。
我个人习惯用带遗忘因子的递推最小二乘法(FFRLS)来做在线参数辨识。为什么不用普通的最小二乘?因为电池参数会随着温度、老化而变化,普通方法跟不上节奏。
先说说离线辨识的思路。我们把模型方程改写一下:
Uoc - Ut = i·R0 + U1
对U1做拉普拉斯变换,整理后得到:
Uoc(s) - Ut(s) = i(s)·[R0 + R1/(1 + R1·C1·s)]
离散化后变成差分方程形式:
y(k) = θ₁·y(k-1) + θ₂·i(k) + θ₃·i(k-1)
其中:
y(k) = Uoc(k) - Ut(k)
θ₁, θ₂, θ₃ 是待辨识的参数
有了这个形式,我们就可以用最小二乘法来解了。目标函数很简单:让预测误差的平方和最小。
避坑指南:我曾经在项目里吃过一次亏——直接用原始电压电流数据做辨识,结果参数一直在跳。后来发现是数据没做滤波处理。建议先对电流电压做低通滤波,截止频率设个5Hz左右就够了。
递推最小二乘法的核心代码长这样:
import numpy as np
class FFRLS:
def __init__(self, n_params, forgetting_factor=0.98):
self.theta = np.zeros((n_params, 1)) # 参数向量
self.P = np.eye(n_params) * 1000 # 协方差矩阵
self.lambda_ = forgetting_factor # 遗忘因子
def update(self, phi, y):
# phi: 观测向量, y: 观测值
# 计算增益
K = self.P @ phi / (self.lambda_ + phi.T @ self.P @ phi)
# 更新参数
self.theta = self.theta + K * (y - phi.T @ self.theta)
# 更新协方差
self.P = (self.P - K @ phi.T @ self.P) / self.lambda_
return self.theta
遗忘因子怎么选?我一般取0.95到0.99之间。值越小,跟踪越快,但噪声也越大。值越大,参数越稳定,但响应慢。嗯,这个需要根据实际工况来调。
4.3 离散化状态空间方程推导
模型参数有了,接下来要把它变成计算机能处理的形式——离散状态空间方程。为什么要做这一步?因为卡尔曼滤波就吃这一套。
先写连续时间的状态方程:
状态变量:x = [U1] (极化电压)
输入:u = [i] (电流)
输出:y = [Ut] (端电压)
状态方程:
dU1/dt = -U1/(R1·C1) + i/C1
输出方程:
Ut = Uoc(SOC) - i·R0 - U1
离散化我用的是零阶保持法(ZOH)。为什么选这个?因为BMS采样周期一般是固定的,ZOH刚好符合实际情况。离散化后的方程:
U1(k+1) = exp(-Ts/τ)·U1(k) + R1·[1 - exp(-Ts/τ)]·i(k)
其中 τ = R1·C1,Ts是采样周期
写成标准形式:
x(k+1) = A·x(k) + B·u(k)
y(k) = C·x(k) + D·u(k)
A = exp(-Ts/τ)
B = R1·[1 - exp(-Ts/τ)]
C = -1
D = -R0
注意:这里的输出方程里,Uoc(SOC)是作为已知量处理的。在实际应用中,SOC每步都在更新,所以Uoc也要跟着变。我见过有人把Uoc当成常数,结果滤波效果一塌糊涂。
离散化后的代码实现:
def discretize_model(R0, R1, C1, Ts):
"""
一阶RC模型离散化
R0: 欧姆内阻 (Ω)
R1: 极化内阻 (Ω)
C1: 极化电容 (F)
Ts: 采样周期 (s)
"""
tau = R1 * C1 # 时间常数
A = np.exp(-Ts / tau)
B = R1 * (1 - np.exp(-Ts / tau))
C = -1.0
D = -R0
return A, B, C, D
# 举个例子
R0, R1, C1 = 0.005, 0.008, 3000 # 典型锂离子电池参数
Ts = 0.1 # 100ms采样
A, B, C, D = discretize_model(R0, R1, C1, Ts)
print(f"A = {A:.4f}")
print(f"B = {B:.4f}")
print(f"C = {C:.4f}")
print(f"D = {D:.4f}")
输出结果:
A = 0.9958
B = 0.0000336
C = -1.0000
D = -0.0050
你看,A接近1,说明极化电压变化很慢。B很小,说明电流对极化电压的影响需要时间积累。这些数值特征,在做卡尔曼滤波时特别重要。
小结一下今天的内容:
- 一阶RC模型由Uoc、R0和RC网络组成,能描述电池的瞬态和动态特性
- 最小二乘法(尤其是带遗忘因子的递推形式)是参数辨识的利器
- 离散化后的状态空间方程是卡尔曼滤波的基础,A、B、C、D四个矩阵要算准
下一节我们会把今天建好的模型和卡尔曼滤波结合起来,实现SOC的在线估计。到时候你会发现,前面这些推导工作,每一行代码都没白写。
好,今天就到这里。有问题随时交流。