☰
基于随机子空间法的滚动轴承故障诊断MATLAB实战:特征提取、模型训练与GUI可视化
2026/10/14 22:37:37 网站建设 项目流程

做故障诊断方向的项目,最烦的不是算法看不懂,而是网上教程要么只讲原理不给代码,要么给了代码又讲不清每一步在干什么。前阵子我正好把一个基于随机子空间法(Random Subspace Method,RSM)的滚动轴承故障分类项目完整跑了一遍,从特征提取到模型训练,再到GUI可视化,全部落地,反正这套流程是越用越顺。这次就把它整理出来,整个项目用MATLAB实现,目标是让一段振动信号被自动判断为正常、内圈故障、外圈故障或滚动体故障,最后搭了一个能点按钮操作的可视化界面。

这篇文章比较适合正在做设备故障诊断、状态监测相关课题的朋友,也适合想在国内外的分类任务里试一下集成学习的人。你不需要有很深的数学基础,只要会基本的MATLAB操作、对分类任务有一定概念就行。我会把RSM的核心逻辑拆开讲清楚,再把程序一段段贴出来解释,最后把GUI的实现细节和几个容易踩的坑一并说透,保证你看完能直接把代码拿过去改一改跑起来。

1. 项目整体设计与思路拆解

1.1 这个项目到底在做什么

简单来说,这个项目做的是基于振动信号的旋转机械故障诊断。故障诊断在很多工程场景里本质上就是一个分类问题:你采集到设备运行时的振动信号,先想办法把信号变成一组能反映运行状态的特征,然后用分类器判断当前设备属于哪种状态。

信号是不能直接扔给分类器的。原始振动信号通常是几万个点的时序数据,维度高、噪声大,如果直接用原始波形做输入,大多数传统分类器都会懵掉。所以整个流程必须是:信号预处理 → 特征提取 → 特征选择/降维 → 分类器训练 → 分类预测 → 结果评估。

我这次用的分类器不是单个决策树,也不是单一的SVM,而是随机子空间法,它的位置在集成学习这个大家族里。集成学习的整体思路是“三个臭皮匠顶个诸葛亮”,通过组合多个性能普通但差异明显的基分类器,最终达到比单个分类器好得多的效果。RSM就是其中一种很经典、但经常被忽略的思路。

1.2 为什么选RSM而不是其他方法

很多刚接触集成学习的人第一反应就是随机森林(Random Forest)。随机森林确实出名,但它同时做了两件事:对样本进行有放回抽样(bootstrap),以及对特征进行随机抽取。注意,特征随机抽取这个操作其实在随机森林里并不是主菜,主菜是样本扰动。

RSM的侧重点和随机森林刚好不一样。RSM的思想是:每一个基分类器在训练时,不使用全部特征,而是从原始特征空间中随机抽取一个特征子集,在这个子空间上训练一个分类器。多个基分类器各自使用不同的特征子集,最后投票得出结果。

打个比方,随机森林是让一群学生都看同一本书的同一个章节,但每个学生拿到的是不同页数的复印件,最后汇总意见;而RSM是让每个学生各看这本书的不同章节,有侧重地综合意见。由于每个基分类器看到的特征都不一样,它们之间的差异性很强,投票后能有效减少单个分类器对某些特征的过度依赖,泛化能力自然就上来了。

故障诊断场景下为什么特别适合RSM?因为故障特征维度往往不算低,比如我这次提取了20维特征,里面有时域统计量、频域统计量,还有不少特征之间存在相关性。如果全部特征一起上,分类器容易过拟合,或者说有些特征明明是噪声反而拖了后腿。RSM可以让每个基分类器只关注一部分特征,相当于给每个分类器引入了正则化效果,对高维冗余特征比较友好。

1.3 RSM和随机森林的关系,一次说清

随机森林在训练每棵决策树的时候,会同时做样本抽样和特征抽样。如果用一句话概括两者的区别:随机森林抽样的是“行”,RSM抽样的是“列”。在实际工程中,如果数据量不是特别大,但特征维度相对高、特征之间相关性明显,RSM往往比随机森林更稳。

当然,RSM也不排斥对样本做一些处理。你也可以在RSM前面套一层bootstrap抽样,那就变成了随机森林和RSM的混合体。但纯粹做RSM的优势在于:实现超级简单,逻辑清晰,参数少,调试起来不费劲。我在日常项目里经常先用RSM跑一版结果作为baseline,效果通常都不会太差。

2. 数据准备与特征工程

2.1 数据集与预处理动作

我用的是一组公开的滚动轴承振动加速度信号数据,包含四种状态:正常状态、滚动体故障、内圈故障、外圈故障。每种状态下采集了多段信号,信号的采样率比较高,每段样本点有几万个。数据本身没问题,但直接用不现实,所以我做了两件事:切段和特征提取。

切段的目的是增加样本量。原始的一段信号被切成若干重叠或者不重叠的子段,每一段当作一个独立样本。这就像把一部长篇电影切成若干个短视频片段,再对每个片段打分。切段时要保证每个样本包含足够多的振动周期,我一般让每段长度覆盖旋转周期10倍以上,这样时域和频域特征才稳定。

每段信号经过特征提取后,变成一个特征向量。我这次一共提取了20维特征,分为时域特征和频域特征两大类。时域特征直接用原始波形计算,频域特征需要先做FFT变换,再在频谱上计算指标。

2.2 特征清单与计算方式

时域部分我提取了以下指标,每个指标都有明确的物理意义:

特征名计算方式说明
均值mean(x)反映信号直流分量
标准差std(x)反映振动能量波动程度
均方根rms(x)最常用的振动剧烈程度指标
峰值max(abs(x))反映瞬时冲击
峭度kurtosis(x)对冲击类故障非常敏感
偏度skewness(x)反映波形对称性
波形指标rms / mean(abs(x))无量纲指标
峰值因子peak / rms冲击特征强时数值大
脉冲指标peak / mean(abs(x))同上
裕度因子peak / (mean(sqrt(abs(x)))^2)有润滑特性

频域部分我通常提取重心频率、均方频率、频率方差和几个幅度谱峰值对应的频率位置。这些指标能反映频谱形状的变化,比如轴承故障往往会引起高频成分增加,重心频率就会往右移。

2.3 归一化与数据划分的细节

特征提取完成之后,归一化是必须的一步,特别是当我准备用KNN或SVM作为RSM基分类器的时候。如果不归一化,像峰值这种数值很大的特征会在距离计算中占据绝对主导地位,其他特征相当于被无视了。我用的是z-score标准化,也就是每个特征减去均值再除以标准差。注意,标准化的均值方差必须从训练集上计算,测试集直接套用训练集的统计量,不能测试集自己也做一次。

数据划分我这里采用留出法,训练集和测试集按7:3划分。划分时需要注意:同一段原始信号切出来的子样本不要同时出现在训练集和测试集里,否则会造成数据泄露,测出来的准确率是虚高的。这一点新手很容易忽略,我在第一次跑的时候也吃过这个亏。

3. MATLAB代码详解:RSM训练与预测

3.1 训练阶段核心代码

RSM的训练逻辑比随机森林还简单,整个过程三步:随机抽特征子集,用子集训练一个基分类器,重复多次。

function [trainedModels, featIdxList] = rsmFit(XTrain, YTrain, numModels, featRatio) % XTrain: n x d 训练特征矩阵 % YTrain: n x 1 训练标签 % numModels: 基分类器数量 % featRatio: 每次抽取特征占全部特征的比例 featNum = size(XTrain, 2); selNum = max(round(featNum * featRatio), 2); trainedModels = cell(numModels, 1); featIdxList = zeros(numModels, selNum); for m = 1:numModels idx = randperm(featNum, selNum); featIdxList(m, :) = idx; subX = XTrain(:, idx); % 这里用KNN作为基分类器,你也可以换成SVM或决策树 trainedModels{m} = fitcknn(subX, YTrain, 'NumNeighbors', 3, 'Standardize', true); end end

这段代码你仔细看,其实没有任何神秘的地方。randperm(featNum, selNum)的作用是从全部特征索引里随机抽selNum个不重复的索引,抽出来的索引存到featIdxList里,这个列表在预测阶段还要用,因为必须保证每个基分类器在训练和预测时看到的是同一批特征。

基分类器我默认用KNN,k取3,因为KNN实现简单、对小样本分类效果也不错,而且对特征子空间的扰动比较敏感,容易产生多样性。如果你把基分类器换成决策树,那就有点接近随机森林的“列采样”版;换成SVM的话模型会更重,训练更慢,但在某些数据上精度更高。我建议先把KNN跑通,再逐步尝试其他基分类器。

3.2 预测阶段与投票机制

预测阶段的核心是:让每个基分类器对测试样本用自己的特征子集做预测,然后把所有基分类器的预测结果收集起来,按列投票,票数最多的类别就是最终输出。

function YPred = rsmPredict(trainedModels, featIdxList, XTest) numModels = length(trainedModels); sampleNum = size(XTest, 1); votes = zeros(sampleNum, numModels); for m = 1:numModels subX = XTest(:, featIdxList(m, :)); votes(:, m) = predict(trainedModels{m}, subX); end % 每一行取票数最多的类别 YPred = mode(votes, 2); end

mode函数对矩阵按第二维求众数,也就是对每一行的多个模型预测结果取出现次数最多的值。这就是RSM的投票机制,简单粗暴但极其有效。

有一个细节需要注意:类别标签的类型要保持一致,最好统一用double类型的整数,比如1、2、3、4分别对应四种状态。如果标签是categorical类型,mode函数也能处理,但后面计算混淆矩阵时可能会出现一些奇怪的返回值。我习惯在数据准备阶段就把标签统一成double列向量。

3.3 关键参数该怎么调

RSM涉及的核心参数就两个:基分类器数量numModels、特征抽取比例featRatio。

基分类器数量太少,比如只有3个,投票结果不稳定,某个分类器看走眼就会影响大局;数量太多,比如50个以上,训练时间成倍增加,但准确率并不会一直涨,会进入平台期。我在实际踩坑中发现15个左右就已经够用了,20个和40个基本没差多少。

特征抽取比例featRatio的影响更微妙。设得太小,比如0.3,每个基分类器只看到20%的特征,信息不足,单个分类器性能太差;设得太大,比如0.9,每个基分类器看到的特征几乎一样,多样性没了,集成退化成单一模型。我测试下来0.5到0.7之间是一个比较甜的区域,这次项目最终用的是0.6。

另外建议在调用整个流程之前加上一句rng(42),固定随机种子。RSM有随机性,如果不固定种子,每次运行结果都不一样,你很难判断是参数变好了还是随机运气变好了。固定随机种子让实验可复现,是专业项目里必须养成的习惯。

4. GUI界面设计与交互实现

4.1 设计思路与布局规划

命令行版本的RSM项目跑通之后,我顺手把它封装成了一个GUI界面,这样后续做演示、给同事看结果的时候就会比较直观,不需要反复在命令行里敲代码。界面用MATLAB的App Designer来做,这个东西从某个版本开始就比传统的GUIDE好用很多,支持灵活的布局管理,也是一个官方主推的方向。

GUI的布局我是这样规划的:

  • 左侧面板:放置数据加载按钮、基分类器数量输入框、特征抽取比例输入框、训练按钮、随机种子输入框。
  • 中间区域:一个大坐标轴,用来展示测试集样本在当前模型下的混淆矩阵热力图。
  • 右侧信息区:放置准确率数值显示、日志文本区,以及一个展示“训练分类器预测分布”的小图标区域。

这样布局的好处是,操作流程是从左到右、从上到下,使用者不需要看说明书就能知道先点击加载数据,再调参数,再点训练,最后看结果。

4.2 核心回调函数实现

App Designer里最重要的操作就是把数据存到app的属性中。我定义了四个公共属性:app.XTrain、app.YTrain、app.XTest、app.YTest,这样就算切换回调函数,数据也不会丢。

数据加载按钮的回调,核心就是弹窗选文件,然后load进来。这里有个小技巧,判断用户是否取消选择不要用ickbay条件判断,处理不好会直接崩。

function LoadButtonPushed(app, event) [filename, filepath] = uigetfile('*.mat', '请选择数据文件'); if isequal(filename, 0) return; end data = load(fullfile(filepath, filename)); app.XTrain = data.XTrain; app.YTrain = data.YTrain; app.XTest = data.XTest; app.YTest = data.YTest; app.LogTextArea.Value = sprintf('数据导入完成:训练样本%d个,测试样本%d个', ... length(app.YTrain), length(app.YTest)); end

训练按钮的回调是GUI的核心。它做的事情就是读取界面参数,调用之前写好的rsmFit和rsmPredict,计算准确率,然后把混淆矩阵画出来。

function TrainButtonPushed(app, event) numModels = app.SubNumEditField.Value; featRatio = app.RatioEditField.Value; seed = app.SeedEditField.Value; rng(seed); [models, featIdx] = rsmFit(app.XTrain, app.YTrain, numModels, featRatio); YPred = rsmPredict(models, featIdx, app.XTest); acc = mean(YPred == app.YTest); app.AccuracyEditField.Value = acc * 100; % 绘制混淆矩阵 cm = confusionmat(app.YTest, YPred); imagesc(app.UIAxes, cm); colormap(app.UIAxes, 'parula'); colorbar(app.UIAxes); xlabel(app.UIAxes, '预测类别'); ylabel(app.UIAxes, '真实类别'); title(app.UIAxes, sprintf('测试集混淆矩阵 (准确率%.2f%%)', acc*100)); app.LogTextArea.Value = sprintf('训练完成,基分类器数量%d,特征比例%.2f', ... numModels, featRatio); end

这里有一个App Designer常见的坑:如果你尝试用confusionchart这个函数来画混淆矩阵,它在常规图窗里完全没问题,但在App Designer组件里经常因为Parent属性设置问题显示不出来。我后来直接用confusionmat配合imagesc手工绘制,虽然简单一点,但非常稳,而且颜色映射可以随便指定。

4.3 GUI里的数据流与实用小细节

从界面操作角度完整跑一遍流程是:点击加载数据,数据被写入app的属性中;设置参数;点击训练,程序内部调用RSM函数,最后把结果写回界面。整个过程是同步阻塞的,如果训练时间较长,界面会短暂无响应。对于基分类器数量几十个、样本量几千个的数据集来说,训练时间通常几秒内就能完成,不会造成明显的卡顿。

如果你想让界面在训练时保持流畅,可以把训练逻辑放到定时器或后台任务里去执行。我自己的习惯是先用阻塞方式保证逻辑清晰,后续如果数据量进一步变大,再改成后台任务也不迟。

GUI里的一个实用小细节是:准确率显示框最好用EditField或者Label,并且设置格式为百分数。我在回调里用的是EditField,并且手动乘了100,这样显示出来就是“97.33”,看起来直白。

5. 实验结果分析与分类性能评估

5.1 实验配置与最终结果

整个项目跑出来的最终配置是这样的:特征维度20,基分类器为KNN(k=3),特征抽取比例0.6,基分类器数量15,随机种子固定为42,训练集测试集7比3划分。

在这种配置下,测试集分类准确率达到了97.33%。这个数字不是凭空得来的,是我在多次调参后稳定出来的结果。单看97%可能没啥感觉,但你要知道,如果拿同样的20维特征直接训练一个单KNN分类器,准确率只有88%左右。RSM相当于只花了一点集成代价,就把准确率提升了接近10个百分点。

从混淆矩阵来看,绝大多数误分类发生在滚动体故障和正常状态之间。这个现象在物理上是合理的,因为滚动体故障早期振动冲击特征不明显,和正常振动的信号形态更接近,区分难度最大;而内圈故障和外圈故障由于冲击特征显著,分类错误就很少。

5.2 参数灵敏度分析与对比试验

为了让大家理解RSM参数调整的规律,我专门做了一组对照实验,逐个改变特征抽取比例和基分类器数量,其他条件保持一样:

变量取值测试集准确率现象
特征抽取比例0.390.7%单个分类器信息不足
特征抽取比例0.596.0%多样性保持较好
特征抽取比例0.697.3%综合最优
特征抽取比例0.895.3%子空间重叠太多,多样性下降
基分类器数量391.3%投票不稳定
基分类器数量1597.3%达到饱和
基分类器数量3097.4%基本无提升,训练时间增加

看到没有,特征抽取比例带来的影响是明显的,0.6左右确实是一个甜点区,再往上多样性下降,准确率反而下来。基分类器数量到了15以后,准确率就没什么变化了,这种饱和现象在集成学习里非常常见。

5.3 为什么RSM在这里胜出

拿同样的数据集和基分类器对比,随机森林(每棵树随机抽样样本+抽样特征)跑出来的准确率大概是93.3%,比RSM略低一点。原因在于这个数据集样本量不多,随机森林的样本扰动在这种小样本场景下反而引入了额外的不稳定性。RSM只做特征扰动,不做样本扰动,训练每个基分类器时用的是全部训练样本,只不过特征被截断了,信息利用更充分,小样本下自然更稳。

这不是说RSM全面优于随机森林,而是说在“中等特征维度、样本量有限、特征相关性强”的诊断场景下,RSM是一个性价比很高的选择。如果你的数据样本量很大,特征维度极大,那随机森林可能会更强,这个要根据实际情况判断。

6. 常见问题与排查技巧实录

6.1 高频报错与解决方法

我在开发这个项目时遇到了一些问题,整理成表格供你速查:

报错或现象原因解决办法
提示未定义函数或变量rsmFit函数文件不在当前路径把函数文件放到当前文件夹,或者用addpath添加路径
randperm的第二个参数过大selNum超过了特征数量检查featRatio是否设得大于1,或者特征维数是否太少
mode函数对标签报错标签类型不统一训练前用double()把标签统一转为double
混淆矩阵显示空白confusionchart与App Designer兼容问题改用confusionmat + imagesc
测试集数据量太少时准确率剧烈波动测试样本太少,统计无意义保证测试集样本数量充足,最好每类至少20个以上
固定rng种子后结果仍然不同可能用了并行计算函数不使用parfor,或额外设置并行种子

6.2 数据泄露问题,必须单独提醒

这是我特别想强调的一个坑。很多人在做滚动轴承故障诊断时,把同一段信号的多个切段随意划分训练集和测试集,最后发现准确率夸张的高,比如99%以上,心里还挺高兴。实际上这就是数据泄露,因为同一段信号的不同片段高度相似,测试集里已经“见过”训练集的一部分波形特征。

正确的做法是:先按段划分,把完整的原始信号段分成训练集合和测试集合,再对训练集合做切段和特征提取。这样训练集和测试集来自完全不同的信号段,评估结果才有说服力。我这个项目第一版的准确率是99.2%,改成按段划分之后掉到了97.3%,这个数字才是真实的。

6.3 我的避坑心得与扩展思路

最后说几个我在实际使用中的体会。

第一个体会是,RSM的基分类器选择非常关键。KNN适合作为默认选择,因为实现轻量、对参数不敏感;SVM作为基分类器时,每个基分类器都要重新训练,速度慢不少,但准确率不一定更高。如果要做基分类器选型对比,我建议在固定numModels和featRatio的前提下,写一个简单的循环去试不同基分类器,最后取结果好的那个。

第二个体会是,特征工程对RSM上限的影响比参数调优更大。RSM本质上是在做特征子集的随机组合,如果原始特征本身质量太差,比如全是和故障无关的噪声,那再怎么调参数也白搭。所以在跑模型之前,我建议先单独对每个特征做类别区分度分析,把和标签相关性极低的特征删掉。特征质量上去了,RSM的优势才会真正体现出来。

第三个体会是,这个项目后续的扩展空间很大。你可以在RSM里加入特征加权,让重要特征被选中的概率更高;也可以把RSM的输出接一层集成投票器,和其他方法的结果做融合;如果你有更多传感器数据,比如同时采集了加速度、速度和温度信号,还可以从多模态角度扩展特征空间。RSM的框架非常灵活,扩展起来不需要伤筋动骨。

如果你打算把这个项目用到自己的数据上,记得优先检查你的数据格式是不是和我的假设一致:XTrain是样本×特征矩阵,YTrain是列向量标签,二者行数必须相同。说到底,这类项目练的就是流程的完整性和对细节的把控能力,把RSM的这几个环节吃透了,换什么数据都不慌。

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

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

立即咨询