Go Down

Topic: [RESOLU] Lisser mes valeurs lues en PPM (Read 1 time) previous topic - next topic

UniseV

Feb 20, 2013, 12:17 pm Last Edit: Feb 24, 2013, 07:55 pm by UniseV Reason: 1
Bonjour,

Je lis un signal PPM grâce l'INPUT CAPTURE PIN de l'Atmega 328P.

Pour ceux que n'imagine pas ce qu'est un signal PPM, voici un exemple trouvé sur le net :


Dans mon cas j'ai 6 canaux, voici ma sortie COM de DEBUG :



En rouge se sont les valeurs calculées des 6 canaux (ce sont des demi micro-secondes, 2000 vaut donc 1ms)

En vert ce sont des valeurs mappées comme suit, à partir des canaux 2,3 & 6 et qui servent à allumer une LED RVB.
Code: [Select]
   R=map(cha[1],1960,3960,0,255);
   V=map(cha[2],1960,3960,0,255);
   B=map(cha[3],1960,3960,0,255);

En Orange, ce sont les "jitters" qui me posent problème.
Ma question :
Comment "lisser" ces valeurs ?

PS : Le code complet dans le message suivant...
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

UniseV

#1
Feb 20, 2013, 12:18 pm Last Edit: Feb 20, 2013, 12:22 pm by UniseV Reason: 1
Le code :

Code: [Select]
// Lecture PPM6ch & pilotage LED RVB
// C'est le timer1 qui est utilisé la lecture du PPM

#define ledRouge 3 // Constante pour la broche 3
#define ledVert 5 // Constante pour la broche 5
#define ledBleu 6 // Constante pour la broche 6
#define ledPin 13 // Pin de la LED "locale" de l'Arduino


// Données de capture du Rx
boolean endTrame=0,failsafe=0,firstEdge=1;
volatile unsigned int vchUp[8];
volatile unsigned int vchPos[6];
unsigned int cha[6];
byte curEdge;

// Valeurs RVB
int R,V,B;

// Variables permettant de faire clignoter la led de l'Arduino
int ledState = LOW;
long previousMillis = 0;
long interval = 500;

void setup()
{
 // Pin setup, parmetrage des PIN en sortie :
 pinMode (ledVert,OUTPUT); // Broche ledVert configurée en sortie
 pinMode (ledRouge,OUTPUT); // Broche ledRouge configurée en sortie
 pinMode (ledBleu,OUTPUT); // Broche ledBleu configurée en sortie
 pinMode(ledPin,OUTPUT); // Arduino LED

 digitalWrite(ledPin,HIGH); // Allumage de la LED de l'Arduino

 // Sortie DEBUG
 Serial.begin(57600);
 Serial.println("");
 Serial.println("PPM read & Led RVB");
 // Timer1 setup
 TCCR1A=B00000000; // OCR2A/OCR2B : disconnected, Timer: normal mode
 TCCR1B=B00000010; // Falling edge CAPTUREPIN detection, Timer:normal mode, Prescaler=clk/8
 TIMSK1=B00100001; // Activate CAPTUREPIN interrupt & OVF interrupt

 delay(1000); // pour etre certain que les interruptions aient tourné avant la main loop

}

ISR(TIMER1_CAPT_vect)
{
 vchUp[curEdge]=ICR1; // Capture des timestamp 1 à 6
 curEdge++;
 if (curEdge>7) {     // A partie du 7ème...  
   TCNT1=0;           // RESET du counter pour éviter le FailSafe
   if ((vchUp[7]-vchUp[1]) > 30000) {  //Si la trame totale dépasse 15ms - Trame KO ou mauvaise synchro
     curEdge=0;            //Remise à 0 du compteur de fronts                      
   }
   else {                  //Sinon, une bonne trame est capturée, on calcule donc la valeur des canaux
     curEdge=1;
     for (int i=0;i<6;i++) {
       vchPos[i]=(vchUp[i+2]-vchUp[i+1]); //Mesure des canaux (diviser par 2 pour obtenir des µs)
     }
     endTrame=1;           // Pour dire à la MainLoop que de nouvelles valeurs sont disponibles
   }
 }
}

ISR(TIMER1_OVF_vect)
{
 failsafe=1;  // Le compteur a attends sa limite (32ms), on déclenche donc le FailSafe
}

void loop()
{
 if (endTrame==1) {
   cli();
   for (int i=0;i<6;i++) {
     cha[i]=vchPos[i]; // Recopie les valeurs des canaux
   }
   sei();
   endTrame=0;
   LedBlink(500);
   if (failsafe){
     failsafe=0;
     Serial.println("End of FailSafe");
   }

   // La 1ere partie "intelligente" du code se trouve ICI
   // Elle n'est jouée que si les canaux ont "bougé"

   for (int i=0; i < 6; i++){ // Imprimer les valeurs des canaux sur le port série
     Serial.print(cha[i]);
     Serial.print(";");
   }

   // Mapper trois canaux vers les couleurs RVB

   R=map(cha[1],1960,3960,0,255);
   V=map(cha[2],1960,3960,0,255);
   B=map(cha[3],1960,3960,0,255);

   ledRVBpwm(R,V,B); // génère impulsion largeur voulue pour la couleur

   // Imprimer les valauers de la LED

   Serial.print(R);
   Serial.print(";");
   Serial.print(V);
   Serial.print(";");
   Serial.print(B);
   Serial.println(";");
 }

 // La 2eme partie "intelligente" du code se trouve ICI
 // Elle est jouée à chaque tour de boucle

 if (failsafe){

   if (R>255) R=0;    // En mode FAILSAFE, on fait varier les 3 couleurs progressivement & simultanément
   else R++;
   if (V>255) V=0;
   else V++;
   if (B>255) B=0;
   else B++;


   ledRVBpwm(R,V,B); // génère impulsion largeur voulue pour la couleur
   Serial.print("FAILSAFE");
   LedBlink(50);
   Serial.print(R);
   Serial.print(";");
   Serial.print(V);
   Serial.print(";");
   Serial.print(B);
   Serial.println(";");
 }
}


void ledRVBpwm(int pwmRouge, int pwmVert, int pwmBleu) { // reçoit valeur 0-255 par couleur

 //--- attention - avec une LED RGB anode commune : la LED s'allume sur niveau BAS !

 analogWrite(ledRouge, 255-pwmRouge); // impulsion largeur voulue sur la broche 0 = 0% et 255 = 100% haut
 analogWrite(ledVert, 255-pwmVert); // impulsion largeur voulue sur la broche 0 = 0% et 255 = 100% haut
 analogWrite(ledBleu, 255-pwmBleu); // impulsion largeur voulue sur la broche 0 = 0% et 255 = 100% haut


}

void LedBlink(long interval) { // reçoit délais de clignotement

 unsigned long currentMillis = millis();

 if(currentMillis - previousMillis > interval) {
   // save the last time you blinked the LED
   previousMillis = currentMillis;  

   // if the LED is off turn it on and vice-versa:
   if (ledState == LOW)
     ledState = HIGH;
   else
     ledState = LOW;

   // set the LED with the ledState of the variable:
   digitalWrite(ledPin, ledState);
 }
}


EDIT : Le mode FAILSAFE indique que le signal PPM en entrée n'est plus fourni...
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Jean-François

PPM, c'est le principe des signaux pour les servos de modelisme non ?
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5<br />imac PPC G3 os X Pa

fdufnews

#3
Feb 20, 2013, 01:11 pm Last Edit: Feb 20, 2013, 01:22 pm by fdufnews Reason: 1
Quote
Comment "lisser" ces valeurs ?

Filtrage sur une fenêtre glissante
on fait un tableau avec les n dernières valeurs et on calcule la moyenne sur ces valeurs.

ou alors un filtre récursif
  valeur = nouvelle_valeur*p + valeur*(1-p)
0 < p < 1
on joue sur p pour fixer le temps de réaction du filtre. Plus p est petit plus lente sera la réaction à une variation sur la nouvelle valeur.

Artouste


Quote
Comment "lisser" ces valeurs ?

Filtrage sur une fenêtre glissante
on fait un tableau avec les n dernières valeurs et on calcule la moyenne sur ces valeurs.

ou alors un filtre récursif
  valeur = nouvelle_valeur*p + valeur*(1-p)
0 < p < 1
on joue sur p pour fixer le temps de réaction du filtre. Plus p est petit plus lente sera la réaction à une variation sur la nouvelle valeur.

bonjour fdufnews
pas mieux et le recursif simple ici est surement la meilleure méthode sur arduino.
je n'arrive pas a remettre la main sur la vieille étude qui concluait statistiquement que P=0.85 (0.15)  était le meilleur compromis
C'est ce que j'utilise "tout venant"

Jean-François


je n'arrive pas a remettre la main sur la vieille étude qui concluait statistiquement que P=0.85 (0.15)  était le meilleur compromis
C'est ce que j'utilise "tout venant"


Le principe de Pareto
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5<br />imac PPC G3 os X Pa

UniseV


PPM, c'est le principe des signaux pour les servos de modelisme non ?


Oui, c'est le signal avant "dégroupage", le récepteur génère à partir de cette trame un signal PWM servo pour chacun des servo :



J'utilise une radio 6 voies comme "joystick" de DEBUG  ;)

Merci pour vos réponses... je m'y plonge.
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

UniseV


Quote
Comment "lisser" ces valeurs ?

Filtrage sur une fenêtre glissante
on fait un tableau avec les n dernières valeurs et on calcule la moyenne sur ces valeurs.

ou alors un filtre récursif
 valeur = nouvelle_valeur*p + valeur*(1-p)
0 < p < 1
on joue sur p pour fixer le temps de réaction du filtre. Plus p est petit plus lente sera la réaction à une variation sur la nouvelle valeur.


Dans cet exemple, doit-on faire le calcul à chaque fois avec l'ancienne valeur CALCULEE et l'ancienne valeur LUE ?
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Jean-François

Pas sur que ce soit celui dont parle fddunews :

http://arduino.cc/forum/index.php?topic=50134.0
MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5<br />imac PPC G3 os X Pa

fdufnews

Si, si c'est bien la même chose

Jean-François

MacBook intel core 2 duo  os X snow Leopard 10.6<br/> eMac PPc G4  os X Leopard 10.5<br/>powerbook G4 os X Leopard 10.5<br />imac PPC G3 os X Pa

UniseV

Merci à tous !

Après différent essais avec Excel, j'ai finalement opté pour un solution plus "radicale".

Je ne prend en compte la nouvelle valeur mesurée que si elle dépasse un écart minimum.

Code: [Select]
int const ecart=3;    // C'est la valeur de l'hystérésis...

   for (int i=0;i<6;i++) {
     if (vchPos[i] > (cha[i]+ecart) || vchPos[i]< (cha[i]-ecart)) { // Si la nouvelle valeur dépasse l'hystérésis
     cha[i]=vchPos[i]; // Recopie les valeurs des canaux
     }


Ici c'est 3, ça représente 1,5 µs, ça ne bouge donc qu'à partir de 2µs d'écart, pour un servo ça laisse 500 pas, ça me parait correct.
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

UniseV

Hey,

Ca me donne une idée, cette fameuse lecture PPM, je l'utilise finalement assez souvent, et ça me fait faire pas mal de copier/coller de la reprendre à chaque fois.

Vous parait-il possible de la "librariser" ?

Ca serait ma première...  :~
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

UniseV

Voici le code en question :

Code: [Select]
// Lecture PPM6ch par INPUT CAPTURE UNIT
// C'est le timer1 qui est utilisé pour la lecture du PPM
// La lecture du PPM ne peut se faire QUE sur la PIN 8 (ICP1)

boolean endTrame=0,failsafe=0,firstEdge=1;
volatile unsigned int vchUp[8];
volatile unsigned int vchPos[6];
unsigned int cha[6];
byte curEdge;
int const ecart=3;    // C'est la valeur de l'hystérésis...

void setup()
{
 // Timer1 setup
 TCCR1A=B00000000; // OCR2A/OCR2B : disconnected, Timer: normal mode
 TCCR1B=B00000010; // Falling edge CAPTUREPIN detection, Timer:normal mode, Prescaler=clk/8
 TIMSK1=B00100001; // Activate CAPTUREPIN interrupt & OVF interrupt
}

ISR(TIMER1_CAPT_vect)
{
 vchUp[curEdge]=ICR1; // Capture des timestamp 1 à 6
 curEdge++;
 if (curEdge>7) {     // A partie du 7ème...  
   TCNT1=0;           // RESET du counter pour éviter le FailSafe
   if ((vchUp[7]-vchUp[1]) > 30000) {  //Si la trame totale dépasse 15ms - Trame KO ou mauvaise synchro
     curEdge=0;            //Remise à 0 du compteur de fronts                      
   }
   else {                  //Sinon, une bonne trame est capturée, on calcule donc la valeur des canaux
     curEdge=1;
     for (int i=0;i<6;i++) {
       vchPos[i]=(vchUp[i+2]-vchUp[i+1]); //Mesure des canaux (diviser par 2 pour obtenir des µs)
     }
     endTrame=1;           // Pour dire à la MainLoop que de nouvelles valeurs sont disponibles
   }
 }
}

ISR(TIMER1_OVF_vect)
{
 failsafe=1;  // Le compteur a attends sa limite (32ms), on déclenche donc le FailSafe
}


On y retrouve dans l'ordre :

  • La déclaration des variable (dois-je les garder en volatile ?)

  • Le paramétrage du timer1

  • L'interruption sur ICP1

  • L'interruption sur overflow du timer1



C'est principalement ces parties que je souhaite "librariser"...
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

UniseV

#14
Feb 24, 2013, 02:46 pm Last Edit: Feb 24, 2013, 07:57 pm by UniseV Reason: 1
J'ouvre un nouveau sujet pour l'histoire de la librairie, ça sera plus clair  :smiley-roll:

Lisser les valeurs PPM est résolu.
EN: Libraries are my Gurus, they make me believe anything they want !
FR: Les librairies sont mes gourous, elles me font croire ce qu'elles veulent !

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy