缩小 FFT 频谱范围

Reducing a FFT spectrum range

我目前正在 运行ning Python 对 44100Hz 音频样本进行 Numpy fft,这使我的工作频率范围为 0Hz - 22050Hz(感谢 Nyquist)。一旦我对这些时域值使用 fft,我的 fft 频谱中就有 128 个点,每个频率仓大小为 172Hz。

我想将频率仓收紧到 86Hz,并且仍然保持只有 128 个 fft 点,而不是通过调整我创建样本的方式将我的 fft 计数增加到 256 个。

我的问题是这在理论上是否可行。我的想法是仅对 0Hz 到 11025Hz 之间的任何 Hz 值进行 运行 fft。反正我不在乎以上的任何事情。这会将我的工作频谱削减一半,并将我的频率仓置于 86Hz,同时保持我的 128 个频谱仓。也许这可以通过时域中的 window 函数来实现?

目前我用来创建样本然后转换为 fft 的代码是:

import numpy as np

sample_rate = 44100
chunk = 128
record_seconds = 2

stream = self.audio.open(format=pyaudio.paInt16, channels=1,
                        rate=sample_rate, input=True, frames_per_buffer=6300)

sample_list = []

for i in range(0, int(sample_rate / chunk * record_seconds)):
    data = stream.read(chunk)
    sample_list.append(np.fromstring(data, dtype=np.int16))

### then later ###:

for samp in sample_list:
        samp_fft = np.fft.fft(samp) ...

我希望我的措辞足够清楚。如果我需要调整我的解释或术语,请告诉我。

将 FFT 的大小加倍是显而易见的事情,但如果您有充分的理由不能这样做,那么请考虑在 FFT 之前进行 2 倍下采样,以使有效采样率降至 22050赫兹:

- Apply low pass filter with cut off at 11 kHz
- Discard every other sample from filtered output
- Apply FFT to down-sampled data

您所要求的是不可能的。正如您在评论中提到的,您需要很短的时间 window。我假设这是因为您正在尝试检测信号何时到达特定频率(因为我已经回答了您之前关于该主题的问题)并且您希望检测对时间敏感。但是,您的 bin 大小似乎对您的要求来说太大了。

只有两种方法可以减小 bin 大小。 1)增加FFT的长度。不幸的是,这也意味着需要更长的时间来获取数据。 2) 降低采样率(通过采样率转换或在硬件级别),但由于样本到达速度较慢,因此采集数据也需要更长的时间。

我将向您建议第三个选项(根据我从中收集到的内容和您的其他问题可能是更好的解决方案),即:在时域中执行频率检测。这将需要一个时域带通滤波器,然后是一个 RMS 表。在实现方面,这将是一个或多个双二阶滤波器,您可以在 python 中为滤波器实现 - 可能已经有可用的实现。棘手的部分是设计过滤器,但我很乐意在聊天中帮助您。 RMS 表基本上是对滤波器输出样本的平方和求平方根。

如果您不想解决紧邻的频率峰值或噪声之间的问题,那么,对于频率仓间距的一半,您可以对数据进行零填充以使 FFT 长度加倍,而无需等待更多数据。然后,如果您只想要频率范围的下半部分 0..Fs/2,只需丢弃 FFT 结果向量的中间一半(这通常比尝试计算频率的下半部分更有效通过非 FFT 方式的范围)。

请注意,零填充给出与高质量插值相同的结果(如平滑原始 FFT 结果点的图)。它不会增加峰分离分辨率,但如果噪音水平足够低,可能会更容易在图中挑选出更精确的峰位置。