ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

小波包变换谐波检测:频带划分、Python实现与工程参数

小波包变换谐波检测:频带划分、Python实现与工程参数 简介这是一份面向电力系统谐波检测方向的学习者与工程技术人员的小波包变换应用参考文献聚焦小波包变换的基本原理及其在谐波电流检测中的具体用法。内容从多分辨率分析思想切入说明小波包变换相比普通小波变换可实现信号频带的均匀划分从而在高频段获得更精细的频率分辨率并在时域与频域同时具备良好的局部化能力同时结合仿真分析展示基波分量与高次谐波分量的分离过程可用于理解非平稳、暂态扰动信号的检测思路弥补傅里叶变换缺乏时域局部化、小波变换高频段划分不均匀的不足。资源包为单个PDF文件压缩后约225KB适合作为电力技术、系统开发方向的参考资料或课程文献阅读。目前已有108人学习下载可用于谐波检测方法入门、算法对比与仿真验证思路的梳理。1. 从一次频谱泄漏说起为什么谐波电流检测要换到小波包变换一台六脉波整流的变频器电流波形看上去规规矩矩可一旦负载在半工频周期内切换FFT 出来的频谱就糊成一片——3 次、5 次谐波峰值被抹平旁边还冒出一堆本不存在的谱线。问题不在算法实现而在前提FFT 默认分析窗内信号严格平稳而电力电子设备的开关动作恰好把这个前提打碎。小波包变换走的是另一条路。它对低频近似和高频细节同时做逐层二分把整个奈奎斯特频带均匀切成 2^J 段等宽子带3 次、5 次、7 次谐波可以各自落到独立节点上。再通过单节点重构把某段频率的时域波形从原始信号里单独抠出来幅值和相位都能求且不要求整段窗内平稳。这套方法主要服务三类人做电能质量监测装置的硬件与固件工程师做有源电力滤波器谐波指令提取的控制工程师以及把谐波含量写进电力系统模型预测控制目标函数的算法工程师。接下来的内容会沿着分解原理、频带划分、Python 最小实现、必调参数、交叉验证这条线走完每一段都能直接对照自己的采样率和工频改。2. 小波包变换的分解重构骨架与谐波频带划分2.1 小波包变换与小波变换的分岔点高频段也要继续二分多分辨率分析里尺度函数 φ(t) 和小波函数 ψ(t) 由一对正交镜像滤波器组确定φ(t) √2 · Σ h[n] · φ(2t − n) ψ(t) √2 · Σ g[n] · φ(2t − n)其中 g[n] (−1)^n · h[N−1−n]经典离散小波变换只对低频近似系数递归分解高频细节系数被直接丢弃不再细分。这在去噪场景里够用但对谐波检测是致命的3 次、5 次、7 次谐波全部位于中高频段只分低频等于把它们全甩进同一层细节系数里混在一起没法分离。小波包变换把分岔口补上——近似和细节两条分支都继续递归。设 d_j^n 表示第 j 层第 n 个节点的系数递推关系写成d_{j1}^{2n}[k] Σ_m h[m − 2k] · d_j^n[m] d_{j1}^{2n1}[k] Σ_m g[m − 2k] · d_j^n[m]第 j 层共产生 2^j 个节点每个节点占据等宽频带形成一棵满二叉树。这正是谐波检测想要的形状等间隔、无重叠、可寻址。重构是上述过程的逆运算从叶子节点逐层上采样、滤波、求和最终还原时域波形。一个常被忽略的细节是节点顺序。递归展开产生的自然访问顺序是格雷码序而不是频率从低到高的顺序。PyWavelets 中get_level(level, ordernatural)返回的路径按字母排列恰好对应频率递增MATLAB 的wpdec节点编号规则不同。跨平台对照代码时用频率范围反查节点路径别直接套编号。2.2 频带划分把采样率、分解层数和谐波次数对上节点带宽由采样率和分解层数唯一确定BW fs / 2^(J1)其中 fs 为采样率J 为分解层数。J 每加一层带宽减半同时节点数翻倍。以 fs 12800 Hz、J 6 为例BW 100 Hz第 6 层共 64 个节点覆盖 0 到 6400 Hz。50 Hz 工频每周期正好 256 个采样点用 Python 一行就能把频带表列出来# 计算第 level 层各节点的频率范围 def freq_bands(fs, level): bw fs / 2 ** (level 1) return [(k * bw, (k 1) * bw) for k in range(2 ** level)] for k, (lo, hi) in enumerate(freq_bands(12800, 6)): if hi 500: print(f节点 {k:2d} 频带 {lo:6.0f} - {hi:6.0f} Hz)运行结果给出低次谐波与节点的对应关系这正是选层数的依据节点序号频带范围对应谐波典型幅值量级00 – 100 Hz基波 50 Hz100 A1100 – 200 Hz3 次 150 Hz20 A2200 – 300 Hz5 次 250 Hz12 A3300 – 400 Hz7 次 350 Hz7 A4400 – 500 Hz9 次 450 Hz3 A一旦某个谐波次数刚好落在两节点交界处能量会被均分到相邻子带重构幅值直接腰斩。工程上的处理办法有两个把采样率调到让谐波落在节点中心或者把分解层数降低一档换取更宽的带宽容差。前者更干净但要和硬件采样时钟一起改。2.3 正交小波基怎么选消失矩、支撑长度与相位的三角关系小波基决定滤波器系数 h[n] 的长度和形状直接影响到两方面分解的频率选择性以及重构波形的失真程度。选择时盯住三个指标。消失矩阶数越高对多项式型平滑信号的逼近误差越小但对突变点的定位会变模糊紧支撑长度越短端点效应污染的范围越小但频带间的泄漏越明显谐波容易串到邻节点正交性保证分解后各节点能量严格守恒这对靠能量判断谐波含量是否可信的校验方法至关重要双正交小波不具备这一点。小波族正交性消失矩滤波器长度相位特性谐波检测适配度db4正交48非线性短窗、低次谐波db10正交1020非线性稳态谐波分离sym8正交816近似线性含相位测量需求coif5正交1030近似线性高精度幅值提取bior3.5双正交3/512/20线性需对称重构稳态谐波检测的默认起点是 db10 或 sym8。若下游要做相位补偿或无功计算sym 系列近似线性相位带来的时延一致性更好若只关心幅值db10 的频带隔离度更占优。3. 用 Python 与 PyWavelets 跑通谐波检测最小闭环3.1 构造一段含 3、5、7 次谐波的合成电流验证算法之前先造一条已知答案的信号。采样率取 12800 Hz工频 50 Hz观测时长 0.2 s 覆盖 10 个完整周期正好让端点效应对中间段的影响可以忽略。import numpy as np import pywt fs, f0, T 12800.0, 50.0, 0.2 # 采样率、工频、时长 t np.arange(0, T, 1 / fs) # 基波 3/5/7 次谐波各带初始相位 I (100.0 * np.sin(2 * np.pi * f0 * t) 20.0 * np.sin(2 * np.pi * 3 * f0 * t 0.30) 12.0 * np.sin(2 * np.pi * 5 * f0 * t - 0.80) 7.0 * np.sin(2 * np.pi * 7 * f0 * t 1.10)) print(len(I), 点, len(I) / fs, 秒)T取整数个工频周期是为了让拼接处连续避免人为引入阶跃。相位项不能省——实际现场各次谐波的相位由整流桥触发角和系统阻抗共同决定只测幅值不测相位的验证是半截验证。3.2 六层小波包分解与目标节点定位分解层数取 6对应 100 Hz 节点带宽3 次谐波落在节点 15 次在节点 27 次在节点 3各自独占一个子带。level 6 wp pywt.WaveletPacket(dataI, waveletdb10, modesymmetric, maxlevellevel) # 只取第 level 层节点natural 顺序即频率从低到高 leaves wp.get_level(level, ordernatural) bw fs / 2 ** (level 1) print(f节点带宽 {bw:.0f} Hz共 {len(leaves)} 个节点) # 3/5/7 次谐波对应的节点序号 for order in (3, 5, 7): f order * f0 k int(f // bw) print(f{order} 次 {f:.0f} Hz - 节点 {k} {leaves[k].path})参数上modesymmetric表示镜像延拓适合首尾不严格闭合的实测波形如果确定信号在窗内严格周期改成modeperiodization能让端点失真更小。maxlevellevel必须显式指定否则 PyWavelets 会按信号长度自动截断节点数和频带表就对不上了。3.3 单节点重构与谐波幅值求取拿到节点系数不能直接读幅值——小波包系数是滤波器组的输出带抽取同一节点内还叠加了镜像频率成分直接做频谱分析会看到折叠。正确做法是把其余节点系数清零只保留目标节点再整树重构回时域。def reconstruct_node(wp, level, path): 只保留 path 节点系数其余清零重构该频带时域波形 zeros np.zeros(wp.data.size) wp_zero pywt.WaveletPacket(datazeros, waveletwp.wavelet, modewp.mode, maxlevellevel) wp_zero[path].data wp[path].data.copy() # 搬运目标节点系数 return wp_zero.reconstruct(updateTrue) def node_amplitude(rec, fs, f0, cycles8): 取中段整数个工频周期按正弦量关系换算峰值 n_per int(round(fs / f0)) half n_per * cycles // 2 mid len(rec) // 2 seg rec[mid - half: mid half] return np.sqrt(2) * np.sqrt(np.mean(seg ** 2)) targets {3: 1, 5: 2, 7: 3} for order, node_k in targets.items(): rec reconstruct_node(wp, level, leaves[node_k].path) print(f{order} 次谐波重构幅值 {node_amplitude(rec, fs, f0):7.2f} A)取中段是有讲究的第 6 层每个叶子节点只有 40 个系数db10 滤波器长度 20端点附近受延拓影响严重的系数按比例不小。用中段 8 个完整周期计算 RMS再乘 √2 换算峰值能把边界污染压到可以忽略。3.4 用 FFT 结果做一次独立对照算法自证不可靠得找第二个工具交叉验证。对同一段信号做加汉宁窗的 FFT读取各次谐波谱线高度。from numpy.fft import rfft, rfftfreq win np.hanning(len(I)) X np.abs(rfft(I * win)) / (np.sum(win) / 2) # 汉宁窗幅值修正 freqs rfftfreq(len(I), 1 / fs) for order in (3, 5, 7): f order * f0 idx np.argmin(np.abs(freqs - f)) print(f{order} 次 FFT 幅值 {X[idx]:7.2f} A)谐波次数设定值小波包重构FFT 对照相对偏差3 次20.00 A20.1 A 量级20.0 A 量级 1%5 次12.00 A12.1 A 量级12.0 A 量级 1%7 次7.00 A7.1 A 量级7.0 A 量级 2%偏差主要来自频带不完全平坦和端点残留。若某一项偏差超过 5%优先检查谐波频率是否压在节点边界其次看小波基的滤波器长度是否远小于窗长。4. 谐波检测落地必须调的四个参数与三类典型坑4.1 分解层数等宽频带的甜蜜点在哪里层数由目标谐波次数和采样率共同反推。先把要分离的最高次谐波频率算出来再要求相邻两次谐波落在不同节点即 BW ≤ f0代入 BW fs / 2^(J1) 得 J ≥ log2(fs / (2 · f0)) − 1。fs 12800、f0 50 时J ≥ 6。分解层数 J节点带宽3 次 150 Hz5 次 250 Hz7 次 350 Hz端点污染区相对长度4400 Hz节点 0节点 0节点 0短5200 Hz节点 0节点 1节点 1中6100 Hz节点 1节点 2节点 3长750 Hz压在节点边界节点 4/5 交界节点 6/7 交界很长层数不是越大越好。每加一层叶子节点系数长度减半受端点延拓影响的系数占总数的比例翻倍0.2 s 的窗在第 7 层只剩 20 个系数滤波器长度就已经 20整段都被污染。J 6 是这套参数下能兼顾分离度和边界安全的档位。4.2 边界延拓symmetric 与 periodization 的取舍延拓模式决定首尾之外假设了什么。实测波形首尾不闭合时symmetric镜像延拓不会引入跳变如果信号确实严格周期periodization首尾相接端点失真最小。判断依据可以直接算首尾点的差值# 首尾不连续度越小越适合 periodization jump abs(I[0] - I[-1]) / np.max(np.abs(I)) print(f首尾相对跳变 {jump:.4f}) mode periodization if jump 0.02 else symmetric0.02 是个经验阈值。工业现场因为负载波动和采样触发抖动首尾几乎不可能严格闭合所以默认symmetric更稳。注意切换延拓模式后所有节点的系数值都会变之前标定好的阈值需要重新跑一次别直接沿用。4.3 坑一直接对节点系数做 FFT 会看到镜像谱正交小波包分解含抽取环节节点系数序列的采样率是 fs / 2^j其频谱同时包含目标频带和一段镜像频带。对系数序列直接做 FFT会在错误的位置看到一根假谱线。判断方法把假谱线的频率按节点采样率折算若正好落在 [0, fs/2^(j1)] 之外就是镜像。规避方式固定成两条——要么只做单节点重构看时域波形要么在计算能量时用系数平方和二者都不需要展开系数频谱。4.4 坑二暂态谐波和稳态谐波不能用同一套阈值稳态谐波幅值变化慢窗长可以取长暂态谐波持续几十毫秒窗长一大就被平均掉。工程上的做法是并行跑两条通道长窗10 个工频周期出稳态含量短窗1 到 2 个周期盯 5 次、7 次所在高频节点的能量突变用相邻两窗能量比作为触发条件。def transient_trigger(wp_now, wp_prev, node_k, ratio1.5): 高频节点能量突增触发暂态标志 e_now np.sum(wp_now[leaves[node_k].path].data ** 2) e_prev np.sum(wp_prev[leaves[node_k].path].data ** 2) return e_now ratio * e_prev, e_now / e_prev比值 1.5 起步风电场和电弧炉场景建议放宽到 2.0避免频繁误触发。4.5 坑三实时性预算被忽略在窗长之外0.2 s 窗每滑动一次做 6 层分解运算量随节点数指数增长。在 1 kHz 级主频的嵌入式 DSP 上一次完整六层分解加三次单节点重构通常在毫秒量级看起来轻松。但重构要对每个目标谐波各跑一遍整树逆变换3 次、5 次、7 次就是三棵树。优化方向是共享中间层三次重构共用前 4 层的上采样结果只在最后两层分叉实测能省掉近一半计算。这一点在选型阶段就要写进预算否则上线后采样率一提就崩。5. 检测结果的两级交叉验证与向下游控制器的传递5.1 用帕塞瓦尔能量守恒做一次快速自检正交小波包分解满足能量守恒各层全部节点系数平方和等于原信号能量。这条性质可以当免费的自检工具用不需要额外数据。e_sig np.sum(I ** 2) level_energy 0.0 wp_chk pywt.WaveletPacket(dataI, waveletdb10, modesymmetric, maxlevel6) for node in wp_chk.get_level(6, ordernatural): level_energy np.sum(node.data ** 2) print(f信号能量 {e_sig:.3f}) print(f节点能量 {level_energy:.3f}) print(f相对误差 {(level_energy - e_sig) / e_sig * 100:.4f}%)相对误差在 1e-10 量级才算正常。若明显偏大八成是maxlevel被自动截断分解没到指定层数节点没取全。这个检查比逐次谐波对幅值快得多适合放进装置上电自检流程。5.2 参照 MATLAB 潮流计算复核谐波电流注入小波包给出的是监测点实测谐波电流要判断它是否合理可以反推各次谐波等效电流源代回网络做一次谐波潮流计算看算出的节点电压畸变率与现场电能质量表读数是否吻合。MATLAB 侧的最小验证脚本如下% 把 Python 侧测得的谐波电流幅值和相位拼成复数相量 orders [3 5 7]; Ih [20.1 12.1 7.1]; % 幅值 A ph [0.31 -0.79 1.12]; % 相位 rad Iinj Ih .* exp(1j * ph); % 网络等效谐波阻抗按次谐波频率逐点计算 Zh zeros(1, numel(orders)); for k 1:numel(orders) f orders(k) * 50; Zh(k) 0.02 1j * 2 * pi * f * 0.8e-3; % R jωL end Vh Iinj .* Zh; % 该节点各次谐波电压 THD sqrt(sum(abs(Vh).^2)) / 220 * 100; fprintf(推算电压畸变率 %.2f%%\n, THD);等效阻抗里的电阻和电感要用现场短路容量折算不能照搬模板值。这一步的价值在于小波包检测只保证测到了什么谐波潮流计算回答这个注入量在网络里合不合理两者对上检测结果才算闭环。5.3 谐波幅值作为电力系统模型预测控制的状态输入电力系统模型预测控制把未来若干步的预测输出与参考值做差滚动求解最优控制量。谐波电流指令正是其中一类需要约束的量把 5 次、7 次谐波幅值作为状态向量的一维控制周期与谐波检测的窗移对齐检测结果按 100 ms 一个点推送控制器就能把谐波含量写进代价函数。对齐时有两个细节一是检测窗长必须是控制周期的整数倍否则每次更新看到的时间区间都不同控制器会出现周期性抖动二是检测结果属于滞后量要按窗内平均时刻打时间戳而不是按计算完成时刻否则相当于给系统额外加了一段纯延迟。把这两个时间戳搞对谐波抑制的带宽能明显提升搞错的话控制器越调越振。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进