
简介SVM支持向量机故障诊断与分类预测的完整Matlab源码包基于西储大学轴承诊断数据经特征提取后的数据集可在Matlab2023环境中直接运行。资源主要面向需要快速开展轴承故障分类研究的科研人员、研究生以及工程应用开发者系统解决支持向量机建模、训练、验证与结果可视化全流程问题。压缩包共包含七十一个文件大小约一点三一兆字节内容涵盖Matlab主脚本、LIBSVM-3.3完整工具箱包括C/C源文件、Python辅助脚本、可直接调用的MEX编译文件、示例数据以及可视化结果图片目录中还区分了tools、java、python、matlab等子模块便于按需查阅。资源已有153人学习体量小巧但功能闭环。借助该包可直接获得完整的SVM故障诊断方案SVM.m训练脚本完成特征训练与分类data.mat提供轴承特征数据集混淆矩阵绘制函数支持精度评价结合libsvm内置工具还能实现参数寻优与模型对比帮助使用者快速复现实验并迁移到自身数据有效缩短算法验证周期。1. SVM故障诊断不是玄学中小样本下比深度学习更稳的Matlab方案拿到一批轴承振动数据让你做SVM支持向量机故障诊断/分类预测多数人的第一反应是找一段源码直接跑看到准确率就收工。但真实产线上这个流程翻车的概率比你想象的高特征没提取对、核参数没寻优、标签不平衡任何一个环节都能让准确率从95%掉到65%。这篇文章把SVM故障诊断从原始信号到Matlab完整源码的每一步拆开讲清楚覆盖特征工程、核函数选型、参数寻优、多分类实现和落地部署读者可以照着复现。适合做设备健康管理、工业机器人产线维护的工程师也适合用Matlab做故障诊断方向毕设的同学。2. 把振动信号变成SVM能吃的特征矩阵时域、频域与小波包SVM不直接吃原始信号。你给它一段1024点的原始波形它连该看哪都不知道——支持向量机分类预测的前提是输入向量在特征空间里近似线性可分而原始采样点的时间序列在特征空间里基本是混沌状态。所以故障诊断的第一步永远是特征提取。常见做法是从原始信号里算出若干统计量拼成一个特征向量每个样本一行标签一列喂给SVM。公开数据集里凯斯西储CWRU轴承数据和西安交大XJTU轴承数据是两种最常见的起点拿它们跑通流程再换产线自采数据。2.1 时域特征先打底12个统计特征哪些对故障敏感时域特征是直接从原始振动信号x(n)上算的计算量小适合做第一层筛选。做滚动轴承故障诊断时我一般会算12个均值、峰值、绝对平均值、方根幅值、均方根、标准差、峭度、偏度、峰值因子、脉冲因子、裕度因子、波形因子。这几个特征对应着不同的故障敏感度。均方根对平稳磨损状态敏感但早期点蚀产生的冲击在RMS里被平均掉了峭度对早期冲击非常敏感局部故障一出现峭度立刻飙升但它的缺点是扛不住随机干扰偶尔一个噪声尖峰就能把峭度顶上去。所以实际使用里我不会只靠一两个特征而是把时域统计量做成一个特征组。特征计算在Matlab里是数组运算不是不能用循环而是没必要。信号长度N已知x是列向量一段段算完拼在一起即可。这样算出来的特征值分布范围差异很大——RMS可能是零点几峭度可能是三到八频域能量可能是几十上百这种量纲差异会在后面归一化环节埋雷具体踩坑记录放到第五章展开。2.2 频域与小波包特征把故障频率从噪声里抠出来时域特征之外必须补频域特征否则很多故障分不开。滚动轴承有典型的故障特征频率——外圈BPFO、内圈BPFI、滚动体BSF这些频率和转频、节径、接触角相关。直接从频谱里读故障特征频率在理想工况下没问题但转频波动和噪声干扰会让峰值位置漂移读数不可靠。我一般对每段信号做FFT然后提取三个频域统计量频谱重心频率、均方频率、频率方差。这三个量描述频谱的集中位置和分散程度轴承内圈故障和外圈故障在这种特征下往往能拉开距离。更常用的做法是小波包分解。对信号做三层小波包分解得到八个频带计算每个频带的能量占比。轴承出现局部故障时能量会向特定频带集中这个“能量分布指纹”比单纯频谱统计量可靠。Matlab里wpdec和wprcoef两个函数就能实现需要Wavelet Toolbox。2.3 特征提取的Matlab代码23维特征向量一段函数跑通下面这段函数输入一段原始信号输出一行特征向量。实际使用时把数据集里每个样本调一次这个函数拼成总特征矩阵。function feat extract_features(x, fs) % x: 单段振动信号列向量 % fs: 采样率单位 Hz % feat: 1xN 特征向量 x x(:); % 强制列向量 N length(x); % ---------- 时域特征 ---------- rms_val sqrt(mean(x.^2)); % 均方根 peak_val max(abs(x)); % 峰值 kurt_val kurtosis(x); % 峭度 skew_val skewness(x); % 偏度 std_val std(x); % 标准差 mean_val mean(x); % 均值 absmean mean(abs(x)); % 绝对平均值 crest peak_val / rms_val; % 峰值因子 impulse peak_val / absmean; % 脉冲因子 shape rms_val / absmean; % 波形因子 sqrt_amp (mean(sqrt(abs(x))))^2; % 方根幅值 margin peak_val / sqrt_amp; % 裕度因子 % ---------- 频域特征 ---------- X fft(x); % 复数频谱 P abs(X(1:floor(N/2))).^2; % 单边功率谱 f (0:floor(N/2)-1) * fs / N; % 频率轴 P P / sum(P); % 归一化能量 fc sum(f .* P); % 重心频率 msf sum(f.^2 .* P); % 均方频率 fvar sum((f - fc).^2 .* P); % 频率方差 % ---------- 小波包能量占比三层8个频带 ---------- wpt wpdec(x, 3, db4); % db4小波包分解 E zeros(1, 8); for i 1:8 r wprcoef(wpt, [3 i-1]); % 重构第3层第i个频带 E(i) sum(r.^2); end E E / sum(E); % 能量占比 feat [mean_val, rms_val, peak_val, std_val, kurt_val, skew_val, ... absmean, crest, impulse, shape, sqrt_amp, margin, ... fc, msf, fvar, E]; end这段代码把三类特征拼成一个向量前面12个是时域统计量中间3个是频域统计量最后8个是小波包能量占比总共23维。wpdec和wprcoef需要Wavelet Toolbox如果你的Matlab装了精简版可以把小波包那几行注释掉用前15维先跑通全流程再决定要不要加回。采样率fs只用于计算频率轴不同采样率下重心频率数值会变但SVM对特征是否带单位不敏感只要所有样本用同一套提取流程即可。db4中的4表示滤波器长度滚动轴承故障诊断里db4是常用起点换db8或sym5不会本质改变能量分布格局。这一段函数是后面所有流程的地基特征提取的质量直接决定SVM分类预测的上限。3. 核函数与参数寻优决定SVM分类预测效果的c和g特征矩阵有了接下来面临两个选择用哪种核函数以及核函数的参数取多少。很多人把SVM当成黑匣子fitcsvm默认参数跑完就收工。但SVM支持向量机故障诊断的精度瓶颈往往不在模型原理而在参数和特征没配对。RBF核在归一化后的特征上表现稳定但它的gamma参数设不好模型要么过拟合要么欠拟合这是最常见的翻车现场。3.1 四种核函数怎么选线性、RBF、多项式、Sigmoid的适用边界核函数的本质是隐式地把样本映射到高维空间。线性核适合特征维度高、样本量中等且本来就近似线性可分的情况——特征维度一高非线性映射带来的收益就不明显反而增加过拟合风险。多项式核能表达非线性边界但阶数高了数值容易溢出Matlab里Polynomial核经常因为内积过大出现NaN。RBF核是故障诊断里的默认选择。它的表达式是exp(-g·||x1-x2||^2)只有一个gamma参数要调映射后的空间维度趋近无穷只要gamma合适理论上能逼近任意决策边界。Sigmoid核行为类似两层的神经网络在某些参数组合下输出异常实际工程里我很少用它。我的选型建议是先归一化特征然后用线性核跑一遍拿准确率基线再用RBF核做网格寻优。如果RBF比线性核高出的精度不超过两个百分点就选线性核——训练快、可解释性好产线师傅也服气。如果线性核在验证集上明显拉胯再上RBF。SVM和CNN相比优势在中小样本下更明显不需要几千张图做预训练几十个样本就能训练出可用的分类器。3.2 网格搜索加K折交叉验证c和g怎么找最稳RBF核有两个参数需要调惩罚系数c和gamma。c是误分类惩罚权重c越大模型越不愿意犯错但也越容易过拟合gamma是RBF宽度的倒数gamma越大每个支持向量的影响范围越小、决策边界越曲折gamma过小则边界过于平滑两类可能直接混在一起。最稳的参数寻优方式是网格搜索配合K折交叉验证。把c和g分别取指数网格搜索范围覆盖2^-5到2^10rng(42); c_list 2.^(-5:1:10); g_list 2.^(-10:1:5); acc_mat zeros(length(c_list), length(g_list)); for i 1:length(c_list) for j 1:length(g_list) % 固定一组c,g做5折交叉验证 acc cross_validate_svm(features, labels, c_list(i), g_list(j)); acc_mat(i,j) acc; end end % 找最高准确率对应的参数 [max_acc, idx] max(acc_mat(:)); [i_best, j_best] ind2sub(size(acc_mat), idx); best_c c_list(i_best); best_g g_list(j_best); fprintf(最优参数: c%.4f, g%.4f, cv_acc%.2f%%\n, ... best_c, best_g, max_acc*100);cross_validate_svm是一个自定义函数内部做5折切分、训练、预测、算准确率。网格的跨度按指数取是因为c和g对精度的响应在指数尺度上近似均匀线性网格会导致小数值区太密、大数值区太疏。网格范围不是固定的如果最优值落在边界上就把网格向那一侧扩展再搜一轮。交叉验证折数一般取5或10。数据量大用5折省时间数据量小用10折更稳。交叉验证只在训练集内部做测试集全程不能参与参数选择否则测试集就失去了独立评估的意义严格说这叫数据泄漏第五章有具体案例。3.3 用fitcsvm还是LIBSVM两条路线怎么选Matlab里做SVM分类预测有两条路一是自带的fitcsvm属Statistics and Machine Learning Toolbox二是台湾大学林智仁教授的LIBSVM第三方包。很多网上流传的老代码是LIBSVM版本新版Matlab里跑这些代码经常报错因为LIBSVM需要自己用mex编译。fitcsvm的好处是官方维护、接口稳定、和Coder工具链集成好训练好的模型可以直接生成C代码用于部署。不利之处是它天生是二分类器多分类要自己包一层One-vs-One或One-vs-AllMatlab里fitcecoc封装好了这些策略但理解成本比LIBSVM高一点。LIBSVM的好处是把多分类、交叉验证、网格寻优封装成现成函数svmtrain和svmpredict一行调用。代价是编译环境配置对新手是个门槛。我做项目的选择是毕设或快速验证用LIBSVM省事需要产线部署或Simulink集成用fitcsvm加fitcecoc官方链路少踩坑。两者对比整理如下对比项fitcsvm / fitcecocLIBSVM依赖Statistics and ML Toolbox需自行编译mex多分类fitcecoc封装OVO/OVA内置多分类交叉验证crossval方法svmtrain自带-v参数模型导出Coder生成C代码需自己写接口参数设置KernelScale、BoxConstraint-c、-g直传4. 在Matlab中跑通SVM故障诊断源码数据划分到混淆矩阵前面特征已经提取完毕特征矩阵features和标签labels已经就绪。第四章直接给一份可完整跑通的训练预测流程按“数据划分→训练→评估”的顺序拆开。每一步都有对应代码把注释读一遍就知道每个变量在干什么。4.1 数据划分分层抽样保证小故障类不缺席故障诊断数据集的标签分布往往不均匀。正常样本可能几千条每种故障样本几百条。如果直接randperm随机打乱划分运气差时某类故障在训练集里只剩一点点样本模型压根没见过这个类的全貌。正确做法是分层抽样按类别比例分别在每个类内部随机抽取保证训练集和测试集中每个类的占比与总体一致。Matlab里cvpartition直接支持分层划分rng(42); % 固定随机种子保证结果可复现 cvp cvpartition(labels, Holdout, 0.3); % 30%做测试集 idx_train cvp.training; idx_test cvp.test; X_train features(idx_train, :); y_train labels(idx_train); X_test features(idx_test, :); y_test labels(idx_test); % 检查划分后各类样本数 tabulate(y_train) tabulate(y_test)cvpartition的Holdout方式指定测试集比例0.3表示留30%做测试内部按类别分层不会出现某个小类在训练集里彻底消失的情况。rng(42)是血泪经验不固定随机种子每次跑结果都不一样你没法判断改动特征提取代码是变好了还是随机波动造成的。4.2 训练与预测fitcecoc多分类代码与LIBSVM对照标签是数值型1、2、3还是字符型normal、inner、outer都行fitcecoc会自动识别。但标签必须是离散类别编号不能当回归问题去拟合。% fitcecoc多分类内部默认One-vs-One策略 t templateSVM(KernelFunction, rbf, ... KernelScale, 1/sqrt(2*best_g), ... BoxConstraint, best_c, ... Standardize, true); mdl fitcecoc(X_train, y_train, Learners, t, ... Coding, onevsone); % 预测测试集 y_pred predict(mdl, X_test); % 整体准确率 acc sum(y_pred y_test) / length(y_test) * 100; fprintf(测试集准确率: %.2f%%\n, acc);注意fitcsvm的KernelScale与LIBSVM的gamma不是简单倒数关系。RBF核在fitcsvm里写作exp(-||xi-xj||^2/(2·sigma^2))而LIBSVM写作exp(-g·||xi-xj||^2)所以sigma1/sqrt(2·g)。网格寻优得到g后KernelScale要填1/sqrt(2·g)这一点漏掉模型效果直接不对。predict是官方版本预测的标准接口。如果你用LIBSVM等价流程是这样% LIBSVM等价流程需先编译好mex文件 model svmtrain(y_train, X_train, ... -s 0 -t 2 -c 4 -g 0.25 -q); y_pred svmpredict(y_test, X_test, model);s表示SVM类型0是C-SVC分类t是核函数类型2对应RBFc和g就是前面寻优得到的惩罚系数和gamma。LIBSVM的输入格式是标签在前、特征矩阵在后和fitcsvm正好相反这也是从网上抄代码时最容易搞反的地方。4.3 评估不能只看准确率混淆矩阵与每类召回率故障诊断里准确率经常骗人。假如正常类占测试集90%把全部样本判成正常准确率也是90%但这个模型毫无用处。所以训练完必须看混淆矩阵和每类的召回率、精确率。% 混淆矩阵 [C, order] confusionmat(y_test, y_pred); % 每类召回率、精确率、F1 num_classes length(order); recall zeros(num_classes, 1); precision zeros(num_classes, 1); for k 1:num_classes recall(k) C(k,k) / sum(C(k,:)); precision(k) C(k,k) / sum(C(:,k)); end F1 2 * precision .* recall ./ (precision recall); % 可视化 confusionchart(y_test, y_pred);confusionmat返回的C矩阵第k行表示真实类别k的样本被分到了哪些预测类别对角线是正确分类。召回率看的是这个故障别漏掉精确率看的是报出来的故障别报错。设备维护场景下漏报比误报更致命——漏报意味着故障在恶化误报最多是停机检查一次。如果某个类召回率明显低于其他类不要急着调SVM参数先回去检查这个类的样本量是不是太少、特征是不是和另一类高度重叠。内圈故障和滚动体故障在早期阶段时域特征几乎一样这种情况调c和g没用得回去补频带能量特征。5. SVM故障诊断的5个常见坑翻车现象、根因与解决这一章写的都是实际跑SVM故障诊断项目时反复遇到的坑按“现象→原因→解决”的格式记录。每一条都花过时间排查看完能省下不少弯路。5.1 样本不平衡导致小故障类全被吞掉现象测试集总准确率95%打开混淆矩阵发现某一类训练样本少的故障几乎全被预测成其他类该类召回率只有40%。原因SVM的优化目标是整体间隔最大化样本占比大的类在约束条件里占多数决策边界为了迁就多数类把少数类样本牺牲掉了。解决给少数类提高先验权重让误分少数类的代价变大。常见做法是按样本量的反比设置Prior% 按每类样本数反比设置先验少数类获得更高权重 [~, ~, ic] unique(y_train); class_counts accumarray(ic, 1); prior 1 ./ class_counts; prior prior / sum(prior); t templateSVM(KernelFunction, rbf, ... KernelScale, 1/sqrt(2*best_g), ... BoxConstraint, best_c, ... Standardize, true, ... Prior, prior); mdl fitcecoc(X_train, y_train, Learners, t, Coding, onevsone);故障诊断里我倾向于权重方案。过采样容易引入重复样本导致过拟合欠采样会丢掉多数类信息权重方案在工程实现上最直接。5.2 特征没归一化RBF核直接失效现象用RBF核训练后准确率比线性核还低甚至所有样本被预测成同一类。原因RBF核里的距离计算对特征数值范围极敏感。某个特征量级是10^4另一个特征量级是3欧式距离被大数值特征主导小数值特征等于报废。解决所有特征在训练前做标准化。标准化的均值和标准差只能用训练集计算测试集复用同一组统计量不能把测试集和训练集混在一起算否则测试集独立性就没了。fitcsvm的Standardize参数就是干这个的LIBSVM要自己手动标准化% 手动Z-score标准化 mu mean(X_train); sigma std(X_train); X_train (X_train - mu) ./ sigma; X_test (X_test - mu) ./ sigma;5.3 交叉验证结果好但测试集崩盘数据泄漏现象5折交叉验证准确率98%一换到测试集只剩75%差距大得离谱。原因常见泄漏有两处。一是特征提取在全部数据上先做完再划分训练测试集比如小波包分解的某些归一化步骤用了全数据集统计量二是网格寻优时用全量数据做交叉验证最优参数已经偷看了测试集分布。解决特征提取逐样本独立处理不做跨样本统计数据划分在任何特征工程之前完成交叉验证只在训练集内部做。还有一个隐蔽泄漏是特征选择也用全量数据筛选和参数寻优泄漏是同一个问题。5.4 多分类用One-vs-One还是One-vs-All现象用fitcecoc默认设置训练某两类故障总是互相混淆或训练时间异常长。原因fitcecoc默认用One-vs-One类别数为N时训练N(N-1)/2个二分类器。5类就是10个10类就是45个数据量稍大训练时间就翻倍。One-vs-All只要N个分类器但每个分类器面对的负样本是其他所有类混在一起决策边界容易被干扰。解决故障类别少5类以内用OVO边界更精细类别多8类以上或训练时间紧张时换OVA。fitcecoc里改一个参数mdl fitcecoc(X_train, y_train, ... Learners, t, ... Coding, onevsall);5.5 训练集有NaN或无穷值模型直接摆烂现象训练没报错预测结果全是NaN或者fitcsvm报“Kernel function returned NaN or Inf”。原因特征提取时某个样本的FFT或小波包重构产生了NaN或者标准化时某特征方差为0除以0产生Inf。这些NaN在fitcsvm里不会自动报错而是直接传染到核矩阵。解决训练前先检查特征矩阵完整性assert(~any(isnan(features(:))), 特征矩阵含NaN); assert(~any(isinf(features(:))), 特征矩阵含Inf); % 找出方差为0的特征列 zero_var_cols var(features) eps; fprintf(%d 列特征方差为0将剔除\n, sum(zero_var_cols)); features(:, zero_var_cols) [];方差为0的特征列对所有样本恒为常数对分类没有信息量剔除后模型反而更稳。处理NaN的根本手段还是在特征提取阶段加保护FFT之前检查信号长度小波包重构后检查非有限值。6. 让模型从离线脚本走进产线模型导出、增量更新与一类SVM模型在测试集上准确率过了95%接下来要解决的是它怎么从Matlab脚本变成产线上能用的东西以及故障类型变化了怎么维护。这章讲三个进阶方向都是SVM故障诊断落地时绕不开的环节。6.1 用Matlab Coder把fitcecoc模型导成C代码如果产线控制器用C语言开发fitcsvm训练好的模型可以一键导出。Matlab Coder支持对分类对象生成可移植C代码入口函数写法如下save(svm_model.mat, mdl); function label predict_label(X) %#codegen mdl coder.loadDeepLearningNetwork(svm_model.mat); label predict(mdl, X); end生成C代码后注意特征提取逻辑也要移植过去特征提取用到的均值、标准差、小波包滤波器系数都要固化在C代码里不能在部署端重新算否则统计量对不上预测直接偏掉。6.2 新故障类型出现重训还是增量更新产线最现实的问题是半年后出现了一种没见过的新故障模型预测不可靠。常见做法有两种。一是把新故障样本加入训练集重新走网格寻优并重训在小样本下最稳缺点是每次要重跑全部流程。二是增量学习但fitcsvm本身不支持增量训练自己实现SMO增量更新复杂度高数据量不大时完全没必要。我一般建议故障库未稳定时每次有新故障就全量重训几千条数据在Matlab里训练也就几分钟故障库稳定后再固化模型部署。6.3 只有正常样本时一类SVM做异常检测有些场景根本没有故障样本——新设备刚上线还没坏过。这时候做不了多分类能做的是一类分类用fitcsvm训练One-Class SVM只吃正常样本就能建一个“正常边界”mdl_oc fitcsvm(X_train_normal, ones(size(X_train_normal,1),1), ... KernelFunction, rbf, ... KernelScale, 1/sqrt(2*best_g), ... OutlierFraction, 0.05); % 预测1表示正常-1表示异常 y_outlier predict(mdl_oc, X_test);OutlierFraction是预设的异常比例根据现场先验估计0.05表示接受5%的正常样本被误报为异常。这类模型适合设备初期预警等积累了足够故障样本再切换成多分类模型。做产线项目这些年我最大的教训就是不要把SVM当成输出准确率的黑匣子参数寻优、特征选择、数据划分任何一个环节偷懒模型都会在你看不见的地方翻车。希望帮到你。本文还有配套的精品资源点击获取