3. 数据采集与预处理:数据源类型、数据清洗方法、异常值处理、数据归一化

做储能EMS,说白了就是跟数据打交道。我刚开始入行那会儿,以为算法牛逼就完事了。结果呢?数据一塌糊涂,算法再牛也白搭。这就像做饭——食材不新鲜,厨神来了也没辙。

所以这一章,咱们聊聊数据的前处理。嗯,这是所有算法的基础,也是我踩坑最多的地方。

3.1 数据源类型:你从哪儿搞数据?

储能系统的数据源,我归纳下来主要有三类。每一类都有自己的脾气,你得摸透了。

数据源类型 典型数据 采集频率 我遇到的坑
BMS(电池管理系统) 电压、电流、温度、SOC、SOH 100ms - 1s 通讯中断,数据直接卡死
PCS(储能变流器) 功率、频率、效率、运行状态 1s - 5s 功率值偶尔跳变,像抽风一样
电表/传感器 并网点功率、环境温度、湿度 1s - 15min 精度不够,小数点后两位就飘

我个人习惯,先把这些数据源按优先级排个序。BMS的数据最核心,PCS次之,电表数据用来做校准。你想想看,如果BMS的SOC都错了,后面所有的策略都是瞎搞。

我的小技巧: 每个数据源都加一个时间戳字段。别信设备自带的时钟,很多设备时间不同步。我一般用EMS服务器的时间统一打标,这样后期对齐数据时能省不少事。

3.2 数据清洗方法:脏数据怎么处理?

数据清洗,说白了就是「去伪存真」。我在项目里见过最离谱的情况——某个电芯电压连续3个小时纹丝不动,一看就是传感器坏了。这种数据不洗掉,算法会以为电池性能好得离谱。

常用的清洗方法,我列了四个:

  • 去重: 同一时间戳出现多条记录,保留第一条或最后一条。我建议保留最后一条,因为可能是重传数据,更接近真实值。
  • 补全: 时间戳缺失,用插值法补上。最简单的就是线性插值,前后两个有效值取平均。
  • 滤波: 高频噪声太多,用滑动平均或中值滤波。我习惯用中值滤波,对尖峰噪声的抑制效果更好。
  • 格式统一: 有的设备传的是字符串,有的是浮点数。统一转成float64,省得后面计算时报类型错误。

避坑指南: 我曾经在清洗时把正常数据也洗掉了。原因是滤波窗口设得太大,把一次真实的功率波动当成了噪声。后来我学乖了——先可视化看一眼数据分布,再动手清洗。

3.3 异常值处理:那些「离谱」的数据

异常值,就是那些明显不符合物理规律的数据。比如电池电压突然从3.2V跳到5.0V,或者SOC从50%瞬间变成100%。

我常用的异常值检测方法有三种:

  1. 阈值法: 设定上下限,超出就标记。比如锂电池电压正常范围是2.5V-4.2V,超出这个范围直接剔除。
  2. 3σ原则: 计算均值和标准差,超出均值±3倍标准差的数据视为异常。这个方法适合正态分布的数据,比如环境温度。
  3. IQR法: 四分位距法。用Q1-1.5*IQR和Q3+1.5*IQR作为边界。这个方法对偏态分布的数据更友好。

处理异常值,我一般分两步走:先标记,再处理。标记出来看看是不是传感器故障,如果是,直接剔除;如果是真实工况(比如大电流放电导致的电压骤降),那就保留。

注意: 千万别一刀切地把所有异常值都删掉。储能系统在极端工况下(比如过充、过放)的数据,恰恰是最宝贵的。删了这些数据,你的模型就学不到边界情况了。

3.4 数据归一化:让数据「站在同一起跑线」

归一化,说白了就是把不同量纲的数据拉到同一个尺度上。电压是伏特(3.2V),电流是安培(100A),温度是摄氏度(25°C)。如果不归一化,算法会天然地「偏爱」数值大的特征。

常用的归一化方法有两种:

方法 公式 适用场景
Min-Max归一化 x' = (x - min) / (max - min) 数据分布有明确边界,比如SOC(0-100%)
Z-score标准化 x' = (x - μ) / σ 数据分布未知,或有异常值存在

我个人习惯,对于BMS数据(电压、电流、温度)用Z-score标准化。因为这些数据经常有异常值,Min-Max会被异常值拉偏。对于SOC、SOH这种有明确物理意义的数据,用Min-Max归一化,保留原始比例关系。

下面是一个简单的Python代码示例,展示如何对电压和电流做Z-score标准化:

import numpy as np

# 假设这是采集到的电压和电流数据
voltage = np.array([3.2, 3.3, 3.1, 3.4, 3.2])
current = np.array([100, 102, 98, 105, 101])

# Z-score标准化
def z_score_normalize(data):
    mean = np.mean(data)
    std = np.std(data)
    return (data - mean) / std

voltage_norm = z_score_normalize(voltage)
current_norm = z_score_normalize(current)

print("标准化后的电压:", voltage_norm)
print("标准化后的电流:", current_norm)

嗯,这里要注意一点:归一化的参数(均值和标准差)一定要用训练集的数据计算,然后应用到验证集和测试集上。我见过有人把全部数据一起归一化,结果模型在线上部署时直接崩了——因为线上数据跟训练集的分布不一样。

3.5 本章知识体系

下面这张图,是我梳理的数据采集与预处理的核心逻辑。从数据源到最终可用的特征数据,每一步都不能少。

数据采集与预处理流程 数据源 BMS / PCS / 电表 数据清洗 去重 / 补全 / 滤波 异常值处理 阈值 / 3σ / IQR 数据归一化 Min-Max / Z-score 特征数据 可用于算法训练 注意:这是一个迭代过程,清洗后如果发现异常,需要返回重新处理 迭代优化

数据预处理这件事,看起来琐碎,但真的决定了算法的上限。我见过太多项目,算法模型选得再好,数据一塌糊涂,最后结果就是「垃圾进,垃圾出」。所以,别嫌麻烦,把数据搞干净了,后面的路就好走了。


专注资料整理