ciao a tutti.
Vorrei leggere un sensore che purtroppo produce segnali spuri. In base al segnale, vengono poi eseguite delle azioni.
L'andamento del segnale misurato è di questo tipo.
In pratica, memorizzo il segnale in x1 (letto a inizio ciclo) e in x2 (che leggo a fine ciclo) e ne faccio la differenza al ciclo successivo. Se la differenza è molto grande, non viene eseguito nulla. Mi perdo il primo e l'ultimo valore del tratto che mi serve ma di meglio non riesco a fare. Il sistema è meno veloce nell'accorgersi dell'evento utile, ma sembra funzionare. Penso che sia una schifezza di algoritmo ma, appunto, non so fare di meglio...
Potresti fare un filtro digitale possa basso, facendo la media con i valori precedenti, oppure considerando il livello basso solo se ha una durata di almeno due campioni. Naturalmente avrai un ritardo di alcuni campioni.
Però potrebbe essere più facile risolvere il problema prima...
Che sensore è?...
Perché ci sono gli errori?
Potresti anche confrontare con il precedente e il successivo ed eliminare i valori isolati.
A parte il fatto che la lettura di x1 avviene un istante dopo la lettura di x2, perché il loop ricomincia immediatamente, io ti ho suggerito di confrontare ciascuna lettura con la precedente e la successiva ed escluderla se non è coerente con le altre due.
Dal grafico si vede che capita solo un campione singolo ogni tanto, ma è proprio così?
Comunque la cosa migliore è evitare che il sole colpisca il sensore. Possibile che non si possa fare?
Vero, però allora mi chiedo, il for (che userei per fare la media) non gira forse ancora più rapidamente del mio loop?
Saprei fare il confronto con la lettura precedente ma non con quella successiva (almeno su due piedi).
In base all'esposizione, ho anche 50-60 eventi non voluti in meno di un minuto.
Infatti ho provato a mettere il sensore in un tubo (ho provato vari diametri, con superficie interna nera e rugosa) ma la cosa non ha sortito alcun effetto (forse ha leggermente ridotto il numero di eventi).
Purtroppo il problema è che gli infrarossi vengono anche diffusi dalle superfici, quindi non è sufficiente evitare l'esposizione diretta al sole. Non mi aspettavo che sarebbe stato un problema. Ma... così è...
Mah ... sai come funziona il "core" Arduino? ... c'è, come in tutti i programmi C/C++, un main() che tu non vedi perché fa parte del "core" e ... che fa esattamente questo:
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
... che, in pratica, se non usi la serialEvent() (... e, normalmente quasi nessuno la usa), si riduce al for che chiama, in un ciclo infinito, loop()
ehm... però se sapessi queste cose forse non avrei avuto nulla da chiedere... no?
In altre parole, cosa vuol dire quello che hai detto? Fare la media è meglio o uguale a quello che ho fatto io? Ovvero, mi conviene fare la media (su due valori) per eliminare quei picchi?
Voglio dire che per ogni lettura x(n) prendi la precedente X(n-1) e la confronti con la x(n) e con la x(n-2), cioè confronti x(n-1) con la sua precedente e la sua successiva. Hai un ritardo di un campione.
Comunque, per quel poco che hai descritto, credo che solo il filtraggio non ti risolverà il problema.
Non conosco esattamente la struttura del sistema 'sensore', non conosco il nome di emettitore-ricevitore (proverò a chiedere). So solo che ha un emettitore IR (sui 900 nm) e un ricevitore IR. La comunicazione è in I2C. Funziona molto bene. Però, appunto, vede radiazione con quella lambda proveniente anche dal sole. L'ipotesi che faccio è confermata dal fatto che se utilizzo il sensore al chiuso non ho questo problema.
già... Queste sono cose che mi sfuggono perchè uso Arduino quando posso o quando proprio ne ho bisogno (in lab). Non ho però una conoscenza approfondita dei microcontrollori. E non sono un programmatore di C/C++. Quindi non so se sia meglio usare il codice in un modo piuttosto che in un altro. E' il motivo per cui ho aperto questo thread. Il codice che ho scritto sembra funzionare ma volevo capire se può essere migliorato (magari banalmente).
Sì sì, a livello logico non è complicato, devo però implementarlo a livello di codice. Domani, ci provo.
Ah... ho provato a fare la media. Il risultato è identico. Mi piacerebbe capire se sia meglio o peggio in termini "computazionali" (quantomeno a livello teorico). E' un po' questo che volevo capire in realtà e, magari, se c'è un modo per rendere più veloce la risposta.
Per ridurre il rumore presente in un segnale di un fotodiodo, potresti implementare un circuito di amplificazione con un alto guadagno, la filtrazione del segnale con un filtro passa-basso, o la media mobile.
Io ho pensato a un filtro esponenziale. Il codice utilizza una funzione exponentialFilter() per applicare un filtro esponenziale al valore grezzo dal fotodiodo, poi utilizza una media mobile per calcolare la media dei ultimi N_SAMPLES valori filtrati.
Il fattore di ponderazione weightingFactor è un valore compreso tra 0 e 1, esso determina quanto forte vuoi che il filtro esponenziale influenzi i dati, maggiore è il valore meno influenzerà i dati recenti e maggiore sarà l'influenza dei dati passati.
Dovrai regolare il valore di weightingFactor al tuo progetto.
#define readPin A0 //adattalo al tuo circuito
#define N_SAMPLES 10 // numero di campioni utilizzati per la media mobile
int samples[N_SAMPLES]; // array per memorizzare i campioni
int index = 0; // variabile per indice corrente dell'array
float filteredValue = 0; // valore filtrato
float weightingFactor = 0.1; // fattore di ponderazione
void setup() {
Serial.begin(9600);
for(int i = 0; i < N_SAMPLES; i++) {
samples[i] = 0;
}
}
void loop() {
int rawValue = analogRead(readPin); // lettura del valore grezzo dal fotodiodo
filteredValue = exponentialFilter(rawValue); // applica filtro esponenziale al valore grezzo
samples[index] = filteredValue; // aggiunge il valore filtrato all'array
filteredValue = 0; // azzera il valore filtrato
for(int i = 0; i < N_SAMPLES; i++) {
filteredValue += samples[i]; // somma tutti i valori nell'array
}
filteredValue /= N_SAMPLES; // calcola la media dei valori nell'array
Serial.println(filteredValue); // invia il valore filtrato al monitor seriale
index = (index + 1) % N_SAMPLES; // incrementa l'indice e riporta all'inizio se necessario
delay(10);
}
int exponentialFilter(int rawValue) {
// implementazione del filtro esponenziale
filteredValue = weightingFactor * rawValue + (1 - weightingFactor) * filteredValue;
return filteredValue;
}
Se invece vuoi utilizzare un filtro passa-basso:
#define readPin A0 //adattalo al tuo circuito
#define N_SAMPLES 10 // numero di campioni utilizzati per la media mobile
int samples[N_SAMPLES]; // array per memorizzare i campioni
int index = 0; // variabile per indice corrente dell'array
float filteredValue = 0; // valore filtrato
void setup() {
Serial.begin(9600);
for(int i = 0; i < N_SAMPLES; i++) {
samples[i] = 0;
}
}
void loop() {
int rawValue = analogRead(readPin); // lettura del valore grezzo dal fotodiodo
samples[index] = rawValue; // aggiunge il valore grezzo all'array
filteredValue = 0; // azzera il valore filtrato
for(int i = 0; i < N_SAMPLES; i++) {
filteredValue += samples[i]; // somma tutti i valori nell'array
}
filteredValue /= N_SAMPLES; // calcola la media dei valori nell'array
Serial.println(filteredValue); // invia il valore filtrato al monitor seriale
index = (index + 1) % N_SAMPLES; // incrementa l'indice e riporta all'inizio se necessario
delay(10);
}
Come ti hanno già suggerito, se la frequenza massima del segnale "pulito" è un dato noto, puoi provare ad implementare un filtro passa basso usando una funzione di trasferimento polinomiale (quelli di tipo Butterworth sono largamente usati in molti ambiti e danno ottimi risultati).
Online ci sono dei notebook python che aiutano a calcolare i coefficienti con semplicità partendo da un set di dati preimpostati. Io ho usato questo tipo di filtro ad esempio per ripulire dei dai provenienti da un accelerometro (qui trovi qualche esempio in "arduinese")
Ci sono anche delle librerie Arduino abbastanza semplici da usare in alternativa. Non le ho mai testate a dire il vero, ma immagino che a livello computazionale richiedano più risorse.
Se il sensore legge gli IR da un'emettitore, puoi provare a sincronizzarli (se possibile, tipo se sono abbastanza vicini), oppure a condizionarli in AC (usando per l'emettitore una portante modulata invece che una DC, ed accoppiando il sensore in AC con un filtro che escluda sia le componenti DC che tutte (il piu possibile) le frequenze diverse dalla portante ... questo di solito si fa quando serve usare sensori IR in ambienti esposti alla luce solare.
Questa è un ottima idea. In pratica è la stessa tecnica dei telecomandi di TV o apparati elettronici più in generale dove il segnale IR viene modulato con una portante di 36-38Khz tipicamente.
E' quello a cui avevo pensato anch'io quando ho chiesto dettagli sul sensore, ma i dettagli non sono ancora arrivati...
Non è nemmeno chiaro sel il sensore ha un funzionamento ON/OFF, per cui la soluzione della luce pulsata sarebbe l'ideale, oppure ha un uscita analogica. Nello pseudo-codice viene usato l'analogRead() per leggere il sensore, che farebbe propendere più per la seconda ipotesi, poi invece sappiamo che la comunicazione è in I2C, il che rende le cose ancora diverse.
Però il thread ha il titolo "pulire il segnale" ed è nella sezione "software". Quindi tutto questo pseudoprocesso non lo capisco proprio.
Il sensore che abbiamo usato è fatto "in casa" e mi è stato dato così com'è (è inglobato in un blocco di silicone). Quindi non chiedetemi com'è fatto perchè non ho modo di avere altre info. Capisco che per voi sia una informazione utile per aiutarmi (cosa di cui vi ringrazio) ma non ho quelle info.
Anche se siamo ormai fuori thread, quale sensore mi consigliereste per fare la misura della distanza (1 m) di un oggetto? (magari immune alla luce del sole)
Vi ringrazio per le brillanti idee che mi avete dato.
Beh, che dire, hai ragione. E' che a giudizio ed esperienza mia, e credo di molti altri, la prima via per 'pulire' un segnale è evitare di 'sporcarlo', per questo paventavamo soluzioni diverse. Certo che se hai a che fare con una 'black box' su cui non puoi intervenire le cose cambiano, e non ti rimane che un filtraggio software.