☰
深度学习轴承故障诊断毕设源码:数据预处理到特征可视化全流程
2026/10/19 0:21:42 网站建设 项目流程

简介:一份基于深度学习的滚动轴承故障诊断完整项目源码与使用说明,出自大四毕业设计,经导师指导并获评审98.5分,适合计算机、人工智能、机械电子等相关专业学生用于毕业设计、课程设计或期末大作业,也可作为故障诊断方向初学者的实战练习项目。项目基于CWRU(凯斯西储大学)公开轴承数据集,覆盖多种工况转速(如1750、1730、1772 r/min),完整包含数据预处理、特征构建、DNN与CNN模型搭建、训练评估以及分类结果可视化等环节,并提供多个说明文档用于环境配置、运行流程与结果解读,方便快速复现与二次开发。压缩包共41个文件,以30个mat格式数据文件为主,辅以7个md格式说明文档和4个py格式Python源码脚本,整体大小约34.86MB,目录结构清晰,按数据、说明、源码等模块划分,便于查阅。目前已有145人学习下载,尤其适合需要快速搭建故障诊断实验或完成相关课题设计的研究者与在校学生。

1. 深度学习轴承故障诊断:这份毕设源码把最难的数据环节铺好了

做滚动轴承故障诊断的毕设,最卡人的往往不是模型,而是拿到原始振动信号后不知道从哪里下手。这套基于 Python 深度学习的滚动轴承故障诊断项目,是一份完整跑通的高分毕设源码,内含 dnn.py、cnn.py、creat_data.py、pre_data.py、plot_scatter.py 等模块,直接对应 CWRU 轴承数据集下 1750、1730、1772 三种载荷工况的故障分类任务。适合正在做毕业设计的学生、需要课程设计或期末大作业的计算机相关专业学习者,也适合想快速在振动信号上验证深度学习流程的从业者。它解决的核心问题是:把原始驱动端振动信号切窗、打标签、训练、评估到特征可视化这条链路一次走通,让你不用在数据处理上反复返工。

2. 数据预处理:从 .mat 原始信号到可训练样本的两个脚本

2.1 creat_data.py 与 pre_data.py 各自做了什么

这套源码里数据环节拆成了两个脚本,分工很明确。creat_data.py 负责把 CWRU 数据集里原始 .mat 格式的振动信号读取出来,完成类别标签的解析,把不同故障直径、不同故障位置的样本整理成统一的中间结构。pre_data.py 则是在这之后做滑窗切分,把长序列振动信号切成固定长度的样本,并生成对应的标签数组和数据集划分。拆成两个脚本的好处在于:第一次处理原始数据时只需要跑一遍 creat_data.py,之后调参验证模型时可以反复用 pre_data.py 重新切窗,不用每次都重新解析 .mat 文件。

CWRU 数据集本身的组织方式比较绕,文件名里通常包含转速、载荷、故障直径、故障位置等信息,比如内圈故障、滚动体故障、外圈故障,每种故障又分 0.007、0.014、0.021 英寸等不同损伤尺寸,再加上正常样本,类别总数一般在 10 类上下。做诊断任务时,最稳妥的做法是按照“故障位置 + 损伤尺寸”来定义类别,而不是只按故障位置粗分。这套源码在 creat_data.py 里处理的就是这个映射关系,把散落各处的文件名解析成统一的类别索引。

import scipy.io as sio import numpy as np import os def load_cwru_mat(file_path): # 读取CWRU .mat文件,去掉Keys里的引号后取出振动信号 mat = sio.loadmat(file_path) for key in mat.keys(): if not key.startswith('__'): data = mat[key] # 取第一列:驱动端加速度信号 signal = data[:, 0] if data.ndim == 2 else data return np.squeeze(signal) raise ValueError(f"No signal found in {file_path}") def creat_dataset(data_dir, file_list, label_list, save_path): signals = [] labels = [] for fname, label in zip(file_list, label_list): sig = load_cwru_mat(os.path.join(data_dir, fname)) # 统一长度到20k点,方便后续切窗时样本数可控 sig = sig[:20000] signals.append(sig) labels.append(label) np.savez(save_path, signals=np.array(signals, dtype=object), labels=np.array(labels))

这段代码的关键点有两个。一是从 .mat 文件里取信号列时,CWRU 的 DE 字段通常是二维数组,取第一列是加速度计驱动端的标准做法;二是统一截取固定长度后再存盘,可以避免不同文件名下信号长度不一致导致后续数组拼接时报维度错误。参数说明:file_list 请按“故障类型_故障尺寸_载荷”的顺序组织,label 从 0 开始编号,正常样本建议固定为 0,方便后面画混淆矩阵时对齐。

2.2 滑窗切分的窗口长度与步长怎么定

滚动轴承振动信号本质上是周期信号叠加噪声,单点分类没有任何意义,必须取一段足够长的窗口才能捕捉到故障冲击的特征。窗口长度的选择直接决定样本数和模型输入维度:窗口太短,特征不充分,训练准确率上不去;窗口太长,样本数骤减,而且会引入过多无关噪声。常见做法是取 1024 或 2048 点,采样率 12kHz 时对应约 85ms 或 170ms 的振动片段,这个时长覆盖轴承旋转数圈,足够包含多个故障冲击周期。

def sliding_window(signal, window_len=1024, stride=512): # 对单条长信号做滑窗,返回样本矩阵 samples = [] n = len(signal) for start in range(0, n - window_len + 1, stride): samples.append(signal[start:start + window_len]) return np.array(samples) def pre_data(signals, labels, window_len=1024, stride=512): X, y = [], [] for sig, label in zip(signals, labels): sig = np.asarray(sig).flatten() # 每条信号切出来的窗都归属同一个故障类别 windows = sliding_window(sig, window_len, stride) X.append(windows) y.append(np.full(len(windows), label)) X = np.vstack(X) y = np.concatenate(y) return X.astype(np.float32), y.astype(np.int64)

这里有两个容易出问题的细节。第一个是步长与窗口长度的关系:stride 等于 window_len 时不重叠,样本数最少;stride 小于 window_len 时相邻窗口有重叠,样本数成倍增加。做训练集扩充时我一般用 512 的步长配 1024 的窗口,每条 20000 点的信号能切出约 38 个样本,10 类数据加起来上千样本,足够喂饱一个小型网络。第二个是标签的生成方式,使用 np.full 对每条信号的所有窗口赋同一个标签,可以保证来自同一条原始信号的样本不会因为切窗顺序被打散到错误的类别,数组维度也对得上。

2.3 三种载荷工况在预处理阶段怎么组织

CWRU 数据集中 1750、1730、1772 分别对应不同的负载功率与转速,本质上是三个不同的工况域。同一个故障,在不同载荷下采集到的振动幅值和冲击能量有明显差异,这就是后面模型需要面对的域偏移问题。预处理阶段的常见组织方式分两种:

第一种是单载荷训练,即只用 1750 工况的数据做训练和验证,模型只学这一个工况下的故障模式。这种方式最简单,适合先验证网络结构和训练流程是否有 bug。第二种是三载荷混合训练,把 1750、1730、1772 三种工况的数据都切窗后混在一起,让模型同时见到不同转速下的振动模式,泛化性更好,但训练难度也会增加。这套源码在文件组织上把三种载荷分开存放,建议你按第一种方式先把流程跑通,再尝试第二种。

提示:混合训练前务必检查不同载荷下同类故障的样本量是否均衡。CWRU 不同工况的文件数量本身就不一致,如果不做数量均衡,模型会被样本量大的工况带偏,损失曲线看起来正常,实际每个类的准确率差别很大。

3. DNN 基线:三层全连接在滚动轴承信号上能学到什么

3.1 为什么先用 DNN 而不是直接上 CNN

很多初学者拿到这个任务直接上 CNN,但完全忽略了一个事实:不管用什么结构,第一步都应该用最简单的模型做一次基线验证。dnn.py 就是这个角色——把切好的 1024 点振动信号展平成一维向量,送进多层全连接网络做分类。DNN 没有卷积层那样强大的局部特征提取能力,但它能验证两件事:一是预处理后的数据是否线性可分或弱可分,二是整个数据管线从切窗到评估是否走得通。如果 DNN 都训练不起来,那问题基本出在数据环节,而不是模型不够强。

import tensorflow as tf from tensorflow.keras import layers, models def build_dnn(input_dim=1024, num_classes=10): model = models.Sequential([ layers.Input(shape=(input_dim,)), layers.Dense(512, activation='relu'), layers.Dropout(0.3), layers.Dense(256, activation='relu'), layers.Dropout(0.3), layers.Dense(128, activation='relu'), layers.Dense(num_classes, activation='softmax') ]) model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model

这个结构的选型思路很直接:第一层 512 个神经元把 1024 维输入压缩到一半,相当于做一次基础的特征融合;两个 Dropout 层都设置在激活之后,训练时随机丢弃 30% 的神经元,防止在样本量不大的情况下过拟合;最后接 128 维的瓶颈层再映射到 10 类。参数层面值得留意的是 loss 函数用了 sparse_categorical_crossentropy,对应前面 pre_data.py 生成的整数标签,不需要做 one-hot 编码,省一步操作。

3.2 训练参数配置与 loss 曲线判读

训练参数在 dnn.py 里一般不会写得太复杂,epochs 在 80 到 100 之间,batch_size 取 128 或 256。振动信号本身的特征相对规律,DNN 的收敛速度很快,通常 30 个 epoch 左右 loss 就会降到一个平台期。关键看两点:训练集和验证集的 accuracy 差距是否持续拉大,以及验证集 loss 是否出现先降后升的拐点。如果训练集已经到 98% 而验证集还在 85% 附近,说明过拟合,应该加大 Dropout 比例或者减少神经元数量,而不是急着加数据。

history = model.fit( X_train, y_train, validation_data=(X_val, y_val), epochs=100, batch_size=128, shuffle=True, verbose=1 )

这里 shuffle=True 是训练集内的随机打乱,与预处理阶段的数据泄漏是两回事。训练过程结束时,保存模型的同时记得把 history 里的 loss 曲线数据导出,后续写毕设论文时可以直接画成折线图。对于 DNN 这个基线,不用追求极致的准确率,它的核心价值是锚定一个“下限”——后面 CNN 如果跑出来的效果比 DNN 还差,那一定是数据处理或者训练配置出了问题。

3.3 DNN 在三种载荷上的准确率预期

需要先说明,具体准确率和切窗参数、随机种子强相关,没法给出一个绝对数字,但这一类任务在公开轴承数据集上有一些稳定的经验规律。单载荷下 DNN 通常能跑到 95% 左右,三载荷混合训练时会掉到 90% 上下,原因是不同转速带来的域偏移让部分故障类别在特征空间里重叠。如果训练曲线震荡剧烈,优先检查学习率是否偏大,其次是批次大小是否过小。DNN 对这两个参数比较敏感——同一次实验,lr 从 0.001 调到 0.0005,最终准确率可能差出两个点。

4. 一维 CNN:把局部冲击特征显式建模

4.1 Conv1D 的结构设计与参数选择

dnn.py 只把振动信号当作一个普通的高维向量,没有利用信号在时间邻域上的相关性。cnn.py 补上的就是这一环:用一维卷积核在时间维度上滑动,每个卷积核相当于一个滤波器,自动学习“什么形状的局部波形对应什么故障”。轴承故障的典型表现是周期性冲击,这些冲击在时域上就是一个个短暂的尖峰,Conv1D 的感受野只要覆盖几个冲击周期,就能把这种模式编码进特征图里。

def build_cnn(input_dim=1024, num_classes=10): model = models.Sequential([ layers.Input(shape=(input_dim, 1)), layers.Conv1D(filters=64, kernel_size=3, padding='same', activation='relu'), layers.BatchNormalization(), layers.MaxPooling1D(pool_size=2), layers.Conv1D(filters=128, kernel_size=5, padding='same', activation='relu'), layers.BatchNormalization(), layers.MaxPooling1D(pool_size=2), layers.Conv1D(filters=256, kernel_size=3, padding='same', activation='relu'), layers.GlobalAveragePooling1D(), layers.Dense(128, activation='relu'), layers.Dropout(0.5), layers.Dense(num_classes, activation='softmax') ]) model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0005), loss='sparse_categorical_crossentropy', metrics=['accuracy']) return model

这里有几个参数是照着振动信号特性选的。第一层 kernel_size=3 只覆盖 3 个采样点,捕捉的是最细粒度的波形变化;第二层 kernel_size=5 扩展感受野,对应故障冲击传播后稍宽的波形形态;第三层又回到 3,是为了在更高层特征上做更精细的判别。两个 MaxPooling 把 1024 点逐级压缩到 256 点,GlobalAveragePooling1D 替代 Flatten,大幅减少全连接层的参数量,从根上抑制过拟合。BatchNormalization 放在卷积和激活之间,保证每层输入的分布稳定,训练时更不容易出现梯度消失。Dropout 加在全连接层前面,0.5 的比例比 DNN 里的 0.3 更激进,因为卷积层本身有较强的正则化效果,主要的过拟合风险集中在最后的全连接层。

4.2 CNN 训练中的学习率与早停策略

CNN 比 DNN 训练慢,但对学习率的容忍度更低。lr=0.001 时 DNN 可以正常收敛,CNN 却可能出现验证集准确率在高位反复震荡、就是稳不下来的情况,常见做法是把学习率降到 0.0005 或者搭配步长衰减。还有一点,CNN 的结构决定了它在训练后期已经学到的卷积核权重变动会破坏前面的特征提取层,所以后期建议用 ReduceLROnPlateau 动态降低学习率,而不是一直用固定值。

from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau early_stop = EarlyStopping( monitor='val_loss', patience=15, restore_best_weights=True ) lr_scheduler = ReduceLROnPlateau( monitor='val_loss', factor=0.5, patience=5, min_lr=1e-6 ) model.fit( X_train.reshape(-1, 1024, 1), y_train, validation_data=(X_val.reshape(-1, 1024, 1), y_val), epochs=120, batch_size=64, callbacks=[early_stop, lr_scheduler], verbose=1 )

patience=15 的意思是连续 15 个 epoch 验证集 loss 没改善就停止训练,restore_best_weights=True 会把模型恢复到验证集表现最好的那一轮权重,这是防止训练后期过拟合的有效手段。batch_size=64 比 DNN 的 128 小一半,是因为卷积层中间特征图占显存更大,批次太大容易把显存顶爆,尤其在只用 CPU 训练的环境里,64 是一个性能和稳定性的折中。

4.3 DNN 与 CNN 的评估结果怎么对比

评估时不能只看准确率一个指标。多分类故障诊断任务里,混淆矩阵比准确率更能暴露问题——某些故障类别(比如滚动体故障)和正常样本在时域特征上相似度很高,容易被相互错分。DNN 和 CNN 跑完后,分别导出混淆矩阵,对比哪些类别的召回率偏低,才能判断模型的真实短板。CNN 在绝大多数情况下准确率会高出 DNN 三到五个点,尤其在混合三种载荷的实验中优势更明显,原因是卷积核天然具备对幅值缩放和微小相位偏移的鲁棒性,而全连接网络对这些变化非常敏感。如果你的实验结果里 CNN 没有明显优势,优先检查输入数据是否做了标准化。

5. 避坑记录:五个让模型“虚高”或“崩溃”的细节

5.1 相邻窗口数据泄漏,验证集虚高

现象:dnn.py 训练完,验证集准确率直接到 99%,但换到另一批真实数据上预测准确率骤降到 80% 出头。

原因:pre_data.py 切窗时,训练集和验证集用的是同一条原始信号切出的相邻窗口。滑窗的步长只有 512,相邻两个窗口有一半数据重叠,相当于训练时已经看到了验证集的“邻居”,模型记忆的是一段信号的局部纹理,而不是故障的通用特征。

解决:切窗完成后,按原始信号的 id 分组,把整条信号的所有窗口放进同一个集合里再做划分。常见做法是先用 train_test_split 对信号索引划分,再做滑窗,确保同一原始信号不会同时出现在训练集和验证集中。从那以后我每次切窗后都强制打印一遍“信号 id 集合交叉验证”,确认交集为空再进模型。

5.2 标签解析对不上号,损失曲线完全不降

现象:creat_data.py 跑完后,训练 loss 在第一个 epoch 就掉到极小值,但验证集准确率只有 20%,接近随机猜测。

原因:文件名解析时把外圈故障和滚动体故障的标签映射反了。CWRU 的文件命名里有两段数字,分别表示故障位置和故障尺寸,解析时索引位写错会导致所有样本的标签整体错位。更隐蔽的是,某些文件里信号字段名和 DE 字段不一致,取到的信号其实是风扇端数据,特征完全不同。

解决:解析完标签后,先画一个每个类别的样本数统计柱状图,人工核对各类数量是否合理。CWRU 数据不同类别文件数本来就不均,但差异不会超过一个量级。如果某类样本数异常多或异常少,回头看文件名解析逻辑。另外,取信号列时先用 print 打印数据维度,确认取到的是驱动端而非风扇端。

5.3 三种载荷直接混合训练,loss 下降速度慢且波动大

现象:把 1750、1730、1772 三组载荷的数据直接拼接后训练 CNN,前 30 个 epoch 的 loss 一直在原地抖动,降到 0.5 以后就再也下不去。

原因:不同载荷对应不同转速,相同故障下振动能量和冲击频率都不同,拼在一起等于把三个分布差异明显的域强行并成一个训练集,网络需要同时拟合三种模式,训练初期梯度方向互相冲突。

解决:先分别训练三个单载荷模型,确认每种载荷下单独跑都能到 95% 以上。之后再做三种载荷的混合训练时,对每个载荷的样本数量做等比例抽样,并适当降低学习率。如果任务目标本身就是跨载荷诊断,优先按“源域训练 + 目标域测试”的迁移思路来设计实验,而不是盲目把所有数据堆进一个模型。

5.4 Dropout 加在 BatchNormalization 前面导致训练不稳定

现象:CNN 结构里 Dropout 放在 BatchNormalization 之前,训练集准确率忽高忽低,每个 epoch 波动幅度超过 10 个百分点。

原因:BatchNormalization 在训练时会根据当前批次的均值和方差做归一化,Dropout 随机丢弃部分神经元后,后续批次的统计量变化更大,BN 层被带得来回震荡。正常顺序应该是卷积层输出 → BatchNormalization → 激活函数 → Dropout,让 BN 先稳定分布再做随机丢弃。

解决:把 Dropout 挪到激活函数之后。如果模型已经写好了,改这一行顺序就能解决,不需要重新设计网络结构。这类细节在短代码里看不出问题,只有在观察训练过程曲线时才会暴露。

5.5 plot_scatter.py 报错 last_layer_data 不存在

现象:跑完 cnn.py 后接着执行 plot_scatter.py,提示找不到 last_layer_data.npy 文件或者 KeyError。

原因:last_layer_data 是模型中间层输出,需要单独导出保存,脚本本身不会自动生成。不同网络结构下倒数第二层(全连接层前的特征层)名字不一样,如果直接从别人代码里拷贝后没改层名,就会取不到。

解决:在训练完成后手动跑一次特征导出,写法是先加载训练好的模型,去掉最后一层 softmax,把输入数据过一遍网络得到中间特征向量,再保存为 npz 文件:

from tensorflow.keras import Model import numpy as np model = tf.keras.models.load_model('cnn_model.h5') # 取倒数第二层作为特征输出层 feature_model = Model(inputs=model.input, outputs=model.layers[-3].output) features = feature_model.predict(X_test.reshape(-1, 1024, 1)) np.savez('last_layer_data.npz', features=features, labels=y_test)

这样导出的 feature 是一个二维数组,shape 为 (样本数, 特征维度)。可视化脚本读入后再做降维画图,就能看到各个类别在特征空间中的分布状态。

6. 最后一层特征可视化与跨载荷迁移验证

cnn.py 训练完成的模型,黑色盒子的部分在于中间层到底学到了什么。plot_scatter.py 配合上一步导出的 last_layer_data,正是揭开这个黑盒子的关键手段。把网络倒数第二层输出的高维特征向量用 t-SNE 或 PCA 降到二维平面,正常样本和不同故障类别的点云就会在图上呈现聚集状态。如果 10 个类别在二维图里各自聚成紧凑的簇、簇间分界清晰,说明模型学到的特征在决策边界上是可分的,这个图放进毕设论文里就是最有说服力的实验结果。反之,如果某个类别的点云散布在其他类别的点云中,直接用混淆矩阵定位是哪两个类别在混淆,回看数据预处理阶段是否把相似故障归错了标签。

更进一步,把三种载荷的数据套进同一个训练好的模型做迁移验证:用 1750 工况训练 CNN,然后分别用 1730 和 1772 工况的测试数据去评估。三种载荷下同一故障的物理表现基本一致,只是幅值和噪声水平不同,所以模型应该具备一定的跨工况识别能力。如果迁移准确率比同工况评估低很多,逐类对比混淆矩阵,看看是不是某个特定故障位置在目标载荷下被系统性误判。这类跨域实验的图表是毕设答辩时的高频提问点,评委通常会追问“模型换一个转速还能不能用”,提前跑一遍,把结果放在论文里,比现场解释有效得多。

做特征可视化时有一个实操技巧:t-SNE 是随机初始化的,每次跑结果都不完全一样,答辩时如果第一次画的图簇间重叠明显,不要急着换模型,先固定随机种子重跑两次再下结论。我一般固定三组种子画三张图,取分离度最好的那张放进论文,同时保留另外两张作为附录备查。整套流程跑完,从原始 .mat 文件到特征图,每一步都留有中间产物,复盘时能精准定位任何一环的问题。希望这个项目的拆解和踩坑记录能帮到你。

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

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

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

立即咨询