4. 数据采集与预处理:传感器数据采集、异常值检测与处理、缺失值填充、数据平滑与滤波
各位同学,咱们今天聊点实在的。做SOC估算,模型再漂亮,算法再高级,如果数据是垃圾,那结果就是垃圾。我见过太多项目,算法调参调了三个月,最后发现是传感器坏了,数据全是错的。所以,数据采集与预处理,是BMS机器学习项目的基石。
我个人习惯,把数据预处理分成四步走:先搞清楚数据怎么来的,再揪出那些捣乱的异常值,然后填上缺失的坑,最后把数据磨平了、磨顺了。下面咱们一个一个说。
核心观点:数据预处理不是可有可无的“清洁工”,而是决定模型上限的“建筑师”。你花80%的时间做数据清洗,一点都不亏。
4.1 传感器数据采集:源头决定质量
BMS里常用的传感器,无非就是电压、电流、温度这三样。电压用ADC采集,电流用霍尔传感器或分流器,温度用NTC热敏电阻。听起来简单,对吧?但坑都在细节里。
我记得有一次,项目现场反馈SOC跳变特别厉害。排查了三天,最后发现是电流传感器的采样频率太低,只有1Hz。你想想看,电动汽车急加速时电流变化多快?1Hz根本抓不住峰值。后来我们把采样频率提到100Hz,问题就解决了。
这里我给大家列个表,看看不同场景下推荐的采样频率:
| 应用场景 | 电压采样频率 | 电流采样频率 | 温度采样频率 |
|---|---|---|---|
| 储能电站(稳态) | 1 Hz | 10 Hz | 0.1 Hz |
| 电动汽车(动态) | 10 Hz | 100 Hz | 1 Hz |
| 消费电子(小电池) | 1 Hz | 10 Hz | 0.2 Hz |
另外,传感器本身也有误差。我建议在采集数据之前,先做一次标定。说白了,就是用标准源去校准传感器,把偏移量和增益误差记下来,后面在软件里补偿掉。
4.2 异常值检测与处理:揪出“坏孩子”
异常值,就是那些明显偏离正常范围的数据点。比如电压突然跳到100V,或者电流瞬间变成-5000A。这些数据如果不处理,模型会被它们带偏。
我常用的方法有三种:
- 3σ原则:假设数据服从正态分布,超过均值±3倍标准差的数据就是异常值。简单粗暴,适合快速筛查。
- 箱线图法:用四分位数(Q1和Q3)来定义异常值。低于Q1-1.5*IQR或高于Q3+1.5*IQR的数据,就是异常值。这个方法对非正态分布的数据更友好。
- 基于物理约束:比如锂电池电压不可能超过4.5V,电流不可能超过电池的额定倍率。这些硬性边界,比任何统计方法都可靠。
我曾经在一个项目中,用3σ原则检测异常值,结果把正常的高温数据也误杀了。后来改用物理约束+箱线图组合,效果就好多了。嗯,这里要注意:异常值检测不是越严格越好,要结合业务场景来定。
小技巧:对于时间序列数据,可以先用滑动窗口计算局部均值和标准差,再判断异常。这样能避免把正常的工况变化误判为异常。
处理异常值,我一般有两种策略:
- 直接删除:如果异常值数量很少(比如不到总数据的1%),直接删掉最省事。
- 替换为边界值:如果异常值比较多,或者你不想丢失数据,可以用正常范围的边界值(比如Q3+1.5*IQR)来替换。
4.3 缺失值填充:别让数据“断片”
传感器偶尔会掉线,或者通信丢包,导致数据出现空缺。缺失值如果不处理,很多机器学习算法会直接报错。
我常用的填充方法有:
- 前向填充:用上一个有效值填充缺失值。适合电压、温度这种变化缓慢的信号。
- 线性插值:用缺失点前后的两个有效值,做线性插值。适合电流这种变化相对平滑的信号。
- 均值填充:用整个序列的均值填充。这个方法比较粗糙,我一般只在数据缺失率极低(<0.1%)时才用。
- 模型预测填充:用KNN或回归模型预测缺失值。这个方法精度高,但计算量大,适合离线处理。
我个人习惯,对于BMS数据,优先用前向填充。为什么?因为电池的电压和温度不会突变,上一个时刻的值就是最合理的估计。我曾经试过用线性插值填充电压缺失,结果在电池快速放电时,插值出来的电压反而比实际值高,导致SOC估算偏大。后来改回前向填充,问题就解决了。
避坑指南:千万不要用全局均值填充!电池的电压和电流是时变的,用全局均值会引入巨大的偏差。我曾经见过一个项目,用均值填充了10%的缺失数据,结果模型精度直接掉了5%。
4.4 数据平滑与滤波:让数据“顺滑”起来
原始数据里总有噪声,比如电磁干扰、量化误差。这些噪声会让SOC估算结果忽高忽低,看起来很不专业。所以,我们需要对数据做平滑处理。
我常用的两种方法:移动平均和中值滤波。
4.4.1 移动平均
移动平均,说白了就是取一个窗口,把窗口内的数据求平均。窗口滑动,平均值也跟着变。公式很简单:
y[t] = (x[t] + x[t-1] + ... + x[t-N+1]) / N
其中N是窗口大小。N越大,平滑效果越好,但信号延迟也越大。我一般把N设为5到20之间,具体看采样频率和噪声水平。
举个例子,假设我们有一组电流数据,用Python实现移动平均:
import numpy as np
def moving_average(data, window_size):
return np.convolve(data, np.ones(window_size)/window_size, mode='valid')
# 示例数据
current = [10.2, 10.5, 10.1, 9.8, 10.3, 10.0, 9.9, 10.4]
smoothed = moving_average(current, 3)
print(smoothed) # 输出: [10.27, 10.13, 10.07, 10.03, 10.07, 10.10]
移动平均的优点是计算快,适合实时处理。缺点是它对异常值很敏感,一个大的噪声点会把整个窗口的平均值拉偏。
4.4.2 中值滤波
中值滤波,就是取窗口内的中位数,而不是平均值。它对异常值有天然的鲁棒性。比如窗口内有数据[10, 12, 100],平均值是40.7,但中位数是12。显然,中位数更能代表真实值。
我建议,如果数据里偶尔有尖峰噪声(比如电磁干扰导致的瞬间跳变),用中值滤波效果更好。如果噪声是高斯白噪声(比如ADC量化误差),用移动平均更合适。
下面是一个中值滤波的Python示例:
from scipy.signal import medfilt
# 示例数据,包含一个异常值
voltage = [3.7, 3.71, 3.69, 4.2, 3.7, 3.68, 3.71]
filtered = medfilt(voltage, kernel_size=3)
print(filtered) # 输出: [3.7, 3.7, 3.71, 3.7, 3.7, 3.7, 3.71]
你看,那个4.2的异常值被直接滤掉了。中值滤波的缺点是计算量比移动平均大,而且会丢失一些细节信息。
我的经验:在实际项目中,我经常把移动平均和中值滤波结合起来用。先用中值滤波去掉尖峰噪声,再用移动平均平滑掉高频抖动。这样既能抗异常,又能保证平滑度。
4.5 本章知识体系总览
为了让大家更直观地理解本章的知识结构,我画了一张流程图。它展示了从原始传感器数据到可用训练数据的完整处理链路。
这张图把咱们今天讲的内容串起来了。从传感器采集开始,经过异常值检测、缺失值填充,再到平滑滤波,最后得到干净可用的训练数据。每一步都有多种方法可选,具体用哪个,得看你的数据特点和业务需求。
好了,数据预处理这块就讲到这里。记住一句话:数据质量决定了模型的天花板,算法只是去接近这个天花板。下一章咱们会聊特征工程,到时候这些预处理好的数据就能派上大用场了。