简体   繁体   English

如何解释 Python 中离散傅里叶变换 (FFT) 的结果

[英]How to interpret the results of the Discrete Fourier Transform (FFT) in Python

There are many questions on this topic, and I have cycled through a lot of them getting conceptual pointers on handling frequencies ( here and here ), documentation on numpy functions ( here ), how-to information on extracting magnitude and phase ( here ), and stepping outside the site, for example this or this .关于这个主题有很多问题,我已经循环浏览了很多问题,以获得关于处理频率的概念指针(此处此处)、有关 numpy 函数的文档(此处)、有关提取幅度和相位的操作方法信息(此处),并走出网站,例如thisthis

However, only the painful "proving it" to myself with simple examples and checking the output of different functions contrasted to their manual implementation has given me a bit of an idea.然而,只有通过简单的例子痛苦地“证明”给自己,并检查不同功能的 output 与手动实现的对比,这让我有了一点想法。

The answer attempts to document and share details related to the DFT in Python that may constitute barriers of entry if not explained in simple terms.答案试图在 Python 中记录和分享与 DFT 相关的细节,如果不简单解释,这些细节可能构成进入壁垒。

The DFT (FFT being its algorithmic computation) is a dot product between a finite discrete number of samples N of an analogue signal s (t) (a function of time or space) and a set of basis vectors of complex exponentials (sin and cos functions). DFT(FFT 是其算法计算)是模拟信号s (t)(时间或空间的 function)的有限离散样本N与复指数的一组基向量(sin 和 cos)之间的点积功能)。 Although the sample is naturally finite and may show no periodicity, it is implicitly thought of as a periodically repeating discrete function.尽管样本自然是有限的并且可能没有周期性,但它被隐含地认为是周期性重复的离散 function。 Even when dealing with real-valued signals (the usual situation) it is convenient to work with complex numbers (Euler's equation).即使在处理实值信号(通常情况)时,使用复数(欧拉方程)也很方便。 It may be intimidating to implement the function on a signal with np.fft.fft(s) only to get the output coefficients in complex numbers and get stuck in their interpretation.在带有np.fft.fft(s)的信号上实现 function 可能会令人生畏,只是为了获得复数的 output 系数并陷入其解释中。 Some steps are essential:一些步骤是必不可少的:

What are the frequencies in the complex exponentials?复指数中的频率是多少?
  1. The DFT does not necessarily preserve the sampling frequency in Hertz. DFT 不一定以赫兹为单位保留采样频率。 The frequencies are indices ( k ).频率是指数( k )。
  2. The indices k range from 0 to N - 1 and can be thought of as having units of cycles / set (the set being the N samples of the signal s ).索引k的范围从0 to N - 1并且可以被认为具有周期/组的单位(该组是信号sN个样本)。 I will omit discussing the Nyquist limit, but for real signals the frequencies form a mirror image after N / 2 , and given as negative decreasing values after that point.我将省略讨论奈奎斯特极限,但对于实际信号,频率在N / 2之后形成镜像,并在该点之后以负递减值给出。 The frequencies used in the FFT are not simply k , but k / N , thought of as having units of cycles / sample . FFT 中使用的频率不仅仅是k ,而是k/N ,被认为具有周期/样本单位。 See this reference .请参阅此参考 Example ( reference ): If a signal is sampled N = 5 times the frequencies are: np.fft.fftfreq(5) , yielding [ 0, 0.2, 0.4, -0.4, -0.2] , ie [0/5, 1/5, 2/5, -2/5, -1/5] .示例(参考):如果对信号进行N = 5次采样,则频率为: np.fft.fftfreq(5) ,产生[ 0, 0.2, 0.4, -0.4, -0.2] ,即[0/5, 1/5, 2/5, -2/5, -1/5]
  3. To convert these frequencies to meaningful units (eg Hetz or mm) the values in cycles/sample above will need to be divided by sampling interval T (eg distance in seconds between samples).要将这些频率转换为有意义的单位(例如 Hetz 或 mm),上述循环/样本中的值需要除以采样间隔T (例如样本之间的距离(以秒为单位))。 Continuing with the example above, there is a built-in call: np.fft.fftfreq(5, d=T) : If the analogue signal s is sampled 5 times at equidistant intervals T = 1/2 sec for a total sample of NT = 5 x 1/2 sec , the normalized frequencies will be np.fft.fftfreq(5, d = 1/2) , yielding [0 0.4 0.8 -0.8 -0.4] or [0/NT, 1/NT, 2/NT, -2/NT, -1/NT] .继续上面的示例,有一个内置调用: np.fft.fftfreq(5, d=T) :如果模拟信号s以等距间隔T = 1/2秒采样5次,总采样为NT = 5 x 1/2 sec ,归一化频率将为np.fft.fftfreq(5, d = 1/2) ,产生[0 0.4 0.8 -0.8 -0.4][0/NT, 1/NT, 2/NT, -2/NT, -1/NT]
  4. Either normalized or un-normalized frequencies are used to control angular frequencies ( ω_m ), expressed as ω_m = 2π k/NT .归一化或非归一化频率用于控制angular 频率( ω_m ),表示为ω_m = 2π k/NT Note that NT is the total duration for which the signal was sampled.请注意, NT是对信号进行采样的总持续时间。 The index k does result in multiples of a fundamental frequency ( ω-naught ) corresponding to k = 1 - the frequency of (co-)sine wave that completes exactly one oscillation over NT ( here ).指数k确实导致基频 ( ω-naught ) 的倍数,对应于k = 1 - (余) 正弦波的频率,该频率恰好在NT上完成一次振荡( 此处)。

Magnitude, frequency and phase of the coefficients in the FFT FFT中系数的幅度、频率和相位
  1. Given the output of the FFT S = fft.fft(s) , the magnitude of the output coefficients ( here ) is just the Euclidean norm of the complex numbers in the output coefficients adjusted for the symmetry in real signals ( x 2 ) and for the number of samples 1/N : magnitudes = 1/N * np.abs(S) Given the output of the FFT S = fft.fft(s) , the magnitude of the output coefficients ( here ) is just the Euclidean norm of the complex numbers in the output coefficients adjusted for the symmetry in real signals ( x 2 ) and for样本数1/Nmagnitudes = 1/N * np.abs(S)
  2. The frequencies are matched to the call explained above np.fft.fftfreq(N) , or more expediently to incorporate the actual analogue frequency units, frequencies = np.fft.fftfreq(N, d=T) .频率与上面解释的调用匹配np.fft.fftfreq(N) ,或者更方便地结合实际的模拟频率单位, frequencies = np.fft.fftfreq(N, d=T)
  3. The phase of each coefficients is the angle of the complex number in polar form phase = np.arctan(np.imag(S)/np.real(S))每个系数的相位是复数在极坐标形式中的角度phase = np.arctan(np.imag(S)/np.real(S))

How to find the dominant frequencies in the signal s in the FFT and their coefficients?如何在 FFT 中找到信号 s 中的主导频率及其系数?
  1. Plotting aside, finding the index k corresponding the frequency with the highest magnitude can be accomplished as index = np.argmax(np.abs(S)) .撇开绘图不谈,找到与具有最高幅度的频率对应的索引k可以通过index = np.argmax(np.abs(S))来完成。 To find the 4 indices with the highest magnitude, for example, the call is indices = np.argpartition(S,-4)[-4:] .例如,要找到幅度最大的4索引,调用是indices = np.argpartition(S,-4)[-4:]
  2. And finding the actual corresponding coefficient: S[index] with frequency freq_max = np.fft.fftfreq(N, d=T)[index] .并找到实际对应的系数: S[index]频率freq_max = np.fft.fftfreq(N, d=T)[index]

Reproducing the original signal after obtaining the coefficients:得到系数后再现原始信号:

Reproducing s through sines and cosines (p.150 in here ):通过正弦和余弦再现s此处为第 150 页):

    Re = np.real(S[index])
    Im = np.imag(S[index])
    
    s_recon = Re * 2/N * np.cos(-2 * np.pi * freq_max * t) + abs(Im) * 2/N * np.sin(-2 * np.pi * freq_max * t) 

Here is a complete example:这是一个完整的例子:

import numpy as np
import matplotlib.pyplot as plt

N = 10000           # Sample points     
T = 1/5000          # Spacing
# Total duration N * T= 2
t = np.linspace(0.0, N*T, N, endpoint=False) # Time: Vector of 10,000 elements from 0 to N*T=2.
frequency = np.fft.fftfreq(t.size, d=T)      # Normalized Fourier frequencies in spectrum.

f0 = 25             # Frequency of the sampled wave
phi = np.pi/8       # Phase
A = 50              # Amplitude

s = A * np.cos(2 * np.pi * f0 * t + phi) # Signal

S = np.fft.fft(s)   # Unnormalized FFT

index = np.argmax(np.abs(S))
print(S[index])
magnitude = np.abs(S[index]) * 2/N
freq_max = frequency[index]

phase = np.arctan(np.imag(S[index])/np.real(S[index]))
print(f"magnitude: {magnitude}, freq_max: {freq_max}, phase: {phase}")
print(phi)

fig, [ax1,ax2] = plt.subplots(nrows=2, ncols=1, figsize=(10, 5))
ax1.plot(t,s, linewidth=0.5, linestyle='-', color='r', marker='o', markersize=1,markerfacecolor=(1, 0, 0, 0.1))  
ax1.set_xlim([0, .31])
ax1.set_ylim([-51,51])
ax2.plot(frequency[0:N//2], 2/N * np.abs(S[0:N//2]), '.', color='xkcd:lightish blue', label='amplitude spectrum')
plt.xlim([0, 100])
plt.show()

Re = np.real(S[index])
Im = np.imag(S[index])

s_recon = Re*2/N * np.cos(-2 * np.pi * freq_max * t) + abs(Im)*2/N * np.sin(-2 * np.pi * freq_max * t)

fig = plt.figure(figsize=(10, 2.5))

plt.xlim(0,0.3)
plt.ylim(-51,51)
plt.plot(t,s_recon, linewidth=0.5, linestyle='-', color='r', marker='o', markersize=1,markerfacecolor=(1, 0, 0, 0.1))  
plt.show()

s.all() == s_recon.all()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM