做了几年波形分析,我最怕的不是信号本身有多复杂,而是拿到一段“看着不对劲、又说不清哪里不对劲”的电压波形。谐波、闪变、暂升这些扰动叠在一起的时候,示波器上就是一坨乱糟糟的曲线,算THD只能告诉你总畸变率,看不出扰动发生在什么时候;FFT能告诉你有哪些频率成分,又完全丢掉时间信息。直到我系统地折腾了一套基于S变换的时频分析方法,才真正把“电能质量扰动识别”这件事从靠感觉变成了可复现的流程:信号进来,S变换出一张时频图,单一扰动有单一定位,复合扰动也能分块拆解。
这篇文章不打算做理论搬运,就围绕S变换对电能质量扰动(谐波、闪变、暂升以及它们组合出的复合扰动)做时频变换识别这条主线,把原理、代码、参数、坑位一次性说清楚。适合正在做电能质量监测、故障录波分析,或者想把手里的波形数据变成分类特征的同行参考。
1. 为什么扰动识别偏偏选中S变换
1.1 谐波、闪变、暂升的时域特征,决定了工具必须“两全”
先说清楚一个问题:电能质量扰动识别,本质上是想从电压(或电流)信号里回答三个问题——有什么扰动、发生在什么时间、强度有多大。
谐波扰动是稳态的,持续存在,特征集中在频域:50Hz基波之外,5次、7次、11次谐波各自在固定频率上有一个峰值。这类信号用FFT看频谱非常清晰,但FFT一旦面对闪变和暂升就露馅了。
闪变本质是工频幅值的低频调幅,调制频率一般在0.05Hz到35Hz之间。它的频谱特征是基频两侧出现一对边带,例如9Hz调制会在50Hz附近出现41Hz和59Hz的旁瓣。光看频谱能发现边带,但如果想知道闪变的持续时间、调制深度变化,FFT完全给不了时间维度。
暂升、暂降这类扰动就更典型了:持续时间往往只有几个周波到几百毫秒,幅值先跳变、维持一段、再恢复。这种信号在频域上能量是散开的,在时域上看幅值跳变倒是清楚,可一旦混入谐波,时域波形又会糊成一片。
换句话说,扰动识别需要的是一个能同时给出“频率在哪、时间在哪”的工具。这决定了短时傅里叶变换、小波变换、S变换三选一的路口。
1.2 三种时频工具的取舍:固定窗、尺度窗和频率自适应窗
短时傅里叶变换(STFT)实现最简单:把信号切成一堆短窗,对每个窗做FFT。但它的窗宽是固定的,选了窄窗,时间分辨率高、频率分辨率低,谐波频率看不准;选了宽窗,频率分开了、时间上又模糊了。闪变和暂升这种“低频慢变+突变”混在一起的情况,固定窗很难两头兼顾。
小波变换用尺度伸缩替代固定窗,高频用窄窗、低频用宽窗,理论上自适应了,但小波输出的尺度与实际频率之间不是线性映射,从时频图里读不出“这个是50Hz基波、那个是250Hz谐波”这种直观信息。做扰动识别的工程师,还是希望坐标轴直接就是频率,否则分类逻辑很难跟物理意义挂钩。
S变换最特别的地方在于它的窗宽随频率倒数变化:低频的时候窗宽、频率分辨率高,正好照顾基波和闪变边带这类低频成分;高频的时候窗窄、时间分辨率高,能定位谐波或暂升突变产生的瞬态成分。而且它是在傅里叶变换框架下构建的,输出坐标天然就是时间和频率,相位信息也保留,不会像小波那样把相位信息弄丢。对谐波、闪变、暂升这类“低频稳、高频瞬”的多模态扰动信号,S变换是三者里最“省心”的。
1.3 一维波形变成二维时频图,识别问题瞬间变成图像问题
S变换把一维时间序列变成二维复数矩阵:横轴是时间,纵轴是频率,每个点的幅值代表该时刻该频率成分的能量强弱。把幅值矩阵画成热力图,就是常说的S变换时频图。
这个变换带来的直接好处是:扰动识别从“波形形态判断”变成“时频图上的特征定位”。谐波是一排水平亮线,闪变是基频线附近的周期性波动带,暂升是一条幅值整体抬升的色块,复合扰动则是这些特征在图上叠加。到了这一步,哪怕不写分类算法,人眼也能在时频图上把扰动成分拆得七七八八。而机器识别更是可以把时频图当二维图像输入,用统计特征或者深度学习模型做分类,这也是我后来把整套流程跑通的核心思路。
2. S变换的原理与扰动在时频图上的“指纹”
2.1 S变换的数学表达与离散实现
S变换的定义容易看懂:
S(τ, f) = ∫ x(t) · w(τ - t) · e^{-i2πft} dt
其中高斯窗w(t)的宽度与频率成反比:
w(t) = (|f| / √(2π)) · e^{-t² · f² / 2}
这个表达式的含义是:对每一个分析频率f,用一个宽度为1/f量级的高斯窗去截取信号,再做傅里叶分析,从而得到该频率下的时间-幅值变化曲线。频率越低,窗越宽,频率分辨率越好;频率越高,窗越窄,时间定位越准。实践中最常用的离散实现方式是借助FFT加快运算:先把信号做FFT得到X(k),对每个频率f,在频域将X(k)与一个平移高斯函数相乘,再做逆FFT,得到该频率对应的时间序列。
下面是我在实际项目里用的一个精简版Python实现:
import numpy as np def s_transform(x, fs, fmin=1.0, fmax=2500.0, nf=256): """ 简化版S变换 x: 输入信号 fs: 采样频率 Hz fmin / fmax: 分析频率范围 nf: 频率轴采样点数 返回 freq_axis, t_axis, st_matrix """ n = len(x) t = np.arange(n) / fs Xf = np.fft.fft(x) freqs = np.fft.fftfreq(n, d=1.0/fs) freq_axis = np.linspace(fmin, fmax, nf) st = np.zeros((nf, n), dtype=complex) for idx, f in enumerate(freq_axis): # 频域高斯窗,宽度与频率f成正比(此处窗在频域表示为关于f的标准差) gaussian = np.exp(-2 * np.pi**2 * (freqs - f)**2 / f**2) st[idx, :] = np.fft.ifft(Xf * gaussian) return freq_axis, t, st这段代码能跑通,但对于实际工况数据,我建议重点关注两件事:一是频率范围不要从0Hz开始,因为高斯窗在0Hz附近会变成一条极宽的平线,计算意义不大;二是采样长度N和频率点数nf决定了输出矩阵的尺寸,默认256×N在大部分工控机上都没问题。
2.2 单一扰动的时频特征速查
把S变换跑完后,我最常做的事情是先画时频幅值热力图。不同扰动在图上留下的“指纹”差异非常明显,我把它们整理成了一张速查表:
| 扰动类型 | 时频图特征 | 典型频段 | 时间形态 |
|---|---|---|---|
| 谐波 | 基频及整数倍频处多条水平亮线 | 50、150、250Hz等 | 持续稳定、无随时间波动 |
| 闪变 | 基频两侧的一对边带,强度周期性波动 | 基频±(0.05~35Hz) | 亮线围绕基频上下振动 |
| 暂升 | 整个频带幅值突增的色块 | 全频带,基频最明显 | 突变起点与恢复点有明显分界 |
| 暂降 | 与暂升相反,幅值突降的凹陷色块 | 全频带,基频最明显 | 同样有明显起止时刻 |
| 暂中断 | 幅值近零的贯穿型缺口 | 全频带 | 持续时间通常较长 |
这里要特别说明一下:我见过不少初学者只盯着基频那条亮线看,其实谐波的亮线位置是判断扰动类型最直观的线索。如果看到150Hz、250Hz上出现了与基频平行的稳定亮线,基本可以断定有3次和5次谐波;如果这些亮线的颜色深浅随时间周期性变化,就要考虑谐波与闪变同时存在的可能。
2.3 从时频图看谐波与闪变:两条最容易识别的“指纹”
谐波的识别在时频图上几乎是最容易的。一个纯谐波扰动信号做S变换后,基频50Hz处是一条贯穿整个时间轴的亮线,150Hz、250Hz等位置也会出现亮线,幅值大小与谐波含量正相关。如果谐波含量稳定,这些线的颜色整段几乎一致。我经常用这种方式快速核对THD计算结果,两三秒就能确认到底是哪几次谐波偏高。
闪变识别则要看基频亮线的“波动性”。因为闪变是对工频幅值的低频调幅,S变换在基频附近的时频幅值会随时间上下浮动,并且在基频两侧出现对称的边带亮线。比如8.8Hz调制引起的闪变,会在41.2Hz和58.8Hz附近各出现一条细线,这两条线的间距就等于调制频率。看边带比看基频波动更准确,因为基频幅值波动有时会被采样噪声干扰,而边带频率位置很少误判。
3. 实操流程:从原始采样到扰动识别
3.1 数据采集与预处理:决定结果的“隐形参数”
做S变换之前,数据质量不过关的话,后面所有特征都白搭。我先说采样:目前电力行业通用测试规范对电能质量监测的采样率要求,一般建议每周波至少128个采样点,也就是在50Hz工频下采样率至少6400Hz。我自己的项目里习惯用每周波128点的配置,既满足谐波分析到50次谐波的需求,又不会让S变换矩阵过大。
预处理有几个容易忽略但影响很大的细节:
- 去直流:采样系统有时会引入很小的直流偏置,不去掉的话,S变换在0Hz附近的能量会污染低频段分析,我一般在做FFT前先减去信号均值。
- 归一化:把波形幅值除以基波幅值,让不同电压等级的监测数据可以横向比较,否则分类模型容易被幅值大小带偏。
- 找基波频率:电网频率在49.8Hz到50.2Hz之间波动,直接把S变换的基频坐标固定成50Hz会引入误差。我习惯先做一次粗略FFT,锁定实际基波频率,再把这个频率代入S变换的频率轴生成逻辑。
3.2 S变换参数怎么定:频率范围、频率点数和窗宽
参数设置方面,我踩过不少坑,这里给出经过验证的推荐起点:
- 频率范围:一般从1Hz到2500Hz。低频从1Hz开始是为了覆盖闪变调制频率,高频到2500Hz对应每周波128点采样率下的奈奎斯特频率3200Hz留出余量。如果只关注谐波和暂升,可以把上限降到1000Hz,计算量能小一半。
- 频率点数nf:默认取256到512。点数太少,频率分辨率不够,谐波亮线可能落在两个频点之间导致幅值被平均;点数太多,比如2048,计算时间和内存会迅速上涨,但识别准确率提升有限。实测下来256个频率点对常见扰动已经够用。
- 高斯窗的离散化处理:理论中的高斯窗是连续函数,离散实现时要注意频率f不能太接近0,否则高斯窗退化成恒定值,整个S变换失去意义。这也是我为什么把fmin设成1Hz而不是0Hz的原因。
计算量有个粗略的估算方法:S变换的计算复杂度约为O(nf × N logN),N是采样点数。假设记录长度是3秒、采样率6400Hz,N=19200,nf=256,单次S变换耗时在普通笔记本上大约零点几秒到一两秒,完全可接受。如果做在线实时监测,可以缩短记录长度到1秒,或者每秒钟只对最新一个周波做滑动S变换,牺牲少量时间分辨率换取实时性。
3.3 特征提取的三种思路:统计量、突变点、分类器
S变换矩阵拿到手之后,怎么把它变成可用于识别的特征,是整套流程里最体现功底的部分。我常用三种思路,分别适用不同场景。
第一种是纯统计特征:对S变换幅值矩阵按频率带划分(基频带、谐波带、边带),分别计算均值、方差、峰值、能量。例如,基频带的幅值方差能反映闪变调制强弱,谐波带的平均幅值能反映谐波含量。这种特征的优点是解释性强,调试的时候能直接对应到物理量。
第二种是突变点特征:提取基频幅值的时间序列,对它做一阶差分或边缘检测,找到幅值跳变的起点和恢复点。两个跳变点之间的时间跨度既是暂升/暂降的持续时间,两个跳变点的幅值差就是扰动深度。对谐波+暂升这种复合扰动,就分别看基频线的幅值阶跃和谐波线的存在情况,两个特征独立提取再合并。
第三种是图像化特征:直接把S变换幅值矩阵作为二维图像,输入卷积神经网络(CNN)做分类。这个思路的好处是省去了人工设计特征的环节,坏处是数据量要求高、解释性差。我建议在项目初期先用前两种方法把流程跑通、建立“时频图长什么样”的直觉,再考虑上深度学习。
下面是一段提取基频幅值序列和暂升/暂降起点检测的示例代码:
def extract_perturbation_features(st_matrix, freq_axis, fs): # 找基频位置 f0_idx = np.argmin(np.abs(freq_axis - 50.0)) base_trace = np.abs(st_matrix[f0_idx, :]) # 基频幅值的一阶差分,用于检测突变时刻 diff_trace = np.diff(base_trace) jump_idx = np.where(np.abs(diff_trace) > 0.15 * np.mean(base_trace))[0] # 暂升/暂降持续时间 if len(jump_idx) >= 2: duration = (jump_idx[-1] - jump_idx[0]) / fs max_deviation = np.max(base_trace[jump_idx[0]:jump_idx[-1]]) \ - np.min(base_trace[jump_idx[0]:jump_idx[-1]]) else: duration = 0.0 max_deviation = 0.0 return base_trace, duration, max_deviation配合后面接一层简单的决策规则,就能把暂升、暂降、短时中断这类以幅值突变为主的扰动识别出来。
3.4 单一扰动识别的完整流程示例
拿电压暂升举个例子:一段3秒的电压波形,在0.8秒到2.1秒之间幅值从1.0pu抬升到1.25pu。
第一步,预处理去直流、归一化;第二步,S变换得到256×19200的时频矩阵;第三步,提取基频幅值时间序列,用差分检测到两个跳变点,起点在0.8秒附近,终点在2.1秒附近,持续时间约1.3秒,幅值抬升幅度约0.25pu;第四步,检查谐波带的平均幅值是否同时升高。在这个例子里,谐波带幅值没有同步变化,于是判定为单一暂升扰动。
整个过程不需要人盯着波形看,程序自动输出“扰动类型=暂升、持续时间=1.3秒、深度=25%”这样的结构化结论。实际项目中,把这套逻辑套到成百上千个录波文件上,比肉眼检查快得多。
4. 复合扰动的识别:时频特征是叠加的,不是糊掉的
4.1 复合扰动到底难在哪
复合扰动是工程里最常见的场景:工厂里的非线性负载一起动,既拉低电压(暂降),又往电网里灌谐波;电弧炉运行的时候,既引起电压闪变又产生间谐波。复合扰动识别难,难在特征互相干扰——暂降发生后谐波幅值也变了,闪变的边带跟间谐波频率靠得很近,单一扰动的判断规则组合在一起经常互相矛盾。
我接触过的一个实际案例是某台区设备投切瞬间录到的波形:电压先短暂跌落,随后出现持续约半秒的谐波畸变。单独看FFT,频谱里既有谐波峰又有低频旁瓣,很难确定哪些是暂降带来的瞬态成分、哪些是稳态谐波;单独看时域波形,跌落过程又淹没在谐波纹波里,根本找不准起点。
4.2 复合扰动的时频图破解法
复合扰动的关键破解点,就是把每个扰动映射到不同的时频区域,然后分区域识别。S变换之后,暂降事件在基频幅值时间序列上表现为明显的凹陷,凹陷的起点和终点非常清楚;谐波畸变在150Hz、250Hz亮线上表现为能量增强。这两个特征在时频图上一高一低、互不遮挡,完全可以在同一张图上分别提取。
实际操作时,我会先把时频图按频率分成三个子带:基频带(45~55Hz)、低频带(1~45Hz)、谐波带(100~2500Hz)。基频带用来判断暂升、暂降、暂中断;低频带用来判断闪变;谐波带用来判断谐波成分。每个子带单独提取特征,最后再组合成一个综合判断。这样做的好处是,一个复合扰动哪怕三个子带同时有特征,也不会因为互相干扰而漏判。
4.3 几个容易混淆的典型场景
复合扰动识别里,我至少踩过三个混淆严重的坑,现在总结如下:
- 闪变与间谐波:两者都能在基频附近产生谱峰,但闪变的边带对称分布在基频两侧,而且边带间隔等于调制频率;间谐波通常只有一个不对称的谱峰。看时频图时,如果只看到单侧谱线,优先怀疑间谐波而不是闪变。
- 谐波+暂降与暂降后的谐波突变:前者谐波亮线在暂降期间依然存在,只是幅值随基波成比例变化;后者是暂降发生后新增的谐波亮线,两个事件在时间上有先后关系。判断方法是看谐波亮线出现时刻是否与暂降起点完全一致。
- 暂升伴随短时中断:这两个从名字上看不应该混淆,但在复合扰动里,先暂升后中断(比如备自投动作过程)会让基频幅值序列出现“先升后断”的形态。如果只检测单个幅值阶跃,很容易漏掉后面的中断。我后来改成对整条基频幅值曲线做分段多项式拟合,把每一段的幅值水平和持续时间都提取出来,才彻底解决这个问题。
5. 常见问题与排查实录
5.1 S变换常见问题速查
按我自己的经验,S变换实现和调参的过程里,大多数“结果不对”都集中在下面几个问题上:
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 时频图低频端一片模糊 | 高斯窗在低频过宽,窗内包含多个周期,时间分辨率丧失 | 适当减小低频起始频率,或提高采样率 |
| 谐波亮线偏粗、频率轴错位 | 频率点数nf不足,或基波频率未校正 | 增加nf到512,先FFT锁定实际基频 |
| 边缘出现大幅值假象 | 信号两端高斯窗不完整产生的边缘效应 | 分析前加窗或边缘截断,只取中间80%区域 |
| 计算耗时严重偏高 | 频率点数太多,或记录长度过长 | 限制fmax,缩短窗口长度,使用FFT加速版本 |
| 暂升/暂降起止时刻不准 | 差分阈值设置过低/过高 | 根据噪声水平自适应设定阈值,或先平滑基频幅值曲线 |
5.2 边缘效应:最容易被误解的“假故障”
边缘效应是S变换里几乎一定会遇到的问题。信号的起始和结束位置,高斯窗有一半是悬空的,窗内能量不足,S变换幅值会明显偏低,表现在时频图上就是左右两端各出现一条“暗带”。如果扰动恰好发生在信号尾部,这个暗带会严重干扰判断。
我的处理办法有两个:一是记录长度留足裕量,比如分析2秒窗,实际采集3秒,只取中间2秒做S变换;二是对提取到的幅值序列做首尾裁剪,判决时只用中间80%的数据。这样做以后,因为边缘效应导致的误判率能下降一大半。
5.3 三个项目里踩过的坑
第一个坑:采样率不匹配导致的谐波分析误差。有一段时间我图省事,直接用FFT结果判谐波,发现5次谐波幅值总是不稳定。后来查明白是采样率只有3200Hz,25次以上的谐波混叠进了低频段,把S变换谱线也污染了。现在我的原则是,能用6400Hz采样就不用32000Hz,谐波次数高需求时直接上12800Hz。
第二个坑:把所有扰动都按同一套频率范围做S变换。闪变分析需要精细的低频分辨率,谐波分析需要尽量高的高频上限。我一开始统一设成0到2500Hz,闪变边带在256个频点下分辨率不够,边带频率读数总是偏。后来改成两遍分析:第一遍低频细化(1到100Hz,256点),第二遍高频粗看(100到2500Hz,128点),闪变谐波两头都照顾到。
第三个坑:复合扰动分类时只看时频图特征,没考虑工况背景。同一种“谐波+暂降”的时频图,在电容投切和电机启动两种场景下,物理本质完全不同。如果只做信号处理不做场景联动,识别结果就只是“像什么样”,而不是“是什么”。后来我在特征向量里加入了事件发生前后的功率曲线、电流波形形态,准确率才真正到了能用的程度。
6. 一点个人心得
把整套S变换扰动识别流程搭起来之后,我的体感是:这方法最大的价值不是“更先进”,而是“更直观”。以前给非专业人员解释谐波和闪变的区别,要对着波形和频谱讲半天;现在直接把时频图摆出来,一条条亮线对应哪类扰动,几分钟就能讲明白。识别算法的调试也变得很简单,哪个特征判断错了,就去看对应频率区域时频图上的实际表现,马上能定位问题。
最后再分享一个小技巧:S变换的幅值矩阵不要只看二维热力图,我建议配合三维瀑布图一起看,把幅值作为高度轴,时间和频率作为水平轴。很多在热力图上看不出来的细微特征,比如闪变调制深度的渐变、复合扰动中两个事件的重叠区间,在三维视角下一目了然。这个方法在我排查复合扰动分类错误时帮过不少忙。
如果后续你想把这套流程做成自动识别系统,建议认真考虑在线滑窗S变换加轻量分类器的组合。先在线检测扰动事件发生时刻,再对事件片段做精细S变换和特征提取,计算量能大幅下降,识别可靠性和实时性也可以兼顾。这条路我自己还在持续优化中,之后有新的实践成果再来分享。