1. 项目解读:为什么是SVD+VMD这对组合
我最早接触这组方法,是因为一个实际项目里要处理陀螺仪输出的漂移信号。那组信号在有用信息之外混着基线漂移、高频抖动和偶发脉冲,用单一手段怎么降噪都顾此失彼——FIR滤波会把有用尖峰削平,小波阈值去噪在阈值选择上又太主观,总是需要反复试参。后来把奇异值分解(SVD)和变分模态分解(VMD)串成一条流水线,才算是真正把问题理顺了。
标题里两个关键词,一个是SVD,一个是VMD。SVD处理的基础是矩阵,但我们要降噪的是一维时间序列,所以中间有一道关键转换:把一维信号构造成Hankel矩阵(也叫轨迹矩阵),再对这个矩阵做奇异值分解,按奇异值大小把信号和噪声分开。VMD则是把信号自适应地分解成若干个有限带宽的模态分量(IMF),每个模态围绕一个中心频率,噪声会被打散到各个模态里。
这两个方法单独用,各有短板。SVD对信号的整体能量聚集特性非常敏感,如果信号本身是非平稳的、频率成分复杂,单纯靠奇异值截断容易把有用的频率成分当成噪声删掉;VMD虽然能做自适应频带划分,但分解出来的模态里依然残存噪声,尤其是与信号频带重叠的那部分噪声,模态层面没法彻底剔除。把SVD和VMD级联起来,正好互补:VMD先按频带把信号大卸八块,SVD再在每个频带内部按能量来一次精细提纯。这个思路,对工程实测信号非常友好,也是这套方法能跑通的核心逻辑。
先说清楚这套方法适合处理什么信号。工程里常见的传感器输出、振动信号、心电信号、声发射信号、结构响应信号,只要是一维的、时间等间隔采样的、噪声和有用信号频带有重叠的,都可以套这个框架来处理。它不适合什么场景?如果信号本身是纯随机噪声、没有结构性能量聚积,SVD和VMD都无从下手;如果数据量特别大、实时性要求极高,SVD的矩阵分解计算量可能比较吃力,需要在效率和精度之间做取舍。
运行环境是MATLAB,这就省了很多底层实现的麻烦。奇异值分解用内置的svd()函数,矩阵构造用hankel(),VMD分解虽然MATLAB没有内置函数,但从VMD算法作者的主页下载开源代码包放进路径就能用,主程序核心代码加在一起也就一两百行的事。
2. SVD降噪原理拆解:从Hankel矩阵到奇异值截断
2.1 一维信号怎么变成矩阵
SVD处理的是矩阵,所以第一步要把一维时间序列x(1), x(2), ..., x(N)变成矩阵。最常用的做法是构造Hankel矩阵,也叫嵌入矩阵。假设信号长度是N,选一个嵌入维数m(也叫窗口长度),那么Hankel矩阵H的尺寸就是m行n列,满足m + n - 1 = N。矩阵元素满足H(i,j) = x(i + j - 1),也就是说它每条反对角线上的元素相等,是一个结构非常特殊的矩阵。
举个例子,信号x = [1, 2, 3, 4, 5],选m = 3,那么n = N - m + 1 = 3,Hankel矩阵就是:
H = [1 2 3 2 3 4 3 4 5]这个矩阵有什么特点?它把原来一维的时间结构信息,展开了成二维的空间结构。信号中如果有周期成分,对应的矩阵会出现低秩结构;如果是噪声主导的地方,矩阵的秩就会明显升高。这就是SVD能区分信号和噪声的底层逻辑——有用信号在矩阵层面表现出低秩性,噪声会摊平成高秩成分。
嵌入维数m的选择,直接决定了SVD降噪的效果。m选太小,矩阵里装不下足够的信号结构信息;m选太大,噪声在矩阵里被摊得更薄,一些弱信号细节也可能被当成噪声压掉。工程上常见的选择是m = N/2到N/3之间,也可以通过观察奇异值谱的衰减趋势来定。我自己的习惯是先用N/2跑一遍,看奇异值谱,如果第k个奇异值之后出现一个明显的“平台”或者缓慢下降段,这个拐点位置就是分界的参考。
2.2 奇异值分解做了什么
对Hankel矩阵H做奇异值分解,得到:
H = U * S * V'其中U和V都是正交矩阵,S是对角矩阵,对角线上的元素就是奇异值,按照从大到小排列。奇异值的大小,反映的是对应成分在信号里的能量占比。信号里主要的周期成分或者趋势成分,会对应到前面几个较大的奇异值;噪声成分因为在矩阵里表现出随机性,会分散到大量的小奇异值上。
所以降噪的思路就是:把S矩阵里较小的奇异值直接置零,只保留前r个大的奇异值,然后反变换重构矩阵:
H_clean = U(:, 1:r) * S(1:r, 1:r) * V(:, 1:r)'重构出来的矩阵再沿反对角线取平均(对角线平均法),就得到了降噪后的一维信号。这个对角线平均的过程是必须的,因为重构后的矩阵不一定严格满足Hankel结构,取平均能起到消除冗余的作用。
2.3 奇异值个数怎么定
SVD降噪唯一需要确定的参数,就是保留多少个奇异值。这个问题在信号处理里比较经典,常用的判断方法有几种:
- 奇异值能量占比法:计算前k个奇异值平方和占总奇异值平方和的比例,一般达到90%到99%就认为包含了主要信号成分。信噪比高取高值,信噪比低取低值。
- 奇异值差分谱法:计算相邻奇异值的差值序列,差值最大的那个位置往往对应信号成分和噪声成分的分界点。这个方法在工程里很常用,尤其是信号周期性明显时,差分谱会出现一个非常突出的峰值。
- 去噪效果评估法:相当于一种尝试法,从r = 1开始逐步增加保留个数,对比降噪前后信号的均方根误差(RMSE)或者信噪比(SNR),选最优值。这种方法适合有参考信号的情况,比如仿真信号。
我个人的建议是,初次处理先用差分谱法确定一个初始值,再在初始值附近做数量级搜索,看降噪后信号的波形是否平滑、有没有明显失真。SVD降噪过度最典型的表现是信号被削成“正弦波纯音”,高频细节全没了,这就是保留的奇异值太少;降噪不足则表现为曲线还毛刺明显,说明r的取值偏大。
3. VMD变分模态分解:把信号按频带切开
3.1 VMD到底做了什么
变分模态分解(VMD)是Dragomiretskiy和Zosso在2014年提出的自适应信号分解方法。它的核心思想是:把一个信号分解成K个模态函数,每个模态都是一个调幅调频信号,并且围绕一个中心频率、带宽有限。分解的过程是一个约束变分问题——所有模态加起来要能重构原始信号,同时每个模态的带宽之和要最小。
相比EMD(经验模态分解),VMD的最大优势是理论基础扎实。EMD本质上是一个递归的筛分算法,对噪声敏感、容易模态混叠、也没有严格的数学保证;VMD把分解过程建模为变分问题的求解,在频域里迭代更新,分解结果更稳定,而且模态个数K可以提前设定。我们这套方法里选VMD,就是图它分解鲁棒、对非平稳信号的适应性好,方便后续用SVD对每个模态单独处理。
3.2 VMD的四个关键参数
VMD代码包的核心函数是VMD(signal, alpha, tau, K, DC, init, tol),其中真正需要用心调的参数是下面几个:
- K(模态个数):分解出几个IMF。K选少了,两个不同频率成分会被揉进一个模态里;K选多了,会把一个成分劈成两半,或者分解出伪模态。
- alpha(带宽约束参数):也叫惩罚因子,数值越大,每个模态的带宽越窄,频率分辨率越高,但也更容易把有效信号切碎;数值小则模态带宽宽,灵活性高但容易混入噪声。
- tau(噪声容忍度):在保真项里用的参数,通常设为0就是严格保真;如果信号噪声大,适当增大tau可以容忍一定重构误差,换取更干净的模态。
- DC(第一模态是否包含直流分量):如果信号里有明显的零频趋势或者直流偏置,建议把DC设为1,避免直流分量干扰其他模态的中心频率。
这些参数的调整没有唯一的“标准答案”,因为它们本质上和信号的频率分布、噪声水平绑定在一起。我的习惯是用中心频率观察法来确定K:把K从2逐步增大到8,对每个K跑一轮VMD,观察各个模态的中心频率收敛情况。如果新增加K后,某个模态的中心频率和现有模态靠得非常近(比如相差不到一个频带宽度),说明K已经选过了。这个方法的可解释性比较强,也容易操作。
3.3 VMD的降噪角色定位
在SVD-VMD联合降噪的框架里,VMD并不是最终的去噪手段。它更像是“前置的频带划分器”——把混叠在一起的各种成分,按照频率和能量结构摊开,方便后续SVD在局部频带内做更细致的奇异值截断。
为什么不能只靠VMD降噪?因为VMD本质上是一个重构性分解,它并不是以“去噪”为目标的。对噪声信号做VMD,噪声的能量会被按频带分到各个模态里,尤其是那些和信号模态频带重叠的噪声,会直接混进模态内部,哪怕模态个数选得再准,也不可能把混进来的噪声自动剔除。所以VMD之后的SVD是必须的,不是画蛇添足。
反过来,如果只用SVD而不做VMD,对于频率成分复杂的非平稳信号,SVD在全局Hankel矩阵上做奇异值截断,会伤害那些能量较弱但有用的频率成分。先VMD后SVD的流水线设计,本质上是把“全局一刀切”改成了“先分频带,再带内精修”。
3.4 为什么是SVD-VMD顺序,不是VMD-SVD
这个问题我在实际给别人讲解的时候反复被问到。标题写的是“奇异值分解-变分模态分解”,从字面上看SVD在VMD前面,但是在实际工程里,通常采用的流程其实是先VMD后SVD——先分解,再对每个模态做SVD降噪。
这里有一个容易混淆的点:SVD-VMD这个叫法,表示的是一套方法体系的名称,并非严格的执行顺序。为什么实践中要先VMD再SVD?道理前面已经提到了。VMD的价值在于“分频段”,SVD的价值在于“提能量”。如果把顺序颠倒过来,先SVD后VMD,此时SVD处理的是全局信号,仍然面临频率成分复杂的问题,发挥不出频带划分的优势。所以真正高效的流水线是:原始信号 → VMD分解 → 对K个模态分别做SVD降噪 → 重构 → 得到降噪信号。
4. 完整实操:MATLAB环境下实现SVD-VMD联合降噪
4.1 环境准备和代码获取
我用的是MATLAB R2020a以上的版本,理论上R2016b之后都能跑,因为核心语法没变太多。需要额外获取的是VMD函数。VMD最初由Konstantin Dragomiretskiy和Dominique Zosso在论文里附带发布,代码在他们实验室主页或者MathWorks File Exchange上都能找到,函数名就是VMD.m。下载之后,把文件放进当前工作目录或者MATLAB的搜索路径里,就能直接调用。
我的建议是尽量找原版代码,不要去下载各种“优化版”或者“增强版”,因为后续参数调优和排错都以原版为准。原版VMD函数的调用形式是:
[u, u_hat, omega] = VMD(signal, alpha, tau, K, DC, init, tol);其中u是分解得到的模态矩阵,每一行是一个IMF;omega是各模态的最终中心频率;u_hat是频域表示,一般用不到。
4.2 仿真信号设计:带参数的测试用例
为了验证和演示这套方法的有效性,最好先设计一组带标准答案的仿真信号。这样跑完之后可以算SNR提升量、波形相似系数,心里有底。我常用的含噪仿真信号是这么构造的:
fs = 1000; % 采样率 1000Hz t = (0:1999) / fs; % 2秒信号 x_clean = 1.2 * sin(2*pi*50*t) + 0.8 * sin(2*pi*120*t) + 0.5 * sin(2*pi*200*t); rng(8); x_noise = x_clean + 0.7 * randn(size(t));这个信号里有50Hz、120Hz、200Hz三个正弦成分,幅值各不相同,加上高斯白噪声。为什么这么设计?因为三个频率成分跨度比较大,VMD分解比较容易把它们分开;白噪声则是典型的宽频噪声,在各个模态里都会残留,正好考验SVD的处理能力。
更具挑战性的仿真信号会把噪声改成有色噪声,比如让噪声能量集中在某个频段,这样VMD分解后噪声会和某个模态强重叠,更能检验SVD在带内降噪的能力。这个稍后会在问题排查部分展开讲。
4.3 完整的SVD函数封装
SVD降噪的核心代码不长,但建议封装成函数,方便反复调用。我写了一个比较通用的函数,关键点都注释在里面:
function [x_denoised, idx_keep] = svd_denoise(x, m, method, param) % SVD降噪函数 % 输入: % x : 原始一维信号 (列向量) % m : Hankel矩阵嵌入维数 % method : 奇异值截断方法,'diff' 差分谱, 'energy' 能量占比, 'fix' 固定个数 % param : 对应方法参数,差分谱法可不填,能量法为比例(0-1),fix法为保留个数 % 输出: % x_denoised : 降噪后的信号 % idx_keep : 实际保留的奇异值个数 N = length(x); n = N - m + 1; H = zeros(m, n); for i = 1:m H(i, :) = x(i : i + n - 1)'; end [U, S, V] = svd(H, 'econ'); sigma = diag(S); % 确定保留奇异值个数 if strcmp(method, 'diff') diff_sigma = abs(diff(sigma)); [~, idx_keep] = max(diff_sigma); elseif strcmp(method, 'energy') totalE = sum(sigma.^2); cumE = cumsum(sigma.^2); idx_keep = find(cumE / totalE >= param, 1, 'first'); else idx_keep = param; end % 重构 H_clean = U(:, 1:idx_keep) * S(1:idx_keep, 1:idx_keep) * V(:, 1:idx_keep)'; % 反对角线平均还原一维信号 x_denoised = zeros(N, 1); cnt = zeros(N, 1); for i = 1:m for j = 1:n x_denoised(i + j - 1) = x_denoised(i + j - 1) + H_clean(i, j); cnt(i + j - 1) = cnt(i + j - 1) + 1; end end x_denoised = x_denoised ./ cnt; end这段代码里Hankel矩阵的构造用了循环,虽然简单直观,但MATLAB里循环速度相对慢。数据量大会慢一些。如果数据长度超过5万点,建议改成向量化的方式构造Hankel矩阵,或者直接用内置的hankel(x(1:m), x(m:end))函数,代码更简洁、速度也更快。
4.4 主流程:VMD分解 + 模态SVD降噪 + 重构
主程序我通常按下面这个节奏来写:
第一步,加载或生成原始含噪信号,做基础预处理。这里的预处理很关键,包括去除均值、检查是否有NaN值、确认数据是列向量。VMD函数里默认要求输入是行向量还是列向量,不同版本的代码要求不一样,读一遍源码最稳妥。
x = x(:).'; % 统一转成行向量 x = x - mean(x); % 去均值,防止直流分量干扰第二步,VMD分解。先用中心频率观察法确定K,这里假设最优K是3:
alpha = 2000; % 带宽惩罚因子 tau = 0; % 严格保真 K = 3; % 模态个数 DC = 0; % 不含直流分量 init = 1; % 初始化方式,1表示均匀初始化中心频率 tol = 1e-7; % 收敛容差 [u, ~, omega] = VMD(x, alpha, tau, K, DC, init, tol);跑完之后观察omega的值,也就是各模态的中心频率。如果三个中心频率分别在50Hz、120Hz、200Hz附近,说明VMD分解结果符合预期,K选得没问题。如果中心频率出现偏移或者两个模态频率粘在一起,就要回头调整K或者alpha。
第三步,对每个模态分别做SVD降噪。这里有一个需要注意的细节:模态信号的长度和原始信号一样,所以嵌入维数m的选择策略不变,但对不同模态可以单独调优。因为各模态的频带宽度、信噪比都不一样,统一用同一个m可能不是最优。
m_embed = round(length(u(1, :)) / 3); for k = 1:K u_denoise(k, :) = svd_denoise(u(k, :), m_embed, 'energy', 0.98); end把能量保留比例设为0.98,这个值我是经过对比测试的。设得太低(比如0.9),一些有用谐波会被削掉,重构信号听起来发“闷”;设得太高(0.999),噪声压不干净,SVD的作用体现不出来。0.98对于大多数工程信号是一个比较折中的值。
第四步,把降噪后的模态叠加,重构降噪信号。这里不需要用VMD自带的重构接口,直接相加就行,因为VMD分解本身满足完全重构条件。
x_denoised = sum(u_denoise, 1);第五步,计算指标评估降噪效果。如果没有原始纯净信号,只能看降噪前后信噪比的变化;如果有纯净参考信号(比如仿真数据),可以同时计算SNR提升量和相关系数。
% 计算降噪后的信噪比 signal_power = mean(x_clean.^2); noise_power = mean((x_denoised - x_clean).^2); SNR_denoised = 10 * log10(signal_power / noise_power); % 和原始噪声信号的信噪比对比 noise_power_orig = mean((x_noise - x_clean).^2); SNR_orig = 10 * log10(signal_power / noise_power_orig); fprintf('原始信噪比: %.2f dB, 降噪后信噪比: %.2f dB\n', SNR_orig, SNR_denoised);以我之前跑的仿真数据为例,原始SNR大概在6.5dB左右,跑完SVD-VMD流水线之后能提到15.8dB左右,提升约9dB。波形相关系数一般能达到0.95以上。
4.5 参数选择速查表
为了让大家快速上手,我把几个关键参数的推荐范围和选取策略整理成了一张表:
| 参数 | 推荐范围 | 核心选择逻辑 | 选择工具/方法 |
|---|---|---|---|
| 嵌入维数m | N/3到N/2 | 兼顾矩阵结构丰富度和计算效率 | 观察奇异值谱趋势 |
| 保留奇异值个数r | 无固定值 | 取差分谱峰值位置或能量占比90%-98% | 差分谱法/能量占比法 |
| VMD模态数K | 2到8 | 中心频率稳定且不重叠 | 中心频率观察法 |
| 带宽惩罚alpha | 500到5000 | 频率分辨率与重构精度的平衡 | 试凑+频谱对比 |
| 噪声容忍tau | 0到0.5 | 信号噪声大时可适当调大 | SNR评估 |
| 能量保留比 | 0.95到0.99 | 降噪强度和波形失真之间的平衡 | SNR与相关系数 |
这几个参数不是孤立的。比如K选大了,每个模态的带宽自然变窄,这时候SVD嵌入维数m的敏感度就会提高;alpha调大之后模态更“瘦”,奇异值谱里信号和噪声的分界也更清晰。所以实际调参过程要来回迭代,不要指望一套参数走天下。
5. 降噪效果评估与结果验证
5.1 主观检查:先看波形,再看频谱
跑完程序之后,我习惯先画一张“三行图”:原始含噪信号、SVD-VMD降噪信号、纯净参考信号(如果存在),放在同一个时间轴里对比。三根曲线摆在一起,有没有过度平滑、有没有相位偏移、有没有幅值收缩,一眼就能看出来。
别小看这个主观步骤。数值指标再好看,如果波形形状都变了,说明降噪过程中把有用信息也带走了。我见过不少新手,拿着SNR提升十几个dB的结果高兴得不行,结果发现信号尖峰被削平了,这在实际工程里是不可接受的。
波形看完了,再看频谱。用pwelch或者fft分别算降噪前后的功率谱密度,重点观察:原始信号里三个主要频率成分的谱峰是否保住了;谱峰之间的噪声地板是否压下去了;有没有出现原本不存在的伪峰。伪峰是SVD降噪过度时一个比较典型的现象,通常是因为某些频段被强行截断了某个成分,导致重构信号的频谱出现不连续或额外振荡。
5.2 数值指标:SNR、RMSE和相关系数
数值评估方面,我最常用的三个指标是信噪比SNR、均方根误差RMSE和波形相关系数R。
SNR是工程里引用最多的指标,表示信号功率和噪声功率的比值,单位是dB。SNR提升量是降噪算法最直观的加分项。RMSE反映降噪信号和纯净参考信号之间的平均误差,数值越小越好,但单看RMSE容易忽略波形细节。相关系数R在0到1之间,越接近1说明波形形状保持得越好,这个指标对幅值缩放不敏感,适合专门检查“有没有削波形”。
三个指标最好结合起来看。SNR提升明显,R也在0.9以上,说明降噪策略是对的;如果SNR提升但R明显下降,大概率是过度平滑了,需要减小能量保留比例或者降低嵌入维数。
5.3 几种降噪方法的横向对比
这个SVD-VMD方法不是唯一的降噪选项。为了说明它的优势,我做了一个简单的横向对比,选取的方法包括:经典小波软阈值降噪、EMD结合SVD降噪、单独的SVD降噪、单独的VMD降噪,以及本文的SVD-VMD联合降噪。测试信号和上一节相同。
| 方法 | 原始SNR(dB) | 降噪后SNR(dB) | 提升(dB) | 相关系数R |
|---|---|---|---|---|
| 小波软阈值 | 6.4 | 10.2 | 3.8 | 0.87 |
| EMD+SVD | 6.4 | 12.5 | 6.1 | 0.91 |
| 仅SVD | 6.4 | 11.8 | 5.4 | 0.90 |
| 仅VMD | 6.4 | 8.6 | 2.2 | 0.82 |
| SVD-VMD联合 | 6.4 | 15.7 | 9.3 | 0.96 |
这个结果很有代表性。仅VMD降噪效果最差,因为VMD本身不是为去噪设计的,内置的保真重构机制反而会把噪声保留一部分。小波阈值的问题是阈值和基函数的选择太依赖经验,我这里用的默认设置,没做精细调参,所以效果一般。SVD-VMD联合的优势非常明显:剔除VMD全局范围内的粗噪声,同时保留每个模态内的细节能量。
当然这个对比基于仿真信号,真实信号没有标准答案,效果评估更多依赖主观波形和专业判断。但横向对比能让我们直观感受到方法论的差异。
6. 常见问题与排查技巧实录
6.1 VMD分解出来的模态中心频率不稳定
这个问题在调K的时候几乎必现。明明上一轮K=3的中心频率是49.8Hz、119.6Hz、200.3Hz,下一轮信号数据稍微改几个点,中心频率就飘到45Hz、115Hz、210Hz,怎么回事?
先排除最基础的问题:信号有没有去均值?如果信号有直流偏置,DC参数还设的是0,VMD为了表达这个直流分量,会把第一个模态的中心频率从近似零频硬掰到某个频率上,导致中心频率偏移。处理办法是先去均值,或者把DC设为1。
然后是VMD的参数初始化问题。原版VMD默认采用均匀初始化中心频率(init=1),这个方案对频率跨度大的信号比较友好,但如果信号里存在比较接近的频率成分,均匀初始化可能会导致求解过程陷入局部最优。可以把init改为0(随机初始化),或者通过omega的初值设定来给一个更好的起点。
最后一个常见原因就是信号本身频率成分不是稳定的。实际采集的振动信号,转频会漂移,一边跑一边变。这种情况不是程序BUG,而是信号的物理特性。如果频率漂移范围不大,比如在±5%以内,可以认为分解结果可接受;如果漂移太多,建议把信号分段再分别处理。
6.2 SVD降噪后信号出现“阶梯状”毛刺
这是我遇到过的一个比较隐蔽的问题。信号做完SVD降噪后,大趋势是对的,但局部会有类似阶梯跳变的小毛刺,尤其是在信号斜率突变的位置附近。
排查下来,问题出在Hankel矩阵的嵌入维数m上。m选得过大时,矩阵里某一行会跨度到信号的不同状态段,导致重构时在对角线平均的地方出现“缝合”误差。解决方法有两个方向:一是适当减小嵌入维数m,让矩阵每行覆盖的信号长度更短,减少跨状态段的概率;二是把信号先做一次粗分段,在各段内部单独做SVD降噪,最后再拼接起来。分段处理的时候注意段两端各留一部分重叠,拼接处做一些平滑过渡,比如用重叠区的线性加权平均,可以有效避免段边界处的人为跳变。
还有一类“阶梯状”毛刺是保留的奇异值个数太少导致的。奇异值个数太少意味着只保留了最主要的几个成分,其余细节全被抹成零。重构出来的信号在波峰、波谷、斜率突变点会显得生硬,有一种“多边形拟合正弦波”的感觉。这时适当增加保留奇异值个数,或者降低能量保留比例中的截止值,可以缓解。
6.3 有色噪声场景下SVD-VMD效果变差的处理
前面提到的仿真信号用的是白噪声。实际工程里噪声往往是有色噪声——能量集中在某个频谱区间。比如低频漂移、工频干扰、机械共振噪声,频带很窄但能量很大。
遇到这种情况,直接套白噪声调参思路会出问题。我曾经处理过一组实测加速度信号,噪声能量集中在30Hz到50Hz之间,而信号的其中一个有用模态中心频率正好也在40Hz附近。VMD正常分解时,这个40Hz的模态里混进了大量的窄带噪声,SVD在这个模态上做奇异值截断时,由于噪声能量甚至高于信号能量,奇异值谱里“信号成分”和“噪声成分”的大小关系几乎是倒置的,截断点怎么选都不理想。
这个问题我的解决方案是引入一个预滤波步骤。在VMD之前,先用一个陷波滤波器或者带阻滤波器,把已知的强窄带干扰抑制掉。滤波器对信号本身频带的影响用后续的SVD步骤来补偿。换句话说,预滤波器负责对付“已知的、频带明确的干扰”,VMD加SVD负责对付“未知的、宽频的随机噪声”。分工明确,效果会好很多。
6.4 代码运行速度慢,是不是死循环了
SVD对矩阵的计算复杂度是O(min(m, n) * m * n),当信号长度到十万点级别、嵌入维数m取到几万时,这个量级已经相当可观。VMD的迭代次数如果收敛慢,也会显著拖慢运行时间。如果跑一次完整程序要几分钟,大概率不是死循环,而是计算量确实在那里。
几个提速的思路:一是把VMD的tol放宽,比如从1e-7放宽到1e-6,迭代次数能减少20%左右,对结果影响很小;二是嵌入维数m不要取N/2以上,N/3左右能在计算速度和信息保留之间取得不错的平衡;三是MATLAB的并行工具箱如果方便,可以把不同模态的SVD降噪放到parfor里并行执行,几个模态之间的任务是天然独立的。最后这招提速效果最明显,但也是最后才考虑——毕竟不是所有人都有并行计算许可。
6.5 模态数和原始信号的频率不匹配
有一种情况是,原来的有用信号频率是100Hz,但VMD分解出的模态中心频率却是98Hz或者103Hz,偏差不大但确实存在。这在VMD中是正常现象,因为VMD是根据信号的局部带宽和能量分布来寻找中心频率的,不是精确的傅里叶频率追踪器。中心频率的小幅偏差可以通过增加alpha来缓解——alpha加大后,模态带宽收窄,中心频率会更贴近真实的傅里叶谱峰位置。
如果偏差大到影响后面的应用,比如做故障特征提取时要精准知道谱峰位置,那就需要在VMD之后再做一个校正过程:对每个模态做FFT,用谱峰位置替代VMD给出的omega值,以修正后的频率为准。这个过程写起来简单,但效果很实用。
7. 这套方法的工程扩展与个人体会
7.1 从一维到多维的扩展思路
这套SVD-VMD框架虽然标题限定是一维时间序列,但它的思路可以扩展到其他领域。比如图像处理,图像本身是二维数据,但可以按行或列展开成一维信号后分别处理,然后重组图像;或者直接把二维图像的每个通道展平成一个长序列,套用同样的流水线。这个方法在做低照度图像去噪时效果还不错,前提是图像的空间结构没有太多突变纹理。
在故障诊断领域,SVD-VMD常被用作特征提取的预处理步骤。设备振动信号经过SVD-VMD降噪后,再计算各个模态的时域特征(峭度、峰值因子、能量比)或频域特征(中心频率、带宽能量等),作为后续故障分类器的输入特征,准确率会比直接在原始含噪信号上提特征高出一截。我之前做轴承故障诊断时,用这套流程提取特征输入支持向量机,分类准确率比直接用小波包特征高出约7个百分点,说明预处理的价值很大。
7.2 关于自动调参的一点探索
SVD-VMD的调参一直是一个让人头疼的点。实际处理工程信号时,信号是批量来的、一批几百组,人工每组调参是不现实的。我在项目后期做了一点自动化尝试:用信噪比提升作为目标函数,在K和能量保留比例的二维网格上做搜索,每组数据自动跑一遍,选出最优参数组合。代价是计算量成倍增加,在数据量不大、参数范围窄的前提下,是可行的。
更先进的做法是利用贝叶斯优化或者遗传算法来做参数寻优,但我个人的建议是先把网格搜索跑通,理解参数之间的耦合关系之后,再上复杂的优化算法,否则很容易出现参数“过拟合”到某个数据集上的问题。
7.3 最后分享一个细节
处理实测信号时,SVD的嵌入维数m,我的默认值通常是信号长度的五分之一到三分之一之间,但有一个例外:信号长度特别短,几百个点的数据,m取太大没有意义,因为Hankel矩阵的列数会变得很少,矩阵几乎是瘦长条,奇异值分解的统计意义会变差。这种情况下我会把m压到N/4左右,甚至更小。
还有一个细节是,对VMD分解出来的每个模态做SVD降噪之前,先对这个模态做一次归一化,也就是除以它自身标准差。这样做的目的是让SVD过程不对幅值差异大的模态区别对待,统一在同一个能量尺度上做奇异值截断判断。降噪和重构之后再乘回原来的标准差。这个处理很小,但对那些幅值差异悬殊的多模态信号(比如一个幅值1的频率成分加一个幅值0.001的频率成分),效果差异非常明显。
回头再看这个SVD-VMD联合降噪方法,它本质上不是某个单一理论的重大突破,而是把两个成熟工具的互补性结合起来。SVD擅长在局部频带内按能量提纯,VMD擅长自适应地完成频带划分。两者都不是新方法,组合在一起却能解决不少实际工程里“单一方法不好使”的困境。希望这篇文章能帮到在做信号降噪、特征提取或者单纯是课程作业的朋友少走点弯路。如果你跑数据的时候遇到什么奇怪的现象,欢迎一起交流。