Problema con la libreria FFT

Salve a tutti, sono un nuovo arrivato appassionato di programmazione ed elettronica. Ultimamente mi sono interessato all' analisi in frequenza di un segnale . Dopo essermi informato ho tentato di utilizzare il mio Arduino Uno con la libreria FFT. Ho provato a utilizzare il programma di esempio che si trova nella libreria per fare alcune prove,collegando alla porta analogica A0 un potenziometro e variando manualmente la tensione. Il programma di esempio dovrebbe mostrare i valori determinati dalla FFT sul monitor seriale ma nel mio caso mostra strane lettere e parentesi che si ripetono ad intermittenza, perchè? Grazie a tutti e scusate l' ignoranza!!!

Posta lo sketch, probabilmente o hai impostato male il baud rate oppure hai inizializzato male la variabile che contiene i valori del potenziometro. Ciao

il baudrate impostato nel sketch corrisponde con quello impostato nel terminale? Ciao Uwe

Ecco il codice…

#define LOG_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft

#include <FFT.h> // include the library

void setup() {
  Serial.begin(115200); // use the serial port
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0
}

void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    fft_mag_log(); // take the output of the fft
    sei();
    Serial.write(255); // send a start byte
    Serial.write(fft_log_out, 128); // send out the data
  }
}

Lo sketch che hai postato, invia sulla seriale il valore di ciascun byte fft_log_out. Questo array contiene dati binari, non dati ASCII.

Il serial monitor si aspetta che i dati in arrivo siano ASCII. Mi spiego meglio, se vede arrivare un byte = 65 lui stampa la lettera A perchè 65 è il codice ASCII di questa lettera. Nel tuo caso invece il numero 65 che arriva significa proprio 65 cioè il valore numerico.

Probabilmente lo sketch originale da cui sei partito, era fatto per comunicare ad un PC lo spettro calcolato da Arduino tramite la FFT. Ma il programma in ascolto sul PC era scritto ad hoc e si aspettava dati binari.

Un'altra cosa, che non riguarda il problema specifico da te riscontrato. La FFT calcola lo spettro di un segnale analogico, ossia l'ampiezza delle frequenza che lo costituiscono. Se colleghi semplicemente un potenziometro all'ingresso, ho idea che il risultato non sia esattamente quello che ti aspetti. Forse dovresti collegare, per esempio, un segnale audio.

Ciao. Vittorio.

C'è un'altra cosa. Sicuro che quello sketch sia compatibile con l'IDE 1.x? A partire dalla versione 1.0 la seriale viene gestita tramite interrupt, se mandi in stampa dei dati e poi disattivi ed attivi gli interrupt, mi sembra logico che ci siano problemi di trasmissione.

Grazie a tutti per i consigli!!
Ora sto riscrivendo lo sketch ma rileggendo le istruzioni sulla funzione fft_run() ( FFTFunctions - Open Music Labs Wiki ), non capisco bene cosa voglia dire che in ingresso sono necessari un blocco di dati già in SRAM.
Ecco la bozza del codice:

#define LOG_OUT 1 
#define FFT_N 256 

#include <FFT.h>


void setup() {
  Serial.begin(115200);
 
    }

void loop () {
  int sensorvoltage = analogRead(A0);
  float voltage = sensorvoltage * (5.0 / 1023.0);
  for( int i=0 ; i< 512 ; i += 2) {
 
    fft_input[i] = voltage;
    fft_input[i+1] = 0;
  }
    fft_window(); 
    fft_reorder(); 
    fft_run(); 
    fft_mag_log(); 
    Serial.write(255); 
    Serial.write(fft_log_out, 128);
    
    
    }

Grazie ancora! :smiley:

kama90: Ora sto riscrivendo lo sketch ma rileggendo le istruzioni sulla funzione fft_run() ( http://wiki.openmusiclabs.com/wiki/FFTFunctions ), non capisco bene cosa voglia dire che in ingresso sono necessari un blocco di dati già in SRAM.

Non lo so, non so come funzioni quella lib. Parliamo di questa lib? http://code.google.com/p/neuroelec/downloads/list Ho visto che esistono 2 versioni, di cui 1 appositamente per l'IDE 1.0 ma che contiene un esempio diverso da quello che hai pubblicato tu. Stiamo parlando della stessa lib? Ma tu quale IDE usi?

leo72: Parliamo di questa lib?

Sembra di no, lui sta usando la libreria che si trova sul sito che ha linkato, a quanto pare non è una fft generica, è un caso limitato e di uso ristretto, da quello che ho visto non c'è nemmeno un programma pc per la visualizzazione dei dati ottenuti che sono sotto forma di array binario.

Ma non è che forse spedisce i dati compressi? Mi pare che accenni qualcosa qui: http://wiki.openmusiclabs.com/wiki/ArduinoFFT Ma non è che quel sito sia ricco di dettagli.

Scusami, magari mi sbaglio, ma io continuo a pensare che ci sia un problema di fondo che avevo già accennato nel mio primo intervento.

Cerco di fare un brevissimo riassunto sulla trasformata per capirci. Magari sarà anche molto approssimativo dal punto di vista matematico perchè si tratta di reminiscenze universitarie.

La trasformata di Fourier consente scomporre un segnale complesso nelle componenti sinusoidali (ossia le loro frequenze) che lo compongono determinando l'ampiezza di ciascuna di esse.

Partiamo da un segnale campionato con una frequenza Fc. Questo significa che abbiamo acquisito N campioni ciascuno ad un intervallo di tempo pari a 1/Fc secondi. Il teorema fondamentale del campionamento afferma che Fc deve essere maggiore o uguale del doppio della massima frequenza contenuta nel segnale da campionare. Quindi, se stiamo campionando una voce umana (che va da 500Hz a 2 KHz) dobbiamo campionare almeno a 4 Khz cioè 4000 campioni al secondo.

Questi campioni costituiscono il tuo vettore di input ossia fft_input. Il calcolo della FFT ti restituisce un nuovo vettore in cui la metà dei valori rappresenta l'ampiezza di N/2 (N erano il numero di campioni) sinusoidi che compongono il segnale originale. Il primo valore è l'ampiezza della componente continua, il valore all'indice N/2 è il valore della massima frequenza campionabile (nel nostro esempio 2KHz), i valori intermedi sono equispaziati. Se avessimo raccolto 1024 campioni (la FFT funziona con un numero di campioni pari ad una potenza di 2) allora il valore all'indice 512 rappresenta l'ampiezza della sinusoide a 2KHz i valori precedenti rappresentano l'ampiezza delle sinusoidi di frequenza pari a 0Hz (componente continua del segnale), 3.8Hz, 7.6Hz, 11.4Hz e così via.

Nel tuo caso, stai campionando un segnale continuo ossia il valore della tensione presente sul centrale del potenziometro. Mi aspetto quindi che in output tu ottenga un unico valore diverso da zero e cioè il primo valore del vettore di output che, come detto, rappresenta l'ampiezza della componente continua del segnale di ingresso.

Scusami per la lunghezza dell'intervento... alla fine la domanda è: ma cosa devi fare? Se ce lo dici forse riusciamo a ragionare insieme sulla implementazione.

Ciao. Vittorio.

Ora rispondo a tutte le domande

  1. L’ IDE che sto usando è 1.0.1 ma credo che la libreria del sito http://code.google.com/p/neuroelec/downloads/detail?name=ffft_Library.zip&can=2&q= sia solo per 1.0
  2. il mio progetto è quello di costruire un analizzatore di campi magnetici che attraverso una sonda di hall mi determini l’ intensità del campo e la frequenza delle onde magnetiche. Per la determinazione dell’ intensità del campo non penso di avere problemi, ma per portare il segnale dal dominio del tempo a quello della frequenza è necessario utilizzare la FFT sulle differenti tensioni inviate dalla sonda di hall ad Arduino.

Grazie ancora per tutti questi consigli XD

kama90: Ora rispondo a tutte le domande 1) L' IDE che sto usando è 1.0.1 ma credo che la libreria del sito http://code.google.com/p/neuroelec/downloads/detail?name=ffft_Library.zip&can=2&q= sia solo per 1.0

Tra 1.0 e 1.0.1 cambia poco.

2) il mio progetto è quello di costruire un analizzatore di campi magnetici che attraverso una sonda di hall mi determini l' intensità del campo e la frequenza delle onde magnetiche. Per la determinazione dell' intensità del campo non penso di avere problemi, ma per portare il segnale dal dominio del tempo a quello della frequenza è necessario utilizzare la FFT sulle differenti tensioni inviate dalla sonda di hall ad Arduino.

Grazie ancora per tutti questi consigli XD

Non so se ti può tornare utile, ma l'autore ha sviluppato una nuova lib che dice essere può performante della FFT, la FHT. Forse, essendo rivista e più recente, ha meno problemi.

vittorio68: Nel tuo caso, stai campionando un segnale continuo ossia il valore della tensione presente sul centrale del potenziometro. Mi aspetto quindi che in output tu ottenga un unico valore diverso da zero e cioè il primo valore del vettore di output che, come detto, rappresenta l'ampiezza della componente continua del segnale di ingresso.

Quoto al 100%, infatti iniettando sul ADC una tensione continua, invece di un segnale che varia nel tempo, in uscita dalla FFT ottieni solo un valore costante.

kama90: sulle differenti tensioni inviate dalla sonda di hall ad Arduino.

Non tensioni, ma segnale che varia nel tempo in ampiezza e in frequenza altrimenti la FFT non serve nulla.

@leo72: avevo già guardato la FHT perchè nel mio caso sarebbe stata più utile e veloce. Purtroppo però l' unica cosa che cambia dalla FFT precedente è che riempe l' array solo con dati reali, e quindi il vettore fft_input[] è grande la metà. Oltre ciò rimane sempre il problema della traduzione dei bit in ASCII...

@astrobeed vittorio68 : io non mando un segnale continuo con il potenziometro perchè altrimenti mi darebbe sempre 0. Muovo il potenziometro velocemente così da produrre un segnale a diversa ampiezza e variabile nel tempo.

Ciao Kama90

Ancora nessuno ha risposto a questa domanda:

leo72: Ma non è che forse spedisce i dati compressi?

Forse i dati arrivano in un qualche formato non direttamente visionabile? Cioè, devono essere elaborati?

Forse i dati arrivano in un qualche formato non direttamente visionabile? Cioè, devono essere elaborati?

Forse in maniera criptica, e me ne scuso, ma pensavo di aver risposto io. Il vettore fft_log_out inviato alla seriale è il risultato della FFT. Sono solo 128 bytes perchè sta facendo la trasformata su 256 campioni (#define FFT_N 256 all’inizio dello sketch) ed i restanti 128 bytes sono simmetrici.

Il primo byte = 255 rappresenta lo start (c’è scritto nel commento) per un programma che deve capire che stanno per arrivare i dati. I successivi 128 sono l’output della FFT.

Se scrivi un programma PC che legge i dati dalla seriale potresti, per esempio utilizzare i 128 bytes per disegnare un istogramma dello spettro.

Se invece vuoi leggere nel serial monitor questi valori, puoi commentare il primo Serial.Write(255) e inviare i successivi 128 con un Serial.println(xxx, DEC). In questo modo se un valore, per esempio è = 12, la println(12, DEC) invierà alla seriale i bytes 49 (codice ASCII del “1”) e 50 (codice ASCII del “2”) e tu vedrai “12”. La Write invece invia il byte 12 e tu vedi il corrispondente ASCII (non ricordo cosa sia…) ossia le lettere strane di cui parli nel primo post.

per essere più chiaro:

    //Serial.write(255); 
    for (int i = 0; i < 128; i++) Serial.println(fft_log_out[i], DEC);

Ciao.

vittorio68: ossia le lettere strane di cui parli nel primo post.

Sì, non ero io ad aver aperto il thread ma avevo però posto la domanda perché mi era venuto il dubbio sui "caratteri strani" che l'amico vedeva: di solito sono appunto la stampa in ASCII di byte.

@leo: nono, sono partito quotando te, ma il post era sempre diretto a kama90 che ha fatto la domanda iniziale, infatti cito il suo post. Conosco bene la tua competenza avendo letto molti tuoi post.

Ciao.