资讯详情

资讯详情

深度自编码网络实现轴承故障诊断:原理、代码与工程落地

设备维护里轴承故障是最常见也最磨人的问题之一。很多工程师都遇到过这种局面振动测点的数值明明还压在报警线之下设备拆开一看轴承内圈却已经出现了明显的剥落和点蚀。等振动总量指标真正抬头的时候损伤往往已经发展到必须立即停机的程度。问题不在于信号里没有早期故障信息而在于早期冲击特征微弱、混在强噪声里靠人工看频谱、看包络谱门槛太高普通班组很难做到日常巡检。我做的这个项目就是围绕“深度自编码网络实现轴承故障诊断”展开的技术栈是Python加TensorFlow。核心思路非常直接只拿正常振动信号训练一个自编码器让它学会重构正常模式然后用重构误差去判别故障样本。你不需要收集几十万条故障数据正常样本管够就能把“有没有异常”这件事做得很稳。这篇文章会从原理、数据准备、网络设计、完整代码到排查技巧把整套方案讲透。适合刚接触故障诊断的工程师、相关方向的学生以及手上故障样本稀少但又想做深度诊断落地的团队参考。1. 为什么故障诊断要选深度自编码网络1.1 传统轴承诊断的局限为什么故障样本少是个硬约束传统诊断最常用的手段是看振动总量比如计算加速度信号的有效值超过报警线就安排检修。这个逻辑本身没问题但RMS是一个全局平均指标早期点蚀或微裂纹产生的冲击成分在总能量里占比极小平均之后基本看不出变化。哪怕做带通滤波、包络谱分析也得先知道轴承几何参数和故障特征频率才能定位边带各种工况一变经验又要重新积累。所以说到底传统方法对人员经验的要求很高而且很难自动化和规模化。于是大家自然想到上深度学习。常规路线是拿CNN或LSTM做监督分类输入振动波形输出内圈故障、外圈故障、滚动体故障之类的类别标签。这个方案在故障样本充足、工况稳定的实验室数据上效果确实很好但到了工业现场就卡壳一台设备正常跑几年故障样本可能只有检修前录下的几十段数据而正常状态的数据每天都在产生这段规模完全不是一个量级。用这么少的故障样本去训一个多分类网络过拟合几乎是必然的。这还没算转速负载变化带来的数据分布漂移问题老样本训出的模型面对新工况往往直接失效。所以我在设计这个项目时先做了一个很现实的判断训练数据天然不均衡稀缺的是故障样本不是正常样本。既然这样方案就应该先把“有没有问题”这件事做出来在只用正常数据建模的框架下做异常检测之后如果有少量故障标签再去做“具体是什么问题”的细化分类。深度自编码网络完全符合这个需求它的检测逻辑不依赖故障样本瓶颈特征又能复用到后续分类任务上成了很自然的基座模型。1.2 自编码器能干什么原理定位与替代方案选型对比可以把自编码器想象成一个只练过标准字帖的临摹师。你给他看一张工整的正常振动波形他能临摹得又快又像但如果你递上一段歪歪扭扭的故障波形他画出来的东西和原稿之间就会出现明显差异。这个差异就是重构误差也是整套诊断方案的核心依据。具体来说深度自编码网络是一种输入和输出维度完全一致的对称结构。编码器把高维振动信号压缩到低维瓶颈层解码器再把瓶颈特征还原成原始尺寸。训练目标是最小化还原信号与输入信号的误差因此网络学到的本质上是训练数据的分布特征。用正常数据训练完之后模型对正常模式的重构能力很强对未见过的异常模式重构能力则很差重构误差就自然成了异常分数。做一个方案选型对比会更清楚它的位置方案对故障样本依赖可解释性能否识别故障类型典型适用场景振动总量阈值报警无高只报警不分类常规产线保护包络谱/特征频率人工分析无高能识别专家巡检CNN/LSTM监督分类依赖大量故障样本低能识别故障样本充足One-Class SVM无需故障样本中只检测异常小样本异常检测深度自编码网络无需或仅需少量中高检测 特征扩展后可分类故障样本稀缺、工况复杂从这张表可以看得很清楚自编码器的核心竞争力不是分类精度而是“只靠正常数据就能建模”的先天属性。在实际项目里它还有一个隐性优势编码器部分抽出来的瓶颈特征就是一套降维后的敏感特征可以直接喂给后续分类器相当于把“检测异常”和“识别故障类型”两件事解耦先解决主要矛盾再解决次要矛盾调试和运维都会清晰很多。2. 动手前的技术准备数据、结构与诊断策略2.1 数据怎么准备采样率、窗口长度与逐窗口标准化数据是整个方案的地基。以常见的公开轴承实验振动数据集为例通常包含正常、内圈故障、外圈故障、滚动体故障这几类每种还有不同损伤程度。加速度信号的采样率常见有12 kHz和48 kHz两档做自编码器检测用12 kHz就够了计算量可控特征信息也完全足够。原始信号动辄几十万甚至上百万个点不可能整段塞进网络必须用滑动窗口切分成固定长度的样本。窗口长度直接影响模型的感知范围。以转频30 Hz、采样率12 kHz为例一个旋转周期大约是400个采样点窗口取512个点覆盖约1.28个转频周期既能包含至少一次冲击事件又不会让样本量掉太多。窗口太短会切碎冲击特征窗口太长则单段信号里重复次数的差异被平均掉反而不利于学习稳定的波形模式。滑动步长我习惯取128相邻窗口保持四分之三以上的重叠这样样本数量充足异常片段也不容易被漏掉。预处理阶段有个容易踩的坑标准化方式。不少人习惯对整段原始信号求均值和方差做一个全局z-score但切窗之后再按时间位置拟合scaler并不合理因为滑动窗口的每个位置并没有固定物理含义。我推荐的方案是按窗口逐样本标准化即每个窗口独立减去窗口内均值、除以窗口内标准差。这么做的好处很直接自编码器学的是波形的形态结构而不是某个测点的绝对幅值水平。不同转速、不同负载下振动幅值可以差出好几倍不做逐窗口标准化的话重构误差会被幅值主导波形形态的异常反而被淹没了。还要特别注意数据划分边界训练自编码器只用正常样本验证集也从正常样本里抽取故障样本全部留到阈值评估阶段再暴露。这样可以避免模型在训练过程中“见过”异常信号保证重构误差对故障的响应是自然涌现的而不是记忆出来的。2.2 网络结构怎么定层数、瓶颈维度与激活函数深度自编码网络的层数没必要堆得很夸张五到七层全连接就足够。输入节点数等于窗口长度512编码器部分常用256、128两层带ReLU的全连接再压到64维瓶颈层解码器对称回来128、256最后输出512维。五个隐藏层加一个输出层就是名副其实的深度自编码网络。瓶颈维度是个需要认真权衡的参数。取太大比如128甚至256模型几乎可以原样保留输入信息正常和故障样本的重构误差都会很小区分度急剧下降取太小正常样本本身的重构误差就压不下来阈值同样难定。我的经验是取输入维度的八分之一到四分之一512维输入对应64左右通常能兼顾重构质量和异常灵敏度。这个结论我是在多次对比实验里验证过的直接抄作业也成立。输出层的激活函数一定要用linear这是初学阶段最常见的翻车点。振动信号经过标准化之后是带正负的连续数值如果输出层用了sigmoid输出被限制在0到1之间和输入数据空间根本不匹配训练多久loss都降不下去。隐藏层用ReLU没问题但如果发现训练后期出现大量神经元死亡也就是loss卡在某个平台不动可以局部换成LeakyReLU缓解。训练参数方面优化器选Adam初始学习率1e-3batch size 128损失函数用MSE。训练过程必须配两个回调EarlyStopping监控验证集重构误差设patience在10到15之间防止过拟合ReduceLROnPlateau在学习率停滞时自动减半最低压到1e-5。这两个回调配合起来基本不会出现训练失控的情况。2.3 诊断策略怎么选异常检测、分类微调与多模型融合模型本身只是工具怎么落地成诊断能力才是关键。根据手头标签数据的多少有三种落地策略可以选。如果什么故障标签都没有就用纯无监督异常检测。训练一个自编码器对每个待测样本计算重构误差超过阈值就报警。这个方案最简单也最贴合“正常数据管够、故障样本为零”的冷启动场景代价是只能判断异常与否不能告诉你是哪类故障。如果还能攒到少量带标签的故障样本就用特征提取加分类头微调。训练完自编码器后把编码器单独抽出来对每个样本取64维瓶颈特征再接一个softmax分类层用少量故障样本微调。这里的关键是只训练分类层不要动编码器权重让特征提取部分完整保留自编码器从海量正常数据中学到的东西。故障类型识别对故障样本的需求会大幅下降。如果各故障类型的样本都有一定数量可以用多自编码器最小重构误差判别。每种故障类型各训一个自编码器待测样本分别输入所有模型哪个模型重构误差最小就判为哪类。逻辑特别朴素但胜在稳定而且每个模型都是独立的新增一种故障类型只需要单独补训一个。我在现场项目里用的就是第一和第三种组合先用检测模型做在线异常趋势报警确认异常后再用多模型细分故障类型分工明确运维人员也容易理解。3. Python TensorFlow 完整实现与核心代码3.1 环境准备TensorFlow版本与依赖安装代码环境不复杂Python 3.9以上、TensorFlow 2.x、numpy、scikit-learn、matplotlib就够了。直接执行命令安装pip install tensorflow2.13.0 numpy scikit-learn matplotlibTensorFlow版本不用追求最新2.10到2.16之间都行API稳定。这个项目规模很小输入512维、五层全连接用纯CPU跑训练也就几十秒的事完全不需要为这个项目专门配GPU。3.2 核心预处理滑动窗口切分与标准化的完整代码第一步把一维原始信号切成二维样本矩阵。窗口512、步长128的切分函数如下import numpy as np def sliding_window(signal, window_size512, stride128): windows [] for i in range(0, len(signal) - window_size, stride): windows.append(signal[i:i window_size]) return np.asarray(windows) train_windows sliding_window(normal_signal) # 正常信号切窗训练用 fault_windows sliding_window(fault_signal) # 故障信号切窗测试用要提醒一点切窗时必须保证原始信号是干净的浮点数组如果有毛刺或异常脉冲先用中值滤波或低通滤波清一遍再做窗口否则单个毛刺会被切进多个窗口造成一堆虚假异常样本。标准化用逐窗口z-score方式。这里把窗口内的均值和标准差直接压到与窗口等长的向量上加减法时广播机制会自动匹配def standardize_windows(windows): mean windows.mean(axis1, keepdimsTrue) std windows.std(axis1, keepdimsTrue) return (windows - mean) / (std 1e-8) x_train standardize_windows(train_windows) x_val standardize_windows(train_windows[::10]) # 正常样本抽10%验证 x_fault standardize_windows(fault_windows) # 故障样本测试用逐窗口标准化的一个额外好处是部署时方便现场采集一段新数据同样按窗口独立标准化不需要维护历史数据的全局统计量对数据长度和幅值变化都不敏感。3.3 核心模型深度自编码网络的搭建与训练模型定义直接走TensorFlow Keras接口。编码器和解码器对称同时返回自编码器模型和编码器模型后者在后续特征提取时直接复用import tensorflow as tf from tensorflow.keras import layers, models def build_autoencoder(input_dim512, encoding_dim64): inputs tf.keras.Input(shape(input_dim,)) # 编码器部分512 - 256 - 128 - 64 x layers.Dense(256, activationrelu)(inputs) x layers.Dense(128, activationrelu)(x) encoded layers.Dense(encoding_dim, activationrelu)(x) # 解码器部分64 - 128 - 256 - 512 x layers.Dense(128, activationrelu)(encoded) x layers.Dense(256, activationrelu)(x) decoded layers.Dense(input_dim, activationlinear)(x) autoencoder models.Model(inputsinputs, outputsdecoded) encoder models.Model(inputsinputs, outputsencoded) return autoencoder, encoder autoencoder, encoder build_autoencoder(512, 64) autoencoder.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), lossmse )训练部分加上EarlyStopping和ReduceLROnPlateau两个回调这是保证模型收敛稳定性的关键callbacks [ tf.keras.callbacks.EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), tf.keras.callbacks.ReduceLROnPlateau(monitorval_loss, factor0.5, patience5, min_lr1e-5), ] history autoencoder.fit( x_train, x_train, epochs100, batch_size128, validation_data(x_val, x_val), callbackscallbacks, verbose1, )训练时注意观察一个细节训练集重构误差和验证集重构误差最终应当收敛到比较接近的水平。如果验证集loss明显高于训练集说明模型开始背训练样本了这时候优先调整EarlyStopping的patience或者考虑给中间层加一点Dropout而不是盲目加大模型容量。训练完成后autoencoder直接保存成模型文件现场部署时加载计算即可。3.4 判定与评估重构误差阈值、ROC曲线与特征可视化模型训练好之后重构误差的计算就是一次前向传播加一次均方差计算def reconstruction_error(model, x): x_pred model.predict(x, verbose0) return np.mean(np.square(x - x_pred), axis1) normal_recon reconstruction_error(autoencoder, x_val) fault_recon reconstruction_error(autoencoder, x_fault)拿到两类样本的重构误差之后先用最简单的99分位法定初始阈值threshold np.percentile(normal_recon, 99) predictions (fault_recon threshold).astype(int) # 1表示故障如果手里有少量已知故障样本更严谨的做法是直接画ROC曲线用约登指数选阈值并用AUC评估整体区分能力from sklearn.metrics import roc_curve, roc_auc_score scores np.concatenate([normal_recon, fault_recon]) labels np.concatenate([np.zeros_like(normal_recon), np.ones_like(fault_recon)]) fpr, tpr, thresholds roc_curve(labels, scores) auc roc_auc_score(labels, scores) youden tpr - fpr best_idx np.argmax(youden) best_threshold thresholds[best_idx]我实际操作时一定会把正常和故障两组重构误差的直方图叠在一起画出来两个分布之间的距离比任何单点指标都直观。分布拉得越开诊断系统越可靠重叠严重就先回去查数据标准化和瓶颈维度而不是反复调阈值。如果希望理解模型到底学到了什么可以借助t-SNE把瓶颈层特征降到二维平面看图。将正常和故障样本都输入encoder得到一个64维特征矩阵再用t-SNE压到二维from sklearn.manifold import TSNE features encoder.predict(np.vstack([x_val, x_fault]), verbose0) tsne TSNE(n_components2, perplexity30, random_state42) feat_2d tsne.fit_transform(features)正常样本通常聚成一团故障样本则散落在团外或形成独立的小簇。t-SNE的perplexity不能超过样本数样本量偏少时改成15到20更稳妥。这一步在向团队汇报“模型为什么判它异常”的时候很有说服力也适合放在项目文档里当技术附件。4. 常见问题排查与方案扩展实录4.1 重构误差分布重叠时按什么顺序排查整个方案跑通之后最常遇到的问题是正常和故障的重构误差直方图分不开阈值怎么选都别扭。遇到这种情况先别急着调模型按顺序排查三件事。第一确认标准化是否做到了逐窗口z-score。如果整段信号用同一个全局均值和方差去标准化不同窗口之间的幅值差异会直接混进重构误差里导致正常样本的误差反而比某些故障样本更大。换成逐窗口标准化后这个问题基本会消失。第二检查瓶颈维度。如果编码器的瓶颈层设置得过大模型对正常模式保留的信息太完整故障信号虽然可能属于另一类但因为表示空间足够宽解码器也能勉强重构回来误差自然拉不开。把瓶颈维度压回输入维度的八分之一左右再试往往立竿见影。第三思考样本内部是否还有工况漂移。比如数据里同时包含不同转速、不同负载下的正常信号它们本身就在特征空间里分成多个簇这时候单一阈值模型会失效。解决办法是按工况分组训练多个自编码器每个模型单独定阈值部署时先判断当前工况再选择对应模型。阈值本身也不是只能靠99分位或者经验拍板。一旦能收集到少量已知故障样本就切换到ROC约登指数来选阈值它在误报率和漏报率之间的平衡比固定分位数更合理现场接受度也更高。4.2 训练不收敛与重构失真四个嫌疑逐一排除训练不收敛或重构效果差不是玄学绝大多数情况下是配置问题。我建议按下面四个嫌疑点逐一排查。第一个嫌疑是输出层激活函数。如果写成sigmoid而输入是标准化的带正负振动波形数据空间完全不匹配loss会一直在高位徘徊换成linear之后问题立刻缓解。第二个嫌疑是学习率偏大。Adam默认1e-3在多数时候没问题但遇到loss震荡、回弹、发散这些症状时直接把learning_rate降到1e-4甚至1e-5多跑几十个epoch通常能稳定下来。第三个嫌疑是输入没有标准化。原始振动信号的加速度幅值可能在正负几百甚至几千MSE数值会大得离谱梯度也跟着被带偏。逐窗口标准化之后输入被控制在零均值单位方差附近重构误差的量级也更容易预期。第四个嫌疑是过拟合。判别标准还是看训练loss和验证loss的差距训练很低、验证偏高就是过拟合信号。优先调整EarlyStopping的patience或者给编码器中间层加dropout(0.1)之类的轻量正则而不是继续增加层数。这里有一个常用的问题排查表可以直接对照着处理症状优先排查项常用对策loss 一直不降输出层激活函数 / 输入标准化改用 linear、逐窗口 z-score训练震荡或发散学习率过大Adam 学习率降至 1e-4训练 loss 低但验证 loss 高过拟合EarlyStopping、Dropout 0.1正常与故障重构误差重叠瓶颈维度偏大 / 工况漂移减瓶颈维度、按工况分组建模4.3 故障类型识别改造分类头微调与多自编码器方案自编码器常被误认为只能做异常检测不能识别故障类型其实改造成分类器非常直接。最常用的方案是在训练好的encoder后面接一个softmax分类头做微调时冻结encoder权重只训练分类层def build_classifier_from_encoder(encoder, num_classes4): inputs encoder.input feats encoder(inputs) feats layers.Dense(64, activationrelu)(feats) outputs layers.Dense(num_classes, activationsoftmax)(feats) classifier models.Model(inputs, outputs) classifier.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] ) return classifier你也可以采用多自编码器方案每类样本单独训练一个自编码器待测样本分别送入各模型取重构误差最小的类别作为结果def classify_by_reconstruction(model_dict, x): errors [] for name, model in model_dict.items(): err np.mean(np.square(x - model.predict(x, verbose0)), axis1) errors.append(err) errors np.stack(errors, axis1) return np.argmin(errors, axis1)用多自编码器方案时有个坑不同类别模型的重构误差绝对值可能有尺度差异比如某类数据的波形本身更复杂模型重构误差整体偏大直接比较会系统性偏向误差小的那类。稳妥的做法是先用每个模型对训练数据的重构误差算出均值和标准差再对待测样本的误差做z-score标准化最后比较标准化后的相对偏差而不是原始误差。我实际项目的体会是两阶段方案比端到端多分类稳定得多。先用单自编码器做异常检测判断“有没有问题”确认异常之后再切换到多模型或分类头去判断“是哪类问题”。故障与故障之间的波形差异有时候比故障与正常之间的差异更难学混在一个任务里会让分类器为了追求准确率而牺牲检测灵敏度分开做反而两边都能调到最优。4.4 方案扩展从轴承到齿轮箱和电机从全连接到卷积这套技术路线的迁移性比想象中强。凡是“正常样本管够、故障样本稀缺”的设备健康管理场景齿轮箱、电机、泵、风机这些旋转机械振动信号结构类似都可以直接套用。迁移时需要调整的主要是窗口长度和预处理节奏比如齿轮箱的啮合频率比轴承故障频率高窗口长度可以适当缩短确保每个窗口包含足够的啮合冲击周期。变转速工况又是另一道坎。转速变化导致相同长度窗口覆盖的物理时长不一致自编码器会把“转速差异”误学成“异常模式”这时候最好先做阶比分析把振动信号从时间域转到角域再切窗让每个窗口对应固定的轴转角度数模型才能忽略转速影响、聚焦波形形态本身。网络结构上也有演进空间。全连接自编码器简单可靠但1D卷积自编码器在局部冲击特征提取上更细腻把Dense层换成Conv1D加MaxPooling再用UpSampling或Conv1DTranspose重建识别冲击类故障时往往更敏锐。如果面对的是长时间序列趋势异常还可以换LSTM自编码器去捕捉时间依赖。重构误差的计算和阈值判定逻辑完全不变迁移成本主要集中在数据处理层。最后再提醒一句自编码器给出的是数据分布层面上的异常得分是强相关信号不是因果证明。现场部署时最好把重构误差图表、时域波形、负载变化曲线放在一起看结合检修记录交叉验证把它定位成“异常筛查的第一道关口”和“专家分析的前置过滤网”这个方案的工程价值会大很多误判风险也会被明显压下来。我个人做完这个项目最大的体会是深度学习在工业诊断里的正确姿态更像是“特征压缩器和异常放大镜”它把物理特征用网络语言重新表达出来而不是替代物理经验和检修判断。代码跑通只是第一步真正有价值的是理解重构误差背后的分布语义以及一套稳定可靠的数据处理流程。如果你想快速验证这套方案我建议先用一段正常样本和一段已知故障样本跑通全流程把两组重构误差直方图叠出来看只要这个分布拉得开后续分类、阈值、部署就都有戏。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →