数据采集与清洗:SCADA系统数据、振动监测数据、气象数据、数据清洗实战
各位同行,咱们直接进入正题。数据采集与清洗,说白了就是给风场做体检前的抽血化验。血样不准,后面所有分析都是白搭。我见过太多团队花大价钱上分析平台,结果数据质量一塌糊涂,最后得出的结论连自己都不信。
SCADA系统数据:风场的"心电图"
SCADA系统,也就是数据采集与监视控制系统。它每时每刻都在记录风机的运行状态。我个人习惯把SCADA数据比作风机的心电图——电压、电流、功率、转速、温度,这些参数一个都不能少。
SCADA数据的特点是什么?
- 采样频率低:通常10分钟一条记录。别嫌少,对于长期趋势分析完全够用。
- 数据量大:一台风机一年产生约5万条记录,一个风场几十台风机,数据量相当可观。
- 字段丰富:少则几十个,多则上百个参数。
核心字段清单(我建议至少保留这些)
| 类别 | 字段 | 说明 |
|---|---|---|
| 时间 | timestamp | 精确到秒 |
| 运行状态 | status | 运行/停机/故障/限功率 |
| 发电量 | active_power | 单位kW |
| 风速 | wind_speed | 机舱风速计测得 |
| 转速 | rotor_speed | 风轮转速,单位rpm |
| 温度 | gear_temp, gen_temp | 齿轮箱、发电机温度 |
| 振动 | vibration_x, vibration_y | 机舱振动值 |
嗯,这里要注意:SCADA数据里经常藏着"假数据"。比如风机明明停机了,功率却显示为0,但风速还在跳。这种数据如果不处理,后面算发电量损失时会出大问题。
振动监测数据:听诊器下的秘密
振动监测,说白了就是给风机戴听诊器。SCADA数据告诉你"风机病了",振动数据告诉你"病在哪"。
振动数据的特点和SCADA完全不同:
- 采样频率极高:通常每秒几千到几万次采样。你想想看,一分钟的数据量就顶SCADA跑一年。
- 数据量爆炸:一台风机一天产生几个GB的振动数据很正常。
- 需要专业处理:原始振动数据没法直接用,得做FFT变换、包络分析等处理。
我在项目中遇到过一件事:某风场连续报齿轮箱故障,SCADA数据显示温度正常,但振动监测发现高频段有异常峰值。后来拆开一看,轴承保持架已经裂了。要是只靠SCADA数据,这台风机会一直带病运行到彻底报废。
我的经验:振动数据不要全量存储。建议只保存:
- 原始数据的统计特征(均值、峰值、峭度等)
- 频谱图中的关键频段能量值
- 报警触发前后的原始数据片段
这样既能保留关键信息,又不会把存储撑爆。
气象数据:风从哪里来
气象数据,尤其是测风塔数据,是风场分析的基准。SCADA上的风速是机舱风速计测的,受叶片旋转影响,数据有偏差。测风塔数据才是"真值"。
气象数据采集要注意什么?
- 高度分层:测风塔通常在10m、50m、70m、90m等高度都有传感器。不同高度的风速差异很大。
- 风向扇区:风场周围有障碍物(其他风机、山体、建筑物)会影响风向。需要做扇区管理。
- 数据完整性:气象站容易出问题,结冰、雷击、传感器老化,数据缺失是常态。
我曾经处理过一个案例:某风场发电量一直低于设计值,SCADA数据显示风速正常。后来一查测风塔数据,发现机舱风速计比测风塔风速低了15%。原来是叶片污染导致风速计测量不准。你看,没有气象数据做对比,这个问题根本发现不了。
数据清洗实战:脏数据无处遁形
好了,数据采集完了,接下来就是重头戏——数据清洗。我常说一句话:数据清洗花的时间,至少占整个数据分析项目的60%。别嫌多,这是常态。
常见的脏数据类型:
- 缺失值:传感器故障、通信中断导致数据为空
- 异常值:传感器漂移、干扰导致数据超出物理范围
- 重复值:通信协议问题导致同一条数据被记录多次
- 逻辑错误:风速为0但功率不为0,或者功率为负值
下面是我常用的清洗流程,用Python实现:
import pandas as pd
import numpy as np
# 读取SCADA数据
df = pd.read_csv('scada_data.csv', parse_dates=['timestamp'])
# 1. 删除完全重复的行
df = df.drop_duplicates()
# 2. 处理缺失值
# 对于连续型参数,用前后均值填充
df['wind_speed'] = df['wind_speed'].interpolate(method='linear')
# 对于状态类参数,用前向填充
df['status'] = df['status'].fillna(method='ffill')
# 3. 剔除物理范围外的异常值
df = df[(df['wind_speed'] >= 0) & (df['wind_speed'] <= 35)]
df = df[(df['active_power'] >= 0) & (df['active_power'] <= 2500)]
# 4. 剔除逻辑错误
# 风速大于切入风速但功率为0,可能是停机状态
mask = (df['wind_speed'] > 3) & (df['active_power'] == 0) & (df['status'] != '停机')
df.loc[mask, 'active_power'] = np.nan
# 5. 标记限功率运行状态
# 风速在额定风速以上但功率低于额定功率的95%
mask = (df['wind_speed'] > 12) & (df['active_power'] < 0.95 * 2500)
df.loc[mask, 'status'] = '限功率'
print(f"清洗前数据量: {len(df_raw)}")
print(f"清洗后数据量: {len(df)}")
print(f"剔除比例: {(1 - len(df)/len(df_raw))*100:.1f}%")
注意:数据清洗不是越狠越好。我曾经见过有人把限功率运行的数据全删了,理由是"功率没达到额定值"。结果分析发电量损失时,数据全没了,白干。清洗前一定要搞清楚业务逻辑。
知识体系总览
说了这么多,咱们用一张图把数据采集与清洗的核心逻辑串起来:
这张图把咱们刚才讲的内容串起来了。三个数据源各有特点,但最终都要汇入数据清洗这个"大熔炉"。清洗干净了,后面的分析才有意义。
实战建议:刚开始做数据清洗时,别想着一步到位。我的做法是:
- 先跑一遍基础清洗(去重、范围校验)
- 可视化看看数据分布,找找明显异常
- 跟现场运维人员确认业务逻辑
- 再跑第二轮精细清洗
这样反复迭代两三次,数据质量基本就靠谱了。
好了,数据采集与清洗这块就聊到这儿。记住一句话:数据清洗不是技术活,是良心活。你花多少心思在清洗上,后面的分析就能给你多少回报。