【问题标题】:Why are frequency values rounded in signal using FFT?为什么使用 FFT 对信号中的频率值进行四舍五入?
【发布时间】:2019-07-09 21:11:10
【问题描述】:

所以,我想弄清楚如何在实践中使用 DFT 来检测信号中的普遍频率。我一直在试图弄清楚傅里叶变换是什么以及 DFT 算法是如何工作的,但显然我还有很长的路要走。我编写了一些代码来生成信号(因为目的是处理音乐,所以我生成了一个大 C 和弦,因此产生了奇怪的频率值),然后尝试回到频率数字。这是我的代码

sr = 44100 # sample rate
x = np.linspace(0, 1, sr) # one second of signal
tpi = 2 * np.pi
data = np.sin(261.63 * tpi * x) + np.sin(329.63 * tpi * x) + np.sin(392.00 * tpi * x)
freqs = np.fft.fftfreq(sr)
fft = np.fft.fft(data)
idx = np.argsort(np.abs(fft))
fft = fft[idx]
freqs = freqs[idx]
print(freqs[-6:] * sr)

这给了我[-262. 262. -330. 330. -392. 392.] ,这与我编码的频率(261.63、329.63 和 392.0)不同。我做错了什么,我该如何解决?

【问题讨论】:

  • 好吧,你还没有说你希望看到的,所以不可能说。
  • 从我的描述中我认为这相当清楚。我希望看到我输入信号的频率,261.63、329.63 和 392
  • @MadWombat 因为您的存储桶(样本)与以 Hz 为单位的采样率一样多,因此最大理论分辨率为 1Hz - 因此,如果没有更多样本,这实际上是不可能的。
  • @MadWombat,您更新了代码,但没有更新您声明的输出。
  • @MadWombat 如果您没有足够的 samples,则更高的采样率没有任何影响。可用频率桶的最大数量为number of samples / 2(也有负分量)

标签: python numpy fft


【解决方案1】:

确实,如果帧持续T 秒,则 DFT 的频率为k/T Hz,其中 k 是整数。因此,只要将这些频率识别为 DFT 幅度的最大值,过采样就不会提高估计频率的准确性。相反,考虑到持续 100 秒的较长帧会导致 DFT 频率之间的间隔为 0.01Hz,这可能足以产生预期的频率。 通过将峰值的频率估计为相对于功率密度的平均频率,可以得到更好的结果。

图 1:即使应用了 Tuckey 窗,加窗信号的 DFT 也不是狄拉克之和:在峰值底部仍然存在一些频谱泄漏。在估计频率时必须考虑此功率。

另一个问题是帧的长度不是信号周期的倍数,它可能无论如何都不是周期性的。然而,DFT 的计算就像信号是周期性的但在帧的边缘是不连续的。它会引起被描述为频谱泄漏的杂散频率。加窗是处理此类问题并缓解与人为不连续性相关的问题的参考方法。实际上,窗口的值在帧边缘附近不断减小到零。 There is a list of window functionsscipy.signal 中提供了许多窗口功能。一个窗口被应用为:

tuckey_window=signal.tukey(len(data),0.5,True)
data=data*tuckey_window

此时,出现最大幅度的频率仍然是 262、330 和 392。应用窗口只会使峰值更明显:窗口信号的 DFT 具有三个不同的峰值,每个峰值都有一个中心瓣和一个旁瓣,取决于窗口的 DFT。 这些窗口的波瓣是对称的:因此,中心频率可以计算为峰值的平均频率,相对于功率密度。

import numpy as np
from scipy import signal
import scipy

sr = 44100 # sample rate
x = np.linspace(0, 1, sr) # one second of signal
tpi = 2 * np.pi
data = np.sin(261.63 * tpi * x) + np.sin(329.63 * tpi * x) + np.sin(392.00 * tpi * x)

#a window...
tuckey_window=signal.tukey(len(data),0.5,True)
data=data*tuckey_window

data -= np.mean(data)
fft = np.fft.rfft(data, norm="ortho")

def abs2(x):
        return x.real**2 + x.imag**2

fftmag=abs2(fft)[:1000]
peaks, _= signal.find_peaks(fftmag, height=np.max(fftmag)*0.1)
print "potential frequencies ", peaks

#compute the mean frequency of the peak with respect to power density
powerpeak=np.zeros(len(peaks))
powerpeaktimefrequency=np.zeros(len(peaks))
for i in range(1000):
    dist=1000
    jnear=0
    for j in range(len(peaks)):
        if dist>np.abs(i-peaks[j]):
             dist=np.abs(i-peaks[j])
             jnear=j
    powerpeak[jnear]+=fftmag[i]
    powerpeaktimefrequency[jnear]+=fftmag[i]*i


powerpeaktimefrequency=np.divide(powerpeaktimefrequency,powerpeak)
print 'corrected frequencies', powerpeaktimefrequency

由此产生的估计频率为 261.6359 Hz、329.637Hz 和 392.0088 Hz:它比 262、330 和 392Hz 好得多,并且满足这种纯无噪声输入信号所需的 0.01Hz 精度。

【讨论】:

    【解决方案2】:

    DFT 结果 bin 在频率上由 Fs/N 分隔,其中 N 是 FFT 的长度。因此,DFT 窗口的持续时间限制了 DFT 结果箱频率中心间距方面的分辨率。

    但是,对于低噪声(高 S/N)中分离良好的频率峰值,您可以通过在 DFT 结果之间插值 DFT 结果来估计频率峰值位置,而不是增加数据的持续时间垃圾箱。您可以尝试抛物线插值来进行粗略的频率峰值位置估计,但加窗 Sinc 插值(本质上是 Shannon-Whittaker 重建)将提供更好的频率估计精度和分辨率(假设感兴趣的频率峰值周围的噪声基底足够低,例如,在您的人工波形案例中没有附近的正弦曲线)。

    【讨论】:

      【解决方案3】:

      由于您希望获得 0.01 Hz 的分辨率,因此您需要对至少 100 秒的数据进行采样。您将能够解析高达约 22.05 kHz 的频率。

      【讨论】:

      • 我想知道。是否可以以限制频率范围为代价修改 DFT 算法以提高精度?
      • 您可以通过降低采样率来缩小范围。只要您采样 100 秒,您仍然可以获得 0.01 Hz 的分辨率。
      • 只有在S/N低的情况下需要增加长度。否则,可以进行插值以获得更高的分辨率。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多