恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
从EMD到CEEMDAN:模态分解演进与Python实现详解
首页
资讯中心
/
从EMD到CEEMDAN:模态分解演进与Python实现详解
从EMD到CEEMDAN:模态分解演进与Python实现详解
发布时间:2026/10/2 16:05:37
简介一套面向信号处理研究者的CEEMDAN改进算法MATLAB实现资源包聚焦EMD、EEMD与CEEMDAN在非线性非平稳信号分析中的模态混叠问题。CEEMDAN利用自适应噪声取代EEMD的随机噪声既能继承集成平均的稳健性又能提高IMF分解精度与收敛速度它会在每次迭代中添加与信号相位相关的白噪声再经多次平均获得更纯净的模态分量适用于地震波多尺度解析、心电脑电信号去噪、机械故障诊断、金融波动识别等场景。压缩包整体仅32KB共5个文件包含4个可直接运行的核心.m算法函数与1个.mat心电信号测试样本覆盖CEEMDAN、EEMD、EMD三种分解方法及配套示例脚本读者可轻松替换数据并对比各算法的分解效果。该资源发布后已吸引5930人学习代码结构紧凑、内置注释清晰既适合初学者从EMD原理出发理解EEMD和CEEMDAN的改进逻辑也能为进阶研究者提供算法对比实验与参数调试的参考基准而且包体极小、部署方便非常适合相关方向的研究者下载使用。1. CEEMDAN算法不是换了个名字三种模态分解到底差在哪做信号处理的人手里几乎都跑过EMD或者至少听过它的大名。EMD能把一段乱七八糟的非平稳信号自适应地拆成若干条本征模态函数IMF这本是件很漂亮的事。但用过的人都知道EMD有个让人头疼的毛病模态混叠。同一个频率的成份被切成几段分到不同的IMF里或者不同频率的成份搅在一起让人分不清谁是谁。2009年前后EEMD的出现算是给了个缓解方案靠加白噪声来“扰动”极值点分布可代价是计算量大增而且重构出来的信号永远带着残留噪声不再是原来的信号了。CEEMDAN的全称是Complete Ensemble Empirical Mode Decomposition with Adaptive Noise中文一般叫完全自适应噪声集合经验模态分解。它的思路是在EEMD的噪声辅助框架上把噪声的加入方式改成“在每一层残差上自适应地加”并且做了全局包络平均既保留了EEMD抑制模态混叠的能力又解决了重构不完整的毛病。这篇文章写给两类人一类是被模态混叠折磨、想换一个更稳的分解工具做特征提取的另一类是已经在用EEMD、但受不了它“分解完信号变了样”的。读完你能直接跑通最小示例也能避开参数设置里那些常见的玄学坑。2. 从EMD到EEMD的演进模态混叠是怎么被压下去的2.1 EMD的核心假设包络、筛分与瞬时频率EMD的基本逻辑是把一个信号看成若干快速振荡分量叠加在一个缓慢变化的趋势上。算法本身没有预设基函数全靠信号自身的极值点来驱动。具体做法是找出信号的所有极大值点和极小值点用三次样条分别插值出上包络线和下包络线取两者的均值作为一条“平均线”然后用原始信号减去这条平均线得到一个去掉慢变成份的新信号。这个减的过程不是做一次就完了。新信号往往还有不对称的波形所以要重复“找极值、插值、算均值、做减法”这个循环直到满足两个条件一是极值点和过零点的数目相等或最多差1二是上下包络关于时间轴局部对称。满足条件后剩下的这一条信号就叫一个IMF。把IMF从原信号里剥掉对残差继续做同样的筛分一层层往下剥最终得到一个单调的残差项。这套流程本身很优雅但它有个致命问题如果原信号里有两个频率很接近的成份包络线拟合的时候会把它们当成同一个振荡或者在局部产生不规则的包络波动筛分出来的IMF就会一会儿是这个频率一会儿是另一个频率这就形成了模态混叠。我在处理轴承振动数据时经常看到这种情况同一个故障特征频率被拆到IMF1和IMF3里各出现一段特征能量被稀释得没法用。2.2 EEMD的噪声辅助思路为什么加白噪声反而有用EEMD的出发点很直接既然极值点分布不均匀会导致混叠那我就往信号里加入均匀分布的白噪声让噪声的极值点“填补”原始信号极值点稀疏的区域。这样一来不同尺度的信号成分在筛分时会被噪声扰动到各自合适的参考尺度上不易混在一起。每次加入不同的白噪声序列重复做多次EMD最后把所有结果按IMF序号做集合平均。因为白噪声的统计均值为零理论上平均足够多次后噪声的贡献会相互抵消。EEMD确实能在一定程度上抑制模态混叠但用过的人都知道它的代价一是计算量成倍增加几百次集合EMD跑下来处理长序列时等待时间让人烦躁二是重构信号无法完美还原因为集合平均只能消除部分白噪声残留的噪声能量会渗进每个IMF里导致分解结果失真三是每次加入的噪声幅值和集合次数都要人工调噪声太小起不到扰动作用噪声太大又把信号本身的极值点给淹没掉。更麻烦的是即使集合次数设得很大不同批次分解结果的IMF数量也可能不一致集合平均时只能截断这在统计上就不严谨。2.3 从EEMD到CEEMDAN噪声的角色从“扰动”变成“参考”CEEMDAN对EEMD的修正最关键的一点是改变了噪声的使用逻辑。EEMD是把白噪声加到原始信号上然后整体做EMDCEEMDAN则是在每一层分解后的残差信号上加入一个由EMD生成的白噪声IMF分量而且噪声的幅值乘以一个系数来适应当前残差的能量水平。我一般把这个系数设为0.2效果比较稳。这样做的好处有二第一每一层加噪声都在残差上进行噪声的能量随分解层数增加而自适应衰减不会从头到尾都用一个固定幅值第二CEEMDAN不是对IMFs做简单平均而是对每一层筛分过程中的包络均值做全局平均这样算出来的IMF天然逼近某个唯一的极限集合次数大了之后结果更稳定也不容易在平均过程中截断出错误的分量。换句话说EEMD的噪声是被动添加的辅助扰动而CEEMDAN的噪声更像一个参与迭代计算的参考信号算法本身在控制噪声的注入和消除。整套流程跑完得到的IMF集合相加起来能精确重构出原始信号这个性质在EEMD里是不存在的。3. CEEMDAN的两次平均自适应噪声与完备性重构3.1 第一次平均对首个残差加噪声做的是包络均值平均先看CEEMDAN的第一步。设原始信号为x(n)我们先生成I组白噪声序列对每一组都做一次EMD得到各自的第一个IMF分量。注意此时不做集合平均而是先计算每个IMF对应的上下包络均值然后对这I组包络均值做平均得到一条平均包络线。用原始信号减去这条平均包络线得到第一个IMF记作IMF1。这一步的精髓在于它避开了“对IMF直接平均”带来的模态错位问题——不同分解结果中序号相同的IMF物理含义未必对齐但对包络均值做平均因为包络本身是连续曲线对齐性远好于对离散振荡的IMF直接平均。我常跟人解释EEMD平均的是“成品”CEEMDAN平均的是“半成品”后者在统计上更稳健。接下来把这个IMF1从原始信号中减去得到第一个残差r1(n)。噪声在第一层的作用只是辅助产生包络均值最终算IMF时噪声并没有直接留在IMF里这就从机制上减少了残留噪声。3.2 第二次平均逐层残差加自适应噪声迭代出每个IMF从第二层开始CEEMDAN进入一个逐层迭代的过程。在得到残差r1(n)之后我们对r1(n)再加入白噪声序列但这里加的不是原始白噪声而是对白噪声先做一次EMD、取它的第一个IMF分量作为噪声模板。把这个模板乘以一个系数一般是0.1到0.3之间加到r1(n)上然后再对加噪后的信号做EMD提取出第一个IMF。对I组噪声模板都重复这个过程得到I个候选IMF再一次对这I个候选的包络均值做平均得到真正的IMF2。然后继续做残差减法得到r2(n)再重复“加自适应噪声模板、做EMD、平均包络均值、提取IMF”的循环。这里有个细节值得注意每一层加的噪声模板都来自对白噪声的EMD分解也就是说它本身是一条合法的IMF加到残差里后不会破坏残差的固有尺度结构。噪声幅值系数需要根据当前残差的标准差动态调整我一般用当前残差标准差乘以一个固定比例这样无论信号本身能量多大注入的噪声能量都是相对可控的。整个迭代一直做到残差无法再分解出满足条件的IMF为止最后剩下的残差就是趋势项。整个过程计算的IMF数量由信号本身的复杂程度决定不需要预先指定。3.3 完备性重构为什么CEEMDAN能比EEMD多保证一条性质完备性completeness是CEEMDAN相比EEMD最容易被忽略、但实际意义最大的改进。所谓完备性就是所有IMF加上最终残差能精确地还原出原始信号误差只来自数值计算精度。EEMD做不到这点因为每次EMD分解出的IMF个数不一样集合平均时只能取最小公倍数之类的对齐方式多出来的分量没法匹配重构时要么丢失能量要么叠加残留噪声。CEEMDAN则不同每一层的IMF都是从上一轮残差中减出来的数学上每一步都满足“残差 上一轮残差 - IMF”把所有层加起来中间的残差项全部抵消自然能精确回到原始信号。这个性质在做信号重构、去噪和趋势提取时特别有用。我做故障诊断时经常需要把分解后的高频IMF叠加起来重构出故障冲击信号EEMD重构出的信号辐值总是偏小还得做幅值修正CEEMDAN基本不需要这步。完备性也为后续的定量分析扫清了障碍。特性EMDEEMDCEEMDAN噪声辅助无加入原始信号逐层加入残差的自适应噪声平均对象无IMF直接平均包络均值平均模态混叠抑制弱中等强重构误差极小有残留噪声近似为零计算开销低很高介于两者之间参数敏感性较低高中等4. 用Python复现CEEMDAN最小代码与四个必调参数4.1 核心迭代代码逐层提取IMF的实现骨架这一小节给出一个能直接运行的CEEMDAN核心循环代码结构上做了简化用scipy的样条插值做包络拟合用标准EMD筛分流程作为内层函数再在外面套CEEMDAN的自适应噪声迭代框架。完整实现里还包含停止准则和残差判断这里为了讲解清楚把主干逻辑单独拎出来。import numpy as np from scipy.interpolate import CubicSpline def _emd_once(signal, max_iter10, tol1e-5): 标准EMD筛分一次返回第一个IMF和残差 imf_prev signal.copy() for _ in range(max_iter): # 找极值点 max_idx (np.diff(np.sign(np.diff(imf_prev))) 0).nonzero()[0] 1 min_idx (np.diff(np.sign(np.diff(imf_prev))) 0).nonzero()[0] 1 if len(max_idx) 2 or len(min_idx) 2: break # 三次样条包络 upper CubicSpline(max_idx, imf_prev[max_idx])(np.arange(len(imf_prev))) lower CubicSpline(min_idx, imf_prev[min_idx])(np.arange(len(imf_prev))) mean_env (upper lower) / 2.0 imf_new imf_prev - mean_env # 判断IMF条件包络均值是否足够接近零 if np.mean(np.abs(mean_env)) tol * np.std(signal): break imf_prev imf_new return imf_prev, signal - imf_prev def ceemdan_decompose(signal, n_ens100, nstd0.2, max_iter10): CEEMDAN主流程逐层提取IMF imfs [] residue signal.copy() n len(signal) while True: # 生成噪声模板白噪声的EMD第一个IMF noise_imfs [] for _ in range(n_ens): white_noise np.random.randn(n) * nstd * np.std(residue) # 对残差加自适应噪声做一次EMD得到候选IMF _, _ _emd_once(residue white_noise, max_iter) # 注意这里实际上需要计算加入噪声后信号的第一个IMF作为候选 # 简化版做法直接取white_noise的EMD第一个IMF做模板 noise_imf, _ _emd_once(white_noise, max_iter) noise_imfs.append(noise_imf) # 平均噪声模板并注入残差 avg_noise np.mean(noise_imfs, axis0) candidate, _ _emd_once(residue avg_noise, max_iter) imfs.append(candidate) residue residue - candidate # 残差极值点不足时停止 if np.sum(np.diff(np.sign(np.diff(residue))) ! 0) 4: break imfs.append(residue) return np.array(imfs)这段代码的核心逻辑分三层理解。第一层是_emd_once它完成单次EMD筛分返回一个IMF和对应的残差判断IMF的条件用的是包络均值逼近零的程度而不是严格的极值点与过零点数目相等因为后者在离散点上很容易出现振荡误判。第二层是噪声模板的生成CEEMDAN要求加白噪声EMD后的第一个IMF作为模板而不是加原始白噪声这段代码里用_emd_once(white_noise)来生成模板就是为了保证模板是一条合法的IMF。第三层是主循环中对残差迭代注入模板噪声每次都把残差减去新提取的IMF直到残差没有足够极值点为止。参数n_ens控制集合次数nstd控制噪声幅值比例max_iter控制每次EMD的最大筛分次数这三个参数就是CEEMDAN最核心的调整对象。4.2 四个必调参数集合次数、噪声幅值、筛分次数与停止阈值用CEEMDAN时很多人上来就默认一组参数跑完结果看分解结果不对劲接着就开始怀疑算法本身有问题。实际上CEEMDAN对参数不算特别敏感但这四个参数必须根据你的信号特征认真过一遍。第一个参数是集合次数n_ens也就是噪声模板重复生成的次数。常见做法是设50到200之间信号越长、信噪比越低需要的次数越多。我处理采样率12800Hz的轴承振动数据时分段长度4096个点设100次就能获得比较稳定的IMF集合如果设少了包络均值平均不充分分解结果里还会出现少量噪声残留的痕迹。第二个参数是噪声幅值比例nstd一般取0.1到0.3之间。它的含义是噪声模板的能量相对当前残差的标准差比例。设大了噪声扰动强抑制模态混叠的效果好但过大的话会把弱信号成分淹没设小了分解结果接近EMD模态混叠问题又回来了。第三个参数是最大筛分次数max_iter默认10到20为宜。筛分次数太少IMF不够平滑可能出现非对称波形太多会过度筛分把有效信号磨成类似正弦波的虚假分量。第四个参数是停止阈值tol判断包络均值是否足够接近零一般设1e-5到1e-6。这个阈值决定了分解的层数和精度设得太宽松会提前停止漏掉微弱成份设得太严格会增加计算时间而且末尾几层IMF容易变成纯数值噪声。我一般建议先固定n_ens100和nstd0.2跑通流程再根据IMF的物理可解释性回调这两个参数。4.3 从分解结果里得到的四个关键输出CEEMDAN跑完后输出是一个二维数组第一维是IMF序号第二维是时间序列值。使用结果时我一般关注四个关键输出。第一个是每个IMF的方差贡献率计算方式是每个IMF的能量除以原始信号总能量这个指标能快速判断哪个IMF承载了主要信号能量帮助定位主故障频率或主振荡模态。第二个是每个IMF的频谱特征对IMF做FFT可以得到中心频率和带宽用于对照已知的故障特征频率。第三个是残差趋势项它代表信号的整体缓变趋势在趋势提取场景中直接就是目标输出。第四个是各IMF的瞬时频率序列通过Hilbert变换计算相位导数得到这是做时频分析的基础。实际应用中把多个IMF的瞬时频率画在一张图上能看到信号频率随时间的变化轨迹比单纯看频谱图直观得多。5. 避坑与常见问题模态分裂、端点效应与参数失灵时怎么看5.1 模态分裂一个故障特征被拆到两层IMF里现象是诊断轴承外圈故障时故障特征频率的能量在IMF3和IMF4里各出现了一部分两个IMF的频谱图上都有特征频率的谱峰但峰高都偏矮直接拿来提取特征值会低估故障严重程度。原因是噪声幅值设得偏大导致相邻两个尺度的成份在筛分时被噪声“顶”开了边界变得模糊。解决办法是把nstd从0.2往回调到0.1左右同时适当增加n_ens到150让包络均值更稳定。如果调完仍然分裂考虑信号本身是否存在两个频率非常接近的成份这时分开分析两个IMF各自的包络谱比强行合并更合理。这个坑在齿轮箱振动信号里尤其常见啮合频率和边频带本来就靠得近分解容易把边频带拆到两个IMF里。5.2 端点效应信号两端飞出来的虚假振荡现象是分解结果的两端出现幅度很大的甩尾现象IMF在信号首尾位置的幅值远大于中间部分甚至出现“波浪形”的假包络。原因是三次样条插值时端点附近的极值点稀疏样条曲线在边界处不受控制地外延包络线在两端失真。解决办法主要有三种第一种是信号延拓常见做法是镜像延拓把信号两端按局部极值对称地延长一段长度做完分解后把延拓部分裁掉这是我在实际中最常用的方案第二种是给两端加窗对延拓后的信号加汉宁窗再做分解注意加窗只影响延拓部分需要拼回原始信号时做交叠过渡第三种是增大n_ens次数通过集合平均削弱端点处的不稳定性但这种做法只缓解不根治。我一般建议把信号延拓作为预处理写在分解函数外面不要指望CEEMDAN内部自动解决端点问题。5.3 分解层数过多或过少残差极值点的判断出了问题现象是同样的信号某次跑出来8层IMF加残差另一次只跑了5层就停了结果还不稳定。原因是停止阈值的判断依赖残差极值点数量而极值点检测在离散信号上容易出现“假极值”——相邻两个点的微小波动就被当成一个极值。解决办法是把极值点检测函数里的相邻差分条件改严格一些例如要求极值点两侧的差分符号相反且差值幅度大于某个阈值比如去掉小于信号标准差1%的微小波动。同时把tol从1e-5放宽到1e-4让筛分循环在出现数值噪声前尽早收敛这样分解层数会稳定很多。我遇到过一个极端案例原始信号含有明显的工频干扰50Hz正弦波在离散采样下极值点非常密集分解层数反复跳动最后通过在极值点判据里加最小间距限制相邻极值点至少间隔3个采样点才稳定下来。5.4 白噪声残留过多面平均后的IMF依然毛糙现象是分解后的IMF曲线不像EMD那样平滑带有明显的锯齿噪声。原因多数是n_ens次数不够包络均值平均不充分或者噪声模板生成时用原始白噪声而非白噪声的EMD第一个IMF导致模板本身携带高频粗糙成份。解决办法是检查代码中生成噪声模板的部分确认用的是_emd_once(white_noise)的IMF1作为模板而不是直接把np.random.randn生成的序列加到残差里。其次把n_ens提高到200再看效果。如果提高后仍然毛糙考虑信号本身的采样率是否过低低于1kHz采样时高频噪声本来就难以和真实信号分离。5.5 计算时间过长数组太长导致循环次数爆炸现象是处理几十万点的长信号时一次完整分解要跑十几分钟甚至更久。原因是CEEMDAN内层有集合循环和EMD筛分循环的嵌套每层残差都要对每组噪声做一次包络拟合时间复杂度接近O(n_ens × max_iter × 分解层数 × 信号长度)。解决办法是分段分解把信号按重叠窗口切成4096或8192点的段分段做CEEMDAN再对重叠区域的IMF做交叉淡化拼接。另一个巧办法是先做一次降采样粗分解得到各层的频率范围再用原始采样率对感兴趣的频率带做窄带CEEMDAN。计算量这件事没有魔法只能从信号长度和集合次数两头砍。6. 排列熵阈值与残差白噪声检验把CEEMDAN的结果量化成可用信号分解跑完之后最让人头疼的问题是如何判断哪些IMF是有物理意义的信号、哪些是纯噪声。CEEMDAN本身不会替你做这个决定但有两个工具配合起来效果很好排列熵和残差白噪声检验。排列熵的计算不依赖信号幅值只依赖时间序列中相邻样本的排序模式。对每个IMF计算排列熵时先做相空间重构设定嵌入维数m和延迟时间tau把每个点映射成m维排序模式统计不同模式出现的概率再按信息熵的公式计算。m一般取5到7tau取1到3。信号成分的排列熵值通常在0.5以下纯噪声的排列熵值接近0.9以上。把每个IMF的排列熵画成一条曲线能看到明显的分界点分界点之后的IMF基本可以判定为噪声主导。这个步骤能在没有先验知识的情况下自动帮你筛掉无效IMF省掉大量人工看频谱的时间。残差白噪声检验则是从另一个角度验证分解的完整性。具体做法是取最后一个残差序列计算它的自相关系数如果残差本质上接近白噪声那么除了零延迟之外的其他自相关系数都应该落在±1.96/√n的置信带内。如果残差中还有明显结构自相关系数会在某些延迟处显著越界说明分解层数不够需要继续往下分解。这个检验还有一个用法把去除噪声IMF后重构的信号与原始信号做差得到的残差序列做白噪声检验如果检验通过说明你的去噪过程没有把有效信号成分一起滤掉。我在做故障特征提取时习惯先跑这两步检验再用保留下来的IMF去做包络谱分析效果比直接拿全部IMF堆特征稳定得多。参数选择上的一个血泪经验是不要为了追求“完美的模态纯净度”把噪声幅值越调越大那样只会把分解结果往虚假模态方向带。我一般先用默认参数跑通再逐个调集合次数和噪声比例每次只改一个参数用排列熵曲线和残差白噪声检验两条标准来衡量变化基本两三轮就能锁定一组可靠参数。做CEEMDAN这件事算法本身的改进点已经写得很清楚了真正的功夫在于判断哪个IMF能用、哪个不能用这套“排列熵过滤 白噪声检验”的验证习惯帮我在不少项目里少走了弯路。希望帮到你。本文还有配套的精品资源点击获取