数据采集与预处理:SCADA系统介绍、数据清洗、缺失值处理、异常值检测

各位同学,咱们今天聊聊风电功率预测里最基础、也最容易被忽视的一环——数据采集与预处理。

说实话,我见过太多项目,算法模型选得漂漂亮亮,结果一跑就崩。为什么?数据太脏。你想想看,风机天天在野外风吹日晒,传感器偶尔抽个风,通信线路断个几秒,这都是家常便饭。所以,数据预处理不是锦上添花,而是地基。

数据采集与预处理 SCADA系统介绍 数据清洗 缺失值处理 异常值检测 风速/风向/功率 10min采样间隔 重复/格式/噪声 插值/填充/删除 前向/后向/线性 3σ/IQR/箱线图 功率曲线校验

1. SCADA系统——风电场的数据神经

SCADA,全称是数据采集与监视控制系统。说白了,它就是风电场的"神经中枢"。每台风机上几十个传感器,把风速、风向、转速、桨距角、有功功率、无功功率……一股脑儿全传回来。

我个人习惯,拿到SCADA数据后,第一件事不是看算法,而是先看采样频率。大部分风场的SCADA系统是10分钟一个点,一天144个点。但有些老旧风场,采样间隔可能不固定,甚至跳变。我在内蒙古一个项目上就遇到过,某台风机下午3点到4点之间数据全丢了,原因是通信光缆被羊啃了……嗯,你没听错,羊啃的。

SCADA核心参数一览:

参数名称单位典型范围说明
平均风速m/s0~3010min平均
最大风速m/s0~5010min内极大值
有功功率kW0~额定功率核心预测目标
桨距角°0~90变桨控制用
发电机转速rpm0~2000双馈/直驱不同

2. 数据清洗——把"脏东西"筛出去

数据清洗,听着简单,做起来全是坑。我总结了三类最常见的"脏数据":

  • 重复数据:同一时间戳出现两条记录。原因可能是通信重传、数据库写入异常。处理方式:按时间戳去重,保留第一条或最后一条。
  • 格式问题:时间戳格式不统一,有的用"2024-01-15 10:00:00",有的用"2024/01/15 10:00"。数值列里混入字符串,比如风速字段出现"---"。
  • 噪声数据:传感器受电磁干扰,数值突然跳变。比如风速从5m/s瞬间跳到50m/s,然后又回来。

我的小技巧:清洗前先做一次数据概览。用 df.info() 看各列非空数量,用 df.describe() 看统计分布。如果某列最大值是风速100m/s,那基本可以断定有问题——17级台风也就60m/s左右。

import pandas as pd
import numpy as np

# 读取SCADA数据
df = pd.read_csv('wind_farm_scada.csv', parse_dates=['timestamp'])

# 去重:按时间戳去重,保留第一条
df = df.drop_duplicates(subset=['timestamp'], keep='first')

# 格式统一:确保数值列是float类型
for col in ['wind_speed', 'power', 'pitch_angle']:
    df[col] = pd.to_numeric(df[col], errors='coerce')

# 时间排序
df = df.sort_values('timestamp').reset_index(drop=True)

3. 缺失值处理——别让"空"毁了你的模型

缺失值,说白了就是数据里出现了"窟窿"。为什么会这样?传感器故障、通信中断、数据入库时异常……原因很多。我建议先看看缺失比例,再决定怎么补。

缺失比例与策略:

缺失比例推荐策略说明
< 5%插值填充线性插值或前向填充
5% ~ 20%插值 + 模型填充用相邻时间窗口均值,或用KNN
> 20%删除该段数据缺失太多,填充反而引入偏差

我个人最常用的是线性插值。原因很简单——风速和功率在短时间内是连续变化的,线性插值既简单又合理。但要注意,如果缺失段太长(比如连续缺失2小时以上),线性插值就不靠谱了。我曾经在甘肃一个风场,某台风机连续缺失了6个小时的数据,我试了各种插值方法,结果模型预测误差直接翻倍。最后狠心把那6小时数据全删了,效果反而更好。

# 线性插值填充缺失值
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')
df['power'] = df['power'].interpolate(method='linear')

# 前向填充(适用于短时间缺失)
df['pitch_angle'] = df['pitch_angle'].fillna(method='ffill')

# 删除缺失比例过高的列或行
df = df.dropna(thresh=len(df) * 0.8, axis=1)  # 删除缺失>20%的列

注意:千万不要在缺失值处理前直接训练模型。我曾经见过一个新手,数据里缺失了30%,他直接跑模型,结果模型学到的全是"怎么处理空值",而不是真正的功率预测规律。嗯,那叫一个惨。

4. 异常值检测——揪出"捣乱分子"

异常值,就是那些明显偏离正常范围的数据点。比如风速10m/s时,功率应该是额定功率的60%左右,结果你看到功率为0或者直接爆表。这种数据不处理,模型会被带偏。

我常用的方法有三种:

  • 3σ原则:假设数据服从正态分布,超出均值±3倍标准差的值视为异常。简单粗暴,但对非正态分布效果一般。
  • IQR方法:四分位距法。小于Q1-1.5×IQR或大于Q3+1.5×IQR的视为异常。对偏态分布更鲁棒。
  • 功率曲线校验:这是风电领域特有的方法。根据风速-功率理论曲线,判断实际点是否偏离太远。比如风速12m/s时,功率应该在额定功率附近,如果只有10%,那肯定是异常。
# IQR方法检测异常值
def detect_outliers_iqr(data, column):
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
    return outliers

# 功率曲线校验(简化版)
def power_curve_check(df):
    # 理论功率曲线:风速与功率的对应关系
    # 这里用简单阈值:风速>3m/s且<25m/s时,功率应>0
    invalid = df[(df['wind_speed'] > 3) & (df['wind_speed'] < 25) & (df['power'] == 0)]
    return invalid

# 标记异常值,不直接删除,先看看
df['is_outlier'] = False
outliers = detect_outliers_iqr(df, 'power')
df.loc[outliers.index, 'is_outlier'] = True

处理异常值的建议:

  1. 先标记,别急着删。有些"异常"可能是真实工况,比如风机限功率运行。
  2. 结合业务逻辑判断。风速低于切入风速(通常3m/s)时功率为0,这是正常的。
  3. 异常值可以替换为NaN,然后走缺失值处理流程。

好了,数据采集与预处理这部分就聊到这儿。记住一句话:数据质量决定了模型效果的上限。你算法再牛,数据不行,一切都是白搭。下一章咱们聊聊特征工程,看看怎么从这些清洗好的数据里挖出更多有价值的信息。


公众号:蓝海资料掘金营,微信deep3321