第三章:数据采集与清洗——SCADA数据特点、采集方式与清洗方法
各位同学,咱们今天聊聊SCADA数据的“入口”问题。说实话,很多搞故障预警的同行,最后模型跑不动,十有八九是数据采集和清洗这关没过。我自己踩过不少坑,今天把这些经验掰开揉碎了讲给你听。
3.1 SCADA数据到底长什么样?
SCADA系统采集的数据,说白了就是工业现场的“体检报告”。它有几个非常鲜明的特点,我总结了一下:
- 高维且相关:一个风电机组,可能同时采集风速、转速、温度、振动、功率等上百个测点。这些变量之间往往高度耦合,比如风速和功率,你想想看,它们肯定不是独立的。
- 时序性强:数据是按时间戳排列的,前后依赖关系非常紧密。上一秒的转速,直接影响下一秒的状态。
- 噪声多且脏:传感器故障、通信中断、电磁干扰……现场环境远比实验室复杂。我见过一个项目,温度传感器因为被鸟粪糊住了,连续三天输出恒定的-40℃,你说这数据能用吗?
- 数据量大:一个中型风场,每秒可能产生上千条记录。一天下来就是几千万条,存储和计算压力都不小。
核心要点:SCADA数据是典型的“脏、乱、大”数据。处理它,不能像处理实验室数据那样“洁癖”,但也不能放任不管。关键是要找到那个平衡点。
3.2 数据采集方式:从现场到数据库
数据是怎么从传感器跑到我们电脑里的?这里我画了一张图,帮你理清整个链路。
实际项目中,采集方式主要有这么几种:
- OPC DA/UA:工业自动化领域最常用的协议。我习惯用OPC UA,因为它跨平台、安全性好。以前用OPC DA时,经常遇到DCOM配置问题,折腾半天连不上,后来换了UA,省心多了。
- Modbus TCP/RTU:老设备上很常见。优点是简单,缺点是传输效率低。如果你要采集几百个测点,Modbus RTU的轮询速度会让你抓狂。
- MQTT:适合物联网场景,轻量级,支持断线重连。我在一个分布式光伏项目里用过,效果不错。
- 文件导入:有些老旧系统不支持直接接口,只能导出CSV或Excel文件。嗯,这种情况虽然原始,但也是现实。
个人建议:如果条件允许,优先选择OPC UA或MQTT。它们对高频采集的支持更好,而且数据丢失率低。我曾经用OPC DA采集100个测点,1Hz频率,结果丢包率高达5%,后来换成UA,丢包率降到0.1%以下。
3.3 数据清洗:把脏数据“洗白”
数据采集回来了,但别急着用。SCADA数据有多脏?我举个例子:一个风速传感器,正常范围是0-30m/s,结果某天突然跳到了999.9。这种明显异常,不处理的话,模型直接崩掉。
数据清洗,我一般分三步走:
3.3.1 缺失值处理
SCADA数据缺失很常见。原因可能是通信中断、传感器故障、或者采集程序崩溃。处理方式有:
- 直接删除:如果缺失比例很小(比如<1%),直接删掉整行。简单粗暴,但有效。
- 前向填充:用上一个有效值填充。适合变化缓慢的变量,比如油温。我习惯用
ffill()。 - 插值:线性插值或样条插值。适合连续变化的变量,比如功率曲线。
- 模型预测:用其他相关变量预测缺失值。这个方法最复杂,但效果最好。我在处理振动数据时用过,因为振动值缺失后,简单的填充会破坏频谱特征。
# 一个简单的缺失值处理示例
import pandas as pd
import numpy as np
# 模拟SCADA数据
data = {
'timestamp': pd.date_range('2024-01-01', periods=100, freq='1min'),
'wind_speed': np.random.uniform(3, 25, 100),
'power': np.random.uniform(100, 2000, 100)
}
df = pd.DataFrame(data)
# 人为制造缺失值
df.loc[10:15, 'wind_speed'] = np.nan
df.loc[30:35, 'power'] = np.nan
# 前向填充风速
df['wind_speed'].fillna(method='ffill', inplace=True)
# 线性插值功率
df['power'].interpolate(method='linear', inplace=True)
print(f"缺失值处理完成,剩余缺失数:{df.isnull().sum().sum()}")
3.3.2 异常值检测
异常值分两种:一种是明显超出物理范围的(比如风速999),另一种是统计意义上的离群点。
我常用的方法:
- 3σ原则:假设数据服从正态分布,超过均值±3倍标准差的值视为异常。简单,但容易误判。
- IQR方法:四分位距法,对非正态分布更鲁棒。我比较推荐这个。
- DBSCAN聚类:基于密度的聚类,能发现局部异常点。适合处理振动数据中的冲击信号。
注意:千万不要一刀切地删除所有异常值!有些异常其实是故障的前兆。比如振动值突然升高,可能是轴承磨损的早期信号。我曾经犯过这个错,把真正的故障征兆当噪声删掉了,结果模型完全失效。
3.3.3 重复值与时间对齐
SCADA系统有时会重复记录同一条数据,或者时间戳错位。处理方式:
- 去重:按时间戳去重,保留第一条或最后一条。
- 重采样:将不规则的时间序列重采样为固定频率(比如1分钟、10分钟)。我习惯用
resample()配合均值或中位数聚合。
# 重采样示例:将1分钟数据聚合为10分钟均值
df.set_index('timestamp', inplace=True)
df_resampled = df.resample('10min').mean()
print(f"重采样前:{len(df)}条,重采样后:{len(df_resampled)}条")
3.4 清洗流程总结
说了这么多,我整理了一个清洗流程,你可以直接套用:
| 步骤 | 操作 | 适用场景 | 注意事项 |
|---|---|---|---|
| 1 | 缺失值处理 | 所有数据 | 缺失率>20%时,考虑删除该测点 |
| 2 | 异常值检测 | 连续变量 | 保留疑似故障的异常值 |
| 3 | 重复值去重 | 所有数据 | 按时间戳+测点ID联合去重 |
| 4 | 时间对齐 | 多源数据 | 统一时区,避免时间漂移 |
| 5 | 数据标准化 | 模型输入 | Z-score或Min-Max归一化 |
核心原则:清洗的目的是“去伪存真”,而不是“过度加工”。保留数据的原始特征,尤其是那些可能预示故障的细微变化。记住,我们做的是故障预警,不是数据美化。
好了,数据采集和清洗这部分就讲到这里。下一章咱们聊聊特征工程,怎么从清洗后的数据里挖出真正有用的信息。到时候我会分享一个我实际用过的案例,保证干货满满。