超出 DFT 泄漏分辨率的谐波信号线性组合幅度、频率和相位的估计

信息处理 matlab fft 频率 估计 频域
2021-12-26 00:01:54

当存在频谱泄漏时,如何找到每个频率的粗略(尽可能准确)幅度。目前,我正在处理一个包含特殊泄漏的系统,这似乎是不可避免的,因为我正在测量具有任何可能频率的真实信号。目前,即使有泄漏,我也可以将分量频率识别到相当准确的水平,但我找不到这个频率的幅度,因为能量分布在多个频率区间。

是否有可能直接从 FFT 或使用识别的频率后记得到频率幅度的粗略预测?

我一直在尝试使用 Windowing,发现这很有用。 https://www.youtube.com/watch?v=VxTx9QW8Zx8&ab_channel=Adash 使用 Hann 窗口并使用频率校正方程。它给出了很好的频率近似值,但没有幅度或相位。

当前用于测试和生成光谱的代码示例。

import numpy as np

def gendata(FreqR,AmplR ,Ssize):
    PhaseR = np.pi # phase range
    FreqR = (FreqR*10) +1
    AmplR = (AmplR*100)+1
    PhaseR = (PhaseR*100)+1
    
    freq_OR = []
    ampl_OR= []
    phase_OR =[]


    for i in range(Ssize):
        freq_OR. append((((FreqR-1)/Ssize)*(i+1)/10)) # currently just equaly spread but can be any value between 0-9 rad/s
        ampl_OR.append(1) # unit amplitude to check for variation and error.
        phase_OR.append(np.random.randint((-PhaseR),(PhaseR))/100) # random phase 

    return(freq_OR,ampl_OR,phase_OR)


def pltsignal(freq_OR,ampl_OR,phase_OR,t_OR):
    
    z = 0 # original signal flat sea's
    if len(freq_OR) == len(ampl_OR):
        for i in range(len(freq_OR)): # for each frequency generate a regular wave.
            wave_OR = ampl_OR[i] * np.cos(2*np.pi*freq_OR[i]*t_OR + phase_OR[i]) # generated wave signal using the frequncy, amplitude and phase of each wave.
           
            z = z + wave_OR # superpostion each wave ontop of each other.
    else:
        print("amplutide and frequency are diffrent lengths")
 
    return z  # returns superimposed irregular wave.

FreqR = 1.5 # htz or (9 rad/s ish)
AmplR = 1 # currently unit amplitude to measure the error
Size = 12 # sample size, also used to move around frequencies

E_OR = 20 # time over wich signal is measured (max values is about 120 ish)
Fs = 6 # Sample rate in Hz

t_OR = np.arange(0,E_OR,1/Fs) # starts at 0, ends at E, steps by 1/Fs

data_OR = gendata(FreqR,AmplR,Size)
z = pltsignal(data_OR[0],data_OR[1],data_OR[2],t_OR) # original signal ( simulation of mesurment )

# anaysisng the signal using NumPy fft, then scaling and finding peaks.
Ramp = np.fft.fft(z) #real aplitudes used to find phase
Rfeq = np.fft.fftfreq(z.shape[-1]) #real frequency domain to find phase


信号模型:

$$ x \left( t \right) = \sum_{i = 1}^{M} {a}_{i} \cos \left( 2 \pi {f}_{i} t + {\phi} _{i} \right) + n \left( t \right) $$

其中$ M, {\left\{ {a}_{i} \right\}}_{i = 1}^{M}, {\left\{ {f}_{i} \right\}}_ {i = 1}^{M}, {\left\{ {\phi}_{i} \right\}}_{i = 1}^{M} $是未知参数,$ n \left( t \右)$是加性高斯白噪声(AWGN)。

可以假设:

  1. SNR 非常非常高。
  2. 观察时间为~ 120 [Sec]
  3. 信号数量为 1-20。
  4. 频率高达2 [Hz].
  5. 频率之间的差距可以小到0.005 [Hz]
4个回答

求解实谐波信号的线性组合

数据模型由下式给出:

$$ x \left( t \right) = \sum_{i = 1}^{M} {a}_{i} \sin \left( 2 \pi {f}_{i} t + {\phi} _{i} \right) + n \left( t \right) $$

其中$ M, {\left\{ {a}_{i} \right\}}_{i = 1}^{M}, {\left\{ {f}_{i} \right\}}_ {i = 1}^{M}, {\left\{ {\phi}_{i} \right\}}_{i = 1}^{M} $是未知参数,$ n \left( t \右)$是加性高斯白噪声(AWGN)。

在此处输入图像描述

估计上述参数需要 4 步解决方案:

  1. 估计模型阶数 ( $M$ )/信号数。
  2. 使用直接法准确估计频率(给定频率数)。
  3. 在给定频率的情况下估计模型的幅度和相位(通常使用非线性最小二乘求解器)。
  4. 估计所有幅度、频率和相位(通常使用非线性最小二乘求解器)。

以上仅适用于非常非常高的 SNR 情况。对于低信噪比,这个问题可能是无法解决的(当然对于大的$ M $)。

如果频率分布良好(彼此相距较远),则可以使用 DFT ( ) 实现步骤 (1) 和 (2 fft())。
然而,如果频率在最好的情况下接近,频率的估计将不准确(来自频率附近的泄漏将移动峰值),在更糟糕的情况下,频率将被混合,信号的数量和信号的数量都不会。频率是可以实现的。

在此处输入图像描述

在上图中,我使用了 12 [Sec] 的观察时间和 4 个频率为[0.95; 1.00; 1.05; 1.10][Hz] 的信号。由于我在您的问题中花费了 1 / 10 的观察时间,因此它相当于在 120 [秒] 观察时间内频率相差 0.005 远。我只是想处理更少的样本。

如上图所示,在 DFT 中,无法估计信号的数量,当然也无法提取频率。

但是使用超分辨率方法可以解决这种情况!
请参阅以下部分中有关频域超分辨率的一些信息。

然而对于这种情况,我使用了不同的方法来实现我无法透露的超分辨率(我作为顾问在商业上使用)。
我还开发了一种估计信号数量的方法。

在上述情况下,我的方法确实估计了 4 个信号,估计的频率是[0.9503, 1.0016, 1.0483, 1.0996][Hz]。

然后应用接下来的两个步骤,我可以进一步改善结果:

在此处输入图像描述

从上面可以看出,估计的信号几乎与地面实况(具有已知参数且无噪声的模型)完全一致。

确实,这里的 SNR 非常高,但如果没有超分辨率方法,它仍然无法工作。

完整代码可在我的StackExchange Signal Processing Q74024 GitHub 存储库中找到(查看SignalProcessing\Q74024文件夹)。不幸的是,我不能透露参数个数估计和超分辨率的代码。

频域超分辨率

旧的非现代方法将使用一些在大多数情况下不足的窗口技巧。您真正需要的是超分辨率。超分辨率基本上相当于一种主瓣很窄,几乎没有旁瓣的“窗口法”。

您可以在频域中使用压缩感知/稀疏表示来实现超分辨率。

一种方法是解决问题:

$$ \arg \min_{\boldsymbol{x}} \frac{1}{2} {\left\| F \boldsymbol{x} - \boldsymbol{y} \right\|}_{2}^{2} + \lambda {\left\| \boldsymbol{x} \right\|}_{1} $$

在这种情况下,超分辨率意味着能够解析比观察时间建议的频率更接近的频率(克服泄漏问题):

在此处输入图像描述

在上面,您可以看到给定频率的 2 个正弦之和的 DFT。高斯模型使用$ {L}_{2} $进行正则化(基本上是阻尼零填充)。

您可能会看到$ {L}_{1} $可以解决两个正弦波,即使它们仅相隔 0.5 [Hz],观察窗口为 1 [Sec]。

答案取自我对使用压缩传感的频域超分辨率的回答。

申请ssq_cwtextract_ridges我得到以下。可以通过更好的窗口化、更多的样本来改进。可以在幅度图上应用平滑,使其更易于解释,而不会损失太多精度。

import numpy as np
from ssqueezepy import ssq_cwt, extract_ridges
from ssqueezepy.visuals import plot, imshow

# z = see OP's code; used np.random.seed(1000)

beta = 24
Tx, Wx, ssq_freqs, scales, *_ = ssq_cwt(z, ('gmw', {'beta': beta}), padtype='zero', fs=6)
ridge_idxs = extract_ridges(Tx, scales, penalty=20)

plot(ridge_idxs, color='k', linestyle='--', xlims=(0, len(z) - 1))
imshow(Tx, abs=1, yticks=ssq_freqs[::-1], ylabel="Frequencies [Hz]",
       title="abs(SSQ_CWT), wavelet=('gmw', {'beta': %s})" % beta)
amplitude = np.abs(Tx[ridge_idxs[:, 0], np.arange(len(z))])
frequencies = ssq_freqs[::-1][ridge_idxs[:, 0]]

plot(amplitude, ylims=(0, None), title="Amplitude vs time, SSQ ridge", show=1)
plot(frequencies, ylims=(0, None), ylabel="Frequencies [Hz]", 
     title="Frequency vs time, SSQ ridge", show=1)

处理频谱泄漏的标准方法是时域加窗。这涉及到相当多的权衡:主瓣宽度、旁瓣峰值和分布、阻带衰减等。这些权衡是通过选择窗口类型和窗口参数(如果适用)来控制的。

最好的权衡是,实际上取决于您的特定应用要求和信号类型。

FFT 对这项任务的装备很差$^{1}$ ; 时频定位,如 STFT 或 CWT,是首选。可以进一步细化所述表示以通过同步压缩随时间追踪频率幅度。

1:我最初将问题理解为跟踪(非平稳)信号的瞬时幅度和频率调制,事实并非如此;见下文。

ssqueezepy提供上述所有功能,并提供多种小波和窗口选择 - 包括脊提取。另请参阅此处的变换和小波选择的比较- 示例:

在此处输入图像描述


更新:OP的模型由

$$ \sum_{i=0}^{M} a_i \cos(2\pi f_i t + \phi_i) $$

其中$\phi_i$是随机的。即,具有相位扰动的固定幅度、固定频率正弦曲线的总和。事实上,FFT 已经为此做好了准备,绕过分辨率限制的“智能 FFT”可能会比时频定位更好,因为后者使用受经典分辨率限制的带通滤波器。

(我不能肯定地说,因为我不理解“超分辨率”;同步压缩也以自己的方式“提高”分辨率,最明显的优势是冗余,可以实现更强大的分析方法。)