Innehållsförteckning:

EasyFFT: Fast Fourier Transform (FFT) för Arduino: 6 steg
EasyFFT: Fast Fourier Transform (FFT) för Arduino: 6 steg

Video: EasyFFT: Fast Fourier Transform (FFT) för Arduino: 6 steg

Video: EasyFFT: Fast Fourier Transform (FFT) för Arduino: 6 steg
Video: Juggling Machine Version 1.1 2024, November
Anonim
Image
Image

Mätning av frekvens från den fångade signalen kan vara en svår uppgift, särskilt på Arduino eftersom den har lägre beräkningseffekt. Det finns metoder tillgängliga för att fånga nollkorsning där frekvensen fångas genom att kontrollera hur många gånger signalen korsar nolllinjer inom den givna tiden. En sådan metod kanske inte fungerar när signalen är en kombination av olika frekvenser.

Detta är på något sätt svårt att koda om du inte kommer från en sådan bakgrund. Men att vara en tinkerer den här koden kan vara mycket användbar för olika projekt relaterade till musik, signalanalys. Motivet för detta projekt var att förbereda en kod som är lätt att implementera på Arduino utan att komma in i bakgrunden av den.

Detta projekt förklarar inte hur FFT fungerar men förklarar tillämpningen av FFT -funktionen. Samma process förklaras också i den bifogade videon.

Om du bara är intresserad av tillämpning av kod och inte för en förklaring av den. Du kan hoppa direkt till steg 3.

Steg 1: Introduktion till frekvensomvandling

Introduktion till frekvensomvandling
Introduktion till frekvensomvandling
Introduktion till frekvensomvandling
Introduktion till frekvensomvandling

Varje signal kan bestå av en kombination av olika sinusformade vågor. Så vilken tidsbaserad signal som helst kan också visas som en kombination av de olika sinuserna för olika amplituder.

Jag försökte förklara hur DFT (diskret Fourier-transform) fungerar i en av de tidigare instruerbara (https://www.instructables.com/id/Arduino-Frequency…). Dessa metoder är extremt långsamma för alla realtidsapplikationer. vilket gör det nästan värdelöst.

På bilden visas en signal som är en kombination av två frekvenser f2 och f5. Denna signal multipliceras med test -sinusvågor med värden f1 till f5.

Det kan matematiskt visas att -summation av multiplikation av två harmoniska datamängder med olika frekvens tenderar till noll (högre antal data kan leda till smetresultat). I vårt fall, Om dessa två multiplikationsfrekvenser har samma (eller mycket nära) frekvens är summan av multiplikationen icke -nolltalet.

Så om vår signal multipliceras med f1 summeras multiplikationen till noll (nära noll för verklig tillämpning). liknande är fallet för f3, f4. Men för värdet kommer f2 och f5 -utgången inte att vara noll, men betydligt högre än resten av värdena.

Här testas en signal med 5 frekvenser, så signalen måste multipliceras med fem frekvenser. En sådan intensiv beräkning tar längre tid. Matematiskt visas att för N antal sampel krävs N*N komplex multiplikation.

Steg 2: Snabb Fourier Transform

För att göra beräkningen av DFT snabbare utvecklades FFT -algoritmen av James Cooley och John Tukey. Denna algoritm anses också vara en av de viktigaste algoritmerna under 1900 -talet. Den delar upp en signal i en udda och jämn sekvenserad del som gör ett antal nödvändiga beräkningar lägre. Genom att använda den totala erforderliga komplexa multiplikationen kan reduceras till NlogN. vilket är en betydande förbättring.

Du kan referera nedanstående referenser som jag hänvisade till när du skrev koden för en detaljerad förståelse av matematiken bakom FFT:

1.

2.

3.

4.

Steg 3: Förklaring av kod

1. Snabb sinus och kosinus:

Beräkning FFT tar värdet av olika sinus och cosinus flera gånger. Den inbyggda funktionen hos Arduino är inte tillräckligt snabb och tar en bra tid att ge det önskade värdet. Vilket gör koden betydligt långsammare (fördubblar tiden för 64 prover). För att motverka detta problemvärde för sinus för 0 till 90 grader lagras som multipel av 255. Om du gör det elimineras behovet av att använda lagringsnummer som float och vi kan lagra det som byte som tar 1/4 plats på Arduino. Sinusdata måste klistra in överst i koden för att deklarera den som en global variabel.

Bortsett från sine_data, deklareras en array som heter f_peaks som en global variabel. Efter varje körning av FFT -funktionen uppdateras denna array. Där f_peaks [0] är den mest dominerande frekvensen och ytterligare värden i fallande ordning.

byte sine_data [91] = {0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, 57, 62, 66, 70, 75, 79, 83, 87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146, 150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225, 227, 229, 231, 233, 235, 236, 238, 240, 241, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 255}; float f_peaks [5];

Eftersom vi har lagrat sinusvärdet för 0 till 90 grader kan varje värde av sinus eller cosinus beräknas. Nedan fungerar den första omgången av siffran till noll decimal och returvärde från lagrade data. denna metod behöver bara en flytande division. Detta kan reduceras ytterligare genom att direkt lagra sinusvärden (inte 255 multipla). men det äter högt minne på Arduino.

Genom att använda ovanstående procedur minskar noggrannheten men förbättrar hastigheten. För 64 poäng ger det fördelen 8 ms och för 128 poäng ger det en fördel på 20 ms.

Steg 4: Förklaring av kod: FFT -funktion

FFT kan endast utföras för provstorleken 2, 4, 8, 16, 32, 64 och så vidare. om värdet inte är 2^n, kommer det att ta den nedre sidan av värdet. Till exempel, om vi väljer provstorleken 70, kommer det bara att överväga de första 64 proverna och utelämna vila.

Det rekommenderas alltid att ha en provstorlek på 2^n. Vilket kan vara:

2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, …

Två floats out_r och out_im tar mycket minne. för Arduino nano fungerar inte för prover högre än 128 (och i vissa fall 128) på grund av brist på tillgängligt minne.

osignerade int -data [13] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048};

int a, c1, f, o, x; a = N; för (int i = 0; i <12; i ++) // beräknar nivåerna {if (data <= a) {o = i;}} int in_ps [data [o] = {}; // input för sekvensering float out_r [data [o] = {}; // verklig del av transform float out_im [data [o] = {}; // fantasifull del av transform

Ytterligare flöde är som följer:

1. Koden genererar lite omvänd ordning för den angivna provstorleken (detaljer om bitväxling på referenser: steg 2)

2. Inmatningsdata beställda enligt genererad order, 3. FFT utfört

4. Amplituden för det komplexa antalet beräknade, 5. Toppar upptäcks och ordnas i fallande ordning

6. resultat kan nås från f_peaks.

[för att komma åt andra data (förutom toppfrekvens) bör koden ändras så att lokal variabel kan kopieras till någon fördefinierad global variabel]

Steg 5: Testa koden

Testar koden
Testar koden
Testar koden
Testar koden

En provtriangelvåg ges som inmatning. för denna våg är samplingsfrekvensen 10 Hz och själva vågens frekvens är 1,25 Hz.

Som framgår av råprodukten matchar värdet med FFT beräknat av Scilab. dessa värden är dock inte exakt samma som vi med låg noggrannhet men snabbare sinusvåg.

I utgångsfrekvensen är arrayfrekvensen 1,25 och 3,75. det är inte nödvändigt att få det exakta värdet varje gång. vanligtvis kallas dessa nummer för frekvensfack. så utgångsvärdet kan vara var som helst inom angivna fack.

Fart:

för Arduino nano krävs:

16 poäng: 4ms32 poäng: 10ms 64 poäng: 26ms 128 poäng: 53ms

Steg 6: Slutsats

Denna FFT-kod kan användas i realtidsprogram. Eftersom det tar cirka 30 ms att slutföra beräkningen. Dess upplösning begränsas dock av ett antal prover. Antalet prov är begränsat av Arduino -minne. Genom att använda Arduino Mega eller andra högre prestandakort kan noggrannheten förbättras.

om du har några frågor, förslag eller korrigeringar får du gärna kommentera.

Uppdatering (2/5/21)

Uppdateringar: // ----------------------------- FFT-funktion --------------- ------------------------------- // float FFT (int in , int N, float Frequency)

Datatypen N ändrades till heltal (befintlig byte) för att stödja> 255 provstorlek. Om urvalsstorleken är <= 128 bör byte datatyp användas.

Rekommenderad: