ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于CNN与Transformer的轴承故障智能诊断:从信号处理到工业落地

基于CNN与Transformer的轴承故障智能诊断:从信号处理到工业落地 简介深度学习技术正推动工业设备运维从传统定期维护向预测性维护变革。卷积神经网络凭借其强大的局部特征提取能力擅长从图像等网格数据中识别模式而Transformer架构则通过自注意力机制有效建模全局上下文依赖关系。结合两者优势的混合模型能同时捕捉信号的局部细节与长程关联在复杂时序数据分析中展现出卓越性能。在工业预测性维护场景下这类模型通过对设备振动信号进行时频转换将其转化为图像分类问题实现对轴承内圈、外圈及滚动体等早期故障的精准识别与预警。这不仅提升了运维效率也为设备健康管理提供了数据驱动的智能决策支持。1. 项目缘起从“事后维修”到“事前预警”的工业运维革命在工业设备运维领域轴承的故障诊断一直是个老大难问题。过去我们主要依赖两种方式一种是“事后维修”也就是设备彻底趴窝了再停机检修损失巨大另一种是“定期维护”不管设备状态好坏到点就拆开检查既浪费人力物力还可能因为频繁拆装引入新的问题。这两种方式本质上都是“盲人摸象”缺乏对设备真实健康状态的精准感知。我接触过不少工厂的运维工程师他们最头疼的就是半夜被叫起来处理突发停机或者面对海量的振动数据报表却不知道哪个信号真正预示着危险。直到深度学习技术特别是卷积神经网络和Transformer架构的成熟才让我们看到了实现“预测性维护”的曙光。这个项目的核心就是利用凯斯西储大学公开的轴承振动数据构建一个高精度的智能诊断模型目标是让机器学会从原始的振动信号中自动识别出轴承是处于健康状态还是出现了内圈、外圈或滚动体的早期故障从而实现故障的早期预警。这不仅仅是换个算法那么简单它意味着运维模式从“凭经验、靠感觉”向“数据驱动、智能决策”的根本性转变。对于工程师而言你不再需要成为信号处理专家也能从复杂的频谱图中洞察设备的“亚健康”状态。接下来我会详细拆解如何一步步实现这个系统从数据理解、模型选型、到实战中的调参避坑分享我这几年在工业AI落地中的真实经验。2. 基石深度理解CWRU数据集与振动信号的本质任何数据驱动项目的成败一半取决于对数据的理解深度。CWRU轴承数据集是学术界和工业界公认的基准但直接拿来就用很容易掉进坑里。我们必须先搞清楚我们喂给模型的到底是什么。2.1 CWRU数据集的“魔鬼细节”CWRU数据集是在一个实验台架上采集的驱动端和风扇端的轴承被施加了不同部位内圈、外圈、滚动体和不同深度0.007英寸0.014英寸0.021英寸的故障。数据通过加速度计采集采样频率为12kHz或48kHz。这里有几个关键细节直接决定了你后续数据处理的逻辑和模型的效果负载状态数据是在电机0马力、1马力、2马力、3马力四种负载下采集的。不同负载下的振动信号特征分布是不同的。如果你在训练时只用了1马力的数据模型很可能无法正确诊断3马力负载下的同类故障。在实际项目中这意味着你的训练数据必须尽可能覆盖设备的各种典型工况否则模型的泛化能力会大打折扣。故障尺寸与采样频率故障尺寸如0.007英寸非常小属于早期故障。12kHz的采样频率对于捕捉轴承的故障特征频率通常几百Hz是足够的但如果你想分析更高频的共振成分可能需要48kHz的数据。选择哪个采样频率的数据取决于你关心的故障特征频带。对于大多数轴承故障12kHz已经足够且数据量更小处理更快。数据分割的陷阱数据集通常提供的是长时间的连续信号文件。一个常见的错误是随机切分数据样本。由于振动信号具有时间连续性随机切分会造成数据泄露——即训练集和测试集包含了来自同一段连续信号的数据导致模型在测试集上表现出虚高的准确率因为它已经“见过”非常相似的片段。正确的做法是按时间顺序或按文件进行分割确保训练集和测试集来自物理上独立的时间段或实验批次。注意很多公开的代码在分割CWRU数据时忽略了这一点导致报告的性能指标如99.5%的准确率在真实工业场景中根本无法复现。这是第一个也是最重要的坑。2.2 振动信号从时域波形到模型“看得懂”的图片原始的振动信号是一维时间序列。CNN最初是为图像二维网格数据设计的Transformer最初是为自然语言一维序列但依赖位置编码设计的。如何让它们“理解”振动信号这就需要特征工程或者更时髦的说法——信号表征学习。最主流且有效的方法是将一维振动信号转换为二维时频图。这背后的物理意义是轴承发生故障时会产生周期性的冲击这些冲击在时域上可能被噪声淹没但在频域特别是通过时频分析会形成清晰的故障特征频率及其谐波。我常用的转换方法是短时傅里叶变换。你可以把它理解为一个“滑动窗口的频谱分析”。假设我们有一段1秒长的信号12000个点我们用一个长度为1024点、重叠512点的窗口从左到右滑动对每个窗口内的信号做FFT就能得到一个频谱随时间变化的二维矩阵频谱图。这个矩阵的行是频率列是时间每个点的值是能量振幅。import numpy as np import librosa import matplotlib.pyplot as plt # 假设 signal 是一段振动信号数据 fs 是采样频率 def create_spectrogram(signal, fs12000, n_fft1024, hop_length512): # 计算STFT得到复数矩阵 D librosa.stft(signal, n_fftn_fft, hop_lengthhop_length) # 转换为幅度谱能量并取对数增强对比度人耳和模型对对数更敏感 S_db librosa.amplitude_to_db(np.abs(D), refnp.max) return S_db # 可视化 plt.figure(figsize(10, 4)) librosa.display.specshow(spectrogram, srfs, hop_lengthhop_length, x_axistime, y_axislog) plt.colorbar(format%2.0f dB) plt.title(轴承振动信号时频图 (Spectrogram)) plt.tight_layout() plt.show()通过这个步骤我们就把一个分类问题转换成了计算机视觉领域经典的图像分类问题。模型的任务变成了识别这张“图片”中特有的纹理、亮斑模式对应故障频率从而判断轴承状态。3. 模型架构选型为什么是CNNTransformer单纯用CNN或者单纯用Transformer行不行当然可以也有很多论文这么做。但结合两者是为了取长补短应对工业振动信号分析中的特殊挑战。3.1 CNN卓越的局部特征提取器卷积神经网络的核心优势在于其局部连接和权重共享的特性这完美契合了图像以及我们转换后的时频图中局部特征如边缘、纹理的提取。局部感知卷积核只关注输入的一小片区域如3x3。在时频图中一个故障特征频率可能表现为某一频率带在时间轴上出现的一串亮斑。CNN的底层卷积层可以捕捉到这些局部的亮斑模式。参数共享同一个卷积核扫描整张图。这意味着无论故障特征出现在时频图的哪个位置早期、中期模型都能用同一套“检测器”将其识别出来这赋予了模型一定的平移不变性。层级结构通过堆叠卷积层和池化层CNN可以构建从低级特征点、边到高级特征纹理、模式组合的层次化表示。对于故障诊断底层网络可能学习到“周期性冲击”的基元而高层网络则学习到“内圈故障冲击序列”这种复合模式。在PyTorch中一个用于时频图分类的基础CNN骨架可以这样搭建import torch import torch.nn as nn import torch.nn.functional as F class SimpleFaultCNN(nn.Module): def __init__(self, num_classes10): super(SimpleFaultCNN, self).__init__() # 输入假设为 [batch, 1, 频率维度, 时间维度] 例如[32, 1, 128, 128] self.conv1 nn.Conv2d(1, 32, kernel_size3, padding1) # 输出 [32, 32, 128, 128] self.pool1 nn.MaxPool2d(2, 2) # 输出 [32, 32, 64, 64] self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) # 输出 [32, 64, 64, 64] self.pool2 nn.MaxPool2d(2, 2) # 输出 [32, 64, 32, 32] self.conv3 nn.Conv2d(64, 128, kernel_size3, padding1) # 输出 [32, 128, 32, 32] self.pool3 nn.MaxPool2d(2, 2) # 输出 [32, 128, 16, 16] # 全连接层前需要展平 self.flatten nn.Flatten() # 计算展平后的特征数: 128 * 16 * 16 32768 self.fc1 nn.Linear(128 * 16 * 16, 512) self.dropout nn.Dropout(0.5) # 防止过拟合 self.fc2 nn.Linear(512, num_classes) def forward(self, x): x self.pool1(F.relu(self.conv1(x))) x self.pool2(F.relu(self.conv2(x))) x self.pool3(F.relu(self.conv3(x))) x self.flatten(x) x F.relu(self.fc1(x)) x self.dropout(x) x self.fc2(x) return x这个简单的CNN已经能取得不错的效果但它有个局限感受野有限且难以建模长距离依赖关系。时频图中故障特征可能表现为相隔较远的多个谐波分量之间的关联或者故障冲击的间隔时间与转速相关是一种全局的时间模式标准的CNN层对此捕捉能力较弱。3.2 Transformer捕捉全局上下文关系的利器Transformer的核心是自注意力机制。它允许序列中的任何一个位置直接与所有其他位置进行计算从而建立全局的依赖关系。全局建模在时频图的语境下我们可以将图像切割成一系列的小图像块Patches每个块作为一个“词”。自注意力机制可以让模型知道左下角的一个频率分量可能与右上角的另一个分量共同指示了某种故障模式。这对于识别复杂的、非局部的故障特征组合至关重要。位置编码由于自注意力本身不包含位置信息我们需要加入位置编码来告诉模型每个图像块在时频图中的原始位置时间、频率。这对于振动信号分析极其重要因为频率和时间的绝对位置直接对应物理意义。然而纯Transformer模型如Vision Transformer需要大量的数据才能训练好且计算复杂度高。对于CWRU这种规模的数据集直接使用ViT很容易过拟合。3.3 CNNTransformer混合架构强强联合的设计哲学因此一个自然而有效的策略是采用混合架构用CNN作为前端特征提取器用Transformer作为后端关系建模器。CNN骨干网络使用一个深度CNN如ResNet, EfficientNet的卷积部分来处理原始的时频图。CNN在这里扮演了“特征工程”和“降维”的角色它将高维的、冗余的像素数据压缩成一组富有语义信息的、低维的特征图。这比直接将图像分块输入Transformer要高效得多也更容易训练。特征图序列化将CNN输出的特征图形状为[B, C, H, W]在空间维度上展平变成一个序列[B, N, C]其中N H * W每个C维的向量代表原图一个区域的特征。Transformer编码器将这个特征序列输入一个标准的Transformer编码器由多头自注意力层和前馈网络层堆叠而成。在这里模型学习这些特征区域之间的全局关系。例如它可能学会“当代表高频冲击的特征A和代表转速谐波的特征B同时被强烈激活时预示着外圈故障”。分类头通常我们会在序列前加一个可学习的[CLS]token或者对所有特征序列进行全局平均池化得到一个全局特征向量最后接一个全连接层进行分类。这种架构的优势非常明显CNN高效地完成了初级的、通用的特征提取大大降低了输入Transformer的序列长度和复杂度而Transformer则专注于学习这些高级特征之间的复杂交互提升了模型的判别能力。在实际测试中这种混合模型通常比纯CNN或纯Transformer具有更好的鲁棒性和泛化能力尤其是在处理噪声干扰或变工况数据时。4. 实战构建从数据流到训练循环的完整链路理解了原理和架构我们来看如何用代码将其串联起来。这里我以PyTorch为例展示一个精简但完整的流程。4.1 数据预处理与加载管道数据管道是模型训练的“第一公里”它的稳定性和效率直接影响整个项目。import os import numpy as np import torch from torch.utils.data import Dataset, DataLoader import scipy.io as sio from sklearn.model_selection import train_test_split class CWRUBearingDataset(Dataset): def __init__(self, data_dir, sample_length1024, transformNone, modetrain, test_size0.2): data_dir: 存放.mat文件的目录 sample_length: 每个样本的长度点数 transform: 数据增强变换如时频图转换 mode: train 或 test self.data_dir data_dir self.sample_length sample_length self.transform transform self.mode mode # 1. 加载所有.mat文件并关联标签 all_files [] all_labels [] # 这里需要根据CWRU文件命名规则解析标签例如 # 98.mat (正常), 105.mat (内圈故障0.007英寸) 等。 # 实际项目中应编写更健壮的解析逻辑。 for file in os.listdir(data_dir): if file.endswith(.mat): file_path os.path.join(data_dir, file) label self._parse_label_from_filename(file) # 自定义解析函数 all_files.append(file_path) all_labels.append(label) # 2. 按文件划分训练集和测试集避免数据泄露 train_files, test_files, train_labels, test_labels train_test_split( all_files, all_labels, test_sizetest_size, random_state42, stratifyall_labels ) if self.mode train: self.file_paths train_files self.labels train_labels else: self.file_paths test_files self.labels test_labels # 3. 预计算每个文件能产生多少个样本 self.samples [] for idx, file_path in enumerate(self.file_paths): # 加载.mat文件假设数据在键X下 data sio.loadmat(file_path)[X].flatten() num_samples len(data) // self.sample_length for i in range(num_samples): start i * self.sample_length end start self.sample_length # 存储样本索引和对应的全局标签 self.samples.append((idx, start, end)) def _parse_label_from_filename(self, filename): # 简化的示例实际需根据CWRU命名规则实现 if normal in filename: return 0 elif inner in filename: return 1 elif outer in filename: return 2 elif ball in filename: return 3 else: return -1 def __len__(self): return len(self.samples) def __getitem__(self, idx): file_idx, start, end self.samples[idx] file_path self.file_paths[file_idx] label self.labels[file_idx] # 加载整个文件数据这里可以优化为内存映射 full_data sio.loadmat(file_path)[X].flatten() signal_segment full_data[start:end].astype(np.float32) # 转换为时频图 (这里调用之前的create_spectrogram函数) # 注意transform应包含归一化等操作 if self.transform: spectrogram self.transform(signal_segment) else: # 默认转换实际应用需要更完整的流程 spectrogram torch.from_numpy(signal_segment).unsqueeze(0) # 模拟未转换 return spectrogram, label # 定义转换包含STFT和归一化 class ToSpectrogram: def __init__(self, n_fft256, hop_length64): self.n_fft n_fft self.hop_length hop_length def __call__(self, signal): # 使用librosa或torchaudio实现STFT # 返回形状为 [1, Freq, Time] 的Tensor # 此处为伪代码 S torch.stft(torch.from_numpy(signal), n_fftself.n_fft, hop_lengthself.hop_length, return_complexTrue) spectrogram torch.abs(S) # 幅度谱 spectrogram (spectrogram - spectrogram.mean()) / spectrogram.std() # 简单归一化 return spectrogram.unsqueeze(0) # 增加通道维 # 创建数据加载器 train_dataset CWRUBearingDataset(data_dirpath/to/cwru, transformToSpectrogram(), modetrain) test_dataset CWRUBearingDataset(data_dirpath/to/cwru, transformToSpectrogram(), modetest) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) test_loader DataLoader(test_dataset, batch_size32, shuffleFalse, num_workers4)4.2 构建CNN-Transformer混合模型下面实现一个简化的混合模型使用一个轻量级CNN提取特征后接一个Transformer编码器。import torch.nn as nn import torch.nn.functional as F import math class HybridFaultDiagnosisModel(nn.Module): def __init__(self, num_classes4, embed_dim128, num_heads4, num_layers3, dropout0.1): super(HybridFaultDiagnosisModel, self).__init__() # 1. CNN特征提取器 (简化版类似几个卷积块) self.cnn_feature_extractor nn.Sequential( nn.Conv2d(1, 32, kernel_size3, stride1, padding1), # [B, 32, F, T] nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2), # [B, 32, F/2, T/2] nn.Conv2d(32, 64, kernel_size3, stride1, padding1), # [B, 64, F/2, T/2] nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2), # [B, 64, F/4, T/4] nn.Conv2d(64, embed_dim, kernel_size3, stride1, padding1), # [B, embed_dim, F/4, T/4] nn.BatchNorm2d(embed_dim), nn.ReLU(), # 不再池化保留空间维度给Transformer ) # 2. 将特征图转换为序列 # 假设CNN输出为 [B, C, H, W] 其中 C embed_dim # 我们需要将其变为 [B, N, C] N H * W self.to_patch_embedding nn.Sequential( nn.Flatten(start_dim2), # [B, C, H*W] nn.LayerNorm(embed_dim), ) # 3. 可学习的分类token [CLS] self.cls_token nn.Parameter(torch.randn(1, 1, embed_dim)) # 4. 位置编码 (可学习的) # 我们需要知道序列长度N但N取决于输入时频图大小和CNN的下采样倍数。 # 这里我们先定义一个占位符在forward中动态创建。 self.pos_embedding None self.embed_dim embed_dim # 5. Transformer编码器 encoder_layer nn.TransformerEncoderLayer( d_modelembed_dim, nheadnum_heads, dim_feedforwardembed_dim*4, dropoutdropout, activationgelu, batch_firstTrue # 重要PyTorch默认是[序列长度批次特征] ) self.transformer_encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) # 6. 分类头 self.mlp_head nn.Sequential( nn.LayerNorm(embed_dim), nn.Linear(embed_dim, embed_dim // 2), nn.GELU(), nn.Dropout(dropout), nn.Linear(embed_dim // 2, num_classes) ) def forward(self, x): # x: [B, 1, Freq, Time] B x.shape[0] # CNN特征提取 features self.cnn_feature_extractor(x) # [B, C, H, W] _, C, H, W features.shape # 转换为序列 [B, N, C] patch_embeddings self.to_patch_embedding(features) # [B, C, N] patch_embeddings patch_embeddings.transpose(1, 2) # [B, N, C] # 添加[CLS] token cls_tokens self.cls_token.expand(B, -1, -1) # [B, 1, C] x_seq torch.cat((cls_tokens, patch_embeddings), dim1) # [B, N1, C] # 添加位置编码 (动态创建) seq_len x_seq.size(1) if self.pos_embedding is None or self.pos_embedding.size(1) ! seq_len: self.pos_embedding nn.Parameter(torch.randn(1, seq_len, self.embed_dim)).to(x.device) x_seq x_seq self.pos_embedding # Transformer编码 encoded self.transformer_encoder(x_seq) # [B, N1, C] # 取[CLS] token的输出作为全局表示 cls_output encoded[:, 0, :] # [B, C] # 分类 logits self.mlp_head(cls_output) # [B, num_classes] return logits4.3 训练策略与核心技巧有了模型和数据训练过程同样充满细节。这里分享几个关键点损失函数选择对于分类任务交叉熵损失是标准选择。但如果你的数据集类别不平衡例如正常样本远多于故障样本可以考虑加权交叉熵损失或Focal Loss。# 类别不平衡处理示例 class_counts [1000, 200, 150, 100] # 假设四个类别的样本数 total sum(class_counts) class_weights [total / count for count in class_counts] weights_tensor torch.FloatTensor(class_weights).to(device) criterion nn.CrossEntropyLoss(weightweights_tensor)优化器与学习率调度AdamW是目前的首选优化器它比Adam有更好的权重衰减处理。配合余弦退火或带热重启的余弦退火学习率调度往往能取得更好的收敛效果和最终精度。from torch.optim import AdamW from torch.optim.lr_scheduler import CosineAnnealingLR model HybridFaultDiagnosisModel().to(device) optimizer AdamW(model.parameters(), lr1e-4, weight_decay1e-4) scheduler CosineAnnealingLR(optimizer, T_maxnum_epochs) # T_max为周期 for epoch in range(num_epochs): # ... 训练循环 ... scheduler.step() # 每个epoch后更新学习率正则化与防止过拟合除了Dropout在CNN部分使用BatchNorm在Transformer部分使用LayerNorm是标配。对于小数据集数据增强是防止过拟合的利器。对于时频图可以尝试时域随机小幅平移、添加高斯白噪声。频域随机掩蔽部分频率带Frequency Masking或时间带Time Masking模拟传感器部分失效或信号衰减。这能显著提升模型鲁棒性。早停与模型保存始终在独立的验证集上监控性能当验证集损失连续多个epoch不下降时触发早停并保存验证集上性能最好的模型。5. 模型评估与工业落地考量超越准确率在实验室跑出99%的准确率只是第一步。工业落地是另一回事你需要关注更多指标。5.1 全面的评估指标体系不要只看整体准确率。一个将所有样本都预测为“正常”占比最大的类别的模型准确率也可能很高但这毫无用处。混淆矩阵这是最重要的工具。它能清晰告诉你模型具体在哪些类别上混淆了。例如是否容易将“内圈早期故障”误判为“正常”这种误判在实际生产中代价最高。精确率、召回率、F1分数针对每一个故障类别计算这些指标。对于故障诊断我们通常更关注召回率——即“所有真实的故障被找出来多少”。宁可误报不可漏报。但误报太多也会导致“狼来了”效应所以需要平衡。ROC曲线与AUC对于二分类问题如正常vs故障非常有用可以评估模型在不同判定阈值下的性能。5.2 面对真实工业数据的挑战CWRU数据是在实验室可控环境下采集的“干净”数据。真实工厂环境要复杂得多噪声干扰现场存在大量其他旋转机械、电磁干扰、背景噪声。你的模型必须对噪声鲁棒。在数据增强阶段加入强噪声是有效的模拟方法。工况变化负载、转速、温度随时在变。CWRU的不同负载数据是宝贵的资源必须充分利用进行跨工况训练和测试。一个只在额定工况下有效的模型是没有实用价值的。未知故障模型只能识别它训练过的故障类型。对于从未见过的故障模式一个健壮的系统应该能给出“未知”或“置信度低”的判断而不是强行归类。这涉及到开集识别或异常检测技术是更前沿的课题。计算资源与实时性模型最终可能需要部署在边缘设备如工控机、嵌入式AI模块上。你需要对模型进行剪枝、量化、蒸馏等优化在精度和速度之间取得平衡。5.3 构建可解释的诊断报告工程师不仅需要知道“坏了”更需要知道“哪里坏了可能多严重”。因此模型的可解释性至关重要。Grad-CAM类热力图可以可视化CNN模型在时频图上关注哪些区域。将热力图叠加回原始时频图可以直观地展示模型是根据哪个频率、哪个时间点的特征做出判断的。这能极大增强工程师对模型的信任。注意力权重可视化对于Transformer部分可以可视化自注意力权重看模型在决策时更关注哪些特征块之间的关系。这有助于理解模型学到的“故障逻辑”。6. 项目复盘与进阶思考走完整个流程后你会发现构建一个可用的原型系统并不算太难但要让它在产线上稳定可靠地运行还有很长的路要走。这里分享几点更深度的思考关于数据工业AI七分靠数据三分靠模型。数据的质量、覆盖度、标注成本是最大的瓶颈。如何利用少量标注样本小样本学习、如何利用大量无标签数据自监督学习、如何做持续的数据闭环迭代是工程落地的核心。关于模型轻量化前面提到的混合模型参数量可能较大。可以考虑用MobileNet、ShuffleNet等轻量CNN作为骨干或者使用更高效的Transformer变体如MobileViT、PoolFormer。在部署前务必使用TensorRT、OpenVINO或ONNX Runtime等工具进行推理优化。关于系统集成诊断模型只是一个算法模块。一个完整的预测性维护系统还包括数据采集传感器、PLC、边缘计算数据预处理、模型推理、云平台数据存储、模型训练、可视化、报警与工单系统等。需要与自动化、IT部门的同事紧密协作。最后的建议不要一开始就追求最复杂的模型。从一个简单的CNN或ResNet开始建立完整的数据管道和评估基准。然后逐步引入更复杂的结构如注意力机制并严格进行A/B测试确认每一次改进都带来了可量化的性能提升。工业场景下模型的稳定性和可维护性往往比那0.5%的精度提升更重要。这个基于CNN与Transformer的轴承故障诊断项目是一个绝佳的起点它能带你深入理解工业AI从理论到实践的全过程其中的方法论可以迁移到电机、齿轮箱、泵阀等各种旋转机械的故障诊断中。本文还有配套的精品资源点击获取
返回列表