写这篇的起因,是上周有人问我,手里有一段振动信号,噪声很大,特征频率都埋在底噪里了,想按频段拆开看趋势,但又不愿意用带通滤波器去硬切,怕边界效应把相位搞坏。我给的方案就是标题里这套组合拳:EEMD 分解降低模态混叠,样本熵量化每个分量的复杂程度,再按熵值大小把 IMF 重组成低、中、高频三组信号。这篇文章就把这套流程的完整逻辑、实际代码思路和踩坑记录都摊开讲清楚,给做故障诊断、生物电信号处理和结构监测的同行一个可以直接拿去参考的落地方案。
1. 从 EMD 到 EEMD:为什么要换这个分解工具
1.1 经验模态分解的基本思路
理解 EEMD 之前,先得说清楚 EMD 到底在干什么。经验模态分解,全称 Empirical Mode Decomposition,做的事情很直观:把一段复杂信号自适应地拆成若干个本征模态函数,也就是 IMF,再加一个残差。每个 IMF 需要满足两个条件:极值点数量和过零点数量相等或至多相差一个;上下包络的均值在任意位置都趋近于零。换句话说,EMD 不是用固定基函数去拟合信号,而是完全跟着信号本身的局部特征走,所以它在处理非线性、非平稳信号时,比傅里叶变换和小波变换更灵活。
理论上,原始信号可以被写成所有 IMF 加上残差的形式。这个分解过程是数据自适应的,不同信号的 IMF 数量和频率成分都不一样。拿机械振动信号举例,如果转频是 50 Hz,啮合频率是 800 Hz,EMD 会依据信号自身的时间尺度,把这两个成分分别分离到不同 IMF 中,这对后续的包络分析或者特征提取非常友好,因为它不需要预先设定滤波器的通带和阻带。
但 EMD 有一个很出名的问题,就是模态混叠。所谓模态混叠,是指分解出的某个 IMF 中出现跨度很大的异尺度成分,或者一个相似尺度成分被拆分到两个不同 IMF 中。最常见的触发因素有三个:信号中存在间歇性高频成分,比如瞬态冲击;噪声水平过高,导致包络拟合失真;以及信号幅值动态范围过大,端点效应和三次样条插值把包络算偏了。模态混叠直接影响后续分析,因为一旦 IMF 成分不纯,希尔伯特谱上就会出现虚假频率带,样本熵也会被杂散成分主导。
1.2 集合平均是怎么抑制模态混叠的
EEMD 的全称是 Ensemble Empirical Mode Decomposition,思路非常巧妙,甚至可以说带着一点“暴力美学”:既然 EMD 对噪声敏感,那就在原始信号上人为添加白噪声,用噪声的统计特性去“填充”信号中缺失的尺度,让 EMD 能更充分地分离各种成分。每添加一次不同幅值的白噪声,就做一次 EMD,得到一组 IMF。重复多次之后,对所有对应的 IMF 取平均,因为白噪声在多次试验中是不相关的,平均之后噪声成分趋近于零,剩下就是真实的模态成分。
写成数学一点的说法是:第 i 次分解时构造信号
xi(t) = x(t) + wi(t)
对 xi(t) 做 EMD,得到 IMFi1(t), IMFi2(t)...,重复 N 次后,最终第 j 个 IMF 是
IMF_j(t) = (1/N) * Σ IMFij(t)
这个平均过程的核心意义在于,每一次加入不同的白噪声,相当于给原始信号提供了不同的局部扰动参考,让极值点在包络拟合时不容易被间歇成分“带偏”。只要集合次数 N 足够大,白噪声贡献的残余就会互相抵消。这就是为什么 EEMD 能有效缓解模态混叠的原理,也是它相对 EMD 最大的工程价值。
但这里要注意,EEMD 不是没有代价。计算成本高是一方面,另一方面如果参数选得不好,比如噪声幅值太小,对模态混叠没有任何改善;噪声幅值太大,又会对原始信号产生过多扰动,导致分解出的 IMF 被噪声污染,反而把信噪比拉低。后面我会专门讲参数怎么定。
2. 样本熵是干什么的,怎么算
2.1 从近似熵到样本熵
分解完之后,每个 IMF 都是一段“较纯”的成分,但问题来了:哪些 IMF 属于低频趋势,哪些属于中频主成分,哪些属于高频细节?如果只看频谱峰值,可以做一个粗略判断,但在实际信号里,频谱峰值往往模糊不清,特别是存在噪声底和高次谐波的时候。这时候我习惯用样本熵作为每个 IMF 复杂度度的量化指标。
样本熵,即 Sample Entropy,是用来衡量时间序列产生新模式概率的指标。序列越规律、越周期化,样本熵越小;序列越随机、越无序,样本熵越大。它和近似熵思路相似,但克服了近似熵对短序列估计偏差较大的问题,也不依赖模板自身的匹配,稳定性更好。
在信号重构中的应用逻辑是:低频分量往往对应信号的主体趋势或慢变特征,周期性强,复杂度低,样本熵最小;高频分量对应细节、冲击、噪声等快速起伏成分,随机性强,复杂度高,样本熵最大。通过计算每个 IMF 的样本熵,我们就能得到一个“复杂度谱”,按阈值把 IMF 分成低频、中频、高频三组,然后重新叠加成三路信号,这就是标题里“按样本熵大小进行信号重构”的技术含义。
2.2 样本熵的计算步骤和参数选择
样本熵的计算涉及几个关键参数:模式维度 m 和容差阈值 r,此外需要一个序列长度 N。计算过程我可以简化描述如下:
把长度 N 的时间序列按顺序构造成 N-m+1 个 m 维向量,每个向量包含连续的 m 个点。对每个向量,和所有其他向量计算切比雪夫距离,即两个向量对应位置差值的绝对值的最大值。统计距离小于 r 的向量对数量,并计算其占比例。然后同样的流程换成 m+1 维向量,再做一次。样本熵的最终公式是负的 ln(两个比例相除的结果)。
这里有一个常见误区,很多初学者对公式本身很熟悉,但参数乱选。实际工程中,Pincus 提出的建议是 m=2,r 取原始序列标准差的 0.1 到 0.25 倍。我自己在振动信号上做过反复测试,m=2 是几乎不会出错的默认设定,因为 m=3 虽然对复杂度分辨更精细,但对噪声更敏感,而且计算量大很多。r 则需要根据信噪比调整:信号比较干净时取 0.1 倍标准差较好,能分辨细微复杂度差异;信号噪声大时取 0.2 倍标准差更稳,避免把噪声当成有效复杂度。
样本熵的值本身没有绝对标尺,它是一个相对比较的指标,脱离信号类型谈“样本熵大于多少算高频”没有意义。这一点我特别强调,因为不同信号的能量分布差异巨大,相同的样本熵值在一段加速度计信号里可能代表噪声居多,在一段心电信号里可能代表生理细节。所以实际做法永远是先对全部 IMF 算出各自的样本熵,再排序、聚类或者按比例阈值分组,而不是对着绝对数值判断。
3. 第三步:按熵分箱,重构出低中高频信号
3.1 IMF 分组策略:阈值怎么定
当我们拿到 N 个 IMF 以及对应的样本熵值 S1, S2, ..., SN 之后,要做的事情就是把它们分成低频组、中频组和高频组。分组策略大致有四种,我按实用性排序:
- 临近算法分组:把样本熵值排序后,设定两个阈值 T1 和 T2,小于等于 T1 的归入低频,大于 T1 且小于等于 T2 的归入中频,大于 T2 的归入高频。T 的取值可以用等百分比法,比如所有熵值的三分位点,也可以用离群值法。
- 聚类分组:对样本熵值做 K-means 聚类,K=3,自动分成三组。这个方法的好处是不需要人为定阈值,坏处是聚类结果不定,需要设置随机种子才能复现。
- 结合能量和熵联合分组:样本熵只反映复杂度,没有反映能量大小。有些场景下,一个熵值很大的 IMF 可能能量极小,对重构结果影响微乎其微,此时可以把能量占比低于 1% 的分量直接忽略,再对剩余分量按熵分组。
- 频率验证兜底:分组完成后,对每组重构信号计算主频,检查是否符合预期频段。例如低频组主频应该在几十赫兹以下,中频组对应转频或故障特征频率,高频组对应啮合频率及边带。如果不一致,大概率是分解参数有问题。
如果追求可复现性和稳定性,我推荐阈值法。具体做法是把样本熵从小到大排序,低频阈值 T1 取第 33 百分位,高频阈值 T2 取第 66 百分位。但注意,这只是一个公平的起点,不是每个信号都均匀落在三组里。有些信号只有中低频,高频组可能一个 IMF 都分不到,这很正常,不必硬凑三组。
3.2 重构过程和细节:残差怎么处理
样本熵分组完成之后,重构就是把同组 IMF 直接求和。假设原信号被分解为 M 个 IMF 加一个残差 R(t),那么低频组重构信号为
L(t) = Σ_{j in low group} IMF_j(t)
中频、高频同理。残差 R(t) 通常是信号趋势项,它是单调序列或者极低频率成分,样本熵必然很小,一般直接归入低频组,也可以单独提出来,看需求而定。我的经验是,如果后续要做趋势分析和基线漂移校正,残差单独保留更好;如果要还原完整信号,残差就放到低频组里。
重构时有一个很容易被忽略的点:IMF 求和后的幅值和原始信号幅值不一定完全一致。原因在于 EEMD 是近似分解,每个 IMF 都可能带有微小噪声残余,平均化处理之后这些噪声不会完全消除。所以重构后我建议做一次幅值校准,具体做法是计算原始信号 x(t) 和重构信号 x_rec(t) 之间的线性回归系数,也就是让 x_rec(t) = a * x(t) + b,用最小二乘估计 a 和 b,如果 a 偏离 1 超过 5%,就要重新审视 EEMD 的参数是否合理。实际上这就是一个简单的代码核对步骤,能很好地暴露分解质量问题。
重构的本质是“按复杂度分箱”,不是“按频段硬滤波”。这是两种完全不同的思路。硬滤波假设信号频率范围已知,且通带内的噪声和有效成分无法区分;而 EEMD 加样本熵是数据自适应的,不预设频段,它更擅长对付那些频率随时间漂移的信号。但这也带来一个缺点:无法精确指定输出的截止频率。如果你需要严格意义上的 0-100 Hz 低频信号,那还是得用滤波器,EEMD 加样本熵给的是一种“近似低频但更保真”的选项。
4. 实操过程:Python 实现一整套流程
4.1 环境准备与关键函数库
我实现这套流程用的主要工具是 Python,核心库是 PyEMD 和 NumPy,辅助库是 SciPy 和 Matplotlib。PyEMD 是专门做 EMD 和 EEMD 的库,GitHub 上一直有人维护,安装直接用 pip。SciPy 用来做信号模板匹配和滤波校验,NumPy 做数组运算,Matplotlib 出图对比。另外如果信号量很大,我建议用 Numba 给样本熵的计算加速,否则纯 Python 循环算几千个点的样本熵会有些慢。
具体实现上,先做两件事:设置 EEMD 参数和定义样本熵函数。PyEMD 库中调用 EEMD 的核心代码如下,这可以作为一个基本框架:
from PyEMD import EEMD, EMD eemd = EEMD( n_splits=100, # 集合试验次数 noise_width=0.02, # 噪声幅值系数 parallel=True, # 并行计算 emd=EEMD().emd if False else None ) # 上面这行 emd 参数只是示例,实际我用的是默认 EMD eemd = EEMD(trials=200, noise_width=0.05) IMFs = eemd.eemd(signal, T)PyEMD 不同版本的接口略有差异,有的版本用 trials,有的版本用 n_splits,使用时先看版本说明。noise_width 是白噪声幅值相对信号标准差的比值,取值 0.01 到 0.1 是比较常见的区间。trials 建议最低 50,追求稳定跑 200 到 500。看到这里你可能觉得 trials 越高越好,但实际要考虑计算时间和随机种子问题。如果 trials 太低,比如 20 次,白噪声残余太多了,均值之后信号会被压低;trials 太高,比如 1000 次,计算时间倍数增长,而最后 200 次到 1000 次的差异很小。
4.2 样本熵函数的实现与验证
样本熵的计算逻辑前面已经讲了,但代码实现上有一些加速手段值得分享。最原始的双重循环写法是这样的:外层对每个模板向量,内层对每个后续向量,计算距离并统计。问题在于当序列长度达到几千点,维度 m=2 时,复杂度是 O(N^2),跑起来会特别难受。对一段 10 秒 1000 Hz 采样的信号,也就是 10000 个点,双循环可能要几十秒。
我的优化思路是利用 SciPy 的空间距离计算函数 cdist 一次性算出所有向量对的距离矩阵,然后直接向量化比较。这个优化对长度在 10000 以内的序列特别有效。样本熵函数可以直接参考这个核心逻辑:
import numpy as np from scipy.spatial.distance import cdist def sample_entropy(x, m=2, r_factor=0.2): x = np.asarray(x, dtype=np.float64) x = x - np.mean(x) std = np.std(x) r = r_factor * std N = len(x) # 构造 m 维向量 def _phi(m): n_v = N - m + 1 mat = np.array([x[i:i+m] for i in range(n_v)]) dist = cdist(mat, mat, metric='chebyshev') count = np.sum(dist < r, axis=1) - 1 count = np.maximum(count, 0) return np.sum(count) / (n_v * (n_v - 1)) phi_m = _phi(m) phi_m1 = _phi(m + 1) if phi_m == 0 or phi_m1 == 0: # 出现零概率时返回极大值或 NaN,实际处理中可以跳过该IMF return float('nan') return -np.log(phi_m1 / phi_m)这里要说明几点。切比雪夫距离用 cdist 的 metric='chebyshev' 直接算,一行搞定。对角线的距离是 0 一定被计入,所以需要减 1。m+1 维向量个数是 N-m,模板对总数是 n_v*(n_v-1),注意分母别算错。如果某组数据过于规律,可能出现 phi_m1 为 0 的情况,此时样本熵定义为无穷大,但实际处理中我会直接返回 NaN,并在分组时手动剔除。这类情况多发生在纯正弦信号或完全周期的仿真信号中,实测信号很少遇到。
4.3 重构校验和三维信号对比
拿到 IMF 和样本熵之后,重构这部分我给出一个比较完整的操作框架。首先计算所有 IMF 的样本熵,得到数组 S。然后设定阈值,把 IMF 分成三组,最后按组求和得到 L、M、H 三个信号。对应代码如下:
# 计算每个IMF的样本熵 entropies = [] for imf in IMFs: se = sample_entropy(imf, m=2, r_factor=0.2) entropies.append(se) entropies = np.array(entropies) # 排除NaN分量 valid_idx = ~np.isnan(entropies) sorted_ent = np.sort(entropies[valid_idx]) t1 = np.percentile(sorted_ent, 33) t2 = np.percentile(sorted_ent, 66) low_group = [] mid_group = [] high_group = [] for idx, se in enumerate(entropies): if np.isnan(se): continue if se <= t1: low_group.append(idx) elif se <= t2: mid_group.append(idx) else: high_group.append(idx) L_sig = np.sum(IMFs[low_group], axis=0) M_sig = np.sum(IMFs[mid_group], axis=0) H_sig = np.sum(IMFs[high_group], axis=0)这段代码里有个隐藏前提:IMFs 数组的行是不同 IMF,列是时间点。如果你的分解结果是列表格式,记得先堆叠成二维数组。重构完之后,我强烈建议出三张图:第一张是原始信号和低频组信号对比,看趋势跟踪是否到位;第二张是中频组信号和原始信号叠加,看主特征频率保留程度;第三张是高频组信号的频谱,确认高频成分没有被中频组吞掉。
用频谱来验证分组是否合理是我每次都必做的环节。可以分别对 L、M、H 做快速傅里叶变换,查看它们的主频是否落在合理的频段内。比如振动信号转速是 30 Hz,故障特征频率是 180 Hz,齿轮啮合频率是 1080 Hz,那预期结果是低频组主频集中在 30 Hz 附近及以下,中频组主频在 180 Hz 附近,高频组主频在 1080 Hz 和更高频段。如果发现高频组主频跑到中频组去了,优先检查 EEMD 的噪声幅值是否过大,再检查样本熵的 r 值是否选小了。
4.4 参数调试实例:一组仿真信号的完整结果
为了演示参数对结果的影响,我构造了一段仿真信号来说明:
fs = 1000 T = np.arange(0, 5, 1/fs) x_low = 0.6 * np.sin(2*np.pi*2*T) x_mid = 1.0 * np.sin(2*np.pi*45*T + 0.5) x_high = 0.4 * np.sin(2*np.pi*180*T) + 0.25*np.sin(2*np.pi*420*T) noise = 0.15 * np.random.randn(len(T)) signal = x_low + x_mid + x_high + noise这段信号理论上应该重构出三个清晰频段。我分别用三组参数跑了一遍,结果差异非常明显:
| 参数组合 | 集合次数 | 噪声幅值 | 重构结果描述 |
|---|---|---|---|
| 第一次 | 100 | 0.02 | 低频和中频分离干净,高频组有底噪混入,包络毛刺多 |
| 第二次 | 100 | 0.05 | 三频段分离都很好,高频组毛刺明显减少 |
| 第三次 | 500 | 0.05 | 和第二次基本一致,但计算时间从 12 秒增加到 50 秒 |
这个实验说明两个结论:噪声幅值比集合次数更敏感;集合次数超过一定值后收益递减。所以实操中我建议先用 100 次和 0.05 噪声幅值跑一遍看结果,如果 IMF 频谱混叠明显再加大 noise_width,如果只想降低随机波动再提高 trials。不要一开始就上 500 次,跑半天发现参数选错了,白白浪费时间。
另外这段仿真信号也体现了样本熵分组的一个特点:三个正弦成分频率分别是 2 Hz、45 Hz、180 Hz,实际上样本熵大小除了和频率有关,还和波形复杂度、信噪比有关。45 Hz 附近如果叠加了高次谐波,它的样本熵可能比 180 Hz 纯正弦还高。这时候按样本熵分组出来的“中频”不一定对应数学上的频段,但它一定是复杂度上更接近的分组。这在某些工程场景正好是优点,因为它能自动把“形态复杂”的成分归到一起。
5. 实际操作中的常见问题和避坑经验
5.1 高频段出现低频趋势:端点效应在捣乱
我在用 EEMD 处理长度较短信号时,经常遇到一个奇怪现象:最后一个 IMF 明明是趋势项,但高频组重构信号里却叠了一个缓慢起伏的基线漂移。最初我以为是分组分错了,后来看了 IMF 的单独波形才发现,问题出在 EMD 的包络拟合在信号两端产生了畸变,导致一个原本低频的成分泄漏到了其他 IMF 中。这种端点效应在信号首尾不连续时尤其严重。
解决这一问题有两个常用手段。第一,在分解前对信号做镜像延拓,PyEMD 库中可以通过设置 spline_kind 和 range_threshold 等参数来控制端点效应,但最稳妥的办法还是手动在信号两端各拼接一段镜像数据,分解后再截断。第二,在分组前把每个 IMF 的首尾 5% 数据做渐变衰减,也就是加一个汉宁窗的斜坡,减少边界异常对整体样本熵计算的影响。不过方法二会损失边界信息,如果边界正好包含瞬态特征,我宁可选择镜像延拓。
我个人的经验是,在分解前先对整段信号做趋势去除。拿原始信号减去它的局部均值或多项式拟合趋势,这样 EEMD 就不需要把大量能量分配给残差值IMF,端点效应会明显减弱。
5.2 样本熵全是一样大:r 值选错了
另一种常见情况是所有 IMF 的样本熵都差不多,分组阈值分不开。这个问题 90% 的概率出在 r 参数上。样本熵计算中 r 是基于每个 IMF 自身的标准差来归一化的,这本身是合理的,因为每个 IMF 的能量尺度不同,绝对容差不同,用标准差归一化可以统一比较。但如果 r 取得太大,比如 0.4 倍标准差,几乎所有向量对都能在 m 和 m+1 维度都找到匹配,比率接近 1,样本熵趋近于 0,区分度全丢了。相反,如果 r 取得太小,比如 0.02 倍标准差,几乎找不到匹配,样本熵全部趋近于无穷或 NaN,同样没有区分度。
我调试时有个快速自检法:打印每个 IMF 的标准差和样本熵。如果所有样本熵都在一个极窄的区间,比如 0.1 到 0.15,说明 r 过大,可以按 0.15 倍标准差重试;如果样本熵出现大量 NaN 或几百上千的天文数字,说明 r 过小,改成 0.25 倍标准差再跑。对于振动信号,0.15 到 0.25 倍是黄金区间;对于心电、脑电等生理信号,因为有效成分更规律,可以试 0.1 到 0.15 倍。这个参数没有绝对最优,它取决于你对“复杂度差异”的分辨需求。
还有一个细节是,不同 IMF 的幅值差异可能非常大,特别是第一个 IMF 和最后一个 IMF 之间可能差一个数量级。如果 r 用全局统一的比例系数 0.2,那么对小幅值的 IMF 来说,容差可能过松,熵值偏低;对大幅值的 IMF 来说,容差可能过紧,熵值偏高。这时候可以选择按每个 IMF 自身的标准差做归一化,这是标准公式的常见做法,但在分组时要意识到这个归一化消除了绝对幅值的影响。
5.3 重构信号和原始信号对不上
这种情况多在 EEMD 参数不合适时出现。表现是低频组加中频组加高频组之和与原始信号相差很大,相关系数很低。我先给一个排错顺序:
- 检查分解是否完整:把 sum(IMFs, axis=0) 近似等于原始信号减去残差,先确认这一步是否成立。如果不等,说明 EEMD 分解没有收敛,或者 trials 太少导致噪声残余过大。
- 检查缺失分量:查看是否因为样本熵 NaN 跳过了某个 IMF,如果跳过了一个能量很大的 IMF,重构信号自然对不上。
- 检查幅值系数:对重构信号和原始信号做线性拟合,得到系数 a。如果 a 在 0.9 到 1.1 之间,说明整体幅值没有大偏差;如果偏离太多,检查是否在分解前做了归一化,但没有在重构后恢复。
如果确定是 trials 太少的问题,把集合次数从 100 提升到 200 到 300 再跑一次。还有一个不太起眼但常见的坑:你在分解前对信号做了去均值或标准化,比如减去了均值并除以标准差,分解和重构后忘了恢复。这会导致重构信号整体偏置和幅度错误,不是方法的问题,是预处理还原的问题。
5.4 计算速度太慢的优化方案
EEMD 本身计算量偏大,集合次数 200 次意味着同一个信号要跑 200 遍 EMD,每一遍还要处理几十个筛选迭代,再加上样本熵的双循环或矩阵计算,整条流程对短信号很轻松,对长信号就可能要吃内存和时间了。我处理过一段 10 分钟、采样 2048 Hz 的信号,总点数超过 120 万,直接跑 EEMD 将近半小时,样本熵如果用 cdist 构造 100 万乘 100 万的矩阵,内存直接爆掉。
优化手段按优先级排列的话:
- 信号分段分析:把长信号切成有重叠的段,比如每段 10 秒、重叠 2 秒,分别做 EEMD 和样本熵,最后拼接。分段还能顺便解决计算复杂度问题。切的时候注意重叠长度要大于最大 IMF 的周期,否则接缝处会有跳变。
- 对样本熵改用 KDTree 搜索替代全矩阵 cdist:当向量数非常大时,用 KDTree 查半径范围内的邻居,可以大幅减少计算量。
- 降低采样率:如果分析的最高频率远低于采样率,比如 2048 Hz 采样但只关心 500 Hz 以内,可以先低通滤波再降采样到 1200 Hz 左右。这个操作要放在 EEMD 之前,降采样后信号更短,计算量下降很明显。
- 并行计算:PyEMD 的 parallel 参数可以开启多进程,多个 trials 同时跑。我实测四核机器上能加速 2.5 到 3 倍,没有线性加速是因为 GIL 和进程调度开销存在。
5.5 边界问题:低中高频三组在时间轴上的平滑性
重构出的 L、M、H 三路信号,如果直接在分组边界处切换,很容易在时间轴上出现跳变。比如某个 IMF 在前一秒属于中频组,后一秒因为信号特征变化,样本熵瞬时升高,就可能被分到高频组。但我们在分组时用的是该 IMF 在整个时间段的样本熵,这是一个全局标量,所以不会出现时刻间的跳变问题。真正要注意的是两个很相近的 IMF,它们的样本熵差异很小,分组时可能因为阈值刚好卡在中间,导致一个 IMF 被分到中频组,另一个被分到高频组,而实际上它们的复杂度几乎一样。
这种情况从时域波形上看不出啥问题,但如果你把 L、M、H 三个信号按频谱图排列,会发现频谱过渡不自然,像是有个断层。要缓解这个问题,可以在全局样本熵之外,再计算每个 IMF 的滑动窗口样本熵,把窗口熵的中位数作为分组依据,这样能滤掉瞬间的复杂度异常。代价是计算量变大,但多了一层稳健性。我通常会根据信号长度决定要不要做这一步,如果信号在 30 秒以内,直接做;如果超过 5 分钟,先分段再处理。
6. 应用场景扩展和工程落地建议
6.1 机械故障诊断里的典型用法
振动信号放进这套流程里,最典型的产出是低频趋势、中频故障成分、高频冲击成分三路信号。它们分别对应轴的转速波动、齿轮或轴承的故障特征频率、以及早期点蚀或裂纹引发的瞬态冲击。这三个成分如果用传统带通滤波器,你得事先知道故障频率,而故障频率本来就是你想要辨识的目标,这就陷入一个先有鸡还是先有蛋的问题。用 EEMD 加样本熵,就不需要先验频率信息,让数据自己分组,分组结果可以直接作为后续神经网络的输入特征。
比如一个常见的做法是,对三路重构信号分别计算 RMS、峰峰值和峭度,得到一个三维特征向量。故障早期,高频组的峭度会先上升,因为瞬态冲击增多;中频组的均方根会上升,因为故障特征频率能量增强;低频组的波动范围则反映出负载变化和轴弯曲趋势。这个三维特征比直接对原始信号算峭度更敏感,因为高频冲击被单独分离出来了,不会被主轴的强振动掩盖。
6.2 生物电信号处理的特殊注意事项
心电和脑电信号与机械信号差异很大。它们更平稳、更有节律性,频率范围低,采样率相对低。用 EEMD 时,白噪声幅值要调小,我用心电信号时一般取 0.01 到 0.02,因为心电本身信噪比较高,噪声幅值太大容易把 P 波和 T 波的细节磨平。样本熵的 r 建议取 0.1 倍标准差,因为心电的复杂度差异主要体现在小尺度的细节变化上,容差太大会把 P 波的变异忽略掉。
此外,心电分解重构时,低频组通常对应呼吸基线漂移,中频组对应 QRS 主波,高频组对应 P 波细节和肌电干扰。这个分组结果可以直接用于去除基线漂移:把低频组从原始信号里减掉就行,比传统的高通滤波器保留更多非线性漂移特征。我实测过对运动状态下采集的心电数据做这种处理,比零相位 Butterworth 滤波效果好很多,因为呼吸引起的基线漂移频率和心率接近时,普通滤波器会伤到 ST 段。
6.3 结构健康监测和地震信号处理
结构健康监测领域经常要处理长期采集的应变、位移和加速度数据。这些数据有一个特点:有效信号极其微弱,环境噪声和温度漂移占比很大。EEMD 加样本熵的分组逻辑在这里特别有用,因为温度漂移导致的低频趋势有很强的周期性和随机性混合特征,简单线性拟合无法消除,而 EEMD 能把温度响应从结构振动响应中自然分离。低频组的趋势可以作为温变特征输入后续的温度补偿模型;中频组对应风和微振动;高频组对应局部冲击或交通载荷。
地震信号处理中,P 波和 S 波到达时刻往往被背景噪声掩盖,EEMD 分解后高频组里会先出现幅值突增,对应 P 波初至,随后中频组出现密集振动,对应 S 波。这个检测逻辑本质上就是样本熵突变检测,因为 P 波到达前信号复杂度低,到达后复杂度突然升高,你可以只对高频组做滑动窗口样本熵,设定一个自适应阈值来触发报警,比直接在原始信号上做 STA/LTA 更少误报。
6.4 工程落地的几个建议
最后聊几个从仿真到实际部署的通用建议。第一个是随机种子问题。EEMD 每次运行随机数不同,导致结果不完全可复现,这在科研中可能不是大问题,但在工业系统里,如果每次程序启动跑出来的 IMF 不一样,自动化流程维护起来会很痛苦。解决方案是在调用 EEMD 前设定全局随机种子,比如 np.random.seed(42),或者在 PyEMD 中指定 noise_seed 参数,不同版本可能命名不同,但思路一致。
第二个是阈值参数的自适应化。样本熵阈值 T1 和 T2 如果写死,换一段信号就失效。工程上更稳的做法是每次跑完都根据当前 IMF 的熵分布动态计算阈值,甚至可以把阈值做成滑动窗口版本,让系统随着设备老化自动调整分组边界,而不需要人工重新标定。
第三个是保存中间结果。EEMD 计算成本高,一次分解结果和样本熵值得序列化保存,后续调参和复盘时直接加载,不用重算。我习惯用 npz 格式保存 IMF 数组、样本熵数组和 EEMD 参数,打包成一个字典,每次实验对应一个文件。这样不仅能复现,还能很方便地做多组参数对比。
第四个要说的可能是最容易被忽略的:EEMD 加样本熵的重构不是滤波器,不要指望它能输出严格频段信号。它输出的低频、中频、高频是“复杂度分组产物”,在绝大多数应用里够用、甚至更好,但如果你的下游算法对频率边界有硬性要求,比如需要严格的 0.1 Hz 到 40 Hz 频段,那一定要在重构后再加一个相同频段的带通滤波。正确的使用姿势是把 EEMD 加样本熵当成一个自适应特征提取器,而不是一个精密滤波工具。
我自己用过这套组合处理过不少信号,最深刻的体会是:参数不会一次到位,但分解结果能告诉你怎么调参数。第一次跑完多看看 IMF 波形和样本熵分布,比盲目翻公式和调阈值有效得多。