Sommario:
- Passaggio 1: installazione della libreria
- Passaggio 2: trasformata di Fourier e concetti FFT
- Passaggio 3: simulazione di un segnale
- Fase 4: Analisi di un segnale simulato - Codifica
- Passaggio 5: analisi di un segnale simulato - Risultati
- Passaggio 6: analisi di un segnale reale - Cablaggio dell'ADC
- Passaggio 7: Analisi di un segnale reale - Codifica
- Passaggio 8: analisi di un segnale reale - Risultati
- Passaggio 9: che dire di un segnale sinusoidale tagliato?
Video: Analizzatore di spettro FFT da 1024 campioni utilizzando un Atmega1284: 9 passaggi
2024 Autore: John Day | [email protected]. Ultima modifica: 2024-01-30 10:01
Questo tutorial relativamente semplice (considerando la complessità di questo argomento) ti mostrerà come puoi realizzare un semplice analizzatore di spettro da 1024 campioni usando una scheda di tipo Arduino (1284 Narrow) e il plotter seriale. Qualsiasi tipo di scheda compatibile con Arduino andrà bene, ma più RAM ha, la migliore risoluzione di frequenza che otterrai. Avranno bisogno di più di 8 KB di RAM per calcolare la FFT con 1024 campioni.
L'analisi dello spettro viene utilizzata per determinare le principali componenti di frequenza di un segnale. Molti suoni (come quelli prodotti da uno strumento musicale) sono composti da una frequenza fondamentale e da alcune armoniche che hanno una frequenza che è un multiplo intero della frequenza fondamentale. L'analizzatore di spettro ti mostrerà tutti questi componenti spettrali.
Potresti voler usare questa configurazione come un frequenzimetro o per controllare qualsiasi tipo di segnale che sospetti stia portando del rumore nel tuo circuito elettronico.
Ci concentreremo qui sulla parte software. Se desideri realizzare un circuito permanente per un'applicazione specifica, dovrai amplificare e filtrare il segnale. Questo precondizionamento è totalmente dipendente dal segnale che si desidera studiare, a seconda della sua ampiezza, impedenza, frequenza massima ecc… Puoi controllare
Passaggio 1: installazione della libreria
Useremo la libreria ArduinoFFT scritta da Enrique Condes. Poiché vogliamo risparmiare RAM il più possibile, utilizzeremo il ramo di sviluppo di questo repository che consente di utilizzare il tipo di dati float (invece di double) per memorizzare i dati campionati e calcolati. Quindi dobbiamo installarlo manualmente. Non preoccuparti, basta scaricare l'archivio e decomprimerlo nella cartella della libreria Arduino (ad esempio nella configurazione predefinita di Windows 10: C:\Users\_your_user_name_\Documents\Arduino\libraries)
Puoi verificare che la libreria sia installata correttamente compilando uno degli esempi forniti, come "FFT_01.ino".
Passaggio 2: trasformata di Fourier e concetti FFT
Avvertimento: se non riesci a sopportare di vedere alcuna notazione matematica, potresti voler saltare al passaggio 3. Comunque, se non ottieni tutto, considera la conclusione alla fine della sezione.
Lo spettro di frequenza è ottenuto tramite un algoritmo di trasformata di Fourier veloce. FFT è un'implementazione digitale che approssima il concetto matematico della trasformata di Fourier. Sotto questo concetto una volta ottenuta l'evoluzione di un segnale lungo un asse temporale, si può conoscere la sua rappresentazione in un dominio di frequenza, composto da valori complessi (reali + immaginari). Il concetto è reciproco, quindi quando conosci la rappresentazione nel dominio della frequenza puoi trasformarla di nuovo nel dominio del tempo e recuperare il segnale esattamente come prima della trasformazione.
Ma cosa faremo con questo insieme di valori complessi calcolati nel dominio del tempo? Bene, la maggior parte sarà lasciata agli ingegneri. Per noi chiameremo un altro algoritmo che trasformerà questi valori complessi in dati di densità spettrale: cioè un valore di grandezza (= intensità) associato a ciascuna banda di frequenza. Il numero di banda di frequenza sarà lo stesso del numero di campioni.
Sicuramente conosci il concetto di equalizzatore, come questo Ritorno agli anni '80 con l'EQ grafico. Bene, otterremo lo stesso tipo di risultati ma con 1024 bande invece di 16 e una risoluzione di intensità molto maggiore. Quando l'equalizzatore fornisce una visione globale della musica, l'analisi spettrale fine consente di calcolare con precisione l'intensità di ciascuna delle 1024 bande.
Un concetto perfetto, ma:
- Poiché la FFT è una versione digitalizzata della trasformata di Fourier, si avvicina al segnale digitale e perde alcune informazioni. Quindi, a rigor di termini, il risultato della FFT se trasformato di nuovo con un algoritmo FFT invertito non darebbe esattamente il segnale originale.
- Anche la teoria considera un segnale che non è finito, ma che è un segnale costante perenne. Dal momento che lo digitalizzeremo solo per un certo periodo di tempo (es. campioni), verranno introdotti alcuni errori in più.
-
Infine, la risoluzione della conversione da analogico a digitale avrà un impatto sulla qualità dei valori calcolati.
In pratica
1) La frequenza di campionamento (nota fs)
Campioniamo un segnale, ovvero misuriamo la sua ampiezza, ogni 1/fs di secondo. fs è la frequenza di campionamento. Ad esempio, se campioniamo a 8 KHz, l'ADC (convertitore da analogico a digitale) che è a bordo del chip fornirà una misurazione ogni 1/8000 di secondo.
2) Il numero di campioni (indicato con N o campioni nel codice)
Poiché dobbiamo ottenere tutti i valori prima di eseguire la FFT, dovremo memorizzarli e quindi limiteremo il numero di campioni. L'algoritmo FFT ha bisogno di un numero di campioni che è una potenza di 2. Più campioni abbiamo meglio è ma richiede molta memoria, tanto più avremo bisogno anche di memorizzare i dati trasformati, che sono valori complessi. La libreria Arduino FFT consente di risparmiare spazio utilizzando
- Un array chiamato "vReal" per memorizzare i dati campionati e quindi la parte reale dei dati trasformati
- Un array chiamato "vImag" per memorizzare la parte immaginaria dei dati trasformati
La quantità di RAM necessaria è pari a 2 (array) * 32 (bit) * N (campioni).
Quindi nel nostro Atmega1284 che ha ben 16 KB di RAM memorizzeremo un massimo di N = 16000*8 / 64 = 2000 valori. Poiché il numero di valori deve essere una potenza di 2, memorizzeremo un massimo di 1024 valori.
3) La risoluzione in frequenza
La FFT calcolerà i valori per tante bande di frequenza quanti sono i campioni. Queste bande spazieranno da 0 HZ alla frequenza di campionamento (fs). Quindi, la risoluzione in frequenza è:
Fresoluzione = fs / N
La risoluzione è migliore quando è inferiore. Quindi per una migliore risoluzione (inferiore) vogliamo:
- più campioni e/o
- un fs inferiore
Ma…
4) Minimo fs
Dal momento che vogliamo vedere molte frequenze, alcune delle quali sono molto più alte della "frequenza fondamentale", non possiamo impostare fs troppo basso. Esiste infatti il teorema di campionamento di Nyquist-Shannon che ci obbliga ad avere una frequenza di campionamento ben al di sopra del doppio della frequenza massima che vorremmo testare.
Ad esempio, se volessimo analizzare tutto lo spettro da 0 Hz per dire 15 KHz, che è approssimativamente la frequenza massima che la maggior parte degli umani può sentire distintamente, dobbiamo impostare la frequenza di campionamento a 30 KHz. Infatti gli elettronici spesso la fissano a 2,5 (o anche 2,52) * la frequenza massima. In questo esempio sarebbe 2,5 * 15 KHz = 37,5 KHz. Le normali frequenze di campionamento nell'audio professionale sono 44,1 KHz (registrazione CD audio), 48 KHz e oltre.
Conclusione:
I punti da 1 a 4 portano a: vogliamo utilizzare il maggior numero possibile di campioni. Nel nostro caso con un dispositivo da 16 KB di RAM prenderemo in considerazione 1024 campioni. Vogliamo campionare alla frequenza di campionamento più bassa possibile, purché sia sufficientemente alta per analizzare la frequenza più alta che ci aspettiamo nel nostro segnale (2,5 * questa frequenza, almeno).
Passaggio 3: simulazione di un segnale
Per il nostro primo tentativo, modificheremo leggermente l'esempio TFT_01.ino fornito nella libreria, per analizzare un segnale composto da
- La frequenza fondamentale, impostata a 440 Hz (LA musicale)
- 3a armonica a metà della potenza della fondamentale ("-3 dB")
- 5a armonica a 1/4 della potenza della fondamentale ("-6 dB)
Potete vedere nell'immagine sopra il segnale risultante. Sembra davvero molto simile a un segnale reale che a volte si può vedere su un oscilloscopio (lo chiamerei "Batman") in situazioni in cui c'è un clipping di un segnale sinusoidale.
Fase 4: Analisi di un segnale simulato - Codifica
0) Includere la libreria
#include "arduinoFFT.h"
1) Definizioni
Nelle sezioni delle dichiarazioni, abbiamo
const byte adcPin = 0; // A0
const uint16_t campioni = 1024; // Questo valore DEVE SEMPRE essere una potenza di 2 const uint16_t samplingFrequency = 8000; // Influirà sul valore massimo del timer in timer_setup() SYSCLOCK/8/samplingFrequency dovrebbe essere un numero intero
Poiché il segnale ha una quinta armonica (frequenza di questa armonica = 5 * 440 = 2200 Hz) dobbiamo impostare la frequenza di campionamento sopra 2,5*2200 = 5500 Hz. Qui ho scelto 8000 Hz.
Dichiariamo anche gli array in cui memorizzeremo i dati grezzi e calcolati
float vReal[campioni];
float vImag[campioni];
2) Istanziazione
Creiamo un oggetto ArduinoFFT. La versione dev di ArduinoFFT utilizza un modello in modo da poter utilizzare il tipo di dati float o double. Float (32 bit) è sufficiente per la precisione complessiva del nostro programma.
ArduinoFFT FFT = ArduinoFFT(vReal, vImag, campioni, frequenza di campionamento);
3) Simulare il segnale popolando l'array vReal, invece di popolarlo con i valori ADC.
All'inizio del Loop andiamo a popolare l'array vReal con:
cicli float = (((samples) * signalFrequency) / samplingFrequency); //Numero di cicli di segnale che il campionamento leggerà
for (uint16_t i = 0; i < campioni; i++) { vReal = float((ampiezza * (sin((i * (TWO_PI * cicli))) / campioni))));/* Crea dati con positivi e valori negativi*/ vReal += float((ampiezza * (sin((3 * i * (TWO_PI * cicli)) / campioni))) / 2.0);/* Crea dati con valori positivi e negativi*/ vReal += float((ampiezza * (sin((5 * i * (TWO_PI * cicli)) / campioni))) / 4.0);/* Crea dati con valori positivi e negativi*/ vImag = 0.0; //La parte immaginaria deve essere azzerata in caso di loop per evitare calcoli errati e overflow }
Aggiungiamo una digitalizzazione dell'onda fondamentale e delle due armoniche con minore ampiezza. Quindi inizializziamo l'array immaginario con zeri. Poiché questo array è popolato dall'algoritmo FFT, dobbiamo cancellarlo di nuovo prima di ogni nuovo calcolo.
4) Calcolo FFT
Quindi calcoliamo la FFT e la densità spettrale
FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
FFT.compute(Direzione FFT::Avanti); /* Calcola FFT */ FFT.complexToMagnitude(); /* Calcola grandezze */
L'operazione FFT.windowing(…) modifica i dati grezzi perché eseguiamo la FFT su un numero limitato di campioni. Il primo e l'ultimo campione presentano una discontinuità (non c'è "niente" da uno dei loro lati). Questa è una fonte di errore. L'operazione di "finestra" tende a ridurre questo errore.
FFT.compute(…) con la direzione "Forward" calcola la trasformazione dal dominio del tempo al dominio della frequenza.
Quindi calcoliamo i valori di grandezza (cioè intensità) per ciascuna delle bande di frequenza. L'array vReal è ora riempito con i valori delle grandezze.
5) Disegno seriale del plotter
Stampiamo i valori sul plotter seriale chiamando la funzione printVector(…)
PrintVector(vReal, (campioni >> 1), SCL_FREQUENCY);
Questa è una funzione generica che permette di stampare i dati con un asse del tempo o un asse della frequenza.
Stampiamo anche la frequenza della banda che ha il valore di magnitudine più alto
float x = FFT.majorPeak();
Serial.print("f0="); Serial.print(x, 6); Serial.println("Hz");
Passaggio 5: analisi di un segnale simulato - Risultati
Vediamo 3 picchi corrispondenti alla frequenza fondamentale (f0), la 3a e la 5a armonica, con metà e 1/4 della magnitudine f0, come previsto. Possiamo leggere nella parte superiore della finestra f0= 440,430114 Hz. Questo valore non è esattamente 440 Hz, per tutte le ragioni spiegate sopra, ma è molto vicino al valore reale. Non era proprio necessario mostrare tanti decimali insignificanti.
Passaggio 6: analisi di un segnale reale - Cablaggio dell'ADC
Poiché sappiamo come procedere in teoria, vorremmo analizzare un segnale reale.
Il cablaggio è molto semplice. Collega le masse insieme e la linea del segnale al pin A0 della tua scheda tramite un resistore in serie con un valore da 1 KOhm a 10 KOhm.
Questo resistore in serie proteggerà l'ingresso analogico ed eviterà lo squillo. Deve essere il più alto possibile per evitare squilli e il più basso possibile per fornire una corrente sufficiente per caricare rapidamente l'ADC. Fare riferimento alla scheda tecnica MCU per conoscere l'impedenza prevista del segnale collegato all'ingresso ADC.
Per questa demo ho utilizzato un generatore di funzioni per alimentare un segnale sinusoidale di frequenza 440 Hz e ampiezza intorno ai 5 volt (è meglio se l'ampiezza è compresa tra 3 e 5 volt quindi si usa l'ADC vicino al fondo scala), tramite un resistore da 1.2 KOhm.
Passaggio 7: Analisi di un segnale reale - Codifica
0) Includere la libreria
#include "arduinoFFT.h"
1) Dichiarazioni e istanze
Nella sezione dichiarazione definiamo l'ingresso ADC (A0), il numero di campioni e la frequenza di campionamento, come nell'esempio precedente.
const byte adcPin = 0; // A0
const uint16_t campioni = 1024; // Questo valore DEVE SEMPRE essere una potenza di 2 const uint16_t samplingFrequency = 8000; // Influirà sul valore massimo del timer in timer_setup() SYSCLOCK/8/samplingFrequency dovrebbe essere un numero intero
Creiamo l'oggetto ArduinoFFT
ArduinoFFT FFT = ArduinoFFT(vReal, vImag, campioni, frequenza di campionamento);
2) Impostazione timer e ADC
Impostiamo il timer 1 in modo che cicli alla frequenza di campionamento (8 KHz) e sollevi un'interruzione sul confronto dell'uscita.
void timer_setup(){
// resetta il timer 1 TCCR1A = 0; TCCR1B = 0; TCNT1 = 0; TCCR1B = bit (CS11) | bit (WGM12); // CTC, prescaler di 8 TIMSK1 = bit (OCIE1B); OCR1A = ((16000000 / 8) / frequenza di campionamento) -1; }
E imposta l'ADC in modo che sia
- Utilizza A0 come input
- Si attiva automaticamente su ogni uscita del timer 1 confronto confronto B
- Genera un interrupt quando la conversione è completa
Il clock dell'ADC è impostato a 1 MHz, prescalando il clock di sistema (16 MHz) di 16. Poiché ogni conversione richiede circa 13 clock a fondo scala, le conversioni possono essere ottenute a una frequenza di 1/13 = 0,076 MHz = 76 KHz. La frequenza di campionamento dovrebbe essere significativamente inferiore a 76 KHz per consentire all'ADC di avere il tempo di campionare i dati. (abbiamo scelto fs = 8 KHz).
void adc_setup() {
ADCSRA = bit (ADEN) | bit (ADIE) | bit (ADIF); // attiva ADC, desidera l'interruzione al completamento ADCSRA |= bit (ADPS2); // Prescaler di 16 ADMUX = bit (REFS0) | (adcPin & 7); // impostazione dell'ingresso ADC ADCSRB = bit (ADTS0) | bit (ADTS2); // Timer/Contatore1 Confronta sorgente trigger Match B ADCSRA |= bit (ADATE); // attiva l'attivazione automatica }
Dichiariamo il gestore di interrupt che verrà chiamato dopo ogni conversione ADC per memorizzare i dati convertiti nell'array vReal e cancellare l'interrupt
// ADC ISR completo
ISR (ADC_vect) { vReal[numerorisultato++] = ADC; if(numerorisultato == campioni) { ADCSRA = 0; // disattiva l'ADC } } EMPTY_INTERRUPT (TIMER1_COMPB_vect);
Puoi avere una spiegazione esauriente sulla conversione ADC su Arduino (analogRead).
3) Configurazione
Nella funzione di configurazione cancelliamo la tabella dei dati immaginari e chiamiamo le funzioni di impostazione timer e ADC
zeroI(); // una funzione che imposta a 0 tutti i dati immaginari - spiegata nella sezione precedente
timer_setup(); adc_setup();
3) Ciclo
FFT.dcRimozione(); // Rimuove la componente DC di questo segnale poiché l'ADC fa riferimento a massa
FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward); // Pesa i dati FFT.compute(FFTDirection::Forward); // Calcola FFT FFT.complexToMagnitude(); // Calcola le grandezze // stampa lo spettro e la frequenza fondamentale f0 PrintVector(vReal, (samples >> 1), SCL_FREQUENCY); float x = FFT.majorPeak(); Serial.print("f0="); Serial.print(x, 6); Serial.println("Hz");
Rimuoviamo il componente DC perché l'ADC fa riferimento a massa e il segnale è centrato intorno a 2,5 volt circa.
Quindi calcoliamo i dati come spiegato nell'esempio precedente.
Passaggio 8: analisi di un segnale reale - Risultati
Infatti vediamo solo una frequenza in questo semplice segnale. La frequenza fondamentale calcolata è 440,118194 Hz. Anche in questo caso il valore è un'approssimazione molto vicina alla frequenza reale.
Passaggio 9: che dire di un segnale sinusoidale tagliato?
Ora lasciamo sovraccaricare un po' l'ADC aumentando l'ampiezza del segnale sopra i 5 volt, in modo che venga tagliato. Non spingere troppo per non distruggere l'ingresso ADC!
Possiamo vedere apparire alcune armoniche. Il clipping del segnale crea componenti ad alta frequenza.
Hai visto i fondamenti dell'analisi FFT su una scheda Arduino. Ora puoi provare a cambiare la frequenza di campionamento, il numero di campioni e il parametro di windowing. La libreria aggiunge anche alcuni parametri per calcolare la FFT più velocemente con meno precisione. Noterai che se imposti la frequenza di campionamento troppo bassa, le grandezze calcolate appariranno totalmente errate a causa del ripiegamento spettrale.
Consigliato:
Come Realizzare un Analizzatore di Spettro Audio LED: 7 Passaggi (Illustrato)
Come realizzare un analizzatore di spettro audio LED: L'analizzatore di spettro audio LED genera il bellissimo schema di illuminazione in base all'intensità della musica. Ci sono molti kit di spettro musicale LED fai-da-te disponibili sul mercato, ma qui faremo uno spettro audio LED Analizzatore che utilizza NeoPixe
Come fare l'analizzatore di spettro musicale con audio LED a 32 bande fai-da-te usando Arduino Nano a casa #arduinoproject: 8 passaggi
Come fare un analizzatore di spettro musicale audio a 32 bande LED fai-da-te usando Arduino Nano a casa #arduinoproject: oggi realizzeremo un analizzatore di spettro musicale audio a 32 bande LED a casa usando Arduino, può mostrare lo spettro di frequenza e riprodurre musica allo stesso tempo. va collegato davanti alla resistenza da 100k, altrimenti il rumore della spea
Analizzatore di spettro acrilico di grandi dimensioni: 7 passaggi (con immagini)
Analizzatore di spettro acrilico di grandi dimensioni: perché dovresti guardare quei piccoli display a led o quei piccoli LCD se puoi farlo in grande? Questa è una descrizione passo passo su come costruire il tuo analizzatore di spettro di dimensioni giganti. Usando piastrelle acriliche e strisce led per costruire una stanza che riempie la luce
Analizzatore di spettro audio FFT fai-da-te: 3 passaggi
Analizzatore di spettro audio FFT fai-da-te: l'analizzatore di spettro FFT è un'apparecchiatura di test che utilizza l'analisi di Fourier e le tecniche di elaborazione del segnale digitale per fornire l'analisi dello spettro. Utilizzando l'analisi di Fourier è possibile convertire un valore, ad esempio nel dominio del tempo continuo
Analizzatore di campioni di roccia: 4 passaggi
Analizzatore di campioni di roccia: l'analizzatore di campioni di roccia viene utilizzato per identificare e analizzare i tipi di campioni di rocce utilizzando la tecnica di vibrazione del martellamento morbido. È un nuovo metodo per identificare i campioni di roccia. Se è presente un meteorite o un campione di roccia sconosciuto, si può stimare il