Cet encodeur est très simple, il permet de déterminer ou programmer la distance parcourue ou mesurer la vitesse de rotation d'une roue. Il n'est cependant pas à quadrature, il ne permet donc pas de déterminer le sens de rotation. On pourrait assez facilement faire l'adaptation nécessaire.

Le principe consiste à coller sur la roue à encoder un réseau de bandes noires et blanches alternatives en rayons. Avec un capteur infrarouge, on compte les bandes qui passent. Il suffit ensuite d'écrire le code autour pour faire ce qu'on veut.

Ingrédients

Pour cet encodeur, il vous faudra en plus d'un microcontrôleur de votre choix (Arduino dans mon cas) :

  • 1 émetteur-récepteur infrarouge du genre QRD1114 (voir cette page pour les instructions de connexion à l'Arduino)
  • 1 transistor ou MOSFET qui bascule dans les 2v (par exemple un BC547)
  • 1 résistance de 10 kOhm
  • 1 imprimante.

Le coût de revient est donc inférieur à 3 euros :)

Phase 1 : le capteur

La roue codeuse

roue_codeuse_600.JPGTout d'abord, il faut créer le cercle encodeur.

Pour cela, j'ai utilisé cette feuille excel trouvée sur cette page. Je l'ai ouverte avec LibreOffice, réalisé un cercle encodeur à 40 rayons (à recolorer en noir et blanc à la main, c'est un peu fastidieux), exporté en PDF, ouvert avec The Gimp et imprimé.

Il n'y a plus ensuite qu'à coller le cercle sur la roue.

La lecture

capteur_encodeur_600.JPGPour lire la roue, il faut placer le capteur tout près mais pas trop : tout près car le capteur étant très peu directionnel il faut qu'il puisse bien différencier les bandes noires et blanches et pas trop parce que s'il touche le cercle il devient aveugle et rate des bandes. J'ai placé les miens à 2mm environ.

En l'état, ça marche déjà! Il suffit de brancher le capteur sur une entrée analogique de l'Arduino, calibrer pour voir les valeurs correspondant aux bandes noires et blanches et compter les changements avec un poil d'hystérésis pour éliminer les valeurs intermédiaires. Ca donne ceci :

#include <Servo.h> // bibliotheque servo

Servo myservo1;  // création de l'objet servo droit
Servo myservo2;  // création de l'objet servo gauche

// encodeur

int encodeurGauche = 4; //encodeur gauche
int encodeurDroit = 5; //encodeur droit

int seuil = 200; // seuil de détection de la bande noire
int detect[6]; // compteur de bandes, on met les valeurs dans un tableau avec le numéro de pin comme index
int bande[6]; // tableau de la couleur des bandes en fonction des capteurs (idem)
int val = 0; // valeur lue sur le capteur
int mesureGauche = 0; // comptage des bandes
int mesureDroit = 0;

// vitesse du servo

int avance   = 110;
int stoppe = 90; // arret

void setup(){
 
  // servo
   
  myservo1.attach(9);  // servo droite sur le pin 9
  myservo2.attach(10);  // servo gauche sur le pin 10
  myservo1.write(stoppe);
  myservo2.write(stoppe);

 // on initialise les encodeurs

  mesureGauche=encode(encodeurGauche);
  mesureDroit=encode(encodeurDroit);
  detect[encodeurDroit] = 0;
  detect[encodeurGauche] = 0;
}

void loop(){
   while(mesureGauche < 60){ // exemple : on avance de 60 graduations
     tourne(avance,avance);
     mesureGauche=encode(encodeurGauche);
     mesureDroit=encode(encodeurDroit);    
   }
   tourne(stoppe,stoppe); // et on s'arrete

}

// fonction d'encodage

int encode(int encodeur){ // on sélectionne l'encodeur en cours
  val = analogRead(encodeur);
  while(val > (seuil+30) && bande[encodeur] == 1){ // bande noire + hysteresis et venant de bande blanche
    val = analogRead(encodeur);
    detect[encodeur] = detect[encodeur]+1; // on incrémente le compteur
    bande[encodeur] = 0; // on note le changement d'état
   }
   while(val < (seuil-30) && bande[encodeur] == 0){ // bande blanche - hysteresis et venant de bande noire
    val = analogRead(encodeur);
    detect[encodeur] = detect[encodeur]+1; // on incrémente le compteur
    bande[encodeur] = 1; // on note le changement d'état
   }  
  return(detect[encodeur]);
}

// fonction de gestion de la vitesse des servos

void tourne(int vitesseGauche, int vitesseDroite) { // on transmet la vitesse de rotation aux servos
   vitesseDroite=map(vitesseDroite,180,0,0,180); // on inverse le sens de rotation du servo droit
   myservo1.write(vitesseDroite);
   myservo2.write(vitesseGauche);
}

Cette approche a l'avantage de l'extrême simplicité matérielle mais l'inconvénient d'avoir à lire les capteurs en permanence dans la boucle. Si la boucle est très longue à exécuter ou si la roue tourne très vite, on risque de perdre des impulsions. D'où la nécessité de se brancher sur les interruptions matérielles de la carte.

Phase 2 : les interruptions

encodeur_transistors_600.JPGPour utiliser les interruptions de l'Arduino, le signal analogique ne convient pas : il faut un signal numérique, en LOW ou HIGH. On va donc transformer le signal analogique du capteur en signal numérique via un transistor.

Un petit tour par le voltmètre m'a montré que le capteur envoie environ 0,2v sur une bande blanche et 2,2v sur une bande noire. Il y a tout un tas de transistors et MOSFET qui réagissent dans ces valeurs là. J'ai testé avec un transistor NPN BC547 et un Mosfet comme celui-ci, ça marche pareil.

encodeur_bb.pngOn branche la sortie du capteur sur la base du transistor, le collecteur sur le pin correspondant à l'interruption (pin 2 pour l'interruption 0 par exemple) ainsi que sur la sortie 5V de la carte avec une résitance "pull-up" de 10 kOhms en série et l'émetteur sur la masse.

Lorsque le transitsor est inactif (bande blanche), le pin 2 est en état HIGH grâce à la résistance pull-up qui le relie au 5v. Quand le transistor est activé, il relie le pin 2 à la masse, celui-ci passe donc à LOW. Il reste à activer l'interruption en surveillant les changements d'état (option CHANGE) et on peut compter les bandes.

Le code est beaucoup plus simple puisqu'on n'a plus qu'à incrémenter le compteur à chaque interruption, et l'ensemble est bien plus fiable grâce à l'interruption : on n'a plus à se soucier de la longueur du code dans loop(). Voici donc le nouveau code qui fait la même chose que le précédent :

#include <Servo.h> // bibliotheque servo

Servo myservo1;  // création de l'objet servo droit
Servo myservo2;  // création de l'objet servo gauche

// encodeur

int mesureGauche = 0; // comptage des bandes
int mesureDroite = 0;

// vitesses

int avance   = 110; // avance normale
int stoppe = 90; // arret


void setup(){

  // interruptions
 
  attachInterrupt(0, rayonGauche, CHANGE); // encodeur gauche
  attachInterrupt(1, rayonDroite, CHANGE); // encodeur droit

  // servo
   
  myservo1.attach(9);  // servo droite sur le pin 9
  myservo2.attach(10);  // servo gauche sur le pin 10
  myservo1.write(stoppe);
  myservo2.write(stoppe);
 
}

void loop(){
  while(mesureGauche < 60){ // exemple : on avance de 60 graduations
     tourne(avance,avance);
  }
  tourne(stoppe,stoppe);
}

// fonctions en cas d'interruption

 void rayonGauche(){
   mesureGauche = mesureGauche+1;
 }
 void rayonDroite(){
   mesureDroite = mesureDroite+1;
 }

// fonction de gestion de la vitesse des servos

void tourne(int vitesseGauche, int vitesseDroite) { // on transmet la vitesse de rotation aux servos
   vitesseDroite=map(vitesseDroite,180,0,0,180); // on inverse le sens de rotation du servo droit
   myservo1.write(vitesseDroite);
   myservo2.write(vitesseGauche);
}

On peut désormais s'amuser avec notre robot qui peut être programmé pour des trajectoires complexes.