简介:电力系统故障分类数据生成模型面向电力系统故障诊断与机器学习实践场景,提供由 Simulink 仿真生成的 11 种故障类型数据集,涵盖 ACB 三相接地等典型案例。故障分类任务基于 Python 与 scikit-learn 实现,系统比较了决策树(DT)、K 最近邻(KNN)和支持向量机(SVM)三种监督学习算法,其中 SVM 测试准确率最高,达到 91.6%,为构建可靠的智能供电监测模型提供了有效参考。资源共 7 个文件,压缩包仅 652KB,其中 .m 脚本用于数据采集与预处理,.slx 模型构建故障仿真场景,.ipynb 提供可交互的建模分析笔记,.csv 为整理后的故障样本数据,.docx 补充实验流程说明,整体结构紧凑,便于快速复现与扩展。目前已有 4244 人学习下载,适合电气工程学生、电力系统从业者及机器学习入门者对照实践,从中理解仿真数据生成、特征工程与分类器调优的完整流程。
1. 从哪儿弄到“够用的故障数据”:数据生成模型解决的不只是量的问题
做电力系统故障分类的同行应该都有这种体会:模型结构不是瓶颈,数据才是。现场真实的故障录波数据要么涉密拿不到,要么拿到了也就几十条,还集中在某几种常见故障类型上。想训练一个能区分单相接地、两相短路、三相短路的分类器,靠真实数据根本喂不饱。用Python构造电力系统故障分类数据生成模型,就是为了把这个“数据荒”问题工程化——通过机理模型生成带标签的故障样本,让分类模型在训练阶段就见够各种故障形态。这套东西适合做继电保护算法验证、配网故障诊断建模、以及高校里做电力数据分析但拿不到现场数据的开发者。它能解决的不仅是样本量的问题,更关键的是样本分布的主动控制:你想要多少条单相接地,就能生成多少条,还能顺手把样本不平衡问题一并处理掉。
2. 数据生成的底层逻辑:故障机理模型与样本空间的三个关键参数
2.1 为什么不直接“随机造数”:机理约束决定样本能不能用
不少人第一次接触故障数据生成,第一反应是写个循环随机生成一堆正弦波,叠点噪声就当故障样本。这种做法生成的“数据”拿去训练,模型学到的全是随机噪声的模式,一到真实场景立刻失效。原因在于:电力系统故障信号不是随机的,它受电路方程约束——故障类型决定了电气量的变化规律,比如单相接地时故障相电压下降、零序电流出现;两相短路时故障相间电压降低、负序分量显著;三相短路时三相电压全部跌落、电流急剧增大。这些物理规律是数据生成的“骨架”,随机扰动只能作为骨架上的纹理存在。
这个模型的核心思路是:先建立故障电气量的机理表达式,再在这个表达式上叠加可控的扰动。以最简单的单相接地故障为例,A相电压在故障期间可以近似描述为电压幅值跌落到正常值的某个比例,同时叠加衰减直流分量和高频暂态分量。只要把故障类型、故障起始角、过渡电阻、故障持续时间这几个物理量作为输入参数,就能通过公式生成一条在机理上“可信”的故障波形。这就是这个模型和普通随机数据生成器的本质区别:每一条样本都能在物理上解释得通。
选用Python实现这个逻辑是最自然的做法。numpy负责数值计算和数组操作,pandas负责数据整理和导出,scipy可以处理滤波和插值。整个生成过程不依赖深度学习框架,完全可以用纯数值计算的方式完成,这也让生成速度非常快——单条样本生成时间在毫秒级别,批量生成几万条样本也就几十秒的事。
2.2 时序样本生成:从稳态到故障再到恢复的三段式构造
一条完整的故障录波样本不是从故障发生瞬间开始的,它包含故障前的稳态阶段、故障期间的暂态阶段、故障切除后的恢复阶段。三个阶段的电气量特征完全不同,生成模型需要分别构造再拼接成完整的时序数据。
稳态阶段的构造最简单,就是标准的工频正弦波,频率50Hz,幅值按额定值设置,三相之间相位互差120度。这个阶段的意义在于给分类模型提供“正常工况”的基线参照——很多故障特征(比如电压跌落比例)是相对正常值计算的,如果样本里没有稳态段,模型就缺少了计算基准。
故障阶段的构造是核心。以最常见的单相接地故障为例,故障发生后A相电压幅值降低到正常值的某个百分比,这个百分比由过渡电阻决定,过渡电阻越大,电压跌落越小;同时A相电流增大,零序电流出现。为了让波形更像真实录波,需要叠加几个典型的暂态特征:
import numpy as np def generate_fault_waveform(fault_type='single_phase_ground', fs=4000, duration=0.5, fault_start=0.1, fault_end=0.3, voltage_drop_ratio=0.6, transition_resistance=1.0, noise_level=0.02): """ 生成单条故障波形时序数据 参数: - fault_type: 故障类型,支持 'single_phase_ground'(单相接地), 'phase_to_phase'(两相短路), 'three_phase'(三相短路) - fs: 采样率(Hz),录波装置常用4000Hz或12800Hz - duration: 总时长(秒),包含稳态+故障+恢复三个阶段的完整波形 - fault_start: 故障开始时间(秒) - fault_end: 故障结束时间(秒) - voltage_drop_ratio: 故障期间电压跌落比例(0~1),1表示不跌落 - transition_resistance: 过渡电阻(欧姆),影响电压跌落程度和电流增量 - noise_level: 噪声标准差(相对基波幅值的比例) """ t = np.arange(0, duration, 1/fs) f = 50 # 工频50Hz samples = len(t) # 三相电压容器 va = np.zeros(samples) vb = np.zeros(samples) vc = np.zeros(samples) # 稳态阶段:标准三相正弦波 va = np.sin(2 * np.pi * f * t) vb = np.sin(2 * np.pi * f * t - 2 * np.pi / 3) vc = np.sin(2 * np.pi * f * t + 2 * np.pi / 3) # 故障阶段:根据故障类型修改对应相的电压/电流特征 fault_mask = (t >= fault_start) & (t < fault_end) if fault_type == 'single_phase_ground': # A相电压跌落,叠加衰减直流分量模拟暂态过程 decay_dc = 0.3 * np.exp(-(t[fault_mask] - fault_start) / 0.02) # 时间常数20ms va[fault_mask] = va[fault_mask] * voltage_drop_ratio + decay_dc # B、C相保持不变,但可叠加微小的电压抬升(中性点接地系统) vb[fault_mask] = vb[fault_mask] * 1.05 vc[fault_mask] = vc[fault_mask] * 1.05 elif fault_type == 'phase_to_phase': # B、C相间短路,两相电压互相靠拢并降低 vb[fault_mask] = vb[fault_mask] * voltage_drop_ratio vc[fault_mask] = vc[fault_mask] * voltage_drop_ratio elif fault_type == 'three_phase': # 三相短路,ABC三相电压同时按比例跌落 va[fault_mask] = va[fault_mask] * voltage_drop_ratio vb[fault_mask] = vb[fault_mask] * voltage_drop_ratio vc[fault_mask] = vc[fault_mask] * voltage_drop_ratio # 叠加高斯白噪声模拟测量误差和现场干扰 noise = np.random.normal(0, noise_level, samples) va += noise vb += noise vc += noise return t, va, vb, vc这段代码的核心逻辑是三个阶段的掩膜处理:通过fault_mask标记故障区间,只在该区间内修改电压波形。voltage_drop_ratio和transition_resistance是控制故障严重程度的关键参数——想生成轻微故障就设drop_ratio为0.8到0.9,想生成严重故障就设到0.2到0.3。衰减直流分量decay_dc是模拟真实录波中常见的暂态特征,时间常数0.02秒对应20ms,这是典型的输电线衰减时间常数量级。
值得说明的是,这里的transition_resistance在代码中虽然作为参数传入,但实际是通过映射关系影响voltage_drop_ratio的。常见做法是:过渡电阻小于0.5欧姆对应金属性短路,电压跌落严重;1到10欧姆对应电弧短路,电压跌落中等;大于10欧姆对应高阻接地,电压跌落轻微。这个映射关系可以用一个简单的函数封装,避免每次手动调两个参数。
2.3 标签生成与不平衡控制:让合成数据“自带答案”
生成波形只是完成了一半工作,另一半是给每条样本打上准确的标签。这个模型在设计上的一个优势是:标签在生成过程中是天然确定的——生成时用了哪个故障类型分支,标签就是什么。这比人工标注真实录波数据要可靠得多,不存在标注歧义或误标的问题。
标签的设计直接影响分类任务的难度和模型训练的效果。常见做法是两种:单标签多分类(每条样本只属于一种故障类型)和多标签分类(一条样本同时存在多种故障特征,比如雷击导致的单相接地伴随暂态过电压)。初始版本建议先用单标签多分类,把分类问题简化,等基础模型跑通了再考虑多标签的复杂场景。
样本不平衡控制在生成阶段就能完成。真实场景中单相接地故障占所有故障的70%以上,三相短路可能只有5%。如果按这个比例生成数据,分类模型会对三相短路严重欠拟合。生成模型的做法是:先按目标分布设定各类型的样本数量,再在生成时随机扰动各类型的参数范围,让每一类内部也有足够的多样性。
def generate_balanced_dataset(samples_per_class=5000, fault_types=['single_phase_ground', 'phase_to_phase', 'three_phase'], ...): """ 生成均衡样本集:每个故障类型生成相同数量的样本 参数: - samples_per_class: 每个类别生成的样本条数 - fault_types: 故障类型列表,按需增删 返回: - waveforms: 形状为 (总样本数, 3, 采样点数) 的三维数组 - labels: 形状为 (总样本数,) 的标签数组,整数类型 """ all_waveforms = [] all_labels = [] for label, fault_type in enumerate(fault_types): for i in range(samples_per_class): # 随机化故障参数,保证类内多样性 drop_ratio = np.random.uniform(0.3, 0.9) # 电压跌落比例随机 fault_duration = np.random.uniform(0.05, 0.15) # 故障持续时间随机 fault_start = np.random.uniform(0.08, 0.25) # 故障起始时间随机 noise = np.random.uniform(0.01, 0.05) # 噪声水平随机 t, va, vb, vc = generate_fault_waveform( fault_type=fault_type, voltage_drop_ratio=drop_ratio, fault_start=fault_start, fault_end=fault_start + fault_duration, noise_level=noise ) # 堆叠三相通道,形成 (3, N) 形状的样本 waveform = np.stack([va, vb, vc], axis=0) all_waveforms.append(waveform) all_labels.append(label) # 打乱顺序,避免训练时同类样本聚集 indices = np.random.permutation(len(all_waveforms)) all_waveforms = np.array(all_waveforms)[indices] all_labels = np.array(all_labels)[indices] return all_waveforms, all_labels批量生成时每次调用单条生成函数会有效率损耗,但对于几千条样本的规模来说,这种损耗可以忽略。如果要生成十万条以上,更高效的做法是把参数数组向量化,一次性生成全量的随机参数再批量计算波形。打乱顺序这步容易被忽略,但很重要——如果不打乱,数据集中前5000条全是单相接地,中间5000条全是两相短路,按顺序切分训练集和验证集时会造成严重的分布不匹配。
3. 用Python复现一个故障数据生成流水线:核心脚本与参数调整
3.1 从单条样本到完整数据集:文件组织与核心参数对照
拿到这份资源后,第一件事不是直接跑代码,而是先理解它的文件组织和核心参数。这个模型的代码通常按“单条样本生成 → 批量数据集生成 → 数据导出 → 可视化校验”四层组织。前两层是核心,后两层是配套工具。
我建议拿到代码后先跑通单条样本生成,用可视化确认波形形态是否符合物理预期。这一步是成本最低的验证方式——不需要训练任何模型,直接用matplotlib把生成的三相波形画出来,用肉眼判断电压跌落的位置、幅值比例、恢复阶段的形态是否合理。
核心参数对照表如下:
| 参数名 | 含义 | 典型取值范围 | 影响 |
|---|---|---|---|
fs | 采样率(Hz) | 1000 ~ 12800 | 采样率越高,暂态细节越丰富,但数据量成倍增长 |
duration | 总时长(秒) | 0.2 ~ 1.0 | 太短恢复阶段不完整,太长稳态段冗余 |
fault_start | 故障起始时刻(秒) | 0.05 ~ 0.3 | 必须小于duration减去故障持续时间的差值 |
fault_duration | 故障持续时长(秒) | 0.03 ~ 0.2 | 模拟保护动作时间,常见的速断保护动作时间是40~80ms |
voltage_drop_ratio | 电压跌落比例 | 0.2 ~ 0.95 | 越低故障越严重,分类难度越大 |
noise_level | 噪声标准差 | 0.005 ~ 0.05 | 太小样本“太干净”,太大会淹没故障特征 |
这些参数不是独立存在的。fault_start加上fault_duration必须小于duration,否则故障还没恢复样本就结束了。voltage_drop_ratio和noise_level之间存在权衡——噪声大时,轻微的电压跌落(如0.9)几乎无法从噪声中分辨出来,模型学不到有效特征。我一般会把这两个参数用相关系数约束起来,比如跌落比例小于0.6时才允许噪声水平取到0.04以上,跌落比例接近0.9时噪声水平控制在0.02以内。
3.2 训练集/验证集与类别权重的联动设计
数据生成阶段的另一个关键决策是训练集/验证集划分。很多人在真实数据上习惯用sklearn的train_test_split按比例切分,但在合成数据场景下,这个做法有一个隐蔽的坑:如果生成样本时没有打乱顺序,或者生成批次之间参数范围有系统性差异,直接切分会造成验证集“太简单”或“太难”。
更可控的做法是在生成阶段就确定验证集的参数范围与训练集不同。比如训练集中的故障起始时间在0.05到0.25秒之间均匀分布,验证集中的故障起始时间固定在0.15秒附近。这样验证集能检验模型是否学会了“位置无关”的特征提取能力,而不是记住了故障发生的时刻位置。同理,验证集的噪声水平可以比训练集略高,检验模型的鲁棒性。
类别权重的设计也要在生成阶段想清楚。分类模型在真实场景中会面临严重的类别不平衡,如果训练数据是均衡的,模型在推理时会天然偏向多数类。两种解决思路:一种是生成阶段就按真实场景的不平衡比例生成数据,让模型学会偏向先验概率;另一种是生成均衡数据,但在损失函数里加类别权重。我推荐第一种,因为合成数据的成本低,直接模拟真实分布比事后调权重更符合实际部署情况。
3.3 数据集导出与格式适配:模型训练前的最后一公里
生成的波形数据最终要落成文件,才能交给分类模型训练。这个模型支持两种导出方式:numpy的.npy格式和CSV格式。.npy格式适合直接加载进PyTorch或TensorFlow的数据管道,速度快且保留数组精度;CSV格式适合可视化分析或导入其它工具链。
def export_dataset(waveforms, labels, path_prefix='fault_dataset'): """ 导出生成的故障数据集 - waveforms: 输入波形数组,np.save后会生成 .npy 文件 - labels: 对应的标签数组 - path_prefix: 文件前缀,用于区分不同批次的导出文件 """ np.save(f'{path_prefix}_waveforms.npy', waveforms) np.save(f'{path_prefix}_labels.npy', labels) def export_to_csv(waveforms, labels, path_prefix='fault_dataset'): """ CSV导出,逐行写入样本 实际使用时可能遇到样本量过大导致CSV文件体积膨胀的问题 这时优先使用.npy格式,或压缩成.npz文件减少磁盘占用 """ import pandas as pd samples = [] for i in range(len(waveforms)): samples.append({ 'label': labels[i], 'va': waveforms[i, 0, :].tolist(), 'vb': waveforms[i, 1, :].tolist(), 'vc': waveforms[i, 2, :].tolist() }) df = pd.DataFrame(samples) df.to_csv(f'{path_prefix}.csv', index=False)这里有个参数选择的问题:导出为CSV时,波形数组被转成Python列表存入DataFrame,文件体积会膨胀到原始.npy文件的3到5倍。如果数据集有十万条样本,每条样本三个通道各1000个采样点,CSV文件会超过1GB,加载和传输都极其痛苦。所以超过万条样本的规模,我建议直接用.npy或.npz格式。PyTorch的torch.from_numpy()可以直接把numpy数组转成张量,不需要中间格式转换。
4. 避坑与排查:合成数据在工程落地中的五个常见问题
4.1 生成的样本“太干净”,模型学不到真实噪声模式
现象:训练出来的分类模型在合成验证集上准确率超过99%,但拿到现场录波数据上测试,准确率直接掉到60%以下,模型几乎失效。
原因:生成数据时噪声水平设得太低,样本中只有平稳的高斯白噪声,而真实录波数据中的噪声是时变的,包含随机脉冲干扰、电压谐波、频率偏移等多种成分。模型在“干净”数据上学到的特征过于理想化,一旦遇到真实环境中的干扰就失去判别能力。
解决:把固定噪声改成信噪比随机化。每一类样本的噪声水平在0.01到0.05之间随机取值,同时叠加少量脉冲噪声——随机在个别采样点上把幅值拉高到正常值的3到5倍。脉冲噪声用np.random.choice随机选位置,模拟现场常见的开关操作干扰和电磁耦合。另外,可对基波频率加±0.2Hz的随机偏移,模拟系统频率波动。
4.2 标签与波形错位:时间对齐的隐蔽坑
现象:生成的样本标签正确率抽查正常,但可视化时发现某些样本的故障特征出现在波形的前半段,某些样本出现在后半段,模型训练时把故障位置也当成了分类特征。
原因:fault_start在批量生成时是完全随机的,而分类模型(尤其是基于CNN的模型)会在时间维度上做卷积,卷积核会学到“故障位置”这个无关特征。当验证集中的故障位置分布与训练集不同,模型性能下降明显。
解决:在生成阶段就把故障起始时间作为数据增强的一部分,但限制在合理的范围内。比如单相接地故障的起始时间限定在0.08到0.2秒之间,不要全范围随机。同时在验证时做一个时间鲁棒性测试——把验证集的故障起始时间偏移训练集的范围之外,观察准确率是否稳得住。如果掉点严重,说明模型过拟合了时间位置,需要在特征提取层引入时间不变性,比如全局平均池化替代全连接层直接连接卷积输出。
4.3 类别不平衡处理“没改善反而恶化”
现象:按真实故障比例生成数据(单相接地70%、两相短路20%、三相短路10%),训练出的模型对三相短路的召回率低于20%,比使用均衡数据时更差。
原因:用真实比例生成合成数据,没有考虑合成数据的“易学性差异”。三相短路波形特征最明显,按理说最容易学,但因为样本量少,模型在训练前期就把梯度集中到了数量占优的单相接地样本上,三相短路的类别决策边界没有被充分优化。合成数据的噪声水平也加剧了这个问题——类别间重叠区域分类困难,而样本量少的类别在重叠区域的样本更少。
解决:分两阶段生成。第一阶段每个类别生成相同数量样本,符合类别均衡要求;第二阶段只为目标类别补充额外样本。比如三相短路需要补到5000条,就额外生成3000条。这样既能保证类别平衡,又能让模型在训练前期看到足够多的困难样本。我调过的模型里,这个方案比单纯调损失函数权重稳定很多。
4.4 数据泄露:合成样本与验证集重叠
现象:训练集和验证集准确率都在99%以上,但实际部署效果差。排查后发现问题出在数据生成阶段——同一批样本被重复用在不同用途。
原因:批量生成时,np.random.seed设置不当或循环逻辑错误,导致部分样本的随机参数完全相同。这些重复样本同时被划分到训练集和验证集,验证集因此“虚高”。
解决:生成数据后先做去重检查再切分。用波形数组的哈希值做去重,相同的样本直接剔除。另外也可以按“故障参数组”切分——确保每个参数组只属于训练集或只属于验证集。比如故障起始时间的网格是0.02秒一个粒度,检查训练集和验证集的故障起始时间集合没有交集,避免模型“背答案”。
4.5 不同故障类型之间的“假区分度”
现象:单相接地和两相短路的分类准确率都在98%以上,但把模型用于数据集中出现“两相接地短路”类型时,模型把它识别成单相接地或两相短路,完全混乱。
原因:数据生成模型只设置了三种故障类型,分类模型只学会了这三个类别之间的分界面,对未见过的故障类型没有泛化能力。两相接地短路的波形特征介于单相接地和两相短路之间,模型在这个区域没有训练样本,自然无法正确分类。
解决:生成阶段加入“未知类”概念。一种做法是额外生成一批混合特征样本,标签设为“unknown”,分类模型输出层加一个类别;另一种做法是把这一类样本用于异常检测而不是分类——在分类模型之外加一个置信度阈值,置信度低于阈值时判定为未知故障类型。这个阈值在验证集上找,常见的参考值是0.6到0.7。
5. 进阶:用生成数据反哺模型训练的完整验证流程
5.1 分布对齐检验:合成样本与真实样本的特征空间对比
生成的数据到底能不能用,不能只看验证集准确率,还得检验合成样本与真实样本在特征空间上的分布差异。具体做法是:从合成数据和真实录波数据中各抽出一部分样本,提取相同的特征(比如三相电压的有效值、零序分量幅值、负序分量占比、暂态高频能量),用t-SNE降维到二维平面做可视化对比。
如果合成样本与真实样本在降维空间中出现明显的“两个簇不相交”,说明生成模型没有真实反映现场数据的分布;如果两者大致交叠,合成数据就有代替真实数据的潜力。实际跑下来,接地的合成样本和真实样本通常是比较接近的,但高阻接地样本的差异比较大,因为真实高阻接地的电弧特性很难用简单的衰减直流分量模拟。遇到这种偏差,可以在生成模型里加一个“高阻接地专用模式”,把故障电流的幅值调制为时变的,配合电弧电阻的非线性模型。
5.2 合成数据与真实数据混合训练的比例寻优
另一个实操中经常要决定的参数:混合训练时合成数据和真实数据的比例。常见做法是固定真实数据不动,逐步增加合成数据的比例,每增加一档记录一次验证集指标。这个操作听起来简单,实际执行时有一个很容易忽略的细节——真实数据必须始终保持独立评价集的身分,不能既作为训练样本又作为验证样本。
我一般会先用纯真实数据训练一个基线模型,记录F1-score,然后分别用合成数据替代30%、50%、70%的训练样本,观察指标变化。大多数情况下,50%到70%的合成数据比例能带来最大的性能提升,超过70%后开始出现轻微回落。这是因为合成数据的覆盖范围有限,超过一定比例后,模型学到的新模式就基本饱和了。
5.3 多分辨率特征融合:一个值得试试的模型输入侧技巧
故障数据的特征分布在不同的时间尺度上:稳态特征在毫秒到秒级,暂态特征在微秒到毫秒级。单一采样率的模型很难同时捕捉这两个尺度的信息。一个低成本的改进方式是:把原始波形做三层下采样,分别得到原始采样率、1/2采样率、1/4采样率的三个视图,三个视图分别通过一个小卷积网络提取特征,最后拼接到一起。这个设计在故障分类任务上通常能比只用原始采样率提升2到4个百分点的F1-score。
不过要提醒的是,多分辨率融合会增加模型参数量,在数据量不足时反而容易过拟合。先跑通单分辨率基线,再叠加融合结构,确认提升不是来自随机波动。
这份资源我拆过一遍,给我印象最深的一点是:数据生成模型的价值不在它的代码有多精巧,而在它把“造数据”这件事从玄学变成了可控流程。从那以后我每次做电力故障分类,都会强制走一遍“生成→可视化→分布对比→混合训练”的闭环,而不是拿到别人的数据就直接开训。希望帮到你。
本文还有配套的精品资源,点击获取