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

2023-12-09

关于这个主题有很多问题,我已经循环浏览了其中很多问题,获得了有关处理频率的概念性指导(here and here),有关 numpy 函数的文档(here),有关提取幅度和相位的操作信息(here),并走出站点,例如this or this.

然而,只有通过简单的例子向自己痛苦地“证明这一点”,并检查不同函数的输出与手动实现的对比,才给了我一些想法。

答案试图记录和分享与 Python 中的 DFT 相关的细节,如果不以简单的术语解释,这些细节可能会构成进入障碍。


enter image description here

DFT(FFT 是其算法计算)是有限离散样本之间的点积N模拟信号的s(t)(时间或空间的函数)和一组复指数的基向量(sin 和 cos 函数)。尽管样本本质上是有限的并且可能不显示周期性,但它隐含地被认为是周期性重复的离散函数。即使在处理实值信号(通常情况)时,使用复数(欧拉方程)也很方便。在信号上实现该函数可能会令人生畏np.fft.fft(s)只是为了获得复数的输出系数并陷入其解释中。一些步骤是必不可少的:

复指数中的频率是多少?
  1. DFT 不一定保留以赫兹为单位的采样频率。频率是指数(k).
  2. 指数k范围0 to N - 1可以被认为有单位循环/组(该集合是N信号样本s)。我将省略讨论奈奎斯特极限,但对于真实信号,频率在之后形成镜像N / 2,并在该点之后给出负递减值(在隐式周期性框架内不是问题)。 FFT 中使用的频率不仅仅是k, but k / N,被认为具有单位周期/样品. See 这个参考。例子 (参考): 如果对信号进行采样N = 5乘以频率为:np.fft.fftfreq(5),产生[ 0 , 0.2, 0.4, -0.4, -0.2], i.e. [0/5, 1/5, 2/5, -2/5, -1/5].
  3. 要将这些频率转换为有意义的单位(例如赫兹或毫米),上面的周期/样本中的值需要除以采样间隔T(例如样本之间的距离(以秒为单位))。继续上面的例子,有一个内置调用:np.fft.fftfreq(5, d=T):如果是模拟信号s被采样5等距次数T = 1/2秒的总样本NT = 5 x 1/2 sec,归一化频率将是np.fft.fftfreq(5, d = 1/2),产生[0 0.4 0.8 -0.8 -0.4] or [0/NT, 1/NT, 2/NT, -2/NT, -1/NT].
  4. 使用归一化或非归一化频率来控制角频率 (ω_m), 表示为ω_m = 2π k/NT。注意NT是总持续时间 对信号进行采样。指数k确实会产生基频的倍数(ω-naught)对应于k = 1- 完成的(同)正弦波的频率 正好一次振荡NT (here).

enter image description here

FFT 中系数的幅度、频率和相位
  1. 给定 FFT 的输出S = fft.fft(s), the 震级输出系数(here)只是输出系数中复数的欧几里得范数,根据实际信号的对称性进行调整(x 2)和样本数量1/N: magnitudes = 1/N * np.abs(S)
  2. 频率与上面解释的呼叫相匹配np.fft.fftfreq(N),或更方便地合并实际的模拟频率单位,frequencies = np.fft.fftfreq(N, d=T).
  3. 每个系数的相位是复数极坐标形式的角度phase = np.arctan(np.imag(S)/np.real(S))

如何在FFT中找到信号s的主频率及其系数?
  1. 抛开绘图,找到索引k对应于最高幅度的频率可以完成为index = np.argmax(np.abs(S))。为了找到4具有最高幅度的指数,例如,调用是indices = np.argpartition(S,-4)[-4:].
  2. 并求出实际对应的系数:S[index]随频率freq_max = np.fft.fftfreq(N, d=T)[index].

获得系数后再现原始信号:

繁殖s通过正弦和余弦(p.150 inhere):

    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) 

这是一个完整的例子:

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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Python 中解释离散傅里叶变换 (FFT) 的结果 的相关文章

随机推荐