Maintenant que je suis en paix avec le fonctionnement de mes servomoteurs, je retourne à mon geckrobot. Cet article est une première tentative d’implémentation d’une librairie Arduino pour piloter les pattes du robot en m’appuyant sur les modèles géométriques directe et inverse ainsi que la librairie HerkuleXLib.
1 Mise en œuvre
En préambule, je décris les éléments du dispositif expérimental qui me permettent de tester le pilotage d’une patte.
1.1 Éléments de la patte
Je réutilise mon premier prototype de patte, dont les mouvements sont décrits par le schéma cinématique, comme présenté ci-dessous.
Pour la liaison entre 01 et 02, j’ai découpé, percé, puis plié une plaque d’aluminium de 1,5 mm (cette vidéo m’a aidé). Pour la liaison entre 02 et 03, j’ai juste assemblé les servomoteurs 1 et 2 ensemble. Enfin pour la liaison entre 03 et 04, j’ai utilisé des éléments standards en aluminium (marque Lynxmotion). Du côté opposé au palonnier, les éléments sont assemblés aux servomoteurs avec un roulement à bille (aligné à l’axe du palonnier) afin d’assurer une liaison pivot plus solide. Aussi j’ai matérialisé le point 04 par la pointe d’un stylo positionné à l’extrémité de ma patte.
Notez que la géométrie de cette patte ne correspond PAS aux solutions optimales calculée ici.
1.2 La maquette
J’ai réalisé une maquette sur laquelle est fixée la patte pour faciliter mes essais. Elle est principalement faite à partir d’Isorel (recyclage d’un fond de tiroir). Les équerres en aluminium sont pliées comme montré sur les photos, à l’aide d’un bout de “corde à piano” de diamètre 3 mm pour arrondir les angles. La carte Arduino est fixée sur la maquette, à l’arrière.
1.3 Configurer les servomoteurs
Repartant de la configuration utilisée pour cet article (ID = 253 pour tous les servomoteurs), il faut me changer les ID. J’utilise modifyID_MEGA.ino
que j’ai rajouté aux exemples de la librairie (disponible depuis la v1.2) pour les numéroter 0, 1 et 2 respectivement pour les liaisons en 01, 02 et 03.
Les liaisons mécaniques limitent les mouvements de la patte, j’ai donc configuré les positions minimale et maximale pour chaque servomoteur. J’ai aussi remis les paramètres d’accélération par défaut et la vitesse de communication avec les servomoteur à 666666 bps. Le code suivant m’a permis de procéder comme le montre la vidéo ci-après.
#include <HkxSetup.h> // global variables byte ID = 0; // ID of the servo HkxPrint* printoutLib; HkxPrint* printoutCode; HkxCommunication* communication; HkxSetup* setupServos; String message; uint16_t overloadPWMThreshold; int16_t minPosition, maxPosition; void setup() { delay(2000); // wait 2 seconds to open the serial monitor printoutLib = new HkxPrint(Serial, 9600); // Printout errors on the serial monitor printoutCode = new HkxPrint(*printoutLib, true, true, true); // Printout errors on the serial monitor communication = new HkxCommunication(HKX_115200, Serial1, *printoutLib); // Communication with the servo on Serial1 setupServos = new HkxSetup(*communication, *printoutLib); // setup the servos setupServos->setAllBaudRate(HKX_666666); setupServos->clearOneStatus(ID); setupServos->setAllAcceleration (50, 504); //Default values setupServos->getPWMPositionLimits(ID, &overloadPWMThreshold, &minPosition, &maxPosition); // All servos torque break and led off, but active servo torque free and led blue setupServos->setAllTorqueLEDControl(HKX_TORQUE_BREAK, HKX_LED_OFF); setupServos->setOneTorqueLEDControl(ID, HKX_TORQUE_FREE, HKX_LED_BLUE); printoutCode->infoPrint(" \" ID \" then number of the servo"); printoutCode->infoPrint(" \" min \" to set the current position as the mimum"); printoutCode->infoPrint(" \" max \" to set the current position as the maxium"); } void loop() { int16_t currentPosition; // Get the behaviour byte error = setupServos->getBehaviour(ID, HKX_NO_VALUE, HKX_NO_VALUE, HKX_NO_VALUE, HKX_NO_VALUE, ¤tPosition, HKX_NO_VALUE, HKX_NO_VALUE, HKX_NO_VALUE, HKX_NO_VALUE, HKX_NO_VALUE); if(error == 0){ message = "ID="; message += ID; message += " position="; message += currentPosition; printoutCode->infoPrint(message); } else if(error == 1){ printoutCode->errorPrint("Input not correct"); } else if(error == 2){ printoutCode->errorPrint("Servo not connected"); } else if(error == 3){ printoutCode->errorPrint("Data not consistent"); } // Get if (Serial.available() > 0) { String income = Serial.readString(); if(income.equals("ID")){ printoutCode->infoPrint("Enter the ID"); while(Serial.available() < 1) {}; setupServos->setOneTorqueLEDControl(ID, HKX_TORQUE_BREAK, HKX_LED_OFF); String income = Serial.readString(); ID = income.toInt(); setupServos->setOneTorqueLEDControl(ID, HKX_TORQUE_FREE, HKX_LED_BLUE); setupServos->getPWMPositionLimits(ID, &overloadPWMThreshold, &minPosition, &maxPosition); printoutCode->infoPrint("ID has been modified"); } else if(income.equals("min")){ minPosition = currentPosition; setupServos->setOnePWMPositionLimits(ID, overloadPWMThreshold, minPosition, maxPosition); message = "min position of ID="; message += ID; message += " has been modified"; printoutCode->infoPrint(message); } else if(income.equals("max")){ maxPosition = currentPosition; setupServos->setOnePWMPositionLimits(ID, overloadPWMThreshold, minPosition, maxPosition); message = "max position of ID="; message += ID; message += " has been modified"; printoutCode->infoPrint(message); } else { printoutCode->warningPrint("Incorrect input"); } } delay(1000); // refresh every second }
Pour télécharger le fichier : setupServos_MEGA
- servomoteur 0, zero0 = – 2,5°
- servomoteur 1, zéro1 = – 34,8°
- servomoteur 2, zéro2 = -111,4°
- X0 = 0 mm
- Y0 = 40 mm
- Z0 = 150 mm
- Y1 = 34 mm
- Y2 = 44 mm
- Y3 =120 mm
2 Objectifs
Je démarre avec cet article l’écriture d’une librairie Arduino pour le Geckrobot. J’implémente pour l’occasion une première classe, dont le but est de piloter une patte et que j’appelle GkrLeg.
- Le mouvement aticulaire pour lequel la génération de trajectoire s’applique aux servomoteurs. C’est en réalité ce qu’il se passe quand on utilise directement la librairie HerkuleXLib. GkrLeg doit malgré tout permettre de surmonter les limitations de la librairie (dues au protocole de communication) – en particulier la limite de temps de mouvement.
- Le mouvement spatial pour lequel la génération de trajectoire s’applique au point 04, permettant de le déplacer linéairement dans l’espace.
La différence entre ces deux types de mouvements est illustrée par la vidéo ci-dessous. À gauche est présenté un mouvement spatial quand à droite un mouvement articulaire, ces deux mouvements ayant les mêmes points de départ et d’arriver.
3 Le code
Je ne publie pas encore mon code parce qu’il n’est pas suffisamment mature. Je précise juste que pour les besoins de cet article j’ai fait des modifications (bug et nouvelles fonctions) de la librairie HerkuleXLib que je publierai dans la version 1.3.
Je vais quand même donner quelques détails algorithmiques du déplacement spatial dont je présente l’organigramme logique ci-dessous.
- Lorsque j’utilise le symbole “→”, c’est que je converti une coordonnée articulaire en coordonnée cartésienne – ou inversement – à l’aide des modèles cinématiques direct ou inverse.
- La variable t est le temps à l’instant donné.
- Vous remarquerez que je génère des sous-mouvements pour atteindre les états i+2 (et non i+1) aussi bien en terme de position que de temps de déplacement. Cela permet d’avoir un recouvrement entre les sous-mouvements. En effet, pour un mouvement donné, le servomoteur génère une trajectoire de vitesse avec phases d’accélération et de décélération qui, sans ce recouvrement, génère des saccades donc des vibrations (illustré au paragraphe 4.1). Nous obtenons ainsi une trajectoire plus fluide.
- J’ai limité le nombre de pas (nS) afin que la durée d’un pas (variable i) ne soit pas inférieure à 50 ms. Cette durée minimum me permet de garantir que le microcontrôleur a le temps d’effectuer les calculs (position et temps) du prochain pas (i+1) pendant le laps de temps du mouvement. Cela signifie aussi que plus on souhaite effectuer un déplacement rapide, moins celui-ci risque d’être précis en terme de position (le pas de 2 mm n’étant alors plus forcément respecté).
- D’un autre côté, je m’assure aussi que la durée d’un pas (variable i) ne dépasse pas 1400 ms. Comme expliqué au second point, j’effectue un recouvrement entre mes sous-mouvements. La durée du sous-mouvement peut donc aller jusqu’à 2800 ms suivant ce critère qui me garanti de ne pas dépasser la durée maximum de mouvement autorisé par le protocole de 2845 ms.
4 Quelques déconvenues
4.1 Recouvrement
Pour mettre en avant l’intérêt d’un recouvrement entre les sous-mouvements j’ai fait la vidéo ci-dessous, à gauche sans recouvrement, à droite avec recouvrement. Le premier déplacement permet de calibrer la position zéro des servomoteurs et utilise un mouvement articulaire (non cartésien). Pour les mouvements suivants, on voit qu’à gauche ceux-ci sont plus saccadés qu’à droite.
4.2 Trop d’approximations
Vous avez bien compris que la maquette telle que je l’ai faite a pour but de tester et illustrer mes développements sur l’exemple d’une table traçante. J’ai réutilisé la trajectoire de l’illustration du recouvrement en descendant jusqu’à Z = 0 (coordonnée théorique du plan de la feuille) pour que le stylo trace une ligne droite de 100 mm. On voit sur ce premier essai que si le stylo écrit bien sur le début de la trajectoire, celui-ci ne laissera sa marque que sur les 20 premier millimètres. J’ai vite compris que l’axe du servomoteur 0 n’était pas exactement perpendiculaire avec le plan de la feuille.
Pour compenser le défaut de perpendicularité, j’ai fait un second essai pour lequel je démarre ma trajectoire à Z=0 et la termine à Z=-1mm. Sur la vidéo qui suit vous remarquerez que cette fois le stylo écrit au début, à la fin, mais pas au milieu… De plus la ligne qui devrait faire 100 mm de long n’en fait que 80 mm et pour couronner le tout, la ligne n’est pas droite (cf. photo après la vidéo) !
5 Sortons les grands moyens
Mon hypothèse est que mes problèmes viennent d’un assemblage peu précis (maquette et patte) et que les mesures approximatives de mes constantes à la règle n’arrangent rien à l’affaire !
Pour pallier à ce problème, j’aimerais d’abord renforcer un petit peu ma maquette pour rigidifier la partie verticale, actuellement trop flexible. Aussi je prévois de tester une approche par apprentissage. Pour cela, il faut commencer par collecter des données expérimentales, puis faire tourner un algorithme d’optimisation pour qu’il calcule les valeurs des constantes qui donnent la meilleurs corrélation avec les données expérimentales. Me restera alors plus qu’à tester expérimentalement mon code avec ces valeurs.