Binome2017-9
Binôme 9 saison 8: Raphaël Bonvalet, Damien Tillaux.
Sommaire
Projet Quireekoo
Le but du projet Quireekoo est de créer un robot communiquant avec d'autre via infra-rouge. Ce projet à eu lieux lors des bureaux d'étude de PEIP en IMA au S4 2018 de notre cursus pour devenir ingénieur. Voici la transcription phonétique de Quireekoo: /Kirikou/.
Cahier des charges
Quireekoo sera un robot fait de A à Z. Le circuit imprimé sera totalement revu et sera guidé grâce à une ATMEGA-328. Le physique sera totalement design par nous même et son programme sera totalement autonome.
Quireekoo étant petit et vaillant est un véhicule de petite taille. A la défaut de jambe, Quireekoo ce déplace avec 2 servos moteurs continue. A la place des yeux, un sonar pour éviter ses obstacles et ne pas trébucher. Pour ses oreilles nous avons prévu 2 Tsop38238 pour pouvoir repérer et fuir ses ennemis.
Réalisation du projet
Voici les différentes étapes par lesquels nous sommes passer lors de la création de Quireekoo.
Réalisation de PCB
Idée principale
Nous voulions refaire nous même le PCB de Quireekoo mais nous n'avions pratiquement aucune base sur comment créer un circuit imprimé pour ce genre de projet. Du coups nous avons pris comme modèle, le modèle donnée sur la page principale du projet. (BE 2017-2018)
Voici le schématique sur lequel nous nous sommes basé.
Notre PCB
Notre PCB a été réalisé sur Fritzing. Nous avons créer ce circuit imprimé à notre image pour notre véhicule. Nous avons enlevé le driver de moteur et les sorties moteurs car nous voulions un robot de petite taille. Donc le moyen de déplacement choisi a alors été le servo-moteur. Pour les réceptions infra-rouge nous n'avons prévu que deux sorties pour des Tsop pour pouvoir laisser un plus grande chance au chasseur de ne pas être repéré, pour une approche plus vrai que nature. Pour savoir si un chasseur nous a attrapé, nous utilisons une sonde de Hall.
De plus, en cas de problème nous avons créé toutes les sorties du microcontrôleur. Si un problème apparaît sur une des pins utilisées, nous pourrons la relocaliser sur une autre sortie.
Un objectif supplémentaire était de réaliser un PCB le plus petit possible pour que le véhicule le soit aussi. Ce fut presque un succès, la carte est d'une taille environ de 5cm*5cm. Elle aurait pu être encore plus petite avec plus de travail mais nous ne pouvions pas nous le permettre si nous voulions tout faire.
Voici le schématique et le PCB de Quireekoo.
Réalisation de la partie mécanique du véhicule
Pour la partie mécanique de Quireekoo on a choisi de le faire sur inkscape afin de pouvoir effectuer une découpe laser. Le robot est composé de deux plaques de plexiglas, une pour le châssis où il y a les servos moteur, la bille et le bloc de pile, et une pour placer le sonar, le circuit imprimée, les Tsop et la sonde de hall.
Voici à quoi ressemble la partie du bas sur inkscape :
Pour la partie du haut c'était un peu plus compliqué car il fallait trouver comment placer les tsop, la sonde de hall et le sonar. Pour pouvoir fixer ceux-ci on a choisi de faire des trous en forme de rectangles avec une taille égale à la taille des pins de chaque capteurs différents. Après il fallait aussi mettre les trous pour fixer le circuit imprimé au même endroit que ceux qui se trouve sur celui-ci. Pour ce faire, on a utilisé le fichier .svg donné par Fritzing. On a également mis des trous en forme de rectangle sur les côtés pour pouvoir laisser passer les fils.
Voici à quoi ressemble la partie du haut sur inkscape :
Pour finir il nous restait les roues à faire. On a opté pour garder les roues données avec les servos moteurs en y rajoutant des partie en plexiglas au pistolet à colle pour rendre celles-ci plus grandes.
Voici à quoi cela ressemble sur inkscape :
Le problème avec cette solution c'est que les roues ne sont pas adhérentes. De ce fait là, on a ajouté une partie d'élastique autour avec de la colle glue.
Voici a quoi le robot ressemble à la fin :
Réalisation du code Arduino
Voici maintenant la présentation du code téléversé dans Quireekoo.
Le code
{
#include <Servo.h> #include <IRremote.h> //déclaration des différentes pins #define SERVO1 11 #define SERVO2 13 #define ECHO 17 #define TRIGG 18 #define HALL A2 #define IR 2 #define VCCIR1 A0 #define VCCIR2 A1 //création de la classe Mouv pour le déplacement du robot class Mouv { Servo servo1; Servo servo2; int servo1Pin; int servo2Pin; int etatRotation; int etatSonar; int etatHall; int etatTsop; long intervalTsop; long intervalMouvement1; long intervalMouvement2; long intervalMouvement3; long intervalMouvement4; unsigned long lastMillis; public: Mouv(int pin1 , int pin2){ servo1Pin= pin1; servo2Pin = pin2; etatRotation = 0; etatSonar = 0; etatHall = 0; etatTsop = 0; lastMillis = 0; intervalTsop = 500; intervalMouvement1 = 1500; intervalMouvement2 = 1700; intervalMouvement3 = 3200; intervalMouvement4 = 3400; } void attacher(){ servo1.attach(servo1Pin); servo2.attach(servo2Pin); } void deplacement(int distanceObstacle , int tsop , int hall, int* attraper){ unsigned long currentMillis = millis(); if (hall == 1){ etatHall = 1; *attraper = 1; } else if (distanceObstacle < 200){ etatSonar = 1; } else if (tsop==1){ etatTsop = 1; lastMillis = currentMillis; } if (etatHall == 1){ servo1.write(90); servo2.write(90); } else if (etatSonar ==1){ if (etatRotation == 0){ etatRotation = random(1,3); } if(etatRotation == 1){ servo1.write(180); servo2.write(180); } else{ servo1.write(0); servo2.write(0); } etatSonar = 0 ; lastMillis = currentMillis; } else if (etatTsop == 1){ if (etatRotation == 0){ etatRotation = random(1,3); } if(etatRotation == 1){ servo1.write(180); servo2.write(180); } else{ servo1.write(0); servo2.write(0); } if (currentMillis - lastMillis >= intervalTsop){ etatTsop = 0; lastMillis = currentMillis; } } else{ etatRotation = 0; if (currentMillis - lastMillis < intervalMouvement1){ servo1.write(180); servo2.write(70); } else if ((currentMillis - lastMillis >= intervalMouvement2) && (currentMillis - lastMillis <= intervalMouvement3)){ servo1.write(98); servo2.write(0); } else{ servo1.write(180); servo2.write(0); if (currentMillis - lastMillis >= intervalMouvement4){ lastMillis = currentMillis; } } } } }; Mouv servo(SERVO1,SERVO2); //varibable pour le sonnar long lectureEcho; long distance; const float SOUND_SPEED = 340.0 / 1000; //varibable pour la sonde de hall int* attraper; //varibable pour les tsop unsigned long last_millis = 0; long interval = 500; int broche_reception1 = VCCIR1; IRrecv reception_ir1(broche_reception1); decode_results decode_ir1; int broche_reception2 = VCCIR2; IRrecv reception_ir2(broche_reception2); decode_results decode_ir2; //instance pour l'emmission infra-rouge IRsend emission_ir; void setup() { pinMode(TRIGG,OUTPUT); digitalWrite(TRIGG,0); pinMode(ECHO,INPUT); pinMode(IR,OUTPUT); digitalWrite(IR,1); servo.attacher(); reception_ir1.enableIRIn(); // démarre la réception reception_ir2.enableIRIn(); attraper = malloc(sizeof(int)); *attraper = 0; } void loop() { servo.deplacement(sonar(),tsop(attraper),hall(),attraper); } int sonar() { digitalWrite(TRIGG, 1); delayMicroseconds(10); digitalWrite(TRIGG,0); long measure = pulseIn(ECHO, HIGH); float distance_mm = measure / 2.0 * SOUND_SPEED; return distance_mm; } int tsop(int * attraper ){ unsigned long current_millis = millis(); int etatTsop = 0; if ((current_millis - last_millis >= interval)&&(*attraper == 0)){ emission_ir.sendNEC(1, 32); last_millis = current_millis; reception_ir1.enableIRIn(); reception_ir2.enableIRIn(); } if (reception_ir1.decode(&decode_ir1)) { if(decode_ir1.value == 1){ etatTsop = 1; } reception_ir1.resume(); reception_ir2.enableIRIn(); } if (reception_ir2.decode(&decode_ir2)) { if(decode_ir2.value == 1){ etatTsop = 1; } reception_ir2.resume(); reception_ir1.enableIRIn(); } return etatTsop; } int hall(){ int etatHall = 0; if(digitalRead(HALL) == 0){ etatHall = 1; } return etatHall; }
}
Description de la structure du code
Fonction sonar()
Cette fonction permet au sonar de calculer la distance séparant le robot d'un obstacle.
{
int sonar() { digitalWrite(TRIGG, 1); delayMicroseconds(10); digitalWrite(TRIGG,0); long measure = pulseIn(ECHO, HIGH); float distance_mm = measure / 2.0 * SOUND_SPEED; return distance_mm; }
}
Fonction tsop()
Cette fonction a deux rôles. Le premier est de lire les entrées des tsop et de renvoyer si un prédateur a été détecter ou non. Le deuxième est d'émettre un signal infra-rouge permettant au prédateur de nous détecter.
{
int tsop(int * attraper ){ unsigned long current_millis = millis(); int etatTsop = 0; if ((current_millis - last_millis >= interval)&&(*attraper == 0)){ emission_ir.sendNEC(1, 32); last_millis = current_millis; reception_ir1.enableIRIn(); reception_ir2.enableIRIn(); } if (reception_ir1.decode(&decode_ir1)) { if(decode_ir1.value == 1){ etatTsop = 1; } reception_ir1.resume(); reception_ir2.enableIRIn(); } if (reception_ir2.decode(&decode_ir2)) { if(decode_ir2.value == 1){ etatTsop = 1; } reception_ir2.resume(); reception_ir1.enableIRIn(); } return etatTsop; }
}
Les particularités de cette fonction sont que, premièrement pour facilité la programmation l'émission et la réception ont été mises dans la même fonction car elles demandent toutes les deux la même bibliothèque et les même timer, donc pour éviter tout conflit ceci fut une bonne solution. Deuxièmement, la fonction tsop() contient un pointeur en paramètre. Ce pointeur permet au robot de savoir s'il doit continuer à émettre ou non s'il est attrapé.
L'émission du signal infra-rouge ici ce fait toute les 0.5s. En effet si les émissions sont trop proche il y a conflit avec la réception.
Fonction hall()
Cette fonction permet de voir si un prédateur nous a détecté et attrapé grâce à l'émission d'une onde électromagnétique.
{
int hall(){ int etatHall = 0; if(digitalRead(HALL) == 0){ etatHall = 1; } return etatHall; }
}
Fonction deplacement()
Pour le déplacement du robot nous avons choisi de créer un classe pour avoir un code plus propre et une fonction loop() ne contenant qu'une seul ligne de code.
La Classe Mouv contient 2 fonctions. La première attacher() permet de lier les instances servo aux pins associées. Elle est appelée dans le setup(). La deuxième fonction est la fonction de déplacement appelée deplacement(). Cette fonction est la seule appelée de la fonction loop().
Cette fonction est alors la fonction principale du robot, car c'est elle qui va décider du déplacement du robot en fonction des différentes entrées des capteurs. Elle contient en paramètre les différentes entrées du sonar, du tsop, de la sonde de hall, mais aussi un pointeur pour pouvoir sauvegarder en mémoire le fait que le robot soit attrapé ou non.
{
void deplacement(int distanceObstacle , int tsop , int hall, int* attraper){ unsigned long currentMillis = millis(); if (hall == 1){ etatHall = 1; *attraper = 1; } else if (distanceObstacle < 200){ etatSonar = 1; } else if (tsop==1){ etatTsop = 1; lastMillis = currentMillis; } if (etatHall == 1){ servo1.write(90); servo2.write(90); } else if (etatSonar ==1){ if (etatRotation == 0){ etatRotation = random(1,3); } if(etatRotation == 1){ servo1.write(180); servo2.write(180); } else{ servo1.write(0); servo2.write(0); } etatSonnar = 0 ; lastMillis = currentMillis; } else if (etatTsop == 1){ if (etatRotation == 0){ etatRotation = random(1,3); } if(etatRotation == 1){ servo1.write(180); servo2.write(180); } else{ servo1.write(0); servo2.write(0); } if (currentMillis - lastMillis >= intervalTsop){ etatTsop = 0; lastMillis = currentMillis; } } else{ etatRotation = 0; if (currentMillis - lastMillis < intervalMouvement1){ servo1.write(180); servo2.write(70); } else if ((currentMillis - lastMillis >= intervalMouvement2) && (currentMillis - lastMillis <= intervalMouvement3)){ servo1.write(98); servo2.write(0); } else{ servo1.write(180); servo2.write(0); if (currentMillis - lastMillis >= intervalMouvement4){ lastMillis = currentMillis; } } } }
}
Cette fonction est divisée en deux partie. La première est la distribution des états du robot en fonction des entrées. Par la suite, la fonction va décidé quel déplacement faire en fonction des priorités des différents états. L'états le plus prioritaire est l'états etatHall. Celui ci indique si le robot est attrapé et une fois passé à 1 il ne peut plus changer et le déplacement associé est un sur-place pour le robot. Après il y a l'état associé au obstacle et si la distance devient inférieur à celle écrite, le robot tourne sur lui même dans une direction aléatoire jusqu'à ne plus percevoir d'obstacle. Le suivant est celui de la détection de prédateur. Ici si un prédateur est détectée, Quireekoo va aussi ce tourner dans une direction aléatoire. Mais la différence c'est que ici il va tourner pendant un certain intervalle de temps rentré dans le code. Le dernier déplacement n'est exécuter seulement si aucun des trois autres n'est fait. C'est le déplacement de base du robot. Ce déplacement est juste en zigzag.