简介:面向旋转机械故障诊断的研究者与Python开发者,压缩包内提供基于多域特征融合与生成对抗网络(GAN)的完整故障诊断方案。核心思路是在传统时域与频域特征基础上引入多域融合框架,借助GAN进行数据增强,并采用并行神经网络集成策略,提升复杂工况下的故障识别准确率,尤其适合处理样本不平衡与泛化性能不足的场景。包内共59个文件,包含28个py脚本(覆盖模型构建、数据预处理、特征提取与训练流程)、2个md说明文档(使用说明与实验记录)、12个log训练日志、10个png结果图像,以及少量txt、pyc和sh辅助文件,整体压缩包约2.21MB。目录结构清晰,包含Transformer、SDAE_RUL等模型实现,并提供PHM2012轴承数据读取、特征提取与可视化脚本,配合训练日志和图像可直观对比实验效果,便于复现、调优与二次开发。目前已有90人学习下载,适合具备一定深度学习基础、希望快速搭建旋转机械故障诊断实验的读者参考。
1. 基于多域特征融合与生成对抗网络的故障诊断方法:这份 zip 到底在解决什么
设备振动信号里,健康样本一天能攒好几个 G,故障样本却往往只有实验室台架上那几十条——「基于多域特征融合与生成对抗网络的故障诊断方法」这类方案,就是冲着这个数据不平衡来的。多域特征融合负责把一维波形的信息榨干,生成对抗网络(也叫对抗生成网络)负责把稀缺的故障样本补齐。这个 zip 解压后,通常就是一套从特征提取到模型训练、再到验证评估的完整故障诊断代码框架,适合正在做滚动轴承、齿轮箱状态监测的硕士生,以及想评估 AI 诊断方法能不能落到车间的设备工程师。它解决的核心问题只有一个:故障样本不够时,模型怎么才能不学偏。
2. 多域特征融合怎么搭才有效:时域、频域、时频域特征提取与拼接参数
2.1 三个域各自能“看”到什么:特征选择的工程理由
故障诊断里有个老经验:只用时域特征,工况一变就翻车;只用频域特征,非平稳冲击又抓不住。所谓多域特征融合,就是把振动信号在三个视角各看一遍,再把结果拼成一条特征向量,交给分类器或 GAN 的判别器。
- 时域特征:均值、均方根值、峭度、峰值因子、峰峰值。它们对幅值变化和瞬时冲击敏感,计算成本几乎为零,但容易受转速、载荷波动干扰。
- 频域特征:对原始波形做 FFT 之后,取频谱质心、频谱标准差、主频带能量占比。它们能把故障的周期性调制规律暴露出来,比如外圈故障的特征频率及其边带,但前提是信号基本平稳。
- 时频域特征:小波包分解、经验模态分解、短时傅里叶变换后的子带能量和熵。它们能同时保留时间位置和频率位置,是捕捉早期微弱冲击的主要手段,缺点是参数选择不当会产生大量冗余。
把三个域的特征拼到一起,目的是让后端的分类器既看得到“幅值变了”,又看得到“哪个频率在跳”,还看得到“冲击发生在哪个时间点”。单域特征更像一个片面描述,故障模式稍微重叠就分不开。
2.2 特征提取最小实现:时域统计量、频谱质心与小波包能量
以下是一段可直接运行的特征提取骨架,输入是一段长度为 N 的一维振动信号,输出是一个约 40 维的特征向量。工程上建议先对原始信号做去直流和带通滤波,再做这步提取。
import numpy as np from scipy.fft import rfft, rfftfreq import pywt def extract_multi_domain_features(x, fs, wavelet='db4', level=3): feats = {} # 时域:对幅值分布和冲击敏感 feats['rms'] = np.sqrt(np.mean(x**2)) feats['peak2peak'] = np.ptp(x) feats['kurtosis'] = ((x - x.mean())**4).mean() / (x.std()**4 + 1e-12) feats['crest_factor'] = np.abs(x).max() / (feats['rms'] + 1e-12) # 频域:频谱质心和频谱标准差描述主频带位置 spec = np.abs(rfft(x)) freqs = rfftfreq(len(x), d=1.0 / fs) spec_sum = spec.sum() + 1e-12 feats['spectral_centroid'] = (freqs * spec).sum() / spec_sum feats['spectral_spread'] = np.sqrt( ((freqs - feats['spectral_centroid'])**2 * spec).sum() / spec_sum ) # 时频域:小波包子带能量占比和熵 wp = pywt.WaveletPacket(data=x, wavelet=wavelet, mode='symmetric') nodes = [wp[node.path] for node in wp.get_level(level)] energies = np.array([np.sum(n.data**2) for n in nodes]) total = energies.sum() + 1e-12 prob = energies / total feats['wavelet_entropy'] = -(prob * np.log2(prob + 1e-12)).sum() for i, e in enumerate(energies / total): feats[f'wp_energy_{i}'] = e return feats逻辑说明:时域四个统计量用来捕捉振动幅值分布的整体形态;频谱质心和频谱标准差刻画谱能量集中在哪个频率区间;小波包分解把信号按频带切成多个子带,各子带能量占比反映故障冲击分布在哪些频段。参数说明:fs是采样率,决定频率轴刻度;level=3产生 8 个子带,特征向量规模适中;wavelet='db4'是振动信号里最常用的 Daubechies 小波,它具有一定的平滑性,对冲击特征保留较好,换sym5或bior3.5也能跑,但特征分布会略有偏移。
2.3 拼接与降维的四个边界坑
特征拼接不是把三组统计量往 list 里一塞那么简单。这里有几个血泪经验。
第一个坑:不同域特征量纲差异过大,导致分类器隐含地被量级大的特征主导。均方根值可能是 0.5 量级,峭度可能是 30 量级,小波能量占比是 0 到 1 之间的小数。直接拼接会让峭度对距离度量的影响压过其他特征。解决做法是分别做标准化或归一化后再拼接,常用StandardScaler,拟合时只用训练集统计量,防止数据泄漏。
第二个坑:小波包分解层数不是越深越好。level 太高,子带数量指数增长,单子带能量稀疏化,特征维度膨胀,样少量小时极易过拟合。设置level=3是默认底线,特征量大时可提到 4,但训练样本低于数百条时慎用。
第三个坑:拼接后特征高度相关,GAN 判别器会学得更吃力。建议拼接后先做主成分分析降维,保留累计解释方差 95% 以上的主成分,一般 40 维能压到 15 到 20 维。分类器在用生成样本训练时,低维特征更稳。
第四个坑:把多个测点的特征直接拼进同一条向量,会引入相位和安装位置干扰。如果同一设备上有多个加速度计,建议每个测点单独提特征,用一维卷积或注意力层融合,而不是简单拼接。
3. 生成对抗网络补样本:从一维信号到图像化训练的完整路径
3.1 为什么直接用 GAN 生原始波形会失败
很多人拿到这个方案后的第一反应是:把原始振动波形直接丢给 GAN,让它生成新的故障波形。方向没错,但坑非常深。原始振动信号长度通常是几千到几万个采样点,生成器要在这么高的维度上拟合时域波形,需要极其庞大的数据和算力;更麻烦的是,振动信号里真正的故障特征往往隐藏在高频细节或小幅度调制里,GAN 很容易学会输出一个“统计上差不多但故障特征被抹平”的波形,判别器看着像,故障诊断模型却用不了。
常见做法是先把振动信号转成二维表示,再让生成对抗网络在这份表示上补样本。常用变换有三种:短时傅里叶变换谱图、连续小波变换时频图、格拉姆角场图。其中短时傅里叶变换保留时间-频率二维结构,与故障的调制机理对应最直接;连续小波变换对非平稳冲击更敏感,但计算量较大。我自己一般优先做短时傅里叶变换,因为它的超参数简单——窗长、重叠率、FFT 点数——踩坑更容易定位。
把频谱图当作图像样本输入 GAN,生成器输出的就是一张张“假时频图”;训练完成后,再把假时频图逆变换回一维波形,与真实故障样本混合后一起喂给诊断分类器。这个路径绕开了高维波形直接生成的难题,也让 GAN 退化为一个成熟的图像生成任务。
3.2 WGAN-GP 训练配置与判别器损失实现
在故障诊断这种小样本场景里,原始 GAN 很容易出现判别器 loss 归零、生成器梯度消失。WGAN-GP 是更稳的选择:它把判别器换成输出一个实数值的评论器,并用梯度惩罚约束评论器的 Lipschitz 连续性。以下是一段核心逻辑,可以直接替换 GAN 训练脚本里的对应部分。
import torch import torch.nn as nn class Generator(nn.Module): def __init__(self, latent_dim=128, out_len=4096): super().__init__() self.net = nn.Sequential( nn.Linear(latent_dim, 1024), nn.LeakyReLU(0.2), nn.Linear(1024, 2048), nn.LeakyReLU(0.2), nn.Linear(2048, out_len), nn.Tanh() ) def forward(self, z): return self.net(z) class Critic(nn.Module): def __init__(self, in_len=4096): super().__init__() self.net = nn.Sequential( nn.Linear(in_len, 2048), nn.LeakyReLU(0.2), nn.Linear(2048, 1024), nn.LeakyReLU(0.2), nn.Linear(1024, 1) ) def forward(self, x): return self.net(x) def gradient_penalty(critic, real, fake): alpha = torch.rand(real.size(0), 1, device=real.device) interp = (alpha * real + (1 - alpha) * fake).requires_grad_(True) out = critic(interp) grads = torch.autograd.grad( outputs=out, inputs=interp, grad_outputs=torch.ones_like(out), create_graph=True )[0] return ((grads.norm(2, dim=1) - 1) ** 2).mean() def critic_loss_fn(critic, real, fake, gp_lambda=10.0): gp = gradient_penalty(critic, real, fake) return -(critic(real).mean() - critic(fake).mean()) + gp_lambda * gp逻辑说明:real和fake都是拉平成 4096 维的时频图特征向量或小尺寸图像展平。评论器输出是标量分数,WGAN-GP 用真实样本分数减生成样本分数来衡量分布距离,梯度惩罚让评论器的梯度范数稳定在 1 附近,避免训练震荡。参数说明:gp_lambda=10是经过反复验证的默认值,损失已经包含梯度惩罚项;真实与插值样本的梯度计算必须用create_graph=True,否则反向传播会断掉。
训练节奏上,评论器每更新 5 次,生成器才更新 1 次。学习率统一设1e-4,优化器用 Adam 且betas=(0.5, 0.9)——这两个参数很关键,默认的betas=(0.9, 0.999)在 WGAN-GP 里会让训练更颠簸。批次大小 64,输出长度需要与后续分类器输入尺寸严格对齐。
3.3 数据画法与类别对齐的调参经验
把振动信号转成图,画法不同,GAN 学到的内容也不同。短时傅里叶变换的典型参数是窗长 256、重叠 128、FFT 点数 256,得到约 128×64 的时频图,再缩放成 64×64 或 128×128 供网络使用。
我一般不建议把振动信号直接切成单帧小图。故障诊断里的一个“样本”应当是一段包含多个转轴的连续信号,比如 1 到 3 秒;切得太零碎,单个样本的故障特征被冲淡,生成器更容易投机取巧只学背景噪声。
类别对齐方面,假设原始数据有四类:正常、内圈故障、外圈故障、滚动体故障。生成器应当按类别条件输入标签向量,也就是用条件 GAN 而不是朴素 GAN,否则生成样本的类别分布会与真实数据不一致。实现上就是在生成器输入端拼上one-hot标签向量,在评论器输入端同样拼上标签,迫使生成结果与类别对应。否则会出现生成样本里“外圈故障”和“滚动体故障”长得几乎一样,分类器白训。
4. 故障诊断 + 对抗生成网络的避坑清单:5 个让代码翻车的细节
4.1 判别器 loss 不收敛、生成样本全像白噪声
现象:训练到几百轮后,评论器的 loss 没有下降反而上下乱跳,生成的时频图看不出任何频率结构,全是细碎噪点。
原因:最常见的是评论器更新次数不够,生成器更新太快,评论器始终追不上;其次是学习率偏大,评论器在梯度惩罚和 Wasserstein 距离对抗之间震荡。
解决:先把评论器更新次数提到 5 到 6 次,生成器更新 1 次;学习率降到1e-4以下,必要时用学习率衰减。再检查梯度惩罚的输出量级,打印gradient_penalty的均值,正常应在 1 到 10 之间,如果长期超过 100,说明gp_lambda或学习率需要调整。
4.2 模式坍塌:生成样本“像”但不“广”
现象:生成的样本单看质量很高,时频图纹理清晰,但整体差异很小;把几十张生成图放到一起,眼里全是同一张图片的局部变化。
原因:生成器找到了一个最容易骗过当前评论器的位置,反复输出相似样本。这是朴素 GAN 在低样本故障诊断里最容易暴露的问题。
解决:除使用 WGAN-GP 外,给生成器增加类别条件约束,并每隔 10 轮计算生成样本两两之间的平均余弦相似度。相似度超过 0.9 就触发训练中断或向生成器损失里加入一个多样性惩罚项。另一个有效技巧是让评论器使用谱归一化,替代批归一化。
4.3 数据泄漏:同一段信号同时进了训练集和验证集
现象:训练精度很高,验证精度也意外地高,但拿到另一台设备的数据上测试,精度断崖式下跌。
原因:用滑窗切样本时,相邻窗口重叠严重,切分训练集和验证集之前没有按“设备运转时间段”分组。同一个连续信号的前后窗口高度相关,验证集实际上变成了训练集的复制品。
解决:切分数据集时按原始记录的时间段分组,同一段时间内的窗口只能进同一份数据集;或者干脆按设备 ID、转速工况划分。这是故障诊断代码里最容易忽略、影响却最大的一个坑,也是很多故障诊断方向论文被审稿人质疑数据泄漏的根源。
4.4 生成样本不是越多越好:样本质量度量怎么设
现象:把生成样本数量翻倍后,分类器精度没有提升,反而轻微下降。
原因:生成样本分布与真实样本分布不可能完全重合,数量过大时,低质量样本把决策边界往偏离真实分布的方向推。
解决:不要盲目扩充。先做质量过滤,计算生成样本与真实样本在特征空间的 Frechet 距离,也就是 FID;只有 FID 值低于设定阈值的生成样本才被保留。我在实际项目里通常把生成样本控制在真实样本数量的 1.5 到 2 倍以内,再配合 dropout 和数据增强,比无限堆样本管用得多。
4.5 实验室指标好、现场就翻车:工况迁移的三个切入点
现象:在公开数据集或实验室台架上测试,诊断精度 98% 以上;部署到车间不同负载、不同转速下,精度回落到 80% 甚至更低。
原因:转速和载荷变化导致特征分布偏移,模型学到的决策边界只适配训练工况。
解决:有三个切入点。第一,训练数据里刻意混合多转速多负载样本,让模型见过足够宽的分布;第二,特征提取时优先选对工况变化不敏感的频带能量占比,而不是绝对幅值;第三,若现场标注困难,用无监督域自适应对齐特征分布,把源域和目标域的特征投影到同一个空间。这套方案如果要写故障诊断二区或三区 SCI,工况迁移实验几乎是必做的一环。
5. 验证这套方法有没有用:从混淆矩阵到 T-SNE 分布重叠检查
5.1 三个必须看的验证指标及其判定阈值
单看最终诊断准确率很容易被高样本量的一类带偏,我在验证时重点盯三个指标。第一个是生成样本与真实样本在特征空间的可视化重叠——用 T-SNE 把生成样本和真实样本投影到二维平面,如果两类样本完全混在一起,说明生成器学到了真实分布;如果生成样本聚成一坨或散布在空白区,说明生成器在钻空子。第二个是 FID 值,用于衡量生成时频图与真实时频图的分布距离,FID 越低越好,一般故障诊断任务建议把它控制在训练数据自比对结果的 5 到 8 倍以内,再逐轮对比取最优生成节点。第三个是类别独立召回率,不用总准确率,而是分别计算每一类故障的查准率和查全率,尤其关注故障样本量最小的那一类。
5.2 从“论文可复现”到“现场可用”的中间态做法
如果你只是复现这个方案跑通流程,按第 2 章和第 3 章的步骤就能获得一份可展示的指标;但要往现场推,我建议额外加一个中间态:把生成样本当作扩充训练集的手段,同时保留一个不掺任何生成样本的原始模型作对照组。每次实验报告里同时列出两个模型的指标,而不是只给混合后的结果。这样即使生成样本帮了倒忙,也能一眼发现。
我做这类项目时养成了一个习惯:拿到一份新的故障数据,先花半天做数据质量审计——确认测点、转速、负载、故障程度标注,再跑十分钟特征提取做可视化,然后才敢动 GAN 训练。否则调了一周参数,最后发现数据本身有问题,那才是真正的血泪教训。这个顺序反过来,性能再好的生成对抗网络也救不了错标的数据。希望这篇拆解能帮你少走几段弯路,快速把这个方案跑成自己手里的稳定工具。
本文还有配套的精品资源,点击获取