Skip to main content
 首页 » 编程设计

python之为什么使用 FFT 对信号中的频率值进行四舍五入

2025年05月04日21zdz8207

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

请您参考如下方法:

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

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

另一个问题是帧的长度不是信号周期的倍数,无论如何它可能不是周期性的。然而,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 精度。