Construire un Robot Hexapode
14 décembre 2011

Sommaire :
Voici une page décrivant la construction d'un robot hexapode (mécanique, électronique, et algorithme).
Le robot hexapode décrit ci-dessous consiste en une structure fixe à laquelle est attachée six pattes, trois pattes parallèles à trois autres.
Le robot a donc une forme de rectangle. Chaque patte doit avoir deux degrés de liberté : elle doit pouvoir se soulever et avancer. On va donc utiliser deux servomoteurs par patte, car ceux-ci permettent de contrôler très facilement et avec précision la position de la patte. Ils permettent de faciliter l'assemblage étant donné que les engrenages de réduction sont présents dans le boitier du servomoteur.
Le robot sera construit avec de l'aluminium : celui-ci permet un usinage facile et une bonne solidité.
La 'marche' d'un hexapode
Il y a de nombreuse façon de faire marcher un hexapode, voici deux façons de faire. Pour simplifier les explications, on va numéroter les pattes :

La marche la plus simple à implémenter mais aussi la plus lente aussi consiste à soulever la patte 1 (la jambe), l'avancer (la cuisse) puis à la reposer (la jambe), et ainsi de suite pour toute les pattes. Puis on remet toutes les pattes (les 'cuisses') dans l'axe du robot, comme à l'état initial. Le robot a ainsi avancé de quelques centimètres.
Un autre type de marche, beaucoup plus efficace consiste à faire avancer l'hexapode 3 pattes par 3 pattes. En effet, seules 3 pattes sont nécessaires pour qu'il tienne debout. Ainsi 3 pattes avancent quand les 3 autres sont statiques.
Tout d'abord, on soulève les 'jambes' 1, 3 et 5 puis on avance les 3 'cuisses' de ces mêmes pattes, et on repose les jambes. Ensuite on soulève les autres jambes (2, 4 et 6) et on remet les cuisses 1, 3 et 5 dans leur état initial (perpendiculaire au sens de la marche). Le robot avance ainsi de quelques centimètres. On continue ensuite la même chose pour les trois autres pattes :
On avance les cuisses 2, 4 et 6; on repose les jambes 2, 4 et 6; on soulève les jambes 1, 3 et 5; on remet dans leur sens initial les cuisses 2, 4 et 6.
Le robot avance un peu, et on se retrouve dans la même configuration qu'au début. On continue donc cette 'boucle' pour avancer.
Pour faire avancer le robot vers la droite ou vers la gauche, il suffit d'avancer plus les pattes d'un côté que de l'autre.
La marche 3 par 3 en images :
La mécanique

Pour construire le robot hexapode, on va utiliser une lamelle d'aluminium de 2 ou 3 mm d'épaisseur.
Construction d'une « cuisse » de l'hexapode
On plie les deux extrémités, de façon à ce que l'on puisse glisser le servomoteur entre les deux pour le fixer (il suffit de serrer la plaque dans un étau et de taper légèrement sur l'extrémité avec un marteau).
Ainsi on peut faire une autre entaille et découper un petit bout de la plaque d'alu afin de pouvoir plier la plaque sur le haut du servomoteur sans gêner le mécanisme.
Ensuite, on découpe une lamelle d'alu d'environ 1,5 fois la longueur du servo. On perce deux trous coincident avec les deux trous de la plaque d'alu précédente (sur le côté du servomoteur). Puis on visse le tout.
Il ne reste plus qu'à faire la fixation pour la 'jambe' du robot. Suivant le palonnier utilisé, les trous changent. Ici on perce deux petits trous pour fixer le palonnier à la plaque et un gros trou pour fixer le palonnier au servomoteur. J'ai agrandi légèrement les trous du palonnier pour pouvoir utiliser des vis de 3mm.
Voilà ! La 'cuisse' de l'hexapode est finie, il faut maintenant faire sa 'jambe'.

Construction d'une « jambe » de l'hexapode
La jambe est plus simple à fabriquer, elle consiste en une 'rallonge' pour le servomoteur afin d'y faire mettre un 'pied'. On coupe donc un bout de lamelle d'alu de quelques centimètres, et on diminue sa largeur pour quelle fasse la largeur du servomoteur. Ensuite on perce deux trous pour la fixer au servomoteur.Pour avoir une plus grande surface d'appuie que le coin de la lamelle, on peut fabriquer un pied en plastique (cf Méthode pour transformer du plastique).
La structure du robot
La structure du robot consiste en un 'rectangle' d'aluminium où sont percés des trous pour la fixation des pattes.
On commence donc par découper une lamelle, qui sera un des deux côtés de la structure, dans laquelle on perce des trous pour la fixation des servomoteurs. La longueur de la lamelle à prendre est proportionnelle à la taille des servomoteurs, ici elle est d'environ 30cm.


Après avoir fait les deux côtés identiques, on fait les deux autres côtés : l'avant et l'arrière du robot. On procède de la même manière, on découpe une lamelle, et on perce dans chaque deux fixations pour les palonniers, mais cette fois ci dans l'autre sens, de façon à ce que ces fixations attachent aussi les quatre côtés de la structure ensemble. Pour donner l'ordre de grandeur, ici cette lamelle fait environ 14cm.
Enfin, on peut rajouter une lamelle en diagonale afin de bien consolider la structure.
Il ne reste plus qu'à tout assembler : on visse la structure, et on attache tous les servomoteurs.
On peut alors utiliser une boite en plastique, que l'on attache sur le dessus, afin d'y mettre l'électronique qui va donner vie au robot.

L'électronique
La partie électronique doit :
- Commander les 12 servomoteurs du robot
- Permettre l'envoi et la réception de donnée sans fil (pour commander le robot à distance)
- Disposer de quelques entrées numériques et/ou analogiques de façon a pouvoir ajouter ultérieurement des capteurs / des nouvelles fonctionnalités
La fonction 'sans fil' peut être effectuée par un module bluetooth/série, qui permet, pour un prix relativement faible, de communiquer avec un ordinateur ou avec un mobile (qui peut faire office de télécommande). Celui-ci est très simple à implémenter car il peut communiquer via une liaison série avec le microcontrôleur.

Pour alimenter les servomoteurs, j'ai utilisé un régulateur à découpage, un LM2679, qui va transformer le 12V de la batterie en 6V sans trop de pertes.
Pour la liaison sans fil, un des nombreux modules uart-bluetooth d'eBay suffit pour une utilisation en intérieur, sans grande portée. Si vous voulez faire de votre téléphone une télécommande, cette page peu vous servir.
La programmation du microcontrôleur
Pour contrôler la position d'un servomoteur, il faut lui envoyer une impulsion de 1 à 2ms (1ms : gauche, 2ms : droite), toutes les 20ms.
Après avoir envoyé les impulsions de commande, c'est à dire pendant les 18ms restants, on va lire les éventuelles données arrivées par bluetooth,
puis on va régler la durée des prochaines impulsions de commande. Les données reçues par bluetooth vont définir la vitesse, la direction du robot et le type de « marche » de robot. La durée des impulsions, qui correspondent à la position des servomoteurs, vont dépendre de l'étape où l'on est dans la marche de l'hexapode. Pour savoir quand faire le mouvement suivant, il faut avoir une approximation du temps qui passe. On va donc compter le nombre de fois où l'on envoie les impulsions de commande, car celles-ci sont envoyées toutes les 20ms.

Voici une implémentation en C de cet algorithme. Le code est indépendant de toute architecture, il doit être adapté au microcontrôleur utilisé (il faut initialiser le microcontrôleur, et faire les fonctions pour l'UART et le changement d'état des pins).
int main( void ) { //... (Initialisation) // Variables contenant la position des servomoteurs signed char cuisse[6]={0}, jambe[6]={0}; // Variables stockant le sens de rotation de chaque servomoteur const signed char cuisse_sens[6]={1, 1, -1, -1, -1, 1}, jambe_sens[6]={1, 1, -1, -1, -1, 1}; // Données de calibration des servos, à ajuster experimentalement const signed char cuisse_calib[6]={10, 7, 15, 16, 16, 6}, jambe_calib[6]={0, 0, 24, 21, 23, 0}; int k=0;// Compteur char step = 0;// Etape de la marche en cours char go = 0;// Type de marche char speed = 100;// Vitesse char dir = 0;// Direction unsigned char rx;// Donnée reçue while(1) { // On passe à l'étape suivante de la marche si assez de temps est passé, // on modifie donc les variables cuisse et jambe, pour modifier la position des servos (ie la durée des impulsions) if(k>speed) { k=0; // Mode de marche 3 par 3 if(go == MODE_TRI) { static char sb = 0; if(step > 2) { step=0; sb = (sb == 0) ? 1 : 0; } switch(step) { case 0: cuisse[sb] = cuisse[sb+2] = cuisse[sb+4] = DP; cuisse[-sb+1] = cuisse[-sb+3] = cuisse[-sb+5] = 0; jambe[sb] = jambe[sb+2] = jambe[sb+4] = DG; jambe[-sb+1] = jambe[-sb+3] = jambe[-sb+5] = 0; break; case 1: cuisse[sb] = cuisse[sb+2] = cuisse[sb+4] = DP; cuisse[-sb+1] = cuisse[-sb+3] = cuisse[-sb+5] = 0; jambe[sb] = jambe[sb+2] = jambe[sb+4] = 0; jambe[-sb+1] = jambe[-sb+3] = jambe[-sb+5] = 0; break; case 2: cuisse[sb] = cuisse[sb+2] = cuisse[sb+4] = DP; cuisse[-sb+1] = cuisse[-sb+3] = cuisse[-sb+5] = 0; jambe[sb] = jambe[sb+2] = jambe[sb+4] = 0; jambe[-sb+1] = jambe[-sb+3] = jambe[-sb+5] = DG; break; } if(dir == DIR_L) {cuisse[0] = cuisse[1] = cuisse[2] = jambe[0] = jambe[1] = jambe[2] = 0;} else if(dir == DIR_R) {cuisse[3] = cuisse[4] = cuisse[5] = jambe[3] = jambe[4] = jambe[5] = 0;} } // Mode de marche jambe par jambe else if(go == MODE_PPP) { if(step > 19) step=0; if(step == 0 || step == 19) { cuisse[0] = cuisse[1] = cuisse[2] = cuisse[3] = cuisse[4] = cuisse[5] = 0; jambe[0] = jambe[1] = jambe[2] = jambe[3] = jambe[4] = jambe[5] = 0; } else { char servo = (step-1)/3; if(servo == 1) servo = 5; else if(servo == 5) servo = 1; if((dir != DIR_L || servo >= 3) && (dir != DIR_R || servo < 3)) { switch((step-1)%3) { case 0: cuisse[servo] = 0; jambe[servo] = DG; break; case 1: cuisse[servo] = DP; jambe[servo] = DG; break; case 2: cuisse[servo] = DP; jambe[servo] = 0; break; } } } } step++; } k++; // Si l'uart a des données dans son buffer if(rxDataReady()) { // Lit le buffer de l'uart (fonction bloquante) rx = readRx(); if(rx == 'g' || rx == 'G') {// Mode de 'marche' rx = readRx(); step=0; if(rx == MODE_TRI+'0') { go = MODE_TRI;speed = 40; } else if(rx == MODE_PPP+'0') { go = MODE_PPP;speed = 20; } else { go = 0; cuisse[0] = cuisse[1] = cuisse[2] = cuisse[3] = cuisse[4] = cuisse[5] = 0; jambe[0] = jambe[1] = jambe[2] = jambe[3] = jambe[4] = jambe[5] = 0; } } else if(rx == 'd') {// Direction rx = readRx(); dir = rx; } else if(rx == 's') {// Vitesse rx = readRx(); rx -= '0'; if(rx >= 5 && rx <= 100) speed = rx; } } //Les servomoteurs utilisés supportent l'envoi d'impulsions toutes les 50/60ms // On sélectionne la sortie des servos 'cuisses' sur le MAX394 setMAX394CommandHigh(); // On envoie les impulsions de commande des servomoteurs 'cuisses' servosPulses(cuisse, cuisse_sens, cuisse_calib); wait10ms(); wait10ms(); wait10ms(); // On sélectionne la sortie des servos 'jambes' sur le MAX394 setMAX394CommandLow(); // On envoie les impulsions de commande des servomoteurs 'jambes' servosPulses(jambe, jambe_sens, jambe_calib); wait10ms(); wait10ms(); } // end while(1) } void servosPulses(signed char k[6], const signed char k_sens[6], const signed char k_calib[6]) { int i, j; // On met les sorties de commande des servomoteurs à l'état haut SetAllServosHigh(); wait500us(); // La boucle dure environ 1.6ms for(i=0;i<32;++i) { wait50us(); for(j=0;j<6;++j) { if(k[j]*k_sens[j]+k_calib[j] == i) { // On met la commande du servomoteur à l'état bas, si l'impulsion a durée assez longtemps. setServoLow(j); } } } // On vérifie que toutes les sorties de commande sont à l'état bas SetAllServosLow(); }