资讯动态

MATLAB参数化半波波形绘图脚本:从原理到实践

发布时间:2026/9/7 23:04:26 来源:尧图企业网站定制
1. 半波图形是什么为什么我建议做成参数化m文件做信号处理、电力电子或者控制系统仿真的人大概率都画过几种固定模板的波形图。正弦波、方波、三角波这些还好用到半波整流波形的时候很多人喜欢直接在命令行敲一句plot(sin(0:0.01:2*pi))完事或者从旧代码里复制粘贴一段改几个数字就凑合着用。这种做法的麻烦在于今天要画3个周期的半波明天要画带相位偏移的5个周期后天导师又说幅值要改成2.5——每次都要从头找代码、改索引、调坐标一个不小心就画成了全波或者波形残缺。所谓半波图形指的是对完整的正弦波做整流处理只保留正半周或者只保留负半周得到的波形。它在实际工程里出现频率非常高电力电子里的单相整流电路输出、电源适配器里的纹波分析、信号检波电路里对AM信号的解调结果甚至音频过载效果器里对信号的半波整形本质都是在处理这类波形。而多周期半波就是把多个周期的正弦信号统一整流后连续画出观察它在较长时域范围内的形态变化。这一篇我就把我自己在用的一个多周期半波绘图m文件完整拆开讲。它不复杂核心逻辑不超过三十行但把参数全部提到了文件头部幅值、基频、周期数、相位偏移、采样密度、整流类型、是否标注峰值这些常用变量改一个数字整个图就跟着变。对需要频繁调整波形参数的场景来说这个脚本能省下大量重复劳动让想画什么就画什么变成一件顺手的事。2. 多周期半波m文件的整体架构设计2.1 把脚本拆成三个区参数、计算、绘图写这类可复用脚本我最看重的一点是改起来方便。不管代码本身写得多优雅如果每次用都要在函数体里翻找要改的变量那就失去了参数化的意义。所以我习惯把整个m文件按功能切成三块用注释大标题分隔开。第一块是参数区里面只放A、f、num_cycles、phase_shift、samples_per_cycle这类用户最关心的量。第二块是计算区根据参数区的值生成时间向量、构造正弦波再做半波截取。第三块是绘图区负责把计算结果用合理的坐标范围、网格、标注呈现出来。这种分层有两个直接好处。第一个是降低使用门槛哪怕完全不懂MATLAB内部实现的人拿到这个文件只需把前三行参数按自己的要求改掉运行后就能得到目标波形。第二个好处是排查问题容易画出来的图不对要么是参数设得不合理要么是计算逻辑有bug要么是绘图属性没设置好三个区域定位起来互不干扰。2.2 参数区设计思路哪些变量应该暴露出来参数区的设计不是把所有内部变量都堆上去得想清楚哪些东西值得被使用者调整。我的原则是只要使用者有可能会为了不同需求而修改的量就放到参数区只服务于内部计算的中间量留在计算区。以半波图形为例我最终保留了这几个可调参数A正弦幅值单位随便用1.0做默认值。f基频单位Hz默认50。电力系统方向的同学对这个数会很有亲切感。num_cycles绘制几个周期的波形默认3。phase_shift初始相位偏移单位弧度默认0。samples_per_cycle每个周期内的采样点数默认500。wave_type整流类型字符串half表示半波保留正半周full表示全波取绝对值negative表示负半波。show_annotation是否在图上标注幅值和周期长度true或false。有些变量我刻意没有放出来比如时间轴的起始点固定为0终止点由num_cycles / f自动算出来。再比如线宽、字体大小、颜色这些虽然也能做成参数但对波形分析来说属于装饰性配置放进参数区反而会让文件变得啰嗦。真要调样式直接去绘图区改两行就行。2.3 计算区与绘图区的职责划分计算区只需要做三件事生成时间向量、构造正弦波序列、根据整流类型截取目标波形。这里有个容易让人困惑的点半波截取到底是在时域上切掉一部分还是对每个采样点的幅值做非线性处理正确答案是后者。半波整流本质是一个逐点映射对每个时间点上的正弦值如果大于等于0就保留小于0就置为0保留正半周的情况。用MATLAB的向量化操作一句y(y 0) 0就完成了对所有负值的处理不需要写for循环也不需要对时间轴分段。这个思想理解了后面写代码就很顺。绘图区的职责比较杂创建图窗、设置坐标范围、画波形曲线、加网格、加标注、设置字体字号、给坐标轴加标签。我习惯把图窗句柄和坐标轴句柄保存下来这样后续如果要在此基础上叠加其他波形可以复用句柄而不必把整个绘图代码再执行一遍。3. 核心代码实现与逐段讲解3.1 参数设置与输入校验第一步是定义参数区。我用大写字母开头的变量名来表示这些是使用者应该关注的输入量和计算区里的小写局部变量区分开。同时我在每行参数后面都写了中文注释说明含义和可选范围。%% 参数设置区 A 1.0; % 波形幅值单位自定 f 50; % 基频单位Hz num_cycles 3; % 绘制周期数正整数 phase_shift 0; % 初始相位偏移单位弧度可用 pi/6 或 0.3 samples_per_cycle 500;% 每周期采样点数 wave_type half; % 整流类型half 保留正半周full 全波negative 保留负半周 show_annotation true; % 是否显示幅值和周期标注这里要多说一句samples_per_cycle的选择。它直接决定了波形曲线的平滑程度也间接和内存占用相关。每周期采500点画几十个周期出来也就是几万个点现代电脑毫无压力。但如果把num_cycles调大到10000还保持每周期500点就会生成500万个点的数组绘图会明显卡顿。这时候要么降低samples_per_cycle要么改用分块绘制策略。这个细节我在后面的参数配合里会展开。为了防止使用者把参数设成明显不合理值导致程序崩溃我加了一段很轻量的输入校验。不需要做得太复杂检查几个关键量就行。%% 输入校验 if num_cycles 0 || round(num_cycles) ~ num_cycles error(num_cycles 必须为正整数); end if samples_per_cycle 20 error(samples_per_cycle 太小波形将严重失真); end if f 0 error(频率 f 必须为正数); end这个错误提示写得比较粗暴但胜在直接。实际调试中参数不合理是常见问题与其让MATLAB抛出一个看不懂的数组维度错误不如自己提前拦截告诉使用者到底哪里出了问题。3.2 时间轴与半波序列生成参数就绪后进入计算区。核心逻辑围绕三个向量展开时间向量t、原始正弦波y_sin、整流后的输出y_wave。时间向量的生成方式决定了整段波形的横轴范围。我使用linspace而不是t 0:dt:t_end原因是linspace能确保最后一个点精确落在终止时间上不会因为浮点舍入导致最后一个周期不完整。%% 波形计算区 t_end num_cycles / f; % 总时长 t linspace(0, t_end, num_cycles * samples_per_cycle 1); y_sin A * sin(2 * pi * f * t phase_shift); switch wave_type case half y_wave y_sin; y_wave(y_wave 0) 0; case negative y_wave y_sin; y_wave(y_wave 0) 0; case full y_wave abs(y_sin); otherwise error(wave_type 仅支持 half、negative 或 full); end这里用到的是MATLAB的logical indexingy_wave(y_wave 0) 0这一句的语义是找到y_wave中所有小于0的元素把它们的值统一改成0。整个过程是向量化执行的不需要for循环代码简洁且运行效率高。关于phase_shift的设置有个使用经验想分享如果只是想看从0相位开始的标准波形保持默认0就行。如果设成pi/2余弦波就出来了波形起点不再是0而是直接从幅值A开始。实际做电机控制或电力电子仿真时经常需要调整初始相位来对齐其他信号这个参数的价值就体现出来了。采样点数还有一个关于最大时间点的细节linspace生成的总点数是num_cycles * samples_per_cycle 1加1是为了让每个周期的起点和终点都能落在采样点上闭合感更好。如果不用加1最后一个周期看起来会少一个点虽然视觉上几乎无差别但处女座会觉得难受。3.3 绘图细节让波形图一眼看懂绘图区决定了一张图能不能看。我见过太多人画半波图曲线出来后横轴是采样点序号而不是实际时间纵轴范围又很随意图例也没有别人根本不知道这张图在表达什么。我的做法是把关键信息都标清楚保证拿出去印在论文里或贴在报告里都说得过去。%% 绘图区 figure(Color, w, Position, [100 100 1000 400]); plot(t, y_wave, b-, LineWidth, 1.8); grid on; xlim([0 t_end]); ylim([-0.3 * A, 1.2 * A]); % 给波形上下留一点余量负半周留出半波置零区域 xlabel(时间 t (s)); ylabel(幅值 y(t)); title(sprintf(多周期%s波形 A%.2f f%.2fHz 周期数%d, ... wave_type, A, f, num_cycles)); if show_annotation hold on; % 标注峰值线 plot([0, t_end], [A, A], r--, LineWidth, 0.8); text(t_end * 0.02, A * 1.05, sprintf(峰值 %.2f, A), Color, r, FontSize, 10); % 标注一个周期的长度位置 if f 0 T 1 / f; stem(T, 0, k, LineWidth, 1.2); text(T, -0.25 * A, sprintf(T%.4fs, T), FontSize, 10, HorizontalAlignment, center); end hold off; end窗口尺寸设成1000x400长宽比更符合信号波形的阅读习惯。ylim的设置有一个小心思上边界留到1.2倍幅值给峰值标注留出空间下边界留到-0.3倍幅值把半波整流后负半周为零这个特点通过图形空白直观呈现——读者一眼就能看出波形不是被截断了而是整流后负半周本来就归零。标题里用sprintf把参数动态嵌入这样每次改参数后运行标题会自动更新省得手动改title。这个操作看似不起眼但当你连跑20组参数对比时就会知道标题跟着参数走有多省事。到这里一个能直接运行、输出多周期半波图形的最小脚本就完成了。把它存成half_wave_plot.m运行后图形窗弹出默认情况下你会看到3个周期的正半波负半周全部压到0轴。4. 参数调节技巧与效果对比4.1 频率、周期数、采样点怎么配合这三个参数在实际使用中是最常被调整的但很多新手对它们之间的关系没有形成直觉。频率f决定单周期持续时长T 1/f周期数num_cycles决定显示多少段采样点samples_per_cycle决定分辨率。一个稳妥的配合方式是优先确定频率和周期数再根据显示需求调整采样密度。比如要画0.5秒内的波形f设为100Hz那num_cycles就是50如果画出来的波形边缘锯齿严重就去提高samples_per_cycle而不是去动其他参数。反过来当你发现每周期500点太密、图形文件特别大时可以适当降到200甚至100。对正弦波这种光滑曲线来说每周期100个点已经能达到很好的视觉平滑度。如果是为了打印到论文里追求极致平滑再考虑300以上。常规信号分析用500点作为默认值是比较均衡的选择。还有一个容易踩的坑num_cycles和samples_per_cycle都很大时linspace生成的总点数是两者的乘积呈线性增长不是指数增长但量级依然可观。比如num_cycles10000、samples_per_cycle500就是500万点plot画这么长的向量会明显卡顿。应对方法后面细说。4.2 相位偏移和整流类型的选择phase_shift这个参数对波形形态的影响非常直观但经常被忽略。我举一个实际场景在电力电子仿真里需要观察交流输入经过半波整流后的输出而输入的交流信号可能带着初始相位这时候你要把phase_shift设成对应的角度整流结果才能和实际电路对应上。把它们放在一起对比效果会更明显% 三组参数快速对比仅改 phase_shift phase_shift 0; % 输出从0开始爬升 phase_shift pi/3; % 输出前一小段为0起始处有一段空白 phase_shift pi/2; % 波形起点在峰值处最接近余弦波形态wave_type的切换同样一改即用。half 模式输出正半波negative 输出负半波full 输出全波。之前在别的脚本里画全波整流波形还要写一段abs()的注释现在直接改参数字符串就切换完成了。我一度还想过增加一个 clipped 类型来做限幅处理后来觉得那样超出了半波的范畴就没有加进去。4.3 参数不合理时的典型翻车场景参数是灵活了但灵活带来的副作用是使用者可能设出一些不合理组合。我梳理了自己实际踩过的几种典型问题做成一张对照表场景现象原因对策samples_per_cycle设成10波形严重锯齿峰值丢失采样点数不足提高到50以上建议500phase_shift设成2*pi波形起点和终点相位几乎重叠相位偏移超过一个周期MATLAB自动wrap效果等同0不必担心num_cycles设成50、samples_per_cycle设成500绘图卡顿响应慢总点数达到2.5万其实不算大但如果循环调用就拖慢降低采样点或改用分块绘制wave_type拼错为Half报错MATLAB字符串区分大小写把错误拦截在switch的otherwise分支里第4种情况最常见所以我在代码里特意加了otherwise分支来抛出清晰错误。这一点对给别人分享脚本尤其重要——你不会希望同事用你脚本时被一堆晦涩报错搞崩溃。5. 常见错误与排查思路5.1 典型报错及修复方案我把自己使用这个脚本、以及帮别人排查时遇到的高频报错整理成了速查表做信号处理的同学大概率用得上错误1Error using plot, Vectors must be the same length出现此报错说明t和y_wave的长度不一致。常见原因是手动改过计算区的linspace点数但y_wave还是基于旧的采样点数生成的。处理方式很简单把所有计算代码从上到下重新运行一遍确保变量同步更新。如果你把脚本当函数用改成function half_wave_plot(A, f, num_cycles, ...)的输入参数形式也能从根本上规避部分同步问题。错误2图形窗口里curve是空的只有坐标轴这种一般发生在把y_wave(y_wave 0) 0这句注释掉或者误删了。没有进行半波截取y_wave和y_sin完全一样而正负值都有的正弦波虽然能正常绘图但如果你期望看到负半周全零的效果就会觉得图形不对。检查一下计算区有没有真正执行整流那一步。错误3标题中文乱码老版本MATLAB对中文的支持不太友好标题里直接写中文可能显示为方框。解决办法有两个一是改用英文标题避免乱码二是预先执行set(0, DefaultAxesFontName, Times New Roman)并把图窗字体设为支持中文的字体。我自己更倾向于用英文变量名的同时在标题里保留简单的英文描述和参数值这样在不同语言环境下都能正常显示。5.2 绘图显示问题的实用技巧图形窗口出来之后影响判断的往往不是代码本身的错误而是一些视觉层面的细节问题。第一个是纵轴范围太宽或太窄。如果ylim设置不当波形看起来会顶天立地或者缩成一团。我的经验是上边留20%空余下边留30%空余这样半波波形零轴以下的空间被展示出来人眼才能意识到哦负半周被削掉了。第二个是网格线的密度。grid on对大致定位很有效但如果周期数特别多纵向上网格可能不够用判断峰值位置时就要开启数据光标工具。可以在图中直接用鼠标点取峰值点或者加一句findpeaks命令来自动定位所有峰值坐标。例如[pks, locs] findpeaks(y_wave, t); fprintf(共检测到 %d 个峰值首个峰值位于 t%.6f s\n, length(pks), locs(1));这个方法在验证整流效果时非常靠谱——如果某个周期的峰值位置和理论值对不上多半是phase_shift的弧度值写错了。第三个是导出图片的清晰度。MATLAB图形窗直接截图通常只有72dpi放到论文里会糊。导出时可以用exportgraphics(gcf, half_wave.png, Resolution, 300)一步到位生成高清图。这是新版MATLAB推荐的做法不需要print那一堆老参数。6. 从脚本到小工具进一步扩展的方向这个m文件做到现在这个程度已经可以解决日常画波形的大部分需求。但我在实际使用中逐渐发现脚本模式还是有一些不方便的地方每次改参数都要去编辑器里改数字画不同参数的对比图需要手动跑多次然后还得自己整理结果图。这些痛点催生了一个自然的演进方向把脚本改造成函数。函数的最外层用输入参数控制幅值、频率、周期数、整流类型内部自动完成计算和绘图需要对比不同参数时写一个for循环批量调用即可。再往下一个层级可以借助MATLAB App Designer做一个极简图形界面界面上放几个滑块和下拉菜单直接全程调参图形实时刷新。我后来就是把这份脚本用在了自己的小工具里效果确实比改脚本参数方便不少。如果你有界面开发的需求从这份结构清晰的脚本迁移到App Designer时会非常顺手——因为三个区的划分正好对应着界面控件、回调函数、绘图组件的分工。不过我也要提醒一句不要把工具化当作炫技。如果你只是画三五张波形图脚本模式完全够用非要做成GUI反而浪费时间。要不要继续做界面取决于你调整参数的频率有多高、以及有没有其他同事/同学要一起用。做技术选型最忌讳的是为了高级而过度设计。7. 版本兼容性与其他运行环境提示MATLAB版本迭代较快老代码在新版本上跑不起来的情况时有发生。我这个脚本使用的核心函数linspace、abs、plot、findpeaks、switch分支、logical indexing全部属于MATLAB的基础功能从我读书时用的R2016a到现在常用的R2023b都没有出现过兼容性问题。唯一想提醒的是exportgraphics这个函数它是R2020a之后引入的老版本里没有。如果你的MATLAB版本较老导出图片需要改用print(gcf, -dpng, -r300, half_wave.png)。这个差别在脚本里我已经用注释标注过使用时留意当前环境就好。此外如果在Octave上运行这份脚本也能跑通大部分逻辑但exportgraphics可能需要替换为print方案。Octave的图形后端和MATLAB略有差异网格显示、字体渲染有一定差别这不影响波形计算本身的正确性。经常有人问我可不可以在MATLAB Online里跑我的回答是可以。这个脚本没有依赖本地文件系统、没有调用工具箱函数全部是基础绘图和数值计算放到MATLAB Online里运行完全没问题。如果你用的是学校或公司提供的云MATLAB环境把m文件传上去就能直接出图。8. 写在最后的实际操作体会这个多周期半波绘图脚本我断断续续改了很多版本。最初只是为了一门课交作业画几张标准半波图后来越用越顺手索性把最常用的参数全部提到了文件开头就成了现在这个形态。我个人在实际操作中的一个深刻体会是参数化脚本带来的价值往往不在脚本本身而在于它帮你节省的重复劳动心情成本。改一个数字、敲一次运行、得到一张新图和翻旧代码、逐行找该改哪里、提心吊胆怕改坏其他功能这两种体验的差距用过的人都懂。最后再分享一个小技巧如果你经常需要对不同参数做对比可以在脚本外面套一层批量逻辑把参数组合放进一个结构体数组里循环调用核心绘图代码并把每张图保存为独立的png文件。这样一次运行就能产出一整套对比图集省去了手动截图的繁琐也方便后续插入文档。这个扩展思路在我自己做的实验报告和论文配图里帮了大忙。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价