使用scipy.fftpack计算FFT绘图出现恒定线的原因与解决方法
解决FFT绘图中出现水平线和垂直线的问题
我来帮你拆解这个FFT绘图的问题,你遇到的水平线和垂直线其实都是FFT处理和绘图时的常见小坑,咱们用你说的12Hz正弦波例子一步步说清楚:
一、为什么会出现这些线条?
1. 垂直线的原因
scipy的fftfreq函数返回的频率数组排序很特殊:前半段是正频率(从0到采样频率的一半,也就是奈奎斯特频率),后半段突然跳转到负频率(从接近-奈奎斯特频率到-最小频率间隔)。如果你直接用plt.plot()连接所有频率点,最后一个正频率点和第一个负频率点之间会因为频率值的巨大突变,画出一条横跨整个x轴的垂直线。
2. 水平线的原因
你看到的恒定水平线,大概率是这几个原因之一:
- 没有计算FFT的幅值:FFT返回的是复数结果,如果你直接绘制实部或虚部,正弦波的FFT在非峰值频率处的实部/虚部接近0,看起来就像一条水平线;
- 频谱泄漏:如果采样时长不是信号周期的整数倍,非峰值频率处会出现小的幅值,视觉上像一条低幅值的水平线;
- 未归一化:FFT的幅值默认是原始信号幅值的N/2倍(N是采样点数),如果没归一化,可能会让轴范围被峰值拉得很大,导致接近0的点看起来像一条水平线。
二、解决方案:画出干净的频谱
咱们用完整的代码示例来实现无干扰的FFT频谱图:
1. 生成标准测试信号
先创建一个符合奈奎斯特准则的12Hz正弦波:
import numpy as np import matplotlib.pyplot as plt import scipy.fftpack # 信号参数 f_signal = 12 # 目标信号频率12Hz fs = 100 # 采样频率100Hz(满足奈奎斯特:fs > 2*f_signal) duration = 1 # 采样时长1秒 n_samples = fs * duration # 总采样点数 # 生成时间轴和正弦波 t = np.linspace(0, duration, n_samples, endpoint=False) signal = np.sin(2 * np.pi * f_signal * t)
2. 正确计算FFT幅值并归一化
FFT的复数结果需要取绝对值得到幅值,同时归一化到原始信号的幅值范围:
# 计算FFT fft_vals = scipy.fftpack.fft(signal) # 计算归一化后的幅值(除以采样点数的一半,还原原始信号幅值) fft_amp = np.abs(fft_vals) / (n_samples / 2) # 获取频率轴 freqs = scipy.fftpack.fftfreq(n_samples, 1/fs)
3. 只保留正频率部分
为了避免频率突变导致的垂直线,我们只筛选正频率(包括0频率)的部分:
# 筛选正频率的索引 positive_freq_idx = freqs >= 0 freqs_pos = freqs[positive_freq_idx] fft_amp_pos = fft_amp[positive_freq_idx]
4. 绘制干净的频谱图
用stem图(适合离散频谱)或者plot图绘制,就能得到没有多余线条的结果:
plt.figure(figsize=(10, 6)) # stem图更清晰展示离散频谱的峰值 plt.stem(freqs_pos, fft_amp_pos, basefmt=" ", use_line_collection=True) plt.xlabel("Frequency (Hz)") plt.ylabel("Amplitude") plt.title("FFT Spectrum of 12Hz Sine Wave") plt.xlim(0, fs/2) # 限制x轴到奈奎斯特频率 plt.grid(True) plt.show()
这样画出来的图,只会在12Hz处有一个清晰的峰值,不会出现之前的水平线和垂直线啦。
内容的提问来源于stack exchange,提问作者lrsppp
相关产品推荐
相关产品推荐

