Description des codeurs
Chaque roue étant équipée d’un codeur incrémental, il est donc possible de commander le mouvement en distance plutôt qu’en durée.
Attention : les contrôleurs des moteurs n’autorisent pas le freinage ! Il y a donc un fort risque de dépassement des positions visées !
Les disques des codeurs sont sur le même axe que les roues, et comportent 20 fentes :

Les deux barrières infrarouge des codeurs sont reliées aux ports numériques (mode entrée) suivants :
|
Codeur gauche |
Codeur droit |
nom de l’interface |
CNTL |
CNTR |
Arduino UNO |
D2 (interruptible) |
D3 (interruptible) |
Raspberry Pi
|
GPIO8 (ou CE0) |
GPIO7 (ou CE1) |
Programmation en Python
Pour connaitre à chaque instant la position d’un codeur, il faut, pour chacun d’entre eux :
- initialiser une position angulaire (dans une variable),
- déterminer le sens de rotation (les codeurs de l’AlphaBot n’ont qu’une seule voie !),
- compter les impulsions (fronts montants et descendants du signal) en modifiant la valeur de la position angulaire.
Question
Les codeurs incrémentaux « double sens » sont dotés d’un double détecteur de front, déphasé d’un quart de période, afin de permettre le décodage du sens de rotation.
Dans le cas d’un robot comme l’AlphaBot, comment peut-on connaitre le sens de rotation alors que les codeurs n’ont qu’un seul détecteur ?
Gestion des interruptions

Une interruption est un arrêt temporaire de l’exécution normale d’un programme par le microprocesseur afin d’exécuter un autre programme (appelé service d’interruption). Les interruptions sont gérées par le gestionnaire d’événement.
Le service d’interruption est exécuté de manière asynchrone (on parle de thread séparé). Pour qu’il puisse être exécutée le plus fréquemment possible, il doit être très court, peu couteux en temps de calcul.
Initialisation
# Trois valeurs sont possibles :
# front montant : GPIO.RISING
# front descendant : GPIO.FALLING
# les deux : GPIO.BOTH
GPIO.add_event_detect(CH, GPIO.BOTH, callback = fct_interrupt, bouncetime = 75)
fct_interrupt(channel)
est une fonction qui sera appelée à chaque fois que le port CH
connaitra un front. L’argument channel
est automatiquement passé à la fonction par le gestionnaire d’événement, il prend la valeur du numéro du port qui a déclenché l’événement.
Voici par exemple comment elle peut être définie :
def fct_interrupt(ch):
print(ch) # affiche le numéro du port sur lequel s'est produit l'événement
Attention : elle doit être définie avant l’ajout de l’événement avec .add_event_detect()
!

bouncetime
est un paramètre de temporisation destiné à éviter l’effet de rebond, un phénomène fréquent lorsqu’il s’agit d’un bouton poussoir qui provoque le front du signal. La valeur de la temporisation nécessaire dépend de la technologie de ce qui fait commuter la tension (contact électrique, transistor, …). Il faut la spécifier en millisecondes.
Suppression
On peut à tout moment annuler (ou simplement interrompre) la détection d’événements :
GPIO.remove_event_detect(CH)
Exercice : codeurs
On décide, au sein d'un module nommé alphabot
, de créer des variables globales posD
et posG
permettant de mémoriser les positions angulaires absolues des deux roues :
# Positions angulaires des roues (en "pas"), initialisées à 0
posD, posG = 0, 0
En utilisant, comme un module, le programme écrit pour les
moteurs (à modifier selon les besoins),
écrire en Python (puis tester !) la fonction
front_codeur()
, qui doit permettre de modifier les variables mémorisant les positions angulaires des deux roues.
CORRECTION
import RPi.GPIO as GPIO
import time
from moteurs import *
GPIO.setmode(GPIO.BCM)
# Ports des codeurs
CDG = 7 # gauche
CDD = 8 # droite
GPIO.setup(CDG, GPIO.IN)
GPIO.setup(CDD, GPIO.IN)
# Positions angulaires des roues (en "pas"), initialisées à 0
posD, posG = 0, 0
########################################################################
#
########################################################################
def front_codeur(port):
global posD, posG
if port == CDD:
if get_vitesse('D') > 0:
posD += 1
elif get_vitesse('D') < 0:
posD -= 1
elif port == CDG:
if get_vitesse('G') > 0:
posG += 1
elif get_vitesse('G') < 0:
posG -= 1
GPIO.add_event_detect(CDG, GPIO.BOTH, callback = front_codeur,
bouncetime = 5)
GPIO.add_event_detect(CDD, GPIO.BOTH, callback = front_codeur,
bouncetime = 5)
########################################################################
# Procédures de test du module
########################################################################
if __name__ == "__main__":
rot_moteur('G', 20)
time.sleep(1)
rot_moteur('G', -20)
time.sleep(1)
rot_moteur('G', 0)
rot_moteur('D', 100)
time.sleep(1)
rot_moteur('D', -50)
time.sleep(1)
rot_moteur('D', 0)
GPIO.cleanup()
Comme pour les positions, on crée au sein du module alphabot, deux variables globales vitD et vitG permettant de mémoriser les vitesses angulaires des deux roues :
# Vitesses angulaires des roues (en "pas/seconde"), initialisées à 0
vitD, vitG = 0, 0
Modifier la fonction front_codeur()
de sorte qu'elle calcule en plus la vitesse angulaire, en pas/seconde, de la roue droite (roue == 'D'
) ou gauche (roue == 'G'
)
On aura besoin de la fonction
time.clock()
(voir article sur
le temps).
CORRECTION
import RPi.GPIO as GPIO
import time
from moteurs import *
GPIO.setmode(GPIO.BCM)
# Ports des codeurs
CDG = 7 # gauche
CDD = 8 # droite
GPIO.setup(CDG, GPIO.IN)
GPIO.setup(CDD, GPIO.IN)
# Positions angulaires des roues (en "pas"), initialisées à 0
posD, posG = 0, 0
# Vitesses angulaires des roues (en "pas/seconde"), initialisées à 0
vitD, vitG = 0, 0
# Une variable pour mesurer le temps écoulé entre deux fronts
t = time.clock()
########################################################################
#
########################################################################
def front_codeur(port):
global posD, posG, vitD, vitG, t
t1 = time.clock()
dt = t1 - t
t = t1
if port == CDD:
if get_vitesse('D') > 0:
posD += 1
vitD = 1/dt
elif get_vitesse('D') < 0:
posD -= 1
vitD = -1/dt
elif port == CDG:
if get_vitesse('G') > 0:
posG += 1
vitG = 1/dt
elif get_vitesse('G') < 0:
posG -= 1
vitG = -1/dt
GPIO.add_event_detect(CDG, GPIO.BOTH, callback = front_codeur,
bouncetime = 5)
GPIO.add_event_detect(CDD, GPIO.BOTH, callback = front_codeur,
bouncetime = 5)
########################################################################
# Procédures de test du module
########################################################################
if __name__ == "__main__":
rot_moteur('G', 20)
time.sleep(1)
rot_moteur('G', -20)
time.sleep(1)
rot_moteur('G', 0)
rot_moteur('D', 100)
time.sleep(1)
rot_moteur('D', -50)
time.sleep(1)
rot_moteur('D', 0)
GPIO.cleanup()
Asservissement en position
On a vu que la commande de rotation des roues basée sur le temps n’était pas fiable, car pour un consigne de vitesse donnée, les vitesses effectives des roues varient.
De plus, les moteurs sont incapables de freiner.
On se propose de réaliser un asservissement en position des roues.

Exercice : asservissement en position
Écrire une fonction position_P(roue, pos, P) permettant de contrôler le mouvement d'une roue ('D'
ou 'G'
), et lui faisant parcourir un déplacement pos
(pas de codeur - nombre entier relatif) en utilisant un correcteur proportionnel de gain P
(valeur à ajuster).
CORRECTION
def position_P(roue, pos, P = 10):
"""
"""
try:
while True:
if roue == 'D':
ecart = pos - posD
else:
ecart = pos - posG
vit = P*ecart
if vit > 100:
vit = 100
elif vit < -100:
vit = -100
rot_moteur(roue, vit)
except KeyboardInterrupt:
pass
Écrire une fonction permettant de faire avancer le robot, chacune des deux roues étant commandée individuellement, en
programmation parallèle.
Écrire une fonction permettant d'asservir le mouvement des roues en vitesse. Ainsi, le robot devrait aller en ligne droite quand les deux consignes de vitesse sont égales (contrairement au fonctionnement en boucle ouverte).
Après avoir observé le comportement du robot et identifié les problèmes, améliorer la fonction position_P()
en réalisant un correcteur plus performant.
Programmation en Arduino