如何从信号的FFT中获取频率和对应的幅度?

1 投票
2 回答
28 浏览
提问于 2025-04-14 15:27

我有一个信号,频率是 1.2Hz/9Hz/15Hz

import numpy as np

total_time = 5
sampling_frequency = 100

t = np.linspace(0, total_time, total_time * sampling_frequency, endpoint=False)
signal = np.sin(2 * np.pi * 1.2 * t) + 0.5*np.cos(2 * np.pi * 9 * t) + 0.75*np.sin(2 * np.pi * 15.0 * t)
plt.plot(t, signal)

这里插入图片描述

还有这个离散傅里叶变换(FFT)的结果。

fft_spectrum = np.fft.rfft(signal)
fft_spectrum_abs = np.abs(fft_spectrum) * 2 / (total_time * sampling_frequency)

freq = np.fft.rfftfreq(signal.size, d=1./sampling_frequency)

plt.plot(freq, fft_spectrum_abs)
plt.xlabel("frequency, Hz")
plt.ylabel("amplitude, units")
plt.show()

这里插入图片描述

我该如何得到FFT的频率和幅度呢?

我正在使用

[
    (amp, freq) for amp, freq in sorted(zip(fft_spectrum_abs, freq), 
    key=lambda pair: pair[0]) if amp > 0.1
]
# [(0.4999999999999984, 9.0), (0.749999999999999, 15.0), (1.0, 1.2000000000000002)]

这个方法是对的,但感觉有点像是临时凑合的办法,有没有更好的方法来解决这个问题呢?

2 个回答

2

你可以使用 np.where 来找到索引,并同时筛选频率和幅度:

peaks_indices = np.where(fft_spectrum_abs > 0.1)[0]
freq_and_magnitudes = list(zip(freq[peaks_indices],
                               fft_spectrum_abs[peaks_indices]))
1

你可以使用 scipy 里的 scipy.signal.find_peaks 这个功能。假设你想找的峰值高度至少要有 0.1,那你可以通过 heights 这个参数来设置。当你指定了 heights 后,返回的 properties 字典里会有一个 "peak_heights" 的键,里面会包含一个 numpy 数组,这个数组就是你找到的峰值高度(名字就说明了)。

from scipy.signal import find_peaks

peaks, properties = find_peaks(fft_spectrum_abs, height=0.1)
peak_freq = freq[peaks]
peak_amp = properties["peak_heights"]
print(peak_freq)  # [ 1.2  9.  15. ]
print(peak_amp)   # [1.   0.5  0.75]

如果你的数据比较杂乱或者有噪声,这个方法可能会更有效。

撰写回答