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

使用SciPy查找FFT峰值并绘图时出现异常问题求助

问题分析与修复方案

核心错误点

  1. 直接对复数FFT结果找峰值
    scipy.signal.find_peaks仅能处理实数值,而scipy.fft.fft返回的是复数数组,直接传入会错误识别实部/虚部的波动,而非信号的幅度峰值。

  2. 频率轴计算逻辑混乱
    代码中xf = np.linspace(0.0, 1.0/(10.0*T), n//10)存在两个问题:

    • 音频信号的有效频率范围是0到奈奎斯特频率(rate/2,即1/(2*T)),超过该范围的FFT结果是镜像冗余数据。
    • 仅取前n//10个点绘制,会丢失大部分有效频率信息,且峰值可能落在未绘制区间,导致显示错位。
  3. 峰值绘制未取幅度绝对值
    plt.plot(z, 10.0/n * yf[z], "x")中yf[z]是复数,直接绘制会出现数值异常,必须取绝对值才能对应幅度轴刻度。

  4. 峰值位置超出绘制范围
    若峰值出现在n//10之后的位置,绘制时会在图上显示完全偏离的错误点。

修正后的代码

import struct
import numpy as np
import scipy
from matplotlib import pyplot as plt

def fft_plot(audio, rate):
    n = len(audio)
    T = 1/rate
    # 计算FFT并提取幅度值
    yf = scipy.fft.fft(audio)
    yf_amp = np.abs(yf)
    # 基于幅度谱查找峰值,可根据信号调整prominence参数
    z, _ = scipy.signal.find_peaks(yf_amp, prominence=100)
    # 生成正确的频率轴:覆盖0到奈奎斯特频率,点数匹配FFT前半部分
    xf = np.linspace(0.0, rate/2, n//2)
    fig, ax = plt.subplots()
    # 绘制归一化后的幅度谱(仅前半部分有效数据)
    ax.plot(xf, (2.0/n) * yf_amp[:n//2])
    # 过滤掉超出绘制范围的峰值,仅显示有效区间内的点
    valid_peaks = z[z < n//2]
    ax.plot(xf[valid_peaks], (2.0/n) * yf_amp[valid_peaks], "x", color="red")
    plt.grid()
    plt.xlabel("Frequency -->")
    plt.ylabel("Magnitude")
    plt.show()

def get_span(fi):
    b = fi.read(768*4)
    if len(b) < 768*4:
        return None
    r = struct.unpack('768i', b)
    return [float(x/(2**31-1)) for x in r]

with open('/tmp/samples.raw', 'rb') as fi:
    while span := get_span(fi):
        fft_plot(span, 48000)

额外说明

  • 对于440Hz方波,其FFT峰值应出现在440Hz、1320Hz、2200Hz等奇次谐波位置,修正后的代码可正确识别这些峰值。
  • 可根据实际信号的幅度大小调整prominence参数,避免误识别噪声带来的伪峰值。
  • 用2.0/n作为归一化系数,更符合实信号FFT幅度谱的物理意义。

内容的提问来源于stack exchange,提问作者ZioByte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 21:05:25