☰
轴承故障诊断100%精度可复现:振动信号转图像与CNN实现详解
2026/9/30 8:34:12 网站建设 项目流程

简介:这份PDF是一篇面向机械工程、设备故障诊断研究人员及深度学习初学者的学术论文,聚焦滚动轴承故障诊断难题。作者提出将振动信号经分帧、加窗、DFT与图像编码构建振谱图,并设计深度卷积神经网络自动提取故障特征、完成模式识别,克服传统奇异值分解、经验模态分解等方法表征信息不完整的问题。资源共1个文件,为PDF格式,压缩包大小约1.28MB,内容包含原理阐述、网络结构设计与训练流程,并以美国凯斯西储大学公开实测振动数据验证,识别准确率达到100%,方法具备工程应用前景。已有571人学习该资料,适合作为故障诊断与深度学习交叉领域的方法参考。

1. 卷积神经网络做轴承故障诊断:这篇论文为什么值得照着复现

你打开这篇论文的时候,别先去看那张 100% 的精度表,先想一个反直觉的结论:把滚动轴承的振动信号转成一张图像,再用卷积神经网络做分类,测试集上能做到全部识别正确。它没有发明什么新网络,也没有堆复杂特征,而是用短时傅里叶变换把一维振动信号变成“振谱图”,然后交给一个五卷积三全连接的 DCNN 去学。数据用的是美国凯斯西储大学公开轴承数据集,实验对象是内圈四种故障尺寸加正常轴承,识别精度 100%。这份 PDF 值得下载的地方在于参数给得具体:数据点数 1024、帧长 192、帧重叠比例 7/8,网络结构图里连每层输出尺寸都标了,很适合正在做旋转机械故障诊断、想把振动信号转图像做深度学习的工程师或研究生。照着复现一遍,你就知道所谓 100% 是怎么来的,以及哪些位置最容易翻车。

2. 从时域到振谱图:STFT 是振动信号特征提取的关键一环

轴承故障诊断的老路子是先采振动信号,再用经验公式从信号里挖一个或几个特征,最后交给支持向量机、K 近邻这类分类器。这套流程不缺理论,缺的是信息完整性:轴承从正常到损伤,信号里同时混着周期冲击、转频调制和噪声,单一特征很难把所有变化装进去。论文引用的奇异值分解、多尺度模糊熵分析、经验模态分解都是这一个思路,效果常常卡在某一类故障能识别、另一类就含糊的尴尬位置。

2.1 为什么不能把轴承振动信号整体拿去傅里叶变换

滚动轴承运行时,故障点每经过承载区就会产生一次冲击,这个冲击的幅度和相位会随转频发生变化。振动信号整体看是非平稳的:均值、方差、频率成分都在随时间变化。如果直接把整段信号做一次 FFT,得到的是一个平均频谱,冲击发生的时间位置完全丢失,早期微弱故障很容易被淹没在平稳分量里。论文抓住了轴承振动信号“整体非平稳、短时平稳”的特点,这是整条技术路线能立住的前提。

这里有一个常见误区:很多人拿到振动信号第一反应就是算频谱,但频谱只能告诉你“有哪些频率”,不能告诉你“这些频率什么时候出现”。故障冲击是瞬态事件,时间信息恰恰是区分正常冲击还是故障冲击的关键线索,所以必须做时频分析。

2.2 短时平稳与三个 STFT 参数的关系

短时傅里叶变换的做法是:把一段振动信号切成若干帧,每帧短到可以近似看成平稳信号,然后对每一帧加窗再做离散傅里叶变换,最后把各帧频谱按时间顺序拼起来。论文里给出的参数组合是数据点数 1024、帧长 192、帧重叠比例 7/8,这不是随手写的,是做了 115 次训练实验扫出来的。

先说物理含义。采样频率是 12 kHz,帧长 192 点对应 16 ms 左右的一段信号,在这么短的时间内轴承振动可以视为平稳;每帧做完 DFT 后取正频率部分,频点数是 T/2+1,也就是 97 个。帧长决定频率分辨率,帧越长频率分辨越细;重叠比例决定时间分辨率,重叠越多帧与帧之间越平滑。

重叠 7/8 这个值在普通频谱分析里很少见,常规做法是 50% 重叠就够。论文把它拉到 7/8,意味着相邻帧只滑动 24 个采样点,谱图时间轴被磨得很细。这样做的好处是 CNN 看到的纹理更连续,不会因为帧边界错动出现断裂感,代价是计算量变大,但在 12 kHz 采样下完全可接受。

2.3 加窗不是可选项,是必须项

DFT 隐含假设信号是周期的,但截取的一帧信号两端往往不是连续的,直接做变换会在频域产生泄漏,把真实的窄带峰值抹成一片旁瓣。论文用汉明窗来处理这个问题,窗函数公式为:

ω(t)=0.54-0.46cos[2πt/(T-1)]

窗长与帧长一致,加窗后再做 DFT。汉明窗的特点是主瓣稍宽但旁瓣衰减快,适合振动信号这种能量集中在若干频带的场景。代码里可以直接用 SciPy 的 hamming,也可以照公式自己写,效果相同。

2.4 振谱图与手工特征的本质区别

把振谱图喂给卷积神经网络,本质上把故障诊断从“设计特征”换成了“让网络自己找特征”。CNN 的卷积核天然适合捕捉图像里的局部能量斑块和纹理,这些纹理对应的正是故障冲击在时频面上的分布。论文对比了此前几类深度学习方法:深度信念网络做到 92% 以上,堆栈自动编码器只取频域信息做到 99%,包络谱加高斯受限玻尔兹曼机做到 93.5%,它们要么只用频域,要么只用包络,没有把时域和频域联合起来。

振谱图同时给出频率、时间、能量三个维度,CNN 对这三类信息一起学习,效果自然比单模态特征更好,这也是论文能够把普通轴承故障诊断推到 100% 的核心原因。实际工程里有人会问,为什么不换连续小波变换?小波尺度轴和母小波选择会引入新的超参数,复现成本更高;STFT 只有三个参数,每个都有明确物理含义,更适合做系统性调参。这个项目追求的是用最少的玄学拿到可复现的好结果。

3. 把振动波形转成 227×227 图像:六步处理与可抄的 Python 代码

论文把振动信号转成振谱图的过程拆成六步:分帧、加窗、DFT、频谱能量密度计算、像素颜色映射、谱线重组与尺度变换。下面是按论文公式整理的实现,直接可以跑通。

3.1 六步流程与关键参数

先记住一组参数:数据点数 N=1024,帧长 T=192,重叠点数 S=168,帧滑动步长 a=T-S=24。按论文式(1),帧数 H=(N-T)/a+1,算出来是 35。每帧 DFT 后取正频率部分,频点数 K=T/2+1=97。所以一张原始振谱图的尺寸是 35×97,远小于网络输入的 227×227,后续需要尺度变换。

3.2 分帧、加窗与 DFT 的代码实现

import numpy as np def hamming_window(n): # 论文式(3),窗长与帧长一致 return 0.54 - 0.46 * np.cos(2 * np.pi * np.arange(n) / (n - 1)) def framing(signal, frame_len=192, hop=24): # 式(1):H=(N-T)/(T-S)+1,hop=T-S n_frames = 1 + (len(signal) - frame_len) // hop frames = [] for i in range(n_frames): start = i * hop frames.append(signal[start:start + frame_len]) return np.stack(frames) def frame_spectrum(frame, win): # 加窗后做DFT,rfft只保留0~fs/2共T/2+1个频点 return np.abs(np.fft.rfft(frame * win))

代码里的hop对应论文中的 a,即帧滑动步长。rfft返回的长度是T/2+1,正好等于 K,不需要再做截断。framing用的是整数除法取整,保证最后一帧不会越界;如果信号长度不够,返回的帧数会比理论值少,后面会提到这是个坑。

3.3 能量对数化与像素颜色映射

DFT 得到的幅度谱需要转成能量密度,论文式(7)和式(8)的做法是取幅度平方再取 10 倍对数,等价于对幅度取 20 倍对数。直接对原始能量做线性映射有一个问题:冲击信号动态范围很大,个别极值会把整张图的灰度拉爆。论文式(9)用的是最大最小值线性归一化到 [0,255],工程上我习惯加一个分位数裁剪,更稳。

def spectrum_to_image(mags, low=2, high=98): # 能量密度转dB,加eps防止log0 power_db = 10 * np.log10(mags ** 2 + 1e-10) vmin, vmax = np.percentile(power_db, low), np.percentile(power_db, high) norm = (power_db - vmin) / (vmax - vmin) return np.clip(norm, 0, 1) * 255

low=2, high=98的意思是取 2% 到 98% 分位数作为映射边界,低于下界的裁成 0,高于上界的裁成 255。这样既保留能量相对关系,又不会让一两个强冲击点占满整个灰度范围。如果不加分位数,图要么黑乎乎一片,要么刺眼过曝,CNN 训练时会非常不稳定。

3.4 谱线重组、尺度变换与三通道输入

把所有帧的频谱线按时间顺序堆叠,得到的就是初始振谱图,尺寸是 H×K。接下来要缩放到 227×227,这是论文设计网络时的固定输入尺寸,对应经典 AlexNet 的输入规格。要注意这里不是 224,是 227。

import cv2 def signal_to_spectrogram(signal, frame_len=192, hop=24): frames = framing(signal, frame_len, hop) win = hamming_window(frame_len) mags = np.array([frame_spectrum(f, win) for f in frames]) img = spectrum_to_image(mags) # 初始图(H, K)=(35,97),resize到(227,227) img_227 = cv2.resize(img, (227, 227), interpolation=cv2.INTER_LINEAR) # 单通道复制成三通道,匹配网络输入 img_rgb = np.stack([img_227] * 3, axis=-1).astype(np.uint8) return img_rgb

cv2.resize的默认插值方式对频谱图来说够了,不需要用太高级的插值。复制成三通道是因为论文里网络输入标注是 227×227×3,CNN 第一层卷积核对应三通道,颜色通道在这里没有额外物理含义,只是为了对齐输入格式。实际操作中也可以保持单通道并把第一层卷积的输入通道改成 1,但那样就和论文结构不一致。

# 调用示例:CWRU数据里一帧信号长度1024 spectrogram = signal_to_spectrogram(signal_data[:1024]) print(spectrogram.shape) # (227, 227, 3)

生成的振谱图可以直接作为训练样本,标签按故障类别映射。整条预处理链路里,最容易出错的是尺度变换前的尺寸确认,最好在signal_to_spectrogram入口处打印一下frames.shape,确保 H 和 K 是预期值。

4. 五卷积三全连接:DCNN 结构与每层参数怎么推算

论文设计的深度卷积神经网络包含 1 层输入层、5 层卷积层(C1 到 C5)和 3 层全连接层(F6 到 F8),中间配了 3 个最大值池化层。网络整体沿用了 AlexNet 的骨架,但把输入尺寸和输出神经元数按轴承故障诊断任务做了调整。这一章把每层参数梳理成表,并给出尺寸推算方法。

4.1 网络整体结构与参数表

论文原文只给了每层输出尺寸,没有逐层标注卷积核大小和步长,但可以根据输出尺寸反推并用 AlexNet 常见配置补齐。下表是复现时使用的结构,和论文图 3 的输出尺寸完全一致。

层名类型核大小 / 池化核paddingstride输出尺寸
Input输入---227×227×3
C1卷积×9611×110496×55×55
P1最大值池化3×30296×27×27
C2卷积×2565×521256×27×27
P2最大值池化3×302256×13×13
C3卷积×3843×311384×13×13
C4卷积×3843×311384×13×13
C5卷积×2563×311256×13×13
P5最大值池化3×302256×6×6
F6全连接---4096
F7全连接---4096
F8全连接+Softmax---5

这里的 5 对应论文实验里的 5 类标签:正常轴承加上 4 种内圈故障尺寸。如果换成外圈、滚动体等多部位分类,F8 的神经元数也要同步改。

4.2 卷积与池化尺寸计算公式

卷积层输出尺寸按式(12)计算:

S(fs)=k×[((H+2×pad-w)/stride)+1]×[((K+2×pad-h)/stride)+1]

其中 H、K 是输入特征图尺寸,w、h 是卷积核尺寸,stride 是步长,pad 是边缘扩展。以 C1 为例:输入 227,核 11,步长 4,pad 为 0,输出 (227-11)/4+1=55,正好对上 96×55×55。

池化层公式同理,只是把卷积核换成池化核。论文选择最大值池化,池化核尺寸是 3×3,步长 2,输出尺寸 (55-3)/2+1=27,也就是 P1 层的 96×27×27。最大值池化在振动信号振谱图上的意义是保留局部区域里能量最强的点,对应故障冲击的显著频带,比平均池化更能突出冲击特征。

# 仅示意卷积部分结构,核与pad对照上表 net_conv = nn.Sequential( nn.Conv2d(3, 96, 11, stride=4, padding=0), # 227->55 nn.MaxPool2d(3, stride=2), # 55->27 nn.Conv2d(96, 256, 5, padding=2), # 27->27 nn.MaxPool2d(3, stride=2), # 27->13 nn.Conv2d(256, 384, 3, padding=1), # 13->13 nn.Conv2d(384, 384, 3, padding=1), nn.Conv2d(384, 256, 3, padding=1), nn.MaxPool2d(3, stride=2) # 13->6 )

这段代码只写了卷积池化部分,省略全连接层。每一层的注释里都标了尺寸变化,方便逐层核对。注意 PyTorch 的 padding 参数和公式里的 pad 含义一致,都是输入边界扩展的像素数。

4.3 用公式反推核对原文结构

复现时最怕的是照着网上某一版 AlexNet 抄,结果尺寸对不上。建议拿到论文图 3 后,先把每层输出尺寸抄下来,再用上面的公式逐层反推:

C1: (227-11)/4 + 1 = 55 P1: (55-3)/2 + 1 = 27 C2: (27+2*2-5)/1 + 1 = 27 P2: (27-3)/2 + 1 = 13 C3: (13+2*1-3)/1 + 1 = 13 P5: (13-3)/2 + 1 = 6

反推过程能发现很多隐藏问题。比如 C2 输入 27,卷积核 5,如果忘记 pad=2,输出会变成 23,后面全乱。我就是在这个环节吃过大亏,后来养成一个习惯:每一行代码后面都写注释,标明输出尺寸预期值,跑之前先用纸笔算一遍。

4.4 训练与收敛细节

网络参数通过前向传播和误差反向传播确定,论文采用随机梯度下降法调整权重。分类输出层用 Softmax,损失函数用交叉熵,这是多分类问题的标准配置。论文没有给出学习率、batch size、epoch 数,但根据图 9 的曲线,训练轮数不需要太多,模型精度随轮数提升到 100%,误差迅速收敛到接近零。

我复现时常用 batch size 64、初始学习率 0.01 并随训练轮数衰减,激活函数用 ReLU。论文式(11)里写的是 Sigmoid 型函数,但 ReLU 在实际训练里收敛更快,梯度消失问题更少。在这里说明一下:这不是论文原话,是工程复现的常见做法。如果你发现训练 loss 降不下去,优先检查学习率是不是太大或太小,其次检查数据归一化是否做干净了。

5. 复现避坑排查:轴承故障诊断最容易翻车的五个位置

这一章写我自己复现这类“信号转图像 + CNN”论文时的真实踩坑记录,每条按现象、原因、解决三个层次说清楚。

5.1 训练集精度奇高、测试集翻车:先查数据划分

现象:训练集 Accuracy 一直往 99% 以上走,测试集一换新样本就掉到 80% 左右。

原因:CWRU 数据集每类故障只有几十秒信号,如果随机切成小段后再随机洗牌划分训练集和测试集,训练集和测试集里会混入来自同一段连续信号的相邻片段。这些片段高度相关,模型本质上在背样本编号而不是学故障特征。

解决:严格按论文表 1 的方式做序列级划分。每类故障取 117 段信号,前 97 段做训练,后 20 段做测试,划分前不要打乱原始顺序。我一般还会打印两个集合各自的起始索引,确保完全没有交集,再开始训练。

5.2 振谱图黑乎乎一片或过曝刺眼:能量归一化没做对

现象:生成的振谱图要么全黑,要么亮成一片,输入网络后 loss 根本不降。

原因:直接对 DFT 幅度取对数时没有加保护值,log(0) 产生负无穷;或者用全局最大值做归一化,一个强冲击点就把整个动态范围拉高,其余细节全被压到低灰度区。

解决:先对能量谱加一个极小的 eps 再取 dB,然后分位数裁剪到 [2, 98] 再做 min-max 映射,最后转成 uint8。这套做法和论文式(9)的纯线性映射不完全一样,但工程上更稳。如果你坚持用论文原式,至少要在分子分母都加同一个极小值防止除零。

5.3 图像尺寸对不上报错:帧数与频点数没算明白

现象:模型训练到第二个 epoch 时突然报 shape mismatch,或者cv2.resize之前发现数组维度和预期不一致。

原因:帧数计算用了浮点除法,或者信号长度不是 hop 的整数倍,导致实际帧数比理论值少一帧。比如论文式(1)的 H=(N-T)/(T-S)+1,如果 N 取 1024、T 取 192、重叠 7/8,H=35;但如果你把整段信号直接传进去而不截断,N 变了,帧数自然对不上。

解决:固定输入长度 1024,用整数除法n_frames = 1 + (len(signal) - frame_len) // hop,切不完的尾部直接丢弃。生成振谱图后先print(img.shape)确认是 (35, 97),再做 resize。这个检查应该写进预处理函数里,不要依赖肉眼。

5.4 层与层之间维度崩掉:卷积步长和 padding 配错

现象:前向传播跑到第二层或第三层时,torch 直接报Expected 4D input或size mismatch,维度算出来是负数。

原因:手工搭的网络某一层卷积核尺寸、padding 或步长写错,例如 C3 用的是 3×3 核但忘了 pad=1,13×13 的特征图会缩到 11×11,后面全乱。

解决:每层都按公式手推一次,并在代码里加断言。比如assert out_h == 13, f"C3 output height {out_h}",跑前向时自动检查。这条断言能帮你把问题定位到具体层,而不是面对一整屏的堆栈报错。

5.5 标签体系混着用:正文和表格里其实是两种标签

现象:训练到后期 loss 变成 NaN,或者精度一直不上,检查数据时发现标签分布完全不符合预期。

原因:论文里存在两套标签描述。表格里是“正常=0,内圈四种故障尺寸=1 到 4”,这是按故障程度分类;正文里又写了“滚动体=0、内圈=1、正常=2、外圈=3、4”,这是按故障位置分类。复现时如果混着用,网络输出层 5 类语义就是乱的。

解决:开工前先确定你要做的是“故障位置分类”还是“故障尺寸分类”。CWRU 数据文件名里有轴承位置和故障直径,建议写一个解析函数,把文件名映射成唯一整数标签,每一类打印样本数核对。把标签映射写进配置文件,别在代码里硬编码数字。

6. 三招验证你的 100% 是真学到了:进阶检查与拿来即用的习惯

验证一个故障诊断模型不能只看最终精度,尤其当精度高到 100% 时,更要怀疑它是不是在背样本。

第一招:训练和测试曲线一起看。论文图 9 左边是测试精度,右边是训练和测试过程 loss 曲线,复现时把每个 epoch 的 train loss 和 test loss 都记录下来。如果 test loss 在训练后期重新抬升,说明已经过拟合;如果 train loss 和 test loss 同步下降并稳定在零附近,才算真正稳稳收敛。

第二招:把全连接层特征拉出来可视化。训练完成后取 F6 层输出的 4096 维特征,降维到二维画散点图。正常学到的模型,五类样本应该各自聚成团,团间有明显间隔;如果画出来所有点糊成一团,说明网络只是在拟合训练标签,没有形成可分的特征空间。

第三招:换转速做一次泛化测试。论文实验用的是 1797 r/min,你可以把模型直接拿到 1772、1750、1730 r/min 的数据上预测,看一下精度掉多少。掉得少说明模型学到了故障的通用时频特征,掉得多说明它对转速工况敏感,后续部署需要按转速分别建模。

python train.py --data drive_end --rpm 1797 python eval.py --data drive_end --rpm 1772 # 跨转速泛化验证

这三招做完,再回头对照论文里的图 6、图 7、图 8,你会发现论文里的每次调参都对应一个明确的精度变化,而不是单纯撞运气。从那以后,我每次复现这类“信号转图像 + CNN”的论文,都强制自己先过一遍这三步:检查数据划分、检查特征可视化、检查跨工况泛化。宁可慢一天,也不发一篇自己都说不清楚的注水结果。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询