简介:这份数据集面向从事电机故障诊断的工程师、设备维护人员及高校研究人员,系统整理了三相异步故障电机的典型频谱特征,帮助读者快速对照振动信号判断故障类型。内容涵盖转子偏心、轴弯曲、转子条断裂、电机缺相、轴承内外圈故障、匝间短路与转子不平衡七类常见故障,逐项给出特征频率、边带规律与诊断建议,可直接用于现场排查与教学参考。资源包共1个docx文件,约608KB,以图文表格形式汇总各类故障的频谱图与备注说明,便于打印或随查随用。目前已有947人学习下载,适合需要建立故障频谱对照体系、提升诊断效率的初、中级技术人员,也可作为开发智能诊断系统的数据参考。
1. 故障电机典型频谱特征整理数据集:从振动信号到可训练样本的完整路径
一台 7.5kW 异步电机在试验台上出现异常振动,加速度传感器采到的时域波形看不出明显问题,但 FFT 之后在 157Hz 附近出现一根幅值不高却持续存在的谱线——这个频率恰好接近转频的 3 倍。如果你手头有一份整理好的故障电机典型频谱特征数据集,这类判断不需要靠直觉,直接做谱峰匹配就能给出候选故障类型。这正是本文要讲的事:把不同故障模式下电机振动的频谱特征提取出来,整理成结构化、可检索、可训练的数据集。它面向的是做设备状态监测、预测性维护、故障诊断的工程师,以及需要频谱样本训练分类模型的研究者。数据集的核心价值不在于数据量多大,而在于每一类故障的频谱特征是否标注准确、物理含义是否清晰、不同工况下是否可复现。
2. 故障电机的频谱特征到底长什么样:从物理机理到可量化指标
2.1 四类典型故障的频谱指纹
电机故障诊断做了这么多年,真正在工业现场高频出现的故障类型其实就那么几类。轴承故障、转子断条、气隙偏心、定子绕组短路,这四类覆盖了绝大多数非电源侧异常。每一类在频谱上都有相对固定的表现模式,这也是构建数据集时分类标签的物理依据。
轴承外圈故障的特征频率由轴承几何参数和转速共同决定,计算公式为 BPFO = (n/2) × fr × (1 - (d/D)cosθ),其中 n 是滚动体数量,fr 是转频,d 是滚动体直径,D 是节径,θ 是接触角。内圈故障频率 BPFI 则在括号内用加号。这些频率通常在 1kHz 到 5kHz 范围内出现,且往往伴随边频带。转子断条的特征更直接:在供电频率 f 两侧出现 (1±2s)f 的边频,s 是转差率。气隙偏心会在转频 fr 及其谐波处产生异常,严重时出现 2fr 的明显峰值。定子绕组短路则在频谱中出现 2f 附近的边频分量。
把这些公式和对应的谱峰位置整理成表,就是数据集的第一层结构:
| 故障类型 | 特征频率表达式 | 典型频段 | 边频特征 |
|---|---|---|---|
| 轴承外圈 | BPFO = (n/2)·fr·(1-(d/D)cosθ) | 1k–5kHz | 以 BPFO 为中心,间隔 fr |
| 轴承内圈 | BPFI = (n/2)·fr·(1+(d/D)cosθ) | 1k–5kHz | 以 BPFI 为中心,间隔 fr |
| 转子断条 | (1±2s)·f | 电源频率附近 | 边频间隔 2sf |
| 气隙偏心 | fr, 2fr, 3fr | 低频段 | 谐波幅值递增或递减 |
| 定子短路 | 2f ± 2sf | 2 倍电源频率附近 | 边频间隔 2sf |
这张表不是凭空写的,每一行都对应一个可验证的物理关系。构建数据集时,标签体系就按这个表来设计,确保每个样本的标注有据可依。
2.2 从原始振动信号到频谱样本的转换链路
有了物理依据,接下来要解决的是数据形态问题。原始数据是加速度传感器采到的时域波形,采样率通常 10kHz 以上,单条记录可能几十万点。直接拿时域波形做训练不是不行,但频谱特征更稳定、更可解释,而且数据量能压缩一到两个数量级。
转换链路分四步:去趋势、加窗、FFT、谱峰提取。去趋势是为了消除传感器温漂带来的基线偏移,用多项式拟合减去慢变分量即可。加窗用 Hanning 窗,目的是减少频谱泄漏——电机振动信号里转频及其谐波能量集中,不加窗的话谱峰两侧会出现虚假的旁瓣。FFT 点数一般取 4096 或 8192,频率分辨率 Δf = fs/N,10kHz 采样配 8192 点就是 1.22Hz,足够分辨转频附近的边频。
谱峰提取是数据集构建的关键步骤。不是把所有频率点都存下来,而是只保留超过局部背景噪声一定倍数的峰。具体做法是:先估计噪声基底,用滑动中位数滤波得到一条平滑的噪声曲线,然后找那些幅值超过噪声基底 3 倍以上的局部极大值。每个峰记录三个值:频率、幅值、相对噪声的信噪比。这样一条原始波形最终变成几十到几百个谱峰记录,数据量可控,物理含义清晰。
2.3 数据集的结构设计与字段定义
数据集的结构直接决定了后续能不能方便地做检索和训练。我一般会设计成三层:样本层、谱峰层、标签层。样本层记录每条原始数据的元信息,谱峰层存提取出来的峰值列表,标签层放故障类型和工况参数。
样本层字段包括:样本 ID、电机型号代号、额定功率、额定转速、采样率、采样点数、负载率、供电频率。这些字段看起来琐碎,但做跨工况对比时缺一不可。比如同样标注为轴承外圈故障,50% 负载和 100% 负载下的谱峰幅值可能差一倍,没有负载率字段就没法做归一化。
谱峰层用 JSON 数组存储,每个元素包含频率、幅值、信噪比三个字段。用 JSON 而不是 CSV 是因为每条样本的谱峰数量不固定,JSON 的嵌套结构更自然。如果后续要转成 CSV 做批量训练,写个脚本展平即可。
标签层是核心。故障类型用枚举值,比如 0 表示正常、1 表示轴承外圈、2 表示轴承内圈、3 表示转子断条、4 表示气隙偏心、5 表示定子短路。严重程度分三级:早期、中期、晚期。早期故障的谱峰幅值通常只比正常状态高 20%–50%,晚期可能高 3–5 倍。这个分级不是拍脑袋定的,而是根据谱峰信噪比和边频数量综合判断。
注意:标签的准确性比数据量重要得多。一条标错的样本对分类模型的伤害,可能需要十条正确样本才能弥补。如果对某条数据的故障类型不确定,宁可标为“疑似”单独存放,也不要硬塞进确定类别。
3. 动手构建频谱特征数据集:从采集配置到批量入库
3.1 采集参数怎么定:采样率、采样时长与触发方式
采集参数定不好,后面所有分析都是空中楼阁。采样率遵循奈奎斯特准则,要覆盖你关心的最高特征频率。轴承故障特征频率可能到 5kHz,那采样率至少 12.8kHz,实际取 12.8k 或 25.6k 更稳妥。如果只关心转频和电源频率附近的特征,5.12kHz 也够用,但会丢失轴承高频信息。
采样时长决定频率分辨率。Δf = 1/T,T 是采样时长。想要 1Hz 的分辨率,至少采 1 秒。但电机振动信号里转频通常几十赫兹,边频间隔可能只有几赫兹,所以实际建议采 2–5 秒,对应 Δf 为 0.2–0.5Hz。采样点数 = 采样率 × 采样时长,12.8kHz × 3s = 38400 点,取 2 的整数次幂 32768 或 65536 做 FFT 更方便。
触发方式有两种:连续采集和阈值触发。连续采集适合稳态工况,数据完整但冗余大。阈值触发只在振动幅值超过设定值时记录,适合捕捉冲击性故障,但可能漏掉渐变型异常。我一般两种都采,连续数据用于建立基线,触发数据用于补充故障样本。
import numpy as np from scipy import signal def acquire_config(fs=12800, duration=3.0, trigger_threshold=None): """ 生成采集配置 fs: 采样率 Hz duration: 采样时长 秒 trigger_threshold: 触发阈值,None 表示连续采集 """ n_points = int(fs * duration) # 取最接近的 2 的整数次幂,方便 FFT n_fft = 2 ** int(np.ceil(np.log2(n_points))) config = { "sampling_rate": fs, "duration": duration, "n_points": n_points, "n_fft": n_fft, "freq_resolution": fs / n_fft, "trigger": trigger_threshold is not None, "trigger_level": trigger_threshold } return config cfg = acquire_config(fs=12800, duration=3.0) print(f"频率分辨率: {cfg['freq_resolution']:.3f} Hz")这段代码做了一件事:根据采样率和时长算出实际需要的 FFT 点数,并给出频率分辨率。参数 fs 和 duration 需要根据你的电机类型和关心的故障频段来调整。如果电机额定转速 1500rpm,转频 25Hz,边频间隔可能只有 1–2Hz,那 freq_resolution 最好在 0.5Hz 以下,对应 duration 至少 2 秒。
3.2 频谱特征提取的完整代码实现
采集到原始数据后,下一步是批量提取谱峰。下面这段代码实现了从时域波形到谱峰列表的完整流程,包括去趋势、加窗、FFT、噪声基底估计和峰值筛选。
import numpy as np from scipy import signal from scipy.ndimage import median_filter def extract_spectral_peaks(waveform, fs, n_fft=8192, noise_factor=3.0, min_freq=5.0): """ 从时域波形提取频谱峰值 waveform: 一维时域信号 fs: 采样率 n_fft: FFT 点数 noise_factor: 峰值超过噪声基底的倍数阈值 min_freq: 忽略低于此频率的峰(去直流和低频漂移) 返回: 峰值列表,每个元素为 (频率, 幅值, 信噪比) """ # 1. 去趋势,消除基线漂移 detrended = signal.detrend(waveform, type='linear') # 2. 加 Hanning 窗,减少频谱泄漏 window = signal.windows.hann(len(detrended)) windowed = detrended * window # 3. FFT 并取单边谱 spectrum = np.fft.rfft(windowed, n=n_fft) magnitude = np.abs(spectrum) * 2.0 / np.sum(window) freqs = np.fft.rfftfreq(n_fft, d=1.0/fs) # 4. 估计噪声基底:用中位数滤波平滑幅值谱 noise_floor = median_filter(magnitude, size=51) noise_floor = np.maximum(noise_floor, 1e-12) # 避免除零 # 5. 计算信噪比并找局部极大值 snr = magnitude / noise_floor peak_indices, _ = signal.find_peaks(snr, height=noise_factor, distance=3) # 6. 过滤低频干扰 peaks = [] for idx in peak_indices: f = freqs[idx] if f < min_freq: continue peaks.append({ "freq_hz": round(float(f), 3), "amplitude": round(float(magnitude[idx]), 6), "snr": round(float(snr[idx]), 2) }) # 按幅值降序排列 peaks.sort(key=lambda x: x["amplitude"], reverse=True) return peaks # 模拟一段含故障特征的信号做验证 fs = 12800 t = np.arange(0, 3.0, 1.0/fs) fr = 25.0 # 转频 25Hz # 构造含 3 倍转频和 157Hz 轴承特征的信号 sig = (1.0 * np.sin(2*np.pi*fr*t) + 0.3 * np.sin(2*np.pi*3*fr*t) + 0.15 * np.sin(2*np.pi*157*t) + 0.05 * np.random.randn(len(t))) peaks = extract_spectral_peaks(sig, fs) for p in peaks[:5]: print(f"频率: {p['freq_hz']} Hz, 幅值: {p['amplitude']}, SNR: {p['snr']}")这段代码的关键参数有三个。n_fft 决定频率分辨率,8192 点在 12.8kHz 采样下给出 1.56Hz 分辨率,对大多数电机故障够用。noise_factor 控制峰值筛选的严格程度,3.0 是经验值,噪声大的现场可以调到 4.0 或 5.0。min_freq 用来跳过直流和低频漂移,一般设 5Hz 或 10Hz。
median_filter 的 size 参数需要根据谱线密度调整。size=51 意味着用 51 个频率点的中位数作为该点的噪声估计,对应约 80Hz 的窗口宽度。如果谱峰很密集,这个窗口可能把真实峰也平滑掉,需要减小 size。反过来如果噪声基底起伏大,增大 size 更稳。
3.3 批量处理与数据集入库
单条数据跑通之后,要处理的是成百上千条记录。批量处理的核心是统一参数、统一格式、统一命名。我一般会写一个批处理脚本,遍历原始数据目录,对每条记录调用提取函数,然后把结果写入统一的 JSON 文件。
import os import json import numpy as np from datetime import datetime def build_dataset(raw_dir, output_path, fs=12800, n_fft=8192): """ 批量构建频谱特征数据集 raw_dir: 原始数据目录,每个文件一条记录 output_path: 输出 JSON 路径 """ dataset = [] files = sorted([f for f in os.listdir(raw_dir) if f.endswith('.npy')]) for fname in files: filepath = os.path.join(raw_dir, fname) waveform = np.load(filepath) # 从文件名解析元信息,命名规则: 电机代号_故障类型_负载率_序号.npy parts = fname.replace('.npy', '').split('_') meta = { "sample_id": fname.replace('.npy', ''), "motor_code": parts[0], "fault_type": parts[1], "load_rate": float(parts[2]), "timestamp": datetime.now().isoformat() } peaks = extract_spectral_peaks(waveform, fs, n_fft) record = { "meta": meta, "peaks": peaks, "peak_count": len(peaks), "dominant_freq": peaks[0]["freq_hz"] if peaks else None } dataset.append(record) with open(output_path, 'w', encoding='utf-8') as f: json.dump(dataset, f, ensure_ascii=False, indent=2) print(f"共处理 {len(dataset)} 条记录,写入 {output_path}") return dataset # 使用示例 # dataset = build_dataset('./raw_data', './spectral_dataset.json')命名规则是批量处理里最容易被忽视但影响最大的环节。文件名里带上电机代号、故障类型、负载率,解析时就不需要额外的标注文件。如果原始数据没有规范命名,建议先写个重命名脚本统一格式,再跑批处理。输出用 JSON 而不是 CSV,是因为谱峰列表是变长数组,JSON 天然支持嵌套,后续要转 CSV 再展平也不迟。
入库之后建议做一次完整性检查:统计每类故障的样本数、检查是否有空谱峰记录、验证频率范围是否合理。这些检查用几行 pandas 代码就能完成,但能避免后面训练时才发现数据有问题。
4. 避坑与排查:频谱数据集构建中最容易翻车的五个地方
4.1 采样率不够导致特征频率混叠
现象:轴承故障特征频率在 4kHz 附近,但频谱上怎么都找不到对应谱峰,反而在低频段出现莫名其妙的峰。原因:采样率只有 5.12kHz,奈奎斯特频率 2.56kHz,4kHz 的特征频率发生混叠,折叠到了 1.12kHz 附近。解决:根据关心的最高特征频率反推采样率,至少取特征频率的 2.5 倍。轴承故障建议 12.8kHz 起步,如果同时关心高频共振解调,25.6kHz 更稳妥。
4.2 窗函数选择不当造成谱峰幅值失真
现象:同一台电机同一工况,两次采集的谱峰幅值差 30% 以上,但转速和负载都没变。原因:一次用了矩形窗,一次用了 Hanning 窗。矩形窗主瓣窄但旁瓣高,Hanning 窗主瓣宽但旁瓣低,对幅值的影响不同。解决:数据集构建过程中固定用一种窗,推荐 Hanning 窗。如果要做幅值精确对比,还需要做窗函数增益补偿,除以窗函数的相干增益。
4.3 噪声基底估计被强谱峰带偏
现象:正常样本提取出几百个谱峰,明显偏多。原因:中位数滤波的窗口太宽,把真实谱峰也当成了噪声基底的一部分,导致噪声估计偏高,信噪比整体偏低,阈值筛选失效。解决:减小 median_filter 的 size,或者改用分位数滤波。另一个办法是先做一次粗筛,把明显的大峰去掉再估计噪声基底。
4.4 标签体系不统一导致跨批次数据无法合并
现象:第一批数据用“轴承故障”做标签,第二批用“外圈故障”“内圈故障”分开标,合并时对不上。原因:标签体系没有提前定义好,不同人不同时间标注标准不一致。解决:动手采集之前先定好标签枚举表,写进项目文档,所有批次共用。如果已经出现了不一致,写映射脚本统一转换,转换规则要记录在案。
4.5 工况参数缺失导致模型学到虚假关联
现象:分类模型在测试集上准确率 95%,换一台电机就掉到 60%。原因:训练集里故障样本全部在高负载下采集,正常样本全部在低负载下采集,模型学到的是负载率而不是故障特征。解决:采集时保证每种故障类型在不同负载率下都有样本,至少覆盖 30%、60%、100% 三档。数据集里负载率字段必须完整,训练时可以做工况归一化或者把负载率作为独立特征输入。
5. 让数据集真正可用:验证方法与一个实用技巧
数据集建好之后,怎么判断它是不是真的能用?我一般做两个验证。第一个是物理一致性检查:随机抽若干样本,看提取出的主谱峰频率是否和理论特征频率对得上。比如标注为轴承外圈故障的样本,主谱峰应该在 BPFO 附近,误差不超过频率分辨率的 2 倍。如果对不上,要么是标签错了,要么是提取参数有问题。
第二个是分类可分性验证。用简单的 KNN 或 SVM 在谱峰特征上跑一遍交叉验证,看类间距离是否足够大。如果准确率低于 70%,说明特征区分度不够,需要回到提取环节调整参数,或者补充更多工况的样本。这个验证不需要复杂模型,简单模型跑不通,深度学习也救不了。
from sklearn.model_selection import cross_val_score from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler import numpy as np def validate_dataset(dataset, n_peaks=20): """ 用 KNN 做快速可分性验证 dataset: build_dataset 的输出 n_peaks: 每条样本取前 n 个谱峰的频率和幅值作为特征 """ X, y = [], [] for record in dataset: peaks = record["peaks"][:n_peaks] # 特征: 前 n 个峰的频率和幅值交替排列 feat = [] for p in peaks: feat.extend([p["freq_hz"], p["amplitude"]]) # 不足 n 个峰的用零填充 while len(feat) < n_peaks * 2: feat.append(0.0) X.append(feat) y.append(record["meta"]["fault_type"]) X = np.array(X) y = np.array(y) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) clf = KNeighborsClassifier(n_neighbors=5) scores = cross_val_score(clf, X_scaled, y, cv=5, scoring='accuracy') print(f"5 折交叉验证准确率: {scores.mean():.3f} (+/- {scores.std():.3f})") return scores.mean() # validate_dataset(dataset)这个验证脚本用前 20 个谱峰的频率和幅值作为特征,跑 5 折交叉验证。如果准确率在 85% 以上,说明数据集的特征区分度不错,可以进入模型训练阶段。如果在 70%–85% 之间,可能需要增加谱峰数量或者补充工况维度。低于 70% 就要回头检查标签质量和提取参数了。
一个实用技巧:在谱峰特征里加入频率的归一化比值,比如把每个峰的频率除以转频,得到“阶次”特征。阶次特征对转速变化不敏感,跨工况泛化能力更强。具体做法是在提取谱峰时同时记录转频,入库时增加一个 order 字段,值为 freq_hz / fr。这样即使电机转速有波动,同一故障的阶次特征仍然稳定。这个改动很小,但对数据集的可迁移性提升明显。
我自己踩过的最大坑是早期贪图样本数量,把不同负载率下的数据混在一起不做标注,结果模型在变负载场景下完全失效。后来老老实实把负载率字段补全,训练时按工况分层采样,效果才稳定下来。数据集这件事,字段设计的完备性比样本数量重要得多,前期多花一小时想清楚要记哪些参数,后面能省掉一周的返工。希望帮到你。
本文还有配套的精品资源,点击获取