Cours:MiniQ v2

De troyesGEII
Aller à : navigation, rechercher

Corrections uniquement accessibles aux enseignants

Le robot MiniQ est vendu maintenant en version 2. C'est cette nouvelle version qui va nous intéresser dans ce chapitre.

Programmation avec arduino

Le robot miniQ est architecturé autour d'un processeur ATMega32U4. C'est le processeur qui équipe les cartes Leonardo et il est d'ailleurs complètement compatible. Il faut donc changer de board qui est par défaut "Arduino UNO" qui est essentiellement utilisé pour les TPs.

Le processeur 32U4 est capable de gérer une liaison série USB sans l'aide d'un circuit spécialisé (FTDI par exemple). L'inconvénient de ce processeur est que sa partie matérielle consacrée aux périphériques n'est pas complètement compatible avec celle du UNO (ATMega328). La bonne nouvelle c'est que l'environnement Arduino est capable de gommer ces différences.

Le schéma peut être trouvé sur cette page commerciale avec des exemples de code et des exercices en anglais.

En résumé :

  • La carte Arduino équivalente est la : Arduino Leonardo
  • Le programmateur d'origine est : AVR ISP
  • Le port série est en général /dev/ACMO sous Linux

Utilisation de analogWrite() pour la MLI

Commande d'un moteur à l'aide d'un timer

On rappelle que pour faire varier la vitesse d'un moteur il suffit de faire varier sa tension de commande. En général ceci est réalisé avec un signal MLI (signal à rapport cyclique variable). Le rapport cyclique varie entre 0 et 1, mais les registres de commandes sont sur 8 bits et donc permettent une correspondance :

  • rapport cyclique = 0 alors commande = 0
  • rapport cyclique = 0,5 alors commande = 128
  • rapport cyclique = 1 alors commande = 255

Tout ceci se fait dans le monde Arduino avec une commande "analogWrite()"

La variable précédente permettra de commander le moteur gauche. Vous devez être capable à ce stade de le faire tourner plus ou moins vite avec l'incrémentation/décrémentation de la section précédente dans un sens fixé par vous même.

Indications :

  • Il y a deux sorties de commandes du moteur gauche (il faudra donc les positionner en sortie) :
    • broche 5 Arduino (PC6) pour la commande rapport cyclique variable
    • broche 12 Arduino (PD6) pour le sens de rotation
  • On vous donne le code source ci-dessous :
    • Le premier paramètre est ce qui ira dans la broche 12 : 0 pour marche en avant et 1 pour marche arrière
    • Le deuxième le rapport cyclique qui ira sur la broche 5
void MotorG(unsigned char M1_DIR,unsigned char M1_EN)//control the motor
{
  //////////M1-->left motor////////////////////////
  if(M1_DIR==0)//en avant
    digitalWrite(12,0);
  else
    digitalWrite(12,1);
  if(M1_EN==0)
    analogWrite(5,LOW);
 else
    analogWrite(5,M1_EN);
}

Indications : On complète la commande du moteur gauche par celle du moteur droit.

  • Il y a deux sorties de commandes du moteur droit (il faudra donc les positionner en sortie) :
    • broche 6 Arduino (PD7) pour la commande rapport cyclique variable
    • broche 7 Arduino (PE6) pour le sens de rotation
  • On vous donne le code source ci-dessous :
    • Le premier paramètre est ce qui ira dans la broche 7 : 0 pour marche en avant et 1 pour marche arrière
    • Le deuxième le rapport cyclique qui ira sur la broche 6
  • La commande Mi_EN (pour i=1 et i=2) qui choisit le rapport cyclique ne doit pas dépasser 75 !
void MotorD(unsigned char M2_DIR,unsigned char M2_EN)//control the motor
{
  //////////M2-->right motor////////////////////////
  if(M2_DIR==0)//en avant
    digitalWrite(7,0);
  else
    digitalWrite(7,1);
  if(M2_EN==0)
    analogWrite(6,LOW);
 else
    analogWrite(6,M2_EN);
}

Travail à faire : Exercice 1 (arduino)

Vous devez réaliser une commande du robot pour qu'il réalise un huit (deux cercles connectés par un point). Vous choisirez le rayon de vos cercles en jouant sur les rapports cycliques. Puis vous jouerez sur les temporisations pour gérer tout cela. Bien entendu, le fait qu'il n'y ait pas d'information de retour sur l'endroit où vous êtes complique un peu la mise au point.

Vous pouvez chercher à innover sur les trajectoires. Des étudiants, par exemple, ont mis deux obstacles et cherché à réaliser un créneau entre ces deux obstacles. Faites-vous plaisir. Il vous faut à tout prix comprendre comment on va en ligne droite et comment on tourne.

Suivre une source de lumière par Conversion Analogique Numérique

Le travail demandé dans cette section utilise encore la conversion analogique numérique. La différence est que maintenant c'est la position d'une source de lumière qui va faire changer les valeurs analogiques.

Étalonnage des capteurs

Détecteurs de lumière sur MiniQ version 2

Vous disposez de deux capteurs de lumière monté comme dans le schéma ci-contre. Utilisez une liaison série pour trouver les zones pour :

  • lumière au centre
  • lumière légèrement à droite
  • lumière très à droite
  • lumière légèrement à gauche
  • lumière très à gauche

Travail à faire exercice 2 : Réalisation d'un suivi de lumière

On vous demande de réaliser un programme capable de suivre parfaitement rotation de la source de lumière qui a servi au calibrage. Pour cela le robot devra tourner sur place.

Utilisation de l'infra-rouge pour détecter des obstacles

Il y a plusieurs détecteurs infra-rouges dans le robot mini-Q. Nous allons commencer par étudier ceux qui sont sur l'avant du robot et qui sont donc destinés à détecter des obstacles.

Utiliser les détecteurs infra-rouge frontaux

Le robot dispose de cinq détecteurs infra-rouge pour le suivi de lignes. Ce n'est pas ces détecteurs que nous allons étudier mais un détecteur et deux émetteurs dirigés respectivement vers la droite et vers la gauche. Cela devrait nous permettre de détecter des obstacles. Le détecteur est relié à INT0/PB0 et peut donc être géré par une interruption.

L'idée générale est d'envoyer un train d'impulsions en émission et de compter ce que l'on reçoit.

Code d'émission

Vous pouvez utiliser le code suivant :

// train d'impulsion Gauche
void L_Send40KHZ(void)//left ir transmitter sends 40kHZ pulse
{
  int i;
  for(i=0;i<24;i++)
  {
    digitalWrite(L_IR,LOW);
    delayMicroseconds(8);
    digitalWrite(L_IR,HIGH);
    delayMicroseconds(8);
  }
}
//train d'impulsion Droit
void R_Send40KHZ(void)//right ir transmitter sends 40kHZ pulse
{
  int i;
  for(i=0;i<24;i++)
  {
    digitalWrite(R_IR,LOW);
    delayMicroseconds(8);
    digitalWrite(R_IR,HIGH);
    delayMicroseconds(8);
  }
}

Si vous regardez attentivement le schéma de principe vous déduisez facilement que l'émission se fait à l'aide d'un zéro. Ce code nécessite les définitions suivantes

#define IR_IN  17//infrared receiver
#define L_IR 13//infrared transmitter in the left side
#define R_IR 8//infrared transmitter in the right side

Réception par interruption

L'interruption est d'abord initialisée à l'aide du code :

void pcint0_init(void)
{
	PCICR = 0X01;
	PCMSK0 = 0X01;//Autorisation de l'interruption INT0
}

Le code de l'interruption est tout simple :

volatile unsigned int count=0;
ISR(PCINT0_vect)
{
	count++;//on incrémente le compteur pour chaque impulsion reçue
}

setup() associé

Les lignes de code associé à la détection infra-rouge à mettre dans la partie setup() du programme sont :

pinMode(L_IR,OUTPUT);//init the left transmitter pin
pinMode(R_IR,OUTPUT);//init the right transmitter pin
pinMode(IR_IN,INPUT);//init the ir receiver pin
digitalWrite(R_IR,HIGH);
digitalWrite(L_IR,HIGH);
pcint0_init();
sei();

Un code pour éviter les obstacles

Tout ce qui précède peut être utilisé pour détecter et donc éviter les obstacles. L'idée générale est très simple :

  • on initialise le compteur "count" à 0
  • On émet à droite 24 impulsions plusieurs fois, on compte ensuite ce que l'on reçoit (cela est réalisé automatiquement à l'aide d'une interruption).
  • si c'est supérieur à 20, on recule suffisamment on tourne du bon côté et on repart en marche avant.
  • on initialise le compteur à 0
  • On émet à gauche 24 impulsions plusieurs fois, on compte ce que l'on reçoit.
  • si c'est supérieur à 20, on recule suffisamment on tourne du bon côté et on repart en marche avant.

La mise au point de ce code peut prendre beaucoup de temps.

Indications : le code pour émettre plusieurs fois peut être par exemple :

count = 0;
for(i=0;i<20;i++) { //left transmitter sends 20 pulses
    L_Send40KHZ();
    delayMicroseconds(600);    
}
if(count>DISTANCE_IR)//if recieved a lot pulse , it means there's a obstacle

Ce code émet 20 fois 24 impulsions.

Le DISTANCE_IR du "if(count>DISTANCE_IR)" peut être défini par une constante. Divers essais chez moi on montré que la valeur 20 est un bon point de départ. Si vous augmentez cette valeur vous détecterez les obstacles plus près et si vous la diminuez ce sera le contraire. Il est défini comme une constante DISTANCE_IR dans le code de la section suivante et nous vous encourageons très fortement d'utiliser cette technique pour la mise au point.

Travail à faire : exercice 3

Vous devez mettre au point un code qui permette de ne jamais percuter des obstacles, c'est à dire qui évite les obstacles qu'il détecte à droite comme à gauche.

Indications : vous n'avez plus à gérer de temps maintenant. Autrement dit la technique utilisant "millis()" n'est plus nécessaire. "delay()" peut suffire car votre programme n'est plus sensé s'arrêter. Le code complet du setup() vous est donné ainsi que le code de loop() pour vous montrer l'architecture du programme recherché :

#define IR_IN  17//infrared receiver
#define L_IR 13//infrared transmitter in the left side
#define R_IR 8//infrared transmitter in the right side

#define ENAVANT 0      // value for forward
#define ENARRIERE 1         //value for go back
 
#define VITESSE 75
#define DISTANCE_IR 20

volatile unsigned int count=0;

void setup()
{
// moteur gauche
  pinMode(5,OUTPUT);
  pinMode(12,OUTPUT);
//moteur droite
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
// infra-rouge
  pinMode(L_IR,OUTPUT);//init the left transmitter pin
  pinMode(R_IR,OUTPUT);//init the right transmitter pin
  pinMode(IR_IN,INPUT);//init the ir receiver pin
// gestion des interruptions
  pcint0_init();
// Et en route pour l'aventure...
  MotorG(FORW,SPEED);//run left motor
  MotorD(FORW,SPEED);//run right motor
}
void loop() {
    Eviter_Obstacle();//obstacle avoidance
}

Ce code doit être naturellement complété par les morceaux de code donnés précédemment ainsi que par l'écriture du sous-programme "Eviter_Obstacle()" qui est le cœur même de ce que l'on cherche à faire. Vous pouvez noter qu'une constante SPEED est définie à 60 ce qui évite de faire de la casse. Une constante DISTANCE_IR est définie à 35 et devra être utilisée dans le test après réception.

La mise au point de la détection infra-rouge peut se faire sans mouvement. On utilisera le robot couché sur le côté et un seul moteur celui qui n'est pas sur la table, par exemple, pour tester quand se fait la détection. On le fait avancer sauf quand il y a détection.

Les tests se feront dans le labyrinthe NAO (salle G006).

Utilisation de l'infra-rouge pour suivre une ligne

Le robot mini-Q est équipé de cinq couples leds/photo-transistors (infra-rouge) dirigés vers le bas. Ils sont destinés à mesurer la réflexion du support sur lequel roule le robot. Si vous prenez un support blanc et que vous fixez un scotch noir vous arriverez à détecter le support noir qui n'a pas la même réflectance infra-rouge que le support blanc.

La mise au point sera difficile car sur le robot que nous avons essayé les sensibilités des capteurs ne sont pas identiques. La calibrage de chacun des capteurs peut se faire par le programme simple :

int data[5]={0X00,0X00,0X00,0X00,0x00};//save the analog value

void setup(){
  Serial.begin(9600);
}
void Read_IRLine(void){ //read the analog value 
// les capteurs A0, A1 sont peu sensibles sur le robot essayé !!!
    data[0]=analogRead(A0); // gauche
    data[1]=analogRead(A1); 
    data[2]=analogRead(A2); 
    data[3]=analogRead(A3);
    data[4]=analogRead(A4);  // droite
}

void loop(){
  Read_IRLine();
  for (char i=0;i<5;i++){
    Serial.print(data[i]);
    Serial.print(" ");
  }
  Serial.println(" ");  
  delay(1000);
}

qui affichera toutes les secondes les valeurs des 5 photo-transistors infra-rouges.

Travail à réaliser : Exercice 4

Le travail à réaliser se décompose en plusieurs étapes :

Question.jpg On vous demande de calibrer vos cinq capteurs avec une plaque blanche de test comprenant une piste en chatterton noir.

Ce travail consiste à utiliser la liaison série pour calibrer vos 5 capteurs. Calibrer veut dire ici, trouver les seuils en-dessous desquels on considère que l'on a du noir. Ces seuils ne sont pas tous identiques pour tous les robots et pour un même robot pour tous les capteurs. Pour les trouver, vous positionnez une fois le capteur sur du blanc, une fois sur du noir et le seuil sera à mi-chemin.

Un moyen de faire cela de manière assez facile à ajuster consiste à définir des constantes comme ci-dessous :

#define SEUILGG 985
#define SEUILG 970
#define SEUIL 750
#define SEUILD 700
#define SEUILDD 750

GG veut dire le plus à gauche, DD le plus à droite.

Question.jpg Une fois les seuils de chaque capteur trouvé, convertir l'ensemble des données en un nombre binaire sur 5 bits. Le bit de poids faible sera à 1 si une ligne noire est présente sous le capteur A0 et ainsi de suite jusqu'au bit b4. Tester de nouveau avec un affichage binaire. Commencez ensuite à lister les valeurs aberrantes et les valeurs possibles de ce nombre binaire.

Indication : Serial.print(78, BIN) donne "1001110"

Le prototype de "Read_IRLine sera changé comme :

 
char Read_IRLine(void)

pour en faire une fonction qui retourne un nombre binaire.

Indication : La conversion binaire vous est partiellement donnée maintenant, mais il vous faut adapter les seuils de vos capteurs... et tester...

//****** seuils très dépendants des ROBOTs
#define SEUILGG 941
#define SEUILG 500
#define SEUIL 400
#define SEUILD 880
#define SEUILDD 600
char Read_IRLine(void){ //read the analog value
    char result=0;
    data[0]=analogRead(A0); // gauche manque sensibilité sur mon robot
    data[1]=analogRead(A1); // manque sensibilité sur mon robot
    data[2]=analogRead(A2);
    data[3]=analogRead(A3);
    data[4]=analogRead(A4); // droite
    if (data[0] < SEUILDD) result |= 0x01; else result &= 0xFE;
    if (data[1] < SEUILD) result |=        else result &=     ;
    if (data[2] < SEUIL) result |=         else result &=     ;
    if (data[3] < SEUILG) result |=        else result &=     ;
    if (data[4] < SEUILGG) result |=       else result &=     ;
    return result;
}

Question.jpg Vous commencerez ensuite à essayer de réaliser un programme qui suit une ligne fermée en avançant. Le principe est d'accélérer le bon moteur quand on dévie de la ligne.

Indication : vous pourrez utiliser un "switch" en C qui n'utilise que les valeurs autorisées de Read_IRLine. La valeur 0x00 sera traitée comme valeur aberrante dans un premier temps.