4. 电池建模:等效电路模型、RC模型参数辨识、模型验证

好,咱们进入电池建模这一章。

说实话,很多做智能手表的工程师,一上来就调算法参数。结果呢?电池状态估得乱七八糟。为什么?因为模型本身就不准。你想想看,地基没打好,上面盖多高的楼都没用。

我个人习惯,做任何电池管理算法之前,先花时间把模型搞明白。模型准了,后面的事情就顺了。

4.1 为什么要给电池建模?

电池不是理想电压源。它的电压会随着电流、温度、老化程度变化。你没法用一个简单的公式描述它。

建模的目的,说白了就是找一个数学表达式,能模拟电池的真实行为。有了这个模型,我们才能估算剩余电量、预测续航、做充放电控制。

我在项目中遇到过,有人直接用开路电压查表法估算电量。结果呢?动态负载下一塌糊涂。就是因为没考虑电池的极化效应和动态响应。

4.2 等效电路模型(ECM)

等效电路模型是最常用的电池模型。它用电阻、电容这些基本元件,搭一个电路来模拟电池特性。

4.2.1 最简单的Rint模型

Rint模型就一个理想电压源串联一个内阻。公式很简单:

V(t) = OCV(SOC) - I(t) * R0

其中OCV是开路电压,R0是内阻。这个模型太粗糙了,只能用在电流变化很小的场景。智能手表这种动态负载,千万别用。

4.2.2 一阶RC模型

这个就实用多了。它加了一个RC并联网络,用来模拟电池的极化效应。

V(t) = OCV(SOC) - I(t)*R0 - Vp(t)

dVp/dt = I(t)/Cp - Vp(t)/(Rp*Cp)

Vp就是极化电压。R0是欧姆内阻,Rp是极化内阻,Cp是极化电容。

嗯,这里要注意:一阶RC模型在大多数场景下够用了。我做过对比,二阶RC模型精度提升有限,但计算量翻倍。对于智能手表的MCU来说,一阶RC是性价比最高的选择。

4.2.3 二阶RC模型

如果你对精度要求特别高,可以用二阶。它有两个RC网络,分别模拟电化学极化和浓差极化。

V(t) = OCV(SOC) - I(t)*R0 - Vp1(t) - Vp2(t)

但说实话,我在手表项目里很少用二阶。算力有限,而且参数辨识难度大,容易过拟合。

4.3 RC模型参数辨识

模型搭好了,参数怎么来?这就是参数辨识要做的事。

4.3.1 辨识方法概述

参数辨识分两类:离线辨识和在线辨识。

  • 离线辨识:在实验室做特定测试,采集数据,然后用算法拟合参数。一次搞定,部署后不再更新。
  • 在线辨识:设备运行时实时更新参数。能适应老化,但计算量大。

智能手表我建议用离线辨识为主,在线微调为辅。为什么?手表算力有限,跑复杂的在线算法会拖慢系统。

4.3.2 脉冲放电测试法

这是最经典的离线辨识方法。流程如下:

  1. 把电池充到满电,静置1小时
  2. 以固定电流放电一段时间(比如10秒)
  3. 停止放电,静置40分钟
  4. 重复步骤2-3,直到电池放空

采集到的电压响应曲线,可以用来提取参数。

4.3.3 参数提取方法

看电压响应曲线:

  • 电流开始瞬间的电压跳变 → 欧姆内阻R0
  • 放电期间的电压缓慢下降 → 极化参数Rp、Cp
  • 静置期间的电压恢复 → 同样反映极化参数

具体计算可以用最小二乘法拟合。我贴一段Python代码,帮你理解:

import numpy as np
from scipy.optimize import curve_fit

# 定义一阶RC模型的电压响应函数
def rc_model(t, R0, Rp, Cp, OCV):
    # t: 时间数组
    # I: 电流(假设恒流)
    I = 0.5  # 放电电流 0.5A
    tau = Rp * Cp
    V = OCV - I*R0 - I*Rp*(1 - np.exp(-t/tau))
    return V

# 假设你采集了时间t和电压V数据
t_data = np.array([0, 1, 2, 3, 5, 8, 10])
V_data = np.array([4.15, 4.10, 4.08, 4.07, 4.06, 4.055, 4.05])

# 初始猜测
p0 = [0.1, 0.05, 1000, 4.2]

# 拟合
popt, pcov = curve_fit(rc_model, t_data, V_data, p0=p0)
R0, Rp, Cp, OCV = popt
print(f"R0={R0:.4f}Ω, Rp={Rp:.4f}Ω, Cp={Cp:.1f}F")
我的经验:做脉冲测试时,电流大小要接近实际使用场景。手表通常几十到几百毫安,你用1A电流测,参数会偏差很大。我曾经犯过这个错,后来老老实实按实际工况来。

4.3.4 不同SOC下的参数变化

电池参数不是常数。R0、Rp、Cp都会随SOC变化。所以你要在不同SOC点做测试,然后建立参数-SOC的查找表。

SOC (%) R0 (mΩ) Rp (mΩ) Cp (F)
100 45 30 1200
80 48 32 1150
60 52 35 1080
40 58 40 950
20 68 48 800
0 85 60 600
注意:不同温度下参数差异巨大。0℃时的内阻可能是25℃时的两倍。所以最好做温度-参数二维表。我见过有人只做常温标定,冬天手表续航直接崩了。

4.4 模型验证

参数辨识完了,怎么知道模型准不准?验证!

4.4.1 验证数据集

千万别用辨识用的数据来验证。那叫过拟合检验,没有意义。你要用独立的测试数据。

我建议准备三种测试工况:

  • 恒流放电:验证基本精度
  • 动态电流:模拟实际使用(比如看视频、刷网页)
  • 脉冲负载:模拟通信、GPS等突发大电流

4.4.2 误差指标

常用的指标有:

  • RMSE(均方根误差):反映整体偏差
  • MAE(平均绝对误差):直观易懂
  • 最大误差:看最坏情况

对于智能手表,我一般要求RMSE小于20mV,最大误差不超过50mV。超过这个范围,算法估计的电量就会不准。

4.4.3 验证结果分析

把模型预测的电压和实测电压画在一起对比。如果偏差有规律,说明模型结构有问题。如果偏差随机,可能是参数精度不够。

# 验证代码示例
import matplotlib.pyplot as plt

# 假设你有实测电压V_real和模型预测V_pred
time = np.arange(len(V_real))
error = V_real - V_pred

rmse = np.sqrt(np.mean(error**2))
mae = np.mean(np.abs(error))
max_err = np.max(np.abs(error))

print(f"RMSE: {rmse*1000:.1f}mV")
print(f"MAE: {mae*1000:.1f}mV")
print(f"Max Error: {max_err*1000:.1f}mV")

plt.figure(figsize=(10, 6))
plt.subplot(2, 1, 1)
plt.plot(time, V_real, label='Real')
plt.plot(time, V_pred, '--', label='Model')
plt.ylabel('Voltage (V)')
plt.legend()

plt.subplot(2, 1, 2)
plt.plot(time, error*1000)
plt.ylabel('Error (mV)')
plt.xlabel('Time (s)')
plt.show()
关键点:模型验证不是一次性的。电池老化后参数会变,建议每3-6个月重新标定一次。我在项目中遇到过,用了半年的模型,误差从15mV漂到了40mV。重新辨识参数后,又回到了18mV。

4.5 实战建议

最后,给你几个我踩过坑后的建议:

  • 别追求完美模型:一阶RC模型够用了。把精力放在参数辨识的准确性上。
  • 注意采样率:电压采样率至少10Hz,才能捕捉到电流跳变时的电压响应。
  • 考虑温度补偿:手表戴在手上,温度变化范围大。一定要做温度补偿。
  • 验证要全面:不同SOC、不同温度、不同负载都要测。我曾经只做了常温验证,结果低温下模型完全不能用。

好了,电池建模这部分就讲到这里。下一章我们聊聊如何用这个模型来做SOC估计。到时候你会发现,模型建好了,算法调起来就顺手多了。