You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:52:21