FFT算法的混淆

7
我正在尝试理解FFT算法,目前我认为我理解了它背后的主要概念。但是,我对“framesize”和“window”之间的区别感到困惑。
根据我的理解,它们似乎是多余的?例如,我将一个帧大小为1024的样本块作为输入呈现。因此,我有byte[1024]作为输入。
那么,窗口函数的目的是什么?最初,我认为窗口函数的目的是从原始数据中选择样本块。
谢谢!
3个回答

14
那么窗口函数的目的是什么呢?它用于处理所谓的“频谱泄漏”:FFT假定一个无限系列,重复给定的样本帧。如果您有一个正弦波在样本帧内是整数个周期,则一切正常,FFT会在正确的频率处给出一个漂亮的窄峰值。但是,如果您有一个正弦波不是整数个周期,则最后一个和第一个样本之间存在不连续性,FFT会给出虚假的谐波。

窗口函数降低了样本帧开头和结尾的振幅,以减少由此不连续性引起的谐波。

来自National Instruments窗口函数网页的一些示意图:

整数个周期:

enter image description here

非整数周期:

enter image description here

获取更多信息:

http://www.tmworld.com/article/322450-Windowing_Functions_Improve_FFT_Results_Part_I.php

http://zone.ni.com/reference/en-XX/help/371361B-01/lvanlsconcepts/char_smoothing_windows/

http://www.physik.uni-wuerzburg.de/~praktiku/Anleitung/Fremde/ANO14.pdf


啊,我明白了。所以基本上,窗函数只是为了“修改”或改善样本数据,以提高FFT算法的结果。我是对的吗?谢谢! - user488792
@user488792:是的,基本上就是这样 - 窗口函数平滑了采样窗口边缘可能出现的瞬态。 - Paul R
是的,我明白了。非常感谢您的澄清和提供如此棒的答案! - user488792
@eryksun:请再读一遍我的回答。FFT会一遍又一遍地重复采样帧。对于非整数采样,你会得到一个跳跃不连续性,这意味着它不是一个纯正弦波,因此必须存在谐波。如果正弦波的周期是整数,为什么会有任何泄漏呢? - Jason S

7
一长为M的矩形窗口具有频率响应sin(ω*M/2)/sin(ω/2),当ω = 2*π*k/M时,其中k≠0时为零。对于长度为N的DFT,其中ω = 2*π*n/N,在n = k * N/M处存在空值。N/M的比值不一定是整数。例如,如果N = 40,M = 32,则在1.25的倍数处存在空值,但只有整数倍数会出现在DFT中,在这种情况下是5、10、15和20。以下是32点矩形窗口的1024点DFT绘图:
M = 32
N = 1024
w = ones(M)
W = rfft(w, N)
K = N/M
nulls = abs(W[K::K])
plot(abs(W))
plot(r_[K:N/2+1:K], nulls, 'ro')
xticks(r_[:512:64])
grid(); axis('tight')

32点矩形窗口的DFT

注意每个 N/M = 32 个bin处的零点。如果N=M(即窗口长度等于DFT长度),则除n=0外,所有bin处都有零点。

当你将一个窗口乘以一个信号时,在频域中对应的操作是窗口谱与信号谱的循环卷积。例如,正弦波的DTFT是位于正负频率上的加权δ函数(即高度无限、极小的延伸和有限面积的脉冲)。将一个频谱与δ函数卷积只会将其移动到δ的位置,并按δ的权重进行缩放。因此,当你在采样域中将一个窗口乘以一个正弦波时,窗口的频率响应被缩放并移动到正弦波的频率。

关于矩形窗口长度的问题,有几种情况需要考虑。首先让我们看一下窗口长度是正弦波周期的整数倍的情况,例如,一个周期为32/8=4个样本的余弦函数的32个样本矩形窗口:

x1 = cos(2*pi*8*r_[:32]/32)    # ω0 = 8π/16, bin 8/32 * 1024 = 256
X1 = rfft(x1 * w, 1024)
plot(abs(X1))
xticks(r_[:513:64])
grid(); axis('tight')

使用32点矩形窗口在w0 = 8π/16时的cosine的DFT

与以前一样,N/M的倍数为32的地方存在空洞。但是窗口的频谱已经被移动到正弦波的256个bin,并按其幅度缩放,幅度在正频率和负频率之间平分(我只绘制正频率)。如果DFT长度为32,则空洞将在每个bin处排列,引发“看上去不存在泄漏”的外观。但这种误导性的外观只与DFT长度有关。如果您用零填充窗口信号(如上所示),则可以查看空洞之间频率上的sinc响应。

现在让我们来看一个窗口长度不是正弦波周期的整数倍的情况,例如角频率为7.5π/16的cosine(周期为64个样本):

x2 = cos(2*pi*15*r_[:32]/64)    # ω0 = 7.5π/16, bin 15/64 * 1024 = 240
X2 = rfft(x2 * w, 1024)
plot(abs(X2))
xticks(r_[-16:513:64])
grid(); axis('tight')

使用32点矩形窗口进行w0 = 7.5π/16的余弦DFT,中心频率偏移至240,不再是32的整数倍。下面我们来看看相应的32点DFT会是什么样子(推断使用32点矩形窗口)。我将计算并绘制x2 [n]的32点DFT,并叠加一个32倍抽取的1024点DFT的副本:

X2_32 = rfft(x2, 32)
X2_sample = X2[::32]
stem(r_[:17],abs(X2_32))
plot(abs(X2_sample), 'rs')    # red squares
grid(); axis([0,16,0,11])

w0 = 7.5π/16时cosine的32点DFT

从上图可以看出,空洞不再在32的倍数处对齐,因此32点DFT的幅度在每个频率点都是非零的。在32点DFT中,窗口的空洞仍然每隔N/M = 32/32 = 1个频率点出现一次,但由于ω0 = 7.5π/16,中心位于第7.5个“频率点”,因此空洞位于0.5、1.5等位置,因此它们不会出现在32点DFT中。

总体来说,窗函数信号的频谱泄漏总是存在的,但如果信号频谱、窗口长度和DFT长度恰好排列成一条直线,则可以在DFT中掩盖这些泄漏。除此之外,应该忽略这些DFT伪像,并集中精力研究信号的DTFT(即通过填充零以更高分辨率采样DTFT,以便清晰地检查泄漏)。

由于卷积窗口的频谱引起的频谱泄漏始终存在,因此制作特定形状的窗口的艺术非常重要。每种窗口类型的频谱都经过了特定任务的调整,例如动态范围或灵敏度。

这里是一个比较矩形窗口和汉明窗口输出的示例:

from pylab import *
import wave

fs = 44100
M = 4096
N = 16384
# load a sample of guitar playing an open string 6
# with a fundamental frequency of 82.4 Hz
g = fromstring(wave.open('dist_gtr_6.wav').readframes(-1),
               dtype='int16')
L = len(g)/4
g_t = g[L:L+M]
g_t = g_t / float64(max(abs(g_t)))
# compute the response with rectangular vs Hamming window
g_rect = rfft(g_t, N)
g_hamm = rfft(g_t * hamming(M), N)

def make_plot():
    fmax = int(82.4 * 4.5 / fs * N)  # 4 harmonics
    subplot(211); title('Rectangular Window')
    plot(abs(g_rect[:fmax])); grid(); axis('tight')
    subplot(212); title('Hamming Window')
    plot(abs(g_hamm[:fmax])); grid(); axis('tight')

if __name__ == "__main__":
    make_plot()

rectangular vs hamming window


那是什么程序?(看起来像Python,但它在哪个环境下?)我一直在寻找MATLAB的替代品。 - Jason S

2
如果您不修改样本值,并选择与FFT长度相同的数据长度,则相当于使用矩形窗口,在这种情况下,帧和窗口是相同的。然而,在时间域内将输入数据乘以矩形窗口等同于在频率域中卷积输入信号的频谱与Sinc函数,这将使任何不完全周期的频率的光谱峰值分布在整个频谱上。
非矩形窗口通常用于使得结果FFT频谱与Sinc函数相比更加“聚焦”。
您还可以使用与FFT长度或孔径不同的矩形窗口。在数据窗口较短的情况下,FFT帧可以进行零填充,这可能会导致外推FFT结果频谱看起来更加平滑。您甚至可以使用比FFT长度更长的矩形窗口,通过将数据环绕FFT孔径以一种总和循环的方式进行,从而产生一些有趣的频率分辨率效果。
由于请求添加:
在时间域内乘以窗口的结果与在频域中卷积该窗口的变换产生的结果相同。
一般来说,时间域窄窗口会产生一个宽的频率域变换。这就是为什么零填充会产生平滑的频率图的原因。时间域窄窗口会产生一个更宽的Sinc函数,相对于帧宽度来说曲线更加宽和平滑,比起相同帧长度的非零填充FFT,从而使得插值的频率结果看起来更加平滑。
反之亦然。更宽的矩形窗口将产生一个更窄的Sinc函数,空洞靠近峰值。因此,您可以使用精心选择的较宽窗口,以产生更窄的Sinc函数,以在离感兴趣的频率不到1个频率bin的位置处抵消频率。如何使用更宽的窗口?将数据环绕并求和,这与使用未被截断为1个FFT帧长度的FT基向量是相同的。但是,由于在执行此操作时FFT结果向量比数据短,因此这是一种有损过程,会引入人工制品,并引入一些新的新颖混叠。但它将为每个bin提供更清晰的频率选择峰值和可放置在不到1个bin之间(例如,在bin之间的中点)的陷波滤波器等。

这是一个更长的FFT,它产生更高的频率分辨率,不只是对相同长度的FFT进行零填充,这不会改变分辨率,但会改变“平滑度”。 FFT的长度和零填充量是两个不同的参数或“旋钮”,您可以独立调整它们。 - hotpaw2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接