简介面向齿轮箱故障诊断与预测性维护场景的开放数据集共十五个文件、约5.91MB适合设备健康管理、机器学习建模和故障特征提取方向的研究者与工程师使用。包内以mat格式保存振动原始数据涵盖正常、单齿崩齿、三齿磨损三种典型工况配套png频谱图/时域图、Python分析脚本、csv参数表、txt数据解读和PDF文献形成从原始信号、特征工程到模型验证的完整链条。csv参数表记录了转速1420rpm、主动轴15齿、从动轮110齿等关键工况信息PDF文献可帮助理解实验台架与采集方案。已有843人浏览学习可直接用于训练SVM、随机森林、LSTM等故障预测模型结合傅里叶变换、小波分析以及温度、扭矩等监测思路进行异常检测、剩余寿命评估对工业现场降低设备停机成本具有实用价值。无论是学术研究还是工程验证均能提供可复现的故障诊断实验基准。1. 齿轮箱故障数据.zip一份数据包背后要解决什么一个名为“齿轮箱故障数据.zip”的文件在设备预测性维护项目里几乎就是“全部家当”。它通常装着齿轮箱在健康与不同失效状态下的振动信号、转速/负载工况和故障标签目标是支撑起一套滚动的故障诊断模型——你拿它训练分类器去识别齿面点蚀、断齿、轴承剥落这些典型失效。适合谁读刚接手振动数据建模的工程师或者正要把自己采集的数据整理成可交付数据集的从业者。我的第一建议可能反直觉拿到zip先别急着解压先问清楚包里的信号是从哪台实验台来的、采样频率是多少、工况是否被记录。否则后面所有特征工程和模型精度都会建立在一个黑匣子上面。2. 打开之前先看包结构故障数据该有哪些东西2.1 一份可复用的故障数据包必须包括的五个部分很多“齿轮箱故障数据.zip”看起来文件不少但真正要建模时会发现少这少那。我整理数据包时的底线是五个部分都要有原始振动信号、工况参数、故障标签、元数据、数据说明。原始振动信号是核心资产不能只留特征或频谱否则后续想换一种采样方式或重做预处理就只能重做实验。工况参数至少包括输入轴转速和负载齿轮箱振动对转速和负载极其敏感缺失工况的数据只能做定性分析没法做跨工况模型。故障标签要和文件名、信号一一对应常见标签有健康、齿面磨损、齿根裂纹、断齿、轴承外圈损伤、内圈损伤等标签命名要统一别一会儿用“fault1”一会儿用“broken”。元数据是采样率和传感器量程这个最关键没有采样率你连FFT横轴都画不出来。数据说明可以是一个README记录台架布置、测点位置、采集仪型号、滤波设置这些信息在模型部署阶段比模型本身还要宝贵。为了直观我会在解压后先看一眼目录。下面是一个常见做法下的目录结构用来存放同一批实验台在多种转速负载下的信号gearbox_fault_dataset/ ├── README.md ├── metadata.json ├── raw/ │ ├── healthy_0_1800rpm_0Nm.csv │ ├── tooth_chipped_1_1800rpm_0Nm.csv │ ├── tooth_broken_2_1800rpm_0Nm.csv │ ├── bearing_outer_3_1800rpm_0Nm.csv │ └── bearing_inner_4_1800rpm_0Nm.csv ├── processed/ │ └── features.parquet └── labels.csv这里的命名规则是“故障类型_样本编号_转速_负载”一眼就能看出实验条件比“data1.csv”这种命名有效得多。raw目录里放原始加速度波形processed放之后计算的特征表labels.csv单独维护每个文件对应的故障标签。metadata.json集中放采样率和台架参数让目录干干净净。这种结构在多人协作和后续扩展时特别舒服不用每次靠猜。2.2 命名与工况组织按故障类型嵌套尽量少用模糊名命名控制是数据包治理的第一件事。我见过太多zip包解压后是“新建文件夹/data/2019-05-06/001.txt”等你一个月后再取数据已经完全想不起001对应的是断齿还是健康的工况。所以命名要带语义但也不能带中文或特殊空格因为在跨平台解压时很容易出现乱码。按故障类型嵌套目录是一个稳妥方案顶层分healthy、cracked_tooth、broken_gear、bearing_outer等每个故障目录内再按转速负载分子目录比如1800rpm_10Nm。这样做的好处是后续写数据加载脚本时可以直接从路径中解析标签和工况减少人工标注的错误。另外metadata.json里面要写清楚传感器的安装位置、方向、单位。常见测点是齿轮箱壳体垂直方向和水平方向同一个故障在两个方向的能量分布完全不同。如果采集时用了抗混叠滤波器也在元数据里写明白很多实验台采集卡默认做了滤波但使用者并不知道导致特征频率范围被人为截断。metadata内容可以做成这样方便程序一次性读取{ sampling_rate_hz: 25600, sensor_type: piezoelectric_accelerometer, sensor_unit: m/s^2, position: gearbox_housing_vertical, shaft_config: { input_shaft_rpm: [900, 1200, 1800], load_nm: [0, 10, 20] }, fault_labels: [ healthy, tooth_chipped, tooth_broken, bearing_outer, bearing_inner ], acquisition: { anti_alias_filter_hz: 10000, signal_length_s: 10, adc_bits: 24 } }这里面每个字段都有实际用途。比如信号长度10秒采样率25600Hz每个文件就是256000点约4MB浮点数组这个长度足够覆盖多次齿轮转周期和故障冲击的重复事件ADC位数决定了信号最低可分辨的幅值抗混叠截止频率10kHz说明后面的频谱分析最高不要超过10kHz。有了这些信息你才能放心地写程序做批量读取。3. 用Python正确读zip从完整性校验到信号切割3.1 完整性与EOCD先修包再谈数据把zip文件当作普通文件直接解压是很多人的第一反应但恰恰在这步会翻车。压缩包在传输过程中可能缺失结尾的EOCDEnd of Central Directory表现为解压时直接报错“could not find end of central directory record”找不到中央目录结尾。还存在一种伪加密情况文件头把加密标志位置1但内部数据流实际没有加密强行要求密码导致后续处理中断。为了不让数据读取被这些琐碎问题卡住我习惯先用Python把zip的完整性和成员列表检查一遍代码加注释之后可以直接抄import zipfile ZIP_PATH gearbox_fault_data.zip # 先用快速判断确认是不是有效zip避免乱传文件浪费后面时间 if not zipfile.is_zipfile(ZIP_PATH): raise RuntimeError(文件不是标准zip格式) with zipfile.ZipFile(ZIP_PATH) as zf: # testzip()会逐个文件校验CRC返回第一个损坏文件名 bad_file zf.testzip() if bad_file is not None: raise RuntimeError(fzip内文件校验失败: {bad_file}) # 打印成员列表确认目录结构符合预期 names zf.namelist() print(f共 {len(names)} 个文件) for name in names[:20]: print(name)代码逻辑分三步先is_zipfile做快速甄别再用testzip校验每个文件的CRC最后通过namelist确认包内路径。testzip返回None表示全部完整返回字符串则是第一个出问题的成员名。这一步看起来简单实际能避开后续大量莫名其妙的解析错误。如果testzip检查不过或者解压时提示找不到EOCD常见做法是先用zip的修复工具重建索引再重新验证zip -FF corrupted_input.zip --out repaired_output.zip unzip -t repaired_output.zip第一行会在文件中搜索EOCD记录并尝试重建中央目录第二行检查修复结果。注意zip -FF对完全截断的文件不一定有效它依赖比较完整的文件尾部。我在实际项目里遇到过下载了一半的zip包修复后虽然能列出文件但中间某个大文件CRC校验还是失败那种情况只能回到源头重新获取不要硬扛。处理完完整性接下来是伪加密。zipfile在遇到加密位时会要求密码但有些打包工具生成的zip只设置了bit 0明文标志却没有真正加密。这种情况下尝试用空密码bytes去读取往往反而能成功with zipfile.ZipFile(ZIP_PATH) as zf: sample_name names[1] # 文件列表中任取一个 info zf.getinfo(sample_name) try: data zf.read(info) except RuntimeError as exc: if encrypted in str(exc): # 伪加密套路头标记与数据流不一致用空密码绕过标志位 data zf.read(info, pwdb)用pwdb的意思是告诉zipfile“我有密码密码是空”从而跳过它读取数据。如果数据真加密空密码会继续报错那就说明打包者确实设置了密码。很多“zip密码移除”工具本质也是在改这个标志位并不适合未知密码的暴力破解场景合法使用场景应该是数据提供方明确告诉你密码但系统属性错乱。我自己遇到伪加密的频次不高但一遇到就是当天最大的阻碍所以值得写进读取流程里。3.2 读取信号文件按块读、滑动窗口切样本zip验证通过后下一步是把csv或mat文件中的波形读成可计算的数组。齿轮箱振动信号往往很大10秒25600Hz的加速度数据是25.6万点多个工况叠加后内存占用轻松上GB。我一般不会一次性把所有文件全部加载到内存而是先读取一个文件的元信息和长度再用滑动窗口切样本边切边决定是否保留。下面这段代码把“按文件读取滑窗切分”做成一个小模块import os import numpy as np import pandas as pd def load_signal_from_csv(path): # 假设CSV有两列时间戳、加速度也可能只有一列直接是波形 df pd.read_csv(path, headerNone) if df.shape[1] 2: return df.iloc[:, 1].to_numpy(dtypenp.float64) return df.iloc[:, 0].to_numpy(dtypenp.float64) def make_windows(signal, window_points1024, stride_points512): total len(signal) if stride_points 0: raise ValueError(stride_points必须大于0) if window_points total: raise ValueError(窗口长度超过信号长度) windows [] # 从0开始以stride递增留下尾部不足一个窗口的数据 for start in range(0, total - window_points 1, stride_points): windows.append(signal[start:start window_points]) return np.asarray(windows)这里window_points是窗口内的样本点数stride_points是相邻窗口的起点间距。比如25600Hz采样率下一个窗口1024点等于40毫秒能够覆盖齿轮轴一个完整旋转周期的一小部分如果想覆盖整转甚至多转窗口就要扩大到2048或4096。stride决定重叠率1024窗口配512步长重叠50%重叠越高训练样本数越多但相邻样本之间相似度越高后面如果不按信号片段划分数据集就会引出一节要说的“数据泄漏”问题所以窗口和步长不是越大越好要配套存好每个窗口在原始信号中的起始位置方便追溯。读取并切出窗口后我的习惯是每个窗口的样本做一个轻量的“是否有效”判断比如方差是不是接近零或者是常数。齿轮箱不会输出完全平的信号出现平信号大概率是传感器断线或采集卡丢数直接丢掉这一窗口比留着干扰模型更合适。写一个简化版判断根据自己的采样率调整阈值def drop_flat_windows(windows, min_variance1e-6): valid [] for w in windows: if np.var(w) min_variance: valid.append(w) return np.asarray(valid)参数min_variance建议用标定过的量程估算一般加速度信号有振动时方差远大于1e-6如果设置为0.0001也合理。关键是不要用零均值之类的固定假设因为有些低转速工况的振动幅值本来就很低一刀切会误删。4. 从原始信号到故障特征时域频域参数怎么选4.1 时域统计量峭度能反应什么不能反应什么把原始波形直接怼进神经网络不是不能做但可解释性和训练稳定性都差工业场景我更倾向先压缩成有物理意义的特征。时域统计量是第一步典型指标包括均方根值RMS、峭度、波形因子和峰值因子。峭度是一个很敏感的高斯分布偏离指标越是冲击型故障越会拉出大量尖峰峭度值升得很明显。但它只能告诉你有冲击不能告诉你冲击来自哪个部位——齿面点蚀和轴承外圈剥落都会产生振动冲击峭度相同也可能对应完全不同的故障齿轮。所以严格做法是把时域统计量作为辅助特征配合频域分析来定位。举个例子一个断齿故障齿轮每转一圈都会撞击一次RPM1800时轴频为30Hz那么冲击周期约为33毫秒而轴承外圈剥落冲击频率可能是轴频的若干倍。如果只看峭度两个信号都可能超过5但分类完全无法区分。所以我在做故障诊断时会给每个窗口计算一组时域参数记录成一个向量但最终不单独依赖它们。计算这些统计量的代码非常标准注意标定好自己的单位def time_domain_features(x): rms_val np.sqrt(np.mean(x**2)) peak_val np.max(np.abs(x)) mean_val np.mean(x) variance_val np.var(x) std_val np.std(x) # 峭度四阶矩/方差平方减3是让健康信号接近0 kurt_val np.mean((x - mean_val)**4) / (std_val**4 1e-12) peak_factor peak_val / (rms_val 1e-12) waveform_factor rms_val / (np.mean(np.abs(x)) 1e-12) return np.array([rms_val, peak_val, variance_val, kurt_val, peak_factor, waveform_factor])计算里加1e-12是为了防止信号值全为0时除零。峭度减3是让标准正态分布峭度为0方便对比有些行业习惯不减3会直接叫“峭度系数”记录时要写清楚否则不同同事间的特征数据会差一个常数。4.2 频域包络谱把故障特征频率从调制中解出来齿轮箱的故障信号常常是调制的齿轮啮合产生高频载波故障冲击在载波上留下幅值调制边带。直接对原始信号做FFT你能看到啮合频率和边带但边带幅度小很难识别因此我会先对带通滤波后的信号做Hilbert变换得到包络再对包络做FFT得到的是故障冲击的包络谱特征频率就非常突出。这个解调流程是齿轮箱故障诊断里最经典也最实用的做法。代码如下from scipy.signal import hilbert, butter, filtfilt from numpy.fft import rfft, rfftfreq def envelope_spectrum(x, fs, band_low, band_high): b, a butter(4, [band_low, band_high], fsfs, btypeband) filtered filtfilt(b, a, x) # Hilbert得到解析信号模长就是包络 env np.abs(hilbert(filtered)) # 去直流减小升降趋势干扰 env env - np.mean(env) spec np.abs(rfft(env)) freqs rfftfreq(len(env), 1/fs) return freqs, spec带通滤波器选择是这里的第一个玄学。band_low通常设在500到1000Hz避开低频基座晃动和电机50Hz干扰band_high要低于采样率一半并在抗混叠截止频率以内例如10kHz。你可以在包络谱上找齿轮啮合频率GMF乘以转频后的边带也可以找轴承特征频率BPFO、BPFI。这些频率先用转速算出来再回包络谱上核对。比如一个齿数为32的齿轮在1800rpm下啮合频率是32×30960Hz如果你的观测边带偏移正好等于30Hz就能初步判断是轴频相关的故障。这种先验频率核对远比拿到谱图“看峰值”可靠。4.3 把特征写成结构化数组标签与样本对齐特征计算完面临最后一步如何把特征和对应的故障标签、工况、所属文件编号保存下来。这里最容易出现标签错位常见翻车是用Python内置zip()直接同时迭代特征数组和类别列表但某个文件没读出来导致样本数和标签数对不上。为了避免这个问题我在保存特征时会把“来源文件”“窗口起始位置”“故障标签”“转速负载”都放在同一个结构化记录里这样后面无论怎么划分训练集验证集都不会破坏样本身份。保存成npz的示意代码如下def save_features(feature_matrix, labels, win_starts, filenames, out_path): np.savez_compressed( out_path, featuresfeature_matrix, labelsnp.asarray(labels), win_startnp.asarray(win_starts), source_filenp.asarray(filenames) )npz是numpy自带格式压缩率不错如果不喜欢npz也可以用parquet。关键是source_file和win_start这两个字段别忽略。它们能在模型表现异常时帮你定位到一个具体信号片段也是后面做分组交叉验证的基础。保存之后最好写一个反向读取并打印形状的验证脚本确保features的行数等于labels的长度。这一步很琐碎但不做的话后面所有统计脚本都处于“大概对”的状态。5. 故障诊断落地中的常见坑从样本不均衡到数据泄漏5.1 重叠窗口随机划分导致“数据泄漏”测试集虚高现象训练集和测试集用同一个zip里的窗口随机划分模型在测试集上准确率95%以上可到了现场另一台齿轮箱上准确率掉到50%像是换了个世界。原因我们在滑窗时如果stride小于窗口长度相邻窗口大量重叠同一个连续信号片段会同时出现在训练集和测试集中。模型相当于在考试时见过题目原题考试分数当然好。齿轮箱信号的连续性好相邻窗口在波形和特征上高度相似这种泄漏比想象中更严重。解决划分数据集时按“信号文件”或“连续采集段”分组而不是按窗口。sklearn里提供GroupShuffleSplit分之前把source_file字段当作组别传入from sklearn.model_selection import GroupShuffleSplit # groups数组长度等于样本数内容为该样本属于哪个文件 groups np.asarray(source_files) gss GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, test_idx next(gss.split(features, labels, groups))这样划分后同一个原始信号文件的窗口不会横跨训练和测试集。如果想更严格可以按设备或实验批次分组但至少不要让文件内窗口泄漏。现场部署时如果有多台齿轮箱数据建议按设备分组验证得到的准确率才接近真实能力。5.2 类别不平衡让少数故障被忽略现象故障数据zip里健康样本占70%断齿样本只占2%。模型训练完断齿这一类F1分数很低或者模型把几乎全部样本都判成健康整体准确率却很高。原因分类器倾向于多数类因为把少数类错判为多数类不会提高损失函数的惩罚。故障诊断场景中最危险的就是把严重故障漏判成健康这类代价远高于误报。解决第一优先是不要做普通过采样复制少数类复制只是让模型背下来不能提升泛化。时域里做轻量增强可以考虑对少数类原窗口做小幅平移、白噪声扰动样本总量控制在一个合理范围或者使用带类别权重的分类器比如在RandomForest里设置class_weightbalanced。下面是一个在训练前检查类别分布的常规做法from collections import Counter label_counts Counter(labels) total len(labels) for label, cnt in label_counts.items(): print(f{label}: {cnt} ({cnt / total:.2%}))如果发现少数类只有几十个样本即使加权模型也学不到稳定的规律。更务实的做法是先做异常检测二分类——把少数故障和所有其他状态区分而不是一开始就做多分类。诊断边界放得越细对样本量的要求越高。等你积累了更多数据再扩大分类标签这比硬撑多分类模型更可控。5.3 zip解压遇到“could not find eocd”或“伪加密”现象解压时提示“could not find end of central directory record”或“file is encrypted, but password not given”但当前文件明明没有密码保护。原因前者是zip的EOCD索引丢失或文件不完整后者是zip头部的加密标志位与真实内容不一致属于伪加密。热词“zip密码移除”常出现在这类坑里很多人去搜加密工具实际上问题根本不在密码而在文件格式标志错乱。解决先按第3章的zipfile.testzip检查完整成员确定具体是哪个文件损坏。再用zip -FF修复EOCD最后用伪加密方案空密码绕过。如果修复后仍打不开关键文件不要在这个包上反复消耗时间回到来源重新下载或让数据提供方重新打包这是最有效率的止损方式。5.4 转速与采样率没对齐故障频率永远算不准现象你在包络谱上看到一个峰值按轴承特征频率公式怎么都对应不上换了个转速文件同样的峰值又跑到了另一个位置。原因采集振动信号时没有同步记录转速或者记录的转速表是实验前的目标转速不是实时值。齿轮箱带负载后会有转差实际轴频和目标设定可能有差异差别超过1%在长时FFT中就会造成特征频率漂移。解决在设计数据包时尽量记录转速通道哪怕只是一路键相脉冲也比没有强。如果历史数据里没有转速通道我会手动从振动信号中提取“轴转频”找频谱里接近理论轴频的最大峰值然后用这个轴频去计算啮合频率和轴承特征频率。提取轴频要注意谱线分辨率如果窗口只有0.1秒频率分辨率只有10Hz转速3500rpm都标不精确把窗口加长到1秒以上频率分辨率到1Hz才比较可靠。特征频率算不准的时候先怀疑转速再怀疑滤波器最后才怀疑算法。6. 让数据集更值钱验证集设计与小样本迁移技巧一套齿轮箱故障诊断数据能不能被长期复用取决于验证方案是否真正反映了现场泛化能力。我的习惯是除了按文件分组还要做“跨工况验证”用900rpm训练用1800rpm测试。这个验证结果才是你部署到新转速时的心理底线。如果跨工况掉点严重就先做特征缩放或者域对齐而不是急着改网络结构。小样本迁移上一个轻量但实用的技巧是从源域特征统计迁移把健康样本的均值和标准差对齐到目标域再做线性变换。下面的代码表达这个思路输入是源域特征矩阵X_src和目标域特征矩阵X_tgt输出对齐后的源域特征def coral_features(X_src, X_tgt): mu_s X_src.mean(axis0) std_s X_src.std(axis0) 1e-8 mu_t X_tgt.mean(axis0) std_t X_tgt.std(axis0) 1e-8 return (X_src - mu_s) / std_s * std_t mu_t这个CORAL变换不要求你有任何目标域标签只要目标域能采集到少量无标注振动信号就能把特征分布拉近。在实际项目中这个方法比直接微调深度学习模型简单得多也更容易向现场同事解释相当于把源域的“说话口音”标准化成目标域的口音再让分类器去识别。然后再用目标域里极少量有标签样本做一次逻辑回归或随机森林微调往往能补回跨工况的大部分掉点。我踩过最重的一次坑是把重叠窗口随机划分当成了默认操作测试集准确率98%现场demo却完全翻车。复盘时才发现数据的source_file字段没有保留无法做分组验证整个训练流程都得重跑一遍。从那以后我把“必须能解释每个样本来自哪段原始信号”写进了数据处理规范里。数据泄漏这类问题靠理论很难察觉但通过在验证集上记录清楚来源就能提前堵住。如果你手头也有一份“齿轮箱故障数据.zip”建议今天先别急着训模型先花半小时做三件事确认EOCD完整、检查每个文件是否有转速记录、按文件分组做一次基线验证。这三步做完数据包到底能不能用你的心里就有数了。希望帮到你。本文还有配套的精品资源点击获取