简介:这份变压器故障谐波信号数据面向电力设备故障诊断方向的学习者与研究人员,尤其适合正在入门或验证神经网络算法的同学使用。资源围绕变压器故障检测场景,提供可用于训练与测试的谐波信号样本,帮助读者搭建从数据输入到故障分类的完整实验流程,弥补公开数据稀缺、自建样本成本高的问题。压缩包共2个文件,以txt说明文档与m脚本为主,前者交代数据背景与使用方式,后者可直接在MATLAB环境中加载与调试,整体约5KB,体量轻便、便于快速上手。目前已有470人学习下载,说明该数据在同类小样本资源中具有一定参考价值。读者可借助它完成BP等神经网络的训练、验证与对比实验,理解谐波特征与故障类型之间的映射关系,并在此基础上扩展预处理、特征提取与模型调参思路,适合作为课程设计、毕业课题或算法预研的起步素材。
1. 变压器故障谐波信号数据:从哪来、长什么样、能解决什么
变压器故障谐波信号数据,说白了就是把变压器在正常和各类故障状态下产生的电压、电流波形采下来,经过处理后得到的一组能反映设备健康状态的数字序列。很多做电力设备监测的同行,手里有振动数据、有温度数据,但一到谐波就犯怵——采样率怎么定、故障特征怎么标、数据量多大才够训练一个能用的模型,全是问号。我最早接触这个方向,是因为一台配电变压器连续烧了两回,事后调录波文件才发现,故障前几个周波的二次侧电流里已经出现了明显的三次谐波抬升,但当时没人盯着这个指标。这件事让我意识到,谐波信号不是“顺带看看”的辅助量,它完全有资格作为独立的数据源来支撑故障诊断。这篇文章面向的是想用谐波数据做变压器故障识别、但不知道从哪下手的一线工程师和算法同学,我会把数据获取、特征构造、模型训练和落地踩坑一条线讲清楚,让你看完能自己搭一套最小可用的验证流程。
2. 谐波数据从哪来:采集方案与仿真补充
2.1 实测采集:传感器选型和采样率怎么定
实测数据永远是最有说服力的,但前提是你得采对。变压器谐波信号的频率范围通常关注到 2 kHz 到 5 kHz 就够用了,因为主要故障特征集中在 2 到 50 次谐波之间。按照奈奎斯特定理,采样率至少要 10 kHz,但我一般会留足余量,直接上 20 kHz 甚至 50 kHz。原因很简单:你永远不知道现场会不会出现高频振荡分量,采低了就再也补不回来,这是血泪经验。
传感器方面,电流信号用开口式电流互感器最方便,不用断开一次回路。选型时注意三个参数:带宽要覆盖 DC 到 10 kHz 以上,精度至少 0.5 级,相位误差在 50 Hz 处小于 1 度。电压信号可以直接从 PT 二次侧取,但一定要加隔离放大器,否则共模干扰会让你怀疑人生。
下面是一个用 Python 做数据采集的骨架代码,基于常见的 NI 或研华采集卡,通过其 Python SDK 读取通道数据:
import numpy as np import nidaqmx from nidaqmx.constants import AcquisitionType # 采样参数设置 SAMPLE_RATE = 50000 # 采样率 50 kHz,留足高频余量 DURATION = 2.0 # 每次采集 2 秒,覆盖 100 个工频周期 CHANNELS = ["Dev1/ai0", "Dev1/ai1"] # ai0 接电流,ai1 接电压 def acquire_harmonic_data(): samples = int(SAMPLE_RATE * DURATION) with nidaqmx.Task() as task: for ch in CHANNELS: task.ai_channels.add_ai_voltage_chan( ch, min_val=-10.0, # 电压输入范围 ±10V max_val=10.0 ) task.timing.cfg_samp_clk_timing( rate=SAMPLE_RATE, sample_mode=AcquisitionType.FINITE, samps_per_chan=samples ) data = task.read(number_of_samples_per_channel=samples) return np.array(data) # 形状: (通道数, 采样点数) raw = acquire_harmonic_data() print(f"采集完成,数据形状: {raw.shape}")这段代码的逻辑很直接:配置两个模拟输入通道,设定采样时钟,一次性读取指定长度的数据。关键参数有三个。SAMPLE_RATE设 50 kHz 是为了给后续谐波分析留出足够带宽,如果你只关心到 25 次谐波,20 kHz 也够,但别低于这个数。DURATION设 2 秒是为了覆盖足够多的工频周期,做频谱分析时频率分辨率等于采样率的倒数乘以点数,2 秒数据做 FFT 后分辨率是 0.5 Hz,足够分辨相邻谐波。min_val和max_val要根据传感器输出范围来设,设大了浪费分辨率,设小了会削顶。
采集到的原始数据不能直接喂给模型,必须先做预处理。我一般会走这么几步:先去工频,用 50 Hz 陷波器把基波滤掉,因为基波幅值比谐波大几个数量级,不去掉的话后续分析根本看不到谐波;然后做分帧,每 10 个工频周期为一帧,约 0.2 秒;最后对每帧做 FFT,取 2 到 50 次谐波的幅值和相位作为特征向量。
2.2 仿真补充:用 Simulink 生成带标签的故障数据
实测数据最大的问题是标签难搞。你不可能为了采故障数据真去把变压器烧了,所以仿真数据在这个方向里不是可选项,是必选项。我一般用 MATLAB Simulink 搭一个三相变压器的谐波模型,通过设置不同的故障类型来生成带标签的数据集。
常见的故障类型和对应的谐波特征是这样的:
| 故障类型 | 主要谐波特征 | 特征频次 |
|---|---|---|
| 绕组匝间短路 | 三次谐波幅值显著上升 | 3 次为主 |
| 铁芯饱和 | 奇次谐波整体抬升 | 3、5、7 次 |
| 局部放电 | 高频分量出现 | 20 次以上 |
| 分接开关接触不良 | 偶次谐波异常 | 2、4 次 |
仿真模型的搭建步骤:先建三相变压器本体模型,设置额定容量、变比和漏抗;然后在二次侧加可编程负载,模拟不同负载率;最后在绕组上并联可变电阻来模拟匝间短路,电阻值从 10 欧姆逐渐降到 0.1 欧姆,对应短路程度从轻微到严重。每改变一次故障设置,跑一次仿真,导出二次侧电流波形,存成 CSV 文件,文件名里带上故障类型和严重程度标签。
仿真数据的价值在于标签绝对准确,而且可以覆盖实测中很难遇到的极端工况。但要注意,仿真数据不能直接拿来训练最终模型,它和实测数据之间存在域偏移。我的做法是先用仿真数据做预训练,再用少量实测数据做微调,这样模型既见过各种故障形态,又能适应现场的真实信号特征。
3. 特征工程:把波形变成模型能吃的数字
3.1 时域特征和频域特征怎么选
原始波形是一维时间序列,直接扔给模型不是不行,但效果通常不如手工构造的特征。我一般会同时提取时域和频域两组特征,让模型自己去学哪组更有用。
时域特征包括:均方根值、峰值因子、峭度、波形因子。这四个指标计算量小,对冲击性故障特别敏感。比如匝间短路发生时,电流波形会出现周期性冲击,峭度值会明显偏离正常范围。
频域特征就是各次谐波的幅值和相位。这里有个细节:不要只取幅值,相位信息在区分某些故障类型时非常关键。比如铁芯饱和和绕组变形可能产生相似的谐波幅值分布,但相位关系完全不同。
import numpy as np from scipy.fft import fft, fftfreq def extract_features(signal, fs=50000, fundamental=50): """从单帧信号中提取时域和频域特征""" features = {} # 时域特征 features['rms'] = np.sqrt(np.mean(signal**2)) features['peak'] = np.max(np.abs(signal)) features['crest'] = features['peak'] / features['rms'] features['kurtosis'] = np.mean((signal - np.mean(signal))**4) / (np.std(signal)**4) # 频域特征:取 2-50 次谐波 n = len(signal) yf = fft(signal) xf = fftfreq(n, 1/fs) harmonic_amps = [] harmonic_phases = [] for h in range(2, 51): target_freq = fundamental * h idx = np.argmin(np.abs(xf - target_freq)) harmonic_amps.append(np.abs(yf[idx]) * 2 / n) harmonic_phases.append(np.angle(yf[idx])) features['harmonics_amp'] = np.array(harmonic_amps) features['harmonics_phase'] = np.array(harmonic_phases) # 总谐波畸变率 fundamental_idx = np.argmin(np.abs(xf - fundamental)) fundamental_amp = np.abs(yf[fundamental_idx]) * 2 / n features['thd'] = np.sqrt(np.sum(np.array(harmonic_amps)**2)) / fundamental_amp return features # 假设 signal 是一帧 0.2 秒的数据 frame = raw[0, :10000] # 取第一通道前 10000 个点 feat = extract_features(frame) print(f"THD: {feat['thd']:.4f}, 峭度: {feat['kurtosis']:.4f}")这段代码里,extract_features函数接收一帧信号,返回一个字典。时域部分计算了四个经典指标,频域部分遍历 2 到 50 次谐波,提取幅值和相位。thd是总谐波畸变率,这是衡量谐波污染程度最常用的综合指标。参数fs要和采集时一致,fundamental在国内是 50 Hz。注意np.argmin(np.abs(xf - target_freq))这一步是在找最接近目标频率的频点,因为 FFT 的频率分辨率有限,不一定刚好落在 50 的整数倍上。
3.2 特征归一化和数据增强的实操细节
特征提取完之后,不同特征的量纲差异巨大。THD 可能只有 0.05,而谐波幅值可能是几百。如果不做归一化,模型会被大幅值特征主导。我一般用 Z-score 归一化,按特征维度分别减均值除标准差。注意均值和标准差只能从训练集算,然后应用到验证集和测试集,否则就是数据泄露。
数据增强在这个方向特别重要,因为故障样本天然稀少。我常用的增强方法有三种。第一种是加高斯白噪声,信噪比从 20 dB 到 40 dB 随机选,模拟不同噪声环境。第二种是时间平移,把信号在时间轴上随机移动几个采样点,模拟触发时刻的微小差异。第三种是幅值缩放,整体乘以 0.9 到 1.1 之间的随机因子,模拟负载波动。这三种方法实现简单,但能显著提升模型的泛化能力。
def augment_signal(signal, noise_snr_db=30, shift_max=50, scale_range=(0.9, 1.1)): """对单帧信号做增强""" # 加噪声 signal_power = np.mean(signal**2) noise_power = signal_power / (10**(noise_snr_db/10)) noise = np.random.normal(0, np.sqrt(noise_power), len(signal)) augmented = signal + noise # 时间平移 shift = np.random.randint(-shift_max, shift_max) augmented = np.roll(augmented, shift) # 幅值缩放 scale = np.random.uniform(*scale_range) augmented = augmented * scale return augmented这个增强函数可以串联使用,也可以单独使用。我一般会在每个 epoch 里对训练样本随机做一次增强,这样模型每次见到的样本都略有不同,相当于变相扩大了数据集。注意验证集和测试集不能做增强,必须用原始信号,否则评估结果不可信。
4. 模型选型与训练:从 SVM 到一维 CNN 的取舍
4.1 小样本场景下为什么我优先选 SVM
如果你手里的故障样本只有几百条,别犹豫,直接用 SVM。我试过在 500 条样本上跑一维 CNN,结果验证集准确率波动超过 15%,而 SVM 稳定在 92% 以上。原因很简单:深度学习需要大量数据来拟合参数,小样本下很容易过拟合。
SVM 的输入就是前面提取的特征向量,把时域四个特征和谐波幅值相位拼在一起,组成一个 100 多维的向量。核函数选 RBF,C和gamma用网格搜索调。我一般让C在 0.1 到 100 之间按对数取点,gamma在 0.001 到 1 之间同样按对数取。交叉验证用 5 折,因为样本少,折数太多每折的验证集太小,评估结果不稳定。
from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV # X_train: 特征矩阵 (n_samples, n_features) # y_train: 标签 (n_samples,) pipeline = Pipeline([ ('scaler', StandardScaler()), ('svm', SVC(kernel='rbf', probability=True)) ]) param_grid = { 'svm__C': [0.1, 1, 10, 100], 'svm__gamma': [0.001, 0.01, 0.1, 1] } grid = GridSearchCV(pipeline, param_grid, cv=5, scoring='f1_macro', n_jobs=-1) grid.fit(X_train, y_train) print(f"最优参数: {grid.best_params_}") print(f"交叉验证 F1: {grid.best_score_:.4f}")这里用 Pipeline 把归一化和 SVM 串在一起,好处是交叉验证时归一化的均值和标准差只在训练折上计算,避免了数据泄露。scoring用f1_macro而不是准确率,因为故障类别通常不均衡,准确率会被多数类主导。probability=True是为了后续能输出各类别的概率,方便做阈值调整。
4.2 数据量上来之后一维 CNN 怎么搭
当你通过仿真加实测攒到几千条以上样本时,可以上一维 CNN。一维 CNN 的优势是能自动学习波形的局部模式,不需要手工设计特征。我一般用三到五层卷积,每层后面接池化,最后全局平均池化后接全连接分类。
import torch import torch.nn as nn class HarmonicCNN(nn.Module): def __init__(self, num_classes=4): super().__init__() self.conv_layers = nn.Sequential( # 输入: (batch, 1, 10000) nn.Conv1d(1, 32, kernel_size=15, stride=2, padding=7), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, kernel_size=9, stride=1, padding=4), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(64, 128, kernel_size=5, stride=1, padding=2), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1) # 全局平均池化 ) self.classifier = nn.Sequential( nn.Linear(128, 64), nn.ReLU(), nn.Dropout(0.5), nn.Linear(64, num_classes) ) def forward(self, x): features = self.conv_layers(x) features = features.squeeze(-1) return self.classifier(features) model = HarmonicCNN(num_classes=4) total_params = sum(p.numel() for p in model.parameters()) print(f"模型参数量: {total_params}")这个网络结构的设计思路是:第一层用大卷积核(15)捕捉工频周期的整体形态,后面逐层减小卷积核,捕捉越来越精细的局部模式。BatchNorm加速收敛,Dropout防止过拟合。全局平均池化代替了全连接层,大幅减少了参数量,这个模型总共只有几万参数,在几千条样本上不容易过拟合。
训练时用 Adam 优化器,学习率 1e-3,配合余弦退火调度。损失函数用交叉熵,但如果类别不均衡,可以加类别权重。批次大小设 32 或 64,太小训练不稳定,太大收敛慢。训练轮数不用设太多,一般 50 到 100 轮就够了,配合早停策略,验证集损失连续 10 轮不下降就停。
5. 避坑与排查:谐波数据做故障诊断的五个翻车现场
5.1 采样率够但抗混叠滤波器没设对
现象:采集到的谐波幅值比理论值偏高,尤其是高次谐波,50 次谐波幅值甚至比 25 次还大。原因:采集卡前端有抗混叠滤波器,但截止频率设在了 25 kHz 以上,导致 25 kHz 到 50 kHz 之间的高频噪声混叠到了低频段。解决:把抗混叠滤波器的截止频率设在采样率的 0.4 倍以下,也就是 50 kHz 采样时截止频率不超过 20 kHz。如果采集卡不支持可调滤波器,就在传感器和采集卡之间加一个无源 LC 低通滤波器。
5.2 工频陷波器把故障特征一起滤掉了
现象:做完 50 Hz 陷波后,三次谐波幅值也明显下降,故障样本和正常样本的区分度变差。原因:陷波器的品质因数设得太低,阻带太宽,把 50 Hz 附近的 3 次谐波(150 Hz)也衰减了。解决:用 IIR 陷波器时把 Q 值设在 30 以上,阻带带宽控制在 5 Hz 以内。或者干脆不用陷波器,改用减去基波拟合分量的方法,先估计基波的幅值和相位,再从原始信号中减去,这样对邻近谐波的影响最小。
5.3 仿真数据训练模型在实测上准确率暴跌
现象:仿真数据上准确率 98%,一到实测数据就掉到 60% 以下。原因:仿真模型过于理想化,没有考虑传感器频响特性、线路阻抗和背景噪声,导致仿真数据和实测数据的分布差异很大。解决:在仿真模型里加入传感器传递函数和线路阻抗模型,让仿真信号经过和实测一样的“污染”过程。另外,用少量实测数据做微调时,不要只微调最后一层,整个网络都用小学习率(1e-4)跑几轮效果更好。
5.4 标签里混入了“疑似故障”样本
现象:模型训练时验证集准确率很高,但上线后误报率居高不下。原因:标注数据时把一些“疑似故障但未确认”的样本也标成了故障类,这些样本的特征其实介于正常和故障之间,导致模型学到的决策边界偏保守。解决:标注时严格执行三分类:正常、故障、疑似。疑似样本单独放一个集合,训练时要么剔除,要么用半监督学习的方法单独处理。宁可少一些故障样本,也不要让标签有歧义。
5.5 只看准确率忽略了故障漏报的代价
现象:模型整体准确率 95%,但故障漏报率高达 30%。原因:故障样本只占总样本的 5%,模型只要把所有样本都预测为正常就能拿到 95% 的准确率。解决:评估指标改用召回率和 F1 分数,训练时给故障类更高的类别权重。更直接的办法是调整分类阈值,把故障类的判定阈值从 0.5 降到 0.3,牺牲一些误报率来换取更低的漏报率。在变压器故障诊断里,漏报的代价远大于误报,这一点必须体现在模型设计里。
6. 用滑动窗口做在线推理:一个能落地的部署技巧
训练完模型只是第一步,真正要落地得解决在线推理的问题。现场采集是连续的,你不可能每来一个采样点就推理一次,那样计算量太大。我一般用滑动窗口的方式:维护一个长度为 2 秒的环形缓冲区,每过 0.2 秒(也就是一个工频周期)就取一次缓冲区里的最新数据做推理。这样推理频率是 5 Hz,计算量可控,而且能及时捕捉到故障的发展过程。
from collections import deque import numpy as np class OnlineDetector: def __init__(self, model, fs=50000, window_sec=2.0, step_sec=0.2): self.model = model self.fs = fs self.window_size = int(fs * window_sec) self.step_size = int(fs * step_sec) self.buffer = deque(maxlen=self.window_size) self.counter = 0 def push(self, samples): """逐点或逐块推入新采样数据""" self.buffer.extend(samples) self.counter += len(samples) if self.counter >= self.step_size and len(self.buffer) == self.window_size: self.counter = 0 return self._infer() return None def _infer(self): signal = np.array(self.buffer) # 这里复用前面的特征提取和模型推理逻辑 features = extract_features(signal, fs=self.fs) # 将特征向量化后送入模型 # pred = self.model.predict(features_vector) # return pred return features # 示意返回 detector = OnlineDetector(model=None) # 模拟逐块推入数据 for chunk in np.array_split(raw[0], 100): result = detector.push(chunk) if result is not None: print(f"推理触发,THD: {result['thd']:.4f}")这个在线检测器的核心是deque环形缓冲区,maxlen设成窗口大小,新数据进来旧数据自动丢弃。push方法每收到step_size个新样本就触发一次推理。实际部署时,_infer里要做的事情包括:对缓冲区数据做和训练时一致的预处理、提取特征、归一化、送模型推理、根据输出概率做决策。注意预处理和归一化必须和训练时完全一致,否则推理结果会偏得离谱。
还有一个工程细节:模型推理时间要控制在步长以内。如果步长是 0.2 秒,推理必须在 0.2 秒内完成,否则数据会堆积。我一般会把模型转成 ONNX 格式,用 ONNX Runtime 推理,速度比原生 PyTorch 快不少。如果还是不够快,就减小窗口长度或者增大步长,但窗口至少要覆盖 10 个工频周期,否则频率分辨率不够。
最后说一个我自己的习惯:每次部署新模型之前,一定先用历史录波数据跑一遍离线验证,把推理结果和实际故障记录对齐,确认没有时间偏移和标签错位。这个步骤花不了多少时间,但能避免上线后才发现模型在“正确的时间给出了错误的结论”这种尴尬局面。希望帮到你。
本文还有配套的精品资源,点击获取