☰
MATLAB局部放电图谱绘制详解:从PRPD到PRPS的完整实现
2026/10/20 18:38:27 网站建设 项目流程

简介:一套基于MATLAB的局部放电数据分析与可视化项目,聚焦三维图谱绘制,面向电力设备状态监测、高压绝缘诊断领域的工程师与研究人员,也适合正在学习MATLAB数据处理和图形绘制的学生。局部放电是高压电力设备绝缘劣化的重要征兆,通过特高频信号分析,可以穿透绝缘材料捕捉丰富的放电信息,提前发现潜在故障。压缩包共2个文件,包含1个m格式源码脚本和1个csv格式实测数据文件,整体大小约27.75MB,数据与代码配套,下载后即可直接运行。目前已有5765人学习下载。资源内提供实测特高频局部放电原始数据,源码完整展示从文件读取、数据预处理到三维谱图生成的全流程,绘制结果能同时呈现放电强度、频率与时间的多维关系,辅助研究者从不同角度理解放电特征,有效支持设备状态评估。代码结构简洁且带必要注释,既可用于教学演示,也可作为其他传感器数据分析与可视化任务的模板,方便二次开发与参数调整。

1. 这包东西能干什么:把放电原始波形变成能写进报告的三类图谱

拿到一部高压设备的局部放电检测数据,最常见的困惑是:示波器导出的 CSV 动辄几十万行,看波形看不出绝缘缺陷类型,写检测报告更是无从下手。这个 MATLAB 项目解决的就是这个“读数到图谱”的落地问题——它把局部放电的工频相位、放电幅值、放电次数整理成 PRPD 相位分辨谱图、PRPS 脉冲序列谱图和三维放电图谱,直接输出可存成 PNG/FIG 的 Figure。适合正在做局部放电模式识别、绝缘状态评估,或者只是被数据整理逼疯的高压试验工程师和研究生。核心价值在于:全套源码带原始放电数据,不用你自己造数据就能复现每一张图,而且代码结构允许替换成你自己的检测数据。

2. 项目结构与技术选型:一个数据处理脚本加一组真实放电数据

2.1 拿到压缩包后先看清目录

解压后根目录下是两层结构:顶层按功能分成code、data、fig三个文件夹,code里再按处理阶段拆分脚本。整体结构如下:

matlab局部放电图谱绘制数据+源码/ ├── code/ │ ├── load_pd_data.m # 数据导入与预处理 │ ├── compute_phase.m # 工频相位提取(基于过零检测) │ ├── plot_prpd.m # PRPD谱图绘制 │ ├── plot_prps.m # PRPS脉冲序列谱图 │ ├── plot_3d_spectrum.m # 三维放电图谱 │ └── run_all.m # 一键执行全部绘制流程 ├── data/ │ ├── sample_1_1_raw.csv # 原始采集数据: 时间、电压、放电幅值 │ └── sample_1_2_raw.csv # 第二组样本, 用于对比不同放电类型 └── fig/ # 运行后输出图片的目录

数据文件是标准的表格式 CSV,每行对应一次采样记录,列包含相对时间、电压瞬时值和放电脉冲幅值。因为在 MATLAB 里用readtable就能直接读,没有引入额外数据库依赖。整个项目跑通只需要 MATLAB R2019b 及以上版本,工具箱方面只用到基础的Signal Processing Toolbox(滤波器设计)和Statistics and Machine Learning Toolbox(hist3做二维统计)。如果你的环境中没有统计工具箱,PRPD 谱图那个脚本需要稍微改动(后面会提到替代写法)。

2.2 数据格式和处理链路

data目录里的 CSV 每行是一条采样点记录,典型结构如下表:

列名含义示例
t_sec相对时间,单位秒0.0000001
v_kv电压瞬时值,用于定位工频相位12.345
pd_mv局部放电脉冲幅值,无放电时为噪声电压45.678

预处理脚本load_pd_data.m干两件事:第一,读表格后截取完整工频周期。通常采样率在 MHz 级,50Hz 工频下每个周期有数万个样本点,直接整段分析会让相位计算产生偏移,所以脚本先用电压过零检测定位周期起点;第二,把放电幅值按正负半周分开,并剔除幅值低于阈值的噪声段,这里阈值默认取峰值的 10%。

function pd_data = load_pd_data(csv_path, fs, power_freq) % 读取原始CSV并按工频周期切分 raw = readtable(csv_path); t = raw.t_sec; v = raw.v_kv; pd = raw.pd_mv; % 过零检测: 找到电压从负到正的时刻作为周期起点 vz_idx = find(v(1:end-1) < 0 & v(2:end) >= 0); period_len = round(fs / power_freq); % 一个周期的采样点数 % 这里保守处理: 只保留起点后一个完整周期的数据 ph = zeros(sum(vz_idx > period_len & vz_idx + period_len <= length(v)), 1); amp = ph; cnt = 0; for i = 1:length(vz_idx) if vz_idx(i) > period_len && vz_idx(i) + period_len <= length(v) cnt = cnt + 1; win_v = v(vz_idx(i) : vz_idx(i)+period_len-1); win_pd = pd(vz_idx(i) : vz_idx(i)+period_len-1); % 把电压瞬时值换算成相位: 默认以正峰值处为90度 shift = find(win_v == max(win_v), 1); ph(cnt) = mod(( (1:period_len) - shift ) / period_len * 360, 360); amp(cnt) = max(win_pd); end end pd_data = table(ph(1:cnt), amp(1:cnt), 'VariableNames', {'phase','amplitude'}); end

参数说明与设计原因:函数头传入fs采样率和power_freq工频,避免硬编码 50Hz,方便扩展到 60Hz 电网。过零检测较容易受到直流偏置干扰,如果电压有直流分量,建议先对v做detrend去趋势处理,否则查到的周期起点是偏的,最终谱图会在相位轴上整体平移。

2.3 依赖函数与运行入口

run_all.m是入口脚本,它按顺序调用三个绘制函数,并在fig目录输出三张 PNG 和对应的 FIG 源文件。脚本开头设置了统一的配色方案和字体大小,保证多组数据导出的图片风格一致。无特殊依赖,理论上在 R2019b 以后版本都能运行。如果你机器上装了更高版本,hist3和pcolor的 API 都没变,属于稳定性较高的用法。程序跑完后在fig目录里看结果,如果某张图没生成,大概率是数据读取阶段出了问题,而不是绘图函数的问题。

3. 核心源码拆解:一个请求从进入到返回的完整链路

本章完整展开三个谱图的实现细节。重点放在 PRPD 和 PRPS 两个脚本上,因为三维谱图本质上是在 PRPD 的基础上多画一个轴。

3.1 PRPD 谱图:放电次数在相位-幅值平面上的密度分布

PRPD(Phase Resolved Partial Discharge)是最常用的局部放电图谱,横轴是工频相位 0° 到 360°,纵轴是放电幅值,颜色深浅表示该位置出现放电的次数。它的物理意义是把大量随机放电事件按相位累计,得到放电“指纹”。同一个绝缘缺陷在 PRPD 上会呈现特定的形状——比如自由金属颗粒放电会在正负半周各出现一个竖条状簇,而气隙放电通常是对称的兔耳形。plot_prpd.m脚本里最核心的是用hist3做二维直方图统计,然后pcolor出图:

function plot_prpd(phase, amplitude) % phase: 0~360度; amplitude: 放电幅值(去掉符号, 取绝对值) x_edges = 0:2:360; % 相位窗口: 2度一段, 共180个窗口 y_edges = linspace(0, max(amplitude)*1.05, 128); % 幅值区间: 按最大值动态划分 % hist3返回二维计数矩阵, 第1维是相位, 第2维是幅值 N = hist3([phase, amplitude], 'Edges', {x_edges, y_edges}); figure('Color','w','Position',[100,100,800,600]); pcolor(x_edges, y_edges, N'); shading interp; colormap(jet); colorbar; xlabel('工频相位 / °'); ylabel('放电幅值 / mV'); title('PRPD谱图'); set(gca, 'YDir', 'normal'); end

这段代码里值得注意的点有两个。第一,hist3返回的矩阵维度是(相位窗口数 × 幅值窗口数),而pcolor的 X 轴和数据矩阵有对应的排列要求,所以绘图时对矩阵做了转置N',否则图会变成横向的。第二,相位窗口设为 2° 而不是 1°,原因是一般放电样本量在几千到几万个脉冲,窗口太小会让相邻相位段的计数抖动很大,图谱呈现“毛刺”感;窗口太大又看不清放电起始相位和熄灭相位的边界。2° 是工程上比较折中的选择。如果你需要更平滑的图谱,可以把着色方式从shading interp改为shading flat,但视觉效果会差一些。

3.2 PRPS 谱图:脉冲序列的时域展开视图

PRPS(Phase Resolved Pulse Sequence)谱图和 PRPD 不同,它把脉冲出现的时间顺序也保留下来。横轴是工频周期序号,纵轴是放电幅值,颜色表示脉冲出现的相位。这样可以看清放电在时间上的连续性和间歇性,比如外部干扰导致的放电往往是随机散点,而内部缺陷放电会形成明显的周期性色带。

function plot_prps(cycle_idx, phase, amplitude) % cycle_idx: 每个脉冲所属的工频周期序号(整数) % phase: 该脉冲在周期内的相位角度 figure('Color','w','Position',[100,100,900,500]); % scatter适合大量散点, 点大小设为8, 颜色按相位映射 scatter(cycle_idx, amplitude, 8, phase, 'filled'); colormap(hsv); colorbar; xlabel('工频周期序号'); ylabel('放电幅值 / mV'); caxis([0 360]); title('PRPS谱图'); end

这个函数和 PRPD 的数据准备逻辑不同:PRPD 只需要相位和幅值两个列;PRPS 必须额外提供周期序号。周期序号在load_pd_data.m里有现成输出,你在调用plot_prpd之后把cycle_idx一并传进来即可。绘制时用scatter而不是plot,是因为放电脉冲在时间轴上是不连续的,用连线反而会把不同时刻的脉冲错误地连成一条线,产生假象。caxis([0 360])强制颜色轴范围对应相位,这样多组数据之间颜色含义是一致的。如果你的数据是连续放电(比如气隙击穿),PRPS 看起来就是一条横贯整个时间轴的长色带;如果是外部导体电晕,PRPS 上会看到东一簇西一簇的碎片。时间轴上如果周期数特别多(超过几百个周期),建议先做周期分箱聚合,再用imagesc显示,否则图会太密。

3.3 三维图谱:把相位、幅值、放电次数画成地形图

三维图谱本质上是 PRPD 的另一种展示形式:X 轴为相位,Y 轴为幅值,Z 轴为放电次数,用带颜色的曲面表示。很多场合比二维 pcolor 更直观,尤其是给非电气专业的人解释放电形态时。绘图脚本先复用 PRPD 里的hist3统计矩阵,然后直接用surf出图:

function plot_3d_spectrum(phase, amplitude) x_edges = 0:2:360; y_edges = linspace(0, max(amplitude)*1.05, 128); N = hist3([phase, amplitude], 'Edges', {x_edges, y_edges}); figure('Color','w','Position',[100,100,1000,600]); surf(x_edges, y_edges, N'); shading interp; colormap(jet); view(45, 30); % 设定视角: 方位角45度, 仰角30度 xlabel('工频相位 / °'); ylabel('放电幅值 / mV'); zlabel('放电次数'); title('三维放电图谱'); end

拿到代码后建议改的第一个参数就是view(45, 30),不同视角下图谱的“山峰”和“山谷”呈现完全不同,选一个让人能一眼看到放电集中区的角度再保存图。如果有大量空白区域(放电次数很少),可以把 Z 轴范围压缩,比如zlim([0 max(N(:)) * 0.9]),避免图谱下半部分全部被空白拉低高度。surf在数据量大的时候渲染比较慢,如果样本点超过几万个,可以在绘制前先对 N 矩阵做一次 2×2 池化降采样,速度能快很多。

3.4 一键运行与结果输出

run_all.m是整套流程的调度者,顺序做了四件事:读取 CSV、计算相位、循环绘制三个图、保存图片。代码结构如下:

% 参数设置区 fs = 100e6; % 采样率: 100MHz power_freq = 50; % 工频频率 out_dir = fullfile('..', 'fig'); if ~exist(out_dir, 'dir'); mkdir(out_dir); end % 遍历data目录下的所有CSV文件 data_files = dir(fullfile('..', 'data', '*.csv')); for k = 1:length(data_files) pd_data = load_pd_data(fullfile(data_files(k).folder, data_files(k).name), fs, power_freq); % 按当前数据动态生成文件名, 避免覆盖 base_name = erase(data_files(k).name, '.csv'); plot_prpd(pd_data.phase, pd_data.amplitude); saveas(gcf, fullfile(out_dir, [base_name, '_PRPD.png'])); plot_prps(pd_data.cycle_idx, pd_data.phase, pd_data.amplitude); saveas(gcf, fullfile(out_dir, [base_name, '_PRPS.png'])); plot_3d_spectrum(pd_data.phase, pd_data.amplitude); saveas(gcf, fullfile(out_dir, [base_name, '_3D.png'])); end

一个小设计是文件命名带base_name,这样你后续加入第三、第四组样本时,输出图片不会互相覆盖,每次都生成独立的 PNG。因为是在for循环里连续画三张图,如果你发现运行时只有最后一张图显示在当前屏幕上,这很正常,saveas已经把每个 Figure 的内容写进文件了。建议把脚本最后一段改成全部绘制完后统一close all,以免内存里积累太多 Figure 窗口。任何一次运行失败,建议从load_pd_data开始排查,多数情况是 CSV 文件里存在空行或者数据格式和示例不一致导致读取报错。

4. 常见问题与避坑:从运行报错到图谱异常的排查记录

4.1 图整体错位,放电分布不在正确的相位区间

现象:PRPD 图谱中放电簇的位置明显偏移,本该出现在 0° 附近的放电跑到了 80° 附近。

原因:过零检测找的“周期起点”不是真正的电压过零。如果 CSV 数据里的电压信号混入了直流偏置,find(v(1:end-1) < 0 & v(2:end) >= 0)找到的过零点会整体前移或后移,相位换算自然失真。

解决:在load_pd_data.m里读取电压后先执行v = detrend(v, 'constant'),把电压的直流分量削掉再做周期起点定位。如果是现场采集的电压有谐波畸变,更稳妥的做法是用锁相环的思想,对电压做带通滤波(中心频率 50Hz)后再过零检测。

4.2hist3报错提示向量长度不一致

现象:运行plot_prpd时报类似“EDGES must be a vector of length 2”的错误。

原因:hist3的Edges参数要求是一个 1×2 的元胞数组,每个元素是一个单调递增的向量。新手容易把{x_edges, y_edges}漏掉花括号,或者把两个维度的边界向量长度写成一维的相同长度,导致维度不匹配。

解决:严格保持'Edges', {x_edges, y_edges}的写法。确认x_edges是 181 个元素(0:2:360),y_edges是 128 个元素,两者长度本来就不同,这是正常的。

4.3 所有图画出来一片纯蓝或纯红

现象:pcolor 的图谱没有层次感,整个图只有一种颜色。

原因:有两种情形。第一种是数据里存在极小别的离群点,比如一个 5000mV 的异常脉冲,导致max(amplitude)被拉得很大,正常放电幅值区间的计数差异显示不明显;第二种是amplitude里存在 NaN 值,hist3会把 NaN 当作单独分组处理,干扰颜色映射。

解决:在绘制前加一句清洗:amplitude(isnan(amplitude)) = [],并考虑用prctile(amplitude, 99)替代max作为幅值轴上限,这样能把离群点排除在显示范围之外。数据清洗是这类绘图项目最关键的一步,血泪经验是永远不要信任现场采集数据的“原生状态”。

4.4 运行到plot_prps时 MATLAB 卡死或内存不足

现象:程序在scatter那行卡顿,或者提示内存不足。

原因:当周期序号跨度很大、脉冲数量达到百万级别时,scatter的每一个点都是一个对象,绘制上百万个点直接吃满内存。这在长期连续采集的数据中经常发生。

解决:先在代码中对数据做降采样:只随机抽取不超过 5 万个脉冲用于显示,或者按周期分箱后用imagesc绘制密度矩阵。抽样不会影响 PRPS 的整体形态,只是视觉上点变得稀疏一些。从那次之后,我只要处理超过 20 万点的散点图,第一反应就是先降采样。

4.5 替换自己的数据后跑不出来,报错“未定义变量或函数”

现象:把自己采集的 CSV 放进data目录,运行run_all.m,结果找不到函数或读取出错。

原因:很大概率是尝试打开压缩包里的源文件直接按F5运行,而没有把当前文件夹切到code目录下。run_all.m里用了相对路径..\data,如果当前目录不是code,路径就指向了错误位置。

解决:在 MATLAB 编辑器里打开run_all.m后,先点击“更改文件夹”让当前工作区切到脚本所在目录,再运行。替换数据时也注意保持 CSV 的列顺序和原始数据一致,最省事的办法是用示例 CSV 另存为模板,在 Excel 里只改数值不调整列。

5. 二次开发方向:从图谱到特征提取与自动识别

拿到这套源码,最容易见效的改造是加入特征量化——把图谱从“肉眼看形状”升级成“算数字做判断”。具体做法是在plot_prpd输出图之后,顺手计算放电的起始相位、熄灭相位、正负半周不对称度和相位分布偏斜度,这些数字可以直接用来区分电晕放电、气隙放电和沿面放电,也是现在很多绝缘诊断系统的判断依据。代码只需要十几行:

% 在plot_prpd函数末尾追加特征计算 N_sum = sum(N, 2); % 按相位维度累计放电次数 peak_phase = find(N_sum == max(N_sum)); % 主放电相位位置(度) skewness_pd = skewness(N_sum); % 相位分布偏斜度 phase_width = sum(N_sum > 0.1 * max(N_sum)) * 2; % 放电相位宽度(度) fprintf('主放电相位: %.1f°, 相位宽度: %.1f°, 偏斜度: %.2f\n', ... peak_phase(1), phase_width, skewness_pd);

改完后同一批数据会直接在命令窗口显示三组特征值,便于横向比较。如果你有不同工况下的数据,建议把特征值写到一个 CSV 表格里,每行一组数据,后续可以用fitcdiscr做线性判别,或者输出给深度学习模型当输入特征。要注意:计算出的放电相位宽度是受相位窗口(默认 2°)影响的,如果你把窗口改成 1°,宽度值会变化,所以横向比较时必须固定窗口参数。另一条值得走的路线是把hist3输出的矩阵直接归一化成灰度图保存,作为轻量级图像输入给 CNN 做模式识别,这就需要额外安装 Deep Learning Toolbox。

这套源码给我的最大启发是,它把“图谱绘制”和“数据质量检查”绑在了一条链路上。我后来每次换一组新的现场数据,都会先跑一遍run_all,看图是否正常生成,再决定要不要做更深一步的特征分析。图谱本身就是数据质量的晴雨表,一团乱麻一样均匀分布的点子往往意味着噪声没有剔除干净,而不是设备真的放出了那么多次电。从那以后,我拿到任何一线采集数据,都强制走一遍“清洗—过零定位—基础谱图—特征量”这个流程,确认没问题再谈后续建模。希望这套图谱绘制工具能帮你少走一段弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询