Python案例如何用Scipy做傅里叶变换

wen python案例 1

Python 案例详解,如何用 SciPy 做傅里叶变换(附完整代码)

目录导读

  • 傅里叶变换是什么?为什么需要用 SciPy?

    Python案例如何用Scipy做傅里叶变换

  • 环境准备与核心库安装

  • 简单正弦波的频谱分析

  • 含噪信号的降噪与特征提取

  • 实际音频文件的频谱可视化

  • 常见问题与避坑指南(Q&A)

  • SciPy 傅里叶变换的实战要点


傅里叶变换是什么?为什么需要用 SciPy?

傅里叶变换是信号处理领域的基石技术,它能将一个时间域(随时间变化的幅度)的信号,转换到频率域(各频率成分的强度),让我们看到信号里包含哪些“频率分量”,以及各自有多强。

问题一:为什么不用 NumPy 自带的 np.fft

:NumPy 提供了基础的 FFT 功能,而 SciPy 的 scipy.fft 模块在接口一致性、支持类型、以及对实际工程场景(如非均匀采样、多维度处理)的覆盖上更全面,尤其对于需要逆变换、带通滤波、希尔伯特变换等进阶操作,SciPy 是更专业的选择,本文案例全部基于 scipy.fft 展开。


环境准备与核心库安装

在开始之前,请确保已安装以下 Python 库:

pip install numpy matplotlib scipy

我们会在代码中用到:

  • numpy:生成模拟信号、数组运算
  • matplotlib:绘制时域与频域图
  • scipy.fft:核心傅里叶变换函数
  • scipy.io.wavfile:读取音频文件(案例三)

案例一:简单正弦波的频谱分析

1 生成一个 50Hz 与 120Hz 混合的正弦波

import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq
# 参数设置
Fs = 1000          # 采样频率 1000 Hz
T = 1.0 / Fs       # 采样间隔
L = 1000           # 采样点数
t = np.linspace(0, L*T, L, endpoint=False)
# 合成信号:50Hz + 120Hz 正弦波
f1, f2 = 50, 120
y = 0.7 * np.sin(2 * np.pi * f1 * t) + 1.2 * np.sin(2 * np.pi * f2 * t)
# 绘制时域图
plt.figure(figsize=(12, 4))
plt.plot(t[:100], y[:100])   # 只显示前100个点'时域信号 (混合正弦波)')
plt.xlabel('时间 (秒)')
plt.ylabel('幅度')
plt.grid(True)
plt.show()

2 执行傅里叶变换并绘制频谱

# 执行 FFT
Y = fft(y)
# 计算频率轴(单侧频谱)
N = L // 2
freq = fftfreq(L, T)[:N]   # 只取正频率部分
amplitude = 2.0 / L * np.abs(Y[:N])
# 绘制频谱
plt.figure(figsize=(12, 4))
plt.stem(freq, amplitude, basefmt=" ", use_line_collection=True)'单侧幅值频谱')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅值')
plt.grid(True)
plt.xlim(0, 200)   # 只看0-200Hz范围
plt.show()

关键解读

  • 频谱图中在 50Hz120Hz 处出现明显尖峰,幅值分别为 0.7 和 1.2,与输入信号完全吻合。
  • fftfreq 生成频率轴,0/L * np.abs(Y[:N]) 是将幅度归一化为真实幅值的标准做法。

案例二:含噪信号的降噪与特征提取

1 给信号添加随机噪声

# 在上个案例的信号中加入高斯白噪声
np.random.seed(42)
noise = 0.5 * np.random.randn(len(y))
y_noisy = y + noise
# 绘制含噪信号
plt.figure(figsize=(12, 4))
plt.plot(t[:300], y_noisy[:300], label='含噪信号')
plt.plot(t[:300], y[:300], 'r--', label='原始信号', alpha=0.8)
plt.legend()
plt.grid(True)
plt.show()

2 通过频域滤波恢复信号

# 对含噪信号进行 FFT
Y_noisy = fft(y_noisy)
freq_all = fftfreq(L, T)
# 设计带通滤波器:保留 40~130 Hz 的成分(覆盖50和120Hz)
filter_mask = (np.abs(freq_all) > 40) & (np.abs(freq_all) < 130)
Y_filtered = Y_noisy * filter_mask   # 其他频率置零
# 逆变换回到时域
from scipy.fft import ifft
y_clean = np.real(ifft(Y_filtered))
# 对比原始与恢复信号
plt.figure(figsize=(12, 4))
plt.plot(t[:200], y[:200], 'k', label='原始信号')
plt.plot(t[:200], y_clean[:200], 'g--', label='降噪后信号', linewidth=2)
plt.legend()
plt.grid(True)'频域滤波降噪效果')
plt.show()

效果:降噪后的波形几乎与原始信号重叠,说明通过频域掩膜(带通滤波)成功去除了带外噪声。


案例三:实际音频文件的频谱可视化

1 读取 WAV 文件

from scipy.io import wavfile
# 读取一个实际音频(请替换为自己的文件路径)
# 若没有,可用下面代码生成一个测试音频
sample_rate, audio_data = wavfile.read('example.wav')
# 如果音频是立体声,取单声道
if len(audio_data.shape) == 2:
    audio_data = audio_data[:, 0]
# 截取前1秒进行分析
duration = 1  # 秒
num_samples = int(sample_rate * duration)
audio_segment = audio_data[:num_samples]

提示:如果没有现成 wav 文件,可以用 from scipy.io.wavfile import write; write('test.wav', 44100, y_noisy.astype(np.int16)) 创建。

2 计算并绘制频谱图

# 执行 FFT
Y_audio = fft(audio_segment)
N_audio = len(audio_segment) // 2
freq_audio = fftfreq(len(audio_segment), 1/sample_rate)[:N_audio]
amp_audio = 2.0 / len(audio_segment) * np.abs(Y_audio[:N_audio])
# 绘制
plt.figure(figsize=(12, 4))
plt.semilogy(freq_audio, amp_audio)   # 用对数纵轴显示弱信号'音频频谱 (对数幅值)')
plt.xlabel('频率 (Hz)')
plt.ylabel('幅值 (对数)')
plt.grid(True, which='both', linestyle='--')
plt.xlim(0, sample_rate/2)   # 奈奎斯特频率
plt.show()

用途:此法可直观分析音乐的人声、乐器频率范围,或检测设备噪声的峰值。


常见问题与避坑指南(Q&A)

Q1:为什么我变换后的幅值不是原始信号的幅值?

A:默认的 fft 结果是未归一化的,需要手动除以采样点数 L,再乘以 2(单侧谱),公式:amplitude = 2.0 / L * np.abs(Y[:N]),对于直流分量(0Hz),幅值应为 0 / L * abs(Y[0])

Q2:FFT 后频率分辨率是多少?

A:分辨率 = Fs / L,若采样率 1000Hz、采样点 1000,则分辨率为 1Hz,提高分辨率需增加采样点数(即延长采样时间)。

Q3fftfreqrfftfreq 有何区别?

Afftfreq 返回完整的正负频率(长度为 L),rfftfreq 只返回正频率(用于实信号的快速计算),一般用 rfftfreqrfft 更高效。

Q4:如何避免频谱泄露?

A:在 FFT 前对信号加窗(如汉宁窗):from scipy.signal import windows; windowed_y = y * windows.hann(L),加窗可减少由于信号非整数周期截断引起的能量扩散。


SciPy 傅里叶变换的实战要点

  • 使用 scipy.fft.fft 执行正变换,ifft 进行逆变换。
  • 理解幅值归一化是确保频谱可读性的关键。
  • 频域滤波是信号降噪的核心手段,通过构造掩膜(布尔数组)实现。
  • 处理真实音频时,注意采样率与奈奎斯特频率(采样率的一半)。
  • 结合 fftfreqwindows 可大幅提升频谱质量。

掌握了上述案例,你就能处理大多数工程场景下的频域分析了,无论是传感器数据、音频处理还是振动信号,SciPy 的傅里叶工具都能帮你快速“透视”信号的频率组成。

抱歉,评论功能暂时关闭!