如何获取FFT中每个值的频率?

177

我有一个FFT结果。这些结果储存在两个双精度数组中:实数部分数组和虚数部分数组。如何确定对应于这些数组中每个元素的频率?

换句话说,我想创建一个数组,用于存储FFT每个实数和虚数分量的频率。


我用C#.net做这个。你能帮我吗? - Rango
9
如果你不理解FFT的实部和虚部的相关性,那么你将无法得到任何有意义的结果,因此你应该寻找一些FFT和信号处理教程,以了解如何解释结果。我认为,无论你使用它做什么,你都需要得到FFT的幅度或功率谱密度。 - the_mandrill
谢谢!我想要获得每个帧的峰值频率(帧长度取决于窗口长度和移动长度)。 - Rango
5个回答

428

FFT中的第一个bin是直流分量(0 Hz),第二个bin是Fs / N,其中Fs是采样率,N是FFT的大小。下一个bin是2 * Fs / N。通常表达式为,第n个bin为n * Fs / N

因此,如果您的采样率Fs为44.1 kHz,FFT大小N为1024,则FFT输出bin的位置为:

  0:   0 * 44100 / 1024 =     0.0 Hz
  1:   1 * 44100 / 1024 =    43.1 Hz
  2:   2 * 44100 / 1024 =    86.1 Hz
  3:   3 * 44100 / 1024 =   129.2 Hz
  4: ...
  5: ...
     ...
511: 511 * 44100 / 1024 = 22006.9 Hz
请注意,对于实数输入信号(虚部全部为零),FFT 的第二半部分(从 N / 2 + 1N - 1 的频率点)不包含任何有用的额外信息(它们与前 N / 2 - 1 个频率点具有共轭对称性)。最后一个有用的频率点(对于实际应用)在于 N / 2 - 1,它对应于上面示例中的 22006.9 Hz。位于频率点 N / 2 的频率代表奈奎斯特频率处的能量,即 Fs / 2(在这个示例中为 22050 Hz),但是这通常没有任何实际用途,因为抗混叠滤波器通常会衰减任何在 Fs / 2 及以上频率处的信号。

1
啊,如果我在这些复数(结果的实部和虚部)上使用sqrt(realreal + imaginaryimaginary),那么它会返回频率(Hz)吗? - Rango
2
@user532017:不是 - sqrt(re*re+im*im) 将是给定 bin 频率处信号的 幅度。如上所述,bin 的频率由其索引确定。如果您找到具有最大 幅度bin,则可以从上面的索引确定此峰值的 频率 - Paul R
29
@PaulR - 感谢您多年来提供给我的这个出色的答案。在我还没有StackOverflow账户的时候,我就会查看这个答案,但一旦注册后就忘记了感谢您。最近我在看FFT相关的东西,想起了您的答案,刚刚又访问了一下。一到这里,我就记得要感谢您……所以非常感谢!每当我与别人争论如何解释FFT水平轴上的每个点时,我就指向这个链接。 - rayryeng
9
非常感谢你。我认为这是我在 Stack Overflow 回答问题五年以来收到的最好的认可! - Paul R
1
@Mike'Pomax'Kamermans:将信号的二进制0分量视为平均值,也就是算术平均数(从数学上讲,这正是它的含义)。从电子或音频角度来看,这是直流分量。不过你说得对,这个分量也会包含一些非常低频信号的能量。 - Paul R
显示剩余11条评论

58
请看我在这里的回答。 回复评论: FFT实际上计算输入信号与正弦和余弦函数(基函数)在一系列等间距频率下的互相关。对于给定的FFT输出,根据我发布的答案,有一个相应的频率(F)。输出样本的实部是输入信号与cos(2*pi*F*t)的互相关,虚部是输入信号与sin(2*pi*F*t)的互相关。将输入信号与sincos函数相关联的原因是为了考虑输入信号和基函数之间的相位差异。
通过取复杂FFT输出的幅度,您可以衡量输入信号与一组频率下的正弦波的相关程度,而不考虑输入信号的相位。如果您只分析信号的频率内容,则几乎总是要取FFT的复杂输出的幅度或幅度平方。

实部和虚部是FFT的结果,用于什么?请为我解释一下。谢谢。 - Rango
1
复数输出的幅度是否需要每次加倍?(如果我将其限制在下半部分的解释) - Wolf
1
@Wolf:DFT使用复指数。实值信号是cos函数的和,指数以共轭对出现(查找“逆欧拉公式”以获取cos函数),使得数学频谱关于0 Hz对称。通常不需要处理负共轭,通过欧拉公式可以将频谱转换回实际的cos函数。在实际中,它包括将正组分的幅度加倍,除了DC(+/-0 Hz)。请注意,负频率指数仅是数学对象,实际(cos)信号仅具有正频率。 - mins

23

我已经使用了以下代码:

public static double Index2Freq(int i, double samples, int nFFT) {
  return (double) i * (samples / nFFT / 2.);
}

public static int Freq2Index(double freq, double samples, int nFFT) {
  return (int) (freq / (samples / nFFT / 2.0));
}

输入如下:

  • i: 要访问的二进制数据
  • samples: 采样率,单位为赫兹(例如8000赫兹,44100赫兹等)
  • nFFT: FFT向量的大小

19
被接受的答案说应该是 i * samples / nFFT。为什么要多加一个 2 呢?我有什么遗漏吗? - yati sagade
如果你的采样频率是44100Hz,那么你实际上能够获得的最高频率是一半,也就是22050Hz。这被称为奈奎斯特频率(请查看维基百科以获取详细信息)。人类听觉系统无法感知大于约20kHz的任何频率,这就是为什么44.1kHz是最常见的采样率的原因。 - N4ppeL
2
此外,我认为在这种情况下,“nFFT”仅涉及实际FFT的一半(许多函数都是这样做的,因为第二个部分只是第一个部分的镜像)。 - N4ppeL

17

对于大小为N的复数输入,FFT输出系数从0到N-1按[LOW,MID,HI,HI,MID,LOW]频率分组。

我认为k处的元素与N-k处的元素具有相同的频率,因为对于实数数据,FFT [N-k] = FFT [k]的复共轭。

从低到高频率扫描的顺序是

0,

 1,
 N-1,

 2,
 N-2

 ...

 [N/2] - 1,
 N - ([N/2] - 1) = [N/2]+1,

 [N/2]

从索引 i=0 到 [N/2],有[N/2]+1组频率,每组的频率为frequency = i * SamplingFrequency / N

因此,FFT[k]中的频率为:

if k <= [N/2] then k * SamplingFrequency / N
if k >= [N/2] then (N-k) * SamplingFrequency / N

5

你的第k个FFT结果的频率为2*pi*k/N。


7
我猜这将会以弧度为单位。 - Barnaby

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