Scipy find_peaks函数distance参数疑问及FFT振幅异常求助
Scipy find_peaks参数异常与FFT振幅计算错误解决
一、find_peaks的distance参数行为解析
distance参数的核心逻辑是:要求两个峰值的索引差必须大于等于设定值,并非直接对应时间间隔。你的采样率是1000Hz,1个样本对应1ms,所以distance=500等价于0.5秒的最小间隔,distance=1000等价于1秒的最小间隔。
看你给出的峰值索引:
- 当
distance=500时,所有相邻峰值的索引差都≥500(比如3928和5047差1119,11451和11955差504),同时满足height=0和prominence=(0.1,1)的条件,因此全部保留; - 当
distance=1000时,相邻峰值需要满足索引差≥1000。例如11451和11955的索引差仅为504,不满足要求,会被过滤;而前面的3928、5047、6243等峰值,要么是相邻间距不足1000,要么是其prominence超出了你设定的(0.1,1)范围,最终只剩11955和19300两个符合所有条件的峰值。
排查建议:
- 先单独调用
peaks, props = scipy.signal.find_peaks(y, height=0, prominence=True),获取所有正峰的索引和对应的prominence值; - 用
np.diff(peaks)计算相邻峰值的索引差,结合props['prominences']就能明确哪些峰值被过滤、以及过滤的原因。
二、FFT振幅计算异常的原因与修正
你得到的FFT振幅看似和实际信号不符,是因为对FFT的振幅计算逻辑理解有误:
np.abs(fft_result)/len(t)得到的是双边谱振幅,实信号的主频率分量会在正、负频率两端各出现一次,能量被均分;- 实际信号的峰值是直流分量(平均值)加上主频率的单边振幅(双边振幅×2)。
你的信号最大值4.2、最小值-2.2,直流分量为(4.2 + (-2.2))/2 = 1.0;主频率的双边振幅是1.58,乘以2后得到3.16,加上直流分量1.0,总峰值为4.16,和实际的4.2几乎完全一致,结果是正常的。
修正后的FFT代码(优化了逻辑并修正了冗余错误):
import numpy as np import matplotlib.pyplot as plt from scipy.signal import find_peaks, peak_prominences # 假设x是0-20秒的时间数组,y是压力信号 t = x signal = y sampling_rate = 1000 n_samples = len(t) # FFT计算与振幅修正 fft_result = np.fft.fft(signal) frequencies = np.fft.fftfreq(n_samples, 1.0 / sampling_rate) # 双边谱振幅 amplitudes_bilateral = np.abs(fft_result) / n_samples # 转换为单边谱振幅(直流分量除外) amplitudes_unilateral = amplitudes_bilateral.copy() # 非直流分量乘以2 amplitudes_unilateral[frequencies != 0] *= 2 # 找到主频率(忽略直流分量) non_dc_mask = frequencies != 0 main_freq_idx = np.argmax(amplitudes_unilateral[non_dc_mask]) main_frequency = frequencies[non_dc_mask][main_freq_idx] main_amplitude = amplitudes_unilateral[non_dc_mask][main_freq_idx] dc_component = np.mean(signal) print("主频率单边振幅:", main_amplitude) print("直流分量(信号平均值):", dc_component) print("理论信号峰值:", main_amplitude + dc_component) # 绘图展示 fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8)) ax1.plot(t, signal) ax1.set_xlabel('时间(秒)') ax1.set_ylabel('压力(mb)') ax1.set_title('原始压力信号') # 仅展示正频率的单边谱 positive_freq_mask = frequencies >= 0 ax2.stem(frequencies[positive_freq_mask], amplitudes_unilateral[positive_freq_mask], basefmt=" ") ax2.set_xlabel('频率(Hz)') ax2.set_ylabel('振幅') ax2.set_title('正频率单边频谱') plt.tight_layout() plt.show()
额外说明:原代码中duration=1.0是错误的(实际信号时长为20秒),不过fftfreq依赖的是样本数和采样率,所以不影响计算,但建议修正为duration = t[-1] - t[0]避免后续混淆。
内容的提问来源于stack exchange,提问作者cbroggi
相关产品推荐
相关产品推荐

