一个关于FFT计算的疑似乌龙事件

0.缘起

最近进行一些频域解析,发现不知道从何时起,大部分都犯了一个相当典型的错误。我是在尝试将数据转至频域去零点,然后再转回时域,发现这个bug的。这个问题波及面很广,估计有很多已经在用的程序都会犯这个错误:

1.错误浮现 - 例程1 时域转频域去零点然后转回时域

我们期待的结果是这样的:

但是如果你尝试一步步用去频点0的谱线,然后进行幅角修正,然后转回时域,你会发现所得到的波形无法和原始波形匹配,类似这样:

1.1错误的利用FFT去除信号零点的方法

import numpy as np
import matplotlib.pyplot as plt

# 生成频率为100Hz和200Hz的信号
fs = 1000  # 采样频率
t = np.arange(0, 1, 1/fs)
signal = 18+0.7*np.sin(2*np.pi*100*t) + 0.3*np.sin(2*np.pi*200*t) + 0.5*np.sin(2*np.pi*150*t)

# 进行FFT
fft_signal = np.fft.fft(signal)
fft_signal_abs = np.abs(fft_signal)

# 去零点进行幅角修正
fft_signal_phase_corrected = fft_signal.copy()
fft_signal_phase_corrected[0] = 0
fft_signal_phase_corrected_abs = np.abs(fft_signal_phase_corrected)
max_val = np.max(fft_signal_phase_corrected_abs)
max_idx = np.argmax(fft_signal_phase_corrected_abs)
print(max_idx, "max value in freq< = max value = ", max_val)
fft_signal_phase_corrected = fft_signal_phase_corrected * np.exp(-1j * np.angle(fft_signal_phase_corrected[max_idx]))
fft_signal_phase_corrected_abs = np.abs(fft_signal_phase_corrected)
fft_signal_with_phase_correction = np.fft.ifft(fft_signal_phase_corrected)
fft_signal_with_phase_correction_abs = np.abs(fft_signal_with_phase_correction)

# 去直流分量(不进行浮点修正)
fft_signal[0] = 0
fft_signal_no_phase_correction = np.fft.ifft(fft_signal)
fft_signal_no_phase_correction_abs = np.abs(fft_signal_no_phase_correction)

# 绘制频谱图
plt.figure(figsize=(12, 6))

# 绘制时间域波形
plt.subplot(2, 2, 1)
plt.plot(t, signal)
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
str_time = 'Time Domain Signal=%dPt, totalTimeSpan=%fS' %(len(t), len(t)*1.0/fs)
plt.title(str_time)

plt.subplot(2, 2, 2)
plt.plot(np.arange(len(fft_signal_abs)), fft_signal_abs, color='blue')
str_fft = 'Original Spectrum, totalPt=%d' %(len(fft_signal_abs))
plt.title(str_fft)
plt.xlabel('Frequency')
plt.ylabel('Amplitude')
plt.grid(True)

plt.subplot(2, 2, 3)
plt.plot(np.arange(len(fft_signal_no_phase_correction_abs)), fft_signal_no_phase_correction_abs, color='red')
plt.title('Spectrum => Time without Phase Correction')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)

plt.subplot(2, 2, 4)
plt.plot(np.arange(len(fft_signal_with_phase_correction_abs)), fft_signal_with_phase_correction_abs, color='green')
plt.title('Spectrum => Time with Phase Correction')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)

plt.tight_layout()
plt.show()

实际运行结果:

2.问题分析 - 例程1修复

在FFT展示时的np.abs()那个地方。FFT的结果,real是幅度,imag存放的幅角。取模没有意义,如果我们把上述代码显示的视图中,2,3,4图都是错的。很容易校回,校回后的时域,频域和去零点后的时域波形:

我只改了这几处地方:

python 复制代码
#第一处改动:第一次时域到频域的验算,实际显示时,谱线高度要除以采样点数:
fft_signal_abs = [z.real/fs for z in fft_signal]

#第二处改动:
fft_signal_with_phase_correction_abs = [z.real for z in fft_signal_with_phase_correction]

#第三处相似改动:
fft_signal_no_phase_correction_abs = [z.real for z in fft_signal_no_phase_correction]

3.后记

这个错误是如此触目惊心。我不知道它的波及面有多广,我在大量的帖子里看到错误的频谱纵坐标。有心人可以考证一下,这个错误是什么人,什么时候引入的。对于现在的大多数运算工具,fft返回的那个复数队列中的每一个点,实部是幅度,虚部是幅角。

np.abs(real+j*imag) = sqrt(real*real + imag*imag)

没有意义。频谱只需要展示实部。

相关推荐
꧁༺❀氯ྀൢ躅ྀൢ❀༻꧂2 分钟前
实验4 循环结构
c语言·算法·基础题
白拾12 分钟前
使用Conda管理python环境的指南
开发语言·python·conda
新晓·故知27 分钟前
<基于递归实现线索二叉树的构造及遍历算法探讨>
数据结构·经验分享·笔记·算法·链表
是刃小木啦~31 分钟前
三维模型点云化工具V1.0使用介绍:将三维模型进行点云化生成
python·软件工程·pyqt·工业软件
总裁余(余登武)38 分钟前
算法竞赛(Python)-万变中的不变“随机算法”
开发语言·python·算法
一个闪现必杀技44 分钟前
Python练习2
开发语言·python
Eric.Lee20211 小时前
音频文件重采样 - python 实现
人工智能·python·深度学习·算法·audio·音频重采样
大神薯条老师1 小时前
Python从入门到高手5.1节-Python简单数据类型
爬虫·python·深度学习·机器学习·数据分析
huapiaoy1 小时前
Redis中数据类型的使用(hash和list)
redis·算法·哈希算法
Mr.D学长1 小时前
毕业设计 深度学习社交距离检测系统(源码+论文)
python·毕业设计·毕设