我怎样才能让我的 FFT 峰值正好在我的信号频率上?

How can I have my FFT peaks, exactly at the frequency of my signal?

我在这里看到了下面的代码,但是如果你绘制它并缩放它,频率峰值并不完全在 50Hz 和 80Hz 上;他们有点偏离。有人知道为什么会这样吗?如何使峰值正好在 50Hz 和 80Hz 之间?

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 600
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)

fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N//2]))
plt.show()

这是一个简单的近似错误,因为 xf 没有假定值 50 and/or 80。如果您更改 N and/or 您的 linspace() 参数,您可以使 xf 准确地在 5080 处进行采样,并且您看不到峰值位移:

import numpy as np
import matplotlib.pyplot as plt
import scipy.fftpack

# Number of samplepoints
N = 80
# sample spacing
T = 1.0 / 800.0
x = np.linspace(0.0, N*T, N)
y = np.sin(50.0 * 2.0*np.pi*x) + 0.5*np.sin(80.0 * 2.0*np.pi*x)
yf = scipy.fftpack.fft(y)
xf = np.linspace(0.0, 1.0/(2.0*T), N//2 + 1)

fig, ax = plt.subplots(figsize=(10, 10))
ax.plot(xf, 2.0/N * np.abs(yf[:N//2 + 1]))
ax.plot([50, 50], [0, 1])
ax.plot([80, 80], [0, 1])
plt.show()

xf:

[  0.  10.  20.  30.  40.  50.  60.  70.  80.  90. 100. 110. 120. 130.
 140. 150. 160. 170. 180. 190. 200. 210. 220. 230. 240. 250. 260. 270.
 280. 290. 300. 310. 320. 330. 340. 350. 360. 370. 380. 390. 400.]

如果正弦波在 FFT 长度内正好是整数周期,则纯 un-modulated 正弦波的频率仅显示为单个 FFT bin 峰值结果。

否则,即使频率与 FFT 的周期性基向量稍有不同,也会出现开窗效应,有时称为 "leakage"。

如果您希望 non-interpolated FFT 峰值幅度恰好处于信号频率,则必须将 FFT 的长度更改为信号周期的精确整数倍(或更改您的信号频率)。

尽管这已经有一个例外答案:

如果需要知道峰值的精确频率,有一些简单的方法无需尝试 trim 采样率。如果您不知道要测量的频率,您会怎么做。所以这不是显示问题的答案,而是 "real life" 检测问题的答案。假设其他峰 "far" 远,有一个使用高斯 window 的简单方法。在对数尺度上,峰将呈抛物线形状,这样三个最高点正好提供最大值的实际位置。这可以转化为一个简单的程序,只求解一个线性方程。这是我用于分析光谱的 class 的一部分:

    def get_base_frequency( self ):
        """
        uses the fact that the fourier of a gaussion is a gaussian.
        folding the virtual deltapeak of a frequency results in a gaussian.
        taking the log and looking at two consecutive bins, preferably near the maximum, 
        allows to calculate the position of the maximum, i.e. the frequency
        (square terms of the gaussians cancel and a linear equation is solved)
        """
        sigma = self.wavePoints / 8.0 ##my choice ... seems to work well
        tau = self.wavePoints / ( 2.0 * np.pi * sigma )
        pf, ps = self.spectrum_single_sided( windowType=[ 'Gaussian', sigma ] )
        ### fft providing freqency and spectral data
        n = np.argmax( ps ) - 1
        slg = tau**2 * np.log( ps[ n + 1 ] / ps[ n ] )
        better = slg + n + 0.5
        return better * self.sampleRate / self.wavePoints

就是这样。

如果需要峰值高度,我建议使用平顶进行第二个 fft windowing。