«

»

Mai 16

Ada & robots

Salut,

Aujourd’hui un article sur comment programmer sur les robots en utilisant l’API fournie au club (sur nos dépôts git). Nous allons voir qu’il y a plusieurs étapes à gérer mais au final, le procédé est très rapide si l’on sait utiliser les bon outils.

MAY THE FORTH BE WITH YOU

Pour commencer, nous allons faire du forth. Forth est un langage super simple à utiliser et vous pouvez trouver pas mal de ressources sur le net. Voici un lien vers les cours de Forth en ROSE : http://www.rfc1149.net/download/documents/22-forth.pdf et un simulateur en ligne pour s’entrainer : http://forthfreak.net/jsforth80x25.html.

Faisons tout de même un tour rapide du langage. Il fonctionne principalement grâce à une interaction directe avec la pile. Une pile est une structure de donnée dans laquelle on peut ajouter des valeurs dessus et on ne peut retirer que la valeur au sommet. Nous retrouvons tous les opérateurs usuels. Prenons un exemple : l’addition. En forth, nous ferrions :

1 2 +

Ce que ce code fait est :

1 . Mettre 1 sur la pile

2. Mettre 2 sur la pile

3. Appeler l’opérateur + qui prend deux valeurs au sommet de la pile et les remplace par leur somme.

Si vous faites cela, vous n’obtiendrez aucun résultat : il est sur la pile. Pour le dépiler et l’afficher, il faut faire « . » . Par exemple :

3 1 – .

Affichera :

2

Il existe pleins d’opération sur la pile : dup duplique le sommet, swap échange la place des deux éléments au sommet, drop retire l’élément au sommet de la pile…

Maintenant que nous savons manipuler la pile, nous allons créer et utiliser des « fonctions » pour agir plus facilement. Forth n’a pas de synthaxe : il fonctionne sur un système de mots que l’on peut changer à volonté. Les actions sont exécuter dans l’ordre où elles sont écrites. Une fonction va être un mot. Il ne faut pas oublier qu’il faut mettre les arguments sur la pile pour appeler la fonction dessus, ce qui signifie donner les arguments avant la fonction, ce qui est un peu surprenant au début.

La structure pour déclarer une fonction est :

: Nom_de_la_fonction mot1 mot2 … motn ;

Attention de ne pas coller les ; et : car forth les prendrait comme partie d’un mot. Prenons un exemple :

: carre ( a — n ) dup * ;

Les commentaires se font entre des parenthèses et il est d’usage de préciser les données entrantes ( a ) et sortant ( n ) séparés par deux tirets. Cela signifie que la fonction prend une valeur sur la pile et en remet une. On peut ensuite utiliser notre fonction :

2 carre .

Affichera :

4

Voila, vous connaissez les bases de forth. C’est à peu près tout ce dont vous aurez besoin. Parfois, vous aurez besoin d’utiliser des structures de contrôle comme if et while, je vous laisse chercher de votre côté leur fonctionnement 😉

 

D’ADA A FORTH

 

Voyons maintenant comment faire pour utiliser forth en pratique. Vous pouvez compiler le programme à plusieurs endroits différents. A la racine, si vous faites un make, tout sera compilé. Sinon, si vous allez dans le dossier stm32, vous aurez la possibilité de compiler pour votre ordinateur (make) ou pour la stm32 (make stm32).

Compilez le programme pour la stm32 et flashez le programme main.

Allez ensuite dans le dossier lnx/main et faites : ./main_forth_interp (qui normalement est apparu après un make, notez que vous pouvez aussi faire make dans lnx/main). Cela a pour effet d’ouvrir un interpréteur Forth qui va permettre de contrôler le robot. Branchez votre ordinateur au port micro usb de la stm32 et normalement si tout se passe bien, le robot est bien reconnu. Vous aurez probablement des Bad chars qui apparaissent le temps que l’ordinateur et la carte se synchronisent. A noter qu’il faut alimenter la carte pour que ça marche. Faites des essais dans l’interpréteur. SI vous avez un robot, faites :

100 Move

Pour faire avancer le robot de 100mm vers l’avant. Vous avez maintenant tous les outils en main pour contrôler le robot. Le passage à forth permet de rapidement tester une stratégie avant de passer au codage en Ada.

LE CONTRÔLE

 

En fait, toutes les fonctions définies en Ada (et exportée) sont utilisable dans l’interpréteur. On a par exemple accès à Ax.Get_Position, Strat.Color_Coef… Vous pouvez regarder dans le code source pour trouver ce que font ces fonctions. Je vous conseille d’utiliser grep -r ‘chaine recherchée’ pour retrouver le fichier qui contient ou utilise la chaîne recherchée. Vous pouvez obtenir la signature de la fonction pour connaitre les arguments grâce à l’ajout de -Help à la fin du nom de la fonction. Nous allons passer en revu les fonctions les plus utiles (l’autocimplétion fonctionne) :

  • Move.Forward ou Move : Permet d’avancer (pour une valeur positive) ou de reculer (pour un valeur négative).
  • Move.Turn ou Turn : Tourne de l’angle donné.
    • Position.Set_X, Position.Set_Y, Position.Set_Theta ou Set_X, Set_Y, Set_Theta: définie la valeur de X, Y ou Thêta pour que le robot sache où il est.
  • Position.Get_X, Position.Get_Y, Position.Get_Theta ou Get_X, Get_Y, Get_Theta : Obtient la valeur actuelle de X, Y ou Theta.
  • Move.GotoXY ou GotoXY : Fait aller le robot à la bonne position.
  • Move.Curve_Add_Forward_Point, Move.Curve_Add_Backward_Point : Ajoute un point en avant ou arrière sur le chemin du robot.
  • Move.Turn_To_Angle : Oriente le robot dans l’angle donné.
  • Ax12.Get_Position, Ax12.Set_Position : Obtient ou impose la valeur d’un Ax12.
  • Move.Wait_Move_End ou WAIT_MOVE_END : Attend que le mouvement actuel s’arrête, sinon il risque d’être effacé par les actions qui arrivent. Il renvoit un Move.Move_End_Record qui peut être Done (Le mouvement s’est fini), Blocked (mouvement bloqué) ou Bad_Guy (Ennemi détecté).
  • Strat.Goto_Front_Border, Strat.Goto_Back_Border : Fait avancer ou reculer le robot jusqu’au bord. C’est très utile pour les calibrations.
  • Strat.Color_Coef : Retourne 1 ou -1 suivant l’équipe.
  • Strat.Yellow_To_All : Convertit un angle pour que celui-ci soit symétrique pour les deux équipe
  • Strat.Yellow_To_All_X : Idem pour le X.
  • Move.Set_Max_Speed : Règle la vitesse maximale
  • Stm32_Config.Set_Enable_Bad_Guy : Activation ou désactivation de l’adversaire

Voila pour les principales commandes. Pour le reste, il faut chercher un peu dans le code et comprendre comment les choses fonctionnent. Avec un peu d’expérience, ça va très vite.

 

SÉQUENCE D’INITIALISATION ET PROGRAMME DE MATCH

La séquence d’initialisation est primordiale : elle permet au robot de savoir au il est et de vérifier que tous les actionneurs marchent bien. Pour la configurer, il faut aller dans la procédure Init_Pos dans big_strat.adb ou small_strat.adb. Ensuite, vous pouvez changer les programmes principaux des robots dans les procédures Do_Match des même fichiers. Inspirez vous des années précédentes pour comprendre comment faire.

CALIBRER LE ROBOT

Il faut aussi bien calibrer le robot pour qu’il aille bien aux bonnes positions. Voici les différentes étapes à suivre (merci à Julien Brette):

Commencez par lancer un main_forth_interp et mettre les coefficient à ‘1’ (valeur centrale) :

ok> 0 32768 Stm32_Config.Set_Adjustment 1 32768 Stm32_Config.Set_Adjustment
Mettre le robot contre un bord:
ok> Strat.Goto_Back_Border
Lancer dans un autre terminal ./test_counter (toujours dans lnx/main).
Faire des huit avec le robot (pour rappel, le but c’est que le robot parcourt la plus grand distance possible, sans faire de tour sur lui même, de façon à ce que les 2 roues parcourent exactement la même distance). Exemple de code (à adapter à la situation) :
ok> : q 500 move wait_move_end 90 turn wait_move end ;
ok> : p 500 move wait_move_end -90 turn wait_move end ;
ok> : huit q q p p p p q q ;
ok> : go 1000 move wait_move_end 10 0 do huit loop ;
ok> go
Apres, si tout va bien, il faut un peu corriger l’angle et faire reculer le robot pour qu’ils soit sur le bord à nouveau.
Là, les deux roues ont fait exactement la même distance, et test_counter va dire ce qu’il a mesuré. Il y a de grande chance pour qu’une roue soit plus grande que l’autre, donc en ‘ticks’ une des deux roues sera au dessus de l’autre. Il faut faire une règle de trois et ajuster le coefficient d’ajustement de la bonne roue.
Les coefficients se trouvent dans stm32/config.adb, dans Ajustement.
Après avoir fait cela, on va faire des tours pour avoir la bonne correspondance ticks/degrés.
Mettre le robot sur  un bord, lancer test_counter, faire avancer le robot et le faire tourner:

ok> 1000 move wait_move end 3600 turn wait_move_end
Corriges l’angle et reculer jusqu’au mur. Prendre la différences en ticks entre les deux roues et en déduire 2 constantes:
L_1 = 2^29 * 2 * PI * 10 / (différence tics)  (il faut faire le calcul autre part et rentrer le résultat pour être sur de la précision)
Top_In_Deg = (somme tics)/10/360
Mettre les deux valeurs dans stm32/config-asserv_config.adb.
On va mainteanant configurer la proportion ticks/distance.
Vous devrez avoir besoin d’une autre valeur: Top_In_M. Pour ça, mettre le robot sur un bord,lancer test_counter, lui dire d’avancer d’un mètre (ou plus si vous avez la place) et mesurer de combien il a vraiment avancé (notez que si l’asservissement est mal réglé et qu’il ne va pas droit, il faut recommencer). Après :
Top_In_M = 1000 * (somme tics) / (distance réel en mm)
Pour info, Top_In_M et Top_In_Deg  servent à calculer de combien le robot doit bouger quand vous lui donnez des ordres en mm/degré. Ca sert aussi quand vous voulez qu’il vous donne sa position en mm/degré.
L_1, c’est une constante vitale pour qu’il calcule sa position: pour calculer sa position, il a besoin de savoir la distance entre ses deux roues de façon hyper précise et L_1 c’est ça (en vrai c’est constante/distance entre les roues). En gros, si la précision de la position est pourrie, c’est surement que L_1 est pas bon (en supposant que vous avez fait les huit).
Ciao,
Julien