轴承故障诊断是机械健康监测领域绕不开的“入门题”,而 CWRU(某知名高校公开的轴承振动数据集)几乎就是所有论文、所有教程的第一站。我这么说吧,不管你是搞设备的、搞算法的,还是刚进实验室的研究生,基本都绕不开这个数据集。我最早跑这个数据集的时候,其实心里很没底,Matlab 也只算半吊子水平。但真等到自己把数据读进来、画出频谱、看到故障特征频率一串干净的倍频峰之后,那感觉怎么说呢——是真的上头,一边跑一边想“再来一组,再来一组”。
这篇文章,我就把整套玩转 CWRU 数据的流程拆开来讲:从数据集怎么选、文件怎么读,到特征频率怎么算、包络谱怎么做,再到怎么把诊断流程做成一个能自动判别的小系统。适合刚接触故障诊断、想拿公开数据练手的朋友,也适合那些已经在跑实验但总觉得没搞明白原理的人。我把那些查不到、也没人写的细节一并放出来,照着敲就能出图,出完图你就知道这东西为什么能让人上头。
1. 认识数据:CWRU 轴承数据到底讲了什么故事
1.1 轴承故障诊断为什么拿它当“通用语言”
所有做故障诊断的人都会遇到一个问题:实验数据不好拿。真实工业现场的故障轴承难得、工况复杂、还涉及保密,没几个学生能随便进车间采集数据。而实验室自己搭试验台又贵又麻烦,转速、负载、传感器安装稍有不慎,数据就废了。这时候 CWRU 公开数据集就派上用场了。它是在一台专门的轴承试验装置上采集的,用加速度传感器分别采集驱动端和风扇端的振动信号,故障是事先用电火花在轴承上加工出来的单点损伤,涵盖正常、内圈故障、外圈故障、滚动体故障四大类,故障尺寸从 0.18mm 到 0.53mm 有好几档,负载从 0 到 3 马力分了四档。可以说,这数据集把初学者需要的基本变量全给了,而且数据本身是高质量的,信噪比在公开数据集里算非常友好。
换句话说,CWRU 就是故障诊断界的“标准普通话”。全世界的研究者都在用同一套数据验证算法,你看论文时看到的 107Hz、162Hz、19Hz 这样的频率值,全都是从这套数据里算出来的。大家用同一口“普通话”交流,方法和结论才能互相翻译。你如果自己闭门造车搞一套数据,论文审稿人第一句就会问你:为什么不用公开数据验证?所以想入门,先把这个数据集玩透,等于把行业的共同经验装进了自己兜里。
1.2 数据文件的命名和结构,先看清楚再动手
CWRU 下载下来的文件是 .mat 格式,用 Matlab 直接 load 就能读,但里面变量名有讲究。比如常见的X097_DE_time,这个变量名拆开看就是:样本编号 097,DE 表示驱动端(Drive End),time 表示时域波形。对应地还有X097_FE_time,这是风扇端(Fan End)的数据。DE 和 FE 是两套传感器,采集的位置不同,信号特征也有差异,做分析前一定要先确认自己读的是哪一端。
这里有一个我见过很多新手踩的坑:直接load('105.mat')之后,工作区里冒出来好几个变量名,有人随手就取第一个用,结果发现波形长得乱七八糟,后来才意识到那可能是 FE 数据或者别的通道。正确做法是 load 完之后先执行一次whos,看清楚变量名再选。另外,CWRU 还提供 12kHz 和 48kHz 两种采样频率的数据文件,48kHz 那批通常文件名里会带标注,下载的时候看清楚,别把不同采样率的数据混在一起做对比,那样得到的频率分辨率是不一样的。
还有一点让我当初折腾了很久:外圈故障数据里,故障点相对加载方向的位置不同,分别有 3 点钟方向和 6 点钟方向两种记录方式。同样一个外圈故障,不同位置测出来的冲击幅度和调制特征是不一样的。所以选数据做训练集的时候,别把这两种混在一起当同一类,分类器会因为样本内部不一致而学得稀里糊涂。这个细节很多教程压根不提,但对实际效果影响很大。
1.3 数据分组怎么选:从最简单的“二分类”起步
我第一次上手的时候贪多,想着一次性把 4 类故障全部分出来,结果光整理文件名就花了半天,模型效果还特别差。后来学乖了,建议所有新手也这样:第一轮只做“正常 vs 外圈故障”的二分类。原因很简单,外圈故障的特征频率最清晰、冲击最稳定,而且它不随转频旋转,故障特征在包络谱上就是一根根等间隔的谱线,几乎没有调制边带干扰。先把这条路跑通,对 CWRU 数据的脾气有感觉了,再逐步加入内圈故障和滚动体故障。
第二轮再扩展成三分类或者四分类也不迟。内圈故障和滚动体故障相对麻烦,因为故障位置随旋转变化,信号的冲击幅度会周期性波动,包络谱里会混入转频及其谐波的调制边带。这是物理规律决定的,不是你代码写错了。提前有这个概念,后面看到奇奇怪怪的谱图就不容易慌。
2. 把特征频率算明白,诊断就成功了一半
2.1 轴承几何参数与特征频率公式
轴承故障诊断的核心思想可以概括成一句话:轴承每转一圈,故障点就会和其他表面接触一次或几次,每次接触都产生一个冲击,这个冲击以一定频率重复出现,这个频率就是故障特征频率。算出这个频率,再在频谱或包络谱里找到对应位置的谱线,诊断就算完成了大半。
要算特征频率,先得知道轴承的几个关键几何参数:滚动体个数 n、滚动体直径 d、节圆直径 D、接触角 α。CWRU 数据用的是 6205-2RS 深沟球轴承,参数大概是滚动体 9 个,滚动体直径约 7.94mm,节圆直径约 39.04mm,接触角取 0 度。把这些参数代入公式,可以得到一个非常实用的结论:外圈故障频率约等于转频的 3.585 倍,内圈故障频率约等于转频的 5.415 倍,滚动体故障频率约等于转频的 2.33 倍,保持架故障频率约等于转频的 0.398 倍。这个倍频系数在 CWRU 社区里已经是被反复验证过的常数。
这里要特别提醒:这个系数是针对 6205 这个型号的轴承。很多初学者把 3.585 当成“轴承外圈故障的万能系数”,拿去算别的轴承,结果对不上号就开始怀疑自己。不同轴承的滚珠个数、直径不同,系数完全不同。正确做法是拿到轴承型号先查参数,再按公式重算一遍。代码里我习惯把轴承参数写成一个结构体,改型号时只改这一处,后面所有计算自动更新。
2.2 拿 0 负载数据手算一遍,建立直觉
选 CWRU 0 负载、0.007 英寸故障的那组数据来做例子。0 负载对应转速大约是 1797 rpm,转频就是 1797 除以 60,约等于 29.95Hz。乘上对应系数,外圈故障特征频率约 107.4Hz,内圈约 162.2Hz,滚动体约 69.8Hz。这几个数,你会在无数论文里反复看到,可以说刻进了故障诊断从业者的 DNA。
这里有个很重要的直觉训练:外圈故障大概 107Hz,说明每秒钟故障点与滚动体接触大约 107 次。而转频只有 30Hz,也就是说外圈故障的冲击频率远高于轴每秒钟转过的圈数。如果你的频谱图上看到 30Hz 或者 60Hz 处有大峰,那大概率是轴转频和它的二倍频,不是故障特征。这种区分能力是看谱图的基本功,用 CWRU 数据练手最合适,因为答案已知,你可以拿计算结果去验证自己的判断。
2.3 为什么直接看频谱经常找不到故障频率
我最初犯过的错误是拿到时域信号直接做 FFT,然后盯着频谱图找 107Hz。奇怪的是,在原始频谱上 107Hz 处往往没有明显尖峰,反而在几千赫兹的地方出现一大片隆起的“山包”。这让我一度怀疑算错了频率。
后来才明白,轴承故障产生的冲击并不以故障特征频率直接辐射振动能量。故障冲击相当于一个很窄的脉冲,在频域上它的能量分布很宽,会激起轴承系统的固有共振。也就是说,高频共振是“载波”,故障特征频率是“调制信号”,传感器测到的其实是共振频段的调幅信号。直接看原始频谱等于只看到载波,根本看不到低频的调制内容。所以要先把高频共振频段滤出来,再做包络解调,把调制信号恢复出来,才能在频谱的低频段看到那一串故障特征频率的谱线。
打个比方,这就像听远处有人敲鼓,你听到的不是一下一下的鼓点,而是敲击引发的山谷回声连成一片,只有把回声滤掉、单独提取鼓点节奏,才能数清楚每秒敲了几下。包络谱做的就是这件事。
3. Matlab 实操:从波形到包络谱的完整流程
3.1 数据读取和参数初始化
打开 Matlab,新建一个脚本,先把轴承参数、采样率、目标特征频率等都定义好。这一步看似琐碎,但在后面调试时会省掉大量重复劳动。
% 参数设置 fs = 12000; % 采样率 12kHz(选DE端数据) load('X097_DE_time.mat'); % 正常轴承数据 x_normal = X097_DE_time; load('X105_DE_time.mat'); % 内圈故障 0.007英寸 0负载 x_fault = X105_DE_time; % 6205轴承参数 bearing.n = 9; bearing.d = 7.94; bearing.D = 39.04; bearing.alpha = 0; rpm = 1797; fr = rpm / 60; % 特征频率系数 bpfo = bearing.n / 2 * fr * (1 - bearing.d / bearing.D * cos(bearing.alpha)); bpfi = bearing.n / 2 * fr * (1 + bearing.d / bearing.D * cos(bearing.alpha)); bsf = bearing.D / (2 * bearing.d) * fr * (1 - (bearing.d / bearing.D * cos(bearing.alpha))^2);这里有三个小提醒。第一,load 进来之后要看变量名,比如X105_DE_time这个字段名一定要和文件里的实际变量名完全一致,写错一个字母就提示找不到变量。第二,先用whos X105_DE_time确认数据类型,CWRU 的 mat 文件里存的是 double 列向量,直接可用。第三,如果你下载的是 48kHz 版本,fs 要改成 48000,不然后面所有频率轴全是错的。
3.2 时域波形对比:先看故障信号长什么样
时域波形虽然不能直接用来分类,但它是建立“手感”的第一步。把正常和故障的波形画在同一张图上,用tiledlayout分两个子图,能很直观地看出区别。
t = (0:length(x_normal)-1) / fs; figure tiledlayout(2,1) nexttile plot(t(1:fs*1), x_normal(1:fs*1)) title('正常轴承时域波形') xlim([0 1]) nexttile plot(t(1:fs*1), x_fault(1:fs*1)) title('内圈故障时域波形') xlim([0 1])正常轴承的波形基本是平稳的随机振动,看不出明显尖峰。故障轴承的波形上能隐约看出周期性冲击,特别是 0.021 英寸这种大故障,冲击幅度大到一眼就能看出来。但 0.007 英寸这种早期微弱故障,时域上冲击并不算突出,直接拿肉眼分辨容易误判。所以 CWRU 数据也好,真实数据也好,早期故障诊断必须靠频域和包络分析,不能看图说话。
3.3 原始频谱:找到共振频带
接下来做 FFT 看频谱。这里我强烈建议加窗处理,否则频谱泄漏会严重影响谱线清晰度。用汉宁窗,取一段数而不是整段 10 万点,通常取 8192 点或 16384 点就够了,既保证频率分辨率又节省计算时间。
seg = x_fault(1:8192); seg = seg .* hann(length(seg)); X = fft(seg); f = (0:length(X)-1) * fs / length(X); X_mag = abs(X(1:length(X)/2)); f = f(1:length(X)/2); figure plot(f, X_mag) xlim([0 6000]) title('故障信号原始频谱')在这个频谱上,你会看到几百赫兹以下通常杂乱但幅值不大,而在 2000Hz 到 5000Hz 之间有一个或多个能量集中的“山包”。这就是轴承系统的共振频带,也是后面带通滤波要选的区域。每个故障类型甚至每个故障位置,共振峰的位置可能略有差异,建议用spectrogram函数看看时频图,确认共振带随时间是否稳定,再确定滤波范围。
有一个非常容易犯的错:有人为了省事,直接把整个频段拿去包络解调,不做带通滤波。这样会在包络谱里引入大量与故障无关的低频干扰和噪声,107Hz 那根谱线会被淹没,根本看不出来。带通滤波不是可选项,而是必需步骤。
3.4 包络谱实现:共振解调的精髓
包络谱的流程是:带通滤波 → Hilbert 变换求解析信号 → 取瞬时幅值(包络)→ 对包络做 FFT。Hilbert 变换在 Matlab 里直接用hilbert函数,但很多初学者会在包络上犯一个错误:hilbert返回的是复解析信号,直接abs得到的是包络,再fft就对了吗?对的,这是标准做法。但要注意,包络信号里还有一个直流分量,代表平均幅值,做频谱时 0Hz 处会有一个大尖峰,别被它吓到,用xlim只看低频段就行。
% 带通滤波,共振带取 2000-5000Hz bp_fault = bandpass(x_fault, [2000 5000], fs); % Hilbert 包络 env = abs(hilbert(bp_fault)); % 对包络做 FFT env_seg = env(1:8192); env_seg = env_seg .* hann(length(env_seg)); Env = fft(env_seg); f_env = (0:length(Env)-1) * fs / length(Env); Env_mag = abs(Env(1:length(Env)/2)); f_env = f_env(1:length(Env)/2); figure plot(f_env, Env_mag) xlim([0 500]) xlabel('频率 (Hz)') ylabel('幅值') title('内圈故障包络谱')在这张包络谱上,你就能看到在 162Hz、324Hz、486Hz 这些位置出现一根根清晰的谱峰,间隔刚好就是内圈故障特征频率。这就是故障冲击的“节奏”。看到这个图,基本就能拍板:轴承内圈存在故障。
关于共振带的选择,我用的是 2000-5000Hz。但如果你的数据和我不一样,别硬套,先看原始频谱里的共振峰在哪。还有一种更稳妥的办法是“窄带包络”对比:分别拿不同的频带做包络谱,看哪个频带下面故障特征峰最干净,就选哪个。多试几次,你就有了选频带的直觉。
3.5 外圈、内圈、滚动体三类故障的谱图对比
跑通一个样本之后,建议把所有故障类型都跑一遍,把四张包络谱放在一起对比。整理成下表,是我实测跑出来的典型结果:
| 数据类别 | 特征频率 | 包络谱特征 |
|---|---|---|
| 正常 | 无明显特征峰 | 谱线平缓,无突出谱峰 |
| 外圈故障(0.007英寸) | 约 107Hz | 107Hz 及其整数倍处出现等间隔谱峰,谱峰尖锐 |
| 内圈故障(0.007英寸) | 约 162Hz | 162Hz 及其整数倍处出现谱峰,但周围可能出现边带 |
| 滚动体故障(0.007英寸) | 约 70Hz | 70Hz 附近谱峰相对较弱,有时需要更长数据才能看清 |
这里有个提醒:滚动体故障的包络谱经常让人怀疑人生,因为同类样本谱峰可能忽高忽低,有时 70Hz 处就是一根小峰,旁边还有一大堆杂峰。原因是滚动体故障并非每转都撞击内外圈,它有时会“失谐”,冲击的幅度和周期不稳定。CWRU 数据里滚动体故障本来就是最难分的一类,如果第一次没跑出漂亮的谱峰,不是你错了,是这一类本身就有难度。主流论文里滚动体故障识别准确率普遍低于内外圈,原因就在这里。
4. 把诊断做成一个小系统:特征指标与自动判别
4.1 时域统计指标:峭度是最好用的“体检指标”
包络谱适合人工判读,但如果你想批量处理很多样本,或者想做一个自动分类系统,只靠谱图肯定不行。得把每个样本压缩成若干数值特征,让分类器来学。时域指标里最经典的是峭度(Kurtosis)。正常轴承振动接近高斯分布,峭度值大约在 3 附近。一旦出现冲击性故障,信号的分布会变得“两头重”,峭度值显著攀升,常常超过 5 甚至更高。
峭度公式如下:
kurtosis_val = kurtosis(x_fault);除了峭度,还可以组合 RMS、峰值因子、波形因子、脉冲因子、偏度等指标。每种指标从不同侧面描述信号的幅值分布和冲击性。它们各自对故障的敏感度不一样,组合起来变成一个特征向量,能较好地表征不同故障类型。我用过的最经典组合是[RMS, 峭度, 峰值因子, 脉冲因子, 波形因子],配 kNN 分类器,就能在 CWRU 四分类任务上拿到相当高的准确率。
4.2 特征自动提取与分类模型搭建
要训练分类器,得先构造样本集。CWRU 每个 mat 文件长达几十秒,里面含很多冲击周期,直接整段作为一个样本太粗,而且样本数不够。正确做法是把长信号切成小段,每段取 0.5 秒或 1 秒,一段就是一条样本,这样每个文件能产生几十条样本,训练集一下子丰富起来。
function feats = extract_features(x, fs) % 每段取 0.5 秒 seg_len = round(fs * 0.5); n_seg = floor(length(x) / seg_len); feats = []; for i = 1:n_seg seg = x((i-1)*seg_len+1 : i*seg_len); rms_val = rms(seg); kurt_val = kurtosis(seg); peak_val = max(abs(seg)); crest = peak_val / rms_val; impulse = peak_val / mean(abs(seg)); shape = rms_val / mean(abs(seg)); feats = [feats; rms_val, kurt_val, crest, impulse, shape]; end end把所有类别的特征拼成一个大矩阵,每行是一个样本的特征向量,再补一列标签,分类器就准备好了:
% X_all 是特征矩阵,y_all 是对应标签 mdl = fitcknn(X_all, y_all, 'NumNeighbors', 5); cv = crossval(mdl, 'KFold', 5); acc = 1 - kfoldLoss(cv); fprintf('5折交叉验证准确率: %.2f%%\n', acc * 100);kNN 是首选,因为它简单、不用调参、解释性强。等你对特征工程有感觉了,再换 SVM 或者随机森林,不会费太大力气。CWRU 数据本身干净,传统特征加上简单分类器已经能做到 95% 以上准确率,这在真实工业数据上几乎不可能,所以别被这个数字迷惑。它适合学原理,但真实现场数据永远更脏、更难。
4.3 深度学习是不是一定更好
这个问题经常被读者问。我的观点是:如果你只是入门,先用传统信号处理和机器学习把流程走通,理解每一步在解决什么问题,再上深度学习也不迟。CWRU 上很多人直接用一维 CNN 对原始信号分类,准确率确实也很高,但问题是,你很难从 CNN 里直观看到 107Hz、162Hz 这些物理量,出了问题也不知道怎么调试。
深度学习的黑箱特性加上公开数据的高质量,很容易让人产生“我什么都会了”的错觉。一旦换成现场数据,噪声、工况变化、转速波动一上来,CNN 的效果立刻缩水。做故障诊断这行,物理直觉永远是底层能力。我的建议是:把包络谱方法吃透,再用特征加分类器建立 baseline,最后才考虑深度学习作为进阶手段。到了那个阶段,你已经有足够能力判断模型到底学到了什么。
5. 踩坑与调试实录:我把常见问题整理成了一张表
5.1 踩过的五个真实大坑
两年多来,我在 CWRU 数据上见过太多“看起来没问题但结果不对”的案例,包括我自己也踩过。这里挑几个最典型的分享一下。
第一个坑是采样率选错。下载了 48kHz 数据却用 12kHz 去算频率轴,特征频率全部偏差 4 倍,谱图看起来“哪儿哪儿都有峰”,就是没有特征峰。解决方法是第一行代码就把 fs 和文件名绑定,互相校验。
第二个坑是bandpass的滤波范围选得太窄或者太低。有次我把带通范围设在 500-1000Hz,结果共振解调效果极差,包络谱全是噪声。后来看时频图,发现这个型号轴承的共振带集中在 3000Hz 附近,滤波范围应该盖住它才行。建议每次换数据都先看原始频谱的“山包”在哪,再定滤波。
第三个坑是窗函数没加。FFT 之前不加窗,频谱泄漏导致谱峰变宽、旁瓣变高,特征频率处的尖峰被“糊”成一片,倍频位置也对不准。用hann窗能立刻改善谱线质量。
第四个坑是负载混用。0 负载和 3 负载下转频不同,特征频率当然不同。有人把所有文件一股脑读进来打上“内圈故障”标签,结果每个样本的特征频率都不一样,分类器当然学不好。不同负载的数据在训练时要么分开建模,要么把转频归一化到特征频率倍频系数上。
第五个坑是外圈故障 3 点钟和 6 点钟位置的数据混在一起。这两个位置的冲击调制特性不同,当成同一类会增加类内差异。建议做训练集时要么只选一种位置,要么把位置信息也作为标签的一部分。
5.2 排查问题的通用思路
面对“跑出来的图不对”的情况,我的排查顺序是这样的:先检查采样率和转频,如果转频都不对,后面全白搭;再检查变量名和数据通道,确认看的是 DE 不是 FE;然后检查滤波范围,看原始频谱决定;最后检查窗函数和 FFT 点数,看谱线分辨率和泄漏。按这个顺序走一遍,90% 的问题都能定位。我还整理了一张速查表,如下:
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 包络谱里找不到特征峰 | 带通范围没对准共振带 | 先看原始频谱,选共振峰所在频段 |
| 特征频率总是差 4 倍 | 采样率设错了 | 确认数据文件对应的是 12k 还是 48k |
| 正常信号也有很多尖峰 | 没加窗或 FFT 点数太少 | 加汉宁窗,增加分段长度 |
| 时域波形看不出冲击 | 故障尺寸太小或选用数据段不对 | 尝试 0.021 英寸故障,或看包络谱而非时域 |
| 内圈故障出现大量边带 | 内圈随旋转导致的调制现象 | 正常现象,不要误判为另一类故障 |
| 同一类样本特征差异极大 | 负载混用或外圈故障位置混用 | 按负载分组,或区分 3 点钟/6 点钟 |
5.3 关于“跑通”一句话心得
我发现一个很有趣的现象:很多人拿到 CWRU 数据以后,第一反应是到处找别人的代码然后直接跑。跑通了,很开心,但问他原理,说不出所以然。这样不行。第一次玩这个数据,一定要逼自己从 load 数据开始,一步一步写,每一步都搞清楚为什么要这么做。包络谱为什么能找到 107Hz?它和原始频谱差在哪?如果你能对着一个完全没标答案的陌生数据,独立跑出特征频率和故障类型,这才算真的入门了。
6. 从 CWRU 到真实数据:入门之后的路还长
CWRU 数据适合入门,但千万别把它当成工业真实数据的拟合。真实轴承数据往往转速波动、负载不定、背景噪声大,还可能存在多个故障同时发生的情况。那时候你可能会发现,CWRU 上屡试不爽的包络谱在真实数据上没那么灵,特征频率会被噪声淹没,共振频带也不稳定。这很正常。
我个人现在的习惯是:接到一个新的诊断任务,仍然先用 CWRU 数据把算法和参数调通,当成“算法自检平台”。算法在 CWRU 上如果跑不出理想效果,大概率是算法自身有问题;在 CWRU 上表现良好、换到现场数据缩水,大概率是数据域差异的问题。这种两段式验证思路,能帮助你快速定位瓶颈到底在哪个环节。最后再分享一个小技巧:每次跑通一个样本,养成把特征频率和谱峰位置截图存档的习惯,积累多了以后,你会发现这些案例彼此对比特别有参考价值。这大概就是这件事让人“上头”的原因——每一次跑出新谱峰,都像是在和机器对话,听懂了它哪里不舒服,那种感觉很踏实。