Python中FFT处理正弦信号时正负频分量问题求解
解决实信号FFT正负频率分量问题
问题原因
实值信号的FFT结果天生具有共轭对称性:正频率分量和对应的负频率分量幅值相等、相位相反。你看到的“20000Hz附近的分量”其实是-50Hz的频率被折叠到了采样频率Fs(这里Fs=20000Hz)的后半段,本质就是负频率分量。单独只保留正频率分量做IFFT,得到的会是复信号,自然还原不了原来的实正弦波。
两种解决方法
方法1:用实信号专用的rfft/irfft(推荐)
scipy.fftpack提供了rfft()和irfft(),专门针对实信号优化:rfft()只返回正频率部分(包括0Hz),点数为N//2 +1,避免了冗余的负频率分量;irfft()可以直接从正频率结果还原出实信号,不需要额外处理。
修改你的代码如下:
from scipy.fftpack import rfft, irfft import matplotlib.pyplot as plt import numpy as np tt = np.arange(0, 0.02, 50e-6) xx = np.sin(2*np.pi*50*tt) # 用rfft计算实信号的FFT XX_r = rfft(xx) N = len(tt) Fs = 1 / 50e-6 # 构造正频率轴:0到Fs/2 ff_r = np.linspace(0, Fs/2, len(XX_r)) plt.figure(figsize=(12, 6)) plt.subplot(121) plt.plot(ff_r, np.abs(XX_r)) plt.xlabel('Freq (Hz)') plt.ylabel('FFT Amplitude') plt.title('RFFT结果(仅正频率)') plt.subplot(122) # 用irfft直接还原实信号 xx_recon = irfft(XX_r) plt.plot(tt, xx_recon, 'r-') plt.xlabel('Time (s)') plt.ylabel('Amplitude') plt.title('IRFFT还原信号') plt.tight_layout() plt.show()
这样你只会看到50Hz的单分量,且还原的信号和原信号完全一致。
方法2:手动处理普通FFT的对称分量
如果一定要用普通fft(),需要手动构造共轭对称的FFT结果,才能还原实信号:
- 找到目标正频率的索引
- 保留该分量,并在对应的负频率位置设置其共轭值
- 0Hz和Nyquist频率(Fs/2)无需处理(因为它们没有配对的负频率)
- 其他位置置0,再做IFFT后取实部
示例代码:
from scipy.fftpack import fft, ifft import matplotlib.pyplot as plt import numpy as np tt = np.arange(0, 0.02, 50e-6) xx = np.sin(2*np.pi*50*tt) N = len(tt) Fs = 1 / 50e-6 XX = fft(xx) ff = np.fft.fftfreq(N, 50e-6) # 用fftfreq生成正确的正负频率轴,更直观 # 找到50Hz对应的正频率索引 target_freq = 50 idx = np.argmin(np.abs(ff - target_freq)) # 对应的负频率索引 neg_idx = N - idx # 构造新的FFT结果,只保留目标频率及其共轭分量 XX_filtered = np.zeros_like(XX, dtype=np.complex128) XX_filtered[idx] = XX[idx] XX_filtered[neg_idx] = np.conj(XX[idx]) # 设置共轭值,保证实信号特性 # 还原信号 xx_recon = ifft(XX_filtered).real # 取实部消除数值误差 plt.figure(figsize=(12, 6)) plt.subplot(121) plt.plot(ff, np.abs(XX_filtered)) plt.xlabel('Freq (Hz)') plt.ylabel('FFT Amplitude') plt.title('处理后的FFT结果') plt.subplot(122) plt.plot(tt, xx_recon, 'r-') plt.xlabel('Time (s)') plt.ylabel('Amplitude') plt.title('还原信号') plt.tight_layout() plt.show()
这里用np.fft.fftfreq()生成了包含正负频率的轴,能更清晰看到-50Hz和50Hz的分量,处理后只保留这两个对称分量,IFFT就能还原出原正弦波。
关于幅值和相位的提取
如果要提取主谐波的幅值和相位:
- 用
rfft()的话,幅值是np.abs(XX_r[idx]) * 2 / N(因为rfft的正频率分量幅值是普通FFT的2倍,除以N是归一化) - 相位是
np.angle(XX_r[idx]),注意正弦信号的相位和余弦的差异(原信号是sin,对应cos的相位减π/2)
用普通FFT的话,正频率分量的幅值是np.abs(XX[idx]) / N,相位是np.angle(XX[idx]),负频率是其共轭。
内容的提问来源于stack exchange,提问作者Poria Divshali
相关产品推荐
相关产品推荐

