Position et détection d'obstacle
Sommaire
- 1 Présentation
- 2 Navigation
- 3 Transmissions des coordonnées vers la carte principal
- 4 Annexes
Présentation
Notre projet à pour but de concevoir un robot pour participer à La coupe de France des IUT GEII à Cachan
Dans cette partie nous allons nous concentrer sur la position du robot sa navigation et sa faculté a éviter les obstacles décrit dans le règlement.
Codeur rotatif
Afin de connaitre la position des roues, de savoir de combien elles ont tournés nous utiliserons un codeur rotatif. Les codeurs rotatifs sont un type de capteurs permettant de délivrer une information d'angle, en mesurant la rotation effectuée autour d'un axe. L'information de vitesse peut alors être déduite de la variation de la position par rapport au temps. Plus le codeur rotatif tourne lentement, plus la déduction de vitesse perd en précision.
Il existe 2 principaux types :
- Le codeur rotatif incrémental qui ajoute ou soustrait (selon le sens de rotation) une unité à un compteur à chaque rotation supérieure à la résolution du capteur. Le compteur est généralement remis à zéro lorsque l'appareil est allumé. C'est le cas de la souris d'ordinateur à boule.
- Le codeur rotatif absolu qui intègre son propre compteur. Ce genre de capteur est généralement calibré et initialisé une seule fois, et il conserve normalement sa valeur lors de l'arrêt de l'appareil. C'est le cas des compteurs kilométriques des automobiles à la différence du "compteur journalier" qui peut être remis a zéro par l'utilisateur.
Nous utiliseront des codeurs incrémentaux fixé sur les axes des deux moteur ce qui nous permettra d'asservir la rotation des robot c'est à dire que nous pourrons avoir un retour sur les commande du moteur de plus on connaîtra l'angle pris par le robot par rapport a sa position de départ. En effet ce genre des capteur a plus de précision par rapport à l'autre car en réinitialisant il va supprimé les erreurs accumulé .
équation de positionnement
Le positionnement du robot en intégration les information reçu des codeur incrémentaux qui constituera des petits déplacements avec les quelle on pourra nous positionner.
Il y a en fait deux manières d'approximer la trajectoire parcourue par le robot pendant un temps :
- En utilisant des segments de droites. On considère alors que le robot va en ligne droite pendant le temps . Ceci revient à supposer que les deux roues ont une vitesse constante et identique sur cet élément de trajectoire. A la fin de cet élément de trajectoire, on corrige l'orientation du robot en fonction de la différence de distance parcourue par les deux roues
- En utilisant des arcs de cercles(permet de tourner) . On considère alors que le robot se déplace et change d'orientation en suivant un arc de cercle pendant le temps . Ceci revient à considérer que chacune des roues a une vitesse constante sur la période mais que les vitesses des 2 roues ne sont pas identiques.
et on peux cacluler les positionement en calculant la distance parcouru et le temps fonctionnement des 2 roues :
et donc on peut accéder à position (coordonnées x,y)
Réalisation de la roue codeuse
Nous avons pris les mesures du moteur et du châssis imposé pour la compétition afin de fabriquer notre roue codeuse puis nous l'avons représenter sur le logiciel de création de Charly robot. Nous avons choisit des fentes ouvertes sur l’extérieur, c'est a dire pas de contour la taille de la fente est limité par les outils que nous avons une fraise de 2 mm donc la taille de nos fente est de 2 mm une taille minimal permet une précision maximal lors de la rotation. La première roue que l'on a usiner était trop fine et donc pas assez rigide :
nous avons donc choisit de mettre deux épaisseurs l'une plus fine (1 mm) pour la partie extérieur et l'autre plus épaisse pour la partie intérieure de la roue (2 mm). :
Cette solution etant aussi fragile que la precedente nous avons finalment opter pour une roue à une seul epaisseur de 2mm pour avoir une bonne rigidité et peu de vibration.
Réalisation du montage électronique
le montage Electronique a pour but d'avoir les signaux de l'optocoupleur TCUT 1300 pour nous permettre de compter les numéros d’encoche parcouru et savoir le sens de rotation grace aux 2 phototransistor . On a d'abord utilisé le montage d'un groupe qui a travaillé sur le projet avant nous , toutefois leur montage n'a pas fonctionner . On a fini par modifié leur montage pour pouvoir le programme et on a abouti au monter suivant :
- Si il y de la lumiere emit par la photodiode et recu par le photo-transistor on a un état logique "1" => trou
- Si il y a quelque chose assez absorbant de rayonnement IR entre la photodiode et le photo-transistor on a un état logique "0" => pas trou.
et donc on devra avoir une signal de cette allure :
Phase de test
On a eu une problème c'est quand la roue codeuse tourne rapidement on arrive plus à détecter la trou donc l’état 0 :
Pour la résoudre on a utilisé à la place d'un RE résistance fixe une résistance variable qu'on a tester les différence valeur à fin d'avoir un rapport cyclique le proche possible de 50% quand on est au vitesse maximal . En effet cette problème est du à la présence d'un capacité au montage donc si on tourne rapidement le rue codeuse la circuit ne peux plus détecter le 0 à cause la constante de temps R*C trop petit donc il faut augmenté R à fin que le temps de réponse sera plus rapide .En effet si RE est petit la tension n'aura pas de temps de descendre puis remonter donc elle reste à l’état haut .
REMARQUE :
- On place un condensateur en parallèle de chaque phototransistors pour filtrer les éventuelles pics qui pourraient venir perturber le fonctionnement des capteurs.
- On a inserer un condensateur de découplage à fin de filterer les pic provenant d'alimentation
Réalisation de la carte
Suite à la phase de test nous avons commencé la conception de la carte
Le schéma:
Le board :
Tout sera relié à un microcontrôleur en occurrence c'est ATtiny44 (mais on a fait les testes en utilisant un arduino Uno) car c'est la moins chere et presente 5 pin qui nous suffit largement (voir annexe pour la documentation )
Compter le numéro d’encoche et détermination de sens
Il faut faire un programme qui, à partir des signaux obtenu dans la partie montage électronique compte le numéro d’encoches tout en tenant compte de sens de rotation !
Il faut tout d'abord initialisé un sens comme le sens direct qui , à chaque encoche parcouru incrémentera le compteur d'un et diminuer à chaque encoche parcouru dans le sens inverse .
Pour détecter dans quelle sens la rue tourne, on compare le retard juste après la changement (on peut comparé juste après la phase montante ou que après la phase descendante mais pour un meilleur précision on compare pour les 2 phases) d'état de la signal A par rapport à la signal B de l’optocoupleur , en effet il y a 4 cas de figures possibles :
- La signal A =1 et B=1 ==> sens directe ==> on incrémente
- La signal A =1 et B=0 ==> sens indirecte ==> on diminue
- La signal A =0 et B=1 ==> sens indirecte ==> on diminue
- La signal A =0 et B=0 ==> sens directe ==> on incrémente
Maintenant il faut écrire le programme :
- En premier temps on a fais le programme en utilisant les fonctions que la bibliothèque d'arduino nous offre pour tester et bien comprendre.
Voici donc le programme:
Programme comptant les encoches
volatile int s=0;
void setup()
{
Serial.begin(9600);
attachInterrupt(0, counnt, CHANGE);
}
void loop()
{
Serial.println(s);
}
void count()
{
if (digitalRead(2)==HIGH)
{
if (digitalRead(3)==HIGH) s++ ;
else s-- ;
}
else
{
if (digitalRead(3)==HIGH) s-- ;
else s++ ;
}
}
- Puis pour essayer d'amiliorer on a programmer les differant registres directement à fin d'amiliorer la vitesse d'excustion tache faite dans la microcontrôleur , en effet il y a une augementation d'un facteur de 30 en programmant les registre .
Voici donc le programme:
Programmation des registres pour compter les encoches
#include <avr/io.h>
volatile int s=0;
ISR(INT0_vect) // programmed'interruption : le programme principal est interrompu,
{ // l'interruptionexécutée et ensuite le programme principal continu normalement sonexécution
if (PORTD&(1<<PIND2)==HIGH)
{
if (PORTD&(1<<PIND3)==HIGH)
s++ ;
else
s-- ;
}
else
{
if (PORTD&(1<<PIND3)==HIGH)s-- ;
else s++ ;
}
}
void setup()
{
Serial.begin(9600);
cli(); // arrêt desinterruptions
EICRA=0x01; // mode dedéclenchement de l'interruption : change
EIMSK=0x01; // choix desinterruptions actives : interruption 0
sei(); // autorisation desinterruptions
}
void loop()
{
Serial.println(s);
}
programme finale : Obtention des coordonnées x , y , l'angle
Programme comptant les encoches
#include <avr/io.h>
#include <math.h>
volatile int d=0;
volatile int g=0;
double delta_moy =0;
double delta_dif =0;
double delta_angle =0;
double delta_x =0;
double delta_y =0;
double angle =0;
double x =0;
double y =0;
double phi =0;
double delta_phi = 0;
double difference_encouche_droite =0;
double difference_encouche_gauche =0;
double COEF_a_trouver=0;
const double pi = 3.14;
void setup()
{
Serial.begin(9600);
cli(); // arrêt des interruptions
EICRA=0x05; // mode de déclenchement de l'interruption : change
// ISC00 =1 // ISC01 =0 // ISC10 =1 // ISC11 = 0
EIMSK=0x03; // choix des interruptions actives : interruption 0 & 1
sei(); // autorisation desinterruptions
}
ISR(INT0_vect) // programmed'interruption : le programme principal est interrompu,
{ // l'interruptionexécutée et ensuite le programme principal continu normalement sonexécution
if (PORTD&(1<<PIND2)==HIGH)
{
if (PORTD&(1<<PIND3)==HIGH)
g++ ;
else
g-- ;
}
else
{
if (PORTD&(1<<PIND3)==HIGH)g-- ;
else g++ ;
}
}
ISR(INT_vect1) // programmed'interruption : le programme principal est interrompu,
{ // l'interruptionexécutée et ensuite le programme principal continu normalement sonexécution
if (PORTD&(1<<PIND3)==HIGH)
{
if (PORTD&(1<<PIND2)==HIGH)
d++ ;
else
d-- ;
}
else
{
if (PORTD&(1<<PIND2)==HIGH)d-- ;
else d++ ;
}
}
void loop()
{
difference_encouche_droite=d-difference_encouche_droite;
difference_encouche_gauche=g-difference_encouche_gauche;
delta_moy =((g+d)/2)*COEF_a_trouver;
delta_dif =d-g;
delta_phi=int(delta_dif/23.3) %int(2*pi);
delta_x=delta_moy*cos(delta_phi);
delta_y=delta_moy*sin(delta_phi);
x=delta_x+x;
y=delta_y+y;
phi=int(delta_phi+phi) % int(2*pi) ;
Serial.print("g=" );
Serial.print(g);
Serial.print("et d =");
Serial.println(d);
Serial.print("et phi =");
Serial.println(phi);
// il faut mieux mesurer L (distanceentre les 2 rue )
// demande au prof la conversion enint pour le modulo
}
Transmissions des coordonnées vers la carte principal
Contrainte et choix de support de transmission
Les constraint :
- Support du transmission le plus rapide à fin une meilleur precision et comptage de nombre d'encouches peu importe à quel vitesse le robt tourne .
- notre carte comunique que avec la carte mere à fin d'envoyer les coordonné donc on pas besoin juste d'un seul fil pour le transfert de donné => pas besoin de beacoup de conxeion sans oublier la masse & le vcc.
- le type de reseau qu'on cherche est maitre-esclave : carte mere- notre carte qui envoi les information sachant que notre microcontrleur supporte que i²c et SPI : on debut on fais une comparaison général des differant supprt de transmission puis on se lmite au I²C et SPI à fin d'en choisir un
en comparant les diferant support de transmission presenté dans le tableau on peu voir que:
- 1wire, i2c & SMBUS sont lents ce qui nous induit au erreur de posionnement => on ne prend pas .
- Il nous reste CAN, SPI et M-BUS qu'on peux prendre => on essai de prendre le plus facile et le plus compatible :
- le M-BUS est peu compatible => on prend pas .
- le CAN est relativment complexe => on prend pas .
- le SPI est relativment facil et je l'ai utlisé au paravant => on prend .
Explication de SPI
SPI ( Serial Peripheral Interface) est une liason serie sychronisé à l'aide d'un horloge ,de type maitre esclave .
Le bus SPI est composé de 4 signaux logiques :
- SCLK — Serial Clock, Horloge (généré par le maître) permet de sychronisé la transmission de donnés .
- MOSI — Master Output, Slave Input (généré par le maître et envoyé à l'esclave )
- MISO — Master Input, Slave Output (généré par l'esclave et envoyé au maître )
- SS — Slave Select, Actif à l'état bas (généré par le maître)
Donc en gros on met SS =0 puis à l'aide de MISO on envoi x,y,phi puis on remet SS=1 .
Méthodes pour programmer un ATtiny44a
Il y a plusier facon de programmer un microprocceseur dont on retient 2 :
- Géneralement et plus facil pour un debut travaillant sur windows est de trouvé un IDE telle que atmel stuio qui oas mal fais mais tres lors lors de compilation.
- Sous Linux il est plus rapide et plus efficase pour prgrammer un microprosseur à l'aide d'un terminale en suivant plus etape :
***** etape de compilation
- se déplacer dans le répertoire de votre programme : cd /home/ge1/deElec/
- CONFIGUER QUELLE µc avr-gcc -Wall -g -Os -mmcu=attiny10 -o prog.o prog.c && avr-objcopy -j .text -j .data -O ihex prog.o prog.hex
- transferer le programme avrdude -c avrisp2 -P usb -p t44
Programme test de fonctionnement
c'est programme pour clignoter UNE led :
Programme test
#define F_CPU 1000000UL#include <avr/io.h>#include <util/delay.h>
int main(void){DDRB|=1<<PB1; while (1) { PORTB^= 1<<PB1; _delay_ms(300); }
Programme de de transmission de donné entre 2 arduino
Il y a un master & un slave :
le master :
Programme du master
#include <SPI.h>
//master
void setup (void)
{
Serial.begin(115200);
digitalWrite(SS, HIGH);
// ensure SS stays high for now
// Put SCK, MOSI, SS pins intooutput mode
// also put SCK, MOSI into LOWstate, and SS into HIGH state.
// Then put SPI hardware into Mastermode and turn SPI on
SPI.begin ();
// Slow down the master a bit
SPI.setClockDivider(SPI_CLOCK_DIV128);
}
// end of setup
void loop (void)
{ uint8_t c,a;
// enable Slave Select
digitalWrite(SS, LOW);
// SS is pin 10
// send test string
a=SPI.transfer (10);
digitalWrite(SS, HIGH);
Serial.println(a,DEC);
// disable Slave Select
delay (100);
// 1 seconds delay
}
le slave :
Programme du slave
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
MCUCR|=1<<ISC00;
MCUCR|=1<<ISC01;
ISR (INT0_vect)
{
static uint8_t x=0;
x++;
}
ISR (USI_OVF_vect)
{
static uint8_t i=0;
// valeur a transmettre pour le coupsuivant
USIDR=i;
//acquittement de l'ovf
USISR|=1<<USIOIF;
// test de transmission
i+=3;
}
int main (void)
{
//MISO comme sortie
DDRA|=1<<PA7;
// mode 3wire
USICR|=1<<USIWM0;
// clock
USICR|=1<<USICS1;
//interruption
USICR|=1<<USIOIE;
sei();
// acquittement d'un ovf eventuel
USISR|=1<<USIOIF;
while (1)
{
// attente du SS
while((PINA&(1<<PA7))!=0);
// fin du SS
while((PINA&(1<<PA7))==0);
// remise a zero du compteur defront
// permet de resynchroniser encas de pb
// a faire plutot parinterruption sur front de SS
USISR &=0b11110000;
}
}
Assemblage de données
Apres avoir reussi à faire un transmission de donné en SPI entre arduino et ATtiny44 on va essieé de ressmeblger les element permettant d'avoir les coordonné :
Simuation des roues codeuse en reliant un GBF au pin : PB2
Programme comptant les encoches
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile uint8_t x=0;
ISR (USI_OVF_vect)
{
// valeur a transmettre pour le coupsuivant
USIDR=x;
}
ISR (INT0_vect)
{
// valeur a transmettre pour le coupsuivant
x++;
}
int main (void)
{
MCUCR|=1<<ISC00;
MCUCR|=1<<ISC01; // RISING EDGEINT0
GIMSK|=1<<INT0;
//MISO comme sortie
DDRA|=1<<PA5;
// mode 3wire
USICR|=1<<USIWM0;
// clock
USICR|=1<<USICS1;
//interruption
USICR|=1<<USIOIE;
sei();
// acquittement d'un ovf eventuel
USISR|=1<<USIOIF;
while (1)
{
// attente du SS
while((PINA&(1<<PA7))!=0);
// fin du SS
while((PINA&(1<<PA7))==0);
// remise a zero du compteur defront
// permet de resynchroniser encas de pb
// a faire plutot parinterruption sur front de SS
USISR &=0b11110000;
}
}
Utilisation de l'algorithme pour avoir les position expliqué précédemment
c'est cette partie le partie finale de projet qu'on a pas eu le temps de le finir: inserer l'algorithme et les different partie plus hauts pour avoir les coordonnés polaire et l'angle
à compléter
#include <avr/io.h>
#include <math.h>
volatile int d=0;
volatile int g=0;
/*
double delta_moy =0;
double delta_dif =0;
double delta_angle =0;
double delta_x =0;
double delta_y =0;
double angle =0;
double x =0;
double y =0;
double phi =0;
double delta_phi = 0;
double difference_encouche_droite =0;
double difference_encouche_gauche =0;
double COEF_a_trouver=0;
const double pi = 3.14;
*/
void setup()
{
Serial.begin(9600);
cli(); // arrêt desinterruptions
EICRA|=0x05; // mode dedéclenchement de l'interruption : change
// ISC00 =1 // ISC01 =0 // ISC10 =1 // ISC11 = 0
EIMSK|=0x03; // choix desinterruptions actives : interruption 0 & 1
sei(); // autorisation desinterruptions
}
ISR(INT0_vect) // programmed'interruption : le programme principal est interrompu,
{ // l'interruptionexécutée et ensuite le programme principal continu normalement sonexécution
if ((PORTD&(1<<PIND2))!=0)
{
Serial.println("h");
if ((PORTD&(1<<PIND3))!=0)
g++ ;
else
g-- ;
}
else
{
Serial.println("l");
if ((PORTD&(1<<PIND3))!=0)g++ ;
else g-- ;
}
}
ISR(INT_vect1) // programmed'interruption : le programme principal est interrompu,
{ // l'interruptionexécutée et ensuite le programme principal continu normalement sonexécution
if (PORTD&(1<<PIND3)!=0)
{
if (PORTD&(1<<PIND2)!=0)
d++ ;
else
d-- ;
}
else
{
if (PORTD&(1<<PIND2)!=0)d-- ;
else d++ ;
}
}
void loop()
{/*
difference_encouche_droite=d-difference_encouche_droite;
difference_encouche_gauche=g-difference_encouche_gauche;
delta_moy =((g+d)/2)*COEF_a_trouver;
delta_dif =d-g;
delta_phi=int(delta_dif/23.3) %int(2*pi);
delta_x=delta_moy*cos(delta_phi);
delta_y=delta_moy*sin(delta_phi);
x=delta_x+x;
y=delta_y+y;
phi=int(delta_phi+phi) % int(2*pi) ;
*/
int tmp=g;
Serial.print("g=" );
Serial.println(tmp);
// Serial.print("et d =");
// Serial.println(d);
delay(200);
// Serial.print("et phi =");
// Serial.println(phi);
/* il faut mieux mesurer L (distanceentre les 2 rue )
demande au prof la conversion enint pour le modulo
trouver la coefitien de reductionque j'ai mis à un
*/
}
Annexes
Pages en relation avec le positionnement
http://manubatbat.free.fr/doc/positionning/node5.html
http://www.pobot.org/Asservissement-d-un-moteur-a.html