风电机组故障诊断数据集:面向时空特征融合的工程实践
1. 为什么要做一套时空特征融合的风电机组故障诊断数据集风电机组的故障诊断在国内风电行业已经不是一个新话题了。从十多年前的“坏了再修”到后来的“定期检修”再到今天大家都在讲的预测性维护Predictive Maintenance整个行业的技术路线其实走了一段不短的路。但真正做落地的人心里都清楚卡住整个流程的不是算法不是算力也不是那几个花里胡哨的模型结构而是数据。我这么说是有依据的。很多做风电智能运维的团队最头疼的事情往往不是建模而是手里没有一套完整的、带标注的、能真实反映机组故障演化过程的公开数据集。大部分真实机组的SCADA数据虽然量大但存在几个致命问题一是故障样本太稀疏绝大多数机组一年到头都是正常运行故障数据占比可能不到千分之一二是故障标注混乱运维记录和实际停机原因经常对不上同一个故障代码在不同风场可能有不同的处置逻辑三是数据通道单一只有SCADA的秒级/分钟级平均数据缺乏高频振动、冲击脉冲、声发射这类能够体现机械故障早期特征的信号。而学术界的情况是另一番景象。公开数据集里比较知名的那几个要么是纯仿真数据要么只覆盖了单一的传感器类型比如只有振动信号或者只有SCADA数据很难支撑“多源信息融合”这类研究。这就导致一个很尴尬的局面论文里大家都在用时空特征融合的方法但真正拿真实现场数据验证过的少之又少复现性差。所以我当时和团队商量制作这套数据集目的非常明确做一套带有空间拓扑结构、包含时序演化特征、具备多工况覆盖的风电机组故障诊断数据集。它不是对现有零散数据简单打包而是在数据采集方案、传感器部署、故障模拟策略、标注体系这几个环节都做了针对性设计。这套数据集适合谁三类人。第一类是高校和科研机构里做故障诊断、深度学习模型、信号处理方向的学生和老师它解决了“没有真实风场数据做验证”的痛点。第二类是风电场运维企业和主机制造商的算法团队可以用它来测试和校准自己内部的诊断模型尤其是作为迁移学习的源域数据。第三类是工业软件和数据服务公司可以用这套数据做算法验证、标杆测试和产品Demo演示。一句话概括这是一套能让你扎扎实实把“时空特征融合”这个方向做深做透的真实工程数据底座。2. 数据集的结构设计与采集方案2.1 传感器布置与空间拓扑关系做风电机组故障诊断首先要搞清楚一个问题风机到底哪些部位最需要关注从机械传动的角度看核心链路是叶片 → 主轴 → 齿轮箱 → 发电机。其中齿轮箱和主轴轴承是故障率最高的两个部件也是绝大多数振动监测系统CMS重点盯防的对象。但再加一层考虑发电机轴承、机舱振动、塔筒振动在不同工况下也会携带大量故障信息。所以这套数据集的传感器布局参考的是行业内比较通用的CMS测点方案在以下几个关键位置布设了三轴加速度传感器X径向、Y轴向、Z切向主轴轴承座前端、后端各一个测点齿轮箱输入轴轴承位齿轮箱中间轴轴承位齿轮箱输出轴轴承位发电机驱动端轴承座发电机非驱动端轴承座机舱底座麦塔筒顶部法兰位置这些测点之间不是孤立的它们天然构成了一个空间图结构主轴把风轮和齿轮箱连起来齿轮箱通过弹性联轴器连到发电机机舱把这一整条传动链包在里面。这套拓扑关系恰恰是图神经网络GNN和空间注意力模型最需要的输入结构。所以我在整理数据格式的时候特意提供了一个sensor_graph.csv文件里面记录了每个传感器节点的物理连接关系、空间坐标和故障传播路径方便直接构建邻接矩阵。2.2 数据采集设备与采样参数采集系统使用了工业级的数据采集卡搭配IEPE型三轴加速度传感器。具体参数这样设置振动信号采样率25.6 kHz每个测点三通道同步采集转速信号通过齿轮箱输入轴编码器同步采集记录瞬时转速工况参数风速、有功功率、桨距角、机舱风速仪示值、发电机转速以100 Hz频率同步记录环境参数环境温度、机舱温度、齿轮箱油温采样率1 Hz之所以把采样率定在25.6 kHz而不是常用的10 kHz或者20 kHz是因为齿轮箱的啮合频率通常在几个kHz量级而轴承早期故障的特征频率BPFO、BPFI、BSF、FTF往往落在500 Hz到5 kHz之间。25.6 kHz的采样率配合合适的抗混叠滤波器可以把高频段的故障特征完整捕获。如果是纯SCADA数据这些细节完全看不到。2.3 故障类型与工况覆盖这套数据集目前覆盖了以下故障类型故障标签部件说明严重程度分级NORMAL无正常运行无BFFA主轴前轴承外圈剥落I级/II级/III级BFIA主轴后轴承内圈剥落I级/II级GSFT齿轮箱齿面磨损分布式I级/II级GSTC齿轮箱齿面点蚀局部I级/II级GSBC齿轮箱轴承保持架断裂III级GSBP齿轮箱轴承滚子剥落I级/II级/III级GEND发电机驱动端轴承混合缺陷I级/II级GTWR塔筒结构松动螺栓预紧不足I级/II级每种故障都在三种典型工况下采集额定转速的60%、80%、100%对应的风速大致在切入风速附近、额定风速附近和略高于额定风速。这个设计很重要——同一故障在不同工况下的振动特征差异极大如果只在单一工况下采集模型学到的特征很容易和转速强相关导致泛化能力差。2.4 数据切分与文件组织整套数据集按“机组-工况-故障类型-样本序号”进行文件组织每个样本是一个10秒的振动信号片段256,000个采样点每通道同时附带有同步的转速信号、工况参数和环境参数。数据目录结构大致长这样wind_turbine_fault_dataset/ ├── sensor_graph.csv ├── readme.md ├── DataDescription.pdf ├── train/ │ ├── Normal/ │ │ ├── 60%/... │ │ ├── 80%/... │ │ └── 100%/... │ ├── GSFT/ │ │ ├── 60%/... │ │ ├── 80%/... │ │ └── 100%/... │ └── ... ├── val/ └── test/每个样本文件夹里包含这样几个文件vibration_XX.npy测点XX的三轴振动数据形状为 (3, 256000)speed.npy瞬时转速序列长度256000与振动同步采样scada_parameters.csv该10秒窗口对应的SCADA参数包括风速、功率、桨距角、油温等label.json故障标签、严重程度、工况信息、采集时间戳有一点要提前说明由于故障注入实验是在真实停机维护窗口期内执行的部分故障类型比如齿轮箱分布式齿面磨损的数据量天然少于其他类型。这是真实数据集的常态也是行业里大家都要面对的问题并不是数据采集时的疏忽。这样做的好处是更贴近真实工业场景坏处是类别不均衡问题尤为突出处理时需要额外关注。3. 时空特征融合数据里隐藏的信息如何被提取3.1 空间维度的特征多测点之间的关联可能有人会问既然是故障诊断直接对单个测点的振动信号做频谱分析不就行了为什么要搞“空间”融合答案在于风电机组的传动链是一个多刚体耦合系统一个部件故障产生的振动冲击会沿着机械连接路径传播到其他测点。这意味着齿轮箱内部轴承的早期故障在主轴轴承座和发电机轴承座上也能观测到微弱的响应信号只不过幅值衰减、相位偏移。如果只孤立地分析单个测点很容易漏掉故障初期那些微弱但关键的异常成分。而如果把多个测点的时域波形对齐后放在一起分析就能捕捉到故障冲击从源头向外传播的路径和规律这就是空间特征最大的价值。具体到建模层面空间特征融合有两种主流做法做法一基于图结构的显式建模。把每个测点当成图中的一个节点用前面提到的sensor_graph.csv定义边和邻接矩阵输入到图卷积网络GCN、图注意力网络GAT或者Graph Transformer里。节点特征可以是每个测点提取到的统计特征向量峭度、均方根、峰值因子等也可以是时域波形的embedding。这种方式的好处是可以显式建模故障传播路径可解释性强。做法二基于注意力机制的隐式建模。把多个测点的特征拼接成一个序列送进Transformer或者SE-Net这类注意力模块让模型自己学习不同测点之间的依赖关系。这种做法不需要显式的拓扑结构在大规模数据场景下往往表现更好但可解释性相对弱一些。从我的实际经验来看对于这种带有明确物理结构的风机传动链显式图建模在故障类别较多、数据集规模中等的情况下效果往往比纯注意力机制好1-3个百分点而且训练稳定性更好。但如果是海量数据混合模型的效果上限更高。3.2 时间维度的特征故障演化的动态信息时间维度的信息可以从两个尺度去理解。第一是高帧率的“短时时间特征”。振动信号本身就是时间序列单个测点的时域波形里包含了冲击特征、调制特征、周期特征。短时傅里叶变换STFT得到的时频谱图能让我们直观看到某一频段的能量随时间的变化包络谱分析Envelope Spectrum则擅长提取轴承故障的特征频率。这些手段解决的是“故障什么时候发生、频率成分是什么”的问题。第二是长周期监控里的“工况-状态时序关系”。风电机组是一个强非平稳系统转速、载荷、温度都在持续变化。单纯从一个10秒片段去判断故障状态容易受到工况波动的影响。更可靠的思路是把连续运行多个时间段的特征拼接成序列观察特征的演化趋势。比如轴承故障从I级发展到II级的过程中高频段的峭度值往往会出现明显的先增后稳趋势齿轮箱磨损的加剧则体现在边频带能量占比的缓慢上升。这种趋势信息如果不用时间序列模型RNN、LSTM、TCN去捕捉就浪费了。3.3 一个可以直接上手的融合示例从实践角度我基于这套数据集跑通了一个简单的时空融合基线模型用不到80行PyTorch代码实现。思路是先用每个测点的短时频谱图提取空间特征把所有测点堆成一个通道维度再用一维卷积和时序注意力融合时间信息最后用全连接层分类。import torch import torch.nn as nn import torch.nn.functional as F class SpatioTemporalFusionNet(nn.Module): def __init__(self, num_sensors8, num_classes10, embed_dim128): super().__init__() # 空间特征提取每个传感器用共享的CNN处理 self.space_encoder nn.Sequential( nn.Conv1d(3, 16, kernel_size64, stride16, padding32), nn.BatchNorm1d(16), nn.ReLU(inplaceTrue), nn.Conv1d(16, 32, kernel_size32, stride8, padding16), nn.BatchNorm1d(32), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool1d(128) ) # 时间特征提取跨测点融合后做时序建模 self.temporal_encoder nn.TransformerEncoder( nn.TransformerEncoderLayer( d_modelembed_dim, nhead4, dim_feedforward256, dropout0.1 ), num_layers3 ) self.fc nn.Linear(embed_dim, num_classes) self.pos_embed nn.Parameter(torch.randn(1, num_sensors, embed_dim) * 0.02) def forward(self, x): # x形状: (B, num_sensors, 3, 256000) B x.size(0) # 各测点独立提取空间特征 per_sensor [self.space_encoder(x[:, i]) for i in range(x.size(1))] fused torch.stack(per_sensor, dim1) # (B, num_sensors, 128) fused fused self.pos_embed fused fused.transpose(0, 1) # Transformer需要序列维度在前 out self.temporal_encoder(fused) # (num_sensors, B, embed_dim) out out.mean(dim0) # 池化空间的节点特征 return self.fc(out)这个模型的结构设计有一个关键点位置编码Positional Embedding在这里起到了物理拓扑编码的作用。由于Transformer本身不关心输入顺序我必须通过位置编码把测点的物理位置关系注入模型。如果直接把多个测点简单拼接模型无法区分主轴轴承和发电机轴承的差异这会严重损害性能。训练阶段有几个小细节值得注意每个Batch内尽量保持同一严重程度的故障样本避免模型学到“严重程度”这种混淆特征训练集和验证集必须按机组划分下面会详细讲不能随机打乱损失函数不要直接用默认的CrossEntropyLoss建议加上标签平滑label smoothing对噪声标签有一定的容忍度3.4 为什么很多论文里的融合策略迁移过来会失效这一点我要重点说一下因为踩过的坑太多了。很多公开论文里的多传感器融合做法是直接把所有测点拼接成一个长序列输入同一个CNN或者Transformer。这种做法在实验室数据集上效果可能不错但换到这套真实风场采集的数据上就会出问题。原因在于不同测点的信号量级差异巨大。主轴轴承座的振动加速度幅值可能只有0.2 m/s²级别而齿轮箱输出轴的振动可能达到5 m/s²以上。直接把它们拼接在一起小量级的测点特征会被大量级的测点完全淹没模型相当于在做某种“只盯着最大信号看”的特征选择丢失了关键信息。合理做法是先做测点级标准化per-sensor normalization再进行融合。这也是我在数据集说明文档里反复强调的预处理要求。有兴趣的读者可以用拼接模型和标准化融合模型分别在测试集上对比差距通常在5个百分点以上非常直观。4. 用这套数据做实验必须注意的几个关键问题4.1 数据切分方式的坑如果用这套数据集做模型训练和评估第一个要避开的坑就是数据泄露。风电机组的振动信号具有较强的片段相关性——同一个连续运行时间段内采集的样本它们的噪声模式、工况波动、背景干扰高度相似。如果随意乱序打乱数据再随机划分训练集和验证集那么验证集里很可能包含了与训练集来自同一个连续时间片的样本。这样验证出来的准确率会有严重的虚高。正确的做法是按机组和采集时间段划分保证训练集和验证集来自不同的连续运行窗口。我在整理数据集时已经按这个原则做了默认的train/val/test切分但还是要提醒如果你基于这份数据重新划分训练集和验证集务必确认没有时间重叠。4.2 类别不均衡带来的评估指标误导前面提到过不同故障类型的数据量差异很大。如果只看整体准确率Accuracy模型几乎可以靠“永远预测正常”这种愚蠢策略拿到80%以上的准确率——因为正常样本占比太高。这显然不是我们想要的结果。建议评估时以加权F1-score和混淆矩阵为第一指标准确率仅供参考。对故障诊断而言漏报把故障判为正常的代价远大于误报把正常判为故障所以如果要设计评价指标也应该引入类似工业界常用的“检测率-误报率”组合指标。比如可以定义检测率 正确识别为故障的样本数 / 实际故障样本总数误报率 正常样本被误判为故障的样本数 / 正常样本总数对一个诊断系统来说追求检测率大于90%的同时把误报率控制在5%以内往往比单纯追求98%的准确率更有工程意义。4.3 转速变化对振动特征的影响还有一个很容易被忽略的问题振动信号的频谱结构与转速强相关。齿轮箱的啮合频率等于输入转速乘以齿数如果转速从额定值的80%变为100%整个频谱的峰值位置都会发生平移。一套只在单一转速工况下训练的模型在另一个工况下测试时性能必然大幅跳水。针对这种情况有效缓解手段包括阶比跟踪Order Tracking、转速归一化、变工况注意力机制、在输入端同时送振动信号和转速信号。在这套数据集里每个样本都附带了同步的瞬时转速序列做阶比分析时可以直接用不需要额外估计转速。这也是我当初坚持同步采集转速的原因。4.4 硬件资源与训练效率当你真正开始用这套数据训练深度学习模型时会发现数据量其实不小。每个10秒样本的原始振动有8个测点乘以3个通道乘以256,000个采样点转换为浮点数后每个样本约24 MB。训练集如果有一两千个样本就是几十GB的数据量。有些团队习惯直接把所有数据读入内存再开始训练这样很容易把一台32GB内存的服务器直接跑爆。我的建议是用PyTorch的Dataset和DataLoader机制开启num_workers多进程加载和pin_memory每次只读取当前Batch的数据到显存。如果实在想要提升IO效率可以先离线把原始波形降采样到25.6 kHz已经是原生采样率、或者预提取时频特征后保存为更小的数组文件训练时只加载特征。实际测下来在一张消费级GPU显存12-16GB上用上面那个基线模型的参数配置一个Batch大小设8、10秒的完整波形跑前向加反向大约需要1-2秒。如果感觉太慢可以把输入截取成5秒窗口128,000点性能损失不大但训练速度快将近一倍。5. 一个基于该数据集的完整实验流程参考说完了理论和注意点我再用一次完整的实验流程把整个使用过程串起来方便大家“抄作业”。以下是我的团队在这套数据上跑过的标准流程所有脚本可以直接复用。5.1 数据加载与预处理第一步是读取数据并做预处理。核心包括测点级标准化、滑窗切分、转速对齐。import numpy as np import os import json from torch.utils.data import Dataset class WindTurbineFaultDataset(Dataset): def __init__(self, data_dir, window_size256000, stride128000): self.file_list [] self.labels [] for fault_type in os.listdir(data_dir): fault_dir os.path.join(data_dir, fault_type) if not os.path.isdir(fault_dir): continue for sample_name in os.listdir(fault_dir): sample_dir os.path.join(fault_dir, sample_name) label_file os.path.join(sample_dir, label.json) with open(label_file, r) as f: label_info json.load(f) self.file_list.append(sample_dir) self.labels.append(LABEL_MAP[label_info[fault_type]]) self.window_size window_size self.stride stride def __len__(self): return len(self.file_list) def __getitem__(self, idx): sample_dir self.file_list[idx] # 读取8个测点的三轴振动数据 sensor_data [] for sensor_id in range(8): vib np.load(os.path.join(sample_dir, fvibration_{sensor_id}.npy)) sensor_data.append(vib) # 形状: (3, 256000) x np.stack(sensor_data, axis0) # (8, 3, 256000) # 测点级标准化 for s in range(8): for c in range(3): mean x[s, c].mean() std x[s, c].std() x[s, c] (x[s, c] - mean) / (std 1e-8) # 可选的滑窗截取 if self.window_size x.shape[-1]: start np.random.randint(0, x.shape[-1] - self.window_size) x x[..., start:start self.window_size] return x.astype(np.float32), self.labels[idx]有一个点需要额外说明标准化必须在单个样本内做不能使用全局统计量。因为不同时间段的背景噪声水平有差异使用全局统计量会让模型对这种噪声水平的偏移过于敏感降低泛化性能。5.2 模型选型建议基于多轮实验我给出不同场景下的选型参考表场景推荐方案理由快速验证多测点特征拼接 1D CNN简单稳定适合建立基线追求更高精度GAT/Graph Transformer 包络谱特征显式建模传动链拓扑结构可解释性强计算资源有限单测点 CNN 转速归一化省资源但损失了空间信息强非平稳工况时序注意力 阶比跟踪特征对转速变化鲁棒需要解释故障位置图注意力 注意力权重可视化可以输出每个测点的贡献度从我的实际测试看GAT方案比“拼接CNN”在整体F1上能高2-4个百分点但训练时间大约增加一倍。对于刚上手这个数据集的读者建议先跑基线再逐步升级模型结构。5.3 一个完整训练循环参考下面是训练循环的核心部分包含混合精度、学习率余弦退火、早停等实践性配置def train_one_epoch(model, dataloader, optimizer, criterion, scaler): model.train() total_loss 0 total_correct 0 total_samples 0 for x, y in dataloader: x x.to(device) y y.to(device) with torch.cuda.amp.autocast(): logits model(x) loss criterion(logits, y) optimizer.zero_grad() scaler.scale(loss).backward() scaler.unscale_(optimizer) torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) scaler.step(optimizer) scaler.update() total_loss loss.item() * y.size(0) pred logits.argmax(dim1) total_correct (pred y).sum().item() total_samples y.size(0) return total_loss / total_samples, total_correct / total_samples训练过程中建议记录每个Epoch的训练/验证F1值。如果训练F1持续上升而验证F1停滞甚至下降说明模型已经进入到过拟合阶段。在风电这种强噪声数据上过拟合往往比想象中来得更快千万不要等训练完再看曲线。5.4 迁移学习视角的一点心得最后一个有价值的视角是迁移学习。由于这套数据采集自真实机组包含了非常贴近实际的工况波动和噪声模式它的特征分布和很多风场的SCADA/CMS数据是高度相似的。这意味着如果你在某个风场有一套新的、没有标注的数据可以用本数据集的正常样本作为源域做无监督/自监督领域的预训练再用少量目标域标注样本微调用本数据集预训练的特征提取器作为初始化权重在目标风场数据上微调通常比随机初始化更快收敛且在小样本场景下精度更高我自己试过用本数据集训练一个特征提取器然后迁移到另一台机组的未标注数据上做异常检测效果比直接用原始波形做自编码器要稳定很多。这就是真实数据作为“特征词典”的独特价值——它包含了太多仿真数据无法模拟的现实偏差和不确定性。6. 当前局限与后续扩展方向说实话这套数据集做到现在我已经比较满意了但它仍然有不少值得吐槽和继续改进的地方。第一个局限是故障样本主要通过故障注入和故障模拟获得和真实运行中自然形成的故障在微观形态上还是存在差异。自然磨损的故障特征更复杂、更隐蔽边界更模糊。要彻底解决这个问题只能靠长期跟踪真实故障机组这是一个需要数年时间沉淀的工作。第二个局限是目前的严重程度分级标注主要依赖人工经验判断。虽然团队里有多年风电后市场的工程师协助审核但不同专家对I级和II级故障的判定标准仍然存在主观差异。我建议使用者在做模型训练时优先考虑把I级和II级合并成一个“早期故障”类别降低标注噪声的影响。第三个局限是工况覆盖范围仍然有限。这套数据集的最高风速工况设定在额定风速附近没有覆盖高湍流强度、极端阵风这类恶劣场景。在这些场景下机组的非线性振动响应会更突出模型的适用范围需要进一步验证。关于后续扩展我目前正在做的方向有两个一是把叶片气动不平衡、变桨系统故障、偏航系统故障纳入数据集因为它们主要影响的是低速段和控制系统单靠高速轴振动信号不一定能有效感知二是为每个样本额外提供一段同步采集的声学信号用于多模态融合研究。声学信号对轴承故障尤其是早期故障的敏感度比振动还要高但工业现场的干扰噪声非常大如何做有效分离本身就是一个值得研究的问题。如果你正在考虑用这套数据做系列研究我建议可以沿着这样的路线推进先做单测点诊断建立基线再做多测点融合对比然后引入时间序列建模最后升级到图结构和注意力机制的时空联合建模。每走一步就把评估指标的瓶颈找出来而不是一步到位直接上最复杂的模型——这样不仅论文的技术路线更完整实验结论的说服力也会强很多。我自己在跑这套数据时最大的感受是好的数据集不应该是“用完就扔”的它应该像一个不断生长的活体。在做项目的过程中随着对故障机理理解的加深和数据规模的扩充诊断模型的边界也在不断被拓宽。工程上永远不缺新鲜的问题缺的是能承载这些问题的可靠底座而这套数据集就是想做那个底座。