滚动轴承故障诊断系统设计:基于公开数据集的完整工程实践
搞旋转机械状态监测这些年我越来越觉得滚动轴承故障诊断是最值得投入的入门方向。机械设备里头轴承是第一大故障来源又是典型的“看不见内部状态”的部件等你能听到异响的时候往往已经损伤到需要停机更换了。所以基于振动信号的故障诊断系统一直是状态监测领域的硬话题。这一篇想聊的就是如何基于目前业界用得最广的一套某高校公开滚动轴承振动数据集从零设计一个滚动轴承故障诊断系统。这里面既包含了数据读取与切片、时域频域特征提取、包络谱解调也包括了传统机器学习模型和深度学习模型的对比与选型。适合正在做课程设计、毕业设计或者刚接触故障诊断方向想快速上手的工程人员。整个流程我尽量按实际操作顺序来讲包括中间踩过的坑和最终验证下来的可行方案。1. 项目整体思路与系统框架1.1 为什么拿公开数据集做故障诊断机器学习和故障诊断结合的论文这几年特别多但你真去复现的时候就会发现最难的往往不是模型而是没有合适的故障数据。现场的设备一般都不会让它在故障状态下长期跑就算有故障样本也未必有对应的健康基准数据更别提不同损伤程度的样本了。公开数据集的出现把这个问题解决了。那套经典的滚动轴承振动数据集最早由国外某研究机构通过电机驱动端加速度传感器采集覆盖了正常、滚动体故障、内圈故障、外圈故障四种状态每种故障还分了不同损伤直径。它之所以被广泛使用是因为实验条件相对固定、信号质量高、标签明确而且被复现过太多次大家心里都有底。拿它来做系统设计和算法验证能把精力集中在“故障诊断流程”本身而不是花大量时间在数据清洗和标注上。当然也有不少人觉得这套数据集“太干净了”和现场数据有差距。这个说法有道理但作为系统设计的验证平台它的价值仍然不可替代。相当于练枪要先练固定靶把一套标准流程跑通再拿现场数据去磨路径会顺很多。1.2 系统模块划分与数据流向我一开始做这个项目时容易陷入“直接读数据丢给模型”的误区。后来把整个系统拆成几个独立模块思路一下就清晰了数据读取层负责解析原始文件提取驱动端振动信号。数据预处理层完成滑窗切片、去均值、归一化、标签对齐。特征工程层计算时域特征、频域特征、包络谱特征并做降维。诊断模型层使用机器学习或深度学习模型完成故障分类。评估与部署层完成模型评估、混淆矩阵分析、结果输出。模块化的好处很明显每一层替换成本都很低。比如你后续想换一种特征提取方法或者把随机森林换成卷积神经网络不需要动其他模块。系统内部的数据流向就是原始时域波形 - 切片样本 - 特征矩阵 - 类别标签。在实际工程里这里的“类别标签”不只是故障类型还应该包括故障位置、损伤程度甚至可以扩展为剩余寿命预测。但是作为第一版系统只做四分类已经能把所有环节跑通后续扩展就是加模块的事。2. 认识数据集结构、参数与故障机理2.1 数据集的构成和关键约定我最早拿到数据的时候第一反应是这结构怎么这么乱。文件命名规律不统一有些是.mat格式有些解压后里面有多组信号通道。这里先给新手把关键点理清。这套数据主要采集了驱动端和风扇端的振动加速度信号采样率有两个档位12kHz和48kHz。做故障诊断时绝大多数研究都选驱动端12kHz的数据因为信号对轴承故障更敏感数据量也适中。每个.mat文件里通常包含DE_time驱动端加速度信号FE_time风扇端加速度信号BA_time基座加速度信号部分文件里有电机转速、负载功率等工况参数故障类型由文件名标识例如内圈故障、外圈故障、滚动体故障损伤直径用英寸表示常见的有0.007、0.014、0.021三挡对应轻微、中等、严重损伤。负载状态下电机转速会有轻微变化直接影响故障特征频率。我常用的做法是把文件名解析成一个字典用正则表达式提取故障类型、损伤直径、负载档位然后生成一个统一的数据清单表。这一步一定要做不然后面标签对齐会错得离谱。2.2 三类故障的振动特征到底差在哪做故障诊断不能只当调包侠至少要懂一点故障机理。轴承故障产生的振动本质上是周期性冲击。不同位置的故障冲击间隔和传递路径不同在信号上表现差异很大。内圈故障的典型特点是冲击发生在内圈滚道经过承载区时由于内圈随轴旋转冲击位置不断变化振动幅值会受到转频调制。在包络谱中故障特征频率附近会出现边带族这是判断内圈故障的重要线索。外圈故障相对好识别因为外圈固定在轴承座内冲击位置相对固定每次滚动体经过故障点时的冲击间隔比较均匀包络谱中故障特征频率的幅值通常清晰且突出。滚动体故障最头疼滚动体既围绕轴心公转又绕自身自转冲击激励源不稳定故障特征往往比其他两类微弱甚至会被噪声淹没。这也是很多模型在滚动体故障上准确率偏低的原因。这三类故障对应的特征频率工程上常用BPFO、BPFI、BSF表示分别对应外圈、内圈、滚动体故障。计算公式涉及滚动体直径、节径、接触角和转速。做数据处理时可以用这些理论频率来校验包络谱峰值的位置确认提取到的特征是不是真的对应故障。3. 数据预处理与特征工程模型效果的隐形胜负手3.1 数据读取与滑窗切片很多人跑完整个流程后发现模型准确率不稳定问题多半出在数据预处理上。原始振动信号往往长达几十万甚至上百万个采样点如果整段塞进模型既费算力又无法扩充样本量。所以必须做滑窗切片。先看数据读取函数import numpy as np import scipy.io as sio def load_de_signal(file_path): mat sio.loadmat(file_path) for key in (DE_time,): if key in mat: signal mat[key].ravel() return signal raise ValueError(未找到驱动端振动信号)读取后要对信号做预处理常见操作是去均值和归一化。去均值是为了消除直流分量因为振动信号本身是围绕零均值的加速度值如果传感器有偏置会影响后续频域分析。归一化则是为了不同样本之间量纲一致方便模型收敛。滑窗切片的关键参数是窗口长度和步长。窗口长度要至少覆盖一个完整的轴旋转周期。假设转速是1797转/分转频就是30Hz左右每个周期约0.033秒。在12kHz采样率下约400个采样点是一个旋转周期。但特征计算需要更长的数据窗口才稳定我建议窗口设为2048或4096个采样点这样包含多个旋转周期能有效反映周期性冲击特征。步长一般取窗口长度的50%保证相邻窗口之间有重叠样本量也够。def sliding_window(data, win_len4096, step2048): n len(data) starts np.arange(0, n - win_len 1, step) return np.stack([data[i:i win_len] for i in starts])切片之后每段样本对应一个故障标签。但要注意同一个原始文件中切出来的不同样本理论上相关性强。如果你把所有样本混在一起随机划分训练集和测试集就可能出现信息泄露模型准确率虚高。正确的做法是先把原始文件划分成训练文件组和测试文件组再分别对文件做滑窗切片保证测试样本和训练样本来自完全不同的时间段。3.2 时域特征和频域特征怎么取特征提取是整个流程里最体现工程经验的一环也最需要解释清楚“为什么取这些特征”。时域特征取均值、峰值、均方根值、峭度、偏度和峰值因子。均值反映信号是否有直流偏置峰值能捕获瞬态冲击的极限幅值均方根值直接反映振动能量大小峭度对冲击型缺陷特别敏感早期故障信号中峭度往往明显增大偏度则能反映波形分布的不对称性。def time_features(x): feats {} feats[rms] np.sqrt(np.mean(x ** 2)) feats[peak] np.max(np.abs(x)) feats[kurtosis] np.mean((x - np.mean(x)) ** 4) / (np.std(x) ** 4) feats[skewness] np.mean((x - np.mean(x)) ** 3) / (np.std(x) ** 3) feats[peak_factor] feats[peak] / (feats[rms] 1e-8) return feats频域特征要借助快速傅里叶变换把时域信号转到频谱上观察能量分布。轴承故障的冲击成分会在高频段激起共振所以提取频谱重心、均方频率、频率方差、主频带能量占比例等指标能补充时域特征捕捉不到的信息。一个值得强调的点是FFT反映的是整体频谱能量分布对于周期性冲击直接看频谱往往不够直观更高效的方法是做包络谱。3.3 包络谱分析与特征降维包络谱是轴承故障诊断的经典手段。原理是先对信号做希尔伯特变换求出解析信号取模得到包络信号再对包络信号做FFT得到的频谱就是包络谱。通俗地说包络谱把高频冲击信号“解调”成了低频的冲击重复频率这正好对应故障特征频率。from scipy import signal def envelope_spectrum(x, fs12000): analytic signal.hilbert(x) envelope np.abs(analytic) freqs, psd signal.periodogram(envelope, fsfs, nfft8192) return freqs, psd实际操作时我习惯在包络谱中提取故障特征频率处的幅值和边带幅值作为补充特征。还可以选择几个有代表性的频段比如500Hz以下、500Hz到2000Hz、2000Hz以上分别计算能量占比作为整体频带分布特征。把这些时域特征、频域特征、包络谱特征拼在一起后每个样本的特征维度可能到几十维甚至上百维。特征多了不一定好有些特征高度相关还会引入噪声。这时可以做一次相关性热力图检查把相关系数超过0.95的冗余特征去掉再用主成分分析把维度压到20到30维。这里要提醒一下PCA要只在训练集上拟合然后对测试集做变换而不是合并之后一起降维。4. 诊断模型训练与效果对比4.1 先用机器学习模型打底做模型选型时我坚持一个原则上来先跑传统机器学习模型能用特征工程解决的问题先不急着上深度网络。不是说深度学习不好而是传统模型训练快、可解释性强、容易定位问题。在特征矩阵上我主要跑了三类模型随机森林对高维特征稳健不容易过拟合不用做复杂的归一化。支持向量机在小样本高维特征下表现突出但需要调C和gamma。梯度提升树在结构化特征上经常拿到最高精度但是调参耗时多一点。随机森林是最适合做基线的模型。几百棵树的规模训练起来就几秒钟还能输出特征重要性帮你看哪些特征对诊断最有贡献。实际做下来峭度、包络谱故障频率幅值、频段能量占比这几项通常排在前列。SVM的调参要关注惩罚系数C和RBF核的gamma值。C太大容易过拟合C太小又欠拟合gamma控制着单个样本的影响半径。我习惯用网格搜索交叉验证来选参数但网格别太粗也别太细先粗跑一轮锁定范围再细分一轮。from sklearn.model_selection import GridSearchCV from sklearn.svm import SVC param_grid {C: [0.1, 1, 10], gamma: [0.001, 0.01, 0.1]} clf GridSearchCV(SVC(kernelrbf, probabilityTrue), param_grid, cv5, scoringaccuracy) clf.fit(X_train, y_train)在四分类任务中这类模型在测试集上达到95%以上准确率并不难但如果想拿到99%以上就要靠数据切分的严谨性和特征工程的精细化。4.2 一维卷积网络端到端诊断传统机器学习需要人为构造特征那么这个环节能不能让模型自己学完全可以这就是一维卷积神经网络的价值。一维CNN可以直接吃原始振动波形自动提取局部冲击模式和多尺度特征省去了手工特征工程的过程。我用的是一个结构比较简单的模型输入为4096个采样点的一维序列from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout model Sequential([ Conv1D(16, 64, activationrelu, input_shape(4096, 1)), MaxPooling1D(4), Conv1D(32, 16, activationrelu), MaxPooling1D(4), Flatten(), Dense(64, activationrelu), Dropout(0.3), Dense(4, activationsoftmax) ]) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy])选择64和16的卷积核尺寸是为了匹配轴承故障冲击信号的脉冲宽度。第一层大卷积核能覆盖较长的冲击模式第二层小卷积核专注于局部细节。训练时设置早停监控验证集损失防止过拟合。端到端模型的好处是省事但坏处也明显可解释性差、训练时间长、对数据量的需求更大。这套数据集每类样本能切出几千段勉强能用但和传统特征方法相比并没有绝对优势。实际对比后发现CNN在滚动体故障这类困难类别上略好一些但在整体准确率上并没有显著拉开差距。这说明特征工程在中小规模数据上仍然非常能打。4.3 实验结果与选型建议我最终对比的结果大致是随机森林准确率约96%到98%训练时间秒级。SVMRBF核调好后准确率约96%左右略低于随机森林但对高维特征稳定。一维CNN准确率约96%到99%训练时间几分钟滚动体故障识别率更高。选型建议很简单做课程设计和快速验证优先随机森林加特征工程做横向对比展示深度学习方法上一维CNN实际工业部署中特征工程加梯度提升树往往是最稳定可靠的选择因为现场环境的数值偏移对树模型影响更小。5. 系统化封装与现场落地要点5.1 从离线脚本到可调用的诊断系统训练脚本跑通只是第一步。标题里说的是“系统设计”那就不能只是一堆散落的脚本应该有一个可以被外部调用的统一入口。我习惯把离线训练和在线推理分开。离线训练模块负责数据读取、特征提取、模型训练和模型保存产物是一个序列化的模型文件。在线诊断模块负责读取实时或新采集的一段振动信号做同样的特征提取加载模型输出故障类型和对应的置信度。import joblib # 保存模型 joblib.dump(rf_model, bearing_diagnosis_model.pkl) # 推理接口 def predict_signal(signal, model): feats extract_all_features(signal) feat_scaled scaler.transform([feats]) proba model.predict_proba(feat_scaled)[0] label int(np.argmax(proba)) return label, proba如果项目时间充裕可以再用CLI脚本或简单的仪表盘页面把可视化部分补上显示波形、包络谱、分类结果。但核心永远是模型能否稳定工作界面只是锦上添花。5.2 跨工况与噪声环境下怎么保持可用性现场数据不会像公开数据集这么听话。转速可能波动负载可能变化传感器位置也可能不同。这些都会导致特征分布漂移。想提高系统的鲁棒性有几件事可以做。第一训练数据尽量覆盖多个负载档位。公开数据里有0到3马力不同负载的数据把它们混在一起训练模型对负载变化的容忍度会大大提高。实际测试中只用单一负载训练然后在另一个负载上测试准确率可能掉到80%以下混入多负载后能恢复到95%以上。第二对信号做带通滤波滤除低频转频成分和高频随机噪声保留轴承故障能量集中的频带。滤波参数不是随便选的要结合传感器安装位置和轴承结构特征来确定。第三部署时加入置信度阈值判断。当模型输出的最高置信度低于某个阈值时不要强行分类而是标记为“未知状态”转给人工分析。这个在工程上非常重要宁可让系统说“我不确定”也不能盲目报警。6. 常见问题与避坑经验汇总6.1 高频踩坑记录我在这个项目上反复出现过的问题整理成一张表照着排查能少走很多弯路。问题现象根本原因解决措施模型准确率异常高或异常低训练测试样本来自同段数据数据泄露文件级切分保证测试集完全独立所有样本都被预测为正常故障样本量占比太少模型偏向多数类对少数类过采样或调整类别权重滚动体故障识别率特别低冲击特征弱包络谱峰值不明显增强包络谱特征或让CNN模型自行学习特征值数量级差异大时域和频域特征量纲不同做标准化处理部署时和训练时保持一致PCA后测试集报错用全部数据拟合了PCA再划分数据集只在训练集拟合PCA再变换测试集训练集准确率100%测试集偏低模型过拟合增加正则化参数加强交叉验证6.2 我建议的新手入门顺序如果完全是从零开始我的建议是先不要急着上复杂模型按这个顺序走一遍第一步把数据读取和文件解析跑通搞清楚每个文件里的信号是什么。第二步盯着某一类故障比如外圈故障画时域波形和FFT频谱肉眼确认冲击特征。第三步做包络谱分析找到故障特征频率的峰值。第四步构建特征矩阵上随机森林先把流程跑通。这个过程听起来慢其实是效率最高的。很多人上来就写CNN跑了半天发现数据集划分有问题最后准确率虚高反而要回头补特征工程的课。还有一个小技巧每次切片和特征提取之后把样本对应的原始文件名记录下来。某个样本预测错了你能直接追溯到原始数据和特征值排查起来方便得多。我在实际做这个项目时最大的体会是数据理解和特征纪律远比模型选择重要。公开数据集最大的价值不是让你刷出多高的准确率而是让你在一个可靠、可复现的平台上建立对轴承故障诊断全流程的正确手感。以后拿着现场数据再去设计系统就知道先看什么、先做什么、哪些环节最容易出问题。这一套流程跑完之后再往里面添加新工况、新传感器数据或者扩展成迁移学习模式都是顺手的事。