Go Down

Topic: Contagiri con LED - riduzione dell'errore con pulseIn (Read 3111 times) previous topic - next topic

crix87

Ciao a tutti, sono nuovo nel forum ed ho iniziato ad usare Arduino da poco, tuttavia sono a corto di tempo e necessiterei di qualche delucidazione.
Ho fatto una ricerca tra i vari contagiri RPM ma non riesco a trovare una risposta all'idea del software adeguata.
Tanto per iniziare, ho realizzato il seguente circuito: http://mckgyver.pbworks.com/w/page/20654133/Arduino_Tachometer
l'ho testato con il codice presente sotto e funziona, tuttavia non riesce a mantenere un numero costante di giri, ma ha un errore abbastanza alto( a 2500 giri, varia +/- di circa 100 giri).
Invece di contare il numero di impulsi in un certo intervallo, ritengo sia più preciso misurare il tempo trascorso tra due impulsi, ed ho provato ad utilizzare il pulseIn.
Inserendo il seguente sketch e testando con un fan F6015B12LY (9 pale, max 3000 giri/min), la durata tra un segnale ad un'altro è assurda (+1000000 us suppongo); inoltre non riesce a leggere i giri/min.
Lo sketch è il seguente, cos'è che sto sbagliando?
è sensato inoltre dire che contando gli la durata tra due impulsi, la misura dovrebbe essere più precisa?

Code: [Select]

//Pulsein code for rpm counting

//Define Variables
const float pi = 3.14;
unsigned long holes__angle = pi/2;
unsigned long duration;  //
unsigned long rad_sec;  //
unsigned long rpm;  //
unsigned long timeout = 10000000;  //
int ledPin = 2;             // LED connected to digital pin (changes state with each tic of code wheel)
int statusPin = 13;             // LED connected to digital pin (changes state with each tic of code wheel)

void setup()
{
   Serial.begin(115200);
   Serial.println("CLEARDATA"); //clean excel spreadsheet
   Serial.println("LABEL,Time,Duration,RPM"); //clean excel spreadsheet
   pinMode(statusPin, OUTPUT); //
   pinMode(ledPin, INPUT); //
}
void loop()
{
   duration=pulseIn(ledPin,HIGH,timeout);
   rad_sec=(holes__angle*1000000)/duration;
   rpm=(rad_sec*60)/(2*pi);
   Serial.print("DATA,TIME,");Serial.print(duration);Serial.println(rpm);
    }


lucaleo

#1
Aug 28, 2013, 03:05 pm Last Edit: Aug 28, 2013, 03:11 pm by lucaleo Reason: 1
http://forum.arduino.cc/index.php?topic=157511.0
Ciao
Io permetto di suggerirti di dare un'occhiata a questo mio topic
Ho sbattuto un bel po il muso pure io su pulse in senza ricavarne nullA di buono sostanzialmente
Se vai al penultimo post della prima pagina trovi il codice che ho fatto per avere rpm con interrupt
Non blocca il codice come pulsein ed e molto preciso in quanto riesco senza problemi a governare l, accensione di un motore a benzina
Spero possa esserti di aiuto
EDIT
Una cosa che ho imparato nei miei mesi di prove e che spesso la lettura imprecisa non arriva dal sensore ma da quello che fai con la lettura
Se fai operazioni con numeri interi ogni volta ti taglia via i decimali e a ogni passaggio perdi in precisione

leo72

Concordo con lucaleo sia sulla lentezza ed imprecisione dei calcoli con i float sia sull'uso degli interrupt al posto della pulseIn.

lucaleo

#3
Aug 28, 2013, 03:46 pm Last Edit: Aug 28, 2013, 04:02 pm by lucaleo Reason: 1

Concordo con lucaleo sia sulla lentezza ed imprecisione dei calcoli con i float sia sull'uso degli interrupt al posto della pulseIn.


questa la incornicio ahaha
è la prima volta che su due cose che scrivo non c'è manco una cagata :D

in ogni caso crix87 ho guardato un po' il tuo codice e onestamente non capisco bene come funziona (o meglio quello che mi pare di aver capito non può andare)

pulsein ti restituisce il tempo trascorso (in millisecondi? non ricordo, ma attenzione perchè non è detto che sia quello che pensi di usare) durante HIGH o LOW a seconda delle tue preferenze
tuttavia tu devi GIA SAPERE a quanti gradi corrisponde quel segnale sulla rotazione totale (puoi calcolarla sommando la durata di high e low se hai un solo dente a un regime stabile di giri o moltiplicando la somma per il numero di high e low dentro a un giro a seconda di quante pale, denti eccetera tu abbia)

il motivo è molto semplice, vedrai che a parità di dente se tu allontani o avvicini il sensore  al centro della rotazione la velocità angolare sarà sempre uguale, ma quella lineare cambia e quindi cambia anche l'ampiezza in gradi di un dente di esempio 3 millimetri di dente a 3 cm da centro o a 1 cm dal centro non sono per niente la stessa cosa

una volta che hai il sensore posizionato e lo hai calibrato allora da li non avrai grossi problemi, solo non fare un hardware ballerino perchè butti via ore e ore a caso (te lo garantisco  :P)

spero di non aver scritto tutto ciò inutilmente e che ti sia di aiuto ahaha

EDIT
ultima info per rendere tutto più comprensibile
non farti ingannare dal fatto che stai misurando una ventola

poniamo caso che hai 6 pale

un conto è guardare quando ci metti a leggere 6 pale e dividere un minuto per quel tempo ottenendo rpm (che va bene)

un conto è misurare quando dura una pala
di istinto viene da pensare 6 pale, 6 spazi quindi 6 pale 180° e 6 spazi 180° quindi ogni pala e ogni spazio valgono 30°
ma non è detto che sia così!!
6 pale e 6 spazi potrebbero anche essere 240° le pale e 120° gli spazi (a seconda di come è fatta la ventola) quindi ogni pala misurerebbe 40° e ogni spazio 20°

per questo per sfruttare la durata della pala o dello spazio è FONDAMENTALE sapere a quanti gradi corrisponde quella pala o quello spazio a quella DETERMINATA distanza dal centro della rotazione

crix87

Cari Lucaleo e Leo72, vi ringrazio per le risposte.
Lucaleo, in realtà probabilmente la mia situazione è più semplice, perchè devo testare delle turbine eoliche, il sensore misurerà la rotazione di un volano su cui andrò ad eseguire 4 fori (credo bastino, che ne dite), quindi spaziati di 90° (pi/2 radianti).
Ho capito la questione sulla distanza dal centro, ma non ho capito se si applichi al mio caso, in quanto io misurerei solo velocità angolari, non lineari, o mi sbaglio?
Adesso provo ad implementare il codice che mi hai suggerito, sperando che le cose migliorino! 8)

lucaleo

#5
Aug 28, 2013, 05:07 pm Last Edit: Aug 28, 2013, 05:09 pm by lucaleo Reason: 1
tutto dipende da come affronti il problema
se tu "conti i buchi" non ti interessa nulla di quello che ho scritto sul calcolo dei gradi
se tu conti "quanto dura un buco" devi per forza mettere questo dato in relazione a quanto dura un giro e per "indovinare" quanto dura il giro devi per forza sapere a quanti gradi su 360 corrisponde il buco
se misuri gli spazi senza buco moltiplicando per 4 avrai una misura molto vicina alla realtà ma ti mancheranno sempre i micros dei buchi che non puoi stimare se non sai quanti gradi sono :D

nel tuo caso potresti anche ottenere un buon risultato tramite gli interrupt (se usi pulsein blocchi tutto in continuazione) misurando sia la durata di un buco che la durata dello spazio senza, sommare i valori e moltiplicare per 4 e ottenere il tempo di un giro senza sapere a quanti gradi corrisponde il buco e lo spazio

oppure ancora più preciso fai un contatore, poi prendi i valori di ogni buco che passa e ogni spazio vuoto fino a quando non sono passati 4 buchi e 4 spazi, sommi tutto e hai il tempo esatto di ogni giro

io sul motore non posso usare questi metodi perchè devo sapere quanti giri sto facendo e in che posizione sono del giro prima di un certo momento, ma tu non hai questo limite credo

crix87

Mi consola il fatto che il codice dovrebbe essere più semplice di quello ce hai postato nell'altro topic  :)

Quindi, per misurare i giri, posso ripartire da qui:
http://mckgyver.pbworks.com/w/page/20654133/Arduino_Tachometer
Tuttavia, invece di far aumentare i giri, come faccio a calcolare il tempo tra gli interrupt? uso sempre la libreria stopwatch o ci sono funzioni più semplici?

lucaleo

guarda stopwatch l'ho usata perchè sono scarso e non riesco a usare la funzione millis o micros che sia  :smiley-roll-sweat:

tuttavia prendo spunto dal codice che hai postato e provo a modificarlo per quello che serve a te

magari imparo qualcosa su millis intanto :D

Code: [Select]

 //Arduino Code
 
 /*
 
  * Tachometer
 
  *
 
  * M.G.McKinley
 
  * 6/1/2009
 
  * Adjust
 
  */
 
 //Define Variables
 
 int ticsPerRev = 16;       // define number of tics per rev of code wheel
 
 float rpm = 0.0;  // I prefer float for rpm
 
 volatile int rpmcount =0;  // volitile because the variable is manipulated during the interrupt
 
 unsigned long timeold = 0; // used to calculate d_t= millis()-timeold;
 
 int d_t;
 
 
 
 
 
 void setup()
 
 {
 
   Serial.begin(9600);
 
   //Interrupt 0 is digital pin 2, so that is where the IR detector is connected
 
   //Triggers on FALLING (change from HIGH to LOW)
 
   attachInterrupt(0, rpm_fun, FALLING);
 
 
 
 
 
 }
 
 void loop()
 
 {
 
   //Update RPM every second
 
 
 
   //Don't process interrupts during calculations
 
   detachInterrupt(0);
 
   d_t=millis()-timeold;
 
 
 
   if (d_t >= 1000)
 
   {
 
     rpm = float(60.0*1000.0)/float((d_t))*float(rpmcount)/ticsPerRev;
 
 
 
     timeold = millis();
 
     d_t=0; //reset d_t
 
 
 
     //Serial Port Output
 
 
 
     Serial.println(rpm);
 
 
 
     rpmcount = 0; //reset rpmcount
 
 
 
   }
 
   //Restart the interrupt processing
 
   attachInterrupt(0, rpm_fun, FALLING);
 
 }
 
 void rpm_fun()
 
 {
 
   //This interrupt is run at each codewheel tic
 
   detachInterrupt(0); //im not sure if this is necessary here
 
 
 
   rpmcount++; //update rpmcount
 
 
 
 
 
 
 
   attachInterrupt(0, rpm_fun, FALLING);
 
   }


ho tolto un po' di roba che faceva confusione per capire la dinamica del codice

così DOVREBBE funzionare, non sono per nulla bravo...

prova :D

ah mi raccomando ricordati di modificare il numero delle pale, per ora sono 16

crix87

grazie per la revisione, in effetti sembra andare meglio!
Li tolgo i float dal codice? meglio dichiarare le variabili con long?

lucaleo

mah dipende da quanto può durare la rotazione forse puoi usare pure int visto che usi millis
però non me ne intendo... ho capito qualcosina da poco

in ogni caso quel codice non è pensato per essere molto preciso secondo me, quello imposta un intervallo per il loop di 1 secondo e ogni secondo guarda quanti denti sono passati e con qualche calcolo ti dice i giri

io proverei a far partire un contatore alla prima pala e fermarlo quando inizia la 16esima (con falling quando inizia la pala tu hai l'interrupt), in questo modo avresti il vero tempo di un giro

se ho scritto delle boiate che qualcuno mi corregga :D

leo72

Millis restituisce un unsigned long, quindi devi usare questo tipo di dato per immagazzinare il valore restituito dalla funzione.

lucaleo


Millis restituisce un unsigned long, quindi devi usare questo tipo di dato per immagazzinare il valore restituito dalla funzione.


possibile che tu mi abbia appena spiegato perchè non sono mai riuscito a usare millis :D

crix87

Vi allego il codice aggiornato - credo funzioni ma la ventola di prova non è attendibile :)

Code: [Select]

//Arduino Code
/*
* Tachometer
*
* M.G.McKinley
* 6/1/2009
* Adjust
*/
//Define Variables

int ticsPerRev = 7;       // define number of tics per rev of code wheel
unsigned long rpm;  //
volatile int rpmcount;  // volitile because the variable is manipulated during the interrupt
unsigned long timeold; // used to calculate d_t= millis()-timeold;
unsigned long d_t;
 
void setup()
{
  Serial.begin(9600);
  Serial.println("CLEARDATA"); //clean excel spreadsheet
  Serial.println("LABEL,Time,RPM"); //clean excel spreadsheet
  attachInterrupt(0, rpm_fun, FALLING);
}

void loop()
{
  detachInterrupt(0);
  d_t=micros()-timeold;
  if (d_t >=500000)
   {
     rpm = (60*1000000)/((d_t))*(rpmcount)/ticsPerRev;
     timeold = micros();
     d_t=0; //reset d_t

//Serial Port Output
     Serial.print("DATA,TIME,");Serial.println(rpm);
     rpmcount = 0; //reset rpmcount
  }
     attachInterrupt(0, rpm_fun, FALLING);
}

void rpm_fun()
{
    detachInterrupt(0); //im not sure if this is necessary here
    rpmcount++; //update rpmcount
    attachInterrupt(0, rpm_fun, FALLING);

}

crix87

Tuttavia come detto non è ancora abbastanza accurato, stavo pensando di spostare l'INPUT dal pin2 al pin8 e utilizzare il timer1 inserito nel codice. tuttavia il timer1 non mi è chiarissimo e non riesco ad utilizzarlo:
1) credete che sia possibile utilizzardo, spostando solo un filo a livello hardware?
2) consigli a livello software? non riesco a trovare degli sketch adeguati per un rpm! :smiley-roll-sweat:

lucaleo

ma cosa devi fare?
è più semplice aiutarti se lo sappiamo
e poi sono curioso ;)

Go Up