电量计 -- 阻抗追踪算法原理:电池模型与动态阻抗估计

阻抗追踪算法原理:电池模型与动态阻抗估计

为什么需要阻抗追踪

电池内阻不是一个固定值。它随放电深度(DoD)、温度、老化状态动态变化,在满充和深放时明显增大,低温下急剧上升。如果使用静态查表来估计内阻,在工况变化时误差会迅速累积,直接影响SOC精度和剩余容量预测。

TI的Impedance Track(阻抗追踪)技术的核心思路是:在电池正常充放电过程中,利用实时的电压、电流、温度数据,动态反推电池内部阻抗参数,而非依赖出厂时标定的固定值。这使得电量计能够自适应电池老化和环境变化,始终保持高精度的SOC估计。

本文基于TI BQ41Z50的IT-DZT算法和我们的Python仿真实现,详细介绍阻抗追踪的原理。

电池等效电路模型

阻抗追踪采用二阶RC等效电路模型来描述电池的电气特性:

1
2
3
4
5
6
7
       Rs        R1        R2
OCV ---RRRR---+--RRRR--+--RRRR--+--- V_term
| | |
[C1] [C2] |
| | |
+---------+--------+
V_k1 V_k2

各元件的物理意义:

元件 物理意义 时间尺度
OCV 开路电压,反映电池真实电化学状态 静态
Rs 欧姆内阻(导线、电解液、接触电阻) 瞬时响应
R1/C1 电化学极化(电荷转移过程) 秒级
R2/C2 浓差极化(离子扩散过程) 分钟级

端电压由KVL方程描述:

$$V_{term} = OCV - I \times R_s - V_{k1} - V_{k2}$$

其中 $V_{k1}$、$V_{k2}$ 是RC网络上的电压(放电时为正值,表示电压降)。

参数表征体系

R_LF:低频阻抗基准

模型中所有电阻都由一个**基准值 R_LF(Low-Frequency Resistance)**派生。R_LF是在参考温度(10°C)下测量的总低频阻抗,随DoD变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
R_LF_Table = [
0.0586, # DoD=0.0 (满充,阻抗较大)
0.0464, # DoD=0.1
0.0386, # DoD=0.2
0.0352, # DoD=0.3
0.0341, # DoD=0.4 (最低点)
0.0361, # DoD=0.5
0.0367, # DoD=0.6
0.0386, # DoD=0.7
0.0428, # DoD=0.8
0.0512, # DoD=0.9
0.0593 # DoD=1.0 (深放,阻抗增大)
]

R_LF呈U型分布:满充和深放时阻抗高,中间SOC区域阻抗最低。

阻抗分配系数 gamma

R_LF被分配给三个电阻元件:

$$R_s = R_{LF} \times \gamma_{Rs}$$
$$R_1 = R_{LF} \times \gamma_{R1}$$
$$R_2 = R_{LF} \times \gamma_{R2}$$

约束条件:$\gamma_{Rs} + \gamma_{R1} + \gamma_{R2} = 1$

gamma系数随DoD变化,反映不同SOC下极化特性的差异。例如在DoD=0(满充)时,R1占总阻抗的74.6%,而Rs只占11%,说明满充状态下极化效应主导。

电容分配系数 beta

RC网络的电容由参考电容 $C_{ref}$ 和分配系数 $\beta_{Ck}$ 决定:

$$C_{ref} = \frac{Q_{max}}{OCV}$$
$$C_k = \beta_{Ck} \times C_{ref}$$

$C_{ref}$ 将电池容量和电压关联起来,使电容参数具有物理意义。

温度补偿

所有电阻参数都需要温度补偿。采用指数模型

$$R_k(T) = R_{LF} \times \gamma_{Rk} \times e^{\beta_{Rk} \times (T - T_{ref})}$$

其中 $\beta_{Rk}$ 是温度系数(典型值为负数,约-0.06~-0.10),表示温度升高时电阻降低。

温度系数分为两段:

  • 低温段(T < 10°C):系数较大,阻抗对温度更敏感
  • 高温段(T ≥ 10°C):系数较小,阻抗变化相对平缓

这种分段处理是因为电池在低温下的电化学动力学变化更剧烈。

核心算法:动态R_LF更新

问题定义

已知实时测量值($V_{term}$, $I$, $T$)和当前RC状态($V_{k1}$, $V_{k2}$),如何反推当前的 $R_{LF}$?

方程推导

从KVL方程出发:

$$V_{term} = OCV - I \times R_s - V_{k1} - V_{k2}$$

将所有电阻参数用 $R_{LF}$ 表达,并考虑RC网络的状态方程,可以推导出一个关于 $R_{LF}$ 的非线性方程:

$$term_1 - term_2 - term_3 = 0$$

其中:

$$term_1 = R_{LF}^2 \times \gamma_{Rs} \times I \times e^{\beta_{Rs} \times (T - T_{ref})}$$

$$term_2 = R_{LF} \times \left(OCV - V_{term} - \sum_{i=1}^{2}\left(V_{ki} + \frac{I \times \Delta t}{C_i}\right)\right)$$

$$term_3 = \Delta t \times \sum_{i=1}^{2} \frac{V_{ki}}{\gamma_{Ri} \times C_i \times e^{\beta_{Ri} \times (T - T_{ref})}}$$

转化为二次方程

整理为标准形式 $A \times R_{LF}^2 + B \times R_{LF} + C = 0$:

$$A = \gamma_{Rs} \times I \times e^{\beta_{Rs} \times \Delta T}$$

$$B = -\left(OCV - V_{term} - \sum\left(V_k + \frac{I \times \Delta t}{C_k}\right)\right)$$

$$C = -\Delta t \times \sum \frac{V_k}{\gamma_{Rk} \times C_k \times e^{\beta_{Rk} \times \Delta T}}$$

通过求根公式解出 $R_{LF}$,选择物理合理的正实数解。

Python实现

Python版本使用 scipy.fsolve 直接求解非线性方程:

1
2
3
4
5
6
7
8
9
10
11
def equation(R_LF):
term1 = R_LF**2 * gammaRs * I * np.exp(Rbs * (T - self.T_ref))
vk_sum = sum(self.V_k[1][i] + I * delta_T / C_k[i] for i in range(2))
term2 = R_LF * (OCV - V_term - vk_sum)
term3 = delta_T * sum(
self.V_k[1][i] / (gammaRk[i] * C_k[i] * np.exp(Rbk[i] * (T - self.T_ref)))
for i in range(2)
)
return term1 - term2 - term3

new_R_LF = fsolve(equation, self.R_LF, xtol=1e-10)[0]

注意 fsolve 以当前 $R_{LF}$ 作为初始猜测值,利用牛顿迭代法逐步逼近解。

RC网络状态更新

每个RC网络的电压遵循一阶微分方程的解析解:

$$V_k(t) = V_k(t-1) \times e^{-\Delta t / \tau_k} + R_k \times (1 - e^{-\Delta t / \tau_k}) \times I$$

其中时间常数 $\tau_k = R_k \times C_k$。

这个公式的物理含义:

  • 衰减项 $V_k(t-1) \times e^{-\Delta t / \tau_k}$:前一时刻的电容电压按指数衰减
  • 充电项 $R_k \times (1 - e^{-\Delta t / \tau_k}) \times I$:电流对电容的充电效应

为什么用解析解而非欧拉法?因为采样间隔(1秒)可能与RC时间常数(数秒~数十秒)同量级,简单的欧拉前向差分在 $\Delta t / \tau$ 接近1时会产生较大误差,而解析解在任意步长下都是精确的。

1
2
3
4
5
for k in range(2):
tau_k = R_k[k] * C_k[k]
exp_factor = np.exp(-delta_T / tau_k)
self.V_k[0][k] = self.V_k[1][k] * exp_factor + R_k[k] * (1 - exp_factor) * I
self.V_k[1][k] = self.V_k[0][k] # 保存为下一时刻的"前值"

OCV-SOC查表与插值

OCV(开路电压)是SOC的单调函数,通过预先标定的查找表实现转换。表中存储65个均匀分布的SOC点(0%~100%,步长约1.56%)对应的OCV值。

查找过程使用线性插值:

1
2
3
4
5
6
def lookup_OCV(self, DoD):
SOC = 1 - DoD
for i in range(len(soc_points) - 1):
if soc_points[i] >= SOC >= soc_points[i + 1]:
weight = (SOC - soc_points[i + 1]) / (soc_points[i] - soc_points[i + 1])
return ocv_values[i + 1] + weight * (ocv_values[i] - ocv_values[i + 1])

DoD(放电深度)与SOC的关系简单:$SOC = 1 - DoD$。DoD每次更新基于库仑计数:

$$DoD_{new} = DoD_{old} + \frac{I \times \Delta t}{Q_{max} \times 3600}$$

算法整体流程

1
2
3
4
5
6
7
8
9
10
11
flowchart TD
A["初始化: 加载R_LF表, OCV表, 温度系数"] --> B["查OCV表获取当前开路电压"]
B --> C["计算RC参数: R_k, C_k, R_s"]
C --> D{"|I| >= 10mA ?"}
D -->|是| E["求解非线性方程更新R_LF"]
D -->|否| F["保持R_LF不变"]
E --> G["更新RC网络电压状态"]
F --> G
G --> H["库仑计数更新DoD"]
H --> I["输出: SOC, R_LF, R_total, OCV"]
I --> B

算法每秒执行一次。只有在电流足够大(|I| > 10mA)时才更新R_LF,因为静置状态下KVL方程退化(电流趋零,R_LF无法从方程中分离)。

Python仿真验证

完整的仿真类 BatteryModel 封装了上述所有计算。核心 update_parameters 方法在每个时间步执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class BatteryModel:
def __init__(self, DoD_init=0.0, Q_max=6.55):
self.Q_max = Q_max
self.R_LF = self.R_LF_Table[round(DoD_init * 10)]
self.V_k = [[0, 0], [0, 0]]
self.T_ref = 10.0

def update_parameters(self, I, V_term, T, delta_T):
OCV = self.lookup_OCV(self.DoD)
R_k, C_k, R_s = self.calculate_RC_parameters(T)

if abs(I) >= 0.01:
# 求解非线性方程更新R_LF
new_R_LF = fsolve(equation, self.R_LF)[0]
if 0 < new_R_LF < 3.0:
self.R_LF = new_R_LF

# 更新RC状态
for k in range(2):
tau_k = R_k[k] * C_k[k]
exp_factor = np.exp(-delta_T / tau_k)
self.V_k[0][k] = self.V_k[1][k] * exp_factor + R_k[k] * (1 - exp_factor) * I

# 更新DoD
self.DoD += (I * delta_T / 3600.0) / self.Q_max

仿真以0.3A恒流放电、10°C环境温度运行,可以观察到:

  • R_LF随DoD增大呈U型变化
  • RC电压在放电开始时快速建立,随后缓慢增长
  • 端电压 = OCV - 所有电压降

总结

阻抗追踪的核心是将电池建模为二阶RC等效电路,通过一个基准阻抗R_LF和一套分配系数来参数化所有元件,再利用温度补偿的指数模型适应不同工况。算法的关键创新在于:从实时电压/电流数据反推R_LF,而非依赖静态查表,使得电量计能够自适应电池状态变化。

下一篇将介绍如何在没有浮点运算单元的MCU上实现这套算法,包括定点数运算、泰勒级数逼近指数函数、牛顿迭代求平方根等工程技巧。

系列文章

  • 本文:阻抗追踪算法原理 - 电池模型与动态阻抗估计
  • 下一篇:阻抗追踪的嵌入式实现 - 定点运算与牛顿迭代