VB FFT - 对结果与频率之间的关系理解不清

3

我正在使用(窃取)(回收利用)的 fft(快速傅里叶变换)例程,试图理解它。输入是一个包含 512 个数据点的数组,这些数据点是样本波形。测试数据生成到该数组中。fft 将此数组转换为频域。试图理解频率、周期、采样率和 fft 数组中的位置之间的关系。我将举几个例子来说明:

========================================

采样率为 1000 次/秒。 在 10Hz 下生成一组样本。

输入数组在 arr(28)、arr(128)、arr(228) 处具有峰值... 周期=100 个采样点

fft 数组中的峰值位于索引 6 处(不包括 0 处的巨大值)

========================================

采样率为 8000 次/秒 在 440Hz 下生成一组样本

输入数组峰值包括 arr(7)、arr(25)、arr(43)、arr(61) ... 周期=18 个采样点

fft 数组中的峰值位于索引 29 处(不包括 0 处的巨大值)

========================================

如何将 fft 数组中峰值的索引与频率联系起来?

8个回答

2
如果忽略虚部,频率分布在各个区间之间是线性的: 每个区间的频率=(采样率/2)*(i/区间总数)。
因此,在第一个例子中,假设有256个区间,则最大的区间对应的频率为1000/2 * 6/256 = 11.7 Hz。 由于输入为10Hz,我猜测第5个区间(9.7Hz)也有很大的成分。 要获得更好的精度,需要取更多的样本以获得更小的区间。
第二个例子给出的频率为8000/2*29/256 = 453Hz。同样接近,但需要更多的区间。 这里的分辨率仅为4000/256 = 15.6Hz。

2
如果您能提供样本数据,那将非常有帮助。
我猜测您可能有所谓的采样伪像。频率为0的直流信号表明了这一点。
在调用fft之前,您应该始终确保输入数据的平均值为零——找到平均值并从每个样本点中减去它是一个好习惯。
同样,在采样窗口伪像方面,您必须小心。重要的是,第一个和最后一个数据点接近于零,因为否则从采样窗口外部到内部的“步进”会在不同的频率注入大量能量。
总之,进行fft分析需要比简单地回收某个fft例程更加小心谨慎。
这是问题描述中的10Hz信号的前100个样本点,经过处理以避免采样伪像。
> sinx[1:100]
  [1]  0.000000e+00  6.279052e-02  1.253332e-01  1.873813e-01  2.486899e-01  3.090170e-01  3.681246e-01  4.257793e-01  4.817537e-01  5.358268e-01
 [11]  5.877853e-01  6.374240e-01  6.845471e-01  7.289686e-01  7.705132e-01  8.090170e-01  8.443279e-01  8.763067e-01  9.048271e-01  9.297765e-01
 [21]  9.510565e-01  9.685832e-01  9.822873e-01  9.921147e-01  9.980267e-01  1.000000e+00  9.980267e-01  9.921147e-01  9.822873e-01  9.685832e-01
 [31]  9.510565e-01  9.297765e-01  9.048271e-01  8.763067e-01  8.443279e-01  8.090170e-01  7.705132e-01  7.289686e-01  6.845471e-01  6.374240e-01
 [41]  5.877853e-01  5.358268e-01  4.817537e-01  4.257793e-01  3.681246e-01  3.090170e-01  2.486899e-01  1.873813e-01  1.253332e-01  6.279052e-02
 [51] -2.542075e-15 -6.279052e-02 -1.253332e-01 -1.873813e-01 -2.486899e-01 -3.090170e-01 -3.681246e-01 -4.257793e-01 -4.817537e-01 -5.358268e-01
 [61] -5.877853e-01 -6.374240e-01 -6.845471e-01 -7.289686e-01 -7.705132e-01 -8.090170e-01 -8.443279e-01 -8.763067e-01 -9.048271e-01 -9.297765e-01
 [71] -9.510565e-01 -9.685832e-01 -9.822873e-01 -9.921147e-01 -9.980267e-01 -1.000000e+00 -9.980267e-01 -9.921147e-01 -9.822873e-01 -9.685832e-01
 [81] -9.510565e-01 -9.297765e-01 -9.048271e-01 -8.763067e-01 -8.443279e-01 -8.090170e-01 -7.705132e-01 -7.289686e-01 -6.845471e-01 -6.374240e-01
 [91] -5.877853e-01 -5.358268e-01 -4.817537e-01 -4.257793e-01 -3.681246e-01 -3.090170e-01 -2.486899e-01 -1.873813e-01 -1.253332e-01 -6.279052e-02
这段代码展示了一个名为`sinx`的向量,包含100个值,这些值是正弦函数在0到2π之间均匀分布的结果。每个值都保留了15位小数。

这里是FFT频域的绝对值结果

 [1] 7.160038e-13 1.008741e-01 2.080408e-01 3.291725e-01 4.753899e-01 6.653660e-01 9.352601e-01 1.368212e+00 2.211653e+00 4.691243e+00 5.001674e+02
[12] 5.293086e+00 2.742218e+00 1.891330e+00 1.462830e+00 1.203175e+00 1.028079e+00 9.014559e-01 8.052577e-01 7.294489e-01

1

我已经有一段时间没有做傅里叶变换了,但是这是我记得的。

傅里叶变换通常需要复数作为输入和输出。所以我不太确定输入和输出的实部和虚部如何映射到数组。

我真的不明白你在做什么。在第一个例子中,你说你对采样率为1000 Hz的样本缓冲区进行10Hz的处理?因此,你应该每秒处理10个样本缓冲区,每个缓冲区有100个采样值。我不明白你的输入数组如何至少达到228个采样值。

通常,输出缓冲区的前半部分是从0频率(=dc偏移)到1/2采样率的频率分量。而后半部分则是负频率。如果您的输入只是实际数据,并且虚信号为0,则正负频率相同。输出上的实数/虚数信号的关系包含了来自输入信号的相位信息。


1

我对数学和信号处理也有点生疏,但有了额外的信息,我可以试一试。

如果您想知道每个频率段的信号能量,您需要使用复杂输出的幅度。因此,仅查看实际输出是不够的。即使输入只是实数。对于每个频率段,输出的幅度是sqrt(real^2 + imag^2),就像勾股定理一样 :-)

0到449个频率段是从0 Hz到500 Hz的正频率。500到1000个频率段是负频率,并且对于实信号应该与正频率相同。如果您每秒处理一个缓冲区,则频率和数组索引将很好地对齐。因此,索引6处的峰值对应于6Hz,这有点奇怪。这可能是因为您仅查看实际输出数据,而实际和虚拟数据组合在一起会在索引10处产生预期的峰值。频率应线性映射到频率段。

0处的峰值表示直流偏移。


1

对于bin i的频率为i *(采样率/ n),其中n是FFT输入窗口中的样本数。

如果您正在处理音频,由于音高与频率的对数成比例,因此随着频率的增加,bin的音高分辨率也会增加 - 很难准确地解析低频信号。为了做到这一点,您需要使用更大的FFT窗口,这会降低时间分辨率。对于给定的采样率,存在频率与时间分辨率之间的权衡。

您提到一个在0处具有大值的bin - 这是频率为0的bin,即DC分量。如果这很大,那么您的值通常应该是正的。 bin n / 2(在您的情况下为256)是奈奎斯特频率,即采样率的一半,这是可以在此速率下解析采样信号的最高频率。

如果信号是实数,则bin n / 2 + 1到n-1将包含bin n / 2-1到1的复共轭。 DC值仅出现一次。


1

正如其他人所说,这些样本在频率域中是等间距的(不是对数间距)。

例如1,您应该得到以下结果:

alt text http://home.comcast.net/~kootsoop/images/SINE1.jpg

对于另一个示例,您应该得到

alt text http://home.comcast.net/~kootsoop/images/SINE2.jpg

因此,您的答案在峰值位置方面都是正确的。

我没有理解的是大的直流分量。您确定您正在生成正弦波作为输入吗?输入是否为负?对于正弦波,只要您获得足够的周期,直流电压应该接近于零。


1

另一种方法是制作一个戈策尔算法,用于查找每个音符中心频率。

一旦您获得了算法的一个实现,您可以使其接受参数以设置其中心频率。通过这样做,您可以轻松地运行88个或任何您需要的集合,并扫描峰值。

戈策尔算法基本上是单个bin FFT。使用此方法,您可以将您的bin对数地放置,就像音乐音符自然发生的那样。

来自维基百科的伪代码:

s_prev = 0
s_prev2 = 0
coeff = 2*cos(2*PI*normalized_frequency);
for each sample, x[n],
  s = x[n] + coeff*s_prev - s_prev2;
  s_prev2 = s_prev;
  s_prev = s;
end
power = s_prev2*s_prev2 + s_prev*s_prev - coeff*s_prev2*s_prev;

表示前两个样本的两个变量将保留到下一次迭代。这可以在流应用程序中使用。我认为功率计算也应该在循环内部。(但维基文章中没有这样描述。)

在音调检测的情况下,会有88个不同的系数、88对先前的样本,以及88个功率输出样本,指示该频率段中相对级别。


0
WaveyDavey表示他正在通过计算机的音频硬件从麦克风捕获声音,但他的结果不是零中心化的。这听起来像是硬件问题。它应该是零中心化的。
当房间很安静时,从声音API传来的值流应该非常接近0幅度,周围噪音会有轻微的+-变化。如果房间里存在振动声(例如钢琴、长笛、人声),数据流应该显示基本上是正弦波形式的波,既正又负,并且平均接近于零。如果不是这种情况,系统就有一些问题!
- Rick

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