Outils pour utilisateurs

Outils du site


Panneau latéral

Navigation

Plan auto

robotique:r_cerda:programmation

Programmation de R.Cerda

Cet article décrit une partie de la construction de R.Cerda, un robot simple basé sur le Raspberry pi. Il fait suite à un précédent article sur l'assemblage du robot : Montage pratique du robot : construction d'un châssis, fixation des roues, etc.. Le plan général du tutoriel de construction est le suivant :

Il est maintenant temps de transformer cet assemblage de matériel en un vrai Robot. Pour cela, nous allons maintenant voir l'ultime étape : la programmation. Nous verrons trois algorithmes, du plus simple au plus avancé. Cela devrait vous permettre d'avoir une base pour programmer votre robot comme vous le souhaitez, et pourquoi pas quelques idées !

Commençons tout de suite, en rentrant dans le vif du sujet. Ce robot utilise le MCP23017 pour commander les L293D qui à leur tour commandent les moteurs. J'ai donc choisi python pour écrire le programme des robots, puisque c'est sur cette base que nous avons vu l'utilisation du MCP23017. En premier lieu, voici le lien vers le GitHub ou je met les divers fichiers relatifs au code du robot Cerda.

Dans ce dépôt, vous trouverez divers fichiers, donc la classe I2C d'Adafruit, et la classe permettant de gérer le MCP23017. Assurez vous d'avoir ces deux fichiers dans le répertoire contenant les fichiers de votre robot, ils sont nécessaires pour son fonctionnement, et doivent être importés dans le script du programme de votre robot. Vous noterez également un script robotStop.py, qui sert simplement à arrêter les moteurs du robot. Sur mon raspberry pi, j'ai fait un alias de ce script vers la commande stop, ce qui fait que je peux arrêter le déplacement du robot en tapant stop dans un terminal, n'importe ou.

Divers scripts sont présents, pour tester les différents organes du robot :

N'hésitez pas à tester vos capteurs avant de lancer le robot, pour vérifier si tout va bien. J'intégrerai également ultérieurement des scripts pour tester les moteurs.

Algorithme basique d'évitement d'obstacles : r1.py

Passons maintenant au programme principal du robot lui même. Voyons d'abord vite fait l'algorithme. Dans une boucle infinie, on répète les instructions suivantes :

Lire la distance mesurée devant le robot par le capteur à ultrasons.
Si cette distance est inférieure à 8 pouces, on recule.
Sinon, si cette distance est inférieure à 16 pouces, on tourne,
Sinon, on avance.

Et c'est tout! Ce simple algorithme suffit déjà à permettre au robot d'éviter des obstacles! Cet algorithme est implémenté dans le programme r1.py, dont la source se trouve sur le GitHub. La vidéo ci dessous permet de visualiser le comportement du robot exécutant ce programme :

Je vais détailler un peu le code. Au début, vous verrez tous les “import”, qui récupèrent les fonctions nécessaires. En dessous, vous retrouverez notre fonction “readadc”, que nous avons vue avec le MCP3008 (le tutoriel sur cette puce est disponible ici : Ajouter des entrées analogiques avec un MCP3008 et lire la valeur d'un potentiomètre), qui nous permet de lire une entrée analogique (ici le capteur à ultrasons) sur les 8 que propose le MCP3008. Voyez donc ces deux liens pour plus de détails sur le sujet. En dessous viennent les variables définissant les broches utilisées pour le SPI; je vous renvoie encore aux précédents tutoriels pour plus d'explications. Si vous avez suivi les schémas de montage de R.Cerda, il n'y a rien à changer ici.

Vient ensuite le corps du programme à proprement parler. Vous verrez alors une série de fonctions, dont la fonction readDistanceInch, qui permet de récupérer la distance en pouces plutot que la valeur brute. SI vous utilisez un autre capteur, il faudra adapter (ou bien utiliser la valeur brute, en faisant des essais). Les fonctions suivantes servent à commander directement le robot :

def moveForward(m1a,m1b,m1e,m2a,m2b,m2e):    
mcp.output(m1a, 1)    
mcp.output(m1b, 0)    
mcp.output(m1e, 1)    
mcp.output(m2a, 1)    
mcp.output(m2b, 0)    
mcp.output(m2e, 1)

Cette fonction par exemple permet mettre les broches de commande du premier moteur à 1 et 0, la broche enable à 1 pour activer ce moteur, et idem pour le second moteur. J'ai fait ces fonctions, car si le câblage change, il suffira de modifier les valeurs dans ces fonctions plutot que dans la boucle principale du programme. De la même manière, on a une fonction pour reculer, arrêter les moteurs, tourner à gauche, et à droite.

Cette fonction ne définit pas la durée pendant laquelle vous voulez faire une action. Une fois que vous appellerez l'une de ces fonctions, le robot continuera à exécuter cet ordre jusqu'à ce que vous l'arrêtiez. Ce qui veut dire que si vous souhaitez avancer d'une petite distance, vous devrez faire:

moveforward(...)
time.sleep(X)
stopMotors(...)

Plus bas, je définis les broches sur lesquelles sont connectés les détecteurs de contact, et deux fonctions pour lire leur valeur. En pratique, ces fonctions retournent 0 si le contact n'est pas pressé, et 1 si il est pressé, au lieu de 0 si le contact est pressé, et une valeur non nulle dans le cas contraire. Les broches du MCP23017 qui commandent les deux moteurs sont définies juste en dessous, et on peut alors mettre les broches en sortie pour les moteurs, ou en entrée pour les détecteurs de contact. Passons maintenant au corps du programme en lui même :

    try:
		#boucle principale et infinie du moteur
		while (True):
			#lecture de la distance de l'obstacle le plus proche
			d=readDistanceInch(0, SPICLK, SPIMOSI, SPIMISO, SPICS)
			#en dessous de 8 pouces, le robot recule pendant au moins 0.2s
			if(d<8):
				moveBackward(m1a,m1b,m1e,m2a,m2b,m2e)
				#time.sleep(0.2)
			#entre 8 et 16 pouces, le robot tourne pendant au moins 0.2s
			elif(d<16) :
				turnLeft(m1a,m1b,m1e,m2a,m2b,m2e)
				time.sleep(0.2)	
			#le reste du temps le robot avance pendant 0.05s
			else :
				moveForward(m1a,m1b,m1e,m2a,m2b,m2e)
			time.sleep(0.05)	  
    except KeyboardInterrupt:
		print ""
		print "stopping motors"
		stopMotors(m1a,m1b,m1e,m2a,m2b,m2e)
		print "motors stopped, exiting."
		sys.exit(0)

Vous noterez un bloc try et un bloc except. La partie dans “try” est le code du robot à proprement parler. Il s'agit de l'implémentation de l'algorithme décrit plus haut. On notera toutefois que j'ai introduit un délai quand le robot tourne, pour l'obliger à tourner au moins d'un certain angle. Comme mon robot est large et avec des angles proéminents, ça lui évite de coincer ses coins sur les bords de l'obstacle. La durée d'attente dépend de votre robot, à vous de tester! Dans tous les cas, avant de recommencer, j'ai mis un temps d'attente de 50ms, à vous de voir si ça vous convient également. C'est une attente que j'ai mise qui correspond à la fréquence de rafraîchissement du capteur de distance que j'utilise.

Le bloc “except KeyboardInterrupt” sert à gérer le CTRL+C qu'on utilise pour quitter le programme. Dans la précédente version, il n'y avait pas ce bloc, et du coup, quand on faisait CTRL+C pour arrêter l'exécution de l'algorithme, le robot restait bloqué sur la denrière instruction, par exemple, avancer, jusqu'à ce qu'on lance la commande robotStop.py. Ici, il arrêtera les moteurs avant de quitter le programme.

Algorithme basique d'évitement d'obstacles avec gestion des collisions : r2.py

La seconde version de ce code, r2.py, tient cette fois compte des capteurs de contact, qui sont prioritaires sur le capteur à ultrasons (si un contact est détecté, alors le robot recule, puis tourne dans la direction opposée, quoi que dise le capteur à ultrasons). Le reste du code est le même que dans le programme r1.py.

On peut voir le comportement du robot exécutant cet algorithme dans la vidéo ci dessous :

Algorithme basique d'évitement d'obstacles avec gestion des collisions et changement de sens de rotation : r3.py

La troisième version de ce code, r3.py permet au robot de changer de sens de rotation plutôt que de tourner toujours à gauche. En effet, dans les précédentes version, le robot évite systématiquement les obstacles par la gauche, ce qui entraîne qu'il finit par tourner en rond. Ici, lorsque le robot à tourné un certain nombre de fois du même côté, il décide d'éviter les obstacles en tournant dans l'autre sens. Nous considérons ici que tourner d'un coté inclut toute la série d'instructions entre le moment ou le robot commence à tourner pour éviter un obstacle et le moment ou il a complètement terminé sa rotation, reprenant sa route. On s'assure ainsi que le robot ne fasse pas des allers retours de gauche à droite : quand il commence à tourner, il continue jusqu'à avoir trouvé un chemin dégagé.

Cette vidéo illustre le comportement du robot lorsqu'il exécute ce programme :

Algorithme avancé d'évitement d'obstacle, avec analyse de l'environnement proche : r4.py

La quatrième version de ce code, r4.py reprend les bases posées sur les précédents algorithmes. Toutefois, au lieu d'éviter d'un côté ou de l'autre les obstacles “au hasard”, le robot qui rencontre un objet devant lui se tournera légèrement à gauche et à droite pour mesurer la distance de chaque côté. Il choisira alors de tourner du côté ou la distance est la plus grande. Une fois qu'il a commencé à tourner, il continuera jusqu'à ce que l'obstacle ne soit plus dans sa trajectoire. Cette approche permet au robot de choisir la trajectoire d'évitement la plus rapide lorsqu'il rencontre un mur : ainsi il s'éloignera du mur par le côté le plus ouvert.

La vidéo suivante illustre le comportement du robot lorsqu'il exécute ce programme :

Je n'ai pas encore téléversé le fichier sur le github, mais je vais chercher le code pour pouvoir le faire au plus vite, à l'adresse du lien en début de cette section.

robotique/r_cerda/programmation.txt · Dernière modification: 21/02/2015 17:27 par sky99