ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MATLAB EMD工具箱实战:从经验模态分解到HHT时频分析

MATLAB EMD工具箱实战:从经验模态分解到HHT时频分析

简介:Matlab EMD经验模态分解工具箱,面向信号处理、故障诊断与振动分析领域的研究人员与工程师,用于对非线性、非平稳信号做自适应分解并提取有物理意义的本征模态函数分量。压缩包共38个文件,其中10个m脚本承担核心算法与在线/局部分解入口,17个c文件实现底层快速计算,11个h头文件提供接口声明,整体仅55KB,轻量易部署。资源内置标准EMD、复数扩展EMD与C加速实现,可灵活处理实信号与复信号,适配科研实验、课程教学和工程诊断等场景。目前已有729人学习下载,适合需要快速搭建EMD分析环境、二次开发或集成到现有Matlab流程中的用户,是一份精简实用的信号分解工具集。

1. EMD工具箱:非平稳信号分析的入场券

matlab EMD工具箱解决的是经验模态分解的落地问题:一套能在 MATLAB 里直接跑通的函数集,把非平稳、非线性信号自适应地拆成若干个本征模函数(IMF)和一条残差。我之前帮某实验室处理设备启动瞬间的振动数据,前 0.5 秒信号直接做 FFT 根本看不出频率成分,换成 EMD 分解之后,特征频段一下子拆开了。这套资源适合三类人:刚接触经验模态分解、想跳过论文伪代码的在校学生,需要做 HHT 时频分析的信号处理工程师,以及手里攒了一堆实测波形、想低成本提取特征的分析人员。下面从原理开始,逐步讲到参数调整、批量处理和验证技巧。

2. 从原理到函数:EMD拆出了什么,怎么读懂IMF

2.1 IMF的定义与筛分循环:三次样条包络在做什么

EMD 的核心假设是:任意复杂信号都能分解为若干本征模函数。IMF 要满足两个条件:一是极值点数量与过零点数量相等或相差一个,二是上下包络的均值在任意点都接近零。第一条保证波形接近正弦的“局域对称”,第二条保证瞬时频率有意义。两个条件都是经验性的,这正是“经验模态分解”名字的由来。

以某跨平台系统的振动信号分析为例,我的理解是 EMD 本质上是一套自适应筛分循环。具体流程是:先找信号的局部极大值和极小值,用三次样条分别插值成上包络和下包络,算出包络均值;原信号减去均值后得到候选分量,判断它是否满足 IMF 条件,不满足就继续重复筛分,满足就把它抽出去,对残差继续执行同样的过程。整个过程像一层层剥洋葱,每一层都去掉一个相对高频的振荡成分,剩下的残差通常是单调趋势或直流偏置。

三次样条是 EMD 的默认插值方式,因为它的二阶光滑性能让包络线在连接处不产生突兀拐点。不过样条也带来两个副作用:一是计算量大,二是端点处的外推不受控。前者在长信号上会直接拖慢速度,后者会引发下文要说的端点效应。所以在理解原理的时候,把“包络生成——均值相减——迭代判定”当作一条主线就够了,细节参数先不用背。

选 EMD 而不是其它分解方法,主要看三点。FFT 只能把整段信号折算成全局频谱,频率随时间的变化会被平均掉;小波分解需要提前选基函数和分解层数,选错了结果就差很远;EMD 不需要预先设定基函数,完全按信号自身的极值分布来筛分。代价是计算结果对极值点的分布和插值方式敏感,同一段数据在不同参数或不同版本下可能筛出不同数量的 IMF。这是它的经验性决定的,不是 bug。

2.2 工具箱函数地图:emd 主函数与 hht 谱分析

我在项目里常用的是官方 Signal Processing Toolbox 自带的 emd 函数,它的标准调用方式是[imf, residual] = emd(x)。imf 输出是一个矩阵,每一列是一个 IMF 分量,列数由信号本身的复杂度决定;residual 返回最终残差。官方版本还支持 Name-Value 形式的可选参数,比如控制筛分终止条件、最大 IMF 数量、插值方式等,具体会在第 4 章展开。

配套的 hht 函数用于做 Hilbert-Huang 变换。它的输入是 imf 矩阵和采样率,输出是瞬时频率、瞬时幅值和 Hilbert 谱。很多人会把 EMD 和 HHT 混着叫,其实前者是做分解,后者是对各 IMF 做 Hilbert 变换并画时频图。拿到 imf 矩阵之后,先看每个分量波形是否规律,再用 hht 画谱,才能确认分解结果能不能用于特征提取。

除此之外,工具箱还带了自带绘图逻辑。比如 emd 在 Display 参数打开时会在命令行输出筛分迭代信息,配合tiledlayout可以自己拼一个 IMF 逐行排列的波形图。这个图几乎是验收分解效果的标配:看一眼 IMF 数量、每个分量的幅值量级、残差形态,基本就能判断分解是否可信。关于这套函数更完整的用法,包括各种返回值和绘图辅助,我在做完源码包的阅读笔记之后,整理了一张函数与用途对照表放在后面的章节里。

3. 安装与第一次分解:环境配置到读图全流程

3.1 版本选择与路径配置:别让老脚本坑了你

经验模态分解在 MATLAB 里的可用性经历了几个阶段。比较旧的第三方脚本是用纯 MATLAB 写的,函数名通常叫emd,参数顺序和官方版本不一样;后来官方在 Signal Processing Toolbox 里内置了 emd 和 hht,函数命名更规范,底层也做了优化。我手边用过的 2019、2020、2023 版本都自带这个函数,基本不用额外下载安装包。

如果你拿到的是第三方工具箱压缩包,常见做法是解压后把整个文件夹加入 MATLAB 搜索路径。路径配置只需要在 MATLAB 主界面执行addpath,我一般还会用savepath把路径持久化,不然重启后又要重新加。判断当前环境里用的是官方版本还是第三方版本,最直接的方法是which emd -all,找到结果列表里第一个 emd.m 的路径,看它在不在 toolbox 目录下。这样处理过的环境问题多了之后,我的习惯是把这一条检查写在每个脚本开头,注释掉也行。

3.2 第一次分解脚本:生成仿真信号并画出 IMF

这里直接用可复现的仿真信号来跑通流程。我选取了一个带调幅特征的目标信号,叠加一个低频正弦和一个线性趋势项,模拟真实测试台里“转子轻微不对中加基座松动”的典型组合:

%% 生成仿真信号:调幅分量 + 低频分量 + 线性趋势 fs = 1000; % 采样率 1000 Hz t = (0:fs*2-1)/fs; % 2 秒时间向量 x = (1 + 0.5*cos(2*pi*3*t)) .* cos(2*pi*50*t) ... % 50 Hz 载波受 3 Hz 调幅 + 0.3*sin(2*pi*8*t) ... % 8 Hz 低频正弦 + 0.1*t; % 线性趋势项 %% 执行 EMD 分解 [imf, residual] = emd(x, 'Display', 1); %% 逐行绘制各 IMF 与残差 figure tiledlayout(size(imf, 2) + 1, 1) for k = 1:size(imf, 2) nexttile plot(t, imf(:, k)) ylabel(sprintf('IMF%d', k)) end nexttile plot(t, residual) ylabel('残差') xlabel('时间/s')

这段代码把采样率设成 1000 Hz,总长 2 秒,第一个分量用调幅形式模拟齿轮啮合频率受转频调制的情况。emd返回的 imf 矩阵按列存放分量,所以循环里取的是imf(:, k)。

Display 参数打开后,命令行会输出每种停止条件对应的迭代次数,能直观看到筛分过程收敛情况。通常第一个 IMF 对应最高频成分,往后频率递减,残差保留趋势。如果某个 IMF 的幅值明显偏离原始信号量级,或者残差里还残留振荡成分,说明信号本身太复杂或参数需要调整。

3.3 读图判成败:数量、残差与端点形态

分解完成后不要急着做下一步,先用眼睛扫三样东西:IMF 数量是否合理、残差是不是平滑的单调趋势、每个分量的端点是否出现发散。以这段仿真信号为例,理论上有三个可辨识成分,但 IMF 数量不一定正好等于三,因为调幅分量的包络变化可能被拆成两个模态。

残差形态是最直观的判据。正常情况下残差要么是直线,要么是低阶多项式形状,如果残差还带着明显周期性振荡,说明高频分量没有被充分筛干净。端点发散也很好认,波形两端会出现幅度突然放大或凹进去的“蝴蝶翅膀”形状。遇到这种情况,先检查是不是插值方式选错,再去考虑端点效应处理。

另一个容易误判的地方是幅值量级。原始信号峰峰值大概在 1.5 左右,分解出的 IMF 幅值如果动辄超过这个范围,那多半是出现虚假分量了。我一般会用max(abs(imf))批量看一眼每个分量的最大幅值,量级相差 10 倍以上的分量基本可以忽略。读图这一步看似基础,却是整个 EMD 流程里最省时间的一步,能提前发现 80% 的异常分解。

4. 参数调整与批量处理:让EMD在业务数据上落地

4.1 五个核心参数的默认值与调参方向

官方 emd 函数提供了几个常用参数,默认值在设计上是通用场景的折中。实际项目里我会根据信号特点做针对性调整,下表是整理后的参数说明:

参数名默认值作用我的调参习惯
MaxNumIMF自动限制最大 IMF 数量,防止筛出过多噪声分量知道先验成分数时设为比期望数多 1~2
SiftRelativeTolerance0.2筛分过程新建 IMF 的停止容差信号噪声大时降到 0.1
SiftMaxIterations100单次筛分最大迭代次数复杂振动信号调到 300
SiftMaxNumExtrema1残差极值点数不大于此值时停止保持默认
Interpolationspline包络插值方式,可选 pchip波形有异常尖峰时换 pchip

SiftRelativeTolerance 是最值得调的参数。它控制筛分过程中相邻两次包络均值的变化幅度,值越小筛分越严格,IMF 越接近理想定义,但会显著增加迭代次数和计算时间。某图像处理 Demo 的采集信号里混着明显的白噪声,默认 0.2 会把噪声当成分量筛进 IMF 的低频段,降到 0.1 之后噪声被更多地留在残差里。

MaxNumIMF 在信号成分数量已知时很有用。比如振动信号里已知是转频、倍频加调制边带,一共 6 个特征分量,把 MaxNumIMF 设为 8,能避免算法溢出多余的噪声分量。Interpolation 只在波形有明显冲击时切换成 pchip,这种插值不会像三次样条那样产生大幅度过冲,代价是包络光滑度稍差,筛出的 IMF 波形偏锐。这几个参数之间会互相影响,不建议一次只调一个,最好按“先定 MaxNumIMF,再调 SiftRelativeTolerance,最后换 Interpolation”的顺序来。

4.2 从 IMF 到时频图:hht 的两行调用

分解完成后,下一步通常是做 HHT 谱分析,也就是把每个 IMF 做 Hilbert 变换求瞬时频率和瞬时幅值。官方 hht 函数可以一步到位:

%% 由 IMF 计算希尔伯特谱与时频谱 [hs, f, t_hht, imf_env, imf_freq] = hht(imf, fs, 'FrequencyLimits', [0 100]); %% 绘制 Hilbert 谱热力图 figure imagesc(t_hht, f, hs) axis xy xlabel('时间/s') ylabel('频率/Hz') colormap parula colorbar

这里 hht 的输入是 imf 矩阵和采样率 fs。FrequencyLimits 用来限制输出的频率范围,因为 Hilbert 变换在端点会产生虚假频率分量,限制输出范围可以把两端的高频噪声挡在视野外。输出里 hs 是谱密度矩阵,f 是频率轴,t_hht 是时间轴;imf_env 和 imf_freq 分别是瞬时幅值包络和瞬时频率矩阵。

在多分量信号里,hht 谱上能直接看到频率随时间的波动。如果某条频率线在中途发生断裂或明显跳变,通常是模态混叠或端点效应引起的,这时要回上一章去调分解参数。还有个常见坑是画图时颜色范围被个别极大值拉满,导致微弱分量看不清,可以手动设置caxis的下限,比如caxis([0 0.5*max(hs(:))])。

4.3 长信号的滑窗拆分与结果拼接

实际采集的信号动辄几十万点,直接喂给 emd 会算得很慢。我一般会先用滑窗把长信号分成多段,每段单独分解,再拼接起来分析。窗口长度取信号中最低特征频率的 5~10 倍周期,比如关注频率是 10 Hz,采样率 1000 Hz,窗口至少取 0.5 秒,取 1 秒更稳。

%% 滑窗分解长信号 winLen = 1024; % 窗口长度 hopLen = 512; % 窗口步长 nWin = floor((length(x) - winLen) / hopLen) + 1; imfCell = cell(nWin, 1); resCell = cell(nWin, 1); for idx = 1:nWin seg = x((idx-1)*hopLen + 1 : (idx-1)*hopLen + winLen); [imfCell{idx}, resCell{idx}] = emd(seg); end

窗口重叠设置为长信号信号分析里的常见做法是重叠 50%,保证边界处不会漏掉关键事件。每个窗口分解出的 imf 矩阵列数不一定相同,所以用 cell 数组保存。拼接前先检查各窗口的列数分布,取众数作为统一分量数,再对齐叠加。因为 EMD 的自适应特性,相邻窗口的同一个物理分量可能被排到不同序号上,直接按列拼接反而会串位,最常见的处理是按频率高低重排 IMF,再对齐做后续统计。

批量处理里最耗时的是循环体内的重复包络插值,如果每段窗口都很长,建议先把采样率降下来。降采样前用resample而不是decimate,避免混叠。分段之后还能用parfor并行跑,但每个工作进程都要有一份 imf 函数的访问权限,路径配置要在 parpool 启动前完成。

5. 避坑与排查:端点效应、模态混叠与版本差异

5.1 端点效应:两端发散,特征频率被拉低

分解结果在信号两端出现明显上翘或凹陷,中间部分的波形正常,端点处的 IMF 幅度远超信号本身幅值。这是 EMD 里最常见的现象,我一开始以为是参数没调好,反复调整 SiftRelativeTolerance 也没用。

原因在于三次样条包络在端点只有单侧有数据,外推曲线自由度大,包络均值在端部严重偏离真实值。这个误差会随着筛分迭代被逐级放大,最终表现为两端发散。

解决思路有两类。一类是延拓法,在分解前把信号首尾做镜像延拓,分解后裁掉延拓段;另一类是修正法,改用 pchip 插值降低过冲,或者直接丢弃两端各 5% 的采样点。我现在的习惯是:先判定关注特征是不是在信号中部,如果在中间,就直接裁剪端点;如果全段都要保留,就做镜像延拓。用官方的wextend函数做对称延拓,再送进 emd,比手工拼段更可控。

5.2 模态混叠:一个 IMF 里混进了间歇高频

某个低频频段的 IMF 波形在一个时间点突然叠加上一段高频振荡,波形看起来像被“刺”了一下,表现为单个 IMF 内既有趋势又有瞬态冲击。

原因是 EMD 筛分依赖极值点分布,间歇性出现的强冲击会局部改变极值分布,让原本属于高频的成分析出到低频箱中。这个问题的底层逻辑是包络无法区分“混合在同一时段的不同频率事件”,本质上是由极值点驱动的自适应筛分对间歇信号的天然敏感。

解决方向有两个层面。第一个是加白噪声辅助的总体平均思路,把多个含噪副本的分解结果做平均,让间歇频率在各副本间被统计平均掉,这就是 EEMD/CEEMDAN 的基本逻辑;第二个是预滤波,把明显已知的频段先用带通滤波器切开,再对各段做 EMD。我用过一段时间的 CEEMDAN 库,发现抑制混叠效果好不少,但计算量变大会使参数迭代更耗时。

5.3 大内存与慢循环:批量分解的提速方案

处理 60 万点的长信号时,单个 emd 调用跑了将近十分钟,内存占用也一路飙到好几 GB,任务管理器里看到内存直冲,程序直接卡到无响应。

原因有两点:一是 emd 内部每一轮筛分都要对整段信号做三次样条插值,迭代次数一多,耗时成倍增长;二是 imf 矩阵提前分配的空间和实际筛出的分量数不一致,返回时可能发生重新复制和拼接,内存开销更大。

解决手段按见效顺序排序:先降采样,把采样率降到特征频率的 5 倍左右即可;再分段并行,按第 4.3 节的方式切窗并用parfor跑;最后限制迭代次数,把SiftMaxIterations从默认 100 降到 50,MaxNumIMF设一个明确的数。最有效的是分段,因为 EMD 的计算复杂度接近 O(N^2),每段缩短一半,总时间就能降到原来的四分之一以下。从那以后我给所有批量处理都加了“先分段、再并行、后调参”的固定顺序。

5.4 官方版本与第三方库:函数签名和结果都有差异

同一份脚本在一台机器上正常跑,换到另一台机器就报参数错误,或者跑出来的 IMF 数量明显不对。

原因是不同来源的 emd 实现差异很大。官方 Signal Processing Toolbox 的函数签名是按 Name-Value 方式传参,比如'MaxNumIMF';而一些第三方工具箱用位置参数,有的还规定第一个输出是 cell 数组而非矩阵。更隐蔽的是插值逻辑不同,第三方库有的用线性插值做包络,有的默认不做端点延拓,结果自然对不上。

解决方法是先在脚本开头做环境自检:用which emd查看路径,确认是官方函数还是第三方脚本;再用一段固定的基准信号跑一次分解,把期望的 IMF 数量作为阈值判断是否兼容。切换环境时不要只复制代码,一定要把工具箱目录或版本说明一起带走,否则复现结果会凭空消耗大量精力去对比差异。这套排查流程在读者下载到的 EMD 工具箱里同样适用,装好之后先用自带示例跑一遍基准测试,再上真实数据比较靠谱。

6. 验证IMF质量的三个技巧:相关性、正交性与瞬时频率检查

6.1 相关性检查:IMF 与原始信号

分解完之后,我先算每个 IMF 与原始信号的皮尔逊相关系数。物理意义在于,真实分量应该与原始信号共变,相关系数太低大概率就是虚假振荡。阈值上,振动信号里我一般取大于 0.3 才算有效分量,低于这个数的可以直接剔除或作为噪声处理。这一步整体上非常快,却能避免把无效模态当特征送进分类器。

6.2 正交性检查:一个数值衡量分解质量

第二个技巧是计算 IMF 之间的正交性指标。EMD 假设各分量彼此正交,但实际分解总会有少量泄漏,所以用归一化交叉积来衡量“泄漏”程度。我写一段简短验证代码:

%% 计算 IMF 之间的正交性指标 ioSum = 0; for m = 1:size(imf, 2) for n = 1:size(imf, 2) if m ~= n ioSum = ioSum + abs(sum(imf(:, m) .* imf(:, n))); end end end ioIndex = ioSum / sum(x.^2);

这个指标越接近 0 表示分量间越正交,分解越可信。我在某项目里看到过正交性指标超过 0.5 的情况,当时立即回去查,发现是信号同时存在两个相近频率分量,EMD 把它们拆成了四五个交叠模态。这时就改用 CEEMDAN 思路重新分解,指标立刻降到 0.15 以下。

6.3 瞬时频率曲线:看边界和跳变

最后一个检查是画每个 IMF 的瞬时频率曲线。瞬时频率本身应该平滑变化,如果某一段曲线突然跳到高频又跳回,说明该分量内部混叠或端部失真。我会把瞬时频率跟原始信号的关键事件叠加对比,确认每条曲线都能对应到物理意义。

从那以后,我每次 EMD 跑完都强制走一遍这三项验证,不超过十分钟,能筛掉九成以上的无效分解。希望这套流程对你有用,也期待你拿到工具箱之后先跑通仿真信号,再上真实数据,踩过的坑会少很多。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进