Codeurs

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 :

  1. initialiser une position angulaire (dans une variable),
  2. déterminer le sens de rotation (les codeurs de l’AlphaBot n’ont qu’une seule voie !),
  3. 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

 

 

Vous aimerez aussi...

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *