做声发射检测的人一定绕不开一个操作——把传感器贴到试件上然后盯着示波器上那一串跳出来的毛刺。理论上这就是声发射波形但实际工程里你很难凭运气等到一个标准的单峰事件现场有电磁干扰、结构噪声、多源叠加很多时候你抓到的那一段数据根本分不清是材料开裂还是隔壁电钻在响。所以很多团队会把模拟单个声发射波形作为第一步先把干净的、理想的信号做出来用MATLAB画出来再拿去和实测数据比对、调试特征提取算法。这篇文章就以一个名为 Planck 的示例事件为例完整走一遍声发射波形模拟、参数设计与绘图的流程适合刚接触声发射数据分析、或者正在做MATLAB信号处理课设的同学直接参考。我会把这一步的内容拆清楚为什么用衰减正弦叠加模型、采样率怎么定、衰减系数和相位怎么调、画出来的图怎么看还会把我在这个过程中踩过的坑和排查思路一起整理出来。Planck 这个名字你就当作某个试件或某个通道的事件标签比如你实验里随便起的文件名换成你手头的编号完全不影响模拟逻辑。1. 声发射波形模拟的思路与选型1.1 为什么要先模拟单个声发射波形现实中的声发射信号是从材料内部微裂纹萌生、扩展、断裂时释放出来的瞬态弹性波。传感器贴在表面拾取到的是一串快速衰减的机械振动波形本身有几个明显特征起振突然、频谱宽、持续时间短、幅度按指数规律快速衰减。这种波形和我们熟悉的连续正弦波、方波都不一样它不是稳态信号用一句通俗的话讲就是响一下然后很快安静下来。但实测信号里很少只有一下——材料受力时往往多个裂纹同时活动加上结构反射、边界回波传感器收到的其实是许多个衰减事件在时域上的叠加。如果一上来就直接处理这种混合信号你连最基本的这个波形从哪里开始、什么时候结束、峰值频率是多少都不好回答。所以我建议先把单个声发射波形的模拟搞明白它让你有一条已知真值的参考信号可以用来验证你写的峰值检测算法靠不靠谱、包络提取准不准、FFT分析对不对。模拟信号还有一个实际用途训练和调参。比如你在做声发射特征参数提取需要统计振铃计数ringdown count、上升时间、持续时间这些参数在老旧的声发射仪里是硬件电路算出来的现在很多软件要自己实现但如果你手上没有带标准事件的数据集模拟波形就是最方便的调试素材。再比如BP神经网络、支持向量机做损伤分类训练集往往先由模拟波形扩充再结合少量实测样本迁移到真实工况。1.2 Planck到底是什么一个典型AE事件样本的标签化处理可能有人第一眼看到 Planck 会联想到普朗克分布觉得是不是要用普朗克公式来拟合声发射能量的分布。这个方向确实存在——声发射信号的能量幅度统计有时会用类似指数/对数正态的分布去描述但那是对大量事件做统计的工作不是针对单个波形。单个波形层面Planck 更像一个事件标签或文件名。你去看不少实验里数据采集系统会按照触发通道和事件序号自动生成文件比如 Planck_mat_01.dat、AE_Sensor2_PlanckPlanck 可能就是某个试验件编号、某个传感器位置代号或者记录批次里随手起的名字。在MATLAB里处理的时候这个标签唯一的作用就是让你知道我现在处理的是哪一个事件。你完全可以用 Event_001、Sample_A 或者任何字符串替代。真正有物理意义的其实是那组参数峰值频率、衰减快慢、起振时刻、噪声水平。我下面给出的代码会把 Planck 当作一个结构体字段或者事件名称来用这样你后面扩展成多事件数据集的时候每个事件有清晰的归属不会乱。1.3 衰减正弦叠加模型AE波形模拟的核心数学表达单个声发射波形最常见的数学模型是衰减正弦波也就是一个频率固定的正弦振荡它的幅度随时间呈指数衰减。写成公式就是x(t) A · e^(−α(t − t0)) · sin(2πf(t − t0) φ) n(t)这里每个参数都有明确物理意义A 是起始峰值幅度反映声发射事件释放能量的强弱α 是衰减系数反映波在传播路径上的损耗快慢材料阻尼越大、传播距离越远α 越大f 是主频率或主导频率裂纹开裂、纤维断裂、摩擦滑动产生的事件主频区间往往不一样t0 是事件起始时刻声发射特征参数里的上升时间就是从 t0 到第一个峰值的时间φ 是初始相位决定起振瞬间波形是往上还是往下冲一下n(t) 是环境噪声不用多说。为什么正弦模型能站得住脚因为传感器等效成一个谐振系统它接收到瞬态激励后会按照自身的谐振频率做衰减振荡所以传感器输出端的电压信号天然就像一个被阻尼的窄带振荡。这也是为什么声发射波形通常用频率-衰减-幅度三个量就能描述得很清楚。需要注意的是真实声发射信号可能同时有几个频率成分比如低频的结构谐振叠加高频的裂纹信号这时候模型升级成多个衰减正弦的叠加就好每个分量有各自的频率、幅度和衰减系数。这个扩展不复杂后面代码里我会留接口。2. 参数准备与计算过程2.1 采样率、时间窗与信号频率的匹配模拟波形之前先定采样率这是最容易被新手忽略的一步。根据奈奎斯特采样定理采样率至少要是信号最高频率的两倍MATLAB里才能无失真地还原波形。但工程上声发射信号的频率范围通常在50kHz到1MHz之间某些复合材料甚至到几MHz如果你只在传感带宽内感兴趣采样率至少要定在最高感兴趣频率的5到10倍才有余量。以150kHz的主频为例我通常取 fs 1e6也就是每秒100万个采样点这样每个正弦周期里有大约6.7个采样点画出来波形不光光滑峰值附近也不会发生明显失真。时间窗又要怎么定声发射单次事件的持续时间一般在几十微秒到几毫秒之间取决于衰减系数。衰减慢的金属结构可能拖到几毫秒衰减快的复合材料几百微秒就结束了。我这里取1ms足够容纳一个完整的衰减过程也方便后面做FFT时频率分辨率够用。时间序列直接生成t 0 : 1/fs : 1e-3。还有一个分辨率的问题值得提一下。频率分辨率等于 fs/NN是采样点数。fs1e6时间窗1ms时N1001频率分辨率大约是1kHz左右对150kHz的主频来说足够精细了。如果你需要更细的频率分辨率可以加长时间窗或者用零填充我后面会在常见问题里展开。2.2 衰减系数、初始相位与噪声底的设计逻辑衰减系数 α 是模拟里最手感的参数。取大了波形很快就缩成一片小抖动看起来不像典型的声发射振铃取小了波形拖得很长像持续振荡又不像瞬态事件。以150kHz为例一个周期大约是6.7微秒如果希望波形在10个周期内衰减到幅度的5%以下可以估算一下e^(−α · T_at) ≈ 0.05其中 T_at 10 / f ≈ 6.7e-5 秒算出来 α ≈ 45000。这算是一个衰减很快的情况。如果实际想模拟钢板上传感器贴得比较近的情形衰减慢一些α 取 10000 到 20000 更常见。我代码里用的是 12000大约在20个周期左右衰减到原来的不到2%视觉上看得出来明显拖尾但又不会没完没了。初始相位 φ 我习惯设成 π/4。为什么不设成0因为工程问题里事件起点很少正好落在正弦的零点相位给一个非零值能让波形起振瞬间更真实而且上升时间的计算会因为相位不同而不同。设置成π/4后信号在 t0 时刻就有一个正的、不为0的跳变这就引出了一个新的真实细节——机械冲击在传感器上产生的初始响应往往不是从零开始慢慢爬升而是陡峭跳变所以这种起振就有一个明显幅度的感觉是对的。噪声底的设计也很有讲究。完全无噪声的信号只能用来验证算法不能代表实测。我给信号叠加白噪声 std 0.05也就是噪声幅度约为信号峰值幅度的5%做出来信噪比大约在20dB左右。这个数值看起来不高不低对模拟来说比较合适峰值检测仍然容易但包络线上已经能看到一些毛刺更接近传感器实际输出的感觉。如果你的应用场景是远距离传感器或者小幅度事件可以把噪声调到0.1甚至更高会显著影响特征提取的结果这就留给你自己实验。3. 完整实操MATLAB实现与绘图3.1 生成原始波形与包络这一节直接给能跑的代码。我会逐步写而不是甩一个完整脚本就完事因为每一步发生了什么最好自己心里有数。% 基本参数 fs 1e6; % 采样率 1 MHz t 0 : 1/fs : 1e-3; % 时间窗 1 ms t0 0.0001; % 事件起始时刻给一个偏移避免从0就开始 A 1.0; % 峰值幅度 f0 150e3; % 主频 150 kHz alpha 12000; % 衰减系数 phi pi/4; % 初始相位 % 模拟单个声发射波形 env A * exp(-alpha * max(t - t0, 0)); % 指数衰减包络 x_raw env .* sin(2*pi*f0 * max(t - t0, 0) phi);% 衰减正弦振荡 x_raw(t t0) 0; % 起振前清零 % 绘制时域波形 figure(Color,w,Position,[100 100 900 500]); plot(t*1e3, x_raw, LineWidth, 1.2); xlabel(时间 (ms)); ylabel(幅度 (V)); title(模拟声发射波形 - 事件 Planck); grid on;这里我特意用了max(t - t0, 0)保证事件开始之前指数包络和正弦都严格为0而不是让衰减指数从负无穷开始算。很多教程直接exp(-alpha*t)结果时间轴原点处信号幅度都衰减掉一大截看起来就像拿到的是事件中段完全不符合从零开始触发的直觉。这个细节值一行代码但很重要。画完这张图你会看到什么起始时刻 t0 处波形突然跳起然后幅度先冲到最大随后按指数形式慢慢变稀、变小最后趋向于零尾部还能看到明显的振铃。这就是一个标准的理想单事件声发射波形。不要觉得它太简单后面加噪声、做包络、算振铃计数全在这条曲线的框架内。包络线画出来能更清楚地看到衰减趋势。MATLAB里最稳的方式是用希尔伯特变换求解析信号analytic hilbert(x_raw); env_hilbert abs(analytic); hold on; plot(t*1e3, env_hilbert, r--, LineWidth, 1.5); legend(原始波形, 希尔伯特包络);希尔伯特包络相当于把正弦振荡的外壳提取出来是一条平滑的指数下降曲线。这条红线在实际声发射系统中非常有用——计算上升时间、持续时间、幅度都依赖包络而不是原始毛刺。3.2 加噪、滤波与FFT分析干净波形只能算模拟的第一步真实信号一定是叠着环境噪声的。我加白噪声的方式是noise_std 0.05; x_noisy x_raw noise_std * randn(size(t));加完噪声之后先别急着画频谱先看一眼时域里是否还能看清单个振荡周期。如果信噪比太低波形淹没在噪声里那就需要考虑滤波了。声发射模拟里最常用的是带通滤波器把频带外的能量全部挡掉。频率下限取50kHz是因为声发射信号很少低于这个频段上限取300kHz是因为我们的主频150kHz留一个倍频程余量足够。band_lo 50e3; band_hi 300e3; x_filtered bandpass(x_noisy, [band_lo band_hi], fs);滤波器这里我有一个建议先确认MATLAB版本支持bandpass函数。旧版本里用的是designfilt加filtfilt如果你用的是2020年之前的版本建议升级或者换用函数包。另外bandpass默认会引入相位偏移虽然模拟里影响不大但如果你要做时间精确的触发定位最好用filtfilt零相位滤波替代。频域分析就一句话画功率谱密度。[pxx, f] pwelch(x_filtered, [], [], [], fs); figure(Color,w,Position,[100 100 900 500]); plot(f/1e3, 10*log10(pxx), LineWidth, 1.2); xlabel(频率 (kHz)); ylabel(功率谱密度 (dB)); title(模拟声发射波形频谱 - 事件 Planck); grid on; xlim([0 500]);你会看到150kHz附近出现一个非常明显的单峰。这就是这个事件的主频是多少的直接证据。如果没有出现理想峰先检查是不是忘了设置xlim因为白噪声的功率分布会在0到500kHz范围内铺开150kHz的峰在旁边显得不太起眼。再一个容易犯的错是没有把横轴换算成kHz直接用Hz画刻度数字挤成一团观感很差。3.3 保存与读取Planck.mat数据文件做完一组参数还不算完后面可能还要用这些数据训练分类器、跑特征提取所以我习惯把模拟结果连同参数一起存成 .mat 文件。用事件名 Planck 作为文件名这样以后再读取时不需要额外翻笔记。Planck.Name Planck; % 事件标签 Planck.fs fs; % 采样率 Planck.t t; % 时间轴 Planck.x_raw x_raw; % 干净波形 Planck.x_noisy x_noisy; % 加噪波形 Planck.x_filtered x_filtered; % 滤波后波形 Planck.f0 f0; % 主频 Planck.alpha alpha; % 衰减系数 Planck.t0 t0; % 起振时刻 Planck.noise_std noise_std; % 噪声标准差 Planck.description 模拟单事件声发射波形参数见表; save(Planck.mat, Planck);这个结构体是模拟工作中最值得养成习惯的一步。把参数和波形放在一起做特征提取时随时可以回溯这个衰减系数为什么这样设做批量仿真时只要循环里生成不同的事件名存储、读取和数据管理都会顺很多。读取方式也很直接S load(Planck.mat); P S.Planck; figure; plot(P.t*1e3, P.x_filtered); xlabel(时间 (ms)); ylabel(幅度 (V)); title([读取 P.Name 事件波形]);从文件读出来的信号和内存里直接生成的是一致的这让你可以在多个脚本之间共享数据集。比如一个脚本负责批量生成模拟事件另一个脚本专门做特征提取两个脚本通过 .mat 文件解耦互不干扰。我自己的习惯是模拟数据和参数作为一份独立资产不随手放进处理脚本里否则改一行参数后面所有结果都得重新生成一遍。4. 常见问题与排查技巧实录4.1 时域波形发飘先检查包络与相位明明设置了指数衰减画出来的波形却不像指数衰减反而像幅度先涨后跌这是常见的第一个问题。排查顺序我先建议看包络把希尔伯特包络叠加到原始波形上如果包络在事件起始后就一路下降说明衰减系数没问题如果包络先上升后下降那你模拟的实际上是一个具有上升段的突发信号这也不一定是错的——真实声发射波形确实有上升时间但你要清楚自己模拟的是哪一类事件。第二个嫌疑是初始相位。当 φ 约等于 π/2 时正弦函数在起始时刻达到局部最大值看起来信号像是直接冲上去再掉下来衰减没那么直观当 φ 约等于 0 时信号从零附近开始第一眼更平滑。两者对应的上升时间完全不同特征提取算法算出来的结果也会差不少。如果你关心的是振铃计数相位影响不大如果你要提取的是上升时间这个特征相位选择会直接改变结果所以模拟时一定要把 φ 记入参数表不要默认它无关紧要。4.2 频域出现不该有的谱峰采样和混叠的锅有一种情况很迷惑时域波形看起来正确频谱图上却出现了莫名其妙的额外峰比如在350kHz处冒出一个和主峰幅度差不多的尖峰。先别怀疑模型十有八九是采样率不够导致的频谱混叠。150kHz的正弦信号如果采样率只有500kHz虽然勉强满足两倍奈奎斯特条件但对带噪声的信号来说噪声中的高频成分会被折叠回低频区间形成虚假峰。解决办法很简单把采样率提高到1MHz以上或者先做一个300kHz的低通前置滤波再采样这就是为什么我一开始就强调取5到10倍余量的原因。还有一种情况是频谱图上看起来有两个相邻峰其实是时间窗太短导致的谱泄漏。150kHz左右的信号时域只截了三个周期频域就会发散那个峰不是两个频率成分而是一个峰旁边的栅栏效应旁瓣。解决方法是加窗函数比如汉宁窗把泄漏压下去或者加长信号的时间窗让频谱分辨率变细。如果你要区分150kHz和151kHz的两个相近频率成分那时间窗至少需要10ms量级这已经不属于单事件模拟的范畴了。4.3 绘图与导出别让显示效果坑了你模拟波形画出来之后最容易被吐槽的是图不够清楚、细节看不清。我踩过几个具体的问题直接列成速查表现象原因解决方法图像只显示一条粗黑带时间轴跨度太大波形振荡挤在一起设置xlim只显示事件附近区间比如0到0.3ms尾部振荡完全看不见幅度太大尾部被压缩到0附近用ylim或分两段画图一段全貌一段放大尾部频谱图杂乱无章没加窗、没设置频率上限用pwelch配合xlim([0 500])必要时加汉宁窗默认图片分辨率太低直接saveas位图放大发虚用exportgraphics(gcf, Planck.png, Resolution, 300)导出高分辨率图线条颜色看不清用了默认配色浅色在白色背景下看不清波形用深蓝色包络用红色虚线线宽1.2以上另外保存图片时我建议不要用saveas存jpg声发射波形是连续曲线jpg的有损压缩会在包络区域出现振铃噪声影响你做截图标注。导出PNG格式300dpi起步或者直接print -depsc转成矢量图放到论文里效果最好。4.4 储备一个Batch脚本思路多事件模拟的扩展聊到这里肯定会有同学问如果我需要模拟20个不同事件频率、衰减、噪声都不一样怎么写效率更高我的建议是做一个简单的批量脚本骨架把上面所有参数变成输入参数function P simAEevent(name, f0, alpha, A, phi, fs, t0, noise_std) % 输入参数后返回结构体 P包含信号和参数 t 0 : 1/fs : 1e-3; env A * exp(-alpha * max(t - t0, 0)); x env .* sin(2*pi*f0 * max(t - t0, 0) phi); x(t t0) 0; x x noise_std * randn(size(t)); P.Name name; P.f0 f0; P.alpha alpha; P.t t; P.x_raw x; P.fs fs; P.noise_std noise_std; end然后循环里生成10个事件event_list [100e3, 150e3, 200e3; 8000, 12000, 15000; 0.8, 1.0, 1.2]; for k 1:3 name sprintf(Planck_%02d, k); P simAEevent(name, event_list(1,k), event_list(2,k), event_list(3,k), pi/4, fs, 1e-4, 0.05); save([name .mat], P); end这样生成的数据集可以满足后续的特征提取实验、模型训练甚至文件管理。不要觉得这是过度设计我做声发射特征提取的时候就是靠这样一组批量生成的数据集先把算法的参数定下来再拿真实数据去验证省了特别多现场调试时间。最后再分享一个我个人的经验模拟波形和实测波形配对使用效果远好于只依赖其中任意一个。先跑MATLAB模拟把模型参数和算法跑通再拿真实AE事件去算同一组特征如果两者偏差太大不要急着调算法先回头检查一下你的模型是否覆盖了传感器的谐振频响。很多时候模拟与实测对不上差的不是算法而是没有在模型里加入传感器带宽这个因素这一点想明白之后你的声发射数据处理能力会上一个台阶。