Aller au contenu


Photo
- - - - -

Lire les signaux d'une radiocommande RC avec Arduino


  • Veuillez vous connecter pour répondre
15 réponses à ce sujet

#1 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 14 avril 2018 - 01:31

Salut à tous, 

 

Voici un petit fil de discussion pour parler des techniques de lecture des signaux d'une radiocommande RC de modélisme avec Arduino  pour éventuellement m'aider à rédiger le billet de mon blog en cours : Comment utiliser une radiocommande RC de modélisme pour piloter nos robots de manière optimale ? 

En effet nous avons déjà rédigé un tutoriel très simple expliquant la méthode " facile" pour lire les signaux d'une télécommande RC de modélisme avec arduino 

cependant cette méthode n'est pas super optimisé car elle utilise des delay et la fonction pulseIn, qui sont toutes les deux des fonctions " blocante" ( Rappelez moi que je dois pondre un article sur qu'est ce qu'une fonction bloquante, pourquoi faut il les éviter, quelques exemples de fonctions blocante, comment faire des fonctions non blocante ! ) 

 

Donc je m'intéresse à comment faire cette lecture de signaux RC sans utiliser de fonctions bloquantes pour la suite ! 

 

Pour ma part au vu de la problématique je ne vois que 3 approches possible : 

Des interruption sur changement d'état : les interruption sur changement d'état des channels de la radion
Des interruptions sur timer : On va interrompre très fréquement notre code pour regarder l'état des channel de la radio

Une machine à état avec l'exécution d'un état etant super court, et on boucle à l'infini entre : lire les channel, mettre à jour l'état, exécuter l'état. 

Avant de me lancer dans le code je fais quelques recherches sur ce qui existe déjà je tombe sur ces liens qui semblent être intéressant : 

 

Sur le forum Anglais " RC Arduino" ( tien tien ce nom de forum a en effet visiblement vaguement un air de famille avec notre sujet )

 

http://rcarduino.blogspot.fr/2012/04/how-to-read-multiple-rc-channels-draft.html   avril 2012

 

http://rcarduino.blogspot.fr/2012/11/how-to-read-rc-channels-rcarduinofastlib.html novembre 2012

 

On notera particulièrement que le travail présenté sur ces deux sujets est celui de Duane B

 

Question et réponses sur arduino cc

 

Question et réponses sur Stackoverflow 

 

Readreceiver ?

 

RC lib ?

 

 

 

Bref vant que je ne continue plus loin mon investigation je me suis dit que je pouvais partager cette réflexion pour vois si certains utilisent déjà une librairie ou autre qui serait sympas ! =) 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#2 Path

Path

    Made By Humans

  • Modérateur
  • PipPipPipPipPip
  • 2 504 messages
  • Gender:Male
  • Location:Paris

Posté 14 avril 2018 - 03:09

L'arduino sait lire une tension analogique. Un petit circuit RC voire LRC doit te permettre de lisser le signal PWM. Et là pas besoin de lib et de code non bloquant ;)

Hou ... un tas de truc me reviennent en mémoire ... intégrateur, passe-bas :) 


Modifié par Path, 14 avril 2018 - 03:23 .


#3 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 14 avril 2018 - 05:08

Lecture analogique ça ne me plait pas du tout pour un signal PPM comme ceux des arduino ! Voir ce tuto que j'ai fais qui explique la différence entre PWM PPM etc... 

En effet lire la valeur analogique d'un signal PWM dont le rapport oscille entre 3,5 et 10,5 % ça ne te laisse que 7% de toute la plage analogique exploitable ...  en gros en utilisant des filtres passifs dans le meilleur  des cas tu pourras lire une valeur numérique avec analog read entre 35 et 107 ... soit 72 valeurs ce que je trouve bien peu ...  

 

J'aimerais bien au minimum une résolution 8 bits avec  256 valeurs =) grosso modo une précision au centième de ms. Ce qui est tout à fait atteignable en numérique vu que la précision des timers et de la fonction micros permettent d'avoir une comptage du temps dont la résolution est de 4 µS ...

 

Bon après tu peux utiliser des AOP pour Amplifier le signal et soustraire la constante et faire en sorte que le signal varie ainsi entre 0 et 5V quand les signal RC est passe du min au max , et ajouter des suiveurs pour éviter que les opérations se pertubent de la même façon que ce que j'ai fais pour mon robot RMAD le robot fou ... et ainsi on aurait une résolution de 10 bits mais bon ... le but c'est que tout le monde puisse l'utiliser sans ajouter d'hardware ... =) 

 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#4 ashira

ashira

    Pilier du forum

  • Modérateur
  • PipPipPipPipPip
  • 1 333 messages
  • Gender:Male

Posté 14 avril 2018 - 05:42

Tu peux aussi appliquer une tension particulière sur la pin AREF pour changer la plage de conversion analogique de 0-5v à 0-Varef. En gros si tu appliques 1v tu as tes 10 bits de résolution sur 0-1v.

Mais je suis d'accord avec toi vaut mieux trouver une solution en numérique.



#5 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 14 avril 2018 - 06:36

Bon vous avez gagné je ferais un paragraphe dans le billet sur la façon de faire en analogique si quelqu'un fait les essais ;) mais le but du billet sera de conclure sur la façon numérique de faire la plus simple que je trouverais ;)


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#6 Path

Path

    Made By Humans

  • Modérateur
  • PipPipPipPipPip
  • 2 504 messages
  • Gender:Male
  • Location:Paris

Posté 14 avril 2018 - 08:29

Ok pour le numérique :)

 

J'élimine pulseIn(). Cf. la doc de Mike sur le PPM. Et pis de toute façon la fonction est bloquante donc de base, elle me plait pas :)

 

The timing of this function has been determined empirically and will probably show errors in longer pulses. Works on pulses from 10 microseconds to 3 minutes in length.

 

Les interruption sur timer, je les trouve trop imprécises. Je remmènerais les choix à un seul : l'interruption sur changement d'état.



#7 Path

Path

    Made By Humans

  • Modérateur
  • PipPipPipPipPip
  • 2 504 messages
  • Gender:Male
  • Location:Paris

Posté 15 avril 2018 - 11:22

Parce que oui, je post des conneries pour voir si y en a qui suivent ... (sifflote)

 

Alors comme personne ne suit ce sujet. Je retire ce que je dit au dessus. La fonction pulseIn() doit fonctionner pour le PPM. Elle a une limitation basse à 10 microsecondes. La plage PPM va de 0,7 à 2,1 millisecondes. Donc, j'ai dit des conneries. Bah oui, ce matin, je me suis demandé mais comment elle fait cette fonction pour nous permettre d'utiliser le module Ultrason HC-SR04 ... Bref. Il reste qu'elle est bloquante :)

 

Aller, pour me faire pardonner, voilà un petit code non bloquant avec des interruptions. Voilà comment je ferai. J'ai pas de signal RC sous la main pour valider.

#define PIN_RC_SIGNAL 2 // interrupt 0 arduino nano

volatile unsigned long risingTimestamp = 0L;
volatile long duty = 0L;

void interruptOnRising() {
	risingTimestamp = micros();
}

void interruptOnFalling() {
	duty = micros() - risingTimestamp;
}


void setup() {
	Serial.begin(115200);

	pinMode(PIN_RC_SIGNAL, INPUT);

	attachInterrupt(digitalPinToInterrupt(PIN_RC_SIGNAL), interruptOnRising, RISING);
	attachInterrupt(digitalPinToInterrupt(PIN_RC_SIGNAL), interruptOnFalling, FALLING);
}

void loop() {
	if(duty > 0) {
		if(duty < 20000) { // On n'a pas raté l'interruption rising.
			Serial.println(duty, DEC); // Le temps passé à l'état haut en microsecondes
			duty = 0L;
		}
	}
}


NB. Sur les nano (et je suppose sur tous les 16MHz, la fonction micros() a une résolution de 4 us. C'est un multiple de 4. Mais c'est toujours mieux que millis().

https://www.arduino....ns/time/micros/


Modifié par Path, 15 avril 2018 - 12:05 .
ajout du code


#8 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 15 avril 2018 - 01:18

 

Les interruption sur timer, je les trouve trop imprécises. Je remmènerais les choix à un seul : l'interruption sur changement d'état.

 

Bon aller juste pour l'exercice et rien que pour te contredire par esprit de contradiction ( bien que je pense que l'interruption sur changement d'état soit une très bonne alternative ;) ) je vais essayer de voir ce que je peux faire d'optimisé sans regarder ce qui existe sans utiliser l'interruption sur changement d'état et ensuite j'essayerais de comparer avec un code utilisant le changement d'état =).

 

Bon l'idée général de construction du code : 

Je vais mettre tout les canaux RC à lire sur le même port pout optimiser la lecture, par exemple tout sur les pins 0 à 7 ( PORTD ) ou tout sur les pins 8 à 13 ( PORTB) ou tout sur les analog (PORTC) 

Ainsi je pourrais lire tout les états en une seule fois en faisant : etatcourant = PIND ;  ( si on est sur PORTD, sinon PINC ou PINB ... )  avec état courant sur 8 bits. 

Je pourrais ainsi facilement détecter un changement en faisant : if(etatcourant ! = oldetat)  =). 

Je pense créer 3 tableaux à N éléments N étant le nombre de signaux ,
Sur ces 3 tableaux
2 seraient des uint32_t qui contiendront des références de temps de départ et d'arrêt de chaque signal en micro secondes 

1 serait le temps du signal en microsecondes sur un uint16_t

 

et le code utilisé serait un truc du style : 

// Code permettant de lire les canaux d'un récepteur RC
// Définition des variables utilisées

#define NBCHANNEL 8        // Nombre de channel
uint32_t timeus;           // temps en microseconde
uint8_t oldetat;           // ancien etat des pins RC
uint8_t etatcourant;       // état courant des pins RC 
uint32_t arret[NBCHANNEL]; // contient la valeur de l'instant en microseconde du falling de chaque état
uint32_t start[NBCHANNEL]; // contient la valeur de l'instant en microseconde du rising de chaque état
uint16_t temps[NBCHANNEL]; // différence entre arret et start
bool val;                  // valeur intermédiaire pour représenter l'état du signal Haut ou bas


oldetat = etatcourant ;                      // avant de mettre à jour l'état courant on enregistre le courant dans old
etatcourant = PIND;                          // on met à jour je courant
if(etatcourant != oldetat) {                 // on regarde si il y a un changement entre le courant et le précédent
 timeus = micros ();                         // on enregistre le temps 
 for (uint8_t i = 0; i<NBCHANNEL ; i++) {    // on va boucler sur les channels 
  val = etatcourant&(1<<i);                  // on regarde l'état de chaque channel 
  if ( val != oldetat&(1<<i) ) {             // on regarde si il y a un changement de l'état du channel par rapport à son état précédent
   if (val) start[i] = timeus;               // si on est en signal Haut, on vient de faire un rising on enregistre l'instant dans start 
   else {                                    // sinon on est en signal bas et on a donc fait un falling
    arret[i] = timeus;                       // on enregistre l'instant dans arrêt
    temps[i] = arret[i] - start[i];          // on calcul le temps du signal
   }
  }
 }
}

EDIT : Mince j'ai pas vu la réponse de de path avant :) mais ça ne change pas ce que j'ai marqué =)


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#9 Path

Path

    Made By Humans

  • Modérateur
  • PipPipPipPipPip
  • 2 504 messages
  • Gender:Male
  • Location:Paris

Posté 15 avril 2018 - 01:34

Pas mal ^^

Tu peux gérer plus de 2 canaux. C'est mieux que les interruptions. Et c'est toujours non bloquant ;)



#10 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 15 avril 2018 - 02:40

En effet =) là techniquement ça peut gérer 8 canaux sur PIND... 6 sur PINC et PINB  et comme  ma radio a que 6 canaux... c'est nickel =)

 

Bon maintenant on va chercher à améliorer encore un peu le bout de code =) . 

On peut par exemple " vérifier " l'intégrité de notre résultat avant de l'enregistrer =) 

// Code permettant de lire les canaux d'un récepteur RC
// Définition des variables utilisées

#define NBCHANNEL 8        // Nombre de channel
#define TMAX 2500          // 2,5 ms = 2500us
#define TMIN 700           // 0,7ms  = 700us

uint32_t timeus;           // temps en microseconde
uint8_t oldetat;           // ancien etat des pins RC
uint8_t etatcourant;       // état courant des pins RC 
uint32_t arret[NBCHANNEL]; // contient la valeur de l'instant en microseconde du falling de chaque état
uint32_t start[NBCHANNEL]; // contient la valeur de l'instant en microseconde du rising de chaque état
uint16_t temps[NBCHANNEL]; // différence entre arret et start

bool val;                  // variable intermédiaire pour représenter l'état du signal Haut ou bas
uint32_t t;                // variable intermédiaire pour vérifier l'intégrité du résultat 

oldetat = etatcourant ;                      // avant de mettre à jour l'état courant on enregistre le courant dans old
etatcourant = PINB;                          // on met à jour tous les channels d'un coup en lisant le port complet
if(etatcourant != oldetat) {                 // on regarde si il y a un changement entre le courant et le précédent
 timeus = micros ();                         // on enregistre le temps 
 for (uint8_t i = 0; i<NBCHANNEL ; i++) {    // on va boucler sur les channels 
  val = etatcourant&(1<<i);                  // on regarde l'état de chaque channel 
  if ( val != oldetat&(1<<i) ) {             // on regarde si il y a un changement de l'état du channel par rapport à son état précédent
   if (val) start[i] = timeus;               // si on est en signal Haut, on vient de faire un rising on enregistre l'instant dans start 
   else {                                    // sinon on est en signal bas et on a donc fait un falling
    arret[i] = timeus;                       // on enregistre l'instant dans arrêt
    t = arret[i] - start[i];                 // on calcul le temps du signal
    if( t < TMAX &  t > TMIN)                // on vérifie l'intégrité du résultat
      temps[i] = t;                          // on écrit le résultat que si il est intègre
   }
  }
 }
}

Reste maintenant à en faire une fonction créer l'interruption sur timer et spécifier les types de variables qui seront volatile (  variables gloabales utilisées en interruption )

 

 

En gros tout ça ça vous donne un aperçu du type de code qui se trouve sur Robil =) 

 

 

Et pour ceux qui veulent creuser plus sur le sujet des interruption avec un microcontrôleur Atmega 328 p


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#11 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 21 avril 2018 - 12:21

Maintenant le code qui permettrait d'exploiter les signaux RC  en utilisant ce qui est au dessus :

// Code pour arduino uno ou équivalent, réalisé par Mike118 
// Ce code permet de montrer comment piloter un robot télécommandé par radiocommande RC 
// Retrouver le projet complet sur www.robot-maker.com

// Configuration hardware
// 0 et 1 UART free
// A4 A5 I2C free
// 5 et 6 PWM vitesse des moteurs
// A0 A1 A2 A3 => Direction des moteurs
// 8 9 10 11 12 13 => Channels RC
// Timer 1 => Lecture des channels RC => cela va affecter la librairie servo...
// 2 et 3 hardware interrupt => free possible bumper
// 4 et 7 free => Des leds ?

#include <TimerOne.h> // Pour le timer 

// Contrôle des Moteurs
#define SPEEDA 5       // Contrôle vitesse moteur 1
#define SPEEDB 6       //controle vitesse moteur 2
#define IN1A A0
#define IN2A A1
#define IN1B A2
#define IN2B A3

// Lecture Channel
#define NBCHANNEL 6
#define CHANNEL0 8 
#define CHANNEL1 9
#define CHANNEL2 10 
#define CHANNEL3 11
#define CHANNEL4 12
#define CHANNEL5 13

#define TMAX 2500          // 2,5 ms = 2500us
#define TMIN 700           // 0,7ms  = 700us
#define NEUTRE 1900

#define TIMEOUT 50        // 50 ms

// volatile => pour toute variable qui sera utilise dans les interruptions 

volatile uint16_t temps[NBCHANNEL];
volatile bool change = 0;


// variable globale
int16_t vitesseLineaire;
int16_t vitesseAngulaire;
int16_t vitesseD; 
int16_t vitesseG;
uint32_t reftime;



void setup()
{

 // https://www.arduino.cc/en/Reference/PortManipulation
 DDRD = DDRD | 0b01100000;   // permet de mettre les pins 5 et 6 en sortie
 PORTD = PORTD & 0b10011111; // permet de mettre les pins 5 et 6 à 0 
 DDRC = DDRC | 0b00001111;   // permet de mettre les pins A0 à A3 en sorte
 PORTC = PORTC & 0b11110000; // permet de mettre les pins A0 à A3 à 0 
 DDRB = DDRB & 0b11000000;   // permet de mettre les pins 8 à 13 en entrée
 
  //initialisation moniteur serie
  //Serial.begin(9600);       // facultatif uniquement pour feedback

 Timer1.initialize(4); // set a timer of length 4 microseconds
 Timer1.attachInterrupt( timerIsr );
}

void loop(){

  if(change) {                                        
   reftime = millis();
   // on fait nos traitements sur les temps lus
   vitesseLineaire = temps[0] - NEUTRE;          
   vitesseAngulaire = temps[1] - NEUTRE;
   vitesseD = vitesseLineaire - vitesseAngulaire;
   vitesseG = vitesseLineaire + vitesseAngulaire;
   //setMotorSpeeds( vitesseD, vitesseG);
   change = 0;
  }
  else {
    if (millis() - reftime > TIMEOUT ) {
      // Action à effectuer en boucle si on ne reçoit plus de données ...
      //setMotorSpeeds( vitesseD, vitesseG);
    }
  }
}


void timerIsr()
{

 uint32_t timeus;                  // temps en microseconde
 static uint8_t oldetat;           // ancien etat des pins RC
 uint8_t etatcourant;              // état courant des pins RC 
 static uint32_t arret[NBCHANNEL]; // contient la valeur de l'instant en microseconde du falling de chaque état
 static uint32_t start[NBCHANNEL]; // contient la valeur de l'instant en microseconde du rising de chaque état
 
 bool val;                         // variable intermédiaire pour représenter l'état du signal Haut ou bas
 uint32_t t;                       // variable intermédiaire pour vérifier l'intégrité du résultat 
 
 etatcourant = PINB & 0b00111111;             // on met à jour tous les channels d'un coup en lisant le port complet et on met à 0 les bits lus des channels non utilisés
 if(etatcourant != oldetat) {                 // on regarde si il y a un changement entre le courant et le précédent
  timeus = micros ();                         // on enregistre le temps 
  for (uint8_t i = 0; i < NBCHANNEL; i++) {   // on boucle sur les channels 
   val = etatcourant&(1<<i);                  // on regarde l'état de chaque channel 
   if ( val != oldetat&(1<<i) ) {             // on regarde si il y a un changement de l'état du channel par rapport à son état précédent
    if (val) start[i] = timeus;               // si on est en signal Haut, on vient de faire un rising on enregistre l'instant dans start 
    else {                                    // sinon on est en signal bas et on a donc fait un falling
     arret[i] = timeus;                       // on enregistre l'instant dans arrêt
     t = arret[i] - start[i];                 // on calcul le temps du signal
     if( t < TMAX &  t > TMIN) {              // on vérifie l'intégrité du résultat
      temps[i] = t;                           // on écrit le résultat que si il est intègre
      change = 1;
     }
    }
   }
  }
        

  oldetat = etatcourant ;                         // avant de quitter on enregistre le courant dans old
 }
}


il me reste à faire la fonction setMotorSpeeds mais bon c'est pas le but de ce sujet ;) la suite sera disponible sur le blog ! =)

 

C'est pas encore testé x) mais au moins j'indique le fil de ma réflexion =) 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#12 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 23 avril 2018 - 09:47

Salut,

 

Merci pour l'explication, c'est super d'avoir le raisonnement, c'est beaucoup plus formateur que de copier/coller qqc dont on ne comprend pas les subtilités.

 

 

Il me semble en lisant rapidement ton dernier code que tu ne "reset" pas change dans loop, donc après le premier changement dans l'interruption, tu passeras tjs par le bloc "if (change)" dans loop().

De même, oldetat n'est pas mis à jour dans l'interruption, il faudrait le faire juste après "if(etatcourant != oldetat) {timeus = micros ();".

 

Une question sur 

etatcourant = PIND & (1 << NBCHANNEL);

je ne comprends pas le but, pour moi ici tu ne mets à jour que le bit 6 de PIND, c'est bien ce qui est recherché ?

J'aurai mieux compris de ne mettre à jour que les 6 premiers bits (qui sont les 6 channels) et ne pas mettre à jours les 7 et 8 même si changement, mais dans ce cas moi je ferais qqc du genre:

etatcourant = PIND & 0x3F;

//ou en version plus propre par rapport au nombre de channel
etatcourant = PIND & ((1<<NBCHANNEL) -1);

Toujours dans un soucis de lisibilité et d'explications, je pense qu'il faut expliquer briévement le rôle des "static" devant des variables (comme tu l'as fait pour volatile).

 

 

Et dernier point ou je suis moins sur, il me semble que quand on joue avec du > 8bits (donc plus qu'un byte quoi) sur les variables volatiles, il est conseillé de de desactiver les interruptions lors de la lecture de la variable (genre "vitesseLineaire = temps[0]"), puisque la lecture n'est pas faite atomiquement mais en deux passes. Et que donc sur un coup de pas de bol, l'interruption peut arriver entre la lecture du premier octet et celle du second.



#13 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 23 avril 2018 - 10:43

En effet il y a quelques coquilles, ( absence de la remise à zéro de change  et l'erreur du masque ) que j'ai corrigé =) C'est ça de poster du code non testé ^^

 

Par contre oldEtat est bien mis à jour à la fin de l'interruption, il faut pas le faire trop tôt car il est utilisé pendant le traitement =) 

 

je vais voir prendre en compte la remarque pour la taille de la variable vu qu'elle est en 16bits ... peut être la passer directement en 8bits ... 

Oui c'est ça,  Je vais soustraire le " Neutre " et faire des variables en int8_t directement dans l'interruption ... 

 

=> 

// Code pour arduino uno ou équivalent, réalisé par Mike118 
// Ce code permet de montrer comment piloter un robot télécommandé par radiocommande RC 
// Retrouver le projet complet sur www.robot-maker.com

// Configuration hardware
// 0 et 1 UART free
// A4 A5 I2C free
// 5 et 6 PWM vitesse des moteurs
// A0 A1 A2 A3 => Direction des moteurs
// 8 9 10 11 12 13 => Channels RC
// Timer 1 => Lecture des channels RC => cela va affecter la librairie servo...
// 2 et 3 hardware interrupt => free possible bumper
// 4 et 7 free => Des leds ?

#include <TimerOne.h> // Pour le timer 

// Contrôle des Moteurs
#define SPEEDA 5       // Contrôle vitesse moteur 1
#define SPEEDB 6       //controle vitesse moteur 2
#define IN1A A0
#define IN2A A1
#define IN1B A2
#define IN2B A3

// Lecture Channel
#define NBCHANNEL 6
#define CHANNEL0 8 
#define CHANNEL1 9
#define CHANNEL2 10 
#define CHANNEL3 11
#define CHANNEL4 12
#define CHANNEL5 13

#define TMAX 2200          // 2,2 ms = 2500us
#define TMIN 700           // 0,7ms  = 700us
#define NEUTRE 1450        // 1,45 ms 

#define TIMEOUT 50        // 50 ms

// volatile => pour toute variable qui sera utilise dans les interruptions 

volatile int8_t consigne[NBCHANNEL];
volatile bool change = 0;


// variable globale
int16_t vitesseLineaire;
int16_t vitesseAngulaire;
int16_t vitesseD; 
int16_t vitesseG;
uint32_t reftime;


void setup()
{

 // https://www.arduino.cc/en/Reference/PortManipulation
 DDRD = DDRD | 0b01100000;   // permet de mettre les pins 5 et 6 en sortie
 PORTD = PORTD & 0b10011111; // permet de mettre les pins 5 et 6 à 0 
 DDRC = DDRC | 0b00001111;   // permet de mettre les pins A0 à A3 en sorte
 PORTC = PORTC & 0b11110000; // permet de mettre les pins A0 à A3 à 0 
 DDRB = DDRB & 0b11000000;   // permet de mettre les pins 8 à 13 en entrée
 
  //initialisation moniteur serie
  //Serial.begin(9600);       // facultatif uniquement pour feedback

 Timer1.initialize(4); // set a timer of length 4 microseconds
 Timer1.attachInterrupt( timerIsr );
}

void loop(){

  if(change) {                                        
   reftime = millis();
   // on fait nos traitements sur les consignes
   vitesseLineaire = consigne[0];          
   vitesseAngulaire = consigne[1];
   vitesseD = vitesseLineaire - vitesseAngulaire;
   vitesseG = vitesseLineaire + vitesseAngulaire;
   //setMotorSpeeds( vitesseD, vitesseG);
   change = 0;
  }
  else {
    if (millis() - reftime > TIMEOUT ) {
      // Action à effectuer en boucle si on ne reçoit plus de données ...
      //setMotorSpeeds( vitesseD, vitesseG);
    }
  }
}


void timerIsr()
{

// Variables static pour que les valeurs soient gardées en mémoire et pas effacée à la fin de l'éxecution de la fonction)
 static uint32_t start[NBCHANNEL] = {0}; // contient la valeur de l'instant en microseconde du rising de chaque état => 

// Variable importante de fonctionnement
 uint32_t timeus;                  // temps en microseconde
 static uint8_t oldetat;           // ancien etat des pins RC
 uint8_t etatcourant;              // état courant des pins RC 

 bool val;                         // variable intermédiaire pour représenter l'état du signal Haut ou bas
 uint32_t t;                       // variable intermédiaire pour vérifier l'intégrité du résultat 
 
 etatcourant = PINB & 0b00111111;             // on met à jour tous les channels d'un coup en lisant le port complet et on met à 0 les bits lus des channels non utilisés
 if(etatcourant != oldetat) {                 // on regarde si il y a un changement entre le courant et le précédent
  timeus = micros ();                         // on enregistre le temps 
  for (uint8_t i = 0; i < NBCHANNEL; i++) {   // on boucle sur les channels 
   val = etatcourant&(1<<i);                  // on regarde l'état de chaque channel 
   if ( val != oldetat&(1<<i) ) {             // on regarde si il y a un changement de l'état du channel par rapport à son état précédent
    if (val) start[i] = timeus;               // si on est en signal Haut, on vient de faire un rising on enregistre l'instant dans start 
    else {                                    // sinon on est en signal bas et on a donc fait un falling
     t = timeus - start[i];                 // on calcul le temps du signal
     if( t < TMAX &  t > TMIN) {              // on vérifie l'intégrité du résultat
      consigne[i] = constrain((t - NEUTRE)/2, -128, 127);
      change = 1;
     }
    }
   }
  }

  oldetat = etatcourant ;                         // avant de quitter on enregistre le courant dans old
 }
}

Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#14 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 23 avril 2018 - 03:06

Ah oui j'ai pourtant bien reverifié 2 fois avant de parler de oldetat ;)

:dash2:  :laugh1:

 

Par contre il me semble que maintenant consigne se doit d'être "volatile".

 

J'aime plus ce code la, déjà ya un tableau de moins, ça allège le code :)



#15 Mike118

Mike118

    Staff Robot Maker

  • Administrateur
  • PipPipPipPipPip
  • 9 934 messages
  • Gender:Male
  • Location:Anglet
  • Interests:Robotique, Entrepreneuriat, Innovation, Programmation, Résolution de problème, Recherche de solutions, Mécanique, Electronique, Créer, Concevoir

Posté 23 avril 2018 - 03:17

 

 

Par contre il me semble que maintenant consigne se doit d'être "volatile".

 

 

Exactement ! :P ( j'ai edit pour corriger la coquille du coup ;)

Il doit en rester encore deux trois, et puis je dois tester, mais comme dit plus haut le but c'est de partager la réflexion ;) 


Si mon commentaire vous a plus laissez nous un avis  !  :thank_you:

Nouveau sur Robot Maker ? 

Jetez un oeil aux blogs, aux tutoriels, aux ouvrages, au robotscope  aux articles,  à la boutique  et aux différents services disponible !
En attendant qu'une bibliothèque de fichiers 3D soit mise en place n'hésitez pas à demander si vous avez besoin du fichier 3D d'un des produits de la boutique... On l'a peut être ! 
Si vous souhaitez un robot pilotable par internet n'hésitez pas à visiter www.vigibot.com et à lire le sous forum dédié à vigibot!

 

Les réalisations de Mike118  

 

 

 


#16 cocothebo

cocothebo

    Membre passionné

  • Membres
  • PipPipPip
  • 341 messages
  • Gender:Male

Posté 23 avril 2018 - 08:04

Oui oui je sais bien, et mon but n'était pas non plus de juste critiquer, surtout que la tu expliques la démarche c'est super.

 

C'est plus pour éviter les erreurs, et vu que je pense beaucoup (souvent juste de passage sur le forum) risquent de prendre le code sans réfléchir, il vaut mieux qu'il soit le plus fonctionnel possible.

 

 

Après peut être qu'on pourrait voir comment optimiser au maximum la routine d’interruption, jusqu'à avoir un truc horrible à lire ;) mais sérieusement en gardant en point de mire que plus une interruption est courte mieux c'est.






0 utilisateur(s) li(sen)t ce sujet

0 members, 0 guests, 0 anonymous users