Site ELEC344/ELEC381

Partie interactive du site pédagogique ELEC344/ELEC381 de Télécom ParisTech (occurrence 2010).

Catégories

Wheely deuxième vidéo

Voici une deuxième vidéo illustrant l’état d’avancement du robot wheely.
Il nous reste encore à améliorer en priorité l’asservissement en position et l’interface de pilotage du robot.

Heliokter : premiers vols

Depuis le dernier post, nous essayons de customiser notre PID pour parvenir à un résultat en vol meilleur. Le gros problème, c’est que c’est dur de définir ‘meilleur’, l’hélicoptère bouge forcément un peu, c’est difficile de le laisser completement libre dans la salle mais dès qu’on le contraint, on modifie son comportement …

Vendredi et samedi, on a quand même réussi à caractriser un comportement oscillatoire de grande période et grande amplitude, vraisemblablement dû à l’intégrale du PID : Si le le mouvement a tendance à être sinusoïdal, l’intégrale est aussi sinusoïdale mais déphasée de π/2, ce qui entretient voire amplifie les oscillations.

Pour remédier à cela, Alexis nous a conseillé une méthode de remise à zéro de l’intégrale dès que l’erreur change de signe pour eviter ce phénomène. Nous avions essayé d’autres méthodes de remise à zéro de l’intégrale avant mais celle-ci semble bien marcher.

Nous avons donc pu avoir, hier soir, un vol qu’on peut considérer comme stable d’une quinzaine de secondes, visible là :

L’hélicoptère descend lentement, c’est normal, il n’y a pas encore d’asservissement en altitude et  nous avons préféré le faire descendre plutôt que de le faire monter !

Voilà, sinon comme vous pouvez l’entendre, on gagne haut la main le prix du projet le plus bruyant ! :p

Alors evidemment, ce n’est pas parfait, mais c’est vrai qu’on a du mal  à savoir si on peut mieux faire et comment, et comment le savoir !

Heliokter et le yaw-yaw


Dans la journée de jeudi, on s’est finalement fixé sur un meilleur protocole que la planche qui roule (qui changeait un peu l’inertie du copter et qui n’etait pas vraiment no plus une rotation pure autour de l’axe), en tenant juste l’Heliokter avec ses mains (mais c’est peut-être plus dangereux et certainement plus fatiguant). En mettant ses pouce et index en anneau autour des deux branches d’un axe, on laisse donc au copter une liberté totale autour de cet axe et seulement autour de cet axe, sans modifier le bras de levier des moteurs.

Cette histoire de bras de levier devient très gênante puisque dès qu’on veut tester l’hlicoptere en vol à quelques centimètres au dessus du sol (ou de la table en l’occurence), et qu’un des pieds vient toucher le sol, le copter se bloque sans essayer de revenir à l’horizontal (enfin il essaye mais la table le bloque). Maintenant que j’y pense, il faudrait en fait poser son ventre sur un truc assez haut et assez fin pour ne pas que les pieds entravent son mouvement et lui laisser une plus grande liberté … Si le truc en question est un tuyau (genre PVC), on peut même faire passer la ficelle dedans, comme sur ce schéma :

Grâce à ça, on a pu régler jeudi de manière assez correcte l’asservissement en pitch et roll (tangage et roulis). Après une perturbation, il revient au bout de deux oscillations àa sa position stable. Nous avions aussi un problème d’erreur statique que nous n’arrivions pas à corriger avec notre coefficient intégral puisque l’intégrale était en fait remise à zéro periodiquement. Samuel nous a conseillé d’utiliser une intégrale bornée qui a le mérite de de ne pas diverger tout en jouant correctement son rôle d’intégrale. La mise à jour du calcul de l’intégrale (une ligne de code) a révélé une sorte de bug a priori inexplicable qui nous a necessité plusieurs heures. C’est soit un problème de pile, soit un problème d’initialisation (on a pu le corriger en mettant une fois l’intégrale à zéro quelques secondes après le reset) …

Et jeudi soir, on a essayé l’asservissement en lacet (yaw en anglais, cf. titre hilarant), sans grand succès pour le moment, l’hélico tourne sans jamais s’arreter bien que les coefficients intégral et dérivé soient nuls …

J-8 Heliokter l'indomptable

Mardi

Comme on a travaillé ensemble avec FX je ne détailles pas les parties déjà explicitées, mais mardi, après avoir réglé des problèmes d’orientation et de précédence dans les angles, nous avons commencé à asservir l’Heliokter en angle, ce n’est pas une partie de gâteau, pour avoir un protocole de test cohérent, nous avons avec FX tracé les courbes correspondant aux erreurs du PID, les commandes des moteurs et l’évolution des différents angles. Nous avions trouvé une façon de limiter les mouvements à un seul degré de liberté, mais la technique consistant à attacher une des pattes de l’engin s’est révélée inadaptée dans la mesure où cela introduit une dissymétrie dans les bras de levier des moteurs que l’on voulait asservir.

Mercredi

Idem on a travaillé ensemble avec FX, grâce à la modification du code nous permettant de régler les coefficients à la volée et la mise en place d’un nouveau protocole pour avoir un degré de liberté mais sans les problèmes de mardi, ( Heliokter scotché à une plaque de polystyrène posée sur un cylindre de bois sous le centre de gravité ), nous avons constaté que le réglage du double asservissement restait très délicat, de plus les mesures des gyros étaient inexploitable car variant trop vite. Donc sur les conseils d’Alexis nous avons fait deux choses :

  • Filtrer les gyros sur un centième de seconde, ( Un+1 = alpha * Un + (1-alpha)*gyro où alpha = tau/(dt+tau) avec dt temps entre deux échantillons de gyros ( 5 ms chez nous ) et tau 1/fréquence de coupure donc tau = 10 ms chez nous ) .
  • Schinter le deuxième asservissement pour les régler l’un après l’autre.

Dans la soirée nous ne réussissions plus à démarrer les moteurs, nous avons perdu un bout de temps pour nous rendre compte que lors d’un merge malheureux, une version vieille de l’i2c avait écrasé la version fonctionnelle ( et que la numérotation du versionnage par hg peut être trompeuse ). Après ça, peu de temps avant les derniers métro, nous avons eu un asservissement décent en roulis et tangage à la fois, prochaine étape : régler le lacet et l’altitude.

A toute vitesse

Ces derniers jours, tout le groupe s’est acharné à faire rouler le robot à nouveau, puisqu’après les exploits de dimanche nous sommes allés de déboire et déboire. D’abord, un bug d’initialisation du gyro nous a empéché d’avancer une journée entière, avec un robot qui ne tenait plus du tout. Ensuite, grâce à des conseils avisés d’Alexis qui a beaucoup travaillé sur nos filtres, nous avons grandement amélioré le filtre, en introduisant un filtre de Kalman en plus de notre filtre complémentaire, pour annuler le drift du gyroscope de façon satisfaisante. Le filtre de Kalman étant moins performant en translation, nous avons néanmoins conservé nos calculs.

Nous avosn pu commencer un début d’asservissement en x, encore beaucoup de tests en perspective…

En paralléle, Fabien a fait un script pour afficher les courbes en temps réel, ce qui nous a permis de faire des acquisitions et de nous rendre compte plus facilement de nos réussites et de nos problèmes. Ainsi, la vitesse en x est toujours nulle (on s’y attèle très vite).

Par contre, les résultats pour la vitesse angulaire sont assez bon, au vu de nos premier tests :

La courbe bleue représente la vitesse angulaire calculée par le robot (décalée pour les besoins de l’observation), alors que la verte donne la dérivée de l’angle.

Heliokter bourdonne

J’ai passé la fin des vacances sur le filtre de Kalman, pour l’affiner/le corriger. Je me suis rendu compte avec la carte IMU que je ne prenais en fait pas en compte les gyroscopes et que je me basais seulement sur l’accéléromètre/magnétomètre (l’accéléromètre étant excentré par rapport au centre de rotation de la carte IMU, on s’en aperçoit mieux). Le modèle cinématique de mise à jour de létat en fonction des gyros était en fait légèrement erroné (une petite matrice par-ci …).

Bon bref j’ai réussi à regler tout ça et avoir des resultats corrects et sans lag.

Hier, on a commencé à intégrer tout le code et à contrôler les moteurs correctement avec l’I2C, mais je laisse les autres détailler, je n’etais pas là l’après-midi …

Et aujourd’hui, c’etait au tour de l’asservissement d’être integré dans le code et testé. Il a fallu évidemment faire face aux éternels problème d’orientation  (argh, la carte est à 45° par rapport à l’avant de l’Helico !) et d’angles, en passe d’être résolu ce soir.

Fernando et Miguel s’occupent d’un protocole de communcation par ZigBee ou Bluetooth (compatible avec celui de Mikrkopter) qui nous permettra de régler les coefficients du PID de manière dynamique (sans avoir à reflasher à chaque fois). Je leur laisse le soin de détailler.

Voilà une petite vidéo du rodéo actuel, avec un asservissement inexact et trop fort.

Améliorations pour le code de Wheely

Cette nuit, Alexis nous a communiqué le fruit de ses recherches sur le filtrage. Sa conclusion : Le filtre complémentaire que nous utilisions est très bon une fois qu’on lui soustrait le drift, mesuré à l’aide d’un filtre de Kalman tournant en parallèle.

Avec l’aide du code fourni par Alexis, j’ai intégré le Kalman et la soustraction du drift. Le code d’Alexis m’a par ailleurs inspiré sur pas mal de points, en terme de propreté, donc j’en ai profité pour faire une bonne petite refonte du code de Wheely, avec des variables plus claires, et une API pratique et compréhensible.

Cet après midi nous avons testé le code. Après que nous ayons corrigé quelques coquilles, nous obtenons sans l’asservissement sur X des résultats plutôt satisfaisant. Le robot tient assez bien sur place, à condition que le léger offset sur l’angle soit bien réglé à la main. Mais il est assez stable, autour de 0.6°, désormais codé en dur.

Difficile pour l’instant de bien contrôler les effets d’un asservissement sur X lorsque nous l’introduisons.

Impasse

Voilà trois jours que je travaille à améliorer le contrôle des moteurs mais je suis dans une impasse.

Le problème se situe à basse vitesse, à vitesse moyenne et haute, la mesure en vitesse des encodeurs est bien proportionnelle à la commande en PWM.

Les moteurs font des à-coups à basse vitesse et ne commencent à bouger que lorsque le duty cycle dépasse une valeur d’environ 1300 / 16384.  Malheureusement, ce seuil et le comportement du moteur à basse vitesse dépendent directement du voltage en entrée du pont en H et ce de façon assez sensible.

J’ai alors réfléchi à un asservissement. Problèmes : les encodeurs n’ont pas une définition excellente (360 tic / tours) et donc je ne peux mesurer la vitesse des roues que sur des intervalles relativement long et de fait, les corrections agissent comme des à-coups qui viennent s’ajouter à ceux dus à la mécanique des moteurs… Et les moteurs présente également des phénomène d’hystérésis (si j’applique une commande de  1300/16384 alors que le robot vient de s’allumer, les roues ne bougent pas. Si je mets le robot en route et fait décroitre la commande en PWM jusqu’à 1300/16384… Le moteur bouge…

Donc au final, je vois pas trop ce que je peux faire mais ceci étant dit, je pense que le Wheely ne tient pas debout à cause du bruit dans les capteurs engendré par le mouvement et non par les moteurs : on peut trouver sur internet pleins d’exemple de robot en équilibre en Légo Mindstorm et ceux-ci ont des moteurs dont les caractéristiques sont bien pires que les notres.

Et quand ça n'avance pas...

Après avoir laissé vendredi soir Wheely tout juste monté, nous avons pu partir contents de nous pour un week-end bien mérité.

En rentrant, j’ai fait quelques tests sur le LQR pour remarqué quelques problémes bètes, en particulier un problème de saturation qui faisait repartir le robot dans le mauvais sens quand il était trop penché… Après quelques heures de tests, nous avons décidé de commencer par essayer de faire rouler Wheely grâce à un PID sur l’angle, sans l’asservir du tout en position, puisque cela ne faisait que compliquer la recherche pour le moment.

Lundi, nous avons donc refait toue une série de tests tous ensemble (puisque les avions ne volent plus…) sur le PID et Fabien a rajouté un étage au robot pour monter encore le centre de gravité.

Mardi, je me suis penchée sur le problème de la mise en flash du code. Daniel y travaillait déjà sans succés, donc on s’est dit qu’un autre essai ne ferait pas de mal. Après de longues recherches et un certain désespoir, j’ai fini par découvrir que notre problème venait d’une mauvaise initialisation de la flash, que nous faisions trop tard (ie après que l’horloge soit réglée sur la PLL et non avant). Sam a aussi remarqué un warning du compilateur qui nous a poussé à faire une petite modification dans le ld script.

Ce matin, j’ai donc modifié le hardware_init et le ld script pour toutes les cartes, ainsi tous nos codes seront harmonisés, en vérifiant bien que ce code fonctionnait aussi bien en flash qu’en ram, et que toutes les tâches fonctionnent toujours correctement (vu les problèmes rencontrés par les autres gruopes, je m’attendais à tout!). J’ai fait tous les tests qu’il me semblait possible de faire sur la carte propulsion et je n’ai rencontré aucun problème majeur.

Ne reste plus qu’à faire rouler Wheely maintenant…

J-22

Alors comme ça fait plusieurs jours que je n’ai pas mis à jour voici ce que j’ai fait durant les derniers jours :

Jeudi 15

J’ai amélioré mon code de benchmark, voici des résultats plus extensifs sur les opérations :

.

.

Math timings double Timing ( µs ) Cycles

.

offset add mul div cos sin abs sqrt abs

.

offset 14492 14.492 1043.424

.

add 28827 28.827 2075.544

.

mul 28823 28.823 2075.256

.

div 28826 28.826 2075.472

.

cos 14464 14.464 1041.408

.

sin 14487 14.487 1043.064

.

offset abs 14463 14.463 1041.336

.

sqrt(abs()) 41676 41.676 3000.672

.

Math timings floats

.

offset add mul div cos sin abs sqrt abs

.

offset 14501 14.501 1044.072

.

add 28826 28.826 2075.472

.

mul 28855 28.855 2077.56

.

div 28855 28.855 2077.56

.

cos 14473 14.473 1042.056

.

sin 14492 14.492 1043.424

.

offset abs 14501 14.501 1044.072

.

sqrt(abs()) 42032 42.032 3026.304

Je ne suis pas vraiment satisfait car les résultats ne me semblent pas vraiment cohérents, mais peut être est-ce à cause de l’influence de divers paramètres pas controlés : cache, optimisation, autre.

Explication de la méthode : le temps à gauche représente le temps d’exécution pour 1000 opérations en µs.
offset : temps d’exécution d’une fonction pour avoir une nombre alétatoire double ou float( mul, add, modulo, sauvegarde dans une variable double ou float static ), fonction appelée myrandd() ou myrandf()
add/mul/div : 2 appels à myrand, 2 cast int->float ou double, addition/mulitplication/division des deux nombres et sauvegarde dans une variable float/double.
cos/sin : 1 appel à myrand(), 1 cast, 1 sauvegarde
offset abs : 1 appel à abs( myrand( ) ) 1 cast et 1 sauvegarde
sqrt : 1 appel à sqrt(abs(myrand( ))) 1 cast et 1 sauvegarde

Le problème est que l’on ne peut pas vraiment soustraire l’offset des opérations … car le temps d’exécution n’est pas vraiment linéaire. Un autre point qui m’étonne est le nombre de cycles qu’il faut pour faire une simple addition …

Bref je vous donne les résultats as is, car j’ai du mal à les interpréter. On retiendra qu’une opération arithmétique sur un double ou un float est du même ordre de grandeur : quelques dizaines de µs.

Vendredi 16

J’ai écrit le code pour les télémètres, ce qui était assez rapide puisque j’ai juste eu à m’intégrer dans le code de FX pour les gyros. Mais comme les télémètres étaient bloqués au Royaume Uni à cause de l’arrêt du traffic aérien, une fois mon code fini, j’en ai profité pour me mettre à jour sur le code qu’avaient écrit les autres.

J’ai aussi commencé à nettoyer mon code et à agencé un peu l’architecture globale du code.

Samedi , Dimanche, Lundi 19

Vacances

Mardi 20

Retour en A405 par une magnifique journée de printemps. Aujourd’hui j’ai écrit le squelette de notre version propre alpha pour l’heliokter avec des prises de sémaphore bien claires, des delays là où il faut et une beau découpage en fichier pour la compilation séparée. Ca m’a pris un peu de temps dans le sens où j’ai du lire le code un peu de tout le monde et réfléchir à comment wrapper les fonctions de chacun pour ne pas avoir à réécrire trop de choses.

Ensuite comme les télémètres étaient arrivés, j’ai pu vérifier mon code, qui marchait du premier coup ! ( merci FX ) ensuite j’ai pu étalonner le télémètre et maintenant je récupère une distance en cm. Celle-ci est bonne pour une distance entre 8cm et 70 cm environ ( comme nous n’avions qu’une règle de 30 cm ( merci Flavia ) , les mesures au delà de 30 cm n’étaient pas très précises).

La méthode pour étalonner : regarder la valeur en « int12″ renvoyée par l’ADC à 8 cm, celle renvoyée à 30 cm, regarder la courbe sur la datasheet qui ressemble fortement à a/x+b, ensuite trouver a et b à partir de ces deux points. Ci dessous le résultat sous forme de courbe ( axes : distance en cm )

  • En rouge : y=x
  • En vert : y= f(x) où f est la fonction d’interpolation. Dans l’idéal elle devrait donner l’identité.

C’est assez satisfaisant !

Mercredi 21

Aujourd’hui on a réussi à avoir une version « sale » mais complète du code qui compile ( c’est à dire senseurs, filtrage, asservissement et moteurs ).

On a ensuite fait un vol d’essai pour faire une acquisition et un test du filtre de Kalman depuis notre propre carte. Ce qu’on a constaté dans ce premier vol et qui m’inquiète un peu c’est que les angles mis en jeu pour le roll et le pitch sont très faibles, je me demande aussi l’amplitude des valeurs utilisées par les moteurs ( +- 3 sur un unsigned char ou bien +- 100 ? ).