快速傅里叶变换及Python代码实现

news2024/12/24 2:11:12

一、前言

  我想认真写好快速傅里叶变换(Fast Fourier Transform,FFT),所以这篇文章会由浅到细,由窄到宽的讲解,但是傅里叶变换对于寻常人并不是很容易理解的,所以对于基础不牢的人我会通过前言普及一下相关知识。

  我们复习一下三角函数的标准式:

y=Acos⁡(ωx+θ)+k

  A代表振幅,函数周期是2πw,频率是周期的倒数w2π,θ是函数初相位,k在信号处理中称为直流分量。这个信号在频域就是一条竖线。

  我们再来假设有一个比较复杂的时域函数y=f(t),根据傅里叶的理论,任何一个周期函数可以被分解为一系列振幅A,频率ω或初相位θ正弦函数的叠加

y=A1sin(ω1t+θ1)+A2sin(ω2t+θ2)+A3sin(ω3t+θ3)

  该信号在频域有三条竖线组成,而竖线图我们把它称为频谱图,大家可以通过下面的动画了解

  如图可知,通过时域到频域的变换,我们得到了一个从侧面看的频谱,但是这个频谱并没有包含时域中全部的信息。因为频谱只代表每个正弦波对应频率的振幅是多少,而没有提到相位。基础的正弦波Asin(wt+θ)中,振幅,频率,相位缺一不可不同相位决定了波的位置,所以对于频域分析,仅仅有频谱(振幅谱)是不够的,我们还需要一个相位谱。

  我依稀记得高中学正弦函数的是时候,θ的多少决定了正弦波向右移动多少。当然那个时候横坐标是相位角度,而时域信号的横坐标是时间,因此我们只需要将时间转换为相位角度就得到了初相位。相位差则是时间差在一个周期中所占的比例

θ=2πtT

  所以傅里叶变换可以把一个比较复杂的函数转换为多个简单函数的叠加,将时域(即时间域)上的信号转变为频域(即频率域)上的信号,看问题的角度也从时间域转到了频率域,因此在时域中某些不好处理的地方,在频域就可以较为简单的处理,这就可以大量减少处理信号计算量。信号经过傅里叶变换后,可以得到频域的幅度谱以及相位谱,信号的幅度谱相位谱是信号傅里叶变换后频谱的两个属性

傅里叶用途

  • 时域复杂的函数,在频域就是几条竖线
  • 求解微分方程,傅里叶变换则可以让微分和积分在频域中变为乘法和除法

傅里叶变换相关函数

  假设我们的输入信号的函数是

S=0.2+0.7∗cos⁡(2π∗50t+20180π)+0.2∗cos⁡(2π∗100t+70180π)

可以发现直流分量是0.2,以及两个余弦函数的叠加,余弦函数的幅值分别为0.7和0.2,频率分别为50和100,初相位分别为20度和70度。

freqs = np.fft.fftfreq(采样数量, 采样周期)  通过采样数与采样周期得到时域序列经过傅里叶变换后的频率序列

np.fft.fft(原序列)  原函数值的序列经过快速傅里叶变换得到一个复数数组,复数的模代表的是振幅,复数的辐角代表初相位

np.fft.ifft(复数序列)  复数数组 经过逆向傅里叶变换得到合成的函数值数组

案例:针对合成波做快速傅里叶变换,得到分解波数组的频率、振幅、初相位数组,并绘制频域图像。

 python代码实现

 MATLAB实现

补充一些复数知识(很重要)

1、复数S的几种表示形式:

  • 实部、虚部(直角坐标系):a+bj      (a是实部,b是虚部)
  • 幅值、相位(指数系):rejθ  (r是幅值,θ是相角,ejθ是相位)
  • 极坐标表示法:r∠θ
  • 指数系<−−>指教坐标系:rejθ=r(cosθ+jsinθ)=rcosθ+jrsinθ

  因此,我们可以通过以下方法得到:

实部: a=rcosθ, real = np.real(S) 

虚部: b=rsinθ, imag= np.imag(S) 

幅值:r=a2+b2, magnitude = np.abs(S)  或  magnitude = np.sqrt(real**2+imag**2) 

相角(以弧度为单位rad):θ=tan−1(ba) 或 θ=atan2(b,a)。 angle = np.angle(D(F, T)) 

相角(以角度为单位deg):deg=rad∗180π,rad2deg(atan2(b,a))。 deg = rad * 180/np.pi  

相位: phase = np.exp(1j * np.angle(S)) 

基于傅里叶变换的频域滤波

从某条曲线中除去一些特定的频率成份,这在工程上称为“滤波”。

含噪信号是高能信号与低能噪声叠加的信号,可以通过傅里叶变换的频域滤波实现降噪。

通过FFT使含噪信号转换为含噪频谱,去除低能噪声,留下高能频谱后再通过IFFT留下高能信号。

案例:基于傅里叶变换的频域滤波为音频文件去除噪声。

  1、读取音频文件,获取音频文件基本信息:采样个数,采样周期,与每个采样的声音信号值。绘制音频时域的:时间/位移图像

import numpy as np
import numpy.fft as nf
import scipy.io.wavfile as wf
import matplotlib.pyplot as plt

# 读取音频文件
sample_rate, noised_sigs = wf.read('./da_data/noised.wav')
print(sample_rate)  # sample_rate:采样率44100
print(noised_sigs.shape)    # noised_sigs:存储音频中每个采样点的采样位移(220500,)
times = np.arange(noised_sigs.size) / sample_rate

plt.figure('Filter')
plt.subplot(221)
plt.title('Time Domain', fontsize=16)
plt.ylabel('Signal', fontsize=12)
plt.tick_params(labelsize=10)
plt.grid(linestyle=':')
plt.plot(times[:178], noised_sigs[:178], c='orangered', label='Noised')
plt.legend()

 

  2、基于傅里叶变换,获取音频频域信息,绘制音频频域的:频率/能量图像

# 傅里叶变换后,绘制频域图像
freqs = nf.fftfreq(times.size, times[1] - times[0])
complex_array = nf.fft(noised_sigs)
pows = np.abs(complex_array)

plt.subplot(222)
plt.title('Frequency Domain', fontsize=16)
plt.ylabel('Power', fontsize=12)
plt.tick_params(labelsize=10)
plt.grid(linestyle=':')
# 指数增长坐标画图
plt.semilogy(freqs[freqs > 0], pows[freqs > 0], c='limegreen', label='Noised')
plt.legend()

  3、将低频噪声去除后绘制音频频域的:频率/能量图像

# 寻找能量最大的频率值
fund_freq = freqs[pows.argmax()]
# where函数寻找那些需要抹掉的复数的索引
noised_indices = np.where(freqs != fund_freq)
# 复制一个复数数组的副本,避免污染原始数据
filter_complex_array = complex_array.copy()
filter_complex_array[noised_indices] = 0
filter_pows = np.abs(filter_complex_array)

plt.subplot(224)
plt.xlabel('Frequency', fontsize=12)
plt.ylabel('Power', fontsize=12)
plt.tick_params(labelsize=10)
plt.grid(linestyle=':')
plt.plot(freqs[freqs >= 0], filter_pows[freqs >= 0], c='dodgerblue', label='Filter')
plt.legend()

  4、基于逆向傅里叶变换,生成新的音频信号,绘制音频时域的:时间/位移图像

filter_sigs = nf.ifft(filter_complex_array).real
plt.subplot(223)
plt.xlabel('Time', fontsize=12)
plt.ylabel('Signal', fontsize=12)
plt.tick_params(labelsize=10)
plt.grid(linestyle=':')
plt.plot(times[:178], filter_sigs[:178], c='hotpink', label='Filter')
plt.legend()

  5、重新生成音频文件

# 生成音频文件
wf.write('./da_data/filter.wav', sample_rate, filter_sigs)
plt.show()

离散傅里叶变换 (DFT)

  离散傅里叶变换(DFT)对有限长时域离散信号的频谱进行等间隔采样,频域函数被离散化了,便于信号的计算机处理。DFT的运算量太大,FFT是离散傅里叶变换的快速算法。

  说白了FFT和DFT它俩就是一个东东,只不过复杂度不同,

  有时候我们能够看到N点傅里叶变换,我个人理解是这个N点是信号前面N个连续的数值,即N点FFT意思就是截取前面N个信号进行FFT,这样就要求我们的前N个采样点必须包含当前信号的一个周期,不然提取的余弦波参数与正确的叠加波的参数相差很大。

  如果在N点FFT的时候,如果这N个采样点不包含一个周期呢?或者说我们的信号压根不是一个周期函数咋办?或者有一段是噪音数据呢?如果用FFT计算,就会对整体结果影响很大,然后就有人想通过局部来逼近整体,跟微积分的思想很像,将信号分成一小段一小段,然后对每一小段做FFT,就跟分段函数似的,无数个分段函数能逼近任意的曲线((⊙o⊙)…应该没错吧),这样每一段都不会互相影响到了。

二、短时傅里叶变换 (STFT)

  在短时傅里叶变换过程中,窗的长度决定频谱图的时间分辨率和频率分辨率,窗长越长,截取的信号越长,信号越长,傅里叶变换后频率分辨率越高,时间分辨率越差;相反,窗长越短,截取的信号就越短,频率分辨率越差,时间分辨率越好,也就是说短时傅里叶变换中,时间分辨率和频率分辨率之间不能兼得,应该根据具体需求进行取舍。

计算短时傅里叶变换,需要指定的有:

  • 每个窗口的长度:nsc
  • 每相邻两个窗口的重叠率:nov
  • 每个窗口的FFT采样点数:nff

可以计算的有:

  • 海明窗:w=hamming(nsc, 'periodic')
  • 信号被分成了多少片
  • 短时傅里叶变换:

python库librosa实现

librosa.stft(y, n_fft=2048, hop_length=None, win_length=None, window='hann', center=True, pad_mode='reflect')

短时傅立叶变换(STFT),返回一个复数矩阵使得D(f,t)

参数:

  • y:音频时间序列
  • n_fftFFT窗口大小,n_fft=hop_length+overlapping
  • hop_length帧移,如果未指定,则默认win_length / 4。
  • win_length每一帧音频都由window()加窗。窗长win_length,然后用零填充以匹配N_FFT。默认win_length=n_fft
  • window:字符串,元组,数字,函数 shape =(n_fft, )
    • 窗口(字符串,元组或数字);
    • 窗函数,例如scipy.signal.hanning
    • 长度为n_fft的向量或数组
  • center:bool
    • 如果为True,则填充信号y,以使帧 D [:, t]以y [t * hop_length]为中心。
    • 如果为False,则D [:, t]从y [t * hop_length]开始
  • dtypeD的复数值类型。默认值为64-bit complex复数
  • pad_mode如果center = True,则在信号的边缘使用填充模式。默认情况下,STFT使用reflection padding。

返回:

  • STFT矩阵D,shape =(1 + nfft2,t)
librosa.magphase(D, power=1)

将复值频谱D分离成其幅度(S)和相位(P)

参数

  • D:复值谱图,np.ndarray [shape =(d,t),dtype = complex]
  • power:幅度谱图的指数,例如,1代表能量,2代表功率,等等。

返回

  • D_mag :D的幅值,np.ndarray [shape =(d,t),dtype = real]
  • D_phase :D的相位,np.ndarray [shape =(d,t),dtype = complex],exp(1.j * phi)其中phiD的相位
y, _ = librosa.load("p225_002.wav", sr=16000)

D = librosa.stft(y, n_fft=2048, hop_length=None, win_length=None, window='hann', center=True, pad_mode='reflect')
print(D.shape)    # (1025, 127)

# 将复值频谱D分离成其幅度(S)和相位(P)的部件
magnitude, phase = librosa.magphase(D, power=1)
# magnitude        # 赋值 [shape =(d,t),dtype = real] (1025, 127)
# phase.shape    # 相位 [shape =(d,t),dtype = complex] (1025, 127) 

angle = np.angle(phase)     # 获取相角(以弧度为单位)

tensorflow实现

一句话实现分帧、加窗、STFT变换

# [batch_size, signal_length].  batch_size and signal_length 可能会不知道
signals = tf.placeholder(tf.float32, [None, None])

# `stfts` 短时傅里叶变换:就是对信号中每一帧信号进行傅里叶变换 
# shape is [batch_size, ?, fft_unique_bins]
# 其中 fft_unique_bins = fft_length // 2 + 1 = 513.
stfts = tf.contrib.signal.stft(signals, frame_length=1024, frame_step=512,
                                                            fft_length=1024)

wlen:窗长

frame_length是信号中帧的长度

frame_step是帧移

fft_length:做fft变换的长度,或一种说话:fft变换所取得N点数,在有些地方也表示为NFFT。

注意:FFT的长度必须大于或者等于win的长度或者帧长。以获得更高的频域分辨率

FFT后的分辨率(频率的间隔)为fs/NFFT。当NFFT>wlen时就是在数据补零后做FFT,做的FFT得到的频谱等于以wlen长数据FFT的频谱中内插。

numpy库实现

上面的一行代码相当于下面一大段代码

 View Code

三、frequency bin

在读paper的时候总是会遇到 frequency bin (频率窗口)这个词,

  frequency bin 是指:raw data 经过FFT后得到的频谱图(频域率)中,频率轴的频率间隔或分辨率,通常取决采样率和采样点。

采样率采样点数frequencybin=采样率采样点数=fsampleNrecode

  Nrecode  是信号在时域的采样点数,频谱中的频率点或线的数量为Nrecode2  (奈奎斯特采样定理)

  频谱的第一个频点始终为直流(频率=0),最后一个频点为fsample2−fsampleNrecode  。频点采用相等的间隔,这间隔通常用frequency bin频率窗口)或FFT bin表示

例子1:我们可以作用82MHz的采样频率,取得8200个数据记录,frequency bin=820000008200=10000=10kHz。

例子2:frequency bin是频率域中采样点之间的间隔。例如,如果采样率为100赫兹,FFT为100个点,frequency bin=1,则在[0 100)赫兹之间有100个点。因此,您将整个100赫兹范围划分为100个间隔,如0-1赫兹、1-2赫兹等。每一个如此小的间隔,比如0-1Hz,都是一个frequency bin(频率箱)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/80746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里巴巴2022年最新最全500道Java后端面试大全(值得收藏)

进大厂是大部分程序员的梦想&#xff0c;而进大厂的门槛也是比较高的&#xff0c;所以这里整理了一份阿里、美团、滴滴、头条等大厂面试大全其中概括的知识点有&#xff1a;Java基础、spring、springmvc、springboot、springcloud、JVM、Tomcat、dubbo、netty、zookeeper共有50…

Java中四大线程池应用及详解

线程池的思想 我们使用线程的时候就去创建一个线程&#xff0c;这样实现起来非常简便&#xff0c;但是就会有一个问题&#xff1a; 如果并发的线程数量很多&#xff0c;并且每个线程都是执行一个时间很短的任务就结束了&#xff0c;这样频繁创建线程就会大大降低系统的效率&a…

移动网络技术--名词介绍

GPRS网络制式&#xff08;General Packet Radio Service&#xff09;为“通用分组无线服务”&#xff0c;它是利用“包交换”&#xff08;Packet-Switched&#xff09;的概念所发展出的一套基于GSM系统的无线传输方式。 GGSN&#xff08;Gateway GPRS Supporting Node,网关GPR…

Nginx入门到弃坑---安装与使用篇(2)

1 下载 官网传送门下载传送门点击下载最新Windows-1.23版下载传送门点击下载最新Linux-1.23版下载传送门 2 Windows安装 2.1 环境介绍 下载完成后解压缩 目录如下 配置文件地址&#xff1a;.\nginx-1.23.2\conf\nginx.conf&#xff0c;默认配置的nginx监听的端口为80&…

监控系列(一)DM8+Prometheus+Grafana搭建

一、背景 近期进行适配&#xff0c;因用户统一监控平台使用的是promethesugrafanaaltermannger这一套&#xff0c;因此对达梦数据库进行适配对接。 目前主要有两种方式&#xff1a; 1. 部署Dem管理系统对外提供接口推送到prometheus进行采集数据&#xff0c;采集项可查看《De…

【云计算与大数据技术】分布式计算、虚拟化技术、并行编程技术等技术讲解(超详细必看)

一、分布式计算 分布式计算是一种计算方法&#xff0c;和集中式计算相对&#xff0c;随着计算的发展&#xff0c;一些应用需要巨大的计算能力才能完成&#xff0c;如果采用集中式计算则需要耗费很长的时间&#xff0c;而分布式计算将应用分解成许多更小的部分&#xff0c;分配…

文献阅读(195)物理设计/时序分析

文章目录物理设计时序分析题目&#xff1a;Intelligent Design Automation for 2.5/3D Heterogeneous SoC Integration时间&#xff1a;2020会议&#xff1a;ICCAD研究机构&#xff1a;国立台湾大学 本篇论文的主要贡献&#xff1a; 物理设计&#xff1a;包括RDL布线和板级布…

蚁群优化算法解决TSP问题(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

CSS 3万字超详细总结

文章目录1. CSS简介2. CSS的使用2.1 行内样式2.2 内部样式表2.3 外部样式表2.4 多重样式与样式优先级3. CSS选择器3.1 简单选择器3.1.1 元素选择器3.1.2 id选择器3.1.3 class选择器3.2 组合器选择器3.2.1 后代选择器3.2.2 子选择器3.2.3 相邻兄弟选择器3.2.4 通用兄弟选择器3.3…

C# 流程控制语句

一 结构化程序设计的三种基本流程 1 顺序 分支 循环 2 简单语句 最简单的语句&#xff1a;方法调用语句及赋值语句 后面有个分号 如&#xff1a; System.Console.Write("Hello World"); ba>0?a:-a; sTextBox1.Text; dint.Parse(s);注意&#xff1a;没有表达式…

SpringBoot简单优雅实现图片上传功能(超详细)

文章目录前言技术栈项目目录前端实现index.htmlscript.js后端实现MultipartFile 介绍配置文件实体类ControllerMapperService拦截器测试结果展示前言 最近有一个需求需要实现图片上传&#xff0c;因此&#xff0c;本人找到了一个可以快速实现该功能的插件mini-upload-form。在…

CAS:2374782-03-1,NOTA-FAPI-4化学试剂供应

试剂描述 NOTA-FAPI-4是FAPI-4的类似物和成纤维细胞活化蛋白&#xff08;FAP&#xff09;抑制剂。NOTA-FAPI-4可作为PET示踪剂用于检测与成纤维细胞活化蛋白相关的紊乱。 试剂基本信息 1、名称&#xff1a;NOTA-FAPI-4 2、CAS编号&#xff1a;2374782-03-1 3、分子式&#x…

Seata模式

爬虫组件分析目录概述需求&#xff1a;设计思路实现思路分析1.一、AT 模式参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge Survi…

_4LeetCode代码随想录算法训练营第四天-C++

_4LeetCode代码随想录算法训练营第四天-C 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II 24.两两交换链表中的节点 整体思路 不是简单地交换值&#xff0c;而是交换指针地指向。 终止条件为&#xff1a; cur->next ! nul…

偏微分方程重要的前置知识

现在觉得很dog 开学期末考试正好美赛。无法评论&#xff0c;无法评论。乐淘淘&#xff0c;乐淘淘。期末考试不要延迟&#xff0c;求求了或者不安排在下学期第一周也可以。。。。反正求求了&#xff0c;美赛机会难得当然&#xff0c;如果是偏微分方程的问题的话&#xff0c;其实…

springboot连接Oracle的注意点(数据库信息配置、主键精度问题、OJDBC jar包、Oracle主键自增问题)

开篇废话&#xff1a;&#xff08;前段时间因为太忙没有坚持写博客&#xff0c;导致很久没有更新&#xff0c;今天终于忙里偷闲写上一篇&#xff09; 最近做了一个项目&#xff0c;数据库用的是Oracle&#xff0c;由于之前一直用的是MySQL&#xff0c;所以在一些细节配置上不是…

详细教你用NPS搭建内网穿透服务

文章目录 前言一、NPS概述 NPS的原理 二、NPS服务器搭建 1、下载软件2、云服务器配置 2.1、防火墙配置2.2、用WinSCP远程上传服务文件2.3、使用SSH终端安装启动2.4、修改配置文件 三、客户端连接总结 前言 相信大家外出旅游或者出差都是背着轻薄本&#xff0c;如果空闲之余想…

【Dubbo3高级特性】「实战开发」适配日志框架并支持运行时动态切换使用的日志框架开发实战

日志框架适配及运行时管理 本节内容主要是针对于如何在Dubbo中适配日志框架并支持运行时动态切换使用的日志框架&#xff0c;首先前提是需要进行启动我们Dubbo服务的Qos服务&#xff0c;它主要用于作为我们的操作对应的日志切换的功能实现机制 特性说明 日志框架适配&#x…

MATLB|基于matpower优化调度的风力模型预测

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

T6300A 网络综合测试仪 以太网数据 千兆以太网测试仪

一款功能强大、便携式、方便使用、价格便宜的高性价比手持式以太网测试仪是企业中网络管理和维护人员的刚需仪器。好的以太网测试仪可以帮助工作人员迅速解决网络不通、网速慢、丢包、延迟等问题。 当今以太网测试仪市场参差不齐&#xff0c;说的功能一个比一个强&#xff0c;…