Go Down

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

crix87

devo fare dei test in galleria del vento di una piccola turbina eolica, praticamente partendo da zero devo misurare la velocità massima della turbina e il tempo con cui ha raggiunto il massimo dei giri (per intenderci, ad 1 m/s di velocità d'aria, la turbina magari gira a 200 rpm, arduino mi deve indicare nel modo più accurato in quanto tempo la turbina partendo da zero è arrivata a 200 rpm).
In poche parole al momento il codice non va bene perchè non resta stabile ad un picco di giri, in quanto magari varia di +/- 100 giri a 2500rpm, ed inoltre il software non sembra capace di leggere i giri al di sotto dei 15 rpm. L'hardware invece va bene perchè è stato testato con l'oscilloscopio, quindi è solo una questione di software!
Fatemi sapere se vi viene in mente una soluzione!!  :smiley-roll-sweat:

leo72


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:

Se vuoi usare il timer 1 come Input Capture con timestamp, il timestamp altro non è che il valore del registro del timer 1 stesso,. Dovresti impostare una certa frequenza, altrimenti quel valore è inutilizzabile, poi volta che viene registrato l'evento, il timer 1 ti salva il contenuto del suo registro in un altro registro. A quel punto devi far sollevare un interrupt, dall'interrupt estrarre il valore e poi resettare il contatore.

crix87

se invece utilizzassi Stopwatch senza Interrupt per capire il tempo tra due HIGH?
Ha senso dire nelle variabili byte status = LOW ; poi fare un loop che attivi lo StopWatch al momento dell'HIGH (Stopwatch_start), e lo fermi al secondo HIGH (Stopwatch_stop), per poi riportare Stopwatch_start=Stopwatch_stop e calcolare i giri tramite la different Stopwatch_stop-Stopwatch_start?

lucaleo

prova questo

Code: [Select]

#include <StopWatch.h>



const byte pin2 = 2;     // interrupt startt
const byte pin3 = 3;     // interrupt stopp

volatile int statestartt = HIGH;    // stato normale startt
volatile int statestopp=HIGH;       // stato normale stopp

StopWatch sw_micros(StopWatch::MICROS);    // timer durata dente



long time = 0;           //valore durata dente microsecondi



int rpm = 0;             //numero giri
int gradidente = 6;     //ampiezza dente in gradi

void setup() {
Serial.begin(9600);     //comunicazione seriale

 pinMode(pin2, INPUT_PULLUP);
 pinMode(pin3, INPUT_PULLUP);
 attachInterrupt(0, startt, FALLING);   //interrupt in inizio dente
 attachInterrupt(1, stopp, RISING);     //interrupt fine dente
}

void loop()
{
 //se il dente è iniziato
 if(statestartt == LOW){
   sw_micros.start();//inizia a contare durata dente
   statestartt=HIGH;// reimposta startt
 }

 //se il dente è finito
 if(statestopp == LOW) {
   time=sw_micros.elapsed();                  //memorizza durata dente
   
 
   statestopp = HIGH;                         //reimposta stopp
   sw_micros.reset();     //azzera timer durata dente
 
    rpm=60000000/((time/gradidente)*360);      //calcolo rpm
Serial.println(rpm);                       //stampo rpm
 
 

   
 
 }


 
} // End Loop

void startt()//interrupt startt
{
 statestartt = !statestartt;  //inverti startt
}

void stopp()//interrupt stopp
{
 statestopp = !statestopp;    //inverti stopp
}


devi solo calcolare i gradi della pala (misura il raggio dal sensore al centro, fai la circonferenza, misura la larghezza della pala a quel raggio e ottieni una buona approssimazione
dovresti ottenere una stampa di rpm ogni pala alla fine della pala (quindi nello spazio vuoto dandoti il tempo di fare calcoli e stampare)

collega il filo del sensore sia al pin 2 e al pin 3
per legere i buchi o gli spazi inverti rising e falling negli interrupt

prova :D

crix87

Perfetto lo provo subito! L'unico problema è che mi dice che sulla riga
Stopwatch sw_micros(Stopwatch::MICROS);    // timer durata dente
non mi rileva stopwatch e la variabile sw_micros
non è questa la libreria stopwatch?
http://www.avdweb.nl/arduino/hardware-interfacing/stopwatch.html#h0-1-5-1-stopwatchh

crix87

risolto per la libreria, era questa: http://playground.arduino.cc/Code/StopWatchClass

lucaleo

esatto
poi se ti metti a giocare un po' con le operazioni e soprattutto il tipo di dato con cui le fai puoi ottenere risultati di rpm più precisi
a me non è mai servito quindi un po' approssima rpm fatto così

crix87

più tardi lo testo e vi aggiorno sui risultati!

PaoloP


crix87

Non sono ancora riuscito a testare lo sketch, spero di testarlo nel pomeriggio.
Nel frattempo ho trovato questo sito:
http://www.fiz-ix.com/2012/11/measuring-signal-frequency-with-arduino/
ed ho provato a cambiare la porta di ingresso da 2 a 8 per usare il timer.
Non avendo il display ho tolto alcune righe relative all'LCD, ed ho modificato lo sketch (vedere sotto), tuttavia il sensore legge dati in continuo. Credo che il problema sia nel while e nella formula per contare la frequenza, non capisco perchè divide le pulsazioni per 13???

Code: [Select]

//define all the variables
int inputpin = 2;
unsigned long stateChanges;
//double freq;
unsigned long freq;
unsigned long rpm;
unsigned long temp = 0;
int state1 = 0;
int state2 = 0;
unsigned long prerpm;

void setup(){
  pinMode(inputpin, INPUT);
  pinMode(13,OUTPUT);
  Serial.begin(9600);
  freq = 0;
  rpm = 0;
  stateChanges = 0;
  prerpm = 0;
}

void loop(){
  state1 = digitalRead(inputpin);
  delayMicroseconds(10);
  state2 = digitalRead(inputpin);             //check for debounces...
  if (state1 == state2) {                     //check if both readings are same
  temp = micros();
  while((micros()-temp) < 1000000) {         //Wait for 1 second and measure number of state changes during that interval.
    if(digitalRead(inputpin) != state2) {    //check if state has changed..
      stateChanges++;                     
      state2 = digitalRead(inputpin);
    }
   }
  freq = (stateChanges)/13;            //calculate frequency...
  rpm = freq*60;                            //calculate rpm
  Serial.println(rpm);
  prerpm = rpm;
  stateChanges = 0;
  freq = 0;
  rpm = 0;
  }
}

lucaleo

buongiorno :)
secondo il mio modesto parere quel 13 significa che sul suo hardware aveva 6 pale e 6 spazi

se tu provi a disegnare una croce su un pezzo di carta e metti un numero prima e dopo ogni riga (un cambio stato a inizio pala, uno a fine pala eccetera) vedrai che per fare un giro completo devi avere 9 cambi di stato e non 8, quindi li 13 e non 12, ma è solo una teoria :)
1 e 9 dovrebbero essere lo stesso punto dopo un giro

lui in pratica conta i cambi di stato in un secondo e lo divide per il numero di cambi in un ciclo, ottiene giri al secondo e moltiplica per 60 che sono rpm

per la lettura in continuo non capisco cosa intendi, direi che quel codice deve stampare rpm ogni secondo circa

tra l'altro è carino sto codice :D

crix87

Buongiorno Lucaleo  8)
infatti non è male lo sketch, grazie per la delucidazione sul 13!
per continuo intendo che il software legge impulsi senza inserire la turbina in mezzo!
Quale parte del software è sbagliata??

crix87

torniamo al caro vecchio sketch iniziale updated by Lucaleo:
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 = 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;
unsigned long d_t;
 
void setup()
{
  Serial.begin(115200);
  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.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);

}


Visto che l'hardware funziona e che il contagiri a 550 rpm max sembra sfalsare di +/-50 rpm, c'è qualche algoritmo che mi permetta di ottenere una media più decente?

lucaleo

#28
Aug 29, 2013, 06:06 pm Last Edit: Aug 29, 2013, 06:44 pm by lucaleo Reason: 1
Mah.... io abbandonerei quel codice o per lo meno va riscritto tutto perche secondo me non arriverai mai a una grande precisione....
Il codice che ti ho messo modificando quello mio della centralina credo che sia quello che ci vuole, prova a vedere se funzionicchia poi possiamo metterci un contatore e stampare rpm una volta al giro invece che ogni pala o qualunque altra cosa ti serva
Senza considerare che quel codice che ho messo se tu invece che lavorare con rpm ottenuto tramite calcoli lavori direttamente con i micros (con un hardware fisso tu puoi sapere con un po di matematica quando ci mettera il tuo buco a passare davanti al sensore a un qualunque numero di giri) di essere estremamente preciso nella misurazione ;)
Invece che lavorare con rpm potrai lavorare cosi
Sai che (numeri inventati) a 200 rpm il tuo buco passa in 1060 micros e a 201 in 1050
Se hai una lettura di 1057 potrai dire di essere a 200 rpm con un'ottima precisione :)
Il valore stampato di rpm sara sempre arrotondato nei vari calcoli ma i micros no, sono cosi come.escono....

EDIT
tra l'altro per misurare quanto ci metti da 0rpm a x rpm in maniera accurata devi avere rpm una volta al giro almeno, con quel codice avrai sempre un errore misterioso di +- 2 secondi max dato dal fatto che il contagiri ti aggiorna una volta al secondo sia quando inizia a girare che quando arrivi a rpm max ma non puoi sapere se hai iniziato a girare all'inizio o alla fine di quel secondo e lo stesso quando arrivi a rpm max

ha ragione paolo, è interessante sto problema :D
tra l'altro guardando i giri da un'ottica diversa da quelli del motore mi stanno venendo delle idee per modificare il mio di progetto :D

crix87

Grazie Lucaleo per il feedback, la prossima settimana ho anche un maggiore supporto nella parte software quindi dovremo essere in grado di realizzare qualcosa di interessante.
Per quanto riguarda l'hardware, che schema hai usato?

Go Up