ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SVM故障诊断实战:从振动信号到分类模型的完整流程

SVM故障诊断实战:从振动信号到分类模型的完整流程 简介这是一份面向Matlab开发者与机械故障诊断学习者的SVM故障诊断/分类预测源码包基于西储大学轴承公开数据经特征提取后建模可完成滚动轴承正常、内圈、外圈、滚动体等常见故障的识别与判别。压缩包共71个文件涵盖Matlab主程序.m、libsvm-3.3完整工具箱、数据文件.mat、混淆矩阵可视化脚本、辅助训练脚本及说明文档整体仅1.31MB轻量且结构清晰。已有152人学习适合需要快速跑通SVM分类流程的读者。资源提供可直接运行的完整代码从数据加载、特征输入到模型训练与评估均有现成实现便于理解支持向量机在故障诊断中的应用细节借助附带的可视化与示例数据还可自行替换数据集验证不同特征组合的分类效果。1. 为什么故障诊断的落地项目里总是绕不开 SVM设备振动信号到手一看样本量几十条故障数据标注还靠老师傅手挑深度学习模型连热身都不够。这种“小样本、高特征维度、类别失衡”的场景恰好是SVM支持向量机的舒适区不需要海量数据核函数映射到高维空间后依然能找到分类边界训练一次快模型体积小部署到产线工控机里做分类预测几乎没有压力。如果你的工作涉及轴承故障诊断、齿轮箱状态识别、旋转机械异常检测Matlab里用SVM搭一个分类器是投资回报最高的一条路径。这篇笔记不给你灌理论直接讲怎么用SVM把“振动信号”变成“故障标签”以及那些让新手半夜改代码的坑在哪里。2. 用 SVM 做故障诊断先想清楚三件事再写代码2.1 多分类思路SVM 本质是二分类故障诊断却是多分类SVM 标准算法解决的是二分类问题——正类和负类之间找一个最大间隔超平面。但设备故障诊断通常是多分类正常、内圈故障、外圈故障、滚动体故障有时候还分严重程度。常见的做法是拆解成多个二分类问题主流策略有三种一对一One-vs-One、一对多One-vs-All和有向无环图DAG。Matlab 里不需要你手动实现这些策略。fitcecoc函数把 ECOC纠错输出编码框架封装好了底层可以用任意二分类器传入Learners指定为 SVM 模板即可。默认情况下 ECOC 用的是 One-vs-One 编码训练 K 个类别需要 K*(K-1)/2 个二分类器类别多时训练量增长明显但每个子问题样本量小实际算下来仍然很快。我在做 4 类轴承故障识别时特征维度 12 个、样本 1600 条fitcecoc训练时间不超过 5 秒。如果你更习惯用开源工具LibSVM 在 Matlab 里也能用但它封装的是 One-vs-OneAPI 风格和 Matlab 自带的fitcecoc不一样需要先make编译再手动管理svmtrain和svmpredict的数据格式。我的建议是本地调试用 Matlab 内置函数够用且不用折腾编译器特别在意训练速度或者要复现论文结果时再上 LibSVM。2.2 特征工程振动原始波形不能直接进 SVM直接把原始振动信号按点排列成特征向量是一种常见的错误做法。原因很简单原始信号长度往往上千甚至上万点SVM 在这种超高维稀疏特征上泛化能力差而且对噪声极其敏感。正确姿势是先从信号里提取“能描述设备健康状态”的特征再做分类。工程上最常用的是时域特征加频域特征组合。时域特征包括均方根值RMS、峰值、峭度、峰值因子、波形因子、脉冲因子、裕度因子这些指标对磨损、冲击类故障敏感频域特征则是对信号做 FFT 后提取频带能量占比、重心频率等。常见的特征组合如下表特征类别特征名称对哪种故障敏感时域均方根值 RMS整体能量变化磨损类故障时域峭度指标冲击类故障如轴承点蚀时域峰值因子早期故障冲击时域波形因子、脉冲因子波形畸变频域频带能量占比特定频段共振响应频域重心频率频谱形态变化单个特征通常无法区分所有故障类型所以做法是把多种特征拼成一个向量标准化后喂给 SVM。特征数量控制在 1020 个之间即可不是越多越好——特征多了SVM 对样本量的要求会指数上升反而导致过拟合。2.3 第一个能跑通的分类预测最小流程把思路落到代码上。下面是一个最小可行的 Matlab 流程特征是预先提取好的特征矩阵不是原始信号这一步跑通后你才有底子去调整细节。% 假设特征矩阵 feature 和标签 label 已就绪 % feature 维度N 个样本 × M 个特征 % label 维度N × 1取值 1,2,3,4 分别代表正常、内圈、外圈、滚动体 rng(42); % 固定随机种子保证实验结果可复现 % 分层划分训练集和测试集每个类别各取 70% cv cvpartition(label, HoldOut, 0.3, Stratify, true); idxTrain training(cv); idxTest test(cv); % 标准化只用训练集的均值和标准差防止数据泄漏 mu mean(feature(idxTrain, :)); sigma std(feature(idxTrain, :)); featureTrain (feature(idxTrain, :) - mu) ./ sigma; featureTest (feature(idxTest, :) - mu) ./ sigma; % 创建 SVM 模板核函数选择 RBF svmTemplate templateSVM(KernelFunction, rbf, ... BoxConstraint, 1, KernelScale, auto); % 训练 ECOC 多分类模型 mdl fitcecoc(featureTrain, label(idxTrain), ... Learners, svmTemplate, Codeword, onevsone); % 测试集预测 pred predict(mdl, featureTest); % 计算准确率并展示混淆矩阵 acc sum(pred label(idxTest)) / numel(pred); disp([测试集准确率: , num2str(acc * 100, %.2f), %]); confusionchart(label(idxTest), pred);这段代码有几个值得注意的点。cvpartition里的Stratify, true保证了每个类别在训练集和测试集中的比例一致避免某类故障恰好全落在测试集里导致准确率虚低。标准化只计算训练集的均值和标准差测试集直接套用这是防止数据泄漏的关键后面第 5 章还会单独讲。KernelScale 先交给 Matlab 自动估计后续再手动调。2.4 和深度学习对比什么时候不该用 SVM不是所有场景都适合 SVM。如果样本量超过几万条故障类型有几十种特征分布极其复杂深度网络的表征能力优势就体现出来了。SVM 在小样本下的优势恰恰是深度学习的劣势但反过来SVM 的特征工程需要人工经验特征选不好核函数再强也救不回来。我的经验是故障诊断项目起步阶段先用 SVM 跑通基线拿到一个可靠的准确率数字再决定要不要上深度学习。如果你连 SVM 都跑不出可用结果说明特征提取环节有问题换深度学习只会更难排查。3. 手把手实现从振动信号到 SVM 分类模型的完整代码3.1 数据准备整理目录结构和标签做故障诊断项目第一步是把数据整理成统一格式。常见做法是每个故障类别一个文件夹文件夹下存放若干段振动信号文件文件名包含转速、负载等工况信息。Matlab 里可以用dir递归读取所有文件再根据父文件夹名称生成标签。% 数据目录结构示例 % ./data/正常/normal_001.mat % ./data/内圈故障/inner_001.mat % ./data/外圈故障/outer_001.mat rootDir ./data; classNames {正常, 内圈故障, 外圈故障}; feature []; label []; for c 1:length(classNames) classDir fullfile(rootDir, classNames{c}); fileList dir(fullfile(classDir, *.mat)); for f 1:length(fileList) data load(fullfile(classDir, fileList(f).name)); % 假设变量名是 signal采样率 fs vibSig data.signal; % 后续提取特征得到一行特征向量 featRow featRow extractFeatures(vibSig, fs); feature [feature; featRow]; label [label; c]; end end每个 .mat 文件对应一个样本这个是最稳妥的数据组织方式。实际项目里一个样本可能是一段几秒钟的连续信号也可能是从长信号里切出来的多个窗口片段。切窗口时要注意窗口长度至少要包含若干个旋转周期比如转速 1500 rpm 对应 25 Hz 转频一个周期 0.04 秒窗口长度取 0.2 秒以上才能覆盖足够多的周期信息。3.2 特征提取函数时域和频域特征一次算完下面给一版可以直接用的特征提取函数输入是一段振动信号输出是一个特征向量。这里覆盖了 6 个时域特征和 4 个频域特征共 10 维。这个函数是按“单段信号”设计的窗口切分在上层完成。function feat extractFeatures(sig, fs) % 输入sig 振动信号列向量fs 采样率 % 输出10 维特征向量 n length(sig); % 时域特征 rms_val sqrt(mean(sig.^2)); peak_val max(abs(sig)); kurt_val kurtosis(sig); cf peak_val / rms_val; % 峰值因子 sf rms_val / mean(abs(sig)); % 波形因子 if mean(abs(sig)) 0 if_val peak_val / mean(abs(sig)); % 脉冲因子 else if_val 0; end % 频域特征 % 对信号去均值后做 FFT取单边频谱关心 0 到 fs/2 范围 sig sig - mean(sig); NFFT 2^nextpow2(n); spec abs(fft(sig, NFFT)); spec spec(1:NFFT/21); freqs linspace(0, fs/2, NFFT/21); totalE sum(spec) eps; % 把频带分成 4 段计算能量占比 bandE zeros(1, 4); for b 1:4 idx (b-1)*floor(length(spec)/4)1 : b*floor(length(spec)/4); bandE(b) sum(spec(idx)) / totalE; end feat [rms_val, peak_val, kurt_val, cf, sf, if_val, bandE]; end特征函数里有两个细节值得注意。时域特征里kurtosis用的是 Matlab 自带函数计算的是随机变量的超量峰度正常振动信号的峭度值接近 3早期故障信号会明显大于 3。频域特征做 FFT 前必须先减去均值否则直流分量会淹没低频段的能量分布。频带划分我按等宽分了 4 段实际项目中应根据设备的共振频段调整边界。3.3 训练与评估交叉验证与混淆矩阵一起吃下跑通最小流程后就该认真评估模型了。只报一个测试集准确率是不够的故障诊断场景里漏报一次内圈故障可能带来设备损坏误报一次正常状态为故障可能造成产线停线。所以评估时看混淆矩阵、查准率、召回率和 F1 值。% 在 3.1 的 feature 和 label 基础上继续 % 先用 5 折交叉验证看整体稳定性 rng(42); svmTemplate templateSVM(KernelFunction, rbf, ... BoxConstraint, 1, KernelScale, auto); mdlCV fitcecoc(feature, label, ... Learners, svmTemplate, ... CVPartition, cvpartition(label, KFold, 5)); % 查看每一折的损失 cvError kfoldLoss(mdlCV); fprintf(5折交叉验证平均错误率: %.2f%%\n, cvError * 100); % 用全部数据训练最终模型 mdlFinal fitcecoc(feature, label, Learners, svmTemplate); % 对训练集进行预测检查是否有过拟合 predTrain predict(mdlFinal, feature); fprintf(训练集回判准确率: %.2f%%\n, ... sum(predTrain label) / numel(label) * 100);交叉验证得到的错误率比单次划分训练测试集更可靠因为每个样本都被当过测试样本。训练集回判准确率是诊断过拟合的快速手段如果回判准确率接近 100%但交叉验证错误率很高说明模型记住了训练集而不是学到了规律。遇到这种情况优先尝试降低BoxConstraintC 值或者调大核函数的KernelScale。3.4 模型保存与部署训练一次处处复用模型训练完成后保存并部署到目标机器上是项目的最后一步。Matlab 里用save保存模型结构体新机器上直接加载并调用predict即可不依赖训练数据。% 保存模型连同特征提取参数一并记录 save(svm_fault_model.mat, mdlFinal, mu, sigma, classNames); % 部署时加载模型 S load(svm_fault_model.mat); mdl S.mdlFinal; mu S.mu; sigma S.sigma; % 新来一段信号走同样的特征提取和标准化流程 newSig load(new_sample.mat).signal; newFeat extractFeatures(newSig, fs); newFeat (newFeat - mu) ./ sigma; % 预测并显示故障类型 predLabel predict(mdl, newFeat); fprintf(预测结果: %s\n, S.classNames{predLabel});部署时最容易翻车的是特征顺序不一致。训练时特征按 [RMS, 峰值, 峭度, ...] 排列部署时特征提取函数改动了一个特征顺序模型预测结果就全乱了。建议定义特征提取函数时把特征顺序用注释固定下来模型文件里同时保存一个特征名称列表部署时逐一核对。另外标准化参数mu和sigma必须跟着模型走用模型的参数对新数据做标准化不能在新机器上重新用新数据计算——那相当于把测试集混进训练集了。4. 参数调优C 和 gamma 的搜索空间怎么定4.1 理解 RBF 核的两个关键参数SVM 用 RBF 径向基核函数时有两个参数直接决定模型表现惩罚系数 CBoxConstraint和核宽度 gammaKernelScale的倒数。C 越大模型越不允许训练样本被分错边界越复杂越容易过拟合C 越小边界越平滑但欠拟合风险增加。gamma 控制每个训练样本的影响范围gamma 越大决策边界越曲折同样容易过拟合gamma 越小决策边界越接近线性可能欠拟合。这两个参数必须同时调。你很难单独确定一个合适的 C 值因为它受 gamma 影响很大。如果数据标准化做好了C 的参考范围一般是 0.01 到 100gamma 的参考范围是 0.001 到 10但最终取值完全由数据分布决定没有放之四海而皆准的数值。我见过有人直接把 C 设成 1000、gamma 设成 1训练集准确率 100%测试集一塌糊涂这就是典型的参数爆炸。4.2 网格搜索配合交叉验证最稳的调参组合调参的经典做法是网格搜索在 C 和 gamma 的取值网格上每一组参数都做一次 K 折交叉验证选交叉验证平均准确率最高的一组。网格搜索的缺点是计算量大但故障诊断项目样本量通常不大几百到几千条样本完全跑得动。% 网格搜索 C 和 KernelScale C_list [0.01, 0.1, 1, 10, 100]; scale_list [0.1, 0.5, 1, 2, 5, 10]; bestAcc 0; bestParams []; for C C_list for scale scale_list t templateSVM(KernelFunction, rbf, ... BoxConstraint, C, KernelScale, scale); mdl_temp fitcecoc(featureTrain, labelTrain, ... Learners, t, ... CVPartition, cvpartition(labelTrain, KFold, 5)); acc 1 - kfoldLoss(mdl_temp); if acc bestAcc bestAcc acc; bestParams [C, scale]; end fprintf(C%.2f scale%.1f acc%.2f%%\n, C, scale, acc*100); end end fprintf(最优参数: C%.2f, KernelScale%.1f, 交叉验证准确率%.2f%%\n, ... bestParams(1), bestParams(2), bestAcc*100);这段代码循环了 5×630 组参数每组做 5 折交叉验证也就是训练 150 次模型。特征维度不高时总耗时通常在几分钟内。跑完选出最优参数再在独立测试集上验证一次得到最后的可信准确率。注意网格搜索选参数时只能用训练集的交叉验证结果不能用测试集参与任何参数选择否则测试集就不再是未见数据了。4.3 不只是准确率故障诊断要看 F1 和混淆矩阵参数调优的目标函数如果只选准确率类别不平衡时容易翻车。假设 99% 的样本是正常1% 是故障分类器全预测正常就有 99% 准确率但显然毫无意义。故障诊断里故障样本才是重点关注的类别。处理方式有两步。第一步评估指标从准确率改成宏平均 F1 值或混淆矩阵第二步如果类别不平衡问题严重在fitcecoc里指定Cost参数给漏报故障样本的惩罚加大。下面给一个在网格搜索中改用 F1 作为选择标准的示例。% 自定义评估函数计算宏平均 F1 f1score (pred, trueLabel) computeMacroF1(pred, trueLabel); % 训练时使用代价矩阵漏报故障的代价设为 10误报正常为 1 % 类别顺序按 sort(unique(labelTrain)) 排列 costMatrix ones(4) ; costMatrix(2:4, 1) 10; % 故障类别预测为正常代价 10 costMatrix costMatrix - diag(diag(costMatrix)); % 对角线置 0 t templateSVM(KernelFunction, rbf, ... BoxConstraint, C, KernelScale, scale); mdl_temp fitcecoc(featureTrain, labelTrain, ... Learners, t, Cost, costMatrix, ... CVPartition, cvpartition(labelTrain, KFold, 5));这里Cost参数是一个 K×K 矩阵Cost(i,j)表示真实类别是 i、预测为 j 时的惩罚代价。代价矩阵加大了对漏报的惩罚模型为了最小化总代价会把更多的样本判为故障类别代价是可能增加误报。具体数值要根据实际场景权衡——对设备损坏后果严重的故障漏报代价可以设到 10 或更高如果误报会导致频繁停线则需要把误报代价调大两边拉平衡。4.4 分层采样别让随机划分毁掉你的评估结果交叉验证和训练测试集划分都需要分层采样。cvpartition的Stratify, true保证每个折里各类别比例与原始数据一致。如果不分层某些折里可能恰好一个故障样本都没有模型训练出的边界就会出现偏差。我见过一个真实案例用默认的随机划分做了 70/30 训练测试划分某个小类别故障总共只有 8 个样本随机分配后 6 个落进训练集、2 个落进测试集测试集准确率显示 99%看起来完美但实际部署时这个小类别频繁漏报。根本原因就是测试集太小评估指标方差极大。样本量少的类别至少要保证测试集里有 5 个以上样本这个类别的结果才有参考价值。5. SVM 故障诊断的 5 个常见坑现象、原因与排查5.1 准确率 100%但新数据一测就崩现象训练集回判准确率 100%交叉验证准确率 98%但拿到现场新采集的信号预测结果乱七八糟。原因典型过拟合。参数搜索时模型在微小波动和噪声里找到了决策边界这些模式只存在于训练数据中。另一个常见原因是数据划分不严谨比如同一个连续信号切出来的多个窗口样本被分到了训练集和测试集两侧造成信息泄漏评估结果虚高。解决先检查数据划分方式确保同一个原始信号切出的窗口要么全部在训练集要么全部在测试集然后降低 C 值观察交叉验证准确率是否下降明显最后检查特征提取是否混入了与标签间接相关的信息比如样本编号、采集时间这类不该出现在特征里的变量。5.2 标准化放在划分前导致数据泄漏现象训练测试划分后先用全部数据的均值和标准差做标准化再划分训练测试集得到的交叉验证准确率异常高。原因标准化时用了测试集的统计量相当于把测试集的分布信息提前泄露给了训练过程。这一点我在前面反复提过因为实际项目里太容易犯先zscore整个数据矩阵再划分数据集。解决把标准化放在划分之后且只对训练集计算mu和sigma测试集直接套用训练集的参数。更严谨的做法是把标准化放进交叉验证的每一折内部每一折用训练子集计算mu和sigma再标准化该折的训练和测试子集。用fitcecoc的Prior配合模板函数做标准化或者在特征提取后手动实现这是排查时最需要盯住的代码位置。5.3 标签和特征错位预测结果像随机数现象训练过程没有报错但准确率始终停留在各类别占比附近和随机猜测差不多。原因特征矩阵和标签矩阵没有按行对齐。常见于先循环拼特征再循环拼标签两段循环的样本顺序不一致或者在shuffle样本时只打乱了特征矩阵的行没有同步打乱标签。解决把特征和标签并成一个表格用sortrows或按索引统一打乱。最简单稳妥的做法是每次 shuffle 时记录索引idx randperm(N); feature feature(idx, :); label label(idx);打乱的索引一定要复用。养成习惯特征矩阵和标签矩阵在程序里始终放在相邻位置处理不要跨代码段操作。5.4 类别极度不平衡每个故障样本都是宝现象数据集里正常样本 1500 条内圈故障样本 15 条模型对故障类别的召回率极低。原因SVM 的优化目标是最大化整体分类间隔没有显式考虑类别占比。正常样本占据了大部分空间决策边界会偏向故障类把故障样本判成正常可以换取整体准确率的提升代价恰好是你的业务不能接受的。解决三层手段叠加。首先在fitcecoc里设置Prior, empirical让模型感知先验分布其次用代价矩阵Cost给漏报故障加大惩罚如果样本量实在太少再加数据增强。对振动信号做轻微幅值扰动、时间平移、添加低幅值噪声都能扩充故障样本。数据增强后的样本要保证物理可行性比如频率成分不能改变太大否则模型学到的是人工痕迹。5.5 混淆矩阵角落里的零某个故障类永远预测不对现象训练结束后混淆矩阵显示某两类的识别结果互相混淆严重或者某个小类别在所有测试样本上都被预测错。原因两类故障的特征在提取的特征空间里高度重叠。比如轴承内圈故障和外圈故障的时域特征非常接近仅仅用 RMS 和峭度无法区分或者某个类别样本特征差异太大SVM 边界只能把它分成几块中间区域的样本必然误分。解决先做特征可视化用tsne把高维特征降到二维看各个类别的点云分布是否可分。如果两类重叠严重回特征提取环节增加新的区分性特征比如包络谱故障特征频率及其边频带能量。这一类需要领域知识补充特征单纯调 SVM 参数解决不了。先用 t-SNE 图确认可分性再决定是加特征还是换分类器这是排查此类问题的标准路径。6. 换个思路用 SVM特征选择、单分类和半监督的三角进阶前三节都在讲标准的特征加分类流程最后聊三个不常见但很实用的变体用法分别针对特征工程、异常检测和标注稀缺三个场景。特征选择用 Lasso 或前向搜索筛出关键特征。特征维度超过 20 个时可以先做一次特征选择再训练 SVM。常见做法是用 Lasso 回归对每个类别做特征筛选或者用前向贪心搜索每次加入一个特征跑一次交叉验证保留提升最大者。这样做的收益不只是准确率很多时候还能让模型从 15 个特征精简到 6 个部署时传感器采样率不变但计算量大幅下降现场工控机的负载压力小很多。单分类用 SVDD只有正常样本时也能做异常检测。某些故障类型几乎没有历史数据但有大量正常样本。这种场景用标准的多分类 SVM 无从下手改用一类分类——SVDD支持向量数据描述。Matlab 里fitcsvm的单分类模式可以实现把所有正常样本作为一个类别学习一个紧凑的球面边界新样本落在边界外即为异常。代价是只能报“是否异常”无法区分故障类型。半监督思路伪标签扩充故障样本。当某个故障样本极少时可以先用已有的 SVM 模型对大量未标注信号做预测把置信度超过 0.95 的样本人工复核后加入训练集。这个做法有风险模型本身有偏差伪标签会放大偏差。稳妥做法是只对预测结果非常稳定的样本取用且加入不超过现有故障样本量的一半多次迭代逐步扩充。我现在做故障诊断项目的习惯是先把特征提取做到位用 t-SNE 快速检查各类别可分性再交给 SVM 调参。特征空间要是分不开换任何分类器都是原地打转。这几个变体用法不需要一开始就用上但遇到数据不平衡、标注困难、特征冗余的卡点时回头翻一翻这篇笔记也许就省了一个通宵。希望帮到你。本文还有配套的精品资源点击获取
返回列表