
简介齿轮箱故障数据.zip 面向从事机械故障诊断、信号处理与机器学习的研究生、工程师及算法爱好者提供一套可直接用于实验与建模的齿轮箱运行数据集帮助解决早期故障检测与预防性维护中的样本获取难题。压缩包共15个文件约5.91MB以mat振动信号、png时频图、py分析脚本、csv参数表、txt说明及pdf实验文档为主覆盖正常、断齿、磨损等多种工况便于开展特征工程与模型训练。已有844人学习下载说明其在故障诊断领域具备一定参考热度。读者可借助配套脚本完成振动信号频谱分析、声音与温度趋势观察、扭矩转速波动分析并利用标注数据训练支持向量机、随机森林或LSTM等模型同时通过交叉验证与混淆矩阵评估预测效果为搭建实时监测与预警系统提供完整的数据与代码基础。1. 齿轮箱故障数据.zip一份压缩包背后藏着旋转机械诊断的完整落地链路很多人第一次看到「齿轮箱故障数据.zip」这个文件名会下意识以为它只是一堆振动信号表格解压完就完事。但真正做过旋转机械故障诊断的工程师知道这个压缩包其实是一条完整链路的起点从传感器布点、工况标注、信号切片到特征提取、模型训练、上线推理每一步都决定了最终能不能在产线上跑起来。它解决的核心问题是——让算法工程师在没有真实故障设备的情况下也能拿到带标签、带工况、带转速信息的齿轮箱振动数据把诊断模型从零训到可用。适合谁适合正在做设备预测性维护、想从公开数据集切入但不知道数据怎么用、特征怎么提、模型怎么评的从业者。下面我按自己踩过的路径把这份数据从解压到出结果讲透。2. 先搞清楚齿轮箱故障数据里到底有什么从文件结构到标签体系2.1 解压后先别急着读 CSV先看目录层级和命名规则拿到「齿轮箱故障数据.zip」之后我一般不会直接pd.read_csv而是先tree一遍目录。常见的组织方式是按「工况 / 故障类型 / 测点」三层分文件夹文件名里带转速和负载。比如load_0hp_speed_1730rpm/gear_root_crack/这种路径本身就编码了标签信息。如果你跳过这一步直接合并所有文件后面做交叉验证时会出现同一工况的数据同时出现在训练集和测试集里指标虚高得离谱这就是典型的「数据泄漏」翻车现场。# 先看目录结构不要急着读数据 find ./gear_fault_data -maxdepth 3 -type d | head -50 # 统计每个故障类别下的文件数量判断类别是否均衡 find ./gear_fault_data -name *.csv | awk -F/ {print $(NF-1)} | sort | uniq -c上面第一条命令帮你快速看清层级第二条命令按倒数第二级目录统计文件数。如果某个故障类型只有两三个文件而正常状态有上百个那训练时就必须做重采样或类别加权否则模型会倾向于全部预测成正常。参数上-maxdepth 3是防止目录太深刷屏head -50先看个大概。这一步花五分钟能省掉后面调参时两天的困惑。2.2 振动信号的采样率、通道数和单位决定了你后面能提什么特征齿轮箱振动数据通常包含多个通道输入轴径向、输出轴径向、轴向有的还带麦克风或温度。采样率常见是 12kHz、20kHz 或 48kHz。采样率直接决定你能分析的频率上限——根据奈奎斯特12kHz 采样只能看到 6kHz 以内的成分而齿轮啮合频率往往在几千赫兹边带可能在更远处。如果你要做包络谱分析轴承故障采样率不够就会混叠特征频率对不上模型学到的全是假模式。import scipy.io as sio import numpy as np # 以某通道为例读取一段振动信号 data sio.loadmat(./gear_fault_data/load_0hp/speed_1730/gear_crack/ch1.mat) signal data[vibration].flatten() # 展平成一维 fs 12000 # 采样率必须从说明文件或元数据里确认不能猜 t np.arange(len(signal)) / fs print(f信号长度: {len(signal)} 点, 时长: {t[-1]:.2f} 秒, 采样率: {fs} Hz)这段代码的关键不是读取本身而是fs这个参数。我见过太多人直接拿默认值或者从别人博客里抄一个采样率结果算出来的故障特征频率和理论值差了一大截。正确做法是找数据集自带的说明文件或者看文件名里有没有12k、48k字样。flatten()是为了防止 MATLAB 文件读出来是二维数组后面做 FFT 时维度对不上。时长打印出来是为了确认单段信号够不够做一次有效分析一般至少要有 1 秒以上的稳态数据。2.3 标签体系怎么映射成模型能吃的格式齿轮箱故障数据的标签通常分两大类齿轮故障断齿、裂纹、磨损和轴承故障内圈、外圈、滚动体。有的数据集还会标注故障程度轻微、中度、严重。做分类任务时我一般会建一个映射字典把文件夹名映射成整数标签同时保留原始名称用于可视化。import os from pathlib import Path label_map { healthy: 0, gear_crack: 1, gear_wear: 2, bearing_inner: 3, bearing_outer: 4, bearing_ball: 5 } def build_file_list(root_dir): samples [] for path in Path(root_dir).rglob(*.mat): # 从路径中提取故障类型假设倒数第二级是类别名 category path.parent.name if category in label_map: samples.append((str(path), label_map[category])) return samples file_list build_file_list(./gear_fault_data) print(f共找到 {len(file_list)} 个样本) for cls in set(l for _, l in file_list): print(f类别 {cls}: {sum(1 for _, l in file_list if l cls)} 个)这里用rglob递归查找所有.mat文件path.parent.name取的是文件所在文件夹名。映射字典的键必须和实际文件夹名完全一致大小写敏感。打印每个类别的样本数是为了检查均衡性如果某个类别样本极少后面就要考虑数据增强或者换评价指标比如用宏平均 F1 而不是准确率。这一步的输出直接决定你后面是走普通分类还是异常检测路线。3. 从原始振动到特征向量时域、频域、时频域三条路怎么选3.1 时域特征计算快但容易被工况带偏适合做基线时域特征包括均方根、峰值、峭度、裕度等计算量小适合在边缘设备上做实时粗筛。但它们的绝对值受负载和转速影响很大同一故障在不同工况下可能数值差好几倍。我一般会先算一批时域指标看看类间有没有可分性如果有说明数据质量不错如果完全混在一起就得往频域走。from scipy.stats import kurtosis, skew def time_domain_features(signal): rms np.sqrt(np.mean(signal**2)) peak np.max(np.abs(signal)) kurt kurtosis(signal) skewness skew(signal) crest peak / rms if rms 0 else 0 return { rms: rms, peak: peak, kurtosis: kurt, skewness: skewness, crest_factor: crest } feat time_domain_features(signal) print(feat)rms反映能量kurtosis对冲击成分敏感齿轮断齿或轴承剥落时峭度会明显升高。crest_factor是峰值和有效值的比值对早期故障比 RMS 更灵敏。但注意峭度对转速变化也敏感如果数据集里转速波动大峭度会跟着抖。我一般会把时域特征和转速一起作为输入让模型自己去学工况补偿。3.2 频域特征要先把转速搞清楚否则阶次分析全是错的齿轮箱的频域特征核心是啮合频率及其边带。啮合频率等于轴转频乘以齿数。如果你不知道齿数可以看频谱里能量最高的那个峰反推啮合频率再结合转速算齿数。边带间隔通常等于故障齿轮所在轴的转频这是定位故障齿轮的关键。def freq_domain_features(signal, fs): n len(signal) fft_vals np.fft.rfft(signal * np.hanning(n)) # 加汉宁窗减少泄漏 fft_mag np.abs(fft_vals) / n freqs np.fft.rfftfreq(n, 1/fs) # 找前5个峰值 idx np.argsort(fft_mag)[-5:][::-1] peaks [(freqs[i], fft_mag[i]) for i in idx] return freqs, fft_mag, peaks freqs, mag, peaks freq_domain_features(signal, fs) for f, m in peaks: print(f频率 {f:.1f} Hz, 幅值 {m:.4f})加汉宁窗是为了减少频谱泄漏否则峰值附近会出现虚假的旁瓣干扰边带判断。rfft只算正频率部分因为实信号的负频率是共轭对称的没必要算。找前五个峰值后你要拿这些频率和理论啮合频率、转频去对对上了说明数据没问题对不上就要检查采样率或者转速标注是不是错了。这一步是后面所有频域特征的地基地基歪了模型再复杂也没用。3.3 时频域用 STFT 或小波适合变转速工况但计算量大如果数据集里转速是变化的比如升降速过程纯频域分析会 smear 掉故障特征这时候要用短时傅里叶变换或小波变换。STFT 的窗口长度是核心参数窗口太短频率分辨率不够窗口太长时间分辨率不够。我一般先用 256 或 512 点窗口试看时频图里故障冲击有没有形成清晰的竖条纹。from scipy.signal import stft def time_freq_features(signal, fs, nperseg256): f, t, Zxx stft(signal, fsfs, npersegnperseg, noverlapnperseg//2) mag np.abs(Zxx) # 按频带统计能量比如0-1kHz, 1-2kHz... band_energy [] for i in range(0, len(f)-1, 10): band_energy.append(np.mean(mag[i:i10, :])) return f, t, mag, np.array(band_energy) f, t, mag, band_energy time_freq_features(signal, fs) print(f时频矩阵形状: {mag.shape}, 频带数: {len(band_energy)})nperseg256在 12kHz 采样下对应约 21ms 的时间窗频率分辨率约 47Hz。noverlap设成一半是为了让帧之间平滑过渡。band_energy是把频谱按每 10 个频点一组求平均相当于做了个粗糙的频带能量统计可以直接作为特征喂给分类器。时频特征维度高后面一定要做降维或者用 CNN 自动提特征不然传统分类器会被维度灾难拖垮。4. 把特征喂给模型从随机森林到一维 CNN 的选型与调参4.1 传统机器学习基线随机森林在几百个样本上往往比深度学习稳如果每个故障类别只有几十到几百个样本我第一版一定用随机森林或 SVM 做基线。特征就是前面算的时域加频域统计量维度控制在 20 到 50 之间。这个基线跑出来你才知道数据本身有没有可分性。如果随机森林都做不好上深度学习大概率也是白搭。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score from sklearn.preprocessing import StandardScaler import numpy as np # 假设 X 是特征矩阵y 是标签 X np.array([list(time_domain_features(s).values()) for s, _ in file_list]) y np.array([l for _, l in file_list]) scaler StandardScaler() X_scaled scaler.fit_transform(X) clf RandomForestClassifier(n_estimators200, max_depth10, random_state42) scores cross_val_score(clf, X_scaled, y, cv5, scoringf1_macro) print(f5折交叉验证 F1: {scores.mean():.3f} ± {scores.std():.3f})StandardScaler必须加因为峭度和 RMS 的量纲差好几个数量级树模型虽然对尺度不敏感但 SVM 和后续的神经网络很敏感。n_estimators200是经验值再多提升有限。max_depth10是防止过拟合样本少的时候树太深会记住噪声。cv5做五折交叉验证f1_macro对类别不均衡比准确率更公平。如果 F1 低于 0.6先别调模型回去检查特征和标签有没有对错。4.2 一维 CNN 直接吃原始信号省去手工特征但需要更多数据当样本量上千时我会用一维 CNN 直接对原始振动信号做端到端分类。输入是固定长度的信号段比如 2048 点。网络结构一般三到四层卷积加池化最后全局平均池化接全连接。这样省去了手工设计特征的麻烦但需要的数据量也更大而且对信号对齐很敏感。import torch import torch.nn as nn class GearCNN(nn.Module): def __init__(self, num_classes6): super().__init__() self.conv1 nn.Conv1d(1, 16, kernel_size15, stride2, padding7) self.conv2 nn.Conv1d(16, 32, kernel_size9, stride2, padding4) self.conv3 nn.Conv1d(32, 64, kernel_size5, stride2, padding2) self.pool nn.AdaptiveAvgPool1d(1) self.fc nn.Linear(64, num_classes) self.relu nn.ReLU() self.bn1 nn.BatchNorm1d(16) self.bn2 nn.BatchNorm1d(32) self.bn3 nn.BatchNorm1d(64) def forward(self, x): x self.relu(self.bn1(self.conv1(x))) x self.relu(self.bn2(self.conv2(x))) x self.relu(self.bn3(self.conv3(x))) x self.pool(x).squeeze(-1) return self.fc(x) model GearCNN() print(model)第一层卷积核设 15是为了覆盖低频冲击的持续时间后面逐层减小核尺寸让网络从局部波形逐渐抽象到全局模式。stride2是下采样减少计算量。BatchNorm放在卷积和激活之间能加速收敛。AdaptiveAvgPool1d(1)把任意长度的特征图压成一个值这样输入信号长度可以灵活调整。训练时学习率一般从 1e-3 开始用 Adam 优化器batch size 设 32 或 64。如果验证集损失震荡先把学习率降到 1e-4。4.3 迁移学习把在仿真数据上预训练的模型搬到真实数据上真实故障数据往往很少但仿真数据可以无限生成。我一般会先在仿真数据上训一个 CNN然后把卷积层冻结只微调全连接层。这样在小样本上也能拿到不错的精度。关键是仿真数据的工况分布要尽量覆盖真实场景否则迁移效果会大打折扣。# 假设 pretrained_model 是在仿真数据上训好的 pretrained_model GearCNN() # 冻结卷积层 for param in pretrained_model.conv1.parameters(): param.requires_grad False for param in pretrained_model.conv2.parameters(): param.requires_grad False # 只训练全连接层 optimizer torch.optim.Adam(filter(lambda p: p.requires_grad, pretrained_model.parameters()), lr1e-4)冻结卷积层是因为底层特征边缘、冲击在仿真和真实之间差异不大而高层语义差异大需要重新学。学习率设 1e-4 比从头训练小一个数量级防止把预训练权重冲掉。如果微调后效果还是差可以解冻最后一层卷积一起训但学习率要再降。这个策略在样本少于 500 时特别有用但前提是仿真数据不能太假否则就是负迁移。5. 避坑与排查齿轮箱故障诊断里最容易翻车的五个地方5.1 现象交叉验证 F1 很高上线后一塌糊涂原因数据泄漏。同一段信号被切片后不同片段分到了训练集和测试集模型其实在背答案。解决按「文件」或「工况」划分训练测试集而不是按切片。用GroupKFold按工况分组确保同一工况不出现在两边。5.2 现象模型把正常样本全预测成故障原因类别极度不均衡正常样本占 90% 以上。解决训练时给少数类加权或者用 focal loss。评价指标换成宏平均 F1 或 AUC别再看准确率。5.3 现象频域峰值对不上理论啮合频率原因采样率标错或者转速单位是 rpm 而代码里当成了 Hz。解决统一单位转速除以 60 才是转频。检查数据集说明文件别信文件名里的数字。5.4 现象CNN 训练损失不下降原因输入信号没有归一化或者标签编码从 1 开始而 PyTorch 要求从 0 开始。解决对每个信号做 z-score 标准化标签统一减最小值。检查CrossEntropyLoss的输入范围。5.5 现象时频图里故障冲击被噪声淹没原因信噪比太低或者 STFT 窗口选得不对。解决先做带通滤波保留啮合频率附近的频带。窗口长度根据冲击持续时间调一般取冲击周期的 2 到 3 倍。6. 进阶技巧用包络谱和阶次分析把早期故障从噪声里抠出来前面讲的时域和频域特征对中度以上故障很有效但早期故障的冲击能量很弱直接看频谱往往被啮合频率的边带和噪声盖住。我后来习惯先做包络谱对信号做带通滤波只保留轴承或齿轮共振频带然后做希尔伯特变换取包络再对包络做 FFT。这样故障特征频率会以清晰的峰值出现在低频段比原始频谱好认得多。from scipy.signal import hilbert, butter, filtfilt def envelope_spectrum(signal, fs, band(2000, 4000)): # 带通滤波保留共振频带 b, a butter(4, [band[0]/(fs/2), band[1]/(fs/2)], btypeband) filtered filtfilt(b, a, signal) # 希尔伯特变换取包络 envelope np.abs(hilbert(filtered)) # 对包络做FFT n len(envelope) env_fft np.abs(np.fft.rfft(envelope - np.mean(envelope))) / n freqs np.fft.rfftfreq(n, 1/fs) return freqs, env_fft freqs, env_spec envelope_spectrum(signal, fs) # 找包络谱里前三个峰值 idx np.argsort(env_spec)[-3:][::-1] for i in idx: print(f包络谱峰值: {freqs[i]:.1f} Hz)带通范围(2000, 4000)不是随便写的要先看原始频谱里共振峰在哪个频段。butter(4, ...)是四阶巴特沃斯滤波器filtfilt做零相位滤波避免相位失真。希尔伯特变换后取模得到包络减均值是为了去掉直流分量。包络谱里的峰值如果和轴承内圈、外圈、滚动体的理论故障频率对上基本就能确诊。这个方法的坑在于带通范围选错如果共振频带没覆盖到包络谱里什么也看不到。我一般会先画原始频谱手动找共振峰再定带通。另一个进阶方向是阶次分析。变转速工况下频率会随时间漂移但阶次频率除以转频是固定的。做阶次分析需要转速信号如果数据集没给可以从振动信号里用瞬时频率估计反推。算出阶次谱后齿轮故障的阶次成分会非常稳定不受转速波动影响。我一般会把阶次谱的前几十个阶次能量作为特征喂给分类器效果比直接用频谱好一截。最后说一个我自己的习惯每次拿到新的齿轮箱数据先花半天时间做可视化把每个类别的时域波形、频谱、包络谱各画几张图肉眼看看有没有明显差异。如果肉眼看不出区别就别指望模型能学出来。这个习惯帮我省掉了无数次无效调参。希望帮到你。本文还有配套的精品资源点击获取