第三章:数据采集与清洗——SCADA数据解析、传感器数据预处理、缺失值处理与异常检测实战
大家好,我是老张。在风电行业摸爬滚打了十几年,今天咱们聊聊数据采集与清洗。说实话,很多刚入行的朋友觉得算法模型才是核心,但我得说一句——数据质量不过关,再牛的模型也是白搭。我在项目里见过太多「垃圾进,垃圾出」的案例了。
这一章,咱们就聚焦在SCADA数据解析、传感器预处理、缺失值处理和异常检测这四个硬核环节上。嗯,都是实战中绕不开的坑。
3.1 SCADA数据解析——从原始报文到结构化数据
SCADA系统,说白了就是风电场的「黑匣子」。它每秒都在记录风速、转速、功率、温度等几十个参数。但原始数据长什么样?我给你们看个例子。
# 原始SCADA报文示例(CSV格式)
# 时间戳, 风速(m/s), 转速(rpm), 有功功率(kW), 桨距角(°), 机舱温度(℃)
2024-01-15 10:00:00, 12.3, 16.8, 1500, 2.1, 35.2
2024-01-15 10:00:01, 12.5, 17.0, 1520, 2.0, 35.3
2024-01-15 10:00:02, 12.1, 16.5, 1480, 2.2, 35.1
解析时要注意什么?我个人习惯先做三件事:
- 时间戳对齐:不同传感器可能采样频率不同,必须统一到同一时间基准
- 字段类型校验:风速应该是float,状态码应该是int,别混了
- 单位换算:有些老机组用m/s,新机组用km/h,统一成国际单位
我的小技巧:解析时先读前100行做「探针」,看看有没有隐藏的换行符或乱码。我曾经因为一个不可见字符,让整个解析程序跑了三天才发现问题。
3.2 传感器数据预处理——去噪与归一化
传感器数据,说白了就是「带噪声的真实信号」。风速传感器受湍流影响,振动传感器受机械共振干扰。怎么处理?
第一步:去噪
我常用的方法是滑动窗口平均和中值滤波。举个例子:
import numpy as np
from scipy.signal import medfilt
# 原始风速序列(含噪声)
wind_speed_raw = [12.3, 12.5, 11.8, 13.2, 12.1, 11.9, 12.4]
# 滑动窗口平均(窗口大小=3)
def moving_average(data, window=3):
return np.convolve(data, np.ones(window)/window, mode='valid')
# 中值滤波(窗口大小=3)
wind_speed_median = medfilt(wind_speed_raw, kernel_size=3)
print("原始数据:", wind_speed_raw)
print("滑动平均:", moving_average(wind_speed_raw))
print("中值滤波:", wind_speed_median)
第二步:归一化
不同传感器的量纲差异巨大——风速0-30m/s,温度-20-50℃,功率0-2000kW。不归一化,模型会「偏科」。我建议用Z-score标准化:
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
data_normalized = scaler.fit_transform(data_raw)
注意:归一化参数(均值和标准差)必须用训练集计算,然后应用到测试集。千万别用全局数据算,否则会引入未来信息,造成数据泄露。
3.3 缺失值处理——别让「空」毁了你的模型
SCADA数据缺失是常态。停机维护、通信中断、传感器故障,都会导致数据缺失。怎么处理?我分三种情况:
| 缺失类型 | 占比 | 推荐方法 | 我的经验 |
|---|---|---|---|
| 随机缺失 | <5% | 线性插值 | 简单有效,适合连续变量 |
| 连续缺失 | 5%-20% | 前向填充 + 后向填充 | 注意边界效应,别把停机数据补成运行数据 |
| 大量缺失 | >20% | 删除该特征或使用模型预测 | 我曾经用KNN填充,效果不错但计算量大 |
import pandas as pd
# 线性插值
df['风速'].interpolate(method='linear', inplace=True)
# 前向填充(最多填充3个连续缺失)
df['功率'].fillna(method='ffill', limit=3, inplace=True)
# 删除缺失超过20%的列
df.dropna(thresh=len(df)*0.8, axis=1, inplace=True)
避坑指南:我曾经在处理一个风场数据时,发现连续缺失了2小时的功率数据。用线性插值补上后,模型训练效果奇差。后来才发现——那2小时机组在停机维护,功率本来就是0。所以,缺失值处理前一定要先理解数据背后的物理含义。
3.4 异常检测实战——揪出「捣乱」的数据点
异常数据,说白了就是「不符合物理规律」的点。比如风速20m/s但功率只有100kW,或者转速突然跳到100rpm。这些点会严重干扰模型训练。
我常用的方法有三种:
- 基于统计的3σ原则:超出均值±3倍标准差的数据视为异常
- 基于物理约束:风速-功率曲线有理论边界,超出边界的就是异常
- 基于孤立森林:无监督学习,适合高维数据
from sklearn.ensemble import IsolationForest
# 构建孤立森林模型
iso_forest = IsolationForest(contamination=0.05, random_state=42)
anomaly_labels = iso_forest.fit_predict(data_scaled)
# 标记异常点(-1为异常,1为正常)
df['is_anomaly'] = anomaly_labels
df_clean = df[df['is_anomaly'] == 1]
我的经验:孤立森林对连续缺失后的数据效果会变差。所以我的流程是:先处理缺失值,再做异常检测。顺序别搞反了。
3.5 本章知识体系总览
下面这张图,是我自己总结的数据清洗全流程。你想想看,从原始SCADA报文到干净可用的训练数据,中间要经过多少道工序?
嗯,这一章的内容就到这里。数据清洗是个细致活,急不得。你想想看,一个风场一年产生几十亿条数据,每条数据都可能是「定时炸弹」。但只要你把流程走顺了,后面建模就是水到渠成的事。