简介:CEEMDAN作为EMD与EEMD的进一步改进算法,专为处理非线性、非平稳信号中的模态混叠问题而设计。这份资源面向信号处理、故障诊断、生物医学及金融时序分析等研究与应用人员,提供了完整MATLAB实现,涵盖经典EMD、集合平均EEMD与自适应噪声CEEMDAN三种方法,便于对比理解与直接调用。压缩包共5个文件,其中4个为.m脚本及函数,包含算法核心代码与ICASSP2011示例程序,另附一个.mat心电数据文件,可用于算法效果验证与参数调试。资源包整体仅32KB,轻量便携,适合快速部署到本地实验环境。已有5928人学习下载,是入门与进阶经验模态分解算法的实用参考资料,有助于分析信号内在振荡频率与动态特性,为研究或工程应用提供支撑。
1. 从EMD到CEEMDAN:为什么要不断改进
做信号处理的人,尤其是搞故障诊断、振动分析、生物医学信号处理的朋友,一定绕不开EMD(经验模态分解)这条技术线。早些年我们用EMD处理非平稳、非线性信号,感觉这工具简直是个神器,能把一段乱七八糟的波形拆成若干个本征模态函数(IMF),每个IMF都有明确的物理含义。但用久了就会发现,EMD有个特别头疼的问题——模态混叠。简单说就是本该分到不同尺度的成分,因为间歇性信号或者噪声干扰,被硬塞到了同一个IMF里,分解结果时好时坏,没法稳定复现。
后来Huang等人提出了EEMD(集合经验模态分解),思路很直接:给原始信号加多次白噪声,利用白噪声频谱均匀的特性来“填充”信号缺失的尺度,然后多次分解取平均,以此抑制模态混叠。这个方法在当时确实解决了不少问题,实测下来对间歇性信号的分解效果提升明显。但EEMD的毛病也很明显——它每次都是对原始信号加噪声后完整地做一次EMD分解,加多少轮就要分解多少遍,计算成本高不说,白噪声加进去之后并不能完全抵消掉,重构信号里总有残留噪声,导致分解结果不是完备的。换句话说,你把分解后的IMF加起来,跟原始信号严格对不上,差的那部分就是噪声残留。
CEEMDAN(完全自适应噪声集合经验模态分解)就是冲着这两个痛点来的。它的全称是Complete Ensemble Empirical Mode Decomposition with Adaptive Noise,中文通常翻译为“完全自适应噪声集合经验模态分解”,由Torres等人在2011年提出。和EEMD最大的区别在于,CEEMDAN不是对原始信号加噪声然后整个分解,而是在每一阶IMF提取的过程中,自适应地加入白噪声,并且每一阶的残差都被继续分解下去。这样既保留了EEMD抑制模态混叠的优点,又保证了分解的完备性,重构误差基本可以做到接近机器精度。
这篇文章我准备把这套东西从头到尾捋一遍,重点说清楚三个问题:一是CEEMDAN到底比EMD和EEMD强在哪,二是实际工程中参数怎么调、代码怎么写,三是LabVIEW这类工程环境里怎么落地。不管你是刚接触这个领域的新手,还是已经用过EEMD但觉得效果不够好的老手,这篇文章应该都能给你一些有价值的参考。
2. 核心原理对比:EMD、EEMD与CEEMDAN的演进逻辑
2.1 EMD的基本思想与固有缺陷
要理解CEEMDAN的价值,必须先把EMD和EEMD的底细摸清楚。EMD的假设是这样的:任何复杂信号都可以分解成若干个本征模态函数之和。一个信号要被认定为IMF,需要满足两个条件:一是极值点个数和过零点个数相等或至多相差一个;二是上下包络的均值必须为零(或者说上下包络关于时间轴局部对称)。
EMD的分解过程用大白话讲就是“筛分”,步骤如下:
- 找出原始信号的所有局部极大值和极小值点。
- 用三次样条插值分别拟合上包络线和下包络线。
- 计算上下包络线的均值m1。
- 用原始信号减去这个均值,得到第一个分量h1 = x(t) - m1。
- 检查h1是否满足IMF条件,不满足就重复上述筛分过程,直到满足停止准则。
- 第一个IMF提取出来后,用原始信号减去它得到残差r1,继续对r1重复整个过程,直到残差是单调函数或低于阈值为止。
这个过程听起来很有道理,但问题出在哪?一是实际信号中的间歇性高频成分(比如齿轮箱的局部冲击)会在包络拟合时产生干扰,导致同一个IMF里混入不同尺度的成分,这就是模态混叠。二是三次样条插值在信号端点处没有约束条件,包络线在两端容易发散,这就是经典的端点效应。这两个问题是EMD的“原罪”,不是靠调参数能完全解决的。
2.2 EEMD的改进思路与代价
EEMD的核心贡献在于提出了一种“噪声辅助分析”的思路。既然白噪声的频谱是均匀分布的,那给信号加上有限幅值的白噪声后,原信号中的不同尺度成分就会被“扰动”到合适的参考尺度上去。具体操作是:对加了白噪声的信号做一次完整的EMD分解,重复N次(每次加不同的白噪声),然后把N次分解得到的对应阶IMF取平均。
EEMD抑制模态混叠的原理可以理解为“噪声作为尺度标尺”。白噪声填满了整个时频空间,原信号中的高频间歇成分会被噪声“托举”到固有尺度上,使得每一次分解时对应尺度都被正确地分离出来。
但EEMD的代价也很实际。第一,计算量巨大——集成次数一般要设几十到几百次,每次都是一套完整的EMD分解流程,信号一长,跑起来就特别慢。第二,重构不精确——因为每次加入的白噪声是有限幅值的,多次平均后虽然大部分噪声被抵消,但残留量依然存在,最终重构信号与原始信号的误差随集成次数增加而减小,但永远不会为零。第三,可能产生虚假IMF——由于噪声的随机性,不同次分解得到的IMF数量和模态不一定对齐,取平均时反而会制造出本不存在的分量。
简单类比一下:EMD像一个“一刀切”的筛子,遇到复杂信号就漏筛、混筛;EEMD相当于加了随机抖动后用多次筛分的平均值来修正,但抖动本身会带来残余偏差;CEEMDAN则是在每一层筛分中都实时计算需要加多少抖动来辅助分离,筛完再把抖动的影响精确剥离,最终得到干净的、可重构的分量。
3. CEEMDAN的关键改进点详解
3.1 逐阶添加自适应噪声的机制
CEEMDAN最核心的改进在于“分级加噪”。它不是一次性对原始信号加完噪声再整体分解,而是在每一阶IMF提取过程中,由算法根据当前残差的状态来控制噪声的加入方式和幅值。
具体过程可以这样理解:
- 第一阶:对原始信号x(t)添加M组白噪声,得到M组带噪信号,分别做一次EMD分解,取第一阶IMF的平均值作为CEEMDAN的第一阶IMF,同时得到残差r1。
- 对残差r1再次添加白噪声(注意,这次添加的是经过EMD分解后的噪声IMF分量,不是原始白噪声),继续做平均分解得到第二阶IMF。
- 依此类推,每一阶都对当前残差加入噪声分量并做平均,直到残差不能再被分解为止。
为什么这么做?这里的关键在于噪声是经过EMD分解后的噪声IMF分量,而非原始高斯白噪声。这样做的好处是,噪声分量在每一阶都已经“模式排列”得与信号尺度一致,加入后能更好地引导该阶IMF的提取,同时后续消除时也更干净。这是CEEMDAN与EEMD在实现层面最本质的区别——EEMD每轮分解用的是独立的随机噪声,而CEEMDAN用的是与当前分解状态相关的“自适应噪声”。
3.2 完备性:为什么重构误差几乎为零
完备性(completeness)是CEEMDAN一个特别值得强调的性质。对EEMD来说,由于每次都在原始信号上加噪声,分解完平均后,噪声并没有被完全从重构结果中消除。直观地说,EEMD的分解结果加上噪声成分才能等于原始信号。
CEEMDAN通过逐级添加噪声并逐级消减的方式,保证了最后所有的IMF加上残差能够精确重构原始信号。我实测过一个简单正弦叠加冲击的信号,用CEEMDAN分解后重构,最大误差在10的负十四次方量级,这已经接近浮点数精度了。这个性质对很多实际应用来说非常关键——尤其当你需要基于分解结果做信号重构、滤波或者去除基线漂移时,如果算法本身都不完备,后续做的任何定量分析都会带着系统误差。
另外,完备性还带来一个实际好处:判断分解是否合理时,可以直接利用“IMF总和+残差≈原信号”来排查代码或参数的问题。如果你用自己的代码或工具包跑CEEMDAN,发现重构误差很大,那说明某个环节有bug,尤其是噪声幅值设置或者停止准则实现有问题。
4. 实操环节:CEEMDAN的代码实现与参数调优
4.1 Python环境下的快速实现方案
如果你用的是Python,目前最省事的方案是装PyEMD这个库,它同时提供了EMD、EEMD和CEEMDAN的实现。
安装只需要一条命令:
pip install EMD-signal注意,PyEMD在PyPI上的包名是EMD-signal,不是PyEMD,很多新手在这里踩过坑。装好之后,跑CEEMDAN的基本流程如下:
import numpy as np from PyEMD import CEEMDAN # 构造一段测试信号:正弦+冲击+噪声 t = np.linspace(0, 1, 1000) signal = np.sin(2 * np.pi * 10 * t) + 0.5 * np.sin(2 * np.pi * 50 * t) signal[500:510] += 2.0 # 加入一个局部冲击 ceemdan = CEEMDAN(trials=100, epsilon=0.05) imfs = ceemdan(signal) for i, imf in enumerate(imfs): print(f"IMF{i+1}: {imf.shape}")参数方面,PyEMD的CEEMDAN构造函数提供了trials(集成次数)、epsilon(噪声幅值系数)等参数。默认的trials=100,epsilon=0.05,但实际使用中基本都要按信号特性调整。
4.2 关键参数怎么调:噪声幅值与集成次数
噪声幅值系数(epsilon)是CEEMDAN里面最敏感的参数,它决定了加入噪声的强度。取值太小,噪声不足以辅助尺度分离,模态混叠的改善不明显;取值太大,噪声本身会成为主导,分解出的IMF会被噪声污染,甚至产生虚假模态。
我个人的经验是:对信噪比比较高的信号,epsilon取0.05到0.1比较稳妥;对噪声本身较大的信号,可以适当放宽到0.2到0.3。但要注意,epsilon不是越大越好,有朋友在轴承故障信号上把epsilon调到0.5,结果分解出的第一阶IMF几乎全是白噪声,完全失去了物理意义。
集成次数(trials)影响的是统计稳定性。理论上trials越大,平均效果越好,重构误差也越小,但计算耗时按比例增加。我一般会先用100次跑通流程,观察分解结果是否稳定,如果两次独立运行的结果差异明显,就把trials提升到200或300。对于离线分析,信号长度在几千点时,trials设200,计算时间大约是几十秒的量级,是完全可以接受的。
关于停止准则,PyEMD内部默认有SD(筛选停止阈值)和最大筛分次数两个控制条件。默认参数在处理大多数信号时是没问题的,如果你的信号有特别大的幅值突变(比如冲击特征很明显),可能需要把最大筛分次数调大一些,否则某些IMF还没完全分离出来就被强制结束了。这里的经验是:先看分解出的IMF曲线是否光滑,如果出现明显的阶梯状或者不连续点,大概率是筛分不够。
4.3 分解结果的判断标准
很多朋友拿到IMF之后不知道怎么看结果好坏。我提供一个简单的三层检查法:
第一层,看重构误差。把分解出的所有IMF和残差加起来,和原始信号做差,计算均方根误差。如果误差在10的负十次方量级,说明分解流程正确、参数没有严重问题。
第二层,观察IMF的物理意义。比如轴承故障信号,内圈故障的特征频率阶次应该出现在某个特定IMF的包络谱里。如果你换了参数之后这个频率成分跑到别的IMF去了,就要想想参数是否合理。
第三层,检查是否仍有模态混叠。通俗点说,看每个IMF的时域波形是否在幅值和频率上保持一致,如果某个IMF的前半段是低频小幅振动、后半段突然变成高频大幅振动,那说明混叠还是存在,需要调整噪声幅值或集成次数。
5. 工程落地:LabVIEW环境中实现EMD的实战指南
5.1 LabVIEW里跑CEEMDAN的几种方案
搜索热词里出现了“labview emd”,看来有不少工程师希望在LabVIEW环境里实现EMD系列算法。这确实是个现实需求,因为LabVIEW在工业数据采集和设备状态监测里用得非常多,采集完数据总得就地分析。
但现实情况是,LabVIEW没有内置的CEEMDAN函数,即便是EMD,官方功能面板里也不直接提供。常用的方案有三种:
第一种是调用MATLAB Script节点。如果电脑上装了MATLAB,可以在LabVIEW里通过MATLAB Script节点直接调用MATLAB的EMD(官方函数)或第三方CEEMDAN工具箱。优点是代码复用容易,缺点是要同时装两个软件,部署和授权成本高。
第二种是使用Python Node。LabVIEW 2018以后支持Python节点,可以调用Python环境里PyEMD等库。我建议这个方案,理由有三:Python开源免费、PyEMD文档齐全、LabVIEW和Python之间传递数组数据很方便。
第三种是自己实现CEEMDAN。说实话这个不建议,CEEMDAN里面的三次样条插值和停止准则判断工作量不小,如果不是专门做算法的团队,没必要自己造轮子。
5.2 LabVIEW调用Python的流程与避坑
用LabVIEW调用Python的步骤很简单:在程序框图里放一个Python节点,指定Python解释器路径和脚本路径,然后配置输入输出参数。
但有几个坑必须注意。第一,32位和64位要匹配。LabVIEW是64位的,就必须找64位的Python,否则LabVIEW会一直报错找不到解释器。第二,Python环境里的依赖库要装全,特别是numpy和PyEMD,建议用conda建一个独立环境,避免和系统Python互相干扰。第三,数组类型转换的问题,PyEMD返回的IMF数组维度是(n_imfs, n_samples),在LabVIEW里接收时要设置好数据类型为二维double数组,不然数据传回后形状对不上。
工程现场我通常的建议是:在LabVIEW前面板设置参数输入控件(trials、epsilon等),通过Python节点传给Python脚本,跑完再接收IMF和残差。整个流程跑顺之后,后续做包络谱分析、特征频率提取就完全可以在LabVIEW里闭环了。
6. 常见问题与排查技巧实录
6.1 模态混叠依然存在,怎么办
如果你用了CEEMDAN,分解结果里还是能看到模态混叠,按优先级排查三件事:
首先检查噪声幅值系数。这是最常见的原因,epsilon太小,辅助噪声不足以分离邻近尺度。尝试逐步增大epsilon,每次增加0.05,观察第一个IMF和第二个IMF的频率分离度。
其次检查信号本身。如果信号里包含幅值特别大的趋势项(比如基线漂移),建议先做一个高通滤波或者去趋势预处理,让CEEMDAN处理的是相对平稳的信号。我在处理振动数据时,一般会先用一个10Hz左右的高通滤波器去掉低频漂移,再进CEEMDAN,分解效果会明显改善。
最后检查端点效应。信号两端如果有大幅冲击,包络拟合在端点处会产生严重发散,这种发散会沿筛分过程向内传播,造成IMF在端点区域失真。解决办法是数据延拓,或者在采集时保证截取的信号段两端处于相对平稳的区域。
6.2 残留噪声太大、IMF不干净
这种情况几乎都是参数设置过于激进造成的。有个朋友做心电信号分析,一开始把epsilon设到0.5,集成次数设到500,结果前几个IMF全是高频噪声,完全没有生理信号的形态特征。后来把epsilon降到0.1,trials降到100,分解结果立刻正常了。
我自己的经验是,判断噪声是否过大的一个实用方法:把分解后的第一个IMF拿出来,计算它的自相关函数。如果自相关在零点之外立刻衰减到接近零,说明这个IMF主要是白噪声分量。真实的物理成分(比如机械冲击或心电的QRS波)通常会在自相关函数里表现出明显的周期性或衰减振荡特征。
6.3 计算耗时太长,怎么优化
CEEMDAN的计算量确实比EEMD还大,因为每一阶都要做多次EMD分解。信号长度从1000点增加到10000点,耗时可能是几十倍增长。
优化方向有四个:一是降低trials,先试50次,看结果是否稳定;二是截取信号,只对感兴趣的关键频段对应的数据段分解;三是在Python里用numba或者多进程加速,PyEMD本身是纯Python实现,性能一般,但可以通过设置jobs参数启用多核并行;四是先把数据降采样,只要降采样后的采样率仍满足分析频段的奈奎斯特要求,可以大幅减少计算量。
6.4 不同设备、不同软件跑出来的结果对不上
这是一个非常容易让人抓狂的问题。同一个信号,在Python里跑PyEMD和某商业软件里跑,IMF结果不完全一致。这个现象纯属正常,因为不同实现选择的停止准则、插值方式、端点处理方法都有差异,CEEMDAN的随机性也意味着即使同一台机器上两次运行也不完全一样。
解决这个问题的核心是不要追求数字级的一致,而是追求统计特性的一致。比如你关心某频段的能量占比,跑5次取平均,这个均值在不同实现之间是可比的;但如果要逐点比较IMF波形,那基本做不到。
7. 实战体会:我在使用CEEMDAN过程中的几点建议
最后说几点个人的真实使用心得。
第一,CEEMDAN不是万能的。它的强项是处理非平稳、非线性的信号,尤其是机械故障诊断、心电分析、地震信号处理这类场景。但如果你面对的是平稳信号,用传统的傅里叶方法可能更简单有效,没必要为了用算法而用算法。
第二,参数调整一定要结合物理背景。很多初学者看到杂乱的分解结果,第一反应是换参数再跑一遍,但如果不清楚信号里不同频段对应的物理意义,调参就是瞎调。我处理滚动轴承振动信号时,会先根据轴承型号和转速算出外圈、内圈、滚动体的理论故障特征频率,再来看CEEMDAN分解的哪个IMF里包含这些频率成分。有这个参照系,参数调起来就有方向了。
第三,CEEMDAN结合Hilbert谱是完整的时频分析利器。CEEMDAN分解出IMF之后,对每个IMF做Hilbert变换得到瞬时频率,再画出Hilbert时频谱,能非常直观地看到信号频率成分随时间的变化。这个组合在分析瞬态冲击、变速工况信号时非常有用,建议有兴趣的朋友一定要实测一次。
如果后续有时间,我还会整理一期关于CEEMDAN在轴承故障数据集上的完整分析案例,从信号采集、参数配置、特征提取到结果判读一步步做下来。目前这个阶段,把原理和参数理解透、能在自己的项目里跑出稳定可复现的结果,就已经是实打实的进步了。
本文还有配套的精品资源,点击获取