做声发射(AE)信号强度分析时,Matlab 是我最常用的工具。HI(Historic Index)和 Sr(Severity)这两个指标,几乎是判断结构损伤程度的标准配置。我在不少复合材料试验和金属构件疲劳监测场景里都用过这套流程,今天把它沉淀成一篇可以直接照做的记录:从指标含义、公式拆解,到 Matlab 完整实现和绘图复现,顺带把踩过的坑都交代清楚。适合刚开始接触声发射数据处理的人,也适合已经在做 AE 监测、想用强度分析快速出图的人。
1. 声发射强度分析到底在算什么
1.1 HI 和 Sr 的物理含义
声发射(Acoustic Emission,AE)指的是材料在变形、裂纹扩展、纤维断裂、摩擦等过程中,以弹性波形式快速释放应变能的现象。传感器把这种微弱的弹性波转成电信号,再经过前置放大和数据采集,我们就得到一串离散的“AE 事件”。每个事件都能提取到达时间、峰值幅值、持续时间、能量、信号强度等特征。
在强度分析里,最常用的两个指标是 HI 和 Sr。HI 全称 Historic Index,中文常叫历史指数。它做的是“对比”:把最近 K 个事件的信号强度,和从试验开始到当前时刻的全部事件平均强度做比较。如果最近一段时间里出现异常强的信号,HI 会明显大于 1。换句话说,HI 负责捕捉“突变”。
Sr 全称 Severity,中文常叫严重度或严重性。它更简单直接:取最近 J 个事件的信号强度平均值,用来反映当前阶段的能量释放水平。Sr 越高,说明当前阶段的声发射活动越剧烈,往往对应着损伤加速或宏观裂纹扩展。这两个指标一个看突变,一个看水平,搭配起来用,比单看幅值或能量要可靠得多。
1.2 为什么强调信号强度,而不是幅值
不少人刚开始处理声发射数据时,习惯用峰值幅值(dB)来评价事件。幅值确实直观,但它有几个问题:受门槛阈值设置影响很大,信号衰减和传感器耦合情况也会显著改变读数。更重要的是,幅值只反映一个“瞬时峰值”,没有把整个波形的能量信息装进去。
信号强度就不用担心这些。它的定义通常是声发射波形经过整流后对时间积分得到的面积,单位常见是 pVs 或 V·s,也可以理解为波形释放能量的相对度量。因为它是整个波形的积分结果,所以对微弱持续信号和高幅短促信号都能给出稳定的量化值。用信号强度计算 HI 和 Sr,物理意义清楚,抗干扰能力强,这也是强度分析法在复合材料、金属疲劳、压力容器监测中被广泛采用的原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 指标公式与参数选取
2.1 HI 历史指数公式拆解
先把公式写出来。假设我们有一串按时间排列的 AE 事件,第 i 个事件的信号强度记作 S_o(i),当前共有 N 个事件。
HI 在第 N 个事件处的计算公式为:
HI_N = (N / K) × [ S_o(N-K+1) + S_o(N-K+2) + ... + S_o(N) ] / [ S_o(1) + S_o(2) + ... + S_o(N) ]
也就是:
HI_N = (N / K) × (最近 K 个事件的信号强度之和) / (全部 N 个事件的信号强度之和)
这个公式里,K 是滑动窗口长度,它会随着事件总数 N 变化。为什么要乘 N/K?你可以这样理解:把“最近 K 个事件的均值”和“全部 N 个事件的均值”做比值:
(最近 K 个均值) / (全部均值) = (最近 K 个之和 / K) / (全部之和 / N)
化简后正好得到 N/K × 最近K之和 / 全部之和。所以 HI 本质上是一个“当前局部均值相对历史总体均值”的倍数。如果最近事件强度刚好等于历史平均水平,HI = 1;如果最近阶段信号强度普遍升高,HI 就会大于 1。
在实际工程中,试验刚开始时事件数太少,算出来的 HI 没有统计意义。所以我通常的做法是:N 小于 20 时直接把 HI 设为 NaN,不参与后续绘图和判读。有些文献里会把早期 HI 强制设为 1,也能接受,但你要在报告里说明清楚。
2.2 Sr 严重度公式拆解
Sr 的定义相比 HI 简单很多。在当前第 N 个事件处,取最近 J 个事件的信号强度平均值:
Sr_N = [ S_o(N-J+1) + S_o(N-J+2) + ... + S_o(N) ] / J
如果当前事件总数 N 小于 J,一般有两种处理方式:一是把这几个点的 Sr 设为 NaN,二是用前 N 个事件的平均值。我更推荐第二种,因为这样可以保证强度图从早期开始就有数据点,只是要明确标注早期 Sr 的统计窗口期在变长。
Sr 的单位和信号强度一致,比如 pVs 或 V·s。它反映的是当前窗口内平均每个事件释放的能量水平。你可以把 Sr 理解为“最近一段时间的活跃度均值”,而 HI 则是“最近一段时间的活跃度相比历史是否异常”。两者一个看“大小”,一个看“变化”。
2.3 K 和 J 参数怎么选(附带经验表)
K 和 J 直接决定分析结果,但它们不是物理常数,而是经验参数。不同标准、不同材料、不同试验阶段,建议取值可能都不一样。下面这组是我在实际项目中常用的默认值,适合大多数实验室数据。
| 当前事件总数 N | K 取值 | 说明 |
|---|---|---|
| N < 20 | 不计算 HI | 早期数据太少,结果不稳 |
| 20 ≤ N ≤ 1000 | 20 | 常见默认窗口 |
| 1000 < N ≤ 5000 | 50 | 数据量增加后适当拉长窗口 |
| N > 5000 | 100 | 长时间试验建议用更大窗口 |
J 的值相对简单,我一般取 50 或 100。如果事件总数据量很少,J 可以取 20;如果试验时间很长、数据量很大,J 取 200 也常见。原则上 J 越大,Sr 越平滑,对短期波动的响应越慢;J 越小,Sr 越灵敏,但容易毛刺多。
这里要提醒一句:K 和 J 一旦用在前期的对比报告里,后面就尽量不要改。如果不同论文之间比较强度图,你最好把 K、J 参数写进图注,否则别人无法判断你的分区线和坐标范围是怎么来的。
3. 在 Matlab 里完整实现一遍
3.1 准备数据:先模拟一批 AE 事件
在做真实数据复现之前,我建议先用模拟数据把代码流程跑通。这样既能验证算法,也不会被现场数据的噪声带偏。
假设我们有一条 2000 个事件的模拟序列,时间间隔不规则,信号强度服从对数正态分布,并且在后段加入高幅损伤信号,用来模拟损伤加剧阶段。
matlab复制rng(42);
N_total = 2000;
% 模拟不规则的到达时间间隔
dt = 0.01 + 0.05 * rand(N_total, 1);
t = cumsum(dt);
% 基础强度:对数正态 + 缓慢上升趋势
baseIntensity = 50 + lognrnd(1.2, 0.5, N_total, 1) ...
.* (1 + 3 * (0:N_total-1)' / N_total).^2;
% 后段叠加更强的突发信号,模拟严重损伤阶段
baseIntensity(1500:end) = baseIntensity(1500:end) ...
+ lognrnd(4, 0.6, size(baseIntensity(1500:end)));
S0 = max(baseIntensity, 0);
这里用 lognrnd 生成对数正态分布,是为了贴近真实 AE 事件“幅值低的事件多,高幅事件少”的特点。后面那段叠加相当于人为注入损伤信号,方便你直观看到 HI 和 Sr 的响应。
如果你的数据已经存在 Excel 或 CSV 里,结构大概是三列:时间、信号强度、其他特征。读取方式很简单:
matlab复制data = readtable('ae_data.csv');
t = data.Time;
S0 = data.SignalStrength;
3.2 用循环还是向量化:计算 HI 和 Sr
计算 HI 和 Sr 时,因为 K 会随 N 变化,最直观的方式是用循环。N 在几万以内时,Matlab 循环完全跑得动,没必要一上来就追求向量化。
下面这个函数可以直接保存为 compute_HI_Sr.m 使用:
matlab复制function [HI, Sr] = compute_HI_Sr(S0, J, K_mode)
% 输入:
% S0 - 各 AE 事件的信号强度列向量
% J - Sr 的窗口长度,默认 50
% K_mode - 'default' 使用分段 K;'simple' 固定 K=50;其他值固定 K=20
% 输出:
% HI - 历史指数,早期为 NaN
% Sr - 严重度,早期使用当前累积均值
if nargin < 2 || isempty(J)
J = 50;
end
if nargin < 3
K_mode = 'default';
end
N = length(S0);
HI = nan(N, 1);
Sr = nan(N, 1);
% 累积求和,避免每次循环重复计算全部和
cumS = cumsum(S0);
for n = 1:N
% 早期事件太少,HI 无统计意义
if n < 20
continue;
end
K = getK(n, K_mode);
% 最近 K 个事件的信号强度之和
recentSumK = sum(S0(n-K+1:n));
HI(n) = (n / K) * recentSumK / cumS(n);
% Sr 计算
if n >= J
Sr(n) = mean(S0(n-J+1:n));
else
Sr(n) = cumS(n) / n; % 窗口不足时用当前累计均值
end
end
end
function K = getK(n, mode)
switch mode
case 'default'
if n <= 1000
K = 20;
elseif n <= 5000
K = 50;
else
K = 100;
end
case 'simple'
K = 50;
otherwise
K = 20;
end
end
这里有几个细节值得注意。
第一,我用 cumsum 预先算好全部信号强度的累计和,这样每次循环只需要算最近 K 个事件的和,整体复杂度会低很多。如果你在循环里写 sum(S0(1:n)),N 到几万之后会明显变慢。
第二,HI 早期返回的是 NaN,不是 1。这样在 loglog 绘图时可以直接忽略这些点,不会因为 1 或者 0 把坐标轴拉偏。Sr 早期用当前累计均值,保证图上有连续的数据点。
第三,K_mode 参数是为了方便你切换不同 K 策略。我一般在最终报告里只用默认模式,其他模式只用来做敏感性分析。
3.3 绘制强度分析图与趋势图
计算完成后,画图是重头戏。最常见的是“强度分析图”,横轴是 HI,纵轴是 Sr,整张图用 log-log 坐标显示。这样做的好处是把不同量级的点均匀铺开,并且能直观看到是否进入“高 HI、高 Sr”的危险区。
matlab复制% 计算 HI 和 Sr
J = 50;
[HI, Sr] = compute_HI_Sr(S0, J, 'default');
% 去掉早期 NaN,保证 loglog 能正常绘图
validIdx = ~isnan(HI) & ~isnan(Sr) & (Sr > 0) & (HI > 0);
HIv = HI(validIdx);
Srv = Sr(validIdx);
tv = t(validIdx);
figure;
scatter(HIv, Srv, 20, tv, 'filled');
set(gca, 'XScale', 'log', 'YScale', 'log');
xlabel('Historic Index (HI)');
ylabel('Severity (Sr)');
title('AE Intensity Analysis');
grid on;
colorbar;
c = colorbar;
c.Label.String = 'Time / s';
colormap(jet);
这张图里,每个点代表一个 AE 事件,颜色代表事件发生的时间。如果颜色从蓝到红逐渐进入高 HI、高 Sr 区域,说明损伤在随着时间加重。
接着看 HI 和 Sr 的时间趋势。因为两者量级差异大,我习惯用双 y 轴:
matlab复制figure;
yyaxis left;
plot(t, HI, 'b-');
ylabel('HI');
ylim([min(HI(validIdx)), max(HI(validIdx))]);
yyaxis right;
plot(t, Sr, 'r-');
ylabel('Sr');
xlabel('Time / s');
title('HI and Sr vs Time');
grid on;
注意,HI 和 Sr 的曲线会非常密集,如果事件数很多,直接用 plot 会导致线条重叠严重。这种情况下,你可以将数据按时间分段,每段取均值后再画,让趋势更清楚。
3.4 把脚本封装成函数方便复用
如果每次分析都要复制一遍代码,很容易出问题。我会把“读取数据 → 清洗 → 计算 → 画图”拆成几个小函数。
compute_HI_Sr.m 负责计算。另外可以写一个 plot_intensity_analysis.m 负责画强度图:
matlab复制function plot_intensity_analysis(t, S0, J, K_mode)
[HI, Sr] = compute_HI_Sr(S0, J, K_mode);
validIdx = ~isnan(HI) & ~isnan(Sr) & (Sr > 0) & (HI > 0);
figure;
scatter(HI(validIdx), Sr(validIdx), 20, t(validIdx), 'filled');
set(gca, 'XScale', 'log', 'YScale', 'log');
xlabel('Historic Index (HI)');
ylabel('Severity (Sr)');
grid on;
colorbar;
colormap(jet);
end
这样后续只要调用一行:
matlab复制plot_intensity_analysis(t, S0);
就算换了新数据,也能保证出图风格一致,参数统一,少踩很多重复的坑。
4. 实际使用中绕不开的坑
4.1 信号强度的单位和数量级
不同采集系统给出的信号强度单位差异很大。有的用 pVs,有的用 nVs,有的直接给无量纲的“能量值”。在单独看曲线时,单位影响不大;但如果你要在强度图上画固定的警戒线,或者拿两组不同单位的数据做对比,就会出问题。
我的建议是:数据导入后先统一换算成同一个单位,并在脚本开头注释清楚。比如:
matlab复制% 原始单位是 pVs,统一转换为 nVs
S0 = S0 / 1000;
这样后续 Sr 的绝对值才有意义。否则你画的 y 轴刻度可能差好几个数量级,分区线完全错位。
4.2 早期 HI 的 NaN 和除零问题
在计算 HI 时,如果 cumS(n) 为 0,也就是前面所有信号强度都等于 0,那分母一旦出现 0,Matlab 会给出 Inf 或 NaN。真实 AE 数据基本不会全是 0,但模拟数据或仪器门槛设置过高时有可能遇到。
建议在计算前对数据做一次预处理:把所有非正数信号强度统一为很小的正数,或者直接在有效事件里剔除。我更推荐直接剔除,因为信号强度为 0 的“事件”往往不是有效声发射事件,而是电噪声或仪器触发异常。
matlab复制valid_events = S0 > 0;
t = t(valid_events);
S0 = S0(valid_events);
这样处理之后,loglog 不会因为 0 或负值报错,计算过程也更稳定。
4.3 强度图的分区阈值怎么定
很多文献里的强度分析图会划分几个区域,比如“轻微”“中等”“严重”,并用斜线或矩形区域标出来。不少人以为这些分区是行业标准,其实不是。分区线的位置和材料、传感器布置、加载方式都相关,脱离试验直接套论文里的阈值,很容易做出误导性结论。
我更推荐的做法是:先用同样条件下的健康样本建立基线,或者用试验前期的数据当作参考。比如先画一次全过程的强度图,看数据点大致分成几簇,再根据 HI > 1.5 和 Sr > 历史平均值的 2 倍作为临时警戒线。等样本多了,再慢慢把阈值固化下来。
如果一定要在图上画一条参考线,可以在 scatter 之后叠加:
matlab复制hold on;
xline(1.5, 'k--', 'HI=1.5');
yline(2 * nanmean(Sr), 'k--', '2xMean Sr');
hold off;
这条线只适合辅助观察,不建议直接写进最终判定结论。
4.4 数据量大时如何提速
我的一个长时间试验项目里,AE 事件能到几十万甚至上百万个。这种情况下,上面那种直接循环会非常慢,主要瓶颈在每次循环都执行一次 sum(S0(n-K+1:n))。
改进思路是:用累积和来算任意窗口区间的和。因为区间 [n-K+1, n] 的和可以写成 cumS(n) - cumS(n-K),所以不用每次都调 sum。改造后的循环体可以写成:
matlab复制recentSumK = cumS(n) - cumS(n-K); % 注意 n-K 可能为 0,需单独处理
if n-K >= 1
recentSumK = cumS(n) - cumS(n-K);
else
recentSumK = cumS(n); % 窗口越界时直接用累计和
end
对于 K 固定为某个值的情况,还可以直接用 movsum:
matlab复制recentSumK_list = movsum(S0, K);
然后逐点计算 HI,就能省掉内层窗口求和。再进一步,如果 N 超过几百万,考虑把数据分块后用 parfor,但要注意 cumS 和窗口区间要处理好,否则并行容易出错。
5. 再分享一点个人经验
这套流程我在复合材料层压板拉伸试验里复现过很多次。第一次跑出来的时候,HI 曲线和 Sr 曲线会跟着加载阶段走:初期很平稳,中期出现零星尖峰,后期基本是一路抬高。你会发现 HI 对“突然出现的高强度事件”特别敏感,往往在肉眼看到幅值异常之前,HI 就已经给出波动。而 Sr 更像一个“慢变量”,适合判断整体损伤程度。
所以我实际看数据时,不会只盯着一幅强度图。我会把强度图和加载力曲线放在同一个时间轴上,把 HI 超过阈值的时刻标出来,再去回看原始波形。这样做的好处是能区分“真实损伤信号”和“机械摩擦噪声”,减少误判。
最后再提一个很多人忽略的小技巧:绘图的坐标范围要保持一致。如果你今天用自动坐标,明天又改了数据范围,图形会被自动缩放得面目全非,前后对比就失效了。我会在出图前手动固定 xlim 和 ylim,比如:
matlab复制xlim([0.5, 20]);
ylim([1, 1e5]);
这套代码和思路直接平移到你自己的声发射数据集上,基本都能跑通。只要数据里包含时间和信号强度两列,剩下的事就是调参和解释结果了。
