Spisu treści:

Analizator widma 1024 próbek FFT przy użyciu Atmega1284: 9 kroków
Analizator widma 1024 próbek FFT przy użyciu Atmega1284: 9 kroków

Wideo: Analizator widma 1024 próbek FFT przy użyciu Atmega1284: 9 kroków

Wideo: Analizator widma 1024 próbek FFT przy użyciu Atmega1284: 9 kroków
Wideo: Metrologia - Oscyloskop i dokładny pomiar napięcia (dokładność) 2024, Lipiec
Anonim
Analizator widma 1024 próbek FFT przy użyciu Atmega1284
Analizator widma 1024 próbek FFT przy użyciu Atmega1284
Analizator widma 1024 próbek FFT przy użyciu Atmega1284
Analizator widma 1024 próbek FFT przy użyciu Atmega1284

Ten stosunkowo prosty samouczek (zważywszy na złożoność tej tematyki) pokaże, jak można wykonać bardzo prosty analizator widma 1024 próbek przy użyciu płytki typu Arduino (1284 Narrow) i plotera szeregowego. Dowolna płyta kompatybilna z Arduino sprawdzi się, ale im więcej ma pamięci RAM, tym najlepszą rozdzielczość częstotliwości uzyskasz. Do obliczenia FFT z 1024 próbkami potrzeba więcej niż 8 KB pamięci RAM.

Analiza widma służy do określenia głównych składowych częstotliwości sygnału. Wiele dźwięków (takich jak te wytwarzane przez instrument muzyczny) składa się z częstotliwości podstawowej i niektórych harmonicznych, których częstotliwość jest całkowitą wielokrotnością częstotliwości podstawowej. Analizator widma pokaże ci wszystkie te składowe widmowe.

Możesz użyć tej konfiguracji jako licznika częstotliwości lub sprawdzić wszelkiego rodzaju sygnały, które podejrzewasz, że powodują szum w twoim obwodzie elektronicznym.

Skupimy się tutaj na części oprogramowania. Jeśli chciałbyś wykonać stały obwód dla określonej aplikacji, będziesz musiał wzmocnić i przefiltrować sygnał. To wstępne kondycjonowanie jest całkowicie zależne od sygnału, który chcesz badać, w zależności od jego amplitudy, impedancji, maksymalnej częstotliwości itp. Możesz sprawdzić

Krok 1: Instalacja biblioteki

Będziemy używać biblioteki ArduinoFFT napisanej przez Enrique Condes. Ponieważ chcemy oszczędzić jak najwięcej pamięci RAM, użyjemy gałęzi deweloperskiej tego repozytorium, która pozwala na użycie typu danych float (zamiast double) do przechowywania danych próbkowanych i obliczanych. Więc musimy go zainstalować ręcznie. Nie martw się, po prostu pobierz archiwum i rozpakuj je w folderze biblioteki Arduino (na przykład w domyślnej konfiguracji systemu Windows 10: C:\Users\_twoja_nazwa_użytkownika_\Documents\Arduino\libraries)

Możesz sprawdzić, czy biblioteka jest poprawnie zainstalowana, kompilując jeden z podanych przykładów, np. „FFT_01.ino”.

Krok 2: Transformacja Fouriera i koncepcje FFT

Uwaga: jeśli nie możesz znieść oglądania jakiejkolwiek notacji matematycznej, możesz przejść do kroku 3. W każdym razie, jeśli nie rozumiesz wszystkiego, po prostu rozważ wniosek na końcu sekcji.

Widmo częstotliwości uzyskuje się za pomocą algorytmu szybkiej transformacji Fouriera. FFT to cyfrowa implementacja, która przybliża matematyczną koncepcję transformacji Fouriera. Zgodnie z tą koncepcją, gdy uzyskasz ewolucję sygnału podążającą za osią czasu, możesz poznać jego reprezentację w domenie częstotliwości, złożonej ze złożonych (rzeczywistych + urojonych) wartości. Koncepcja jest wzajemna, więc kiedy znasz reprezentację w domenie częstotliwości, możesz ją przekształcić z powrotem do domeny czasu i uzyskać sygnał dokładnie tak, jak przed transformacją.

Ale co zrobimy z tym zbiorem obliczonych wartości złożonych w dziedzinie czasu? Cóż, większość zostanie pozostawiona inżynierom. Dla nas nazwiemy inny algorytm, który przekształci te złożone wartości w dane o gęstości widmowej: jest to wartość wielkości (= intensywność) powiązana z każdym pasmem częstotliwości. Liczba pasm częstotliwości będzie taka sama jak liczba próbek.

Z pewnością znasz koncepcję korektora, taką jak ta. Powrót do lat 80. z korektorem graficznym. Cóż, uzyskamy ten sam rodzaj wyników, ale z 1024 pasmami zamiast 16 io wiele większą rozdzielczością intensywności. Kiedy korektor daje globalny obraz muzyki, dokładna analiza widmowa pozwala precyzyjnie obliczyć intensywność każdego z 1024 pasm.

Idealna koncepcja, ale:

  1. Ponieważ FFT jest cyfrową wersją transformaty Fouriera, przybliża sygnał cyfrowy i traci pewne informacje. Tak więc, ściśle mówiąc, wynik FFT po przekształceniu z powrotem za pomocą odwróconego algorytmu FFT nie dawałby dokładnie oryginalnego sygnału.
  2. Teoria ta uwzględnia również sygnał, który nie jest skończony, ale jest to zawsze trwający stały sygnał. Ponieważ będziemy go digitalizować tylko przez pewien czas (np. próbki), wprowadzimy trochę więcej błędów.
  3. Wreszcie rozdzielczość konwersji analogowo-cyfrowej wpłynie na jakość obliczanych wartości.

W praktyce

1) Częstotliwość próbkowania (oznaczona fs)

Będziemy próbkować sygnał, czyli mierzyć jego amplitudę, co 1/fs sekundy. fs to częstotliwość próbkowania. Na przykład, jeśli próbkujemy z częstotliwością 8 kHz, ADC (przetwornik analogowo-cyfrowy), który znajduje się na chipie, zapewni pomiar co 1/8000 sekundy.

2) Liczba próbek (z zaznaczeniem N lub próbek w kodzie)

Ponieważ musimy uzyskać wszystkie wartości przed uruchomieniem FFT, będziemy musieli je zapisać, a więc ograniczymy liczbę próbek. Algorytm FFT potrzebuje liczby próbek, która jest potęgą 2. Im więcej próbek mamy, tym lepiej, ale zajmuje dużo pamięci, tym bardziej, że będziemy potrzebować również do przechowywania przekształconych danych, które są wartościami złożonymi. Biblioteka Arduino FFT oszczędza trochę miejsca dzięki użyciu

  • Jedna tablica o nazwie „vReal” do przechowywania próbkowanych danych, a następnie rzeczywistej części przekształconych danych
  • Jedna tablica o nazwie „vImag” do przechowywania wyimaginowanej części przekształconych danych

Potrzebna ilość pamięci RAM wynosi 2 (tablice) * 32 (bity) * N (próbki).

Tak więc w naszej Atmega1284, która ma ładne 16 KB pamięci RAM, przechowamy maksymalnie N = 16000*8/64 = 2000 wartości. Ponieważ liczba wartości musi być potęgą 2, przechowamy maksymalnie 1024 wartości.

3) Rozdzielczość częstotliwości

FFT obliczy wartości dla tylu pasm częstotliwości, ile jest próbek. Pasma te będą rozciągać się od 0 Hz do częstotliwości próbkowania (fs). Stąd rozdzielczość częstotliwości to:

Rozdzielczość = fs / N

Rozdzielczość jest lepsza, gdy jest niższa. Więc dla lepszej rozdzielczości (niższej) chcemy:

  • więcej próbek i/lub
  • niższy fs

Ale…

4) Minimalny fs

Ponieważ chcemy zobaczyć wiele częstotliwości, niektóre z nich są znacznie wyższe niż "częstotliwość podstawowa", nie możemy ustawić fs zbyt nisko. W rzeczywistości istnieje twierdzenie o próbkowaniu Nyquista-Shannona, które zmusza nas do częstotliwości próbkowania znacznie powyżej dwukrotności maksymalnej częstotliwości, którą chcielibyśmy przetestować.

Na przykład, jeśli chcielibyśmy przeanalizować całe widmo od 0 Hz do powiedzmy 15 KHz, co jest w przybliżeniu maksymalną częstotliwością, którą większość ludzi może wyraźnie usłyszeć, musimy ustawić częstotliwość próbkowania na 30 KHz. W rzeczywistości elektronicy często ustawiają ją na 2,5 (lub nawet 2,52) * maksymalną częstotliwość. W tym przykładzie byłoby to 2,5 * 15 KHz = 37,5 KHz. Zwykłe częstotliwości próbkowania w profesjonalnym audio to 44,1 KHz (nagrywanie audio CD), 48 KHz i więcej.

Wniosek:

Punkty od 1 do 4 prowadzą do: chcemy użyć jak największej liczby próbek. W naszym przypadku z 16 kB pamięci RAM rozważymy 1024 próbki. Chcemy próbkować z najniższą możliwą częstotliwością próbkowania, o ile jest ona wystarczająco wysoka, aby analizować najwyższą częstotliwość, jakiej oczekujemy w naszym sygnale (przynajmniej 2,5 * ta częstotliwość).

Krok 3: Symulacja sygnału

Symulowanie sygnału
Symulowanie sygnału

Przy pierwszej próbie zmodyfikujemy nieco przykład TFT_01.ino podany w bibliotece, aby przeanalizować sygnał złożony z

  • Częstotliwość podstawowa, ustawiona na 440 Hz (muzyka A)
  • 3. harmoniczna przy połowie mocy podstawowej („-3 dB”)
  • 5-ta harmoniczna przy 1/4 mocy podstawowej ("-6 dB)

Na powyższym obrazku widać wynikowy sygnał. Rzeczywiście wygląda to bardzo podobnie do rzeczywistego sygnału, który czasami można zobaczyć na oscyloskopie (ja nazwałbym go „Batmanem”) w sytuacji, gdy sygnał sinusoidalny jest przesterowany.

Krok 4: Analiza symulowanego sygnału - kodowanie

0) Dołącz bibliotekę

#include "arduinoFFT.h"

1. Definicje

W sekcjach deklaracji mamy

const bajt adcPin = 0; // A0

const próbki uint16_t = 1024; // Ta wartość MUSI ZAWSZE być potęgą 2 const uint16_t samplingFrequency = 8000; // Wpłynie na maksymalną wartość timera w timer_setup() SYSCLOCK/8/samplingFrequency powinna być liczbą całkowitą

Ponieważ sygnał ma piątą harmoniczną (częstotliwość tej harmonicznej = 5 * 440 = 2200 Hz) musimy ustawić częstotliwość próbkowania powyżej 2,5*2200 = 5500 Hz. Tutaj wybrałem 8000 Hz.

Deklarujemy również tablice, w których będziemy przechowywać dane surowe i obliczone

float vReal[przykłady];

float vImag[próbki];

2) Instancja

Tworzymy obiekt ArduinoFFT. Wersja dev ArduinoFFT używa szablonu, więc możemy użyć typu float lub double data. Float (32 bity) jest wystarczający ze względu na ogólną precyzję naszego programu.

ArduinoFFT FFT = ArduinoFFT(vReal, vImag, próbki, częstotliwość próbkowania);

3) Symulowanie sygnału przez wypełnienie tablicy vReal zamiast wypełniania jej wartościami ADC.

Na początku Loop wypełniamy tablicę vReal:

cykle zmiennoprzecinkowe = (((próbki) * częstotliwość sygnału) / częstotliwość próbkowania); //Liczba cykli sygnału, które odczyta próbkowanie

for (uint16_t i = 0; i < próbki; i++) { vReal = float((amplituda * (sin((i * (DWA_PI * cykle)) / próbki))));/* Zbuduj dane z dodatnimi i wartości ujemne*/ vReal += float((amplituda * (sin((3 * i * (DWA_PI * cykle)) / próbki))) / 2.0);/* Buduj dane z wartościami dodatnimi i ujemnymi*/ vReal += float((amplituda * (sin((5 * i * (DWA_PI * cykle)) / próbki))) / 4.0);/* Buduj dane z wartościami dodatnimi i ujemnymi*/ vImag = 0.0; //Część urojona musi być wyzerowana w przypadku zapętlenia, aby uniknąć błędnych obliczeń i przepełnień }

Dodajemy digitalizację fali podstawowej i dwóch harmonicznych o mniejszej amplitudzie. Następnie inicjujemy wyimaginowaną tablicę zerami. Ponieważ tablica ta jest wypełniana przez algorytm FFT, musimy ją wyczyścić ponownie przed każdym nowym obliczeniem.

4) Obliczenia FFT

Następnie obliczamy FFT i gęstość widmową

FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);

FFT.compute(FFTDirection::Forward); /* Oblicz FFT */ FFT.complexToMagnitude(); /* Oblicz wielkości */

Operacja FFT.windowing(…) modyfikuje surowe dane, ponieważ FFT uruchamiamy na ograniczonej liczbie próbek. Pierwsza i ostatnia próbka wykazują nieciągłość (po jednej stronie nie ma „niczego”). To jest źródło błędu. Operacja „okienkowania” ma tendencję do zmniejszania tego błędu.

FFT.compute(…) o kierunku „Naprzód” oblicza transformację z dziedziny czasu do dziedziny częstotliwości.

Następnie obliczamy wartości wielkości (tj. Intensywności) dla każdego z pasm częstotliwości. Tablica vReal jest teraz wypełniona wartościami wielkości.

5) Rysunek seryjny plotera

Wydrukujmy wartości na ploterze szeregowym wywołując funkcję printVector(…)

PrintVector(vReal, (przykłady >> 1), SCL_FREQUENCY);

Jest to ogólna funkcja pozwalająca na drukowanie danych z osią czasu lub osią częstotliwości.

Drukujemy również częstotliwość pasma, które ma największą wartość amplitudy

float x = FFT.majorPeak();

Serial.print("f0="); Serial.print(x, 6); Serial.println("Hz");

Krok 5: Analiza symulowanego sygnału – wyniki

Analiza sygnału symulowanego - wyniki
Analiza sygnału symulowanego - wyniki

Widzimy 3 skoki odpowiadające częstotliwości podstawowej (f0), 3. i 5. harmonicznej, z połową i 1/4 wielkości f0, zgodnie z oczekiwaniami. W górnej części okna możemy odczytać f0=440.430114 Hz. Ta wartość nie jest dokładnie 440 Hz z powodów wyjaśnionych powyżej, ale jest bardzo zbliżona do rzeczywistej wartości. Tak naprawdę nie trzeba było pokazywać tylu nieznacznych cyfr dziesiętnych.

Krok 6: Analiza sygnału rzeczywistego – okablowanie ADC

Analiza sygnału rzeczywistego - okablowanie ADC
Analiza sygnału rzeczywistego - okablowanie ADC

Ponieważ wiemy, jak postępować w teorii, chcielibyśmy przeanalizować prawdziwy sygnał.

Okablowanie jest bardzo proste. Połącz ze sobą masy i linię sygnałową do pinu A0 swojej płytki poprzez rezystor szeregowy o wartości od 1 kOhm do 10 kOhm.

Ten rezystor szeregowy ochroni wejście analogowe i uniknie dzwonienia. Musi być tak wysoki, jak to możliwe, aby uniknąć dzwonienia, i tak niski, jak to możliwe, aby zapewnić wystarczający prąd do szybkiego ładowania ADC. Zapoznaj się z arkuszem danych MCU, aby poznać oczekiwaną impedancję sygnału podłączonego do wejścia ADC.

W tym pokazie użyłem generatora funkcji do podawania sinusoidalnego sygnału o częstotliwości 440 Hz i amplitudzie około 5 woltów (najlepiej, jeśli amplituda wynosi od 3 do 5 woltów, aby ADC był używany w pobliżu pełnej skali), przez rezystor 1,2 kΩ.

Krok 7: Analiza rzeczywistego sygnału - kodowanie

0) Dołącz bibliotekę

#include "arduinoFFT.h"

1) Deklaracje i instancje

W sekcji deklaracji definiujemy wejście ADC (A0), liczbę próbek oraz częstotliwość próbkowania, jak w poprzednim przykładzie.

const byte adcPin = 0; // A0

const próbki uint16_t = 1024; // Ta wartość MUSI ZAWSZE być potęgą 2 const uint16_t samplingFrequency = 8000; // Wpłynie na maksymalną wartość timera w timer_setup() SYSCLOCK/8/samplingFrequency powinna być liczbą całkowitą

Tworzymy obiekt ArduinoFFT

ArduinoFFT FFT = ArduinoFFT(vReal, vImag, próbki, częstotliwość próbkowania);

2) Konfiguracja timera i ADC

Ustawiamy timer 1 tak, aby pracował cyklicznie z częstotliwością próbkowania (8 kHz) i podniósł przerwanie przy porównywaniu danych wyjściowych.

void timer_setup(){

// zresetuj Timer 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = bit (CS11) | bit (WGM12); // CTC, preskaler 8 TIMSK1 = bit (OCIE1B); OCR1A = ((16000000 / 8) / częstotliwość próbkowania) -1; }

I ustaw ADC tak

  • Wykorzystuje A0 jako wejście
  • Wyzwala się automatycznie na każdym wyjściu timera 1 porównaj mecz B
  • Generuje przerwanie po zakończeniu konwersji

Zegar ADC jest ustawiony na 1 MHz, przez przeskalowanie zegara systemowego (16 MHz) o 16. Ponieważ każda konwersja zajmuje około 13 zegarów w pełnej skali, konwersje można osiągnąć z częstotliwością 1/13 = 0,076 MHz = 76 KHz. Częstotliwość próbkowania powinna być znacznie niższa niż 76 kHz, aby ADC miał czas na próbkowanie danych. (wybraliśmy fs = 8 KHz).

void adc_setup() {

ADCSRA = bit (ADEN) | bit (ADIE) | bit (ADIF); // włącz ADC, chcesz przerwać po zakończeniu ADCSRA |= bit (ADPS2); // Preskaler 16 ADMUX = bit (REFS0) | (adcPin i 7); // ustawienie wejścia ADC ADCSRB = bit (ADTS0) | bit (ADTS2); // Zegar/Licznik1 Porównaj źródło wyzwalacza dopasowania B ADCSRA |= bit (ADATE); // włącz automatyczne wyzwalanie }

Deklarujemy obsługę przerwań, która będzie wywoływana po każdej konwersji ADC w celu przechowywania przekonwertowanych danych w tablicy vReal i wyczyszczenia przerwania

// ADC kompletny ISR

ISR (ADC_vect) { vReal[numer wyniku++] = ADC; if(resultNumber == sample) { ADCSRA = 0; // wyłącz ADC } } EMPTY_INTERRUPT (TIMER1_COMPB_vect);

Możesz mieć wyczerpujące wyjaśnienie konwersji ADC na Arduino (analogRead).

3) Konfiguracja

W funkcji konfiguracji czyścimy wyobrażoną tabelę danych i wywołujemy funkcje konfiguracji timera i ADC

zeroI(); // funkcja, która ustawia na 0 wszystkie urojone dane - wyjaśnione w poprzedniej sekcji

timer_setup(); adc_setup();

3) Pętla

FFT.dcUsunięcie(); // Usuń składnik DC z tego sygnału, ponieważ ADC jest odniesiony do masy

FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward); // Dane ważenia FFT.compute(FFTDirection::Forward); // Oblicz FFT FFT.complexToMagnitude(); // Oblicz wielkości // drukowanie widma i częstotliwości podstawowej f0 PrintVector(vReal, (samples >> 1), SCL_FREQUENCY); float x = FFT.majorPeak(); Serial.print("f0="); Serial.print(x, 6); Serial.println("Hz");

Usuwamy składnik DC, ponieważ ADC jest odniesiony do masy, a sygnał jest wyśrodkowany wokół około 2,5 wolta.

Następnie obliczamy dane, jak wyjaśniono w poprzednim przykładzie.

Krok 8: Analiza rzeczywistego sygnału – wyniki

Analiza rzeczywistego sygnału - wyniki
Analiza rzeczywistego sygnału - wyniki

W rzeczywistości w tym prostym sygnale widzimy tylko jedną częstotliwość. Obliczona częstotliwość podstawowa to 440.118194 Hz. Tutaj znowu wartość jest bardzo bliskim przybliżeniem częstotliwości rzeczywistej.

Krok 9: A co z obciętym sygnałem sinusoidalnym?

A co z przyciętym sygnałem sinusoidalnym?
A co z przyciętym sygnałem sinusoidalnym?

Teraz pozwalamy trochę przesterować ADC, zwiększając amplitudę sygnału powyżej 5 woltów, więc jest on obcinany. Nie naciskaj zbyt mocno, aby nie zniszczyć wejścia ADC!

Widzimy pojawianie się niektórych harmonicznych. Obcinanie sygnału tworzy składowe o wysokiej częstotliwości.

Poznałeś podstawy analizy FFT na płycie Arduino. Teraz możesz spróbować zmienić częstotliwość próbkowania, liczbę próbek i parametr okienkowania. Biblioteka dodaje również kilka parametrów, aby szybciej obliczać FFT z mniejszą precyzją. Zauważysz, że jeśli ustawisz zbyt niską częstotliwość próbkowania, obliczone wielkości będą wyglądały na całkowicie błędne z powodu fałdowania widma.

Zalecana: