变分模态分解(VMD)运算步骤及源码解读

2023-11-20

1. 简述

VMD的目标是将实值输入信号 f f f分解为离散数量的子信号(模态) u k u_k uk 。我们先假设每个模态在一个中心频率 ω k \omega_k ωk周围是紧密的,也就是说这个模态包含的频率分量都在 ω k \omega_k ωk附近,而 ω k \omega_k ωk是随着分解来确定。

为了评估一个模态的带宽,提出以下方案:1)对于每个模态,通过希尔伯特变换计算相关的分析信号,以便获得单向频谱。2)对于每种模态,通过与调谐到相应估计中心频率的指数混频,将模态的频谱移至“基带”。3)现在通过解调信号的高斯平滑度,即梯度的平方范数来估计带宽。

得到的约束变分问题如下:

在这里插入图片描述
在这里插入图片描述
求解上述方程,得到模态 u k u_k uk的求解公式为:
在这里插入图片描述
中心频率 ω k \omega_k ωk的求解公式为:
在这里插入图片描述

2. 运算步骤

在这里插入图片描述

3.VMD分解之后的模态和原信号的关系

经过VMD分解之后的k个模态直接相加可以得到一个原信号的近似信号,两信号相减会有一个残差,这是因为对于一个实际信号不管分解多少次都不可能完全用分解出来的模态完全代表原信号。
所以在对分解出来的模态操作时不能忘记残差。

4. 源码解读

# -*- coding: utf-8 -*-
"""
Created on Wed Feb 20 19:24:58 2019

@author: Vinícius Rezende Carvalho
"""
import numpy as np

def  VMD(f, alpha, tau, K, DC, init, tol):
    """
    u,u_hat,omega = VMD(f, alpha, tau, K, DC, init, tol)
    Variational mode decomposition
    Python implementation by Vinícius Rezende Carvalho - vrcarva@gmail.com
    code based on Dominique Zosso's MATLAB code, available at:
    https://www.mathworks.com/matlabcentral/fileexchange/44765-variational-mode-decomposition
    Original paper:
    Dragomiretskiy, K. and Zosso, D. (2014) ‘Variational Mode Decomposition’, 
    IEEE Transactions on Signal Processing, 62(3), pp. 531–544. doi: 10.1109/TSP.2013.2288675.
    
    
    Input and Parameters:
    ---------------------
    f       - 即将被分解的一维时域信号
    alpha   - 数据保真度约束的平衡参数
    tau     - 双重上升的时间步长(为零噪声选择0)
    K       - 要被恢复的模态数量
    DC      - 如果将第一种模态置于并保持为直流(0频率),则为true
    init    - 0 = all omegas start at 0
                       1 = all omegas start uniformly distributed
                      2 = all omegas initialized randomly
    tol     - tolerance of convergence criterion; typically around 1e-6
    		  收敛准则的容忍度; 通常在1e-6左右

    Output:
    -------
    u       - the collection of decomposed modes
    		  分解模态的集合
    u_hat   - spectra of the modes
    		  模态的频谱

    omega   - estimated mode center-frequencies
    		  被估计的模态中心频率
    		  omega 是一个矩阵,他储存了Niter-1组中心频率值,
    		  形状为(Niter-1, K),Niter在vmdpy.py中定义,K为分解数量
    		  omega矩阵储存了中心频率收敛过程中的数据,
    		  所以一般取最后一行来用,这是最终的中心频率
    """
    
    # 
    if len(f)%2:
       f = f[:-1]

    # 输入信号的周期和采样频率
    fs = 1./len(f)
    
    ltemp = len(f)//2 
    # 将f的前半截沿着axis = 0反转,再将f append到反转后的数组中
    # 例如f = np.array([1,2,3,4,5,6,7,8]),处理后变成了
    #  array([4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8])
    fMirr =  np.append(np.flip(f[:ltemp],axis = 0),f)  
    # 接着上面的例子,处理之后变成了array([4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8,8,7,6,5])
    fMirr = np.append(fMirr,np.flip(f[-ltemp:],axis = 0))

	"""
    # Time Domain 0 to T (of mirrored signal)
    # 时域0到T(镜像信号的)
    # 再接上面的例子,T=16
    
    t =
    
    array([0.0625, 0.125 , 0.1875, 0.25  , 0.3125, 0.375 , 0.4375, 0.5   ,
       0.5625, 0.625 , 0.6875, 0.75  , 0.8125, 0.875 , 0.9375, 1.    ])
    """
    
    T = len(fMirr)
    t = np.arange(1,T+1)/T  
    
    # Spectral Domain discretization  谱域离散化
    """
    freqs =
    
    array([-0.5   , -0.4375, -0.375 , -0.3125, -0.25  , -0.1875, -0.125 ,
       -0.0625,  0.    ,  0.0625,  0.125 ,  0.1875,  0.25  ,  0.3125,
        0.375 ,  0.4375])
    """
    freqs = t-0.5-(1/T)

    # Maximum number of iterations (if not converged yet, then it won't anyway)
    # 最大迭代次数(如果尚未收敛,则无论如何都不会收敛了)
    Niter = 500
    
    # For future generalizations: individual alpha for each mode
    # 为了将来的概括:每种模态都有单独的Alpha
    Alpha = alpha*np.ones(K)
    

	"""
    # Construct and center f_hat  构建并居中f_hat
    # np.fft.fft(fMirr)表示对fMirr进行快速傅立叶变换
    # numpy.fft模块中的fftshift函数可以将FFT输出中的直流分量移动到频谱的中央。
    # ifftshift函数则是其逆操作。 
    # 所以这一句的意思是先对fMirr进行快速傅立叶变换,然后将变换后的直流分量移到频谱中央
    """
    f_hat = np.fft.fftshift((np.fft.fft(fMirr)))
    f_hat_plus = np.copy(f_hat) #copy f_hat
    # 把数组前半截元素都置为0
    f_hat_plus[:T//2] = 0

    # Initialization of omega_k
    omega_plus = np.zeros([Niter, K])


    if init == 1:  # 1 = all omegas start uniformly distributed
        for i in range(K):
            omega_plus[0,i] = (0.5/K)*(i)
    elif init == 2: # 2 = all omegas initialized randomly
        omega_plus[0,:] = np.sort(np.exp(np.log(fs) + (np.log(0.5)-np.log(fs))*np.random.rand(1,K)))
    else: #  all omegas start at 0
        omega_plus[0,:] = 0
            
    # if DC mode imposed, set its omega to 0
    if DC:
        omega_plus[0,0] = 0
    
    # start with empty dual variables  从空对偶变量开始
    lambda_hat = np.zeros([Niter, len(freqs)], dtype = complex)
    
    # other inits
    uDiff = tol+np.spacing(1) # update step  更新步长?
    n = 0 # loop counter  循环计数器
    sum_uk = 0 # accumulator  累加器
    # matrix keeping track of every iterant // could be discarded for mem
    # 跟踪每个巡回剂的矩阵//可被mem丢弃
    u_hat_plus = np.zeros([Niter, len(freqs), K],dtype=complex)    

    #*** Main loop for iterative updates***  迭代更新的主循环

    while ( uDiff > tol and  n < Niter-1 ): # not converged and below iterations limit 未收敛且低于迭代限制
        # update first mode accumulator  更新第一模态累加器
        k = 0
        sum_uk = u_hat_plus[n,:,K-1] + sum_uk - u_hat_plus[n,:,0]
        
        # update spectrum of first mode through Wiener filter of residuals
        # 通过残差的维纳滤波器更新第一模态的频谱
        u_hat_plus[n+1,:,k] = (f_hat_plus - sum_uk - lambda_hat[n,:]/2)/(1.+Alpha[k]*(freqs - omega_plus[n,k])**2)
        
        # update first omega if not held at 0 如果没有保持0,则更新第一个omega
        if not(DC):
            omega_plus[n+1,k] = np.dot(freqs[T//2:T],(abs(u_hat_plus[n+1, T//2:T, k])**2))/np.sum(abs(u_hat_plus[n+1,T//2:T,k])**2)

        # update of any other mode
        for k in np.arange(1,K):
            #accumulator
            sum_uk = u_hat_plus[n+1,:,k-1] + sum_uk - u_hat_plus[n,:,k]
            # mode spectrum
            u_hat_plus[n+1,:,k] = (f_hat_plus - sum_uk - lambda_hat[n,:]/2)/(1+Alpha[k]*(freqs - omega_plus[n,k])**2)
            # center frequencies
            omega_plus[n+1,k] = np.dot(freqs[T//2:T],(abs(u_hat_plus[n+1, T//2:T, k])**2))/np.sum(abs(u_hat_plus[n+1,T//2:T,k])**2)
            
        # Dual ascent  双重上升
        lambda_hat[n+1,:] = lambda_hat[n,:] + tau*(np.sum(u_hat_plus[n+1,:,:],axis = 1) - f_hat_plus)
        
        # loop counter
        n = n+1
        
        # converged yet?
        uDiff = np.spacing(1)
        for i in range(K):
            uDiff = uDiff + (1/T)*np.dot((u_hat_plus[n,:,i]-u_hat_plus[n-1,:,i]),np.conj((u_hat_plus[n,:,i]-u_hat_plus[n-1,:,i])))

        uDiff = np.abs(uDiff)        
            
    #Postprocessing and cleanup
    
    #discard empty space if converged early
    Niter = np.min([Niter,n])
    omega = omega_plus[:Niter,:]
    
    idxs = np.flip(np.arange(1,T//2+1),axis = 0)
    # Signal reconstruction
    u_hat = np.zeros([T, K],dtype = complex)
    u_hat[T//2:T,:] = u_hat_plus[Niter-1,T//2:T,:]
    u_hat[idxs,:] = np.conj(u_hat_plus[Niter-1,T//2:T,:])
    u_hat[0,:] = np.conj(u_hat[-1,:])    
    
    u = np.zeros([K,len(t)])
    for k in range(K):
        u[k,:] = np.real(np.fft.ifft(np.fft.ifftshift(u_hat[:,k])))
        
    # remove mirror part
    u = u[:,T//4:3*T//4]

    # recompute spectrum
    u_hat = np.zeros([u.shape[1],K],dtype = complex)
    for k in range(K):
        u_hat[:,k]=np.fft.fftshift(np.fft.fft(u[k,:]))

    return u, u_hat, omega

5. 相关知识点

(1)np.flip:沿着指定的轴反转元素

>>> A = np.arange(8).reshape((2,2,2))
>>> A
array([[[0, 1],
        [2, 3]],
       [[4, 5],
        [6, 7]]])
        
>>> flip(A, 0)
array([[[4, 5],
        [6, 7]],
       [[0, 1],
        [2, 3]]])
        
>>> flip(A, 1)
array([[[2, 3],
        [0, 1]],
       [[6, 7],
        [4, 5]]])

>>>np.flip(A, 2)
array([[[1, 0],
        [3, 2]],
       [[5, 4],
        [7, 6]]])

参考论文链接:https://pan.baidu.com/s/1BCKjc5paLkAOtM1lVlXSjQ
提取码:105f

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

变分模态分解(VMD)运算步骤及源码解读 的相关文章

随机推荐

  • hooks实践总结

    何为hooks 在React中hook是指不编写 class 的情况下使用 state 以及其他的 React 特性 而Vue3也推出了具有相同功能的组合式API 如果你用过Vue3就会知道在 setup 中你应该避免使用 this 因为h
  • 汇编语言(第三版)读书笔记 2 - 第2章 寄存器

    第2章 寄存器 前一章所说的总线 相对于CPU内部来说是外部总线 内部总线实现了CPU内部各个器件 运算器 控制器 寄存器 之间的联系 外部总线实现了CPU和主板上其他器件的联系 不同的CPU 寄存器的个数 结构是不相同的 8086 CPU
  • Redis实现定时任务

    Redis定时任务的核心在于 Schedule 注解 Redis Zset List数据结构 Redis管道技术 就从定时任务的执行流程开始写起 1 前端用户发起定时任务创建定时任务任务 像定时任务模块发起定时任务请求并且携带必要参数 首先
  • 【Python】逆向爬虫-----常见的加密方法

    一 MD5加密 MD5加密是一种被广泛使用的线性散列算法 可以产生出一个128位 16字节 的散列值 hash value 用于确保信息传输完整的一致性 且MD5加密之后产生的是一个固定长度 32位或16位 的数据 若要破解MD5加密 需要
  • C++程序的基本组成简介

    C 程序的基本组成简介 C 程序的基本组成 这个C 程序例子 由一个程序单位 程序文件 注 组成 这是一个简单例子未使用类 注 其中包括 1 头文件 可以认为头文件是你在调用函数时的一个桥梁 格式为 include 引用文件名 c 的程序是
  • set容器

    恭喜主教大人完成了自己的目标 set 容器 set容器基本概念 简介 所有元素都会在插入时所有元素都会在插入时自动被排序 自动去重 可重复插不报错但是去重了 默认从小到大排 本质 set multiset属于关联式容器 底层结构是用二叉树实
  • 最新ChatGPT GPT-4 文本生成技术详解(附ipynb与python源码及视频讲解)——开源DataWhale发布入门ChatGPT技术新手从0到1必备使用指南手册(三)

    目录 前言 最新ChatGPT GPT 4 文本生成技术详解 1 引言 2 文本摘要任务 2 1 什么是文本摘要 2 2 常见的文本摘要技术 2 3 基于OpenAI接口的文本摘要实验 2 3 1 简单上手版 调用预训练模型 2 3 2 进
  • 面向对象的单片机编程

    1 在看别人单片机程序时 你也许是奔溃的 因为全局变量满天飞 不知道哪个在哪用了 哪个表示什么 而且编写极其不规范 2 在自己写单片机程序时 也许你也是奔溃的 总感觉重新开启一个项目 之前的写过相似的代码也无法使用 得重新敲 代码重用度不高
  • 关系数据库(数据库原理)

    目录 一 关系数据结构 二 关系的完整性 三 关系运算 四 关系的规范化 一 关系数据结构 1 关系的定义和性质 1 关系的数学定义 域 一组有相同数据类型的值得集合 笛卡尔积 设任意的N个域D1 D2 Dn 定义D1 D2 Dn的笛卡尔积
  • Android热更新之AndFix就是个大坑

    最近一两年Android插件化热更新此起彼伏 也许Android的开发者也希望有朝一日 来颠覆频繁的去更新版本 而像web前端一样 更改了代码立马生效的效果 确实 如果已经上线的版本 突然有了bug 按照现有模式 开发者不得不去解决bug
  • 类中的getInstance()方法的用法和作用

    class rmt dbutil public public static rmt dbutil getInstance if instance NULL instance new rmt dbutil return instance bo
  • quill编辑器使用

    官方git https github com quilljs quill 官方文档 https quilljs com 中文文档 https kang bing kui gitbook io quill 编辑器是个正经编辑器 就是文档太不正
  • android studio更新到3.6以上后布局文件不能切换到xml编辑器?那就点进来吧

    android studio更新到3 6以上后布局文件不能切换到xml编辑器 只能拖拽写UI了 怎么可能 看下面截图 打开布局文件后 默认是到预览界面的 右上角的三个按钮就是用来切换视图的 自己点击试试就知道啦
  • 正则校验手机号

    正则表达式可以用来校验手机号码的合法性 如果你想使用正则表达式来校验中国大陆的手机号码 可以使用如下的正则表达式 1 3 9 d 9 这个正则表达式可以匹配所有 13 到 19 开头的 11 位数字 即所有中国大陆的手机号码 例如 如果你想
  • 全网最全的人类图解析(上)——九大能量中心与64道闸门

    以下内容来源皆来自 亚洲人类图学院 获得自己的人类图 传送门 文章目录 简介 一 九大能量中心简介 1 头脑中心 Head Center 头脑中心的主题 灵感 2 逻辑中心 Ajna Center 逻辑中心的主题 概念化 3 喉咙中心 Th
  • CSDN平台上怎么样才能赚钱?

    CSDN平台上有多种方式可以赚钱 以下是其中几种常见的 1 写作赚钱 CSDN平台鼓励用户积极创作原创技术博客 通过博客的阅读量和转发量来获取广告收益 用户还可以发表付费文章或参与付费专栏 在文章的阅读量和付费订阅量上获得收入 2 交流赚钱
  • java使用visio画类图,【什么是类图使用类图的方法】使用visio画类图

    类图是显示了模型的静态结构 特别是模型中存在的类 类的内部结构以及它们与其他类的关系等 那么你对类图了解多少呢 以下是由小编整理关于什么是类图的内容 希望大家喜欢 类图的概述 类图 Class diagram 由许多 静态 说明性的模型元素
  • 【数据结构--二叉树】平衡二叉树

    题目描述 代码实现 Definition for a binary tree node struct TreeNode int val struct TreeNode left struct TreeNode right int TreeH
  • Could not proxy request /captchaImage from localhost to http://localhost:8080/.

    项目场景 项目场景 配置若依环境前端通过 run npm dev 启动报500 问题描述 根据报错分析 无法将请求 路径 从本地主机代理到http 本地主机 8080 原因分析 我们可以看到前端配置的端口号80 地址就是本机没有问题 排除前
  • 变分模态分解(VMD)运算步骤及源码解读

    1 简述 VMD的目标是将实值输入信号 f f f分解为离散数量的子信号 模态 u k u k uk 我们先假设每个模态在一个中心频率