4、数据采集与预处理:充放电数据采集、异常值处理、数据平滑与滤波
各位同学,欢迎来到第四讲。
前面我们聊了SOH的定义和基础理论,说实话,那些公式看着挺唬人。但真正到了工程落地,你会发现——数据才是爹。模型再漂亮,喂进去一堆垃圾数据,出来的结果就是一堆垃圾。我当年刚入行时,就吃过这个亏,辛辛苦苦调了一个月的模型,结果发现是采集板上的一个电阻虚焊了,数据全是毛刺。从那以后,我养成了一个习惯:拿到数据第一件事,不是建模,而是先看看数据本身干不干净。
这一章,我们就来聊聊数据采集与预处理。说白了,就是怎么把电池的「体检报告」拿到手,并且把上面的脏东西擦干净。
4.1 充放电数据采集:你得知道测什么
电池管理系统(BMS)里,我们能直接测到的物理量其实就三个:电压、电流、温度。但SOH估算需要的信息,全藏在这三个量随时间的变化曲线里。
采集什么?
- 电压:单体电压和总电压。采样频率建议至少1Hz,做OCV(开路电压)曲线时,需要静置足够长时间(通常2小时以上)。
- 电流:充放电电流。注意方向定义,我习惯用正数表示充电,负数表示放电。采样频率建议10Hz以上,因为电流变化快。
- 温度:电芯表面温度或极柱温度。温度变化慢,0.1Hz就够了,但位置要选对——别贴在散热片上。
- 时间戳:这个容易被忽略。没有时间轴,你后面做积分、做微分全白搭。
采集工况
我个人建议,做SOH标定时,至少采集以下几种工况的数据:
- 恒流恒压充电(CC-CV):这是最标准的充电协议,能清晰看到电压平台和容量拐点。
- 恒流放电:通常用1C或0.5C倍率,放到底。用来算实际容量。
- 动态工况(DST或UDDS):模拟真实驾驶场景。我遇到过有些电池,静态测试容量没问题,一跑动态工况就露馅。
- 静置阶段:充放电前后各静置30分钟以上,用来获取OCV。
核心原则:数据采集的「三同时」——同时刻、同频率、同精度。电压电流的时间戳必须对齐,否则你算出来的内阻和容量全是错的。
4.2 异常值处理:把「坏点」揪出来
数据采回来了,但里面肯定有脏数据。为什么?传感器漂移、通信丢包、电磁干扰、甚至接线端子松动,都会产生异常值。我见过最离谱的一次,一个电芯的电压读数突然跳到了100V,明显是ADC采样通道串扰了。
常见的异常值类型
| 类型 | 表现 | 原因 |
|---|---|---|
| 野点(Spike) | 单个数据点突然跳变,然后恢复 | 电磁干扰、ADC毛刺 |
| 死值(Stuck) | 连续多个点数值不变 | 传感器卡死、通信中断 |
| 漂移(Drift) | 数值缓慢偏离真实值 | 温度漂移、老化 |
| 缺失值(NaN) | 数据为空或无效 | 丢包、存储错误 |
怎么处理?
我常用的方法分三步走:
- 阈值过滤:设定物理上限。比如锂电池单体电压不会超过4.5V,低于2.5V。超出这个范围的,直接剔除。
- 差分检测:计算相邻点的差值。如果电压变化率超过0.5V/s(正常情况远小于这个值),判定为野点。我曾经用这个方法抓出过一个接触不良的故障。
- 插值填补:对于剔除的点和缺失值,用前后有效值的线性插值填补。注意,连续缺失超过5个点,就别插了,直接丢弃这段数据。
小技巧:不要一上来就用复杂的统计方法。先用最简单的3σ原则(拉依达准则)扫一遍,能干掉90%的明显异常。剩下的再用孤立森林或DBSCAN去处理。
4.3 数据平滑与滤波:把毛刺磨平
异常值处理完了,但数据还是「毛茸茸」的。尤其是电流信号,因为负载变化快,噪声很大。你想想看,如果直接用带毛刺的电流去做安时积分,容量误差会越积越大。
为什么要滤波?
说白了,我们要保留电池的真实动态特性,同时去掉高频噪声。电池本身是一个低通系统,电压电流的变化频率不会太高(通常低于1Hz)。所以,高频噪声就是我们要滤掉的东西。
常用的滤波方法
- 移动平均滤波(Moving Average):最简单,窗口大小选5~10个点。优点是快,缺点是会引入相位延迟。我在做实时SOH估算时不太用这个,因为延迟会让我错过容量拐点。
- 中值滤波(Median Filter):对野点特别有效。窗口大小选3或5。我习惯先做一次中值滤波,再做平滑,效果很好。
- 一阶低通滤波(RC滤波):这是BMS里最常用的。公式很简单:
y[n] = α * x[n] + (1-α) * y[n-1]。α越小,滤波越强,但响应越慢。我一般取α=0.1~0.3。 - Savitzky-Golay滤波:这个比较高级,能保留数据的形状(峰值、谷值)。适合处理电压曲线,因为电压的拐点对SOH估算很重要。不过计算量稍大,嵌入式上慎用。
代码示例:一阶低通滤波
def low_pass_filter(data, alpha=0.1):
"""
一阶低通滤波器
:param data: 原始数据列表
:param alpha: 滤波系数,0~1,越小越平滑
:return: 滤波后的数据
"""
filtered = [data[0]] # 初始值取第一个点
for i in range(1, len(data)):
# 核心公式:新值 = α * 当前测量值 + (1-α) * 上一次滤波值
y = alpha * data[i] + (1 - alpha) * filtered[-1]
filtered.append(y)
return filtered
# 举个栗子
raw_current = [10.2, 10.5, 11.8, 10.3, 9.9, 10.1] # 模拟电流数据
smooth_current = low_pass_filter(raw_current, alpha=0.2)
print(smooth_current)
# 输出:[10.2, 10.26, 10.568, 10.5144, 10.39152, 10.333216]
注意:滤波不是万能的。如果你发现滤波后的数据跟原始数据偏差太大,或者相位延迟导致你算出的容量不对,那就得调整参数了。我曾经在一个项目中,把α设成了0.01,结果滤波后的电压曲线几乎成了一条直线,完全看不出充放电平台——那叫一个惨。
4.4 实战经验:数据预处理流水线
好了,理论讲完了。我给大家总结一下,在实际项目中,我一般按这个顺序处理数据:
- 原始数据入库:先存一份原始数据,别动它。万一后面处理错了,还能回滚。
- 时间戳对齐:把电压、电流、温度按时间戳重采样到同一频率。我通常用线性插值重采样到1Hz。
- 异常值剔除:阈值过滤 + 差分检测,干掉野点和死值。
- 缺失值插补:线性插值填补,连续缺失超过5个点则丢弃该段。
- 滤波平滑:先中值滤波(窗口3),再一阶低通滤波(α=0.2)。
- 数据切片:按充放电循环切分成独立片段,每个片段包含一次完整的充放电过程。
嗯,这一步做完,你的数据才算「能用」。下一章,我们会拿着这些干净的数据,去提取SOH相关的特征——比如容量、内阻、OCV斜率。到时候你就知道,前面这些预处理工作有多值了。
一句话总结:数据预处理不是锦上添花,而是雪中送炭。你花在数据清洗上的每一分钟,都会在模型精度上得到回报。
好,今天就到这儿。有问题咱们评论区见。