☰
基于MATLAB的轴承故障诊断:包络谱分析与SVM建模实战
2026/10/16 15:38:26 网站建设 项目流程

简介:面向机械工程领域的研究人员和技术人员,这份资料以Matlab/Simulink为工具,围绕西储数据轴承的动力学建模与仿真展开,可应用在轴承性能评估、故障诊断和优化设计等研究项目中,帮助将实测数据转化为可仿真的动力学模型。资源共4个文件,压缩包约287KB,涵盖PDF、Markdown、HTML和Word四种格式:PDF适合完整研读理论,Markdown与HTML便于快速查阅和在线浏览,Word版则可按需编辑批注。目前已有610人学习下载。内容从轴承动力学基础理论写起,覆盖力学特性与运动规律,并完整演示了从读取西储数据、定义模型参数、构建动力学方程,到使用Simulink完成仿真并输出图表的全流程;附带一段简化Matlab代码示例,可让读者快速跑通建模过程,同时理解不同参数配置对仿真结果的影响。掌握基本Matlab编程后,可在此基础上扩展模型,提前发现潜在故障,降低实验成本并提升设计质量与效率。

1. 轴承数据驱动建模的完整链路:从振动信号到可运行脚本

做轴承故障诊断时,数据驱动建模眼下是绕不开的技术路子——设备缺少精确物理参数,但振动信号一抓一大把。这套基于 MATLAB 的轴承动力学建模与仿真代码,正好把“数据读取→包络谱分析→特征工程→分类器训练→仿真验证”整条链路补齐了。它默认针对一组公开的滚动轴承实验台振动数据,常见于机械工程与故障诊断方向的课程设计和毕业课题。适合谁用?一是做设备健康管理项目的在校学生,二是想快速验证特征频率理论的工艺工程师,三是想拿现成脚本复现完整流程的仿真从业者。下面先把建模思路讲透,再落到每段代码和参数上,最后几节专门讲排查技巧,保证你拿到资源后能直接跑通。

2. 数据驱动模型为什么可行:包络解调与特征工程选型

2.1 物理模型与数据驱动模型的边界

真正的轴承动力学模型,会从赫兹接触、油膜刚度、保持架碰撞这些机理出发,建立一组微分方程。这套做法在学术上很完整,可生产现场往往凑不齐参数:润滑脂牌号影响刚度,游隙和预紧力随着温度漂移,轴承座结构还会带来复杂谐振。真按物理模型去抄参数,仿真结果经常和实测振动对不上。

数据驱动建模的思路是绕开“硬参数”,直接让统计特征和故障模式建立映射。最典型的一组公开实验台数据,是电机驱动端和风扇端在四种工况下的振动文件。这类数据的优势有两个:一是转频、采样率、负载扭矩都是文档化了的,二是同一故障类型包含多组样本,足够喂给分类器。

我的选型建议是:不要一上来就套深度学习。轴承数据一条样本动辄上万点,深度学习模型需要大量样本,而公开数据集的故障类别单类样本往往只有几十组。SVM 这类经典模型在小样本、多类别、特征维度几十到几百的情形下更稳,训练快,出错了也好查。代码包里默认的也是 SVM,适合作为基准方案先跑通,后面想换随机森林或者神经网络时再扩展。

2.2 包络解调与故障特征频率:BPFO、BPFI 怎么算

真实轴承的振动信号不是直接藏在波形里。滚动体路过外圈、内圈故障点时,会产生周期性冲击,这个冲击会调制在轴承系统的固有频率上,固有频率通常在几千赫兹,而故障冲击频率只有几百赫兹。在这类信号里,直接做频谱分析看到的只会是共振峰附近的一大片模糊,故障频率根本看不清。

所以包络解调是必须做的前置步骤。常用的做法是希尔伯特变换:求出原始信号的解析信号,再取模得到包络,包络里保留的就是每个冲击周期的强度起伏,这时候再做 FFT,故障特征频率就清晰了。

计算四个特征频率要记住四组公式:

  • 外圈故障频率(BPFO):f_BPFO = N/2 × f_r × (1 − (d/D)·cosα)
  • 内圈故障频率(BPFI):f_BPFI = N/2 × f_r × (1 + (d/D)·cosα)
  • 滚动体故障频率(BSF):f_BSF = (D/d) × f_r × (1 − ((d/D)·cosα)²
  • 保持架故障频率(FTF):f_FTF = f_r/2 × (1 − (d/D)·cosα)

其中 N 是滚动体个数,f_r 是转频(由转速算出,注意减去电机滑差),d 是滚动体直径,D 是轴承节径,α 是接触角。很多人在这一步翻车,因为只按铭牌同步转速算 f_r,忽略了负载下的滑差。公开数据集中每个工况的负载功率(如 0 HP 到 3 HP)都需要换算考虑。

提示:接触角的数值不要照抄出厂值,要按公开数据的实验条件核对。大多数深沟球轴承在轻载下接触角接近 0°,但载荷增大后 α 会变大,BPFO 计算值差出几个赫兹很常见。

2.3 特征工程:时域统计与频带能量的取舍

包络谱只给出了一个观察角度,分类器还需要一组量化的特征向量。代码包里提取三类特征:

时域统计特征。常用的是均方根值 RMS、峰度、峰值因数和整流平均值。峰度特别适合早期故障预警,因为正常振动接近高斯分布,峰度在 3 左右;一旦出现周期性冲击,信号尾部变厚,峰度能飙到 5 甚至 10 以上。

频带能量特征。把包络谱划分成几个频带,统计每个频带的能量占比。滚动体故障会导致特定区间带能量集中,这些特征带来的区分度远高于只看 RMS。

包络谱峰值频率。直接从包络谱中提取前三个最强的峰值频率及其幅值,这个特征可以认为是故障的直接证据。

这里想提醒一下:特征不是越多越好。数据驱动建模最怕的就是特征维度高、样本量少,SVM 在无关特征干扰下会变得不稳定。建议特征数量控制在 10 到 20 个之间,先跑一轮,再用逐步回归或互信息法筛选一遍。代码包里已经排好了一个固定的特征向量顺序,后面可以自己加列,但一定记得同步改维度。

3. 完整 MATLAB 实现:数据划分、包络谱计算与 SVM 训练

3.1 数据加载与数据集划分

拿到公开数据集后,先不要着急读数据。第一步是搞清楚每个 .mat 文件里的变量名。实际数据文件里,变量可能叫 DE、FE、BA 之类的缩写,也可能混着时间序列和采样率信息,直接按固定变量名加载,没两次就踩坑。

dataRoot = '/path/to/dataset'; fileList = dir(fullfile(dataRoot, '*.mat')); % 先看第一个文件的变量结构 vars = whos('-file', fullfile(dataRoot, fileList(1).name)); disp({vars.name});

逻辑说明:dir列出目录下所有 .mat 文件,whos('-file', ...)不加载而直接读取文件内的变量名。这一步花十秒钟能省掉后面大量的变量名冲突问题。输出结果里会显示变量名和维度,接下来再写加载逻辑就清晰了。

参数说明:dataRoot建议先用绝对路径。MATLAB 的相对路径解析与脚本所在目录、当前工作目录都有关,换机器以后相对路径经常莫名失效。

数据集划分是整个流程中最容易出错的地方。惯用做法是按“实验记录”切分,而不是按“样本行”切分。如果某个文件是一段连续的时域信号,你把这段信号的前 80% 当作训练、后 20% 当作测试,相邻数据点在时间上强相关,训练集与测试集信息重叠,测试准确率虚高会误导后期部署判断。

% 按文件名称分组,同一文件的样本只在训练集或只在测试集 fileIDs = extractBefore({fileList.name}, '_'); [groups, fileIDVec] = ismember(fileIDs, unique(fileIDs)); % 用 cvpartition 划分文件组 cv = cvpartition(unique(fileIDs), 'Holdout', 0.2); testFiles = fileList(cv.test, :); trainFiles = fileList(training(cv), :);

逻辑说明:先用文件名前缀区分不同实验记录,再对实验记录做划分,避免同一记录的样本同时出现在训练集和测试集。

参数说明:Holdout取 0.2 表示留出 20% 的记录做测试,如果总文件数很少,可以调小这个比例。总记录数小于 10 时建议用留一法做交叉验证,Holdout 切出来的测试集太小,误差方差会很大。

3.2 包络谱计算与特征提取

拿到数据后,第一个处理块就是包络解调。注意希尔伯特变换要求输入为实数信号,采样率必须和数据集文档严格一致,否则频率轴全错。

fs = 12000; % 采样率,按数据集文档确认 vib = loadData; % 从文件提取振动信号列向量 N = length(vib); t = (0:N-1)/fs; % 时间轴 % 希尔伯特变换提取包络 env = abs(hilbert(vib)); env = env - mean(env); % 去直流,避免零频峰 NFFT = 2^nextpow2(N); % 补零到 2 的幂,便于 FFT f = fs * (0:NFFT/2-1) / NFFT; envSpec = abs(fft(env, NFFT)); envSpec = envSpec(1:NFFT/2); % 找前三个峰值 [pks, locs] = findpeaks(envSpec, f, 'NPeaks', 3, 'SortStr', 'descend');

逻辑说明:hilbert返回解析信号,abs取模后得到包络,fft计算包络谱。findpeaks直接输出峰值幅值和对应频率,这里的频率就是后续特征向量里最关键的列。

参数说明:NFFT用2^nextpow2(N)会让频谱点数略大于原始信号长度,提高频率分辨率的是原始采样时长而不是补零长度,补零只让谱线变密,不会增加真实分辨率。如果你的样本长度是 12000 点,直接用原始长度做 FFT 更诚实;NFFT补到 16384 不影响结果但会拉长数组,后续循环处理多段数据时要注意内存。

特征提取要把多个文件循环处理,整理成一个二维特征矩阵。单个仿真里常见做法是这样:

nFiles = length(trainFiles); features = zeros(nFiles, 11); labels = strings(nFiles, 1); for k = 1:nFiles d = load(fullfile(dataRoot, trainFiles(k).name)); fld = fieldnames(d); vib = d.(fld{1}); % 按变量名字段动态取用 env = abs(hilbert(vib)); env = env - mean(env); L = length(env); NFFT = 2^nextpow2(L); envSpec = abs(fft(env, NFFT)); fvec = (0:NFFT/2-1) * fs / NFFT; envSpec = envSpec(1:NFFT/2); [pks, locs] = findpeaks(envSpec, fvec, 'NPeaks', 3, 'SortStr', 'descend'); features(k, 1) = rms(vib); features(k, 2) = kurtosis(vib); features(k, 3) = max(abs(vib)) / rms(vib); features(k, 4) = mean(abs(vib)); features(k, 5) = sum(envSpec(1: round(NFFT/4))); features(k, 6) = sum(envSpec(round(NFFT/4)+1: round(NFFT/2))); features(k, 7) = pks(1); features(k, 8) = locs(1); features(k, 9) = pks(2); features(k, 10) = locs(2); features(k, 11) = locs(3); labels(k) = extractBefore(trainFiles(k).name, '_'); end

逻辑说明:循环内部复用前一步的包络谱,特征 1 到 4 是时域统计,特征 5 和 6 是低频与高频频带能量,特征 7 到 11 是包络谱前三个峰值的幅值和频率。特征列对应的含义要记清楚,模型调好的后面看混淆矩阵时,这个映射能帮你判断哪个特征最有区分能力。

3.3 标准化与 SVM 训练

特征矩阵各列的量纲完全不同:RMS 是振动加速度单位,峰度是无量纲数,包络峰值幅值可能相差几十倍。直接把原始特征丢给 SVM,核函数距离会被大数量级的列主导,小波动特征的贡献被淹没。标准化放在划分之后,用训练集的均值和标准差去处理测试集,这在数据处理顺序上是强制要求。

rng(2024); cvp = cvpartition(findTrainIdx, 'KFold', 5); % 用训练集计算均值方差 mu = mean(features(trainIdx,:), 1); sigma = std(features(trainIdx,:), 0, 1); ftrTrain = (features(trainIdx,:) - mu) ./ sigma; ftrTest = (features(testIdx,:) - mu) ./ sigma; % 多分类 SVM,用错误纠错码模型包装 mdl = fitcecoc(ftrTrain, labels(trainIdx), ... 'Learners', templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', 1, 'KernelScale', 'auto')); % 预测并评估 pred = predict(mdl, ftrTest); acc = sum(pred == labels(testIdx)) / numel(labels(testIdx));

逻辑说明:std(X,0,1)求每列标准差,第一个参数 0 表示减去均值,按列计算。fitcecoc把二分类 SVM 扩成多分类模型,内部自动对每个类别对训练独立的二分类器,templateSVM的定义决定了每个子分类器都是 RBF 核支持向量机。

注意:learners参数如果用'svm'字符串就是默认线性核,换 RBF 核必须用templateSVM指定。线性核在高维特征空间通常够用,但遇到故障特征混叠严重要用 RBF 调。BoxConstraint越大对训练误差惩罚越重,例如 100 时训练准确率会明显提升,但测试效果未必变好。

4. 排查:模型训练中五个典型问题与对策

4.1 特征频率算错,包络谱峰值和理论值对不上

现象:内圈故障数据做包络谱后,最强峰出现在 1.2 倍 BPFI 附近,和计算值差了几百赫兹。

原因:多半是转频取错了。公开数据集提供的电机转速是同步转速(例如 1797 RPM),而实际负载下转差率会让转子转速低于同步转速;另一个原因是接触角用了默认值 0°,但大负载工况下接触角变化会让计算出的 BPFI 有几赫兹误差。

解决:用测得的实际转速换算 f_r,不建议用铭牌值;把接触角敏感度写进一个参数,先跑一遍小范围扫描,观察频率峰值是否随预计方向移动。常见做法是 0°、5°、10°、15°各算一次,看哪个值能让包络谱峰频率落在 BPFI 理论值正负 0.5% 内。

4.2 数据集划分不当,测试准确率高得不真实

现象:5 折交叉验证准确率 99%,拿到新采集数据后跌到 70% 上下。

原因:故障信号是周期性强、相邻段高度相关的时间序列,你用随机抽样把同一段振动文件的头尾切到了训练集和测试集,两者的统计特性几乎一样,模型相当于“开卷考试”。

解决:强制按文件名前缀划分文件组,同一实验记录的数据不允许跨集合;切分之后检查训练集和测试集的标签分布比例,如果某一类故障完全没进训练集,先补样本再训练。

4.3 特征归一化放错位置,训练时一切正常、部署时全乱

现象:训练脚本准确率高,换到在线采集场景分类结果明显偏斜。

原因:先对所有特征矩阵做全局标准化,再进行训练测试划分,这会让测试集样本的均值方差混进训练时的归一化参数。实际部署时只有单个样本,没有这批全局统计量兜底,特征分布就变了。

解决:严格划分完再看归一化,mu和sigma只由训练集计算,测试集做同样的平移缩放后送入模型;把mu、sigma、模型对象一起存成 .mat 文件,部署时一并加载。

4.4 低通滤波参数过狠,滚动体故障特征消失了

现象:滤波之后包络谱里 BSF 频段干净得没有峰,反而出现了很多莫名峰。

原因:用了普通 IIR 滤波器,滤波器相位在截止频率附近有非线性失真,冲击信号的包络被拉宽甚至变形。滚动体故障本来就是能量微弱、频率很高的冲击,滤波畸变会把故障特征直接抹掉。

解决:尽量用filtfilt做零相位双向滤波,这样相位失真是零。这里我一般会先保留原始信号,包络谱提取在原始数据上做,滤波只用于辅助观察时域波形。

4.5 SVM 报错提示标签必须为二元分类

现象:调用fitcsvm时直接报错,提示响应变量必须有且仅有两个类别。

原因:fitcsvm只支持二分类,内圈、外圈、滚动体、正常四类数据无法直接塞进去。很多人在这里以为是数据有问题,实际是选错了函数。

解决:换成fitcecoc,它是多分类封装,内部自动生成多个二分类器再做投票。如果某次只想做“正常 vs 异常”二分类,那用fitcsvm没有任何问题,可以自由选择。

5. 进阶操作:用贝叶斯优化调超参数,再把它固化成 Simulink 模块

上一章里的BoxConstraint和KernelScale是我随手拍的值。真正要把模型推向实用,这两个参数不能靠玄学定,交给贝叶斯优化是更高性价比的选择。贝叶斯优化比网格搜索更适合 SVM,因为每次评估都要训练一次模型,网格搜索动辄几十上百组,优化算法一般十几轮就能收敛到不错的区域。

box = optimizableVariable('BoxConstraint', [1e-2, 1e2], 'Transform', 'log'); scale = optimizableVariable('KernelScale', [1e-2, 1e2], 'Transform', 'log'); objfun = @(params) kfoldLoss(fitcecoc(ftrTrain, labels(trainIdx), ... 'Learners', templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', params.BoxConstraint, ... 'KernelScale', params.KernelScale), 'KFold', 5)); results = bayesopt(objfun, [box, scale], ... 'MaxObjectiveEvaluations', 25, 'UseParallel', false);

逻辑说明:kfoldLoss返回 5 折交叉验证的平均损失,作为目标函数;bayesopt在指定范围内搜索、评估、更新概率代理模型,在较少评估次数内逼近最优解。

参数说明:Transform设为'log'表示在对数尺度上均匀采样。这两个参数跨了几个量级,直接线性采样会大量浪费在无效区间。UseParallel设false是因为每个目标函数计算量不算大,并行通信开销反而更高。

优化完成后,用最优参数重新训练最终模型,然后导出供 Simulink 调用:

bestMdl = fitcecoc(ftrTrain, labels(trainIdx), ... 'Learners', templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', results.XAtMinObjective.BoxConstraint, ... 'KernelScale', results.XAtMinObjective.KernelScale)); saveLearnerForCoder(bestMdl, 'bearingModel');

saveLearnerForCoder会把模型序列化成适用于代码生成的格式,之后在 Simulink 的 MATLAB Function 块中调用:

mdl = loadLearnerForCoder('bearingModel'); predLabel = predict(mdl, double(featureRow));

这里要注意三点:一是 Simulink 模块里的featureRow必须和训练时的特征顺序完全一致,少一列或多一列都会报错;二是输入特征向量需要提前做和训练时一致的标准化,mu和sigma也要加载到模块里;三是模型文件路径要写绝对路径或放到 MATLAB 路径下,否则代码生成阶段找不到文件。

把训练好的模型嵌进 Simulink 后,可以接上信号源和 Scope,做“数据输入→特征提取→预测”的连续仿真。这样轴承故障诊断就从一段脚本变成了可演示、可部署的仿真系统。从那以后,我每次跑新数据源都会强制走一遍这个顺序:先确认采样率和转频,再核算故障频率,最后做数据集划分和归一化。这套流程先运行一遍再调参数,能省掉大量返工时间。希望帮到你。

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

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

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

立即咨询