[progetto completo] conta PPM e RPM

Ciao,
andando avanti con il quadricottero io ed un mio amico abbiamo deciso di “mappare” i valori PPM agli RPM che il motore (completo di elica) ci fornisce.

parte HW:
N° 1 L metallica con bracci da 10CM e vari buchi per fissaggio (1€ al pezzo al leroy merlin)
N° 2 L metallica con bracci da 3CM e vari buchi per fissaggio, per caso tondi e misura a led (diametro 5) (0.6€/pezzo)
N° 1 set di dado/bullone (1€ confezione da 12)
N° 4 viti da legno con rondella (1€ confezioni di viti + 1€ confezione di 5 rondelle… alla faccia!)
N° 1 base di legno (in questo caso un ripiano per mobile in offerta a 1.5€)
N° 2 LED IR da telecomando, 1€ l’uno al negozio più vicino (alla faccia!)
N° 1 r da 680ohm per il led emettitore
N° 1 r da 3MegaOhm per il fotodiodo/led usato come fotodiodo (nel mio caso 3 r da 1MegaOhm in serie)

purtroppo i modelli di led usati sono sconosciuti perchè comprati in un negozio che non è riustito a fornirceli. Quello che posso dire è che il fotorilevatore fornito è risultato più scarso di un led emettitore usato come rilevatore…

passiamo all’assemblaggio:
montate una C unendo le due L metalliche piccole. Dovreste ottenereun gap di circa 5cm, ottimo per evitare di toccare l’elica in caso di vibrazioni (vedere immagine)
attaccate la L metallica grnade al piano di legno con 2 (o più) viti da legno e rondelle

fissate temporaneamente la C creata alla L “portante”, poggiate il motore con elica, e segnate l’altezza che deve avere la C sulla L e la posizione del motore perchè le eliche passino tra i led che fisseremo sulla C ma senza incidentarsi sul supporto!

fissate il motore alla giusta distanza (l’elica deve pa sul piano di legno con le altre 2 (o più) viti da legno, fate attenione a lasciare un piccolo gap tra motore e base di legno, poichè moltoi motori hanno l’asse che sporge un poco sotto. oppure fate un buco, vedete voi.

Fate attenzione, queste viti (e il peso della base di legno) 4devono tenere ancorato il motore! Io come test ho sollevato il tutto tenendo dal motore, l’asse di legno pesa qualche kiletto quindi ero sicuronon prendesse il volo :slight_smile:

saldate dei fili ai led per poterci lavorare agevolmente su un circuito un po distante dalle eliche… meno fili passano lì vicino e meglio è!

ho notato che “intubare” i led da un vantaggio molto piccolo, invece le eliche sono quasi trasparenti agli IR, ma lo scotch da elettricista invece è impermeabile agli IR. Ho quindi messo due striscie di scoth sulle eliche, certo ne rovina il profilo/equilibrio, ma non spero non abbia influito troppo.

i valori di riferimento sono:
lettura ADC del fascio senza interruzioni: 800
lettura ADC del fascio senza interruzioni (con fotorilevatore): 600 - a questo punto il fotorilevatore è stato scartato
lettura ADC con elica: 600
lettura ADC con elica e ricevitore intubato: 400
lettura ADC con elica scocciata: 0

Gli RPM sono contati come numero di richiusure del fascio (RISING) attraverso un interrupt (pin 2, INT0), ma con un “antibounce” di 1milliSecondo (quindi rileva circa 20.000 RPM al massimo,ma come limite può essere abbassato di molto IMHO)

I PPM sono generati con la classica libreria Servo ma usando la writeMicroseconds() invece che la write() per avere maggiore precisione. Ogni secondo il valore di interrupt viene stampato, con velocità del PPM e RPM calcolati a partire dal numero di Pale impostato.

Il prossimo post includerà il codice usato

Il circuito usato è, parte lettura

VCC (3.3v)-> LED katodo -> LED anodo -> R da 3MegaOhm -> GND
                                              ^-- al pin 2

Il circuito usato è, parte emettitore (bhe classico led)

VCC (3.3v) -> LED anodo -> LED katodo -> R da 680ohm -> GND

pin 7 al segnale dell’esc, GND arduino al GND esc, VCC esc non collegato in quanto con BEC integrato.

Allego un particolare della C in costruzione/test dei led/rilevatori, e una del prodotto finito

#include <Servo.h> 

const unsigned int minServo = 800;
const unsigned int maxServo = 1800;
const unsigned int secondiAttesa = 1;
const unsigned int numeroPale = 2;
const unsigned int saltoPPM = 100;
 
Servo myservo;
unsigned int microsServo = 800;
boolean direzione = true;
unsigned int numeroVolte = 5;

unsigned int numeroVolteAttuale = 0;

volatile int count = 0;


void setup(){
  Serial.begin(19200);
  
  attachInterrupt(0, conta, RISING);//int0=pin2, int1 = pin3
  
  myservo.attach(9);
  
  myservo.writeMicroseconds(microsServo);
  
  delay(2000);

  Serial.println("Inizializzazione completa, premi un tasto per contnuare");
  while (Serial.read() ==-1){
  }
  Serial.println("START");
  count = 0;
}

void loop(){
  delay(1000*secondiAttesa);
  int countCopia = count;
  count = 0;
  Serial.print("PPM: ");
  Serial.print(microsServo);
  Serial.print(" CAMBI: ");
  Serial.print(countCopia);
  Serial.print(" calculated RPM: ");
  Serial.println( (((float)countCopia/secondiAttesa)/numeroPale)*60 );
  
  if (direzione){
    microsServo+=saltoPPM;
  }else{
    microsServo-=saltoPPM;
  }
  if (microsServo < minServo){
    microsServo = minServo;
    direzione = true;
    numeroVolteAttuale++;
    if (numeroVolteAttuale > numeroVolte){
      Serial.println("END");
      while(true){
        ;//do nothing, stop it
      }
    }
  } 
  if (microsServo > maxServo){
    microsServo = maxServo;
    direzione = false;
  }
  
  myservo.writeMicroseconds(microsServo);
}

unsigned long lastCount = 0;
unsigned long tmpLastCount = 0;

void conta(){
  tmpLastCount = micros();
  if (tmpLastCount - lastCount >= 1000){
    count++;
    lastCount = tmpLastCount;
  }
}

Output (reale, fresco frescoi di sensori)

Inizializzazione completa, premi un tasto per contnuare
START
PPM: 800 CAMBI: 1 calculated RPM: 30.00
PPM: 900 CAMBI: 0 calculated RPM: 0.00
PPM: 1000 CAMBI: 38 calculated RPM: 1140.00
PPM: 1100 CAMBI: 87 calculated RPM: 2610.00
PPM: 1200 CAMBI: 115 calculated RPM: 3450.00
PPM: 1300 CAMBI: 143 calculated RPM: 4290.00
PPM: 1400 CAMBI: 175 calculated RPM: 5250.00
PPM: 1500 CAMBI: 204 calculated RPM: 6120.00
PPM: 1600 CAMBI: 229 calculated RPM: 6870.00
PPM: 1700 CAMBI: 231 calculated RPM: 6930.00
PPM: 1800 CAMBI: 231 calculated RPM: 6930.00
PPM: 1800 CAMBI: 231 calculated RPM: 6930.00
PPM: 1700 CAMBI: 230 calculated RPM: 6900.00
PPM: 1600 CAMBI: 211 calculated RPM: 6330.00
PPM: 1500 CAMBI: 187 calculated RPM: 5610.00
PPM: 1400 CAMBI: 160 calculated RPM: 4800.00
PPM: 1300 CAMBI: 134 calculated RPM: 4020.00
PPM: 1200 CAMBI: 113 calculated RPM: 3390.00
PPM: 1100 CAMBI: 91 calculated RPM: 2730.00
PPM: 1000 CAMBI: 64 calculated RPM: 1920.00
PPM: 900 CAMBI: 32 calculated RPM: 960.00
PPM: 800 CAMBI: 5 calculated RPM: 150.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 900 CAMBI: 0 calculated RPM: 0.00
PPM: 1000 CAMBI: 36 calculated RPM: 1080.00
PPM: 1100 CAMBI: 78 calculated RPM: 2340.00
PPM: 1200 CAMBI: 105 calculated RPM: 3150.00
PPM: 1300 CAMBI: 128 calculated RPM: 3840.00
PPM: 1400 CAMBI: 155 calculated RPM: 4650.00
PPM: 1500 CAMBI: 183 calculated RPM: 5490.00
PPM: 1600 CAMBI: 207 calculated RPM: 6210.00
PPM: 1700 CAMBI: 227 calculated RPM: 6810.00
PPM: 1800 CAMBI: 230 calculated RPM: 6900.00
PPM: 1800 CAMBI: 229 calculated RPM: 6870.00
PPM: 1700 CAMBI: 227 calculated RPM: 6810.00
PPM: 1600 CAMBI: 206 calculated RPM: 6180.00
PPM: 1500 CAMBI: 186 calculated RPM: 5580.00
PPM: 1400 CAMBI: 158 calculated RPM: 4740.00
PPM: 1300 CAMBI: 133 calculated RPM: 3990.00
PPM: 1200 CAMBI: 111 calculated RPM: 3330.00
PPM: 1100 CAMBI: 89 calculated RPM: 2670.00
PPM: 1000 CAMBI: 63 calculated RPM: 1890.00
PPM: 900 CAMBI: 24 calculated RPM: 720.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 900 CAMBI: 0 calculated RPM: 0.00
PPM: 1000 CAMBI: 36 calculated RPM: 1080.00
PPM: 1100 CAMBI: 76 calculated RPM: 2280.00
PPM: 1200 CAMBI: 104 calculated RPM: 3120.00
PPM: 1300 CAMBI: 126 calculated RPM: 3780.00
PPM: 1400 CAMBI: 154 calculated RPM: 4620.00
PPM: 1500 CAMBI: 181 calculated RPM: 5430.00
PPM: 1600 CAMBI: 203 calculated RPM: 6090.00
PPM: 1700 CAMBI: 225 calculated RPM: 6750.00
PPM: 1800 CAMBI: 228 calculated RPM: 6840.00
PPM: 1800 CAMBI: 229 calculated RPM: 6870.00
PPM: 1700 CAMBI: 226 calculated RPM: 6780.00
PPM: 1600 CAMBI: 206 calculated RPM: 6180.00
PPM: 1500 CAMBI: 185 calculated RPM: 5550.00
PPM: 1400 CAMBI: 157 calculated RPM: 4710.00
PPM: 1300 CAMBI: 132 calculated RPM: 3960.00
PPM: 1200 CAMBI: 111 calculated RPM: 3330.00
PPM: 1100 CAMBI: 89 calculated RPM: 2670.00
PPM: 1000 CAMBI: 62 calculated RPM: 1860.00
PPM: 900 CAMBI: 27 calculated RPM: 810.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 900 CAMBI: 0 calculated RPM: 0.00
PPM: 1000 CAMBI: 35 calculated RPM: 1050.00
PPM: 1100 CAMBI: 75 calculated RPM: 2250.00
PPM: 1200 CAMBI: 104 calculated RPM: 3120.00
PPM: 1300 CAMBI: 125 calculated RPM: 3750.00
PPM: 1400 CAMBI: 153 calculated RPM: 4590.00
PPM: 1500 CAMBI: 180 calculated RPM: 5400.00
PPM: 1600 CAMBI: 202 calculated RPM: 6060.00
PPM: 1700 CAMBI: 224 calculated RPM: 6720.00
PPM: 1800 CAMBI: 227 calculated RPM: 6810.00
PPM: 1800 CAMBI: 227 calculated RPM: 6810.00
PPM: 1700 CAMBI: 226 calculated RPM: 6780.00
PPM: 1600 CAMBI: 204 calculated RPM: 6120.00
PPM: 1500 CAMBI: 184 calculated RPM: 5520.00
PPM: 1400 CAMBI: 158 calculated RPM: 4740.00
PPM: 1300 CAMBI: 131 calculated RPM: 3930.00
PPM: 1200 CAMBI: 110 calculated RPM: 3300.00
PPM: 1100 CAMBI: 88 calculated RPM: 2640.00
PPM: 1000 CAMBI: 62 calculated RPM: 1860.00
PPM: 900 CAMBI: 26 calculated RPM: 780.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 900 CAMBI: 0 calculated RPM: 0.00
PPM: 1000 CAMBI: 36 calculated RPM: 1080.00
PPM: 1100 CAMBI: 76 calculated RPM: 2280.00
PPM: 1200 CAMBI: 102 calculated RPM: 3060.00
PPM: 1300 CAMBI: 126 calculated RPM: 3780.00
PPM: 1400 CAMBI: 152 calculated RPM: 4560.00
PPM: 1500 CAMBI: 180 calculated RPM: 5400.00
PPM: 1600 CAMBI: 202 calculated RPM: 6060.00
PPM: 1700 CAMBI: 223 calculated RPM: 6690.00
PPM: 1800 CAMBI: 227 calculated RPM: 6810.00
PPM: 1800 CAMBI: 226 calculated RPM: 6780.00
PPM: 1700 CAMBI: 225 calculated RPM: 6750.00
PPM: 1600 CAMBI: 204 calculated RPM: 6120.00
PPM: 1500 CAMBI: 184 calculated RPM: 5520.00
PPM: 1400 CAMBI: 156 calculated RPM: 4680.00
PPM: 1300 CAMBI: 131 calculated RPM: 3930.00
PPM: 1200 CAMBI: 110 calculated RPM: 3300.00
PPM: 1100 CAMBI: 88 calculated RPM: 2640.00
PPM: 1000 CAMBI: 61 calculated RPM: 1830.00
PPM: 900 CAMBI: 26 calculated RPM: 780.00
PPM: 800 CAMBI: 1 calculated RPM: 30.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
PPM: 900 CAMBI: 0 calculated RPM: 0.00
PPM: 1000 CAMBI: 34 calculated RPM: 1020.00
PPM: 1100 CAMBI: 75 calculated RPM: 2250.00
PPM: 1200 CAMBI: 102 calculated RPM: 3060.00
PPM: 1300 CAMBI: 126 calculated RPM: 3780.00
PPM: 1400 CAMBI: 151 calculated RPM: 4530.00
PPM: 1500 CAMBI: 180 calculated RPM: 5400.00
PPM: 1600 CAMBI: 201 calculated RPM: 6030.00
PPM: 1700 CAMBI: 223 calculated RPM: 6690.00
PPM: 1800 CAMBI: 226 calculated RPM: 6780.00
PPM: 1800 CAMBI: 226 calculated RPM: 6780.00
PPM: 1700 CAMBI: 223 calculated RPM: 6690.00
PPM: 1600 CAMBI: 203 calculated RPM: 6090.00
PPM: 1500 CAMBI: 183 calculated RPM: 5490.00
PPM: 1400 CAMBI: 156 calculated RPM: 4680.00
PPM: 1300 CAMBI: 130 calculated RPM: 3900.00
PPM: 1200 CAMBI: 108 calculated RPM: 3240.00
PPM: 1100 CAMBI: 87 calculated RPM: 2610.00
PPM: 1000 CAMBI: 60 calculated RPM: 1800.00
PPM: 900 CAMBI: 22 calculated RPM: 660.00
PPM: 800 CAMBI: 0 calculated RPM: 0.00
END

Interessante. :wink:

lesto:
N° 1 r da 3MegaOhm per il fotodiodo/led usato come fotodiodo (nel mio caso 3 r da 1MegaOhm in serie)

Da dove viene fuori questo valore assurdo ? Mi sa tanto che stai usando in modo non corretto il foto ricettore, pubblica lo schema.

è sembrato strano anche a me, però per generare un salto da 0 a >3V questo è il valore che ho trovato. Lo schema è lo stesso sia per il fotodiodo che per il fotoricettore.
Non sono sicuro che l’allineamento del simbolo del diodo sia corretta.

Immagine.png

Intanto, usare un fototransistor sarebbe molto meglio ... colleghi emettitore a massa, collettore al +5V con una resistenza da 10K, e dal collettore ci tiri fuori impulsi che vanno da 0,6V (illuminato) a circa 5V (oscurato) ... :wink:

Scusa. ma stai usando il sistema come sensore a riflessione ? ... o il led e' da un lato ed il fotodiodo e' dall'altro ? ... e se misuri al bordo dell'elica, come se la cava il fotodiodo a regimi alti, con quella velocita' periferica ?

lesto:
Lo schema è lo stesso sia per il fotodiodo che per il fotoricettore.

Infatti lo schema è sbagliato :slight_smile:
Il diodo fotoricettore si comporta come un generatore di corrente, e non di tensione come certi asini sostengono, in funzione della quantità di luce, nel corretto spettro, ricevuta, deve essere utilizzato con polarizzazione inversa altrimenti si comporta come un normale diodo :slight_smile:

Dimenticavo, la resistenza va messa tra GND e Anodo del diodo con un valore compreso tra 50-100k, e questo è il punto di misura, il Catodo del diodo direttamente ai +5V.

il led e' da un lato ed il fotodiodo e' dall'altro, per questo ho costruito la C

il fototransistor non ci ho pensato, ma mi risulta essere più lento di un fotodiodo (anche se probabilmente non è il collo di bottiglia)

per quanto riguarda invertire il led, se lo usi invertito, provando a fare delle letture analogiche, si nota che il valore non varia al cambio della luce/telecomando

quindi a parte invertire il led sul disegno, tentare di nuovo col fotodiodo, mi dici di usare 50-100k che credo però non mi diano il drop di tenzione desiderato (ora provo), il resto mi sembra uguale.

sto provando ora con una 40K (10k * 4)… led che usavo prima (letture adc arduino da 0 a 5v) da 0 a 20, fotodiodo1 da 0 a 400, fotodiodo2 da 0 a 1023
il fotoriodo 2 lavora da 0 a 600 circa con 20K… uso 30K e sto tranquillo.

edit: nel frattempo ho lavorato al codice, ora oltre che al numero di giri ogni secondo, faccio un secondo test cheogni 10ms stampa l’ultima frequenza calcolata di “giramento di pale”. Il tutto accellera al massimo e poi al minimo di colpo (a quanche secondo di distanza) in modo da disegnare la curca di risposta del motore. certo è una curva da 0 a 100, e la frequenza non tiene conto del numero di pale, e sicuramente varia partendo da altri valori, ma piano piano si possono costruire tutti i test.
Si accettano consigli. (allego grafico del risultato)

lesto:
di “giramento di pale”.

Meno male che almeno questo l’hai scritto giusto, pensa se ti scappava un “l” di troppo :grin:

ah, il nuovo codice… tral’altro nel grfico c’è un bugghino con i float che mostra solo 6 come frequenza massima, mentre invece è 220, curiosamente la forma d’onda è la stessa.
ora il test fa varie accelerazioni partendo da fermo e da “in movimento”, per non falsare le misure con il fatto che il motore è fermo, cosa che nel mio caso non dovrebbe mai accadere.

#include <Servo.h> 

const unsigned int minServo = 800;
const unsigned int maxServo = 1800;
const unsigned int secondiAttesa = 1;
const unsigned int numeroPale = 2;
const unsigned int saltoPPM = 100;

Servo myservo;
unsigned int microsServo = 800;
boolean direzione = true;
unsigned int numeroVolte = 1;

unsigned int numeroVolteAttuale = 0;

volatile int count = 0;
volatile unsigned long frequenza = 0;

void setup(){
  Serial.begin(19200);
  
  attachInterrupt(0, conta, RISING);//int0=pin2, int1 = pin3
  
  myservo.attach(9);
  
  myservo.writeMicroseconds(microsServo);
  
  delay(2000);

  Serial.println("Inizializzazione completa, premi un tasto per contnuare");
  while (Serial.read() ==-1){
  }
  Serial.println("START");
//  Serial.println("fine calcolo range PPM");
  count = 0;
}

byte stato = 0;
void loop(){
  switch(stato){
    case 0:
      testRange();
      break;
    case 1:
      testSpeed();
      break;
    default:
    myservo.writeMicroseconds(minServo);
  }
}

void misuraFreq(){
  //test for 2 seconds
  unsigned long tmpF;
  for (int i=0; i<400; i++){
    tmpF = frequenza;
    Serial.print("F:");
    Serial.println(tmpF);
    delay(10);
  }
}

void testSpeed(){
  Serial.print("Calcolo tempi di risposta: ");
  Serial.flush();
  //go to STOP
  myservo.writeMicroseconds(minServo);
  delay(4000);//stabilize RPM
  
  //go to FULL
  myservo.writeMicroseconds(maxServo);
  misuraFreq();
  
  //go to SLOW
  myservo.writeMicroseconds(minServo+200);
  misuraFreq();  
  
  //go to FULL
  myservo.writeMicroseconds(maxServo);
  misuraFreq();
  
  //go to SLOW
  myservo.writeMicroseconds(minServo+200);
  misuraFreq();

  //go to FULL
  myservo.writeMicroseconds(maxServo);
  misuraFreq();  
  
  //go to STOP
  myservo.writeMicroseconds(minServo);
  misuraFreq();
  
  Serial.println("fine calcolo tempi risposta");
  stato++;
}

void testRange(){
  delay(1000*secondiAttesa);
  int countCopia = count;
  count = 0;
  Serial.print("PPM: ");
  Serial.print(microsServo);
  Serial.print(" CAMBI: ");
  Serial.print(countCopia);
  Serial.print(" calculated RPM: ");
  Serial.println( (((float)countCopia/secondiAttesa)/numeroPale)*60 );
  
  if (direzione){
    microsServo+=saltoPPM;
  }else{
    microsServo-=saltoPPM;
  }
  if (microsServo < minServo){
    microsServo = minServo;
    direzione = true;
    numeroVolteAttuale++;
    if (numeroVolteAttuale > numeroVolte){
      Serial.println("fine calcolo range PPM");
      stato++;
    }
  } 
  if (microsServo > maxServo){
    microsServo = maxServo;
    direzione = false;
  }
  
  myservo.writeMicroseconds(microsServo);
}

unsigned long lastCount = 0;
unsigned long tmpLastCount = 0;
unsigned long delta = 0;

void conta(){
  tmpLastCount = micros();
  delta=tmpLastCount - lastCount;

  //if (delta >= 1000){
    count++;
    lastCount = tmpLastCount;
    frequenza = 1000000ul/delta;
  //}
}