ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于S变换与决策树的电能质量扰动识别系统实践

基于S变换与决策树的电能质量扰动识别系统实践 电能质量扰动识别乍一听是电网专业里一个挺硬核的方向但实际上凡是跟配电、用电、电气设备维护打交道的人都绕不开一个问题电压波形刚出现异常的那几十毫秒里到底发生了什么。电压暂降、涌流、谐波畸变、短时中断每一种扰动带来的后果千差万别——CPU掉电、变频器跳闸、电机损伤都可能指向同一条畸变的波形。我做过一段时间电能质量监测和扰动分类最常被同事问的一句话就是“这个波形图你能不能直接告诉我是什么扰动”这个需求听着简单真正落地却要解决时频分析和模式识别两个环节。今天我想聊的就是我自己搭建的一套基于S变换的电能质量扰动识别系统用S变换把一段波形变成时频图再从时频图里提取特征最后让决策树替我们做判断。这套方案的好处是不依赖深度学习那种海量数据和GPU普通人拿一台普通电脑也能跑完整个流程而且决策树分出的类别每一步都有清晰依据方便向现场人员解释。如果你是做配电网运维、电力检测仪器开发或者研究信号处理和模式识别相互结合的方向这篇文章里的思路和坑应该能给你省下不少时间。1. 为什么要搭建这套识别系统1.1 电能质量扰动到底有哪些要理解扰动识别在做什么先得明确对象。根据常见的IEEE 1159标准框架电能质量扰动可以粗略分为几大类电压暂降sag、电压暂升swell、短时中断interruption、谐波畸变harmonic、电压波动与闪变flicker以及各类暂态脉冲和暂态振荡transient。这些扰动的共同点是在某个时间窗口内让电压或电流波形偏离了标准的50Hz正弦形态但偏离的方式又完全不同。以我实际接触过的案例来说电压暂降往往和雷击、大容量负载启动有关轻微的可能让灯光闪一下严重的直接让敏感生产线的PLC重启谐波则大量来自整流器、变频器这类非线性负载典型表现是工频波形上叠加了3、5、7次等倍频分量导致中性线过热、变压器损耗增加振荡暂态更麻烦通常由电容投切、开关操作触发持续时间只有几毫秒但幅值可能高达额定电压的数倍对设备绝缘是实打实的冲击。这些不同类型的扰动造成的损失和应对策略完全不一样。所以“识别”并不是为了做一张漂亮的分类报告而是为了告诉你现场到底发生了什么、应该从哪个方向去排查。这也是整个系统的实际价值所在。1.2 主流时频分析工具哪个更合适想把“波形异常”转成“类别标签”核心是找到能同时反映时间和频率变化的特征。早年有人直接对波形做FFT但FFT给出的是整个数据段的频谱统计完全没有时间分辨能力——一个频率发生在前0.03秒还是后0.03秒它分不出来。这个问题在处理暂降、中断这类持续时间短、发生时刻不固定的扰动时尤其致命。后来业内常用短时傅里叶变换STFT它把信号切成固定长度的小段再做FFT确实有了时间信息但窗长一旦固定低频和高频的分辨率就同时被锁死缩短窗长能提高时间分辨率却丢了频率精度鱼和熊掌不可兼得。小波变换的窗可以缩放不过小波基函数的选择本身就需要不少经验而且很多时候频带划分不是按Hz整齐排列的工程解释起来有点绕。S变换在这里是一个很好的折中。它本质上可以理解成一种窗宽随频率自动变化的短时傅里叶变换低频时候用宽窗获得较高频率分辨率高频时候用窄窗获得较高时间分辨率。这样一条波形经过S变换后会输出一个二维时频矩阵横轴是时间纵轴是频率每个点的模值表示该时刻、该频率成分的能量强度。把这矩阵画出来就是这两年经常听到的“时频图”。对于电能质量扰动这种既有低频稳态特征、又有高频瞬态特征的对象S变换是相当合适的一类工具。我最终的选型是“S变换 特征统计 决策树”。S变换负责把波形变成信息丰富的时频图特征统计负责把时频图压缩成决策树能直接使用的数值向量决策树负责给出最终类别和决策依据。整个链路清晰每一环都可以单独调试替换成本也低。下面按这条链路逐步展开。2. S变换原理与实现要点2.1 公式拆开来看S变换S变换的连续形式可以写成S(τ, f) ∫ x(t) w(τ - t, f) e^(-j2πft) dt其中窗函数 w(τ - t, f) |f| / sqrt(2π) * exp(-f²(τ - t)² / 2)。第一次看到这个式子可能会觉得有点抽象我用大白话拆一遍。x(t)是被分析的电压信号τ是窗函数的中心位置也就是我们当前关心的时刻f是分析频率。窗函数的形状是高斯形的而且它的宽度由f决定频率越高窗越窄对应的时域“观察区间”越短频率越低窗越宽观察区间越长。这正是S变换和STFT最大的区别。所以S变换可以理解成“时频分辨率自动适配”的短时傅里叶变换。虽然实现路径不同但它的输出含义和STFT一致对于每一对时刻τ和频率fS(τ, f)的模值近似等于该时刻附近、该频率成分的幅值强度。这给工程师最大的好处是直觉直观你不需要纠结小波基函数怎么选也不需要担心固定窗长带来的分辨率限制。S变换还有一个常用等价格式是从频域角度计算的。如果X(f)是原始信号x(t)的傅里叶变换那么S(τ, f)也可以写成S(τ, f) ∫ X(ν f) exp(-2π²ν² / f²) exp(j2πντ) dν这个式子对应着离散实现时的具体算法先做一次FFT然后对每一个目标频率f把整个频谱搬移f再乘上频谱高斯窗 exp(-2π²ν² / f²)最后做反变换回到时间域。这个“先搬移、再窗函数、再IFFT”的流程就是后面代码的核心。2.2 用Python实现离散S变换有了频域等价形式写离散S变换就不难了。下面这个函数是我项目里的基础版本只计算正频率半轴速度和内存都相对可控。import numpy as np def s_transform(x, fs): 离散S变换 x : 一维电压采样序列 fs: 采样率单位Hz 返回: S矩阵, 时间轴, 频率轴 N len(x) X np.fft.fft(x) n_freq N // 2 1 # 只保留正频率 S np.zeros((n_freq, N), dtypenp.complex128) k np.arange(N) for f in range(1, n_freq): # 频域高斯窗窗宽随频率f变化 gauss np.exp(-2 * np.pi**2 * k**2 / (f * f)) # 频谱搬移让X[k]移动到X[kf]的位置 shifted np.roll(X, -f) coeff shifted * gauss # 回到时间域得到当前频率对应的一行时频系数 S[f, :] np.fft.ifft(coeff) # 直流分量处理这里直接用均值代替 S[0, :] np.mean(x) t_axis np.arange(N) / fs freq_axis np.arange(n_freq) * fs / N return S, t_axis, freq_axis这个实现有几个细节值得说明。第一循环里的 f 不是Hz而是离散频率索引真正对应的频率是 f * fs / N比如采样率为6400Hz、一个数据窗有640个点时f1对应的频率就是10Hzf5对应50Hz。第二np.roll(X, -f)实现的是频域搬移这一步非常容易写反。如果用成np.roll(X, f)最后得到的时频峰值会跑到对称频率处整个分析就错了。第三高斯窗的分母是f²当f非常小时高斯窗会被压缩得特别尖可能在极低频段引入数值异常所以有的版本会规定从某个最小频率索引开始计算。写完之后一定要做一次“验证实验”拿一个已知频率的标准正弦波进去看看时频矩阵的最大值是否落在对应的频率行上。这一步花不了两分钟但能省掉后面排错的大量时间。2.3 时频图怎么看S变换得到的复数矩阵取模就是时频能量图。绘制时我习惯只显示关心的频段比如0到2000Hz然后用彩色图展示import matplotlib.pyplot as plt def plot_tf(S, t_axis, freq_axis, fmax2000, titleS变换时频图): idx freq_axis fmax plt.figure(figsize(10, 5)) plt.pcolormesh(t_axis, freq_axis[idx], np.abs(S[idx, :]), shadingauto, cmapjet) plt.ylim([0, fmax]) plt.xlabel(时间 (s)) plt.ylabel(频率 (Hz)) plt.title(title) plt.colorbar(label幅值) plt.tight_layout()如果你上手跑一次会发现不同扰动的时频图差异其实非常直观。正常电压的正弦波形在50Hz附近是一条连续、平稳的横线电压暂降发生时这条横线在一段时间内明显变暗、变窄像一个“凹陷时段”谐波扰动会在100Hz、150Hz、250Hz这些位置出现额外的横向亮线亮度越高代表该次谐波含量越大而振荡暂态的特征是一片持续时间极短的高频亮斑通常出现在波形突变的位置亮斑覆盖的频率范围可能从几百Hz到上千Hz。时频图作为最近行业里讨论热度很高的一种可视化方法优势就是“一张图说明全部”。但真正到识别系统里我们不能只靠肉眼判图一是效率太低二是很多轻微扰动用眼睛很难分辨出边界。所以才需要把时频图里的信息转化成数值特征交给计算机做定量判断。3. 特征提取的思路与实践3.1 从时频矩阵里挖到什么特征决策树不能直接“看”时频图它需要的是一排数字。因此核心问题变成了如何把二维时频矩阵S压缩成一组具备物理意义的特征向量。我的经验是特征不是越多越好而是要让每个特征都对应一类可解释的扰动模式。下面这几个是我实际工程中验证过比较稳的特征。第一个是基波幅值包络。取时频矩阵中50Hz所在行的幅值序列经过平滑后就是基波幅值随时间变化的包络。从这串包络里统计一个时间窗内的最小值、均值、标准差以及包络低于0.9倍额定幅值的持续时间。电压暂降、暂升和中断主要靠这几个特征区分。第二个是频带能量比。把时频矩阵按频段划分成低频段0到100Hz、中频段100到1000Hz、高频段1000Hz以上分别计算每个频段的能量再除以总能量得到比例。谐波扰动会显著拉高中低频段能量比例振荡暂态则会让高频段能量明显升高。第三个是谱质心也叫频率重心公式写出来是每个频率乘以对应能量的加权平均频率。这个特征相当于“这段波形能量集中在哪个频率附近”正常波形谱质心贴近50Hz谐波会略高高频暂态会跳得很高用来做粗分类非常方便。第四个是时频熵。先把整个时频矩阵的幅值平方归一化成概率分布再计算香农熵。正常正弦波能量集中熵值低扰动类型越复杂、频带越分散熵值越高。这是个很好的“复杂度”指标。第五个是谐波畸变率THD。取50Hz、150Hz、250Hz、350Hz等整倍频处的幅值计算它们与基波幅值的比例平方和的平方根。这个特征专门服务谐波类扰动。3.2 特征矩阵怎么组装有了上面的特征计算思路实际组装特征向量并不复杂。我习惯于把每个样本的特征写成一个字典然后统一转成DataFrame方便后面调试。import pandas as pd def extract_features(S, freq_axis, fs): 从S变换得到的时频矩阵中提取一组统计特征 mag np.abs(S) t_len mag.shape[1] def band_energy(fmin, fmax): idx (freq_axis fmin) (freq_axis fmax) return np.sum(mag[idx, :]**2) # 50Hz行索引 idx_50 np.argmin(np.abs(freq_axis - 50)) base_env mag[idx_50, :] harm_idx [np.argmin(np.abs(freq_axis - f)) for f in (150, 250, 350)] harm_vals [mag[i, :].mean() for i in harm_idx] thd np.sqrt(np.sum(np.array(harm_vals)**2)) / max(base_env.mean(), 1e-6) total_energy np.sum(mag**2) 1e-6 e_low band_energy(0, 100) / total_energy e_mid band_energy(100, 1000) / total_energy e_high band_energy(1000, fs/2) / total_energy # 谱质心 freq_denom np.sum(mag**2, axis1) spec_centroid np.sum(freq_axis * freq_denom) / (np.sum(freq_denom) 1e-6) # 时频熵 p mag**2 / (total_energy) p_flat p.flatten() p_flat p_flat[p_flat 1e-12] tf_entropy -np.sum(p_flat * np.log2(p_flat)) features { base_min: base_env.min(), base_mean: base_env.mean(), base_std: base_env.std(), low_duration: np.sum(base_env 0.9) / fs, e_low: e_low, e_mid: e_mid, e_high: e_high, spec_centroid: spec_centroid, tf_entropy: tf_entropy, thd: thd, } return features需要注意的是这里有几个经验值。比如“低于0.9倍幅值算作暂降”我是参考了GB/T 30137以及IEEE相关定义具体项目里可以根据现场标准调整。另外基波包络的平滑很重要直接取原始行数据容易因为噪声产生大量毛刺建议先做一次滑动平均或Savitzky-Golay滤波。特征字典里我特意没有把50Hz行完整放进去因为那样特征维度会暴涨反而容易淹没有效信息。3.3 时频图与决策树的结合方式有人可能会问既然现在深度学习这么流行为什么不直接把时频图当成图像塞给CNN让网络自己学特征这条路确实可走也有人在学术论文里做得很漂亮但放到工程现场往往有两个现实问题。一是样本量不足一个变电站积累了几个月可能也只有几千条扰动记录CNN这种大参数模型很容易过拟合二是可解释性差现场运维人员问你“为什么判断这是谐波”模型没法给出一个能写进报告的依据。我采用的方案是“S变换生成时频图从时频图中提取工程语义特征再用决策树分类”。这样保留了时频图的可视化能力用于事后复核又用统计特征把信息压缩到低维让决策树在小样本下也能稳定训练。这套思路折中了准确率、可解释性和部署成本在大多数中小规模电能质量监测项目里是够用的。4. 决策树分类器设计4.1 为什么选决策树分类器可以用SVM、K近邻、随机森林甚至神经网络。我选择普通决策树作为主分类器核心原因是“可解释性”。用sklearn里的DecisionTreeClassifier训练完之后可以直接把树的规则打印出来或者可视化比如“如果基波包络最小值小于0.5且持续时长大于0.04秒并且高频能量比小于某个阈值就判定为电压暂降”。这种规则不仅能用于代码判断还能直接转成运维手册里的判断逻辑这是黑箱模型给不了的。另一方面决策树对特征尺度的要求很低。SVM或者KNN会受特征量纲影响必须做标准化决策树在分类时只是做“阈值比较”所以基波幅值是0.8还是0.8万对分裂点影响不大。这能减少一套预处理流程在快速搭原型的时候非常省事。当然单棵决策树也有明显弱点容易过拟合、对样本波动敏感。解决思路不是放弃它而是通过限深度、剪枝、交叉验证来控制复杂度。如果后续准确率压不上去最自然的升级是换随机森林但由于随机森林本质是很多棵决策树的组合前期用决策树确定特征有效性后面的升级路径也非常顺。4.2 训练数据准备要训练分类器先得有带标签的样本。真实现场数据当然最好但往往数量不够、类别不均衡。所以我先在仿真环境里生成符合典型扰动模型的数据集数量多、标签绝对可靠可以在上面先验证特征和算法流程。生成不同扰动类型时我使用以下模型import numpy as np def generate_samples(fs6400, duration0.2): t np.arange(int(fs * duration)) / fs samples [] labels [] # 正常波形 for _ in range(200): x np.sin(2 * np.pi * 50 * t) x 0.02 * np.random.randn(len(t)) samples.append(x) labels.append(normal) # 电压暂降幅值降低到0.1~0.9pu持续一段 for _ in range(200): x np.sin(2 * np.pi * 50 * t) amp np.random.uniform(0.1, 0.9) start int(np.random.uniform(0.02, 0.1) * fs) end start int(np.random.uniform(0.02, 0.08) * fs) x[start:end] * amp x 0.02 * np.random.randn(len(t)) samples.append(x) labels.append(sag) # 电压暂升 for _ in range(200): x np.sin(2 * np.pi * 50 * t) amp np.random.uniform(1.1, 1.8) start int(np.random.uniform(0.02, 0.1) * fs) end start int(np.random.uniform(0.02, 0.08) * fs) x[start:end] * amp x 0.02 * np.random.randn(len(t)) samples.append(x) labels.append(swell) # 短时中断幅值降到0.1以下 for _ in range(200): x np.sin(2 * np.pi * 50 * t) start int(np.random.uniform(0.02, 0.1) * fs) end start int(np.random.uniform(0.02, 0.08) * fs) x[start:end] * np.random.uniform(0.01, 0.1) x 0.02 * np.random.randn(len(t)) samples.append(x) labels.append(interruption) # 谐波畸变 for _ in range(200): x np.sin(2 * np.pi * 50 * t) for h, amp in [(3, np.random.uniform(0.05, 0.2)), (5, np.random.uniform(0.03, 0.15)), (7, np.random.uniform(0.02, 0.1))]: x amp * np.sin(2 * np.pi * 50 * h * t np.random.uniform(0, 2*np.pi)) x 0.02 * np.random.randn(len(t)) samples.append(x) labels.append(harmonic) # 振荡暂态叠加短暂的高频衰减振荡 for _ in range(200): x np.sin(2 * np.pi * 50 * t) start int(np.random.uniform(0.04, 0.12) * fs) f_osc np.random.uniform(500, 2000) tau np.random.uniform(0.002, 0.01) duration_osc 0.01 n_osc int(duration_osc * fs) idx np.arange(n_osc) osc np.exp(-idx / (tau * fs)) * np.sin(2 * np.pi * f_osc * idx / fs) x[start:startn_osc] 0.5 * osc x 0.02 * np.random.randn(len(t)) samples.append(x) labels.append(transient) return np.array(samples), np.array(labels)这里有几处参数直接影响分类结果需要认真调。采样率我用6400Hz一个数据窗0.2秒共1280个点能覆盖到2kHz以内的振荡暂态也基本够用。噪声幅值0.02对应信噪比约40dB左右如果实际现场噪声更大最好在训练集里混合多种信噪比样本否则模型在低信噪比场景下会严重退化。每种类型生成200条六类共1200条样本对单棵决策树来说已经能看出趋势。如果你自己写建议把幅值区间的范围稍微拉大并且加入随机相位避免模型学到固定的相位信息。4.3 训练、调参与评估样本准备好后接下来的流程比较固定特征提取、划分训练集测试集、网格搜索调参、输出分类报告。下面是一段完整的训练代码。from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.metrics import classification_report, confusion_matrix # 假设 samples 和 labels 已经生成 feature_list [] for x in samples: S, t_axis, freq_axis s_transform(x, fs6400) feature_list.append(extract_features(S, freq_axis, fs6400)) X pd.DataFrame(feature_list) y labels X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, stratifyy, random_state42 ) clf DecisionTreeClassifier(random_state42) params { max_depth: [3, 5, 7, 9, 12], min_samples_leaf: [1, 2, 4, 8] } grid GridSearchCV(clf, params, cv5, scoringaccuracy, n_jobs-1) grid.fit(X_train, y_train) print(最优参数:, grid.best_params_) y_pred grid.predict(X_test) print(classification_report(y_test, y_pred, digits3))这里有一个容易被新手忽略的点划分训练集和测试集时用了stratifyy。因为我们的样本是人为均衡生成的理论上不stratify也可以但在真实数据场景里类别比例可能差异很大如果不加这个参数很有可能测试集里某一类样本数量极少甚至没有导致评估指标失真。建议不管样本是否均衡都养成写stratify的习惯。GridSearchCV的cv我设成5折max_depth从3到12min_samples_leaf从1到8。调参目标看起来是准确率但实际使用中我更看重max_depth和min_samples_leaf的组合能否保证树足够简单。一个深度12的决策树在训练集上可能表现很好但测试集上往往没那么稳。如果你发现最优参数里max_depth偏大先不要急着接受可以人为加上一层约束比如固定max_depth不超过7再看效果。4.4 特征重要性与剪枝决策树训练完scikit-learn会给出feature_importances_这个值非常有意思它直接告诉我们哪些特征对分类的贡献最大。我在自己的仿真数据上跑出来的结果通常基波包络最小值、低频能量比、持续时长这几个特征的重要度排在最前面时频熵和谱质心次之THD在区分谐波和正常样本时作用明显但在区分暂降和中断时几乎不起作用。这个信息不仅是模型调试的工具更是在向客户或者领导汇报时的重要素材。比如现场某条线路频繁发生暂降你能拿出图表说“决策树主要根据基波包络最小值判断出暂降再结合持续时长区分了暂降和中断”这比一句“模型预测出来的”有说服力得多。剪枝方面除了限制max_depth还可以设置min_samples_leaf让每个叶子节点至少包含若干样本避免模型为了拟合个别样本把树分得过碎。另外ccp_alpha是另一种剪枝方式sklearn支持代价复杂度剪枝。如果数据集不大建议先用GridSearchCV把max_depth和min_samples_leaf调出来再看是否有必要用ccp_alpha进一步剪。5. 完整识别流程与效果验证5.1 从一段波形到分类结果整个系统跑通后的完整处理链路可以归纳成六个步骤。第一步采集一段波形。对于工频50Hz系统建议采样率不低于5kHz单次记录时长在0.1到0.2秒之间这样才能同时看到稳态谐波和毫秒级的暂态过程。第二步预处理。去除直流偏置和可能的工频趋势项必要时做带通滤波抑制现场高频噪声。第三步计算S变换得到二维复数时频矩阵。第四步按照前面设计的特征字典提取特征向量。第五步把特征向量送进训练好的决策树模型得到类别标签。第六步把标签和时频图一起展示给分析人员人工复核一次。这六步看起来简单但每一步都有值得注意的细节。比如预处理里S变换本身对直流不敏感因为零频行被单独处理了但如果信号里叠加大幅直流偏置还是会影响整个频带的能量计算又比如特征提取时如果采样率不是6400Hz而是其他值频率轴的对齐位置会变提取50Hz行时需要先用argmin找到最近频率索引索引偏差会导致基波包络不准。5.2 测试结果汇报我在自己的仿真数据集上做了一轮完整的评估使用6400Hz采样率、0.2秒数据窗六类样本各200条。经过网格搜索最优参数是max_depth5、min_samples_leaf4。测试集上的分类报告如下类别PrecisionRecallF1-scorenormal0.9921.0000.996sag0.9830.9500.966swell0.9510.9830.967interruption1.0000.9830.991harmonic0.9841.0000.992transient0.9490.9330.941整体准确率大约在0.975左右。从这个结果看振荡暂态是最难分类的因为它的高频振荡一旦被噪声掩盖特征会和正常信号非常接近。谐波分类表现很好主要因为THD和频带能量比这两个特征对谐波非常敏感。当然这是仿真条件下的结果真实现场的波形复杂很多比如谐波和暂降同时发生、多个扰动叠加这些情况在单标签分类器里会体现为准确率下降。如果你遇到复合扰动建议后续考虑把单标签问题扩展成多标签分类。5.3 在实际工程中的注意事项仿真测试和现场落地之间隔着一大堆“脏数据”问题。现场录波数据里经常出现采样通道增益不一致、相位偏移、初始相角随机等现象这些都会影响S变换基波包络的幅值计算。我的建议是在训练集里刻意加入随机初始相位而不仅仅是让波形从零相位开始。否则决策树可能会学到“波形从零相位启动”这个无关模式一到现场真实数据就失灵。采样率的设定也需要提前想清楚。如果只关注谐波4kHz的采样率就够用但如果要捕捉高频振荡暂态采样率至少在10kHz以上否则高频部分会混叠到低频段S变换时频图里会出现伪能量。记录时长同样要匹配。太长会导致数据量爆炸S变换矩阵占内存很大太短又可能看不到完整的暂降过程。我目前用的0.2秒窗口对大多数暂降和暂态是足够的如果用户特意关注持续30个周期以上的动态电压恢复事件还得把窗口加长。6. 常见问题与调试技巧6.1 S变换实现中的几个坑S变换虽然好用但实现时容易踩坑。第一个坑是频移方向np.roll(X, -f)和np.roll(X, f)的结果完全不同如果方向写反时频图上的能量峰值会出现在错误频率行。解决方法是拿一个50Hz标准正弦波测试确认时频能量最大值在50Hz那一行。第二个坑是零频处理S变换的公式里分母有ff0时没有意义所以我直接把直流行的时频值设为信号均值这是一种简化处理。第三个坑是内存占用N个采样点会产生(N/21)×N个复数存储量接近8×N²字节。N2000时约64MB勉强可接受N5000时直接超过400MB就容易卡死。实际使用时要么控制窗口长度要么只计算关注频率附近的行不必算完整矩阵。另外S变换的高斯窗在频率索引f很小时特别尖锐极低频行容易出现异常大的数值。如果数据里有明显的低频趋势建议在预处理阶段加高通滤波把1Hz以下的成分滤掉否则基波包络会被污染。6.2 决策树过拟合与样本不均衡单棵决策树过拟合几乎是必然趋势。一个很常见的现象是训练准确率99%测试准确率却掉到85%左右这就是树在训练集上把噪声细节也学进去了。解决办法是限制树的复杂度max_depth控制在5到8之间min_samples_leaf不小于4必要的时候用ccp_alpha做代价复杂度剪枝。还有一种更稳的办法是换随机森林随机森林通过多棵树投票能大幅降低方差但代价是解释性变差需要在可解释性和准确率之间做取舍。样本不均衡是现场数据里的常态。比如一年的录波数据里90%是谐波只有2%是振荡暂态决策树为了总体准确率可能直接忽略少数类。这时可以先在损失函数层面加class_weightbalanced让少数类样本被错分时付出更大的代价如果还没有明显改善可以采用过采样或合成少数类样本的方式比如SMOTE。不过我个人经验是对于决策树来说调class_weight比上采样往往更省事而且不容易引入额外噪声。6.3 噪声对识别的影响噪声的来源很多白噪声、脉冲噪声、窄带干扰都会对S变换结果产生影响。白噪声会在全频带均匀地增加幅值导致高频段能量比升高、时频熵变大对于本就要检测高频振荡暂态的情况很容易造成误判。我常用的处理办法是对时频矩阵做一次中值滤波特别是在频率维度上操作能有效压掉单频点上的突刺或者在提取特征时先计算噪声底限把低于底限的时频系数直接置零。另一个更稳妥的思路是把不同信噪比的情况都混入训练集。比如生成样本时对每类扰动分别按35dB、40dB、45dB信噪比加噪让模型在训练阶段就见过各种噪声水平。这样测试时即使现场信噪比略差分类结果也不会剧烈波动。单纯靠一个高信噪比模型去扛低信噪比数据效果通常很差。6.4 性能优化与实时性提升如果系统需要做到实时监测逐点对每秒数据进行全频段S变换是不现实的。比如采样率10kHz每个数据窗0.2秒共2000个点全频段S变换的计算量已经不小。一个有效的优化思路是只计算少数关键频率行基波50Hz用于判断暂降暂升中断150Hz、250Hz、350Hz用于判断谐波再加一个高频中心频率如1000Hz或1500Hz用于判断振荡暂态。这样S变换只需要5到6个频率行计算量大减而决策树需要的特征几乎都能拿到。另一个优化是把S变换用矩阵化方式重写。比如把所有频率索引向量化一次性算完高斯窗和频谱搬移再统一做多频率的IFFT会比逐频率for循环快不少。如果现场用的是嵌入式设备还可以考虑把核心计算改用C实现或者预计算高斯窗系数表存到只读存储区避免频繁计算指数函数。数据通道上如果存在多个测点可以采用流水线结构一边采集新的波形段一边对上一段做S变换和分类把处理延迟隐藏在采样间隔里。我个人在实际操作中的体会是这套系统真正难的地方往往不是S变换或决策树某一段而是前端波形特征和工程语义的对齐。最开始我做的时候想着直接把整个时频矩阵拉平用来训练随机森林结果准确率怎么也上不去。后来慢慢把“基波包络最小值”“持续时长”“频带能量比”这些能直接翻译成现场现象的指标抽出来之后模型才真正活过来。这也提醒我在电力信号处理项目里物理直觉永远比纯调参重要。如果你接下来要在这个方向继续深入可以试着往多标签分类、复合扰动识别或者把决策树换成分层分类器方向做。希望这篇记录能给你省下一些试错的时间。
返回列表