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
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
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
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
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

MacBook intel core 2 duo  os X snow Leopard 10.6
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
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
 eMac PPc G4  os X Leopard 10.5
powerbook G4 os X Leopard 10.5
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