为什么使用FFT在信号中舍入频率值?

时间:2019-02-15 17:19:19

标签: python numpy fft

因此,我试图弄清楚如何在实践中使用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)不同。我在做什么错以及如何解决?

3 个答案:

答案 0 :(得分:2)

DFT结果仓的频率间隔为Fs / N,其中N为FFT的长度。因此,DFT窗口的持续时间会限制DFT结果仓频率中心间隔的分辨率。

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

答案 1 :(得分:2)

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

enter image description here 图1:即使在应用了Tuckey窗口之后,窗口信号的DFT也并非狄拉克之和:在峰的底部仍然存在一些频谱泄漏。在估算频率时必须考虑此功率。

另一个问题是帧的长度不是信号周期的倍数,该信号周期可能不是周期性的。尽管如此,DFT的计算就像信号是周期性的,但在帧的边缘是不连续的。它会引起被称为光谱泄漏的杂散频率。窗口化是处理此类问题并减轻与人为间断相关的问题的参考方法。实际上,窗口的值在帧的边缘附近连续减小到零。 There is a list of window functions中提供了scipy.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 :(得分:1)

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