1DCNN轴承故障诊断实战:从原始波形到混淆矩阵

发布时间:2026/10/12 1:39:29

1DCNN轴承故障诊断实战:从原始波形到混淆矩阵 简介一份基于一维卷积神经网络1DCNN的轴承故障诊断深度学习源码实现面向机械状态监测与深度学习交叉领域的工程师和学习者围绕振动时序信号处理利用卷积层沿时间轴滑动提取局部特征经池化与全连接映射为滚珠、内圈、外圈及保持架等故障类别覆盖数据预处理到可视化完整流程。资源共62个文件zip压缩包约3.7MB含13个Python源码、17张故障可视化图、6个pyc编译文件、5个文本说明及配置备份文件代码涵盖main入口、数据切分、CWRU数据上的1DCNN模型、测试与t-SNE降维模块另有混淆矩阵表格和README辅助复现data目录提供实验数据备份文件便于对比调试。目前已有65人学习下载通过研读完整工程可掌握从原始振动信号端到端提取判别特征、训练1DCNN模型并验证效果的方法适合课程设计、毕业设计或深度诊断项目参考。1. 基于1DCNN的轴承故障诊断为什么我扔掉手工特征改学原始波形生产车间里电机驱动端的轴承一出现早期故障传感器采到的振动信号里就会多出周期性的微小冲击。传统做法是先计算RMS、峰值因子、频谱边带再靠老师傅的经验判断故障位置这一套又慢又依赖人而且微弱故障经常被噪声淹没。于是越来越多团队转向了1DCNN轴承故障诊断的深度学习路线一维卷积网络直接拿原始振动波形当输入把特征提取和分类交给网络自己学端到端输出“正常、内圈故障、外圈故障、滚动体故障”。这篇文章不是泛谈原理而是给出一套可复现的源码实现思路——从PyTorch模型定义、数据预处理到训练调度、混淆矩阵评估和边缘部署。适合有一定Python基础、刚接触深度学习的故障诊断从业者也适合想快速把1DCNN从论文落到实验台的设备健康监测工程师。跟我一起把这个方向的坑踩平比看十篇综述有用。2. 用1DCNN给轴承“听诊”模型结构和选型逻辑2.1 为什么是一维卷积而不是把信号先变成图谱很多文章喜欢把振动信号做短时傅里叶变换或者小波变换变成一张时频图再用二维CNN识别。这条路不是不行但它至少多了两步选什么窗函数、窗多长、重叠率多少这些预处理参数调起来很玄学一不小心就把故障冲击的瞬态成分抹掉了。一维卷积没有这个问题它直接把加速度信号的一串时间序列作为输入卷积核沿着时间轴滑动每一层提取不同尺度的局部波形模式。我选择1DCNN的核心理由是它的归纳偏置和故障信号天然匹配轴承故障在时域上表现为周期性的冲击冲击宽度很窄一维卷积核比如5个采样点正好能覆盖这种短促波形同时参数量比二维CNN少一个数量级训练快部署到采集器上不费力。更重要的是原始时域波形保留了相位信息对冲击发生的确切位置敏感而时频图经过加窗和傅里叶变换后这个位置信息变得模糊。做故障诊断时相位对齐的瞬态冲击恰恰是区分内外圈故障的关键线索丢掉它并不划算。2.2 1DCNN的核心结构卷积、池化、全连接跟我写第一版下面是我在PyTorch里最常用的一版1DCNN结构压缩到只有三个卷积块足够在CWRU轴承数据上拿到接近99%的准确率同时参数量只有几十万CPU都能跑。import torch.nn as nn class DCNN1D(nn.Module): def __init__(self, num_classes4): super().__init__() self.features nn.Sequential( nn.Conv1d(1, 32, kernel_size5, stride2, padding2), # 输入(batch, 1, L)输出通道32 nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(kernel_size2), nn.Conv1d(32, 64, kernel_size3, stride1, padding1), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(kernel_size2), nn.Conv1d(64, 128, kernel_size3, stride1, padding1), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1), # 不管前面长度变多少这里统一压缩成1个时间点 ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))第一个卷积层用stride2配合padding2输出长度变为原来的一半后面两层保持长度不变靠MaxPooling降采样。最后用AdaptiveAvgPool1d(1)替代硬编码的展平好处是输入长度不必固定——1024点也好2048点也好在全局平均池化后都变成128维向量再进全连接层。BatchNorm在这类小数据集里特别重要它能稳住每层输入的分布让模型更容易收敛。如果去掉批归一化同样的样本可能需要多训练两三轮才能达到同等精度。2.3 输入形状与标签设计先想清楚再写代码很多新手在这个节点翻车。1DCNN的输入形状是(batch_size, channels, length)对加速度传感器来说通道数通常是1所以每个样本变成了(1, 1024)这种形状代表一个通道、1024个时间采样点。这个1024不是拍脑袋定的它要覆盖至少一个转轴的旋转周期。比如电机转速1800r/min转频是30Hz对应周期约33毫秒如果采样率是12kHz那么一个周期约400个采样点取1024点能包含两到三个冲击周期模型才能看到完整的故障节拍。标签建议先做粗粒度正常、内圈故障、外圈故障、滚动体故障这是num_classes4。等验证完流程再考虑把损伤直径加进去变成不同尺寸的多个子类。我自己习惯先把类别数控制在4到10之间否则样本太少时模型很容易把同类故障的不同尺寸当成不同类别去硬记泛化很差。CWRU这类公开数据集里每个故障状态还有负载、转速等工况信息这些字段暂时不要并进标签先放在边上后面做跨工况验证用。3. 数据准备与预处理从原始振动信号到训练样本3.1 滑动窗口切分把连续信号变成独立样本原始振动信号都是几分钟的长记录不能整段丢给神经网络。常见做法是像切香肠一样用滑动窗口切成固定长度的样本。窗口长度和步长是两个最要命的参数直接影响样本量和数据泄漏。import numpy as np def sliding_window(signal, window_len1024, stride512): samples [] for start in range(0, len(signal) - window_len 1, stride): samples.append(signal[start:start window_len]) return np.array(samples)这个函数很简单signal是某一通道的全部振动数据window_len是每个样本包含多少点stride是窗口每次滑动的步长。取window_len1024, stride512意味着相邻样本有一半重叠。为什么要重叠因为信号是连续的故障冲击可能出现在任意位置重叠采样能让模型在训练时看到不同相位位置附近的波形等于一种隐式数据增强。重叠率别太高我用过stride128结果训练集和验证集里大量高度相似的样本互相“传染”验证准确率高得吓人一换工况就露馅。一般取窗口的一半作为步长比较稳妥。3.2 从原始数据到PyTorch Dataset归一化与标签映射振动信号的单位是加速度不同采集设备、不同传感器灵敏度导致幅值千差万别。归一化是必须的但我强调一点归一化要在每个样本内部单独做而不是在整个数据集上算全局均值和标准差。在实际部署时你拿到的是一条实时数据流根本不知道未来十分钟的全局方差是多少。from torch.utils.data import Dataset class VibrationDataset(Dataset): def __init__(self, samples, labels, normalizeTrue): self.samples samples.astype(np.float32) self.labels labels.astype(np.int64) self.normalize normalize def __len__(self): return len(self.samples) def __getitem__(self, idx): x self.samples[idx] if self.normalize: mean x.mean() std x.std() 1e-6 x (x - mean) / std return x.reshape(1, -1), self.labels[idx]__getitem__里对每个样本做z-score归一化std加上1e-6是为了防止信号段完全恒定时除零。注意reshape(1, -1)才把一维信号变成(1, length)形状符合1DCNN的输入要求。Dataset写好之后交给PyTorch的DataLoader就能在训练时自动打乱、按batch取数。我还习惯在构造函数里额外传一个transform位置但振动信号别乱加增强加正弦工频干扰反而会让模型学到伪特征。3.3 划分训练集和验证集千万别把同一次故障的样本切进两边这是轴承故障诊断里最容易犯错、代价也最惨痛的一步。CWRU数据集里同一个文件里连续滑动窗口产生的样本高度相关如果简单地把所有样本拼起来再train_test_split那同一个物理时刻的波形会同时出现在训练集和验证集里。模型记住的是这段波形上的具体噪声而不是故障本身验证集分数会虚高十多个点跨负载测试时立刻现原形。我现在的做法是按“文件”而不是按“样本”划分。每一个CWRU文件对应某一种故障在某一负载下的完整记录把属于同一个文件的所有窗口归入同一侧。import numpy as np file_dict { 0: [normal_0.csv, normal_1.csv], # 正常类下的文件列表 1: [inner_0.csv, inner_1.csv], # 内圈故障 2: [outer_0.csv, outer_1.csv], # 外圈故障 3: [ball_0.csv, ball_1.csv] # 滚动体故障 } train_files, val_files {}, {} for label, file_list in file_dict.items(): np.random.shuffle(file_list) split int(len(file_list) * 0.8) train_files[label] file_list[:split] val_files[label] file_list[split:]划分完成后训练和验证各自从对应的文件列表里读数据、滑动切窗再组装成VibrationDataset。这样做可能会牺牲一点验证集样本量但换来的评估结果才是可信的。如果用的是自己采集的数据同样的原则按“工况时间段”划分而不是把每个时间段内切出来的窗口随机混洗。做故障诊断数据泄漏比模型选型错误要命多了。4. 模型训练与评估从loss曲线到混淆矩阵4.1 训练循环损失函数、优化器、调度器怎么配分类任务默认用交叉熵损失。优化器我推荐AdamW而不是裸Adam因为AdamW把权重衰减放在解耦的位置上等价于更干净的L2正则化对提升验证集表现有帮助。初始学习率先给1e-3同时用余弦退让调度器让学习率在训练后段逐渐降下来。import torch import torch.nn as nn from torch.utils.data import DataLoader model DCNN1D(num_classes4) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50) train_loader DataLoader(train_dataset, batch_size128, shuffleTrue) for epoch in range(50): model.train() total_loss 0.0 for xb, yb in train_loader: optimizer.zero_grad() out model(xb) loss criterion(out, yb) loss.backward() optimizer.step() total_loss loss.item() scheduler.step() if (epoch 1) % 10 0: avg_loss total_loss / len(train_loader) print(fEpoch {epoch 1}, loss {avg_loss:.4f})T_max50表示学习率在50轮里从初始值余弦降到接近0。如果你的训练轮数改成100T_max也要同步改成100否则学习率在没有训练完时就已经触底了。batch size取128是因为每个样本只有1024个float32约4KB128个也才512KB显存和内存都轻松。全连接层比较小整个模型参数量只有几十万在小数据集上已经够用。4.2 早停与模型保存别拿最后一轮当最优模型训练50轮后最后一轮的模型不一定是最优的因为学习率降低后模型可能已经在小范围内震荡。我习惯在每轮结束后跑验证集记录验证准确率最高的那一次把它的权重单独存下来。best_val_acc 0.0 for epoch in range(50): model.train() for xb, yb in train_loader: optimizer.zero_grad() loss criterion(model(xb), yb) loss.backward() optimizer.step() model.eval() correct 0 total 0 with torch.no_grad(): for xb, yb in val_loader: out model(xb) pred out.argmax(dim1) correct (pred yb).sum().item() total len(yb) val_acc correct / total if val_acc best_val_acc: best_val_acc val_acc torch.save(model.state_dict(), best_1dcnn.pt) print(fBest val acc: {best_val_acc:.4f})这段代码会在验证集准确率刷新时保存一次模型。PyTorch里model.eval()和with torch.no_grad():两个都要写前者把BatchNorm和Dropout切换到推理模式后者停止构建计算图省内存。加载模型做推理时也要先model.load_state_dict()再model.eval()否则BatchNorm在单条样本上还会算有偏的统计量容易让结果漂移。4.3 混淆矩阵和特征可视化准确率也会骗人只有整体准确率就像只知道体检总分哪个部位出了问题是看不出来的。故障诊断里常出现内外圈故障混淆因为它们的冲击周期很接近。打印混淆矩阵能一眼看穿模型到底卡在哪一对类别上。from sklearn.metrics import confusion_matrix, f1_score model.eval() y_true, y_pred [], [] with torch.no_grad(): for xb, yb in val_loader: out model(xb) y_pred.extend(out.argmax(dim1).numpy()) y_true.extend(yb.numpy()) cm confusion_matrix(y_true, y_pred) print(cm) print(weighted F1:, f1_score(y_true, y_pred, averageweighted))confusion_matrix返回的是二维数组第i行第j列表示真实类别i被预测成类别j的样本数。对角线越亮就越健康如果某两个类别互相错到两位数级别就值得增加窗口长度让模型看到更长的故障冲击序列或者考虑在输入里拼接一个同步采集的转速信号。只看准确率不看混淆矩阵很容易把泛化能力差的模型当宝贝留着。5. 避坑1DCNN轴承故障诊断最易翻车的五个场景5.1 验证准确率99%换一台设备就崩现象用CWRU数据随机切分样本验证集准确率能到99%以上把同一组模型拿到另一负载或现场数据上测试准确率立刻掉到60%上下。原因绝大多数情况下是数据泄漏——同一文件的滑动窗口同时进入了训练集和验证集模型记住了文件里的噪声指纹而不是故障波形的一般特征。另外CWRU数据的某一个负载工况下采集到的信号具有特定的转频和负载噪声模型学到的特征与工况强绑定一换环境就失效。解决按文件或按工况划分数据把训练集和验证集彻底隔离。进一步做跨工况验证比如用0HP负载的数据训练用1HP、2HP负载的数据测试。如果跨工况准确率不高那是正常现象说明模型对工况变化敏感需要在训练时加入更多工况的数据或者做域适应方向的研究。以后看到“随机切分99%”的论文先怀疑一下它有没有做好数据隔离。5.2 Loss变成NaN训练中途崩了现象前几个epoch还挺正常loss在下降某个batch之后突然变成NaN接着后面所有loss都是NaN。原因最常见是学习率过大导致梯度更新越过最优区域进入数值爆炸区另一个常见原因是信号样本里存在标准差为0的常量段虽然归一化时加了1e-6的epsilon但除以一个极小值后产生极大数值激活值溢出。PyTorch默认不检查数值稳定性NaN一旦出现就会污染整个计算图。解决先把学习率降到1e-4再试如果还在NaN就在反向传播之后加梯度裁剪把梯度范数限制到1.0torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)同时在数据预处理阶段过滤掉标准差低于某个阈值比如1e-3的样本。我自己的习惯是每次看到NaN先打印出触发NaN的那个batch样本值看是不是有原始的满量程尖峰如果是就在__getitem__里对该样本做边界截断把所有绝对值超过5倍中位数的点缩回去。5.3 训练集loss下降验证集loss不降过拟合现象训练loss一路下探验证loss在第20轮左右开始掉头上扬验证准确率也停止提高甚至下降。原因模型容量相对训练样本太多了。一个CWRU文件滑动窗口可以切出几千个样本但真正独立的物理片段只有一个文件里的那段时程模型很容易记住这些高度重叠样本里的细枝末节。解决先加正则化。把AdamW的weight_decay从1e-4提到1e-3同时在全连接前插入一个nn.Dropout(p0.5)。早停是最实用的后悔药监控验证loss连续10轮不降就停。如果正则化还不够就要怀疑是窗口重叠率太高把stride调大从512改成1024减少训练样本相关性。最后才是考虑压缩模型宽度把第一层32个卷积核减到24个。按这个顺序排查90%的过拟合都能缓解。5.4 不同采样频率的数据混着用现象CWRU数据里有12kHz和48kHz两种采样频率把它们放在同一个训练集里验证准确率永远卡在80%上下调参也上不去。原因同一个物理波形用12k和48k采样后得到的数字序列差异巨大。卷积核尺寸为5时在12kHz下覆盖约0.4毫秒在48kHz下只覆盖约0.1毫秒感受野对应的物理时间宽度完全不一样模型很难学到一个统一的冲击特征。解决最干净的做法是只用一种采样频率。很多公开论文只使用12kHz的驱动端加速度数据简单省事。如果你手里只有48k数据可以先把采样率降下来或者把窗口点数从1024增加到4096保证窗口覆盖的物理时间长度和12k时差不多。我的血泪经验是把不同采样频率当成两个独立的训练集各训一个模型部署时按设备实际采样率选择对应模型而不是强行混合。5.5 类别不平衡正常样本太多模型学会偷懒现象故障类别有好几种但正常样本占80%以上训练完发现模型把所有样本都预测为正常整体准确率照样很好看。原因交叉熵损失在样本数极度不均时趋向于偏向多数类因为把所有样本都预测为多数类就能把平均loss压得足够低。故障样本本来数量少还被随机采样稀释梯度贡献被淹没。解决用类权重重加权。权重反比于每类样本数计算方式为N_total / (num_classes * N_class)import numpy as np classes np.array([1000, 250, 250, 250]) # 各类样本数 weights classes.sum() / (len(classes) * classes) class_weight torch.tensor(weights, dtypetorch.float32) criterion nn.CrossEntropyLoss(weightclass_weight)这样少数类的错误分类会得到更大惩罚。如果加了类权重还是不行就用WeightedRandomSampler对少数类过采样让每个batch里不同类别的比例更均衡。先重加权再考虑过采样因为重加权不会增加训练轮数实现也简单。6. 不止于跑通把1DCNN用到真实设备前的三个验证习惯模型在CWRU上拿到高准确率只算完成了一半真正上设备之前我自己还坚持做三件小事。第一是拿包络谱去对照模型判断。训练结束后对每个预测为“内圈故障”的测试样本做希尔伯特解调再求包络谱看解调谱里有没有内圈故障特征频率BPFI及其边带。如果模型预测内圈但包络谱上出的是外圈特征频率那就要怀疑模型学到了别的混叠特征。这个方法不需要改网络只需要用scipy.signal.hilbert算一遍包络谱几行代码就能给预测结果增加一个物理验证。第二是t-SNE看特征聚类。把模型全连接层前的128维特征取出来映射到二维平面正常样本和每种故障应该聚成几个紧凑的团。如果某种故障的样本散成两瓣通常意味着该类别内部还有子模式比如故障尺寸不同导致波形差异过大这时候就需要回头检查标签是不是太粗了。第三是导出ONNX测推理速度。PyTorch模型在验证机上跑得快还不够常有中间部署环境没有GPU只能用CPU。torch.onnx.export(model, dummy_input, model.onnx)导出后用onnxruntime跑一下量化观察单次推理耗时。我见过1DCNN在树莓派上单条样本只要1毫秒出头完全够用。这三步做完心里才有底把它接到产线上去。现在每训练完一组模型我的第一反应不再是打印准确率而是按文件划分、跨工况验证、打印混淆矩阵。先把这三个习惯变成强制动作再谈调模型结构。希望这一篇基于1DCNN的轴承故障诊断源码实现能够帮你在自己的数据上少走几趟弯路。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/12 1:34:29

ESP32 MicroPython入门:环境搭建、固件烧录与点灯实战

1. 拿到一块 ESP32 开发板之后,先搞清楚它到底能干什么很多人第一次拿到 ESP32 开发板的时候,心态是既兴奋又迷茫。兴奋的是这块小板子据说能连 WiFi、能跑蓝牙、价格还便宜到离谱;迷茫的是,打开包装盒之后,除了板子本…

2026/10/12 1:34:29

CAPL入门只需掌握三个核心函数:on start、on message与output

1. 为什么CAPL入门只需要盯住三个函数很多人第一次打开CANoe的CAPL浏览器,看到满屏的on start、on message、on timer、on key、on signal,第一反应是"这得学到什么时候"。我当初也一样,翻了两天帮助文档,结果连一个能跑…

2026/10/12 1:34:29

显示器无信号的底层排查逻辑:从EDID到PCIe链路训练

1. 为什么“无信号”不是故障,而是一份待解码的通信日志“显示器显示无信号输出”——这行白底黑字的提示,是过去十年里我拆过最多台主机、重插过最多遍线缆、也最常被误判为“显卡坏了”的现场。它不像蓝屏那样带着错误代码,也不像风扇狂转那…

2026/10/12 3:04:33

线性代数之最小二乘法

最小二乘法,可以先理解成一句话:当一组数据无法被完全满足时,找一个方案,让整体误差尽可能小。我们先看一个小例子,再把它写成线性代数的形式。一、为什么需要最小二乘法? 假设你想估计一个角色的移动速度&…

2026/10/12 3:04:33

C++静态检测实战:从编译警告到Clang-Tidy的系统接入指南

先说个我自己的感受。接手过不少C项目之后,你会发现一个特别矛盾的现象:很多人愿意花大把时间调性能、抠内存、折腾无锁队列,但项目本身的编译警告常年开着默认配置,那些本可以在编译期就被拦下来的bug,偏偏要等上线后…

2026/10/12 3:04:33

Litmus Chaos 实战:node-memory-hog 节点内存耗尽实验全解析

云原生运维可观测性 【免费下载链接】litmus Litmus helps SREs and developers practice chaos engineering in a Cloud-native way. Chaos experiments are published at the ChaosHub (https://hub.litmuschaos.io). Community notes is at https://hackmd.io/a4Zu_sH4TZGei…

2026/10/12 3:04:33

FreeRTOS | HAL_UART_Transmit阻塞问题与解决方法

void Seria2_Printf(char *format, ...) {char String[200];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);HAL_UART_Transmit(&huart2,(uint8_t*)&String, strlen(String), HAL_MAX_DELAY); }在FreeRTOS中使用HAL_UART_Transmit函数时…

2026/10/12 2:59:32

STM32驱动DS1302实时时钟:GPIO模拟时序与寄存器配置详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/12 0:04:22

绝缘子缺陷检测数据集清洗与工业级训练实战指南

简介:本资源是面向电力AI研发人员、工业视觉工程师及智能巡检系统开发者的绝缘子缺陷检测专用YOLO格式数据集,解决无人机航拍场景下绝缘子破损、污闪、积雪等9类典型缺陷的精准识别与定位难题。数据集共2139张真实巡检图像(含训练/验证/测试集…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑