3、库仑计数法(CC):电流积分原理、采样率选择、初始SOC校准、累积误差问题
好,咱们接着聊。前面讲了开路电压法,说白了就是查表。但有个问题——电池在放电过程中,你总不能老让它停下来休息吧?电动牙刷用着用着突然停转,就为了测个电压,这用户体验也太差了。
所以,真正在嵌入式系统里用得最多的,其实是库仑计数法。英文叫 Coulomb Counting,简称 CC。我当年第一次接触这方法,是在一个智能穿戴项目上。那会儿电池续航老不准,用户投诉说「明明显示还有 20%,怎么刷着牙就没电了?」——嗯,后来我才意识到,问题就出在库仑计数的实现细节上。
3.1 电流积分原理:说白了就是「数电子」
库仑计数的核心思想很简单:电池的电量,等于电流对时间的积分。
你想想看,电池放电,就是电子从负极跑到正极。每个电子带一个单位的电荷。如果我们能数清楚跑了多少个电子,那剩余电量不就知道了?
数学上,公式长这样:
SOC(t) = SOC(0) - (1 / Q_max) * ∫ I(t) dt
其中:
- SOC(t):当前时刻的剩余电量百分比
- SOC(0):初始电量百分比
- Q_max:电池总容量(单位 mAh)
- I(t):t 时刻的电流(放电为正,充电为负)
- ∫ I(t) dt:从 0 到 t 的电流积分,就是消耗的总电荷量
在单片机里,我们没法做连续积分。只能离散化处理:每隔一个固定时间 Δt,采样一次电流 I_k,然后累加。
SOC_k = SOC_{k-1} - (I_k * Δt) / Q_max
我习惯把这个过程叫做「数豆子」。每过 Δt 时间,就数一次电流,然后从总豆子里减掉。豆子数完了,电池就没电了。
关键点:库仑计数法本身没有「绝对参考」。它只知道相对变化,不知道绝对位置。所以初始 SOC 必须准,否则后面全偏。
3.2 采样率选择:不是越快越好
采样率怎么选?这是个好问题。我在项目里见过有人用 1kHz 采样,也有人用 1Hz。到底哪个对?
其实,采样率的选择取决于电流波形的变化速度。
电动牙刷的电流波形,我实测过。待机时几乎为零,启动瞬间有个冲击,然后进入平稳运行。平稳运行时电流波动不大,大概 ±5mA 以内。
那采样率怎么定?
- 过高(>100Hz):数据量太大,MCU 负担重,而且噪声也会被积分进去。我试过 1kHz 采样,结果积分出来的电量比实际多了 3%。
- 过低(<1Hz):会漏掉电流尖峰。比如启动瞬间的 200mA 冲击,如果采样间隔 1 秒,可能刚好没采到,那这部分电量就丢了。
- 推荐值:我个人习惯用 10Hz ~ 50Hz。既能捕捉到电流变化,又不会产生太多噪声。
我的经验:如果你用的是低功耗 MCU,采样率可以动态调整。牙刷运行时用 20Hz,待机时降到 1Hz。这样既省电,又不丢数据。
3.3 初始 SOC 校准:这是最容易被忽略的一步
库仑计数法最大的坑,就是初始 SOC 不准。
你想想看,如果一开始就告诉单片机「电池是 100%」,但实际只有 95%,那后面再怎么积分,结果都是偏高的。用户看到 20% 的电量,实际可能只剩 15%。
那怎么校准初始 SOC?
我常用的方法有三种:
- 开路电压法校准:牙刷静置 30 分钟以上,测开路电压,查表得到 SOC。这是最准的。
- 满充校准:当检测到充电完成(电压达到 4.2V,电流降到 0.01C),强制把 SOC 设为 100%。
- 空电校准:当电压降到保护阈值(比如 3.0V),强制把 SOC 设为 0%。
我建议三种方法结合使用。平时用库仑计数,每次充电完成或放电到底时,做一次硬校准。
注意:千万不要只依赖满充校准。有些用户可能从来不充满电,那你的 SOC 就永远校准不了。我遇到过这种情况,用户每天只充 20 分钟,结果 SOC 误差越来越大,最后显示 50% 时牙刷就罢工了。
3.4 累积误差问题:库仑计数的「阿喀琉斯之踵」
库仑计数法有个天生的毛病——误差会累积。
为什么会这样?
因为每次采样都有误差。电流传感器有偏移误差,ADC 有量化误差,时间基准有漂移误差。这些误差虽然小,但积分一次就放大一次。时间长了,误差就像滚雪球一样越来越大。
我举个例子:
假设电流采样误差是 ±1mA,采样间隔 0.1 秒。那么每秒钟的积分误差就是 ±10mAs。一天下来(86400 秒),误差就是 ±864000mAs,约等于 ±240mAh。对于一颗 800mAh 的电池,这相当于 30% 的误差!
是不是很吓人?
那怎么解决?
我总结了几条经验:
- 定期校准:每次充电完成或放电到底,强制重置 SOC。这是最有效的办法。
- 使用高精度电流传感器:比如 0.5% 精度的检测电阻,配合 16 位 ADC。误差能降到 0.1mA 级别。
- 温度补偿:电池容量 Q_max 会随温度变化。冬天容量下降,如果不补偿,积分结果会偏大。我习惯加一个温度查表,每 5°C 一个档位。
- 自放电补偿:牙刷待机时,电池也在缓慢放电。这个电流很小(几 μA),但积少成多。我一般每 10 分钟减掉一个固定值。
避坑指南:我曾经在一个项目里,只用了库仑计数法,没有做任何校准。结果三个月后,SOC 误差达到了 40%。用户反馈说「牙刷显示还有 60%,刷到一半就没电了」。后来我加了满充校准和开路电压校准,误差才降到 5% 以内。
3.5 实际代码示例:一个简单的库仑计数器
下面是一个在 STM32 上跑过的简化版代码。注意,这只是演示原理,实际产品里要加滤波和校准逻辑。
// 库仑计数器结构体
typedef struct {
float soc; // 当前 SOC,单位 %
float q_max; // 电池总容量,单位 mAh
float current_ma; // 当前电流,单位 mA
float dt_s; // 采样间隔,单位 s
float accumulated_charge; // 累积消耗电荷,单位 mAh
} CoulombCounter;
// 初始化
void CC_Init(CoulombCounter *cc, float init_soc, float q_max) {
cc->soc = init_soc;
cc->q_max = q_max;
cc->accumulated_charge = 0.0f;
}
// 每次采样时调用
void CC_Update(CoulombCounter *cc, float current_ma) {
// 计算本次采样消耗的电荷量(单位 mAh)
float dq = current_ma * (cc->dt_s / 3600.0f);
cc->accumulated_charge += dq;
// 更新 SOC
cc->soc = cc->soc - (dq / cc->q_max) * 100.0f;
// 限幅
if (cc->soc < 0.0f) cc->soc = 0.0f;
if (cc->soc > 100.0f) cc->soc = 100.0f;
}
// 校准函数:满充时调用
void CC_CalibrateFull(CoulombCounter *cc) {
cc->soc = 100.0f;
cc->accumulated_charge = 0.0f;
}
提示:实际项目中,我还会加一个「低通滤波器」来平滑电流采样值。不然每次 ADC 的噪声都会积分进去,误差会更大。
3.6 小结:库仑计数法的优缺点
| 优点 | 缺点 |
|---|---|
| 实时性好,能动态跟踪电量变化 | 误差会累积,需要定期校准 |
| 实现简单,计算量小 | 初始 SOC 必须准确 |
| 适合动态负载场景(如电动牙刷) | 受温度、老化影响较大 |
| 可以精确到 mAh 级别 | 需要高精度电流传感器 |
嗯,库仑计数法就讲到这里。说白了,它是个好方法,但你不能完全信任它。要配合校准机制,才能做出靠谱的电量显示。下一章咱们聊聊卡尔曼滤波——那玩意儿能把库仑计数和开路电压法的优点结合起来,精度能再上一个台阶。