MODULI: Esempio ACCELEROMETRO 3 ASSI
Per provare il codice presentato in questa pagina occorre avere a disposizione un accellerometro a 3 assi come quello rappresentato in figura (modulo GY-61)
Gli accelerometri misurano l'accelerazione solitamente dovuta al movimento. Quando sono in stato di quiete l'unica accelerazione che il sensore rileva è quella di gravità. Un accelerometro misura il cambio di velocitá e non di quanto si é spostato un oggetto.
Il nostro modulo GY-61 utilizza l'accelerometro ADXL335 (vedi datasheet). Gli accelerometri analogici hanno tante uscite analogiche quanti sono gli assi gestiti e pertanto un accelerometro a 3 assi avrà 3 uscite. Ogni uscita fornisce un valore in tensione direttamente proporzionale al valore di accelerazione (espresso in g) misurato lungo il rispettivo asse.
Ad esempio supponiamo di avere
un accelerometro analogico in grado di misurare fino ad un minimo/massimo
+/-1g e funzionante con una tensione di 3V allora avremo in uscita
questi valori su uno degli assi:
- circa 1.5V quando l’accelerazione
vale 0g (valore “centrale”),
- 3V quando vale 1g,
- 0V
quando vale -1g.
Gli accelerometri analogici vanno interfacciati con il
microcontrollore (MCU) mediante di convertitore analogico digitale (A/D). Uno
dei problemi che si riscontra su tali dispositivi è la questione del rumore sulle
uscite ed è possibile osservarlo come una
continua oscillazione dei valori in uscita dagli assi anche in stato di quiete.
Generalmente per ridurre il rumore si ricorre ad elaborate funzioni matematiche
come il
filtro di Kalman.
Gli accelerometri digitali hanno generalmente
una linea di comunicazione seriale I2C o SPI
[SPI e I2C sono dei bus seriali sincroni e questo
implica che oltre ai dati deve girare un clock. Il clock è fornito da un master
e gli slave lo subiscono. La SPI usa 4 segnali: un clock (SCK), 2 dati (in
uscita (SDO) ed in ingresso (SDI) e un segnale che dice se si tratta di un
master o di uno slave (SS). I2C invece usa 2 segnali: uno per il clock (SCL) e
l'altro per idati (SDA) e che è bidirezionale] e più raramente abbiamo una
UART. In questi casi il valore di accelerazione di ogni asse è
contenuto in uno o più registri e si interroga quindi l’accelerometro per avere
il valore misurato sugli assi.
La scelta tra un accelerometro digitale e un analogico è dettata unicamente dal fatto se è possibile “sacrificare” o meno 3 ingressi analogici. Solitamente l’utilizzo di un accelerometro analogico risulta più semplice.
Come funziona un accelerometro
Per avere uno schema mentale che ci aiuti a visualizzare in qualche modo come opera un accelerometro a 3 assi, immaginiamolo come costituito da una sfera al centro di un cubo, sospesa da 3 molle che la attraversano, agganciate a loro volta al centro di ogni faccia del cubo:
Muovendo il cubo nello spazio, la sfera si muoverà al suo
interno allungando e comprimendo, le molle che la tengono
sospesa. La misurazione del grado di compressione delle molle permette di
stabilire se c’è stata un’accelerazione (ovvero una variazione di velocità)
nella direzione in cui si trova la molla compressa e quindi anche di
quantificarla. Con un accelerometro a 3 assi è possibile rilevare le variazioni di
velocità nelle 3 direzioni dello spazio.
Gli assi di riferimento
dell’accelerometro si originano al centro della sfera, per cui si possono
misurare valori di accelerazione positivi o negativi in dipendenza dal verso in
cui l’accelerazione è diretta. In un accelerometro a 3 assi, la
situazione nel mondo reale non sarà quella rappresentata nella figura precedente ma
piuttosto questa:
La sfera attratta verso il basso dalla forza di gravità determinerà una compressione della parte inferiore della molla orientata lungo l'asse Z.
Un corpo in stato di quiete, quindi, subisce sempre un’accelerazione verso il basso che vale 9.822 m/s2 (o 9.822 N/Kg). Questo valore di accelerazione, dovuto all’interazione gravitazionale, è anche indicato come g (scritto in minuscolo, per distinguerlo dalla G maiuscola che rappresenta la costante di gravitazione universale 6,67x10-11 m3/kgs2).
Per i cellulari non è necessario utilizzare un accelerometro a 3 assi dal momento che le applicazioni che fanno uso di questo “strumento” sono sempre sviluppate su due dimensioni per cui, in questi dispositivi, abbiamo generalmente un accelerometro a 2 assi. In un accelerometro a due assi non è presente la “molla” che attraversa la “sfera” lungo l’asse Z, per cui non è possibile misurare il valore di accelerazione lungo tale asse.
Come determinare l'inclinazione
Oltre a misurare il valore di accelerazione tale
dispositivo può anche essere utilizzato come dispositivo per
misurare
l’inclinazione di un corpo, anzi questo è forse il suo utilizzo più diffuso.
L’accelerometro è in grado sia di misurare l’accelerazione
statica (ad esempio quando lo ruotiamo sul posto) che l’accelerazione
dinamica (ad esempio: lo fissiamo su un’automobile in corsa). Per
applicazioni di tilt sensing (ovvero
la misurazione dell’inclinazione di un corpo) viene appunto sfruttata
la misurazione dell’accelerazione statica.
Assumiamo quindi che la forza di gravità
sia l’unico valore di accelerazione che agisce su un corpo e ignoriamo i valori di
accelerazione superiori ad 1g. Immaginiamo di ruotare il nostro cubo
attorno all’asse
X e di tenerlo fermo con una certa angolazione: la sfera ora comprimerà due molle,
quella lungo l’asse Y e quella lungo l’asse Z (la molla lungo l’asse X mantiene
l'allungamento originale poichè la componente gravitazionale rispetto a tale
asse resta invariato. Possiamo quindi considerare la misura lungo l'asse X come
il valore pari a zero).
Si può facilmente notare che il valore di accelerazione (rappresentato dalla compressione delle 2 molle) letto lungo ciascun asse Y e Z vale meno di 1g per cui si può stabilire che il cubo è in qualche modo è inclinato. Questa assunzione è valida solo se il corpo non è in movimento, stiamo difatti tenendo conto di accelerazioni statiche, ovvero dovute esclusivamente alla forza di gravità terrestre. Misurando il valore di accelerazione lungo gli assi, facendo uso di un po’ di trigonometria, è possibile stabilire di quanti gradi è ruotato il corpo e quindi il dispositivo all’interno del quale l’accelerometro è montato.
Roll - Pitch - Yaw (Rollio - Beccheggio - imbardata)
Introduciamo ora altri concetti relativi alla rotazione di un corpo nello spazio, soprattutto per capire fino a che punto può essere utile un accelerometro per la misurazione dell’ inclinazione. Un corpo può rotare nello spazio lungo 3 assi, facendo riferimento alla dinamica applicata ai velivoli si parla più propriamente di rollio, beccheggio e imbardata. Osservate questa immagine:
Per convenzione la rotazione del velivolo lungo l’asse X
(in rosso) del velivolo si chiama rollio (Roll). La rotazione lungo l’asse Y del
velivolo (in blu) si chiama beccheggio (Pitch) e la rotazione lungo l’asse Z del
velivolo (in verde) si chiama imbardata (Yaw).
Immaginiamo ora che gli
assi X e Y del veivolo si trovino sul piano dell’orizzonte e l’asse Z sia
perpendicolare a questo piano.
Un accelerometro a 3 assi è in grado di rilevare l’angolo di rollio e l’angolo di beccheggio ma non l’angolo di imbardata per quanto appena detto dato che la rotazione intorno a Z non provoca una variazione degli angoli che gli assi formano rispetto all’orizzonte e di conseguenza variazioni sui valori di accelerazione gravitazionale cui i singoli assi X Y sono sottoposti.
Vediamo un ulteriore esempio: prendiamo il cubo dell’immagine
sottostante e facciamolo ruotare unicamente lungo l’asse Z. Le componenti
gravitazionali associate agli assi Y e X non cambiano (gli assi X e Y continuano
a mantenere lo stesso valore di angolazione rispetto al piano dell’orizzonte) e
pertanto le molle restano con gli stessi
valori di tensione.
Per questo motivo non è possibile usare un accelerometro quando occorre anche rilevare l’angolo di imbardata. Per fare questo esistono i giroscopi che sono altri strumenti (ben più costosi!) che servono in maniera più specifica a rilevare gli angoli di rotazione. I giroscopi sono indispensabili sugli elicotteri per evitare la rotazione indesiderata intorno all’asse di rotazione dell’elica principale. Un altro sistema, meno preciso, per rilevare l’angolo di imbardata potrebbe essere quello di utilizzare un magnetometro (una bussola elettronica).
Calcoli per il tilt sensing
Vediamo ora come sfruttare i valori in uscita da un
accelerometro per misurare l’angolo di inclinazione. Partiamo con un
accelerometro ad un solo asse e immaginiamo di averlo in una condizione iniziale
nella quale l’asse X è parallelo all’orizzonte, per cui tutta la forza di
gravità è applicata esclusivamente sull’unico asse.
In questo stato di quiete
l’accelerometro misura 1g sull’unico asse di misura a disposizione. Supponiamo
ora di ruotare l’asse x di un certo angolo θ rispetto al piano dell’orizzonte :
Valutiamo ora la componente della forza di gravità lungo l'asse X (X-Out dell'accellerometro). L’asse X dell’accelerometro è inclinato di un angolo θ rispetto all’orizzonte.
Il massimo valore di accelerazione di gravità che può agire su un corpo vale 1g. La forza che ora agisce sull’asse X dell’accelerometro è rappresentata dal segmento Ax (in viola) che, in base al primo teorema dei triangoli rettangoli (un cateto è uguale al prodotto dell’ipotenusa per il seno dell’angolo opposto o per il coseno dell’angolo adiacente) vale:
Dove il seno(θ) è la proiezione sull'asse Y del segmento unitario che forma un angolo θ con l'asse X.
Se vogliamo raffigurare in un grafico la lunghezza di tale proiezione in funzione dell'angolo θ otteniamo la seguente sinusoide:
Dato che stiamo ragionando sull’accelerazione statica e 1g è un termine unitario, per comodità possiamo omettere la g e sostituirla con 1. La formula precedente diventa
Sfruttando la formula inversa possiamo ricavare l’angolo θ che l’asse X forma con l’orizzonte:
Arcsen è la funzione inversa del seno (arcoseno). Ricordo che θ è misurato in radianti, per avere il valore in gradi basta moltiplicarlo per 180/Π (pigreco)
Utilizzando accelerometri ad un singolo asse, la sensibilità (intesa in questo caso specifico come la variazione del valore in uscita dall’accelerometro in funzione della variazione dell’inclinazione) diminuisce sempre di più man mano l’angolo aumenta: più ci avvicianiamo a ±90° ad una grande variazione di angolo corrisponde una piccola variazione di accelerazione. Questo è evidente nella figura sottostante:
Per apprezzare quindi bene il valore dell’angolo in questi estremi è necessario un accelerometro davvero molto sensibile altrimenti quando siamo prossimi all'angolo retto il valore calcolato con la funzione arcoseno risulterà estremamente impreciso.
Un’altra forte limitazione di un accelerometro a singolo asse è data dal fatto che non è possibile misurare valori di accelerazione su tutti i 360° dal momento che il valore di uscita per un’inclinazione pari a θ (N°) è pari a quello ottenuto per un inclinazione π-θ (180-N°). In altre parole non è possibile distinguere l’inclinazione in un verso o nell’altro. Per alcune applicazioni può anche andar bene, per altre invece no.
Per ovviare a questi
“problemi” si ricorre ad un accelerometro a 2 assi (oppure utilizzando un
secondo accelerometro a singolo asse ma montato in maniera tale che l’asse di
misura sia ortogonale all’asse di misura del primo accelerometro). Nel caso di
un accelerometro a due assi, a seguito ad una rotazione θ
dell'asse X, avremo questa situazione:
Consideriamo le componenti di accelerazione gravitazionale Ax e Ay rispettivamente lungo gli assi X e Y:
Sia φ
l’angolo che l’asse Y dell’accelerometro forma con l’orizzonte. E' evidente la
relazione:
φ = 90°+θ
dato che i due assi sono ortogonali tra loro. L'accelerazione Ay impressa sull'asse Y può essere calcolata allo stesso modo di Ax per cui:
Ay=sin(φ)
Considerando l’angolo opposto (come visibile nella precedente figura) possiamo riscrivere il valore di Ay in funzione θ ovvero:
La somma vettoriale di Ax e Ay (non la somma algebrica) deve fornire il
valore di 1 (1g). La somma vettoriale di due vettori a e b è
così definita:
dove α indica l’angolo che i due vettori formano tra loro. Poichè i due assi
sono tra loro ortogonali segue che il coseno di α
sia zero e quindi la relazione precedente diventa semplicemente:
Al posto di a e
b sostituiamo i vettori Ax e
Ay. La somma
vettoriale deve fornire 1g di accelerazione per cui l'equazione diventa:
L’uso di un accelerometro a 2 assi porta 2 vantaggi:
1) Abbiamo detto che con un solo asse la sensibilità diminuisce man mano ci avviciniamo ad una inclinazione pari a ±90°. Questo continua ad essere vero anche con 2 assi, ma essendo questi assi ortogonali tra loro, quando la sensibilità di un asse diminuisce, la sensibilità dell’altro asse aumenta:
Poichè il valore di accelerazione sull’asse X è proporzionale al seno dell’angolo di inclinazione θ mentre il valore di accelerazione sull’asse Y è proporzionale al coseno dell’angolo di inclinazione θ segue che le due sensibilità si compensino. Il loro rapporto è rappresentato dalla seguente relazione:
Utilizzando la funzione inversa alla tangente (arcotangente) possiamo ricavare il valore di θ:
Questa formula restituisce un valore di θ molto più preciso rispetto alla
θ=arcsen(Ax) dal momento che abbiamo
introdotto un secondo asse che ci garantisce una sensibilità costante in tutto
l’arco di misura.
2) L’altro vantaggio di avere 2 assi è quello che ora
possiamo distinguere l’inclinazione in tutto l’arco dei 360° (con un solo asse
era
possibile stabilire, basandosi sul segno di Ax, solo se si è nei
quadranti in alto (I e II) o in basso (III e IV) :
Questo è possibile dal momento che in ogni quadrante i
valori di accelerazione anche se uguali in modulo, hanno
diverse combinazioni di segno.
Ora passiamo ad un
accelerometro a 3 assi.
Qui la situazione si fa più complessa ma sicuramente più ricca di informazioni.
Misurando i valori di accelerazione sui 3 assi possiamo determinare
l’orientamento dell’oggetto nello spazio (questa affermazione non è proprio
corretta visto che l'imbardata non è determinabile):
Il nostro oggetto non si muoverà più all’interno di una circonferenza ma all’interno di una sfera: la forza di gravità agisce ora su 3 assi di misura, per cui nei calcoli bisogna tenere conto di tutti e 3 gli assi. Qui mi limito esclusivamente a fornire le formule per i calcoli:
Codice Sorgente A
Obiettivo progetto: Costruire un progetto che consenta di calibrare l'accelerometro. La calibrazione non modifica l'output del sensore ma permette di allineare correttamente le uscite del sensore agli assi di riferimento x, y e z.
L'accelerazione di gravità può essere misurata in unità di forza gravitazionale "g" dove 1g rappresenta l'attrazione gravitazionale sulla superficie terrestre.
soluzione: Costruiamo dapprima il seguente circuito (la resistenza è da 10kΩ). Utilizzeremo uno scatolino trasparente per meglio "pilotare" il nostro accelerometro.
Ecco il codice che ci permette di calibrare correttamente il nostro accellerometro
#define DELTA 3 #define N_CAMPIONE 20 #define x_PIN A0 // Cavo Arancio #define y_PIN A1 // Cavo Verde #define z_PIN A2 // Cavo Giallo #define Calibrate_PIN 2 // Inizialmente si pone il minimo 1023 e il massimo 0 - Successivamente // dopo diverse prove ottimiziamo il range e troviamo come valori // accettabili quelli compresi in un range da 270 a 410. // HO IMPOSTATO GIA' IL QUESTO RANGE PER SEMPLIFICARE LE MIE SPIEGAZIONI #define MINSENSOR 270 #define MAXSENSOR 410 // Variabili globali utilizzate per individuare i valori // minini e massimi letti sugli ingressi A0 (x) A1 (y) e A2 (z) int xMin = MINSENSOR; int xMax = MAXSENSOR; int yMin = MINSENSOR; int yMax = MAXSENSOR; int zMin = MINSENSOR; int zMax = MAXSENSOR; int p_xMin = -1; int p_xMax = -1; int p_yMin = -1; int p_yMax = -1; int p_zMin = -1; int p_zMax = -1; int p_x = -1; int p_y = -1; int p_z = -1; void setup() { // Si osservi che in questo progetto la linea successiva // e' stata disabilitata (e in questo caso il range e' 270-410) // analogReference(EXTERNAL); Serial.begin(9600); pinMode(Calibrate_PIN, INPUT); } void loop() { int buttonState = digitalRead(Calibrate_PIN); boolean Cambiato=false; int x = LeggiSensoreAsse(x_PIN); int y = LeggiSensoreAsse(y_PIN); int z = LeggiSensoreAsse(z_PIN); if (buttonState==HIGH) { CalibraAccellerometro(x,y,z); Cambiato=true; } Cambiato=Cambiato || (p_xMin!=xMin); Cambiato=Cambiato || (p_yMin!=yMin); Cambiato=Cambiato || (p_zMin!=zMin); Cambiato=Cambiato || (p_xMax!=xMax); Cambiato=Cambiato || (p_yMax!=yMax); Cambiato=Cambiato || (p_zMax!=zMax); Cambiato=Cambiato || (abs(p_x-x)>DELTA); Cambiato=Cambiato || (abs(p_y-y)>DELTA); Cambiato=Cambiato || (abs(p_z-z)>DELTA); if (Cambiato) { Serial.print("Range Valori (min,max): X = ["+String(xMin)+","+String(xMax)+"]"); Serial.print(" Y = ["+String(yMin)+","+String(yMax)+"]"); Serial.println(" - Z = ["+String(zMin)+","+String(zMax)+"]"); Serial.print("Lettura attuale (x,y,z) = ("+String(x)+","+String(y)+","+String(z)+") :: "); // Converto le letture in millesimi di g (gravità) long xScaled = map(x, xMin, xMax, -1000, 1000); long yScaled = map(y, yMin, yMax, -1000, 1000); long zScaled = map(z, zMin, zMax, -1000, 1000); // riscalo ad una frazione di g float xAccel = round((xScaled-xScaled%10) / 1000.0); float yAccel = round((yScaled-yScaled%10) / 1000.0); float zAccel = round((zScaled-zScaled%10) / 1000.0); Serial.print(xAccel); Serial.print("G, "); Serial.print(yAccel); Serial.print("G, "); Serial.print(zAccel); Serial.println("G"); } p_x = x; p_y = y; p_z = z; p_xMin = xMin; p_xMax = xMax; p_yMin = yMin; p_yMax = yMax; p_zMin = zMin; p_zMax = zMax; delay(500); } // Effettuo più letture per ridurre il rumore // e restituisco la media int LeggiSensoreAsse(int assePin) { long Lettura = 0; analogRead(assePin); //Lettura a vuoto delay(1); for (int i = 0; i < N_CAMPIONE; i++) { Lettura += analogRead(assePin); delay(1); } return Lettura/N_CAMPIONE; } // Determina il range dei valori x,y e z quando // il push button è premuto void CalibraAccellerometro(int x, int y, int z) { Serial.println("Hai premuto il bottone - Calibro..."); if (x < xMin) xMin = x; if (x > xMax) xMax = x; if (y < yMin) yMin = y; if (y > yMax) yMax = y; if (z < zMin) zMin = z; if (z > zMax) zMax = z; }
Si osservi nel codice appena proposto l'uso di una
delle due tecniche utilizzate per limitare gli errori dovuti al rumore.
A
tale scopo possiamo seguire due strade (o entrambe!):
- la prima è quella di
usare il pin AREF dell’Arduino UNO per dare
al convertitore analogico digitale (DAC) di
arduino un riferimento di tensione più basso (di default è 5Vdc). In questo caso
occorre creare un ponte tra il pin AREF e il pin power di 3.3Vdc. Anche nel
sorgente dobbiamo indicare che intendiamo usare il pin AREF
per le operazioni di conversione Analogico-Digitale. L’istruzione che si
utilizza è analogReference(EXTERNAL);
Usando la tensione 3.3Vdc come riferimento per la conversione analogico digitale
otteniamo una precisione maggiore.
- l’altra tecnica è quella di effettuare
diverse letture ed eseguire una media dei valori letti (è quella adottata
nel nostro programma).
Iniziamo con l'agganciare il nostro sensore ad un valido supporto che ci consenta di valutare i valori di risposta in corrispondenza dei tre assi X, Y e Z. Montiamo il sensore in modo su un blocco o uno scatolino di materiale rigido come mostrato in figura. La dimensione non è importante, purché tutti i lati siano tra loro ad angolo retto.
Iniziamo con il posizionare il blocco su una superficie piana (ad esempio un tavolo robusto) in modo che l'asse Y del sensore (serigrafato sul modulo) sia concorde con la forza di gravità.Valutiamo quindi l'asse y (verso concorde alla forza di gravità)
Leggiamo i valori. Avendo posto il range (min,max) dei valori in modo corretto otteniamo un output coerente (-g sull'asse Y e zero su tutti gli altri).
Valutiamo ora l'asse y (verso discorde alla forza di gravità) ribaltando il blocco
Adesso calibriamo l'asse Z (verso concorde alla forza di gravità)
e quindi l'asse Z (verso discorde alla forza di gravità)
Concludiamo con l'asse X (verso concorde alla forza di gravità)
e l'asse X (verso discorde alla forza di gravità)
Una volta che tutti e sei i lati sono stati campionati, i valori registrati nelle variabili p_xMin, p_xMax, p_yMin, p_yMax, p_zMin, p_zMax ad ogni pressione del pulsante di calibrazione rappresenteranno gli estremi di uscita reali per i valori +/- g lungo ogni asse. Riassumento abbiamo:
Codice Sorgente B
Obiettivo progetto: Costruire un progetto che consenta, sfruttando i movimenti di un accelerometro, di simulare nel seguente file di Excel
il rollio (ROLL) e il beccheggio (PITCH) di un modellino di aereoplano.
soluzione: Riutilizziamo lo stesso circuito del precedente esempio ma agganciamo al sensore un oggetto a forma di aereoplano.
Ecco il codice che ci permette di mandare gli opportuni comandi (angoli) al file di Excel per simulare i movimenti del nostro modellino di veivolo
// massimo scostamento per considerare una lettura cambiata #define DELTA 3 #define N_CAMPIONE 20 #define x_PIN A0 // Cavo Arancio #define y_PIN A1 // Cavo Verde #define z_PIN A2 // Cavo Giallo #define Calibrate_PIN 2 #define MINSENSOR 1023 #define MAXSENSOR 0 const float PiGreco=3.141593; // Imposto i valori di Massimo e Minimo ottenuti per ogni asse. // In xMed, yMed e zMed metto le letture quando l'asse x,y e z risulta posta // ad angolo retto rispetto alla forza di gravità (posizioni sul piano // dell'orizzonte)) int xMin=405; int xMax=608; int yMin=409; int yMax=613; int zMin=417; int zMax=617; int xMed=507; int yMed=511; int zMed=516; // Variabili per registrare i valori minimi e massimi reali. // Li memorizzo solo per sfizio poichè questi risentono di accelerazioni // esterne alla gravità int r_xMin = MINSENSOR; int r_xMax = MAXSENSOR; int r_yMin = MINSENSOR; int r_yMax = MAXSENSOR; int r_zMin = MINSENSOR; int r_zMax = MAXSENSOR; // Variabili per valutare il cambio di valore int p_x = -1; int p_y = -1; int p_z = -1; void setup() { // Utilizzo la tensione 3.3v come riferimento analogReference(EXTERNAL); Serial.begin(57600); pinMode(Calibrate_PIN, INPUT); StampaConfigurazione(); } void loop() { int buttonState = digitalRead(Calibrate_PIN); float theta, phi, xGradi, yGradi, zGradi; float argPhi, argTheta; boolean Cambiato=false; int x = LeggiSensoreAsse(x_PIN); int y = LeggiSensoreAsse(y_PIN); int z = LeggiSensoreAsse(z_PIN); AggiornaRange(x,y,z); // valuto per mio interesse i range reali if (buttonState==HIGH) // e li mostro premendo il pushbutton StampaConfigurazione(); // Considero cambiati i valori se la differenza supera // un certo valore DELTA Cambiato=Cambiato || (abs(p_x-x)>DELTA); Cambiato=Cambiato || (abs(p_y-y)>DELTA); Cambiato=Cambiato || (abs(p_z-z)>DELTA); if (Cambiato) { xGradi = mapfloat(x, xMin, xMax, xMed, -90, 90); yGradi = mapfloat(y, yMin, yMax, yMed, -90, 90); zGradi = mapfloat(z, zMin, zMax, zMed, -90, 90); // Phi = angolo asse y e asse forza di gravità (ROLL) // Theta = angolo asse x e asse forza di gravità (PITCH) argTheta=sqrt(yGradi*yGradi+zGradi*zGradi); argPhi=sqrt(xGradi*xGradi+zGradi*zGradi); if (argTheta==0) theta=0; else theta=atan(xGradi/argTheta)*(180/PiGreco); if (argPhi==0) phi=0; else phi=atan(yGradi/argPhi)*(180/PiGreco); Serial.print("START;"); Serial.print("X="+String(x)+";Y="+String(y)+";Z="+String(z)+";"); Serial.print("X^Z="); Serial.print(theta); // PITCH Serial.print(";Y^Z="); Serial.print(phi); // ROLL Serial.print(";X-="+String(r_xMin)+";X+="+String(r_xMax)+";"); Serial.print("Y-="+String(r_yMin)+";Y+="+String(r_yMax)+";"); Serial.println("Z-="+String(r_zMin)+";Z+="+String(r_zMax)+";STOP;"); } p_x = x; p_y = y; p_z = z; delay(20); } // --------------------------------------------------------------------------- // Decodifica non lineare degli angoli poiche' considera anche il punto medio // che corrisponde all'angolo 0 (orizzonte) per aumentare la precisione. Potevo // utilizzare anche il tuning sugli angoli: 30, 45 e 60 gradi // --------------------------------------------------------------------------- float mapfloat(long v, long in_min, long in_max, long in_med, long out_min, long out_max) { float out_med=(out_max-out_min)/2+out_min; if (v < in_min) v=in_min; // Elimino i valori fuori dal range Min, Max if (v > in_max) v=in_max; if (v < in_med) return ((float)(v - in_min) * (float)(out_med - out_min) / (float)(in_med - in_min) + out_min); else return (float)(v - in_med) * (float)(out_max - out_med) / (float)(in_max - in_med) + out_med; } void StampaConfigurazione() { delay(10); Serial.print("CONFIG;X-="+String(xMin)+";X+="+String(xMax)+";"); Serial.print("Y-="+String(yMin)+";Y+="+String(yMax)+";"); Serial.println("Z-="+String(zMin)+";Z+="+String(zMax)+";STOP;"); } // ---------------------------------------------------------------------- // Effettuo più letture per ridurre il rumore // Restituisco la media delle letture scartando la prima // ---------------------------------------------------------------------- int LeggiSensoreAsse(int assePin) { long Lettura = 0; analogRead(assePin); //Lettura a vuoto delay(1); for (int i = 0; i < N_CAMPIONE; i++) { Lettura += analogRead(assePin); delay(1); } return Lettura/N_CAMPIONE; } // ---------------------------------------------------------------------- // Aggiorna il range reale di X, Y e Z. Questo range non è affidabile // poichè muovendo il sensore anche accuratamente si generano // delle accelerazioni aggiuntive a quella gravitazionale che generano errori // sulla stima finale // ---------------------------------------------------------------------- void AggiornaRange(int x, int y, int z) { if (x < r_xMin) r_xMin = x; if (x > r_xMax) r_xMax = x; if (y < r_yMin) r_yMin = y; if (y > r_yMax) r_yMax = y; if (z < r_zMin) r_zMin = z; if (z > r_zMax) r_zMax = z; }
Sul serial monitor visualizzerà i seguenti risultati.