☰
轴承故障诊断实战:从振动信号特征提取到分类模型训练
2026/10/2 3:29:09 网站建设 项目流程

设备维保群里经常有人发来一段轴承振动波形,问"这声音听着像是坏了,到底是内圈还是外圈出了问题?"大多数人的做法是靠耳朵听、靠经验猜,老师傅能猜个八九不离十,但换一个转速、换一台设备就又不准了。轴承故障诊断分类这件事,本质上就是把"老师傅的经验"变成一套可复现的算法流程:传感器采回振动信号,切成长度合适的样本,提取成特征,交给分类模型去判断当前轴承处于什么状态。这篇东西就围绕这条链路来写——数据处理怎么做、特征怎么选、模型怎么训、评估怎么判,以及我在实际操作中反复踩过又爬出来的几个坑。不管你是刚接触故障诊断的研究生,还是工厂里准备上预测性维护的工程师,按这条路线走一遍,基本能独立跑通一个完整的轴承故障分类项目。

1. 轴承故障诊断分类到底在解决什么问题:振动信号里的故障密码

1.1 先搞清自己要分哪几类

做分类之前首先要把类别定义清楚,这是整个项目的锚点。轴承故障最常见的分法是按部件拆:内圈故障、外圈故障、滚动体故障、保持架故障,再加上一个正常状态,这就是最经典的5分类设定。有些项目还会进一步细分故障程度,比如把内圈故障按损伤直径分成早期、中期、严重三档,那类别数量就会膨胀到十几个。我在工程实践中倾向于先做粗分类——把正常和四类常见故障分开,等模型稳定了再考虑加细粒度。理由是类别越多,样本不平衡问题和混淆问题越严重,前期验证链路反而看不清瓶颈在哪。

还有一个容易被忽略的问题:你的分类目标到底是"区分故障类型",还是"区分故障严重程度",这两者对特征和模型的要求完全不同。前者更关注振动信号中故障特征频率的位置,后者更需要关注能量和冲击烈度的变化。我见过不少人拿同一套特征去做这两类任务,效果都一般,原因就是目标没有在数据处理阶段体现出来。

1.2 振动信号里有哪些故障线索

轴承出现局部损伤后,滚动体每次滚过损伤点都会产生一个周期性的冲击,这些冲击在时域波形上表现为脉冲,在频谱上则对应着特定的特征频率。比如外圈故障特征频率BPFO、内圈故障特征频率BPFI、滚动体故障特征频率BSF,它们的数值可以通过转速和轴承几何尺寸直接算出来:

  • 外圈故障频率:BPFO = (n/2) × fr × (1 - d/D × cos α)
  • 内圈故障频率:BPFI = (n/2) × fr × (1 + d/D × cos α)
  • 滚动体故障频率:BSF = (D/(2d)) × fr × (1 - (d/D × cos α)²)

其中n是滚动体个数,fr是转频,d是滚动体直径,D是节圆直径,α是接触角。

现在很多数据驱动方法已经不需要显式计算这些频率了,模型自己能从数据里学出来。但我在项目里仍然建议把特征频率算出来当"参照物",因为后续做结果验证的时候,你会需要确认模型关注的频段和理论故障频率是否对得上。如果模型分类准确率很高但关注频段完全不对,那很可能是学到了数据集本身的一些虚假模式,泛化会出问题。

1.3 公开数据集怎么选

目前轴承故障诊断领域最常用的公开数据是凯斯西储大学(CWRU)轴承数据中心提供的振动数据,基本上是这个方向的事实标准。它有四种状态:正常、内圈故障、外圈故障、滚动体故障,每种故障又有0.007英寸、0.014英寸、0.021英寸等几种损伤直径,采样率分为12kHz和48kHz两档,负载从0到3马力(对应转速大概1730到1797rpm)可以组合。

我建议入门阶段不要一上来就上复杂数据,CWRU就够用了。它的优点是样本质量高、工况标注完整、文献对照丰富;缺点是数据"太干净",特征提取之后分类准确率很容易做到99%,这既是好事也是坏事——方便你验证流程有没有跑通,但容易让人产生"模型很强"的错觉。后面到了实际现场数据,才会感受到落差。关于数据集的划分方式,我特别想强调一点:很多论文的实验设定是在同一负载条件下随机划分训练集和测试集,这种设定下的准确率实际上参考价值有限。更严格的设定是不同负载之间做迁移测试,比如用一个工况的数据训练,去测另一个工况,那才是贴近真实场景的评估方式。这个坑后面详细说。

2. 数据处理链路:把原始振动波形变成特征矩阵

2.1 数据切片与清洗

原始振动数据通常是几秒甚至几十秒连续采集的加速度信号,不能直接整段扔进模型,得先切成长度合适的样本。切片有两种思路:按固定点数切,或者按转频周期切。我推荐按固定点数切,简单可复现,比如每个样本取2048点或者4096点。在转速相对稳定的情况下,2048点在12kHz采样率下大约是0.17秒,已经覆盖了多个转动周期,足以捕捉到周期性冲击的特征。要注意的是,不同转速下同样点数对应的时间不一样,如果转速波动很大,固定点数会导致每个样本包含的转数不同,这时候按转频做角度域重采样再切会更合理,但这是进阶话题,入门阶段先不做。

清洗环节最容易被跳过。传感器偶尔会因为松动、电磁干扰或线缆接触不良产生飞点、断线和异常尖峰,这些数据如果不处理,特征提取阶段会被严重污染。我常用的方法很朴素:先画一下波形总览,看看有没有明显的毛刺和平台段;然后用阈值法把绝对值超过正常范围若干倍的样本直接剔除;再结合缺失值和零值检查,比如某一段连续长时间为0或者恒定值,那就是传感器断线了,直接去掉。这一步虽然不涉及什么高级算法,但直接影响后续所有环节的质量,我见过有人跳过清洗直接提特征,结果模型在飞点上学出了"规律",教训比较惨痛。

2.2 时域特征:最朴素但最有效

时域特征是从波形本身直接统计出来的数值,计算成本低,物理含义清楚。对一段长度为N的振动信号x,常用特征包括:

  • 均方根(RMS):反映振动能量整体水平,公式为 sqrt(1/N × Σx²)。
  • 峭度(Kurtosis):反映波形的冲击特性,正常轴承信号接近高斯分布,峭度约等于3;出现早期点蚀时会明显升高,是检测早期微弱故障的敏感指标。
  • 峰值因子(Crest Factor):峰值与RMS的比值,对磨损类故障比较敏感。
  • 裕度因子、波形因子、脉冲因子、偏度等。

为什么这些指标对轴承故障有效?因为局部损伤会产生周期性冲击,冲击叠加在原本平稳的随机振动上,会显著改变波形的"尖峰程度"和"能量分布"。正常状态下数据比较"平",RMS稳定,峭度接近3;故障状态下波形出现明显的尖峰,峭度和峰值因子会显著变化。这几个特征组合起来,就已经能区分大部分轴承状态了。

下面这段代码是我常用的时域特征提取函数,用numpy十几行就能实现:

import numpy as np def extract_time_features(signal): features = {} features['rms'] = np.sqrt(np.mean(signal ** 2)) features['kurtosis'] = ((signal - signal.mean()) ** 4).mean() / ((signal.std() + 1e-12) ** 4) features['peak'] = np.max(np.abs(signal)) features['crest_factor'] = features['peak'] / (features['rms'] + 1e-12) features['skewness'] = ((signal - signal.mean()) ** 3).mean() / ((signal.std() + 1e-12) ** 3) features['peak_to_peak'] = np.max(signal) - np.min(signal) return features

一个小细节:峭度和偏度计算时分母上的标准差可能为0,特别是恒定的异常段,所以加上一个小epsilon防止除零。代码虽短,但在实际项目中非常稳定。

2.3 频域与时频域特征补全盲区

时域特征能反映能量和冲击,但分辨不了故障在哪个部件上。内圈故障和外圈故障可能有相近的RMS和峭度,但它们在频谱上的能量分布完全不同。所以特征提取还要上频域。

频域特征的计算思路是先把信号做FFT得到频谱,然后从频谱中提取统计量,比如重心频率、均方频率、频率方差、谱峰位置。这些特征抓住了信号在不同频率段的分布差异。更进一步的做法是把原始信号做带通滤波或包络解调,然后再做FFT——包络谱能突出轴承故障产生的低频调制成分,这是振动诊断里一个非常经典有效的预处理手段。

时频域特征则适合转速波动或非平稳工况。常用的有小波包分解、短时傅里叶变换、经验模态分解(EMD)。小波包可以把信号分解成不同频带的子信号,然后计算每个子带的能量占比作为特征,维度可控、解释性也不错。EMD我不建议入门阶段用,它本身不稳定、分解结果受参数影响大,而且计算慢,更适合有明确研究目的的探索场景。实际项目中我通常把时域特征、频域特征、以及简单的小波包子带能量特征组合在一起,构成一个十几维的特征向量,送入传统机器学习模型就足够了。

2.4 标准化、降维与数据集划分

特征提取完以后,数据的量纲差别很大,比如RMS可能是0.1量级,峭度可能是3到10量级。如果不做标准化,距离类模型(比如SVM的RBF核、KNN)会偏向数值大的特征。标准化的标准做法是计算训练集的均值和标准差,然后对训练集和测试集分别应用同一组统计量做z-score归一化。这里有一个我在很多项目中反复看到的错误:先对全部数据做标准化,再做训练测试划分,或者在划分之前用全局统计量做PCA降维。这属于典型的数据泄漏,会让测试结果虚高,后面专门讲。

对于传统机器学习路线,几十维特征已经不算多,降维不是必须的。如果你做可视化或者特征维度特别高,可以用PCA或者t-SNE看一下样本的聚类情况,这对初步判断特征质量和可选模型很有帮助。但要注意PCA必须在训练集上拟合,然后把变换矩阵应用到测试集。

数据集划分是整个实验链路中最关键的一步。两个原则务必遵守:第一,分层抽样,保证训练集和测试集中每个类别的比例大致一致;第二,分组划分,同一个原始数据文件(或者同一个工况)产生的样本要么全部进训练集,要么全部进测试集。如果直接把所有样本随机打乱划分,同文件切出来的相邻样本会同时出现在训练和测试中,模型几乎相当于"见过"测试数据的邻居,准确率会虚高。以我自己的实验为例,同一套特征,随机打乱划分时准确率能做到98%以上,改成按文件分组划分后通常在90%到95%之间,这才是更真实的水平。这个差异在学术论文里经常被忽略,但工程上要落地必须按分组划分来评估。

3. 模型训练的关键环节与选型思路

3.1 传统机器学习:SVM、随机森林与XGBoost怎么选

当特征维度在十几到几十这个量级、样本量在几千到几万时,传统机器学习方法是非常稳妥的选择。我在特征工程做得比较扎实的实验中,SVM(RBF核)和随机森林都能达到很好的效果。SVM在小样本高维特征下有优势,决策边界比较干净;随机森林的优点是对特征量纲不敏感、天然能处理非线性关系,而且可以输出特征重要性,帮助你反过来验证特征提取是否合理。XGBoost在结构化特征上表现也很强,但调参成本略高,样本量不够时反而容易过拟合。

可以这样类比:轴承故障诊断和鸢尾花分类本质上是同一类问题——给定样本、预测类别。鸢尾花数据集直接给你花瓣、花萼的长宽,你拿去喂分类器就行;轴承数据则多了一步,你必须自己从振动波形中构造出"花瓣长度"这个层面的特征。这就是数据处理在故障诊断里为什么如此重要的根本原因。

下面是一个简洁的sklearn训练流程示例:

from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline # X_train, X_test: 特征矩阵; y_train, y_test: 标签 # 注意: StandardScaler只fit在训练集上 model = make_pipeline(StandardScaler(), SVC(kernel='rbf', C=10, gamma='scale')) model.fit(X_train, y_train) acc = model.score(X_test, y_test)

这里有个实操体会:SVM的C和gamma对结果影响挺大,建议用GridSearchCV在训练集上做交叉验证选参,不要直接把sklearn默认参数拿来就用。随机森林对默认参数相对宽容,适合当第一个baseline模型。

3.2 深度学习路线:1D-CNN如何直接吃原始波形

如果你不想做太多手工特征,或者数据量足够大,可以考虑1D-CNN直接在原始波形上做分类。1D-CNN用一维卷积核在时间轴上滑动,自动学习局部冲击形态,卷积堆叠之后能组合出更全局的模式,最终接全连接层输出类别概率。

下面是一个极简但能跑通的PyTorch结构,适合作为baseline:

import torch.nn as nn class CNN1D(nn.Module): def __init__(self, num_classes=5): super().__init__() self.features = nn.Sequential( nn.Conv1d(1, 16, kernel_size=64, stride=8, padding=28), nn.ReLU(), nn.MaxPool1d(kernel_size=2, stride=2), nn.Conv1d(16, 32, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.MaxPool1d(kernel_size=2, stride=2), nn.AdaptiveAvgPool1d(output_size=1), ) self.classifier = nn.Sequential( nn.Flatten(), nn.Linear(32, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, num_classes), ) def forward(self, x): return self.classifier(self.features(x))

在CWRU这类数据量适中的公开数据集上,1D-CNN训练时容易出现过拟合,我一般在训练中打开早停(Early Stopping),同时加上Dropout。还有个容易踩的点是类别不均衡——比如某个故障损伤尺寸的样本明显少于其他类,直接用CrossEntropyLoss会把少数类模型带偏,解决办法是给损失函数加class weight,或者用Focal Loss。多分类用交叉熵,二分类用二值交叉熵BCE,这个概念不要搞混。故障诊断里常做的"正常vs异常"判断就是一个BCE二分类问题,而区分具体故障类型才是多分类交叉熵。

关于预训练模型,目前图像和语言领域动辄搬出resnet、roberta预训练模型,但振动波形领域还没有类似universal的预训练模型可以直接迁移。我在实践中见过两种可行的迁移思路:一是把振动波形转成时频图,然后套用图像分类的预训练模型做微调;二是在大量无标注振动数据上做自监督预训练,再接分类头。第一种做法在公开数据集上提升有限,第二种做起来工程成本不低,除非数据规模真的很大,否则不推荐优先尝试。

3.3 评估指标:准确率之外必须看什么

多分类任务里很多人习惯只看准确率,但轴承故障诊断这种场景,准确率会掩盖很多问题。比如数据集里正常样本占50%,其他故障样本各占10%,你就算把所有样本都判成正常,准确率也有50%。更要命的是,某些故障类型之间天然容易混淆,比如滚动体故障和外圈故障在特征空间里的重叠度往往比较高,只看总准确率根本发现不了这类系统误差。

正确的做法是三件套:混淆矩阵、每个类别的精确率/召回率/F1、以及按故障类型分组的分类报告。混淆矩阵能让你一眼看出哪些类被分错了,分错的方向是什么。精确率关心"你说是这个故障的那些样本里,有多少真的对",召回率关心"这类故障样本里你找回了多少",F1是二者的调和平均。在多分类中建议用macro平均——计算每一类指标后取平均,不受类别样本数量影响。

另外一个在评估中很重要的技术细节是交叉验证方式。朴素分组KFold不够,需要按原始文件或工况分组做GroupKFold,保证同一组的样本不会出现在不同折中。只有这样交叉验证的结果才基本等同真实泛化性能。

3.4 一次完整的实验记录

我把一次典型的实验过程摆出来,给大家一个可对照的基准。特征采用前文提到的时域6维+频域4维+小波包子带能量8维,共18维特征,在CWRU数据上按文件分组划分训练集和测试集,类别为正常、内圈故障、外圈故障、滚动体故障四类。随机森林(n_estimators=500)macro-F1在0.92到0.95之间,SVM(RBF核,C=10)macro-F1能达到0.93到0.96,1D-CNN在不做数据增强的情况下macro-F1大概是0.90到0.95,但训练时间要长得多。三种方法各自的特点对比如下:

方法输入形式核心优势主要短板部署难度
SVM+RBF手工特征向量小样本稳定,可解释特征工程依赖业务知识低
随机森林手工特征向量抗过拟合,特征重要性类别极不平衡时性能下降低
1D-CNN原始波形端到端,无需特征工程数据需求大,需调参中

从这个表格能看出一个规律:特征工程做得好的情况下,传统ML在中等规模公开数据集上已经足够优秀;深度学习的优势要到数据规模变大、特征组合更复杂的时候才真正体现出来。工程项目的起步阶段,我建议永远先用传统ML做一个强baseline,这个baseline如果已经接近业务需求,深度学习就可以缓缓。

4. 实操中踩过的坑与我对诊断泛化的理解

4.1 数据泄漏:准确率虚高的头号元凶

数据泄漏是一个容易踩但后果严重的坑。我把最常见的三种泄漏场景列举出来,每个都是我在实际项目中踩过或者在评审别人代码时见过的。

第一种是全数据集标准化。常见做法是先把所有样本的均值和标准差算出来,然后统一做z-score,再划分训练集和测试集。这样做测试集的统计信息已经被模型间接看到了,测试准确率会比真实值高一点到几个百分点。正确做法是只对训练集做fit,用训练集的均值和标准差去transform测试集。在sklearn里用make_pipeline(StandardScaler(), model)可以天然避免这个问题。

第二种是全数据集特征选择。先用全部数据算出哪些特征重要,然后只保留这些特征再划分训练集测试集。特征选择过程使用了测试集的目标信息,等于考试前老师借了答案给你看一眼。正确做法是特征选择只基于训练集,或者干脆用模型自带的特征重要性属性,在交叉验证循环内部完成选择。

第三种是随机打乱切片划分,这也是影响力最大的一处问题。CWRU数据每段原始记录可以切成几百个样本,同一原始文件切出来的相邻样本,不管在时域还是频域上都高度相似。如果随机把这些样本一起划分,测试集里会混入大量"训练样本的近亲",让模型钻空子。正确做法是按原始文件(或工况)分组,保证同一个文件的所有切片要么训练要么测试,绝不混着来。这一条做得对不对,直接决定你的实验结果可信不可信。

4.2 工况变化导致的泛化崩塌

在公开数据集上训练完模型,你可能觉得效果很好,但拿去换一台设备、换一个转速,准确率可能立刻掉一大截。这个问题的本质是域偏移:不同工况下,轴承的受力状态、转速、负载都变了,振动信号的幅值范围和频率分布都会漂移。我做过一个印象很深的实验:用CWRU的0hp负载数据训练SVM,直接去测1hp、2hp、3hp负载的数据,准确率从95%以上掉到70%出头。而如果训练和测试都来自同一负载,准确率轻松上95%。这说明模型学的更多是特定工况下的信号模式,而不是轴承故障本质特征。

缓解域偏移有几种常用方法。最简单的是做幅值归一化,宣称不同工况下振幅差异带来的影响会被大幅削减;更好一点的做法是提取对转速变化不敏感的特征,比如用特征频率归一化后的能量占比替代原始频谱特征;再进阶的做法是域自适应,利用少量目标工况数据对已有模型做微调。无论采用哪种方法,建模阶段就明确"我要跨工况评估"这件事非常重要——它直接决定了你的训练测试划分方式,也决定了数据采集方案要不要留出目标工况样本。

4.3 样本不平衡与早期故障漏诊

轴承故障诊断里,样本量最少、最难分类的往往是最需要关心的早期故障。CWRU数据里不同损伤直径的样本数量天然不均匀,更大的损伤样本通常更多、特征更明显,模型会偏向学会"厉害故障",对早期小损伤的召回率偏低。从设备维护的角度讲,漏掉一个早期故障意味着后面可能演变成严重停机,代价远大于误报一次。所以我对这类项目的评估标准,永远把早期故障的召回率放在比较高的优先级上。

处理不平衡的思路分三层。第一层是数据层面,少数类过采样或对多数类降采样,或者用SMOTE合成少数类样本,但SMOTE在特征空间线性插值对振动数据有时会产生不合理样本,需要人工检查。第二层是算法层面,给少数类更高的类别权重,或者在损失函数里按类别频率加权,相当于人为抬高少数类的学习优先级。第三层是评价层面,用macro-F1、Recall、PR曲线这类对不平衡更敏感的指标来衡量模型,而不是单看准确率。我在实践中发现,这三层往往需要组合使用,尤其是"数据重采样+损失加权"的组合。

4.4 从公开数据集到现场:部署前你要想清楚的事

跑通公开数据集之后,距离真正的现场落地还有一段不小的路。真实采集的数据没有人工标注的故障类型,你需要自己设计标签来源:可能是检修报告、可能是电流信号联动判断、可能是停机拆检记录。标注成本很高,样本量也相对有限。这时的适切路线是先尽力收集一批可靠标注样本,训练出一个可信度较高的初始模型,再用它去对大量无标注数据进行预测,把高置信度样本人工复核后送回训练集,不断滚动扩充数据,实为半监督迭代,在工程上是比较可行的手段。

现场数据的噪声水平和传感器安装方式也和公开数据集差别很大。公开数据传感器通常紧贴轴承座,信号信噪比高;现场可能有齿轮啮合、电磁干扰、邻近设备振动等多源噪声。我的建议是部署前至少要做三件事:一是用带通滤波把目标频段外的干扰压掉;二是对模型的输出增加置信度阈值,预测概率低于阈值的样本不自动分类,而是进入人工复核队列;三是在现场重新采集一批正常工况数据做域校准,避免模型把现场的固有振动特征误判成故障。这些步骤看起来不起眼,但真正决定了模型能不能被现场运维人员接受。

从我个人的经验来看,整个轴承故障诊断分类项目做完之后回头复盘,最耗时间的地方往往不是模型调参,而是数据划分和特征验证。模型本身只是把数据处理阶段提炼出来的信息做了压缩和映射。所以如果你的效果不理想,别急着换模型,先回去检查数据处理链路。把"按文件分组划分"、"标准化只拟合训练集"、"先看混淆矩阵再看准确率"这三件事刻在脑子里,很多问题能少走不少弯路。后续如果进一步做预测性维护,可以考虑把多个传感器信号融合进来,或者把单个模型扩展成多工况联合训练,但前提还是那一句话:数据处理的严谨性决定了整个分类项目的天花板。

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

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

立即咨询