通俗易懂的講解FFT的讓你快速了解FFT

2020-11-22 電子發燒友

相信網上現在有很多關於FFT的教程,我曾經也參閱了很多網上的教程,感覺都不怎麼通俗易懂。在基本上的研究FFT,並且通過編程的形式實現之後。我決定寫一篇通俗易懂的關於FFT的講解。因此我在接下來的敘述中儘量非常通俗細緻的講解。

本人最早知道傅立葉變換的時候是沉迷於音樂的頻譜跳動無法自拔,當時就很想做一個音樂頻譜顯示器。搜閱了很多資料之後,才了解到傅立葉變換,和FFT。當然這都是以前的事情了,經過了系統的學習+2個星期的研究,自製了一個FFT的算法,不敢說速度上的優勢,但是個人認為是一種通俗易懂的實現方法。經過實際的VC++模擬實驗、和STM32跑的也很成功。

首先,要會FFT,就必須要對DFT有所了解,因為兩者之間本質上是一樣的。在此之前,先列出離散傅立葉變換對(DFT):

,k=0,1,…N-1

n=0,1…N-1

其中: 

但是FFT之所以稱之為快速傅立葉變換,就利用了以下的幾個性質(重中之重!)

周期性: 

對稱性: 

可約性: 

先把這仨公式放到這,接下來會用到。

根據這幾個特性,就可以將一個長的DFT運算分解為若干短序列的DFT運算的組合,從而減少運算量。在這裡,為了方便理解,我就用了一個按時間抽取的快速傅立葉變換(DIT-FFT)的方法。

首先,將一個序列x(n)一分為二

對於 ,k=0,1,…N-1   設N是2的整次冪 也就是N=2^M

將x(n)按照奇偶分組

x(2r)=x1(r)

x(2r+1)=x2(r)                     r=0,1,…..(N/2)-1

那麼將DFT也分為兩組來預算 

  (第一項是偶  第二項是奇)

這個時候,我們上邊提的三個性質中的可約性就起到作用了:

回顧一下: 

那麼這個式子就可以化為:

(式1-1)

則X1(k)和X2(k)都是長度為N/2的序列 x1(k)和x2(k)的N/2點的離散傅立葉變換

其中:

K=0,1,2…N/2-1

至此,一個N點的DFT就被分解為2個N/2的DFT。但是X1(k),和X2(k)只有N/2個點,也就是說式子(1-1)只是DFT前半部分。要求DFT的後半部分可以利用其對稱性求出後半部分為:

(式1-2)

那麼式(1-1)和(1-2)就可以專用一個蝶形信號流圖符號來表示。如圖:

以N=8為例,可以用下圖表示:

通過這樣的分解,每一個N/2點DFT只需(N^2)/4次複數相乘計算,明顯的節省了運算量。

以此類推,繼續將已經得出的X1(k)和X2(k)按照奇偶分解,還是和上邊一樣的方法。那麼就得出了百度上都可以找到的一大推的這個圖片了。  (笑)

對於這張圖片我想強調的一點就是第二階蝶形運算的時候,WN0之後不應該是WN1嗎,為什麼是W2N了,這個問題之前困擾了我一段時間,但是不要忘了,前者的    W0N的展開是W0N/2  因為此時N已經按照奇偶分開了,所以是N/2  而W2N/2也就是  W2N是根據可約性得出的,在這裡不能混淆.。

對於運算效率就不用多提了

以上就是FFT算法的理論內容了,接下來就是用C語言對這個算法的實現了,對於FFT算法C語言的實現,網上的方法層出不窮,介於本人比較懶(懶得看別人的程序),再加上自給自足豐衣足食的原則,我自己也寫了一個個人認為比較通俗易懂的程序,並且為了幫助讀者理解,我特意儘量減少了庫函數的使用,一些基本的函數都是自己寫的(難免有很多BUG),但是作為FFT算法已經夠用了,目前這個程序只能處理2^N的數據,理論上來講如果不夠2^N的話可以在後面數列補0這種操作為了簡約我也就沒有實現,但是主要的功能是具備的,下面是代碼:

/*

時間:2018年11月24日

功能:將input裡的數據進行快速傅立葉變換  並且輸出

*/

#include

#include

#define PI 3.1415926

#define FFT_LENGTH                8

double input[FFT_LENGTH]={1,1,1,1,1,1,1,1};

struct complex1{                //定義一個複數結構體

double real;        //實部

double image;        //虛部

};        

//將input的實數結果存放為複數

struct complex1 result_dat[8];

/*        虛數的乘法

*/

struct complex1 con_complex(struct complex1 a,struct complex1 b){

struct complex1 temp;

temp.real=(a.real*b.real)-(a.image*b.image);

temp.image=(a.image*b.real)+(a.real*b.image);

return temp;

}

/*

簡單的a的b次方

*/

int mypow(int a,int b){

int i,sum=a;

if(b==0)return 1;

for(i=1;i

sum*=a;        

}

return sum;

}

/*

簡單的求以2為底的正整數

*/

int log2(int n){

unsigned i=1;

int sum=1;

for(i;;i++){

sum*=2;

if(sum>=n)break;

}

return i;

}

/*

簡單的交換數據的函數

*/

void swap(struct complex1 *a,struct complex1 *b){

struct complex1 temp;

temp=*a;

*a=*b;

*b=temp;

}

/*

dat為輸入數據的數組

N為抽樣次數  也代表周期  必須是2^N次方

*/

void fft(struct complex1 dat[],unsigned char N){        

/*最終  dat_buf計算出 當前蝶形運算奇數項與W  乘積

dat_org存放上一個偶數項的值

*/

struct complex1 dat_buf,dat_org;

/*        L為幾級蝶形運算    也代表了2進位的位數

n為當前級蝶形的需要次數  n最初為N/2 每級蝶形運算後都要/2

i j為倒位時要用到的自增符號  同時  i也用到了L碟級數   j是計算當前碟級的計算次數 

re_i i_copy均是倒位時用到的變量

k為當前碟級  cos(2*pi/N*k)的  k   也是e^(-j2*pi/N)*k  的  k

*/

unsigned char L,i,j,re_i=0,i_copy=0,k=0,fft_flag=1;

//經過觀察,發現每級蝶形運算需要N/2次運算,共運算N/2*log2N  次  

unsigned char fft_counter=0;

//在此要進行補2   N必須是2^n   在此略

//蝶形級數  (L級)

L=log2(N);        

//計算每級蝶形計算的次數(這裡只是一個初始值)  之後每次要/2

//n=N/2;

//對dat的順序進行倒位

for(i=1;i

i_copy=i;

re_i=0;

for(j=L-1;j>0;j--){

//判斷i的副本最低位的數字  並且移動到最高位  次高位  ..

//re_i為交換的數   每次它的數字是不能移動的 並且循環之後要清0

re_i|=((i_copy&0x01)<

i_copy>>=1;

}

swap(&dat[i],&dat[re_i]);

}

//進行fft計算

for(i=0;i

fft_flag=1;

fft_counter=0;

for(j=0;j

if(fft_counter==mypow(2,i)){                //控制隔幾次,運算幾次,

fft_flag=0;

}else if(fft_counter==0){                //休止結束,繼續運算

fft_flag=1;

}

//當不判斷這個語句的時候  fft_flag保持  這樣就可以持續運算了

if(fft_flag){

dat_buf.real=cos((2*PI*k)/(N/mypow(2,L-i-1)));

dat_buf.image=-sin((2*PI*k)/(N/mypow(2,L-i-1)));

dat_buf=con_complex(dat[j+mypow(2,i)],dat_buf);

//計算 當前蝶形運算奇數項與W  乘積

dat_org.real=dat[j].real;

dat_org.image=dat[j].image;                //暫存

dat[j].real=dat_org.real+dat_buf.real;

dat[j].image=dat_org.image+dat_buf.image;                

//實部加實部   虛部加虛部

dat[j+mypow(2,i)].real=dat_org.real-dat_buf.real;

dat[j+mypow(2,i)].image=dat_org.image-dat_buf.image;

//實部減實部        虛部減虛部

k++;

fft_counter++;

}else{

fft_counter--;                                //運算幾次,就休止幾次

k=0;

}

}

}

}

void main(){

int i;

//先將輸入信號轉換成複數

for(i=0;i

result_dat[i].image=0;        

//輸入信號是二維的,暫時不存在複數

result_dat[i].real=input[i];

//result_dat[i].real=10;

//輸入信號都為實數

}

fft(result_dat,FFT_LENGTH);

for(i=0;i

input[i]=sqrt(result_dat[i].real*result_dat[i].real+result_dat[i].image*result_dat[i].image);

//取模

printf("%lf\n",input[i]);

}

while(1);

}

這個程序中input這個數組是輸入信號,在這裡只模擬抽樣了8次,輸出的數據也是input,如果想看其它序列的話,可以改變FFT_LENGTH的值以及 input裡的內容,程序輸出的是實部和虛部的模,如果單純想看實部或者虛部的幅度的話,請自行修改程序~這就是本次淺談FFT以及FFT算法的基本實現的全部內容了參考書籍:數位訊號處理 

打開APP閱讀更多精彩內容

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容圖片侵權或者其他問題,請聯繫本站作侵刪。 侵權投訴

相關焦點

  • 每日函數——fft
    fft快速傅立葉變換語法Y  = fft(X)Y  = fft(X
  • matlab下實現FFT信號分析
    利用matlab做頻譜分析前我們需要了解分析過程中的一些基礎知識,matlab中的 fft 函數用法、fftshift 函數的用法函數 1  fft :作用:快速傅立葉變換。語法:Y = fft(X)Y = fft(X,n)Y = fft(X,n,dim)語法:Y = fft(X) 用快速傅立葉變換 (FFT) 算法計算 X 的離散傅立葉變換 (DFT)。
  • 史上最簡單的FFT(快速傅立葉變換)
    4 離散傅立葉變換樸素的傅立葉變換還是太慢了,所以我們要進行快速的傅立葉變換,藉助於分治的思想。asin(1.0)*2; typedef complex<double> cp; cp tmp[N<<1],a[N<<1],b[N<<1]; int n,m;cp c(int n,int k){ return cp(cos(2*k*PI/n),sin(2*k*PI/n)); } void fft
  • 用FPGA實現FFT算法(圖)
    快速傅立葉變換(fast fourier transformation,簡稱fft)使dft運算效率提高1~2個數量級。其原因是當n較大時,對dft進行了基4和基2分解運算。fft算法除了必需的數據存儲器ram和旋轉因子rom外,仍需較複雜的運算和控制電路單元,即使現在,實現長點數的fft仍然是很困難。
  • 快速傅立葉變換FFT在MATLAB中的實現
    FFT是DFT的快速算法可以節省大量的計算時間,其本質仍然是DFT。(y); %採集信號的長度t=(0:1:N-1)*T; %定義整個採集時間點t=t';  %轉置成列向量figureplot(t,y)xlabel('時間')ylabel('信號值')title('時域信號')2. fft
  • 數字與信號處理實驗3 用FFT進行譜分析
    四、實驗內容 (1)用Matlab或C語言編制信號產生子程序,產生典型信號供譜分析用; (2)對給出信號逐個進行譜分析,繪出序列和幅頻特性曲線; (3)設計利用快速傅立葉變換FFT 計算序列頻譜程序; (4)對結果進行分析; (5)完成實驗報告。 五、實驗結果1.
  • MATLAB實驗——FFT變換
    圖像的傅立葉變換與一維信號的傅立葉變換變換一樣,有快速算法,具體參見參考書目,有關傅立葉變換的快速算法的程序不難找到。實際上,現在有實現傅立葉變換的晶片,可以實時實現傅立葉變換。實驗代碼:clc;clear;alex=imread('E:\SEO\公眾號\0最菜程序猿\圖片\1.jpg');alex1=rgb2gray(alex);alex2=im2double(alex1);alex=fft2(alex2);subplot(2,1,1);imshow(alex1);alex1=fftshift(alex);alex2
  • 使用STM32 的DSP庫進行FFT變換
    * 使用三角函數生成採樣點,供FFT計算* 進行FFT測試時,按下面順序調用函數即可:* dsp_asm_init();* dsp_asm_test();*/#include "stm32f10x.h"#include "dsp_asm.h"#include "stm32_dsp.h"#include "table_fft.h"
  • 基於MSP430系列微控制器的FFT算法實現
    實測結果顯示對一個信號周期256個採樣點的快速傅立葉變換分析,完成全部計算僅需要0.3 s的時間,前10次諧波的計算相對誤差低於千分之一。所研製的在供電質量監測系統完全滿足用戶要求。1 利用微控制器實現FFT算法快速傅立葉變換在信號處理中的線性濾、相關計算、譜分析等方面起著重要的作用。將N點採樣數據分解為更短的數據段來進行計算可以提高計算效率,目前使用最廣泛的是基2的FFT算法。圖1給出基2按時間抽取的快速傅立葉變換中的基本運算過程379-388181-189。
  • 用C語言實現FFT算法
    /*****************fft programe*********************/#include typedef.h #include math.h本文引用地址:http://www.eepw.com.cn/article/150637.htmstruct compx
  • PyTorch中的傅立葉卷積:通過FFT計算大核卷積的數學原理和代碼
    因為快速傅立葉變換的算法複雜度比卷積低。 直接卷積的複雜度為O(n),因為我們將g中的每個元素傳遞給f中的每個元素。 快速傅立葉變換可以在O(n log n)的時間內計算出來。 當輸入數組很大時,它們比卷積要快得多。 在這些情況下,我們可以使用卷積定理來計算頻率空間中的卷積,然後執行傅立葉逆變換以返回到位置空間。
  • FFT
    最近在項目中需要用到FFT,之前對於FFT也只是有一個模糊的印象也並不清楚他的具體物理意義,之前幾次想學習都被擱置了,現在項目需要又從新學習,在此把我收穫的和大家分享一下:1- FFT簡介FFT是一種DFT的高效算法,稱為快速傅立葉變換
  • FFT 的物理意義
    FFT是離散傅立葉變換的快速算法,可以將一個信號變換到頻域。有些信號在時域上是很難看出什麼特徵的,但是如果變換到頻域之後,就很容易看出特徵了。這就是很多信號分析採用FFT變換的原因。另外,FFT可以將一個信號的頻譜提取出來,這在頻譜分析方面也是經常用的。
  • 基於DSP的FFT算法實現
    基於DSP的FFT算法實現1、FFT的原理快速傅氏變換(FFT)是離散傅氏變換的快速算法,它是根據離散傅氏變換的奇、偶、
  • 快速傅立葉變換(FFT)結果的物理意義是什麼?(附Matlab程序)
    FFT是離散傅立葉變換的快速算法,可以將一個信號變換到頻域。有些信號在時域上是很難看出什麼特徵的,但是如果變換到頻域之後,就很容易看出特徵了。這就是很多信號分析採用FFT變換的原因。另外,FFT可以將一個信號的頻譜提取出來,這在頻譜分析方面也是經常用的。
  • 幾行Matlab代碼教你上手傅立葉變換
    f0 = ones(1,n);g0 = fft(f0);figure, stem(f0), title('原函數');figure, stem(abs(g0)), title('傅立葉變換的幅度');% 實驗1close all; clear; n = 64;