
简介面向配电网行业开发人员与电力专业学生这份基于Python的资源提供了完整的COMTRADE录波文件解析方案可帮助使用者快速读取cfg与dat格式录波文件、解析故障波形数据并支持波形显示与多文件批量转换适用于课程设计、毕业设计及工程调试等场景。压缩包共4个文件包括命令行解析工具、核心解析模块、说明文档和配套配置项整体仅7KB结构简洁、上手成本低。目前已有96人学习下载适合希望系统了解COMTRADE文件处理流程的学习者。借助资源内的模块化设计读者既可以直接调用现成解析接口也可以参照文档进行二次开发按指定目录批量转换多个波形文件从而提高配电网数据分析与故障排查效率是一份实用的小型工具类参考。1. 配电网录波文件为什么绕不开COMTRADE以及Python该怎么切入配电网故障录波、保护动作分析、电能质量评估最终都绕不开COMTRADE这个格式。它由.cfg和.dat两个主文件组成前者描述通道配置与采样率后者存放实际采样点。只要你是做继电保护测试、行波测距或后端波形分析都得分清这两类文件再把它们拼成带时间轴的数据集。这个基于Python的工具和库把命令行工具与可引用模块拆开comtrade命令负责单个文件解析、转换和批量处理comtrade.py则给出结构化类方便你直接在自己的分析流程里调用。对于Python毕业设计、期末大作业中需要处理真实录波数据的场景它能省掉大量造轮子的时间。更关键的是它把非标准文件的处理经验也沉淀成了参数而不是要求你死记标准文档。2. COMTRADE文件结构剖析从cfg字段映射到Python类2.1 cfg文件行格式与通道信息提取一个标准COMTRADE .cfg文件是按行组织的定序文本源自IEEE C37.111。第一行通常是RELAY1,DEVICE1,1999依次是厂站名、设备名、标准年份。第二行写成16,12A,4D意思是总通道数16其中12个模拟量通道、4个数字量通道。从第三行开始每个通道占一行但模拟通道与数字通道的字段数量不同解析时必须先判断类型。我把常见字段整理成下表便于对照实际cfg文件字段位置模拟通道含义数字通道含义1通道号通道号2通道名如UA、UB通道名如CB_POS3相位A/B/C或空空闲通常空4被监测元件空闲5单位V/A/Hz等初始状态0或16a系数-7b系数-8c系数-9d系数-10时间戳偏移标记-11斜率标记-模拟通道行会包含a、b、c、d四个标度系数数字通道行则直接是初始状态。解析时如果搞反后面把所有系数都读成字符串计算采样值就会出错。在comtrade.py中我的做法是把每个通道映射成一个Channel对象存储原始字段、单位、a/b/c/d系数。这样后续处理dat数据时直接拿对象里的数值算不需要反复翻cfg文件。2.2 dat文件两种编码ASCII与二进制dat文件保存实际的采样值。ASCII格式是纯文本每行对应一个采样点比如1,0,0.000000,0.000000,0,1 2,1000,0.120000,0.150000,0,1第一个数字是采样序号第二个是从参考时刻开始的微秒偏移接下来是各模拟通道的原始采样值最后是各数字通道的0/1状态。二进制格式更紧凑通常用16位有符号整数表示模拟值用1字节或按位打包表示数字通道状态。读取方式完全不一样ASCII可以用split(,)二进制必须用struct或numpy。2.3 Python类设计与模块边界我按职责划分了边界ComtradeParser负责读入cfg和dat对外暴露samples二维数组、channels通道元数据、time_seconds绝对时间数组等属性底层的文本解析放在parse_cfg()方法中。命令行工具只是胶水层调用这些类完成转换。下面是cfg解析部分的骨架核心是区分通道类型和读取采样率段class ComtradeParser: def __init__(self, cfg_path): self.cfg_path cfg_path self.channels [] self.sample_rates [] def parse_cfg(self): with open(self.cfg_path, r) as f: lines [line.strip() for line in f if line.strip()] station, device, year lines[0].split(,)[:3] total, analog, digital lines[1].split(,) self.analog_count int(analog.rstrip(A)) self.digital_count int(digital.rstrip(D)) idx 2 for _ in range(self.analog_count self.digital_count): fields lines[idx].split(,) ch_type A if _ self.analog_count else D self.channels.append({ type: ch_type, name: fields[1], unit: fields[4] if ch_type A else None, a: float(fields[5]) if ch_type A else None, b: float(fields[6]) if ch_type A else None, c: float(fields[7]) if ch_type A else None, d: float(fields[8]) if ch_type A else None, }) idx 1 rate_count int(lines[idx]) idx 1 for _ in range(rate_count): rate_fields lines[idx].split(,) self.sample_rates.append([int(x) for x in rate_fields[:3]]) idx 1这里rstrip(A)去掉模拟通道数量后的字母标记。采样率段的每一行包含采样率、该段采样点数、该段起始时间偏移这三个值在重建时间轴时会用到。如果某个cfg文件没有采样率段通常说明设备不支持变采样率解析时可以直接用默认采样率但要打一个警告。2.4 为什么不能直接用split处理二进制文件ASCII文件用split(,)很轻松但二进制文件不行。二进制dat常见的是每个模拟通道用2字节有符号整数加上一个4字节采样序号和4字节时间戳按小端序排列。此时用numpy.fromfile加结构化dtype最可靠import numpy as np dtype np.dtype([ (seq, i4), (time, i4), (analog, i2, analog_count), (digital, i1, digital_count) ]) data np.fromfile(dat_path, dtypedtype)i2表示小端序16位有符号整数analog_count是模拟通道数量。如果直接用通用CSV库去读二进制读出来全是乱码。这也解释了为什么必须先从cfg读取通道类型数量才能知道dat每条记录的长度。真实设备还会遇到nd非标准标记采样值可能是浮点数或4字节整数这时需要根据cfg里c、d参数来推断实际数据类型不能一概而论。3. 命令行工具实战单文件解析与批量转换的正确姿势3.1 命令行参数设计这个工具的命令行用法非常直接传入一个cfg文件或dat文件工具自动识别同目录下的配对文件。可用的参数包括-c指定cfg文件、-d指定dat文件、--output指定输出格式、--dir指定批量处理目录。在没有图形界面的服务器上这个命令行比手动打开Excel效率高得多。python comtrade.py --cfg fault.cfg --output csv如果不指定输出方式默认只打印通道信息和采样点数用于快速验证文件是否损坏。3.2 单文件解析与数据预览假设现在有fault.cfg和fault.dat执行python comtrade.py fault.cfg预期输出类似这样通道数: 12A 4D 采样点数: 12000 采样率分段: 1000 Hz(10 points), 2000 Hz(20 points)如果文件解析失败错误信息会指出具体行号和字段数量方便判断是标准文件还是私有格式。我一般会先用这个模式检查文件再决定后续转换避免盲目输出一堆没意义的空数据。3.3 批量转换目录下的所有录波文件现场一个文件夹里可能有几十个录波文件逐个执行命令行不现实。工具支持直接指定目录python comtrade.py --dir ./wave_data --format npz批量操作的核心是扫描目录获取所有.cfg文件然后逐个解析并按设定格式输出。下面的脚本展示了如何绕过命令行直接用库实现同样功能from pathlib import Path from comtrade import ComtradeParser def batch_convert(wave_dir, out_dir): out_path Path(out_dir) out_path.mkdir(parentsTrue, exist_okTrue) for cfg_file in Path(wave_dir).glob(*.cfg): parser ComtradeParser(str(cfg_file)) parser.parse() out_name cfg_file.stem .npz parser.to_npz(out_path / out_name) print(fconverted: {cfg_file.name} - {out_name})glob(*.cfg)只匹配cfg文件避免同时匹配.dat导致同一个录波被转换两次。如果录波器目录还有子目录可以用rglob(*.cfg)递归扫描但要注意不同子目录下文件名可能重复输出时最好把子目录名加进文件名前缀。3.4 输出格式选型CSV、MAT 还是 NPZ工具支持多种输出格式各有适用场景。我整理了对比输出格式优点缺点适用场景CSV通用、可读性强大文件占空间小样本人工审查MAT兼容Matlab需要scipy复用既有Matlab工具NPZ加载快、保留通道名外部工具不识别Python批量后续处理如果输出CSV内部会用pandas.DataFrame重建带时间戳的表列名直接采用cfg里的通道名。注意通道名可能有空格或特殊字符写入前要用正则替换掉非字母数字字符否则下游程序容易出问题。3.5 常见执行问题定位命令行最常遇到的异常有两类。一类是cfg文件缺少采样率段常见于老型号录波器此时可以调用parse_cfg(ignore_errorsTrue)跳过该段但输出数据的采样率需要人工指定。另一类是dat行数和cfg声明的通道数不一致多发生在手工修改过cfg文件之后。遇到这种问题先打印出错行前后两行对比分隔符数量很快能定位是多了逗号还是少了字段。这段命令行工具的逻辑不难但批量处理时的路径编码、输出命名、格式转换常常比解析本身更费功夫。把这层做好后面做波形分析时才不会卡在数据导入上。4. 波形显示与转换从采样点到可分析的可视化结果4.1 将解析数据绘制成录波图解析出来的数据只是数值必须结合时间轴才能还原成波形。以三相电压为例用matplotlib可以快速绘制import matplotlib.pyplot as plt plt.figure(figsize(12, 6)) for i, ch_idx in enumerate([0, 1, 2]): plt.subplot(3, 1, i 1) plt.plot(parser.time_seconds, parser.samples[:, ch_idx]) plt.ylabel(parser.channels[ch_idx][name]) plt.xlabel(time (s)) plt.show()parser.time_seconds来自dat中的微秒偏移加上cfg记录的绝对时间起点。如果cfg中指定了时间参考还要换算成UTC或本地时间。这一步比波形本身更重要因为多台设备录波数据对比时时间基准必须一致否则故障前后的事件顺序都会对不上。4.2 数字量通道的处理数字量通道在dat里是0/1状态直接和模拟量画在同一纵轴会严重压缩波形。常见做法是给数字通道分配独立子图或者把它们转换为带偏移的阶跃信号。例如断路器分合闸状态可以画成方波用红色绘制便于观察开关动作与故障发生时刻的关系。plt.step(parser.time_seconds, parser.samples[:, 12] 1, r, wherepost)如果数字通道数量多还可以把多个通道的0/1值乘以不同系数后叠加做成一条简单的“状态轨迹”一眼就能看出各开关动作顺序。4.3 将录波文件转换为通用数据集除了绘图这个工具还支持把多个文件合并成一个numpy数组供机器学习模型直接使用。转换时最麻烦的是采样率对齐。COMTRADE允许分段变采样率而神经网络输入要求固定长度所以必须先统一到目标采样率。我通常用scipy.signal.resample插值到指定点数from scipy import signal def uniform_resample(data, src_rate, dst_rate4000): target_len int(len(data) * dst_rate / src_rate) return signal.resample(data, target_len)src_rate来自cfgdst_rate根据分析场景定。配电网工频分析用1 kHz足够而行波测距往往需要1 MHz以上。如果多个文件要拼接必须先把每个文件都重采样到同一频率再按时间戳对齐。否则不同文件的数据点在时间轴上错位模型训练出来的结果不可信。4.4 绘图参数与工程习惯绘制录波图时工程上惯用颜色表示相别A相黄色、B相绿色、C相红色、零序蓝色。左侧标注通道名右侧标注数值单位。用ax.twinx()可以轻松实现。还有一个容易被忽略的细节录波文件通道名称可能重复比如两个装置都叫“IA”绘图和图例就会冲突。我一般会在通道名后面追加设备名或编号确保图例唯一。提示通道数超过20个时不要全部绘制否则图面挤成一团。可以先对通道排序只画关键量或者把高频细节用放大镜工具单独观察。这个库本身只负责解析和转换不强制绘图方式但配合matplotlib的各种交互特性完全可以做成一个轻量录波浏览器。5. 边界条件与踩坑记录时标对齐、浮点精度与非标准文件处理5.1 采样序号和时间戳的起始偏移很多非标准设备的dat文件首个采样点序号不是1时间戳第一点也不是0而是设备自身计时偏移。如果直接按原始时间绘制波形起点会漂移。正确做法是记录第一点时间戳所有时间点减去它再显示。工具中的normalize_start_time参数就是为此设计的。解析时先读取第一条dat记录再决定是否做归一化。5.2 标度系数的完整换算cfg中每个模拟通道的a、b、c、d四个系数标准公式比“a乘加b”复杂。高精度录波器的c、d可能是非零的用于拟合传感器非线性。许多简化解析器忽略c、d导致幅值误差达到百分之几。我在实现里默认按完整公式换算physical a * raw b * digital_state而c、d只在确知设备需要时启用。如果你拿到的是国产配电网终端录波文件大概率c、d为0可以关闭补偿以加快速度。5.3 采样率分段的时间轴重构当cfg说明采样率分段变化时dat里的时间戳仍然是微秒偏移但不同段内的采样间隔不同。错误做法是把整个文件当成恒定采样率在分段边界上会出现时间回退或跳变。正确做法是分段构建时间轴再拼接time_axis [] for rate, count, start_us in sample_rates: segment_time start_us np.arange(count) * 1e6 / rate time_axis.append(segment_time) time_axis np.concatenate(time_axis)start_us是各段起始点相对于参考点的微秒偏移不是绝对时间。这个细节在故障前后录波尤其重要因为故障瞬间采样率往往自动升高前段低频后段高频漏掉分段会导致波形畸变。5.4 中文路径与编码问题配电网现场许多工控机使用中文路径直接读取cfg可能报UnicodeDecodeError。我一般先尝试UTF-8失败后回落到GBKdef read_cfg_text(path): for enc in (utf-8, gbk): try: with open(path, r, encodingenc) as f: return f.readlines() except UnicodeDecodeError: continue raise ValueError(无法识别的编码: path)输出CSV时同样要注意编码使用utf-8-sig否则Excel打开会乱码。5.5 验证解析结果是否正确的快速方法拿到一个录波文件后我先跑三个检查对比解析出的通道数量与cfg声明数量打印前三个采样点与文本版dat文件手工对比检查整段波形的最大值是否超过该通道量程合理范围。如果某通道采样点恒为0多半是二进制dat的字节序不对把i2改成i2再试。项目文档里的自检脚本会对测试录波文件做往返校验解析后再生成标准格式dat与原文件按位比较。这个方式验证解析器兼容性非常有效建议在使用新设备录波文件前先跑一遍确认没有隐藏的解析缺陷。本文还有配套的精品资源点击获取