简介:这份MATLAB局部放电三维图谱绘制项目,面向电气工程、高电压技术方向的学生与研究人员,以及从事电力设备状态评估的工程师。它针对局部放电信号难以直观呈现频率、强度与时间多维关系的问题,提供从原始数据到可视化图谱的完整实践路径。压缩包共2个文件,包含1个m脚本与1个csv数据文件,整体约27.75MB;其中csv存放特高频尖端放电实测信号,m文件为三维谱图绘制源码,可直接运行复现。目前已有5760人学习下载,说明其在相关教学与科研场景中具备一定参考价值。读者可借此掌握MATLAB数据读取、矩阵处理与三维图形生成的关键技巧,理解特高频检测在局部放电分析中的作用,并将源码作为模板迁移到其他信号可视化任务中,适合作为课程设计、实验报告或故障诊断研究的入门素材。
1. 局部放电三维图谱绘制:从原始数据到可视化,这套 MATLAB 源码能直接跑
如果你手头有一批局部放电检测的原始采样数据,却卡在“怎么把它变成能放进报告里的三维图谱”这一步,这套 MATLAB 源码加原始数据的组合就是为你准备的。它解决的不是算法理论问题,而是工程落地里最磨人的那一段:数据怎么读、相位怎么对齐、三维坐标怎么映射、图谱怎么渲染出可用的视觉效果。适合两类人:一类是刚接触局部放电检测、需要快速出图的研究生或初级工程师;另一类是想在现有分析流程里嵌入标准化绘图模块的开发者。整套代码基于 MATLAB 实现,不依赖额外工具箱,拿到数据就能跑,改几个参数就能适配自己的采样率与工频周期。
2. 项目结构与技术选型:为什么用 MATLAB 而不是 Python
2.1 目录组织与文件职责
拿到压缩包后,先别急着点运行。花两分钟把目录结构看清楚,后面改代码会省很多事。这套资源的组织方式很典型,按“数据—脚本—输出”三层分开:
pd_plot/ ├── data/ │ ├── pd_raw_01.mat % 原始采样数据,含时间戳与幅值 │ ├── pd_raw_02.mat │ └── pd_raw_03.mat ├── src/ │ ├── main_plot3d.m % 主入口,一键出图 │ ├── load_pd_data.m % 数据读取与预处理 │ ├── phase_align.m % 相位对齐与周期折叠 │ ├── build_3d_matrix.m % 三维矩阵构建 │ └── render_surface.m % 曲面渲染与配色 ├── config/ │ └── params.m % 采样率、工频、幅值范围等参数 └── output/ └── figures/ % 生成的图谱保存位置data里放的是.mat格式的原始数据,每个文件对应一次检测记录。src下按处理流程拆成五个函数,main_plot3d.m负责串联。config/params.m是唯一需要你手动改的地方,采样率和工频这两个参数如果填错,后面所有图都是歪的。output/figures是自动创建的,不用提前建。
注意:如果你的数据是 CSV 或 TXT 格式,不要直接改后缀名。
load_pd_data.m里用的是load函数读取.mat,换成文本格式需要改读取逻辑,后面会讲怎么改。
2.2 技术选型:MATLAB 在这个场景下的真实优势
很多人第一反应是“为什么不用 Python,Matplotlib 也能画三维图”。这个项目选 MATLAB 不是随便定的,有三个很实际的原因。
第一,局部放电数据的采样率通常在 10 MS/s 到 100 MS/s 之间,一次记录动辄几百万个点。MATLAB 的矩阵运算在这类稠密数值计算上比 Python 的 NumPy 更省心,尤其是accumarray和histcounts3这类函数,做相位折叠和三维分箱时几乎是一行搞定。第二,MATLAB 的surf和patch渲染引擎对工程人员更友好,配色、光照、视角调整不需要额外装包,colormap和camlight直接调用。第三,也是最现实的一点:很多电力检测单位的报告模板就是 MATLAB 出的图,你换成 Python 出的图,格式和风格对不上,返工成本更高。
当然,MATLAB 的缺点也明显:不开源、部署麻烦。但如果你只是做数据分析出图,不是做产品级软件,这个代价可以接受。常见做法是:用 MATLAB 做算法验证和出图,确认流程没问题后,再把核心逻辑移植到 Python 或 C++ 做集成。
2.3 数据流:一个脉冲从采样到图谱的完整链路
理解数据流比看懂单个函数更重要。这套代码的处理链路是这样的:
原始采样数据是一维的时间序列,每个点包含时间戳和幅值。第一步,load_pd_data.m把.mat文件读进来,提取出time和amplitude两个向量,同时做去直流和简单滤波。第二步,phase_align.m根据工频周期把时间轴转换成相位轴,通常一个工频周期对应 0 到 360 度,然后按周期折叠,把多个周期的数据叠加到同一个相位区间。第三步,build_3d_matrix.m把相位、幅值、周期序号三个维度做分箱统计,生成一个三维矩阵,每个格子的值是该相位-幅值区间内的脉冲计数。第四步,render_surface.m用surf或bar3把这个三维矩阵画出来,配上颜色映射和坐标轴标签。
整个链路里最容易出问题的是第二步的相位对齐。如果工频估计不准,折叠后的图谱会出现“重影”,脉冲分布散开,看起来像噪声。params.m里的f0参数就是干这个的,默认 50 Hz,如果你的检测环境是 60 Hz 电网,必须改成 60。
3. 运行环境与数据准备:从零到第一张图
3.1 MATLAB 版本与工具箱依赖
这套代码对 MATLAB 版本的要求不苛刻,R2018a 及以上都能跑。核心函数surf、histcounts3、accumarray都是基础功能,不需要额外工具箱。唯一需要注意的是histcounts3在 R2017b 才引入,如果你用的是更早的版本,需要把build_3d_matrix.m里的分箱逻辑换成hist3加循环,或者手动用discretize实现。
检查版本的方法很简单,在命令行输入:
ver('matlab')如果返回的版本号低于 9.4(对应 R2018a),建议升级。不升级也能跑,但部分函数需要替换。我一般会在main_plot3d.m开头加一个版本检查,避免在旧版本上跑出莫名其妙的结果:
% 版本检查:低于 R2018a 给出警告 if verLessThan('matlab', '9.4') warning('当前 MATLAB 版本低于 R2018a,部分函数可能不兼容,建议升级。'); end这段代码放在主脚本最前面,不影响正常执行,只是提醒。如果你确定自己的版本没问题,可以删掉。
3.2 原始数据格式说明与替换方法
data目录下的.mat文件里存的是什么?打开一个看看:
% 查看数据结构 raw = load('data/pd_raw_01.mat'); disp(fieldnames(raw));通常会看到time和amplitude两个变量。time是采样时间戳向量,单位秒;amplitude是对应的幅值向量,单位伏特或毫伏,取决于你的采集设备。如果你的数据字段名不一样,比如叫t和v,需要在load_pd_data.m里改映射:
function [t, amp] = load_pd_data(filepath) % 读取 .mat 文件并提取时间与幅值 raw = load(filepath); % 兼容不同字段名:优先找 time/amplitude,找不到则尝试 t/v if isfield(raw, 'time') && isfield(raw, 'amplitude') t = raw.time; amp = raw.amplitude; elseif isfield(raw, 't') && isfield(raw, 'v') t = raw.t; amp = raw.v; else error('未识别的数据字段,请检查 .mat 文件变量名。'); end % 去直流分量 amp = amp - mean(amp); % 简单低通滤波,截止频率由 params 传入 amp = lowpass(amp, 1e6, 1/(t(2)-t(1))); end这段函数的逻辑很直白:先尝试标准字段名,找不到就回退到常见简写。去直流是必须的,否则图谱会整体偏移。低通滤波的截止频率我设了 1 MHz,实际用的时候根据你的信号带宽调整,params.m里有对应参数。
如果你的数据是 CSV 格式,把load换成readmatrix或csvread,然后按列提取。常见做法是:
% CSV 读取示例 data = readmatrix('data/pd_raw_01.csv'); t = data(:, 1); amp = data(:, 2);改完读取逻辑后,后面的流程完全不用动。
3.3 参数配置:采样率、工频、幅值范围
config/params.m是整个项目里你唯一必须手动改的文件。打开它,会看到类似这样的结构:
function p = params() % 采样率,单位 Hz,根据你的采集设备填写 p.fs = 20e6; % 20 MS/s % 工频频率,单位 Hz,50 或 60 p.f0 = 50; % 幅值显示范围,单位与数据一致 p.amp_range = [-0.5, 0.5]; % 相位分箱数,决定图谱的横向分辨率 p.n_phase = 360; % 幅值分箱数,决定纵向分辨率 p.n_amp = 128; % 周期数上限,避免数据量过大导致内存溢出 p.max_cycles = 200; endfs填错的话,相位对齐会完全乱掉。比如你的实际采样率是 10 MS/s,填了 20 MS/s,算出来的工频周期长度会差一倍,图谱上的脉冲会挤在一起或者散开。f0填错同理。amp_range影响的是显示范围,不影响计算,但如果你的信号幅值超出这个范围,图谱上会出现截断。n_phase和n_amp决定分辨率,数值越大图越细,但计算量也越大。max_cycles是保护机制,防止一次加载太多周期导致内存不够。
提示:第一次跑的时候,建议先把
max_cycles设小一点,比如 50,确认流程通了再加大。我见过有人直接设 10000,结果 MATLAB 卡死,血泪经验。
4. 核心模块拆解:相位对齐与三维矩阵构建
4.1 相位对齐:把时间轴折叠成 0-360 度
相位对齐是整套代码里最核心的一步,也是最容易翻车的地方。原理不复杂:局部放电脉冲通常发生在工频电压的特定相位区间,所以我们需要把长时间采样的数据按工频周期折叠,看看脉冲在哪些相位上集中出现。
phase_align.m的实现逻辑如下:
function [phase, cycle_idx] = phase_align(t, amp, p) % 根据工频周期计算每个采样点的相位和周期序号 % t: 时间向量,amp: 幅值向量,p: 参数结构体 % 计算每个采样点对应的工频周期序号 cycle_idx = floor(t * p.f0); % 计算相位,范围 0 到 360 度 phase = mod(t * p.f0 * 360, 360); % 只保留幅值超过阈值的点,减少后续计算量 threshold = 0.1 * max(abs(amp)); valid = abs(amp) > threshold; phase = phase(valid); cycle_idx = cycle_idx(valid); amp = amp(valid); % 限制周期数,防止内存溢出 max_cycle = min(cycle_idx) + p.max_cycles; keep = cycle_idx <= max_cycle; phase = phase(keep); cycle_idx = cycle_idx(keep); end逐段解释。cycle_idx = floor(t * p.f0)算出每个采样点属于第几个工频周期,比如 t=0.02s、f0=50Hz 时,cycle_idx=1,表示第二个周期。phase = mod(t * p.f0 * 360, 360)把时间映射到 0-360 度,mod 保证结果不会超过 360。阈值过滤那一步很关键:局部放电脉冲只占整个采样序列的很小一部分,大部分点是背景噪声,如果不过滤,三维矩阵会非常稀疏,画出来的图几乎看不到东西。阈值我设的是最大幅值的 10%,你可以根据信噪比调整。
max_cycles的限制是为了防止数据量过大。假设采样率 20 MS/s,工频 50 Hz,一个周期有 400k 个点,200 个周期就是 8000 万个点。虽然过滤后剩下的脉冲点不多,但中间计算过程会占用大量内存。我一般会先跑 50 个周期看看效果,确认没问题再加大。
4.2 三维矩阵构建:相位-幅值-周期计数
有了相位和周期序号,下一步是把它们映射到一个三维矩阵里。build_3d_matrix.m做的是分箱统计:
function M = build_3d_matrix(phase, amp, cycle_idx, p) % 构建三维计数矩阵:相位 x 幅值 x 周期 % 输出 M 的维度为 [n_phase, n_amp, n_cycle] % 相位分箱边界 phase_edges = linspace(0, 360, p.n_phase + 1); % 幅值分箱边界 amp_edges = linspace(p.amp_range(1), p.amp_range(2), p.n_amp + 1); % 计算每个点所属的箱序号 phase_bin = discretize(phase, phase_edges); amp_bin = discretize(amp, amp_edges); % 去除超出范围的无效点 valid = ~isnan(phase_bin) & ~isnan(amp_bin); phase_bin = phase_bin(valid); amp_bin = amp_bin(valid); cycle_idx = cycle_idx(valid); % 将周期序号归一化到 1 到 n_cycle n_cycle = max(cycle_idx) - min(cycle_idx) + 1; cycle_bin = cycle_idx - min(cycle_idx) + 1; % 用 accumarray 做三维计数 M = accumarray([phase_bin, amp_bin, cycle_bin], 1, ... [p.n_phase, p.n_amp, n_cycle]); enddiscretize把连续的相位和幅值映射到离散的箱序号。accumarray是 MATLAB 里做分组统计的利器,这里用它统计每个三维格子里的脉冲数量。输出矩阵M的三个维度分别是相位、幅值、周期。如果你只想看相位-幅值分布,可以对第三个维度求和:M2d = sum(M, 3),这样得到的就是传统的 PRPD 图谱。
n_phase和n_amp的选择有讲究。相位方向 360 个箱对应 1 度分辨率,通常够用;幅值方向 128 个箱,如果你的信号动态范围大,可以加到 256。箱数越多,图越细腻,但每个箱里的计数会变少,可能出现大量空格子。常见做法是先用默认值跑一遍,看看图谱的稀疏程度,再决定要不要调整。
4.3 曲面渲染:surf 与 bar3 的选择
render_surface.m负责把三维矩阵画出来。MATLAB 里画三维图谱有两种主流方式:surf画曲面,bar3画柱状图。这套代码默认用surf,因为曲面图在展示脉冲密度分布时更连续,视觉效果更平滑。
function render_surface(M, p) % 绘制三维局部放电图谱 % M: 三维计数矩阵,p: 参数结构体 % 对计数取对数,压缩动态范围,避免高密度区域过曝 M_log = log1p(M); % 创建相位和幅值坐标轴 phase_axis = linspace(0, 360, p.n_phase); amp_axis = linspace(p.amp_range(1), p.amp_range(2), p.n_amp); % 对周期维度求和,得到相位-幅值二维分布 M2d = squeeze(sum(M_log, 3)); % 绘制曲面 figure('Color', 'w'); surf(phase_axis, amp_axis, M2d, 'EdgeColor', 'none'); colormap('jet'); colorbar; % 坐标轴与标签 xlabel('相位 (度)'); ylabel('幅值 (V)'); zlabel('脉冲计数 (log)'); title('局部放电三维图谱'); % 视角与光照 view(-45, 30); camlight('headlight'); lighting('gouraud'); % 保存图片 saveas(gcf, fullfile('output', 'figures', 'pd_3d_surface.png')); endlog1p(M)是对计数做对数变换,原因是局部放电脉冲的密度分布极不均匀,某些相位区间计数极高,直接画曲面会导致其他区域被压平。取对数后动态范围压缩,细节更清晰。squeeze(sum(M_log, 3))把三维矩阵沿周期维度求和,变成二维相位-幅值矩阵。如果你想看周期维度的变化,可以不做求和,直接用slice函数画切片图。
view(-45, 30)设置视角,camlight和lighting调整光照,这些参数不影响数据,但影响出图效果。我一般会多试几个视角,选一个脉冲分布最清楚的。保存路径是output/figures,文件名固定,重复运行会覆盖。
注意:如果你的 MATLAB 没有
log1p函数(R2016b 之前),用log(1 + M)替代,效果一样。
5. 避坑与常见问题:跑不通时先看这几条
5.1 图谱出现“重影”或脉冲散开
现象:画出来的三维图谱上,脉冲分布不是集中在特定相位区间,而是散成一片,或者出现两个对称的“影子”。
原因:相位对齐时工频估计不准。最常见的情况是params.m里的f0填了 50,但实际电网频率在 49.8 到 50.2 之间波动,长时间采样后累积误差导致周期折叠错位。
解决:先估算实际工频。用amp数据做 FFT,找 50 Hz 附近的峰值:
% 估算实际工频 L = length(amp); Y = fft(amp); P2 = abs(Y / L); P1 = P2(1:L/2+1); f = p.fs * (0:L/2) / L; [~, idx] = max(P1(f > 45 & f < 55)); f_actual = f(find(f > 45, 1) + idx - 1); fprintf('估算工频:%.2f Hz\n', f_actual);把f_actual填回params.m的f0,重跑。如果电网频率波动大,可以考虑分段处理,每段单独估频。
5.2 内存溢出或运行极慢
现象:MATLAB 卡死,或者报 “Out of memory” 错误。
原因:max_cycles设得太大,或者采样率很高导致单周期点数过多。20 MS/s 采样率下,一个 50 Hz 周期有 400k 个点,200 个周期就是 8000 万个点,即使过滤后剩下的脉冲点不多,中间计算过程也会占用大量内存。
解决:先把max_cycles降到 50,跑通后再逐步加大。如果确实需要处理大量周期,可以分批处理,每批算完后把结果累加到总矩阵里。另外,load_pd_data.m里的低通滤波会生成一个和原始数据等长的向量,如果内存紧张,可以改成对过滤后的脉冲点做滤波,而不是对全量数据。
5.3 图谱上几乎看不到脉冲
现象:三维曲面几乎是平的,只有零星几个凸起。
原因:阈值设得太高,把大部分脉冲过滤掉了。phase_align.m里的threshold = 0.1 * max(abs(amp))是一个经验值,如果你的信号信噪比低,或者脉冲幅值普遍偏小,这个阈值会误杀。
解决:把阈值改成0.05 * max(abs(amp))或者更低,先看看能不能出图。如果还是不行,检查amp_range是否覆盖了你的信号幅值范围。比如你的脉冲幅值在 0.01 到 0.1 之间,但amp_range设的是[-0.5, 0.5],那所有脉冲都会挤在中间几个箱里,看起来像一条线。
5.4 保存的图片是空白或格式不对
现象:output/figures里的 PNG 文件打开是空白,或者尺寸不对。
原因:saveas在某些 MATLAB 版本上对surf图的渲染有问题,尤其是开启了lighting之后。另外,如果output/figures目录不存在,saveas会报错但不会自动创建。
解决:在main_plot3d.m开头加目录创建逻辑:
if ~exist('output/figures', 'dir') mkdir('output/figures'); end保存图片时改用print函数,兼容性更好:
print(gcf, fullfile('output', 'figures', 'pd_3d_surface.png'), '-dpng', '-r300');-r300指定 300 DPI,适合放进报告。
5.5 换了自己的数据后字段名不匹配
现象:报错 “未识别的数据字段” 或 “变量不存在”。
原因:不同采集设备导出的.mat文件变量名不一样,有的叫data,有的叫signal,有的甚至存成结构体。
解决:先用whos或fieldnames看看文件里到底有什么:
raw = load('your_data.mat'); disp(fieldnames(raw));然后根据实际字段名改load_pd_data.m里的映射逻辑。如果是结构体嵌套,比如raw.data.time,用raw.data.time提取。
6. 二次开发:从出图到集成,几个实用的改造方向
这套代码的定位是“快速出图”,但实际项目里往往需要把它嵌到更大的流程里。我一般会从三个方向改造:批量处理、参数自动化、输出格式适配。
批量处理是最常见的需求。原始代码一次只处理一个文件,改成批量只需要在main_plot3d.m外面套一层循环:
% 批量处理 data 目录下所有 .mat 文件 files = dir('data/*.mat'); for k = 1:length(files) filepath = fullfile(files(k).folder, files(k).name); fprintf('处理:%s\n', files(k).name); % 调用主流程,输出文件名带上原始文件名前缀 main_plot3d(filepath, sprintf('fig_%s', files(k).name(1:end-4))); end这里需要把main_plot3d.m改成接受输入参数的形式,而不是硬编码文件路径。改造后每个数据文件生成一张图,文件名带前缀,方便追溯。
参数自动化是第二个方向。手动改params.m在数据量大的时候很痛苦。可以把参数改成从文件名或配置文件读取。比如文件名里带采样率信息pd_20M_50Hz_01.mat,用正则解析:
% 从文件名解析采样率和工频 tokens = regexp(filename, '(\d+)M_(\d+)Hz', 'tokens'); if ~isempty(tokens) p.fs = str2double(tokens{1}{1}) * 1e6; p.f0 = str2double(tokens{1}{2}); end这样换数据的时候不用改代码,只要文件名规范就行。
输出格式适配是第三个方向。MATLAB 默认出的图是 PNG 或 FIG,但很多报告模板要求 EPS 或 PDF 矢量图。把print函数的格式参数改一下就行:
| 格式 | 参数 | 适用场景 |
|---|---|---|
| PNG | -dpng | 网页、Word 插入 |
| EPS | -depsc | LaTeX、学术论文 |
-dpdf | 报告、打印 | |
| FIG | savefig | 后续 MATLAB 中再编辑 |
我一般会同时输出 PNG 和 EPS,PNG 用于快速预览,EPS 用于最终排版。
还有一个容易被忽略的点:三维图谱的视角和配色。默认的jet配色在打印成黑白时区分度很差,如果报告需要黑白打印,换成gray或hot更稳妥。视角方面,view(-45, 30)是一个通用角度,但如果你的脉冲集中在特定相位区间,可能需要调整方位角让那个区间正对镜头。我通常会生成三张不同视角的图,选最清楚的那张。
从那以后我每次换新数据,都强制先跑一遍params.m检查,确认采样率和工频填对了再往下走。这个习惯帮我省了很多返工时间。希望帮到你。
本文还有配套的精品资源,点击获取