电力工程师必看:手把手教你用Python解析COMTRADE文件(附完整代码)

发布时间:2026/6/8 15:53:34

电力工程师必看:手把手教你用Python解析COMTRADE文件(附完整代码) 电力工程师必看手把手教你用Python解析COMTRADE文件附完整代码当电网监控系统或继电保护装置生成故障录波数据时COMTRADE格式往往是工程师们最先接触到的黑匣子。面对一堆.HDR、.CFG、.DAT文件很多工程师会陷入困惑——这些看似简单的文本和二进制文件里究竟藏着哪些关键信息本文将用Python带你拆解COMTRADE文件的每一个字节把原始数据转化为直观的电压电流波形。1. COMTRADE文件结构解析实战COMTRADE标准诞生于1991年是电力系统故障分析的通用语言。一套完整的记录包含四个文件配置文件(.CFG)数据解码的密钥数据文件(.DAT)存储原始采样值标题文件(.HDR)可选的人类可读注释信息文件(.INF)额外的设备信息实际工作中最关键的莫过于.CFG和.DAT文件。让我们从一个真实案例出发某变电站故障录波产生了Fault_2023.CFG和Fault_2023.DAT文件其中.CFG文件内容节选如下NST3000_Simulation, 1,1999 176, 143A, 33D 1, U2:A,A,U2:A,kV,0.002183,0.037750,0.0,-16376,16376,1.0,0.0,p ... 50 2 5000, 6300 10, 200 03/07/03,14:46:48.850000 03/07/03,14:46:49.010000 ASCII这份配置文件透露了几个关键信息使用1999年标准版本包含176个通道143个模拟量33个状态量系统频率50Hz采用双采样率5000Hz(6300点)和10Hz(200点)数据文件为ASCII格式2. Python解析配置文件关键技术2.1 读取通道转换系数模拟量通道的核心转换公式为实际值 fCoefA * 采样值 fCoefB用Python解析.CFG文件中第3行开始的模拟通道配置def parse_analog_channels(cfg_lines): analog_channels [] for line in cfg_lines[2:2num_analog]: # 前两行为文件头 parts line.split(,) channel { id: parts[0].strip(), name: parts[1].strip(), phase: parts[2].strip(), unit: parts[4].strip(), a: float(parts[5]), # fCoefA b: float(parts[6]), # fCoefB min: int(parts[8]), max: int(parts[9]) } analog_channels.append(channel) return analog_channels2.2 处理多采样率配置COMTRADE支持多采样率记录这在故障前后不同阶段非常有用。解析采样率配置时需注意def parse_sample_rates(cfg_lines, freq_line_idx): freq float(cfg_lines[freq_line_idx]) num_rates int(cfg_lines[freq_line_idx 1]) rates [] for i in range(num_rates): rate, count map(float, cfg_lines[freq_line_idx2i].split(,)) rates.append({rate: rate, count: int(count)}) return freq, rates3. 数据文件解析的陷阱与技巧3.1 ASCII格式解析对于ASCII格式的.DAT文件每行代表一个采样点1, 0, 46, -54, 10, 0, 1204, -734, -442, 60, 0, 1, 0, 1解析代码示例def parse_ascii_dat(file_path, analog_count): data [] with open(file_path, r) as f: for line in f: parts list(map(float, line.strip().split(,))) sample { seq: int(parts[0]), time: parts[1], analog: parts[2:2analog_count], digital: parts[2analog_count:] } data.append(sample) return data3.2 二进制格式的字节序问题二进制格式更复杂需处理字节序和数据类型import struct def parse_binary_dat(file_path, analog_count, digital_count): data [] with open(file_path, rb) as f: while True: # 读取4字节序号和4字节时间戳 header f.read(8) if not header: break seq, timestamp struct.unpack(ii, header) # 大端序 analogs struct.unpack( h*analog_count, f.read(2*analog_count)) # 数字量每16通道占2字节 digital_bytes (digital_count 15) // 16 digital_packed struct.unpack( H*digital_bytes, f.read(2*digital_bytes)) data.append({ seq: seq, time: timestamp, analog: analogs, digital: digital_packed }) return data注意不同厂商的设备可能采用不同字节序实践中需要验证解析结果是否正确。4. 数据可视化与高级分析4.1 波形绘制基础使用Matplotlib绘制电压波形import matplotlib.pyplot as plt def plot_waveform(samples, channel_config, channel_idx): times [s[time] * 1e-6 for s in samples] # 微秒转秒 values [channel_config[a] * s[analog][channel_idx] channel_config[b] for s in samples] plt.figure(figsize(12, 4)) plt.plot(times, values, labelf{channel_config[name]} ({channel_config[unit]})) plt.xlabel(Time (s)) plt.ylabel(Value) plt.legend() plt.grid() plt.show()4.2 故障特征提取通过波形分析可以提取关键故障特征def detect_fault_start(samples, channel_idx, threshold): for i, sample in enumerate(samples): if abs(sample[analog][channel_idx]) threshold: return sample[time] * 1e-6 # 返回故障起始时间(秒) return None5. 工程实践中的经验分享在实际项目中处理COMTRADE文件时有几个容易踩的坑字节序混淆某些国产保护装置使用小端序(Little-Endian)而标准未明确规定缺失值处理二进制格式中-32768(0x8000)表示缺失值需特殊处理时标溢出长时间录波可能导致时间戳超过32位整型范围通道映射错误某些设备.CFG文件中的通道顺序与实际.DAT文件不一致一个健壮的解析器应该包含这些检查def validate_data(samples, channel_configs): for ch in channel_configs: min_val ch[a] * ch[min] ch[b] max_val ch[a] * ch[max] ch[b] for sample in samples: raw sample[analog][ch[id]] if raw -32768: # 缺失值 continue value ch[a] * raw ch[b] if not (min_val value max_val): print(f警告通道{ch[id]}值{value}超出范围({min_val},{max_val}))6. 完整代码框架示例以下是一个面向对象的COMTRADE解析器框架class ComtradeParser: def __init__(self, cfg_path): self.cfg_path cfg_path self.analog_channels [] self.digital_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()] # 解析文件头 self.station, self.device, self.version lines[0].split(,) # 解析通道配置 total, analog, digital lines[1].split(,) self._parse_analog_channels(lines[2:2int(analog[:-1])]) self._parse_digital_channels(lines[2int(analog[:-1]):2int(total)]) # 解析采样率 freq_idx 2 int(total) self.frequency float(lines[freq_idx]) self._parse_sample_rates(lines[freq_idx1:]) def parse_dat(self, dat_path): if self.dat_format ASCII: return self._parse_ascii_dat(dat_path) else: return self._parse_binary_dat(dat_path) # 其他方法实现...7. 性能优化技巧处理大型COMTRADE文件时这些技巧可以提升效率使用Numpy向量化运算import numpy as np def convert_analog_values(samples, a, b): raw np.array([s[analog] for s in samples], dtypenp.float32) return raw * a b # 向量化运算比循环快100倍以上内存映射处理大文件def parse_large_binary_dat(file_path, analog_count): with open(file_path, rb) as f: # 使用内存映射避免加载整个文件 mm mmap.mmap(f.fileno(), 0, accessmmap.ACCESS_READ) # 处理代码...多线程处理对多个COMTRADE文件可并行解析8. 扩展应用场景解析后的数据可用于更多高级分析继电保护动作分析比较多个保护装置的录波数据电能质量分析计算谐波畸变率、电压暂降等指标设备状态评估通过电流波形特征判断断路器状态数字孪生验证将实测数据与仿真模型对比例如计算THD(总谐波畸变率)from scipy.fft import fft def calculate_thd(signal, fundamental_freq50, sample_rate5000): n len(signal) yf fft(signal) xf np.linspace(0, sample_rate//2, n//2) fundamental_idx np.argmin(np.abs(xf - fundamental_freq)) harmonic_power np.sum(np.abs(yf[1:n//2])**2) - np.abs(yf[fundamental_idx])**2 return np.sqrt(harmonic_power) / np.abs(yf[fundamental_idx])

相关新闻