做设备状态监测的人都明白一个道理:滚动轴承是旋转机械里最脆弱也最关键的一环。我接手这个课题的时候,正赶上现场一台压缩机无故振动超标,拆解后发现外圈已经出现严重的剥落坑。问题在于,从振动信号出现异常到轴承彻底失效,中间是有足够预警窗口的,如果能在早期从信号里识别出故障特征,完全可以把非计划停机变成计划内检修。这个想法驱动我完整走了一遍“基于MATLAB的滚动轴承故障诊断”的流程,从数据采集、信号预处理、特征提取到故障识别模型,每一步都踩了不少坑。这篇博客把这些经验和代码细节整理出来,给准备入行设备故障诊断、或者正在用MATLAB做振动分析的朋友一个参考。
1. 整体设计思路与MATLAB的选型逻辑
1.1 轴承故障诊断的本质是什么
滚动轴承故障诊断的核心任务,是从振动信号中识别出轴承的健康状态。轴承在运转时,滚动体与内外圈滚道接触,任何局部损伤(如点蚀、剥落、裂纹)都会在接触点产生周期性冲击,这种冲击会激励轴承座和传感器产生共振响应,表现为振动信号中的周期性脉冲成分。诊断的本质就是从淹没在大量噪声和背景振动中的信号里,找到这些周期脉冲,并判断它们对应的是外圈、内圈、滚动体还是保持架的故障。
这个问题的难点在于三点:第一,轴承故障信号通常很微弱,尤其是在早期阶段,特征成分的幅值可能只有正常振动水平的百分之几;第二,实际运行环境复杂,齿轮啮合、轴系不对中、不平衡等干扰都会掩盖轴承特征;第三,不同工况下(转速、负载变化)信号特征会发生偏移,模型泛化困难。理解了这些难点,后面每一步做法的逻辑就清晰了——预处理是为了提高信噪比,特征提取是为了量化故障特征,模式识别是为了自动分类。
1.2 为什么选择MATLAB而不是Python
很多初学者会纠结用MATLAB还是Python。我的项目经历给出的答案是:在信号处理链条上,MATLAB的效率比Python高太多。这不单是个人习惯的问题,而是MATLAB的信号处理工具箱和故障诊断相关函数已经非常成熟,很多标准流程只需要几行代码,比如带通滤波器的设计、包络谱分析、小波时频图绘制,都有现成的、经过大量验证的函数可以直接调用。Python当然也能做,但需要自己拼装scipy、numpy、pywt等库,在项目初期调试阶段效率差距非常明显。
MATLAB还有两个很实际的优势:一个是App Designer和工具箱的交互式操作,可以快速查看信号波形、频谱、时频图,对理解数据非常有帮助;另一个是代码可读性高,矩阵运算语法天然贴近信号处理的数学表达,对于工程技术人员来说学习和维护成本低。我项目后期需要把算法部署到测试台,用MATLAB Coder生成C代码也省了不少事。如果你手头已经有MATLAB使用经验,做故障诊断完全没必要换Python;如果是从零开始,我更推荐先用MATLAB跑通全流程,后面有需要再迁移。
2. 数据准备与信号预处理
2.1 数据来源与采集参数设定
我使用的数据来自公开的凯斯西储大学滚动轴承故障数据集,这是行业内最经典、引用最多的轴承故障数据。实验台由电机、扭矩传感器、测功机和被测轴承组成,通过电火花加工在轴承上设置单点故障,故障直径有0.18mm、0.36mm和0.54mm三档,分别对应轻微、中等和严重故障。采样频率有12kHz和48kHz两档,我选择了12kHz档的驱动端加速度信号,因为它的信号频带已经足够覆盖轴承故障特征,数据量也更适中。
采集参数设定是整个诊断流程的地基。这里有两个关键参数要特别留意:采样率fs和采样时长T。采样率决定了能够分析的最高频率(奈奎斯特频率为fs/2),对于滚动轴承诊断,一般要求采样率至少为最高关注频率的2.5倍。12kHz采样率意味着有效分析带宽到6kHz,对于多数工业轴承的故障特征频率(通常在几百赫兹到两三千赫兹)以及轴承座共振频带(一般在3-6kHz)是足够的。采样时长决定了频率分辨率:Δf = 1/T。如果转频是30Hz,故障特征频率在100-200Hz范围,频率分辨率至少要做到1Hz以下才能准确分辨特征频率附近的边带。我取每段信号1秒,频率分辨率1Hz,够用。如果转速更低,就需要延长采样时长。
2.2 预处理该做什么
拿到原始振动信号后不能直接提取特征,必须先做预处理,这一步直接决定了后面特征提取的质量。我的预处理链条分三步:去均值、带通滤波、数据切分。
去均值是必须的第一步。加速度传感器采集的原始信号往往存在直流偏移,这个偏移来自传感器本身的偏置电压和采集电路的零漂。如果不做去均值,做FFT时直流分量会泄漏到邻近频点,掩盖低频段的有用信息。MATLAB里一句x = x - mean(x)就解决了。
带通滤波的选带逻辑值得细说。轴承故障信号的典型频域特征是:低频段有转频及其谐波,中频段有故障特征频率,高频段有轴承座共振峰。为了突出故障冲击成分,需要做带通滤波。我参考了一个工程经验法则:带通范围选在轴承座共振频带,通常用1kHz-5kHz或者更高。但实际操作中这个范围不能拍脑袋定,要先看原始信号的功率谱密度,找出共振峰的位置再确定频带。我用pwelch函数画出功率谱,观察到这个数据集在3kHz-5kHz有明显的高频共振能量集中区域,于是把带通滤波范围定为2kHz-5kHz。滤波器的实现用designfilt设计巴特沃斯带通滤波器,阶数选4到6,阶数太低过渡带太宽,阶数太高容易引起相位失真。注意滤波后要用filtfilt做零相位滤波,避免信号相位偏移影响后续的时域特征计算。
数据切分是把长信号切成固定长度的样本段。这里有一个非常容易犯的错误:切分时相邻样本之间如果重叠太多,会引入数据泄漏,导致训练集和测试集之间存在信息重叠,模型评估结果虚高。我采用的策略是每段信号长度取2048个采样点(约0.17秒),确保每段至少包含若干个转频周期和故障冲击周期,段与段之间不重叠。切分后的每个样本作为一个独立的样本单元,用于后续特征提取和模型训练。
2.3 预处理完整代码示例
下面这段代码是我预处理环节的完整实现,包含了从读数据到输出干净样本的全过程:
% 读取凯斯西储大学轴承数据 % 文件来自12kHz驱动端采集,负载0马力,转速约1797rpm data = load('97.mat'); % 正常状态数据 x_raw = data.X097_DE_time; % 驱动端加速度信号 fs = 12000; % 采样率 12kHz % 第一步:去均值 x = x_raw - mean(x_raw); % 第二步:看功率谱,确定共振频带 [pxx, f] = pwelch(x, hann(4096), 2048, 8192, fs); plot(f, 10*log10(pxx)); xlabel('频率 (Hz)'); ylabel('功率谱密度 (dB/Hz)'); % 观察共振峰,判断带通范围,我这边看到3k-5k有能量集中 % 第三步:零相位带通滤波 d = designfilt('bandpassiir', ... 'FilterOrder', 4, ... 'HalfPowerFrequency1', 2000, ... 'HalfPowerFrequency2', 5000, ... 'SampleRate', fs); x_filtered = filtfilt(d, x); % 第四步:切分为样本段 segLen = 2048; % 每段采样点数 numSeg = floor(length(x_filtered) / segLen); segments = zeros(segLen, numSeg); for k = 1:numSeg segments(:, k) = x_filtered((k-1)*segLen + 1 : k*segLen); end % 每一列是一个样本段,后续特征提取按列处理这段代码里我觉得最值得反复推敲的就是滤波器参数的确定。designfilt里的阶数和截止频率如果选得不合适,要么滤不掉干扰,要么连故障冲击的有用成分都削掉了。之前我偷懒直接用默认参数跑,后面做包络谱发现特征频率处的幅值异常低,反过来查才发现带通范围设错了,把共振峰的主要频段挡在了外面。从那以后我固定了一个习惯:先可视化功率谱,再确定滤波参数,绝不在参数没看清之前盲目定滤波器。
3. 特征提取——从时域到时频域
3.1 时域统计特征:快速有效的第一道筛选
时域特征是最直观、计算成本最低的故障指示器。基本的思路是:轴承出现故障后,振动信号的统计特性会改变,比如幅值增大、脉冲成分增多、波形偏离正态分布。我提取的时域特征包括:均方根值、峭度、偏度、峰值因子、波形因子、脉冲因子和裕度因子。
RMS值是反映振动能量总体水平的指标,轴承磨损或剥落会导致RMS上升,但它对早期局部损伤不敏感,因为局部冲击的持续时间短、能量占比小。峭度是四阶中心矩标准化的结果,对脉冲成分极其敏感——正常轴承信号的峭度接近3(正态分布),而早期故障信号由于周期性冲击的存在,峭度可能飙升到10以上。这也是为什么峭度被广泛用来做轴承早期故障的快速筛查指标。峰值因子(峰值除以RMS)和脉冲因子也有类似的敏感性,但它们的缺点都是抗噪性差,一个极端噪声尖峰就能把它们拉高。
实际处理中,我会把所有时域特征拼成一个特征向量。比如对每个样本段计算RMS、峭度、偏度、峰值因子、波形因子、脉冲因子、裕度因子,这样每个样本得到一个7维特征向量。正常状态和故障状态在这个特征空间里往往已经有明显的可分性,可以用scatter3先画出来看看分布。不过时域特征有个天然短板——它们丢失了频域信息,无法区分外圈故障和内圈故障(两者都产生周期性脉冲,时域统计特性相近),所以光靠时域特征只能判断“有没有故障”,不能回答“故障在哪里”。要回答这个问题,必须做频域分析。
3.2 频域与包络谱:定位故障部位的关键
频域分析的核心是找到故障特征频率。不同的轴承故障部位对应不同的特征频率,它们由轴承几何参数和转频决定。常用计算公式如下:
- 外圈故障特征频率 BPFO = (n/2) × fr × (1 - d/D × cos α)
- 内圈故障特征频率 BPFI = (n/2) × fr × (1 + d/D × cos α)
- 滚动体故障特征频率 BSF = (D/(2d)) × fr × [1 - (d/D × cos α)²]
- 保持架故障特征频率 FTF = (fr/2) × (1 - d/D × cos α)
其中n是滚动体个数,fr是轴转频(Hz),d是滚动体直径,D是轴承节圆直径,α是接触角。以我实验用的6205-2RS深沟球轴承为例:n=9,d=7.94mm,D=39.04mm,α=0°,在转速1797rpm(fr≈29.95Hz)下:
- BPFO ≈ 4.5 × 29.95 × (1 - 7.94/39.04) ≈ 107.3Hz
- BPFI ≈ 4.5 × 29.95 × (1 + 7.94/39.04) ≈ 162.2Hz
- BSF ≈ (39.04/15.88) × 29.95 × [1 - (0.2034)²] ≈ 69.5Hz
- FTF ≈ 14.975 × (1 - 0.2034) ≈ 11.9Hz
这些计算值就是后面对照频谱图判断故障类型的“标尺”。直接对原始信号做FFT的频谱往往在共振频带处有一大片能量隆起,故障特征频率反而被淹没。这时候就需要用希尔伯特变换做包络解调,把高频共振成分中的调制信息提取出来。包络谱分析的逻辑是:故障冲击激励轴承座共振,形成高频振荡,而故障特征频率恰恰调制在这个高频振荡上。希尔伯特变换能求出信号的上包络(解析信号的幅值),对包络再取FFT,就把“高频载波”换成了“低频调制频率”,故障特征频率就清晰暴露出来了。MATLAB里直接用abs(hilbert(x_filtered))得到包络,然后对包络做FFT。
我在实际诊断中遇到过这样一个典型案例:一段外圈故障信号,直接看原始FFT频谱,只看到4.5kHz附近有个大包,完全分不清故障类型;做了包络谱之后,在107Hz处出现明显峰值,并且还有二倍频约214Hz的谐波,这个“基频+谐波”的谱线组合模式非常典型,基本可以确认外圈故障。内圈故障在包络谱上的特征是故障特征频率FSI处有峰值,同时由于内圈随轴旋转,故障点相对传感器的位置周期变化,会额外产生转频fr的边带调制,所以看包络谱时如果发现BPFI两侧有间隔约fr的边带,更加支持内圈故障的判断。滚动体故障的情况复杂一些,因为滚动体自转的同时还会在保持架内公转,故障点周期性改变径向载荷方向,所以包络谱中BSF处的峰值往往不如外圈故障那么突出,有时还需要结合时频图综合判断。
3.3 时频域方法:处理变转速工况的进阶方案
固定转速工况下,频域分析已经足够。但实际工业设备的转速往往是波动的,转频和故障特征频率随时间漂移,直接用FFT频率成分被“平均”掉,谱峰模糊。这种情况就需要用时频分析。我尝试了两种方法:短时傅里叶变换(STFT)和连续小波变换(CWT)。
STFT通过窗函数把信号切成短段,每段做FFT,得到频率随时间变化的二维图谱。MATLAB里的spectrogram函数可以直接画出瀑布图。它的局限在于窗长固定,时间分辨率和频率分辨率不可兼得——窗越长频率分辨率越高但时间分辨率越差。对于轴承这种瞬态冲击信号,STFT常常显得“糊”。
CWT用可伸缩平移的小波基函数来替代固定窗,在低频段频率分辨率好,高频段时间分辨率好,这种多分辨率特性很契合轴承冲击信号的时频特性。MATLAB里cwt(x_filtered, fs)一行命令就能得到小波时频图,实时显示效果相当直观。在时频图上,正常轴承信号的背景均匀;出现故障后,可以看到与特征频率对应的横向亮条带,冲击越大,条带越亮。
不过,小波变换也有代价:计算量比STFT大得多,12kHz采样率、1秒信号做CWT,在普通笔记本上要几十秒。而且小波基函数和分解层数需要根据信号特性调,我常用的经验是选Morlet小波(MATLAB默认),尺度范围设为自动,实际效果已经足够。如果要用小波特征做批量样本的机器学习分类,时间成本会是个问题。我在项目中做了取舍:定转速数据集用FFT+包络谱做特征提取,变转速或者验证“技术展示效果”时才上CWT时频图。
4. 故障识别模型的选择与实现
4.1 传统机器学习路线:SVM和KNN
特征提取完成后,故障识别问题就变成了一个标准的分类问题。每个样本已经用一个特征向量表示(我拼接了时域特征和包络谱特征,合计约30维),带标签,接下来要做的就是把数据集划分为训练集和测试集,训练分类器,评估泛化性能。
我第一个用的分类器是支持向量机(SVM)。MATLAB里的fitcecoc函数可以直接训练多分类SVM,底层自动为每个类别对构建二分类器,用一对一策略完成多类判别的组合。这里有一个参数至关重要:核函数的选择。我测试了线性核和高斯核(RBF),发现线性核在四分类任务(正常、外圈故障、内圈故障、滚动体故障)上准确率只有85%左右,换RBF核之后可以达到96%以上。原因很直观:不同故障类型的特征空间分布并不是线性可分的,特别是外圈和内圈故障的特征有部分交叠,RBF核通过隐式映射到高维空间,把这些交叠区域分开了。用fitcecoc的时候,我设置了'OptimizeHyperparameters', 'auto',让MATLAB自动做超参数调优,跑一轮交叉验证大概需要几分钟,省去手动调gamma和KernelScale的麻烦。
K近邻(KNN)是另一个简单但有效的选择,fitcknn跑起来极快,关键参数是邻居数K和距离度量方式。我发现K=5、距离度量用欧氏距离效果已经不错,准确率和SVM相当,但预测时间远短于SVM。这给了我很重要的启发:很多场景下不需要追求“最先进”的模型,简单模型如果特征提取得足够好,效果不输复杂模型。我之前看过一些入门的代码,上来就用深度学习,其实没必要——特征质量决定上限,分类器只是逼近这个上限。
4.2 深度学习的尝试:一维CNN和LSTM
传统机器学习方法需要人工设计特征,而深度学习方法可以自动从原始信号中学习特征表示。我用MATLAB深度学习工具箱搭建了一维卷积神经网络(1D-CNN),把原始振动信号(2048点)直接作为输入,网络结构是:卷积层(16个滤波器,核大小3)→ ReLU → 池化层 → 卷积层(32个滤波器)→ ReLU → 池化层 → 全连接层 → Softmax输出层。训练用trainNetwork,优化器用Adam,初始学习率0.001,BatchSize 64,最大训练轮数30。
训练过程中我遇到的最大问题是过拟合——训练集准确率很快跑到99%,但验证集准确率只有88%左右。特征维度高、样本量少(每个类别切分后只有几百个样本),是过拟合的两个直接原因。我加了dropout层(概率0.5),情况有所缓解,但仍不如SVM好。另一个问题是训练时间,CPU训练一轮要十分钟左右,如果没GPU就得很有耐心。最终我评估下来,在这个数据集上1D-CNN的优势并不明显,反而SVM+人工特征的组合更稳定。这让我坚定了“先特征后模型”的思路,深度学习不是银弹,尤其在样本量不够大的工业场景里,人工特征的先验知识仍然很有价值。
LSTM我也试过,用于建模信号中的时序依赖。理论上轴承振动信号是周期性的,LSTM应该能捕捉到这种时间结构。但实践效果一般,主要原因是我的样本段长度只有2048点,LSTM的参数规模相对样本量来说偏大,训练不稳定,准确率在92%左右波动。如果需要处理连续长时信号、在线流式诊断,LSTM才有更大优势,短样本场景意义不大。
4.3 结果评估与对比
模型训练完后,评估不能只看准确率。我用了混淆矩阵和逐类别的精确率、召回率、F1分数来分析每一类的错误分布。MATLAB里confusionchart直接画出混淆矩阵,直观方便。我的SVM模型在正常状态和外圈故障上的识别几乎没有错误,但在内圈故障和滚动体故障之间偶尔混淆。这个现象有物理背景:内圈故障和滚动体故障的包络谱特征本身就比较相似(都有较强的转频调制成分),单纯从频域特征上区分确实困难。后来我加入了时频域特征(比如CWT小波系数的能量分布)作为额外维度,把混淆率降下来了将近一半。
评估时还有一个关键点:数据集划分必须注意不能泄漏。我踩过一个坑——直接把所有样本随机打乱再划分训练测试集,结果性能虚高到99.8%。原因是相邻时间段的样本来自同一段连续信号,高度相关,随机划分导致“训练集中出现过测试集的信息”。正确做法是按“时间片段”划分,保证训练集和测试集来自不同的时间窗口,我后来改成前80%时间的样本作为训练集,后20%作为测试集,性能立刻“正常”地下降到96%左右。这个降幅看似“变差了”,但反映的是真实泛化能力。
5. 常见问题与排查经验
5.1 数据不均衡问题怎么处理
真实工业场景中,正常状态的样本远多于故障状态的样本,可能出现9:1甚至更极端的比例。直接用原始数据训练,分类器会“学会”把大部分样本判为正常,因为这样整体准确率已经很高,但故障样本识别率极低。我在模拟这种场景时试过几种方法:最简单的是欠采样,随机丢弃正常样本使两类数量均衡,但这样浪费了大量数据;更推荐的是过采样,用SMOTE算法合成少数类样本,MATLAB里可以用smote函数实现(部分工具箱版本支持),在特征空间中对少数类样本插值生成新样本。实际测试中,SMOTE后故障类别的召回率提升了20多个百分点,整体F1从0.82上升到0.93,效果明显。如果数据倾斜极端且原始信号段充足,也可以考虑先切分原始信号来扩充少数类,再在特征层面做均衡。
5.2 采样率不足、信号混叠和滤波器失真的排查
采样率不足在早期做实验时常常被忽略。我试过用4kHz采样率去分析一个故障特征频率为160Hz的轴承信号,理论上160Hz远低于奈奎斯特频率2kHz,应该没问题,但实际出来的包络谱乱成一片。排查后发现,问题出在轴承座的共振频率(可能在6-8kHz,远超4kHz采样率的分析范围)与低频故障特征频率之间的调制关系上。包络解调需要“高频载波”存在才能解出“低频调制”,采样率不足导致高频共振成分被混叠到低频区,破坏了包络谱的有效性。解决方法是:要么提高采样率,要么先做模拟滤波(抗混叠滤波)再降采样。工业测试中做预分析时,我一般先用48kHz采样,确认主要能量分布后再决定是否降到12kHz,避免一开始就丢失高频信息。
5.3 滤波器参数调试经验:先画图,再定参
滤波器设计看似简单,实际坑最多。我最初做带通滤波时直接指定2kHz-5kHz,没先观察功率谱,结果把一段在1.8kHz有明显共振峰的信号给滤“削”了。经验教训:任何滤波参数都要基于信号的实际谱特征来决定。具体流程分三步:先用pwelch看全频段功率谱,标出共振峰位置;再根据共振峰宽度确定滤波带宽(比如共振峰中心在3.5kHz、宽度约1kHz,带通就设3kHz-4kHz);最后用fvtool查看滤波器幅频特性,确认过渡带衰减达标。此外要检查滤波是否引入了相位失真,用filtfilt做零相位滤波后,幅值和相位都不会被扭曲,这对后续提取冲击波形很重要。
5.4 数据切分窗口长度选择的经验法则
窗口长度(每段信号的采样点数)选择直接影响特征提取的稳定性。窗口太短,段内可能不包含完整的故障冲击序列,峭度等统计量波动大;窗口太长,段数变少,训练样本不足。我的经验法则是:每段至少包含10到20个故障特征周期,同时确保转频周期的整数倍关系。以转频30Hz、故障频率160Hz为例,10个故障周期约0.0625秒,对应750个采样点,实际取2048个点为好(留足冗余,同时是2的幂,方便FFT计算)。另外切分样本后建议做个快速检查:随机抽几个样本画时域波形,确认冲击特征还在,没有在切分时被切断。
5.5 MATLAB版本与工具箱兼容性
有些读者可能在代码运行中遇到“函数未定义”的问题。我用的部分函数(如designfilt、filtfilt、fitcecoc、cwt)需要Signal Processing Toolbox、Statistics and Machine Learning Toolbox和Wavelet Toolbox。运行代码前先用ver检查工具箱是否安装完整。不同版本间cwt的调用方式有差异:R2021a之前用的是cwt(x, 'bump', fs)这种语法,R2021b之后改为cwt(x, fs),旧脚本可能直接报错。建议在使用前doc cwt查看当前版本的帮助文档。如果工具箱缺失,很多信号处理函数用基础MATLAB也能自己写,但工程效率会低很多,还是建议装好对应工具箱。
结语
这个项目做完之后的体会是,滚动轴承故障诊断的完整链路里,真正决定成败的往往不是模型多高级,而是每一步是否做扎实:预处理时是否确定了正确的带通范围,特征提取时是否理解了每种特征的物理含义和局限,数据划分时是否避免了泄漏,评价时是否看了混淆矩阵而非只看准确率。MATLAB在这条链路上确实提供了非常完整的工具支持,但工具终究只是放大“对问题的理解”,理解越深,工具越能派上用场。如果你刚开始走这条路,我建议先用公开数据集跑通全流程,再尝试换转速、换故障尺寸、加噪声来测试方法的鲁棒性,这个过程获得的感知比任何模型代码都更宝贵。