Tutoriel · Intermédiaire · 20 min de lecture

Odométrie différentielle depuis les encodeurs de roues

Calculez la position d'un robot différentiel depuis les encodeurs gauche et droit, traitez les trajets droits et courbes, et mesurez la dérive d'odométrie.

Un robot à entraînement différentiel a deux roues motrices indépendantes séparées par une voie. Quand les deux roues parcourent la même distance, le robot va droit. Quand l’une parcourt plus, le châssis suit un arc. L’odométrie intègre ces petits mouvements au fil du temps pour estimer la pose : position horizontale x, position verticale y, et cap θ.

L’estimée est locale et cumulative. Elle part d’une pose choisie et ne peut se corriger seule depuis les seuls encodeurs de roues. Les petites erreurs de diamètre de roue, de voie, de temporisation et de patinage s’accumulent, donc le but est une estimée utile à court terme avec des limites mesurées — pas une vérité terrain parfaite.

Définissez une convention de coordonnées

Choisissez un repère avant d’écrire des équations. Une convention bidimensionnelle courante est :

  • x pointe vers l’avant depuis la pose de départ.
  • y pointe à gauche.
  • θ positif tourne dans le sens antihoraire.
  • Les distances des roues gauche et droite sont positives quand le robot avance.

Stockez les angles en radians en interne. Convertissez en degrés seulement pour l’affichage. Normalisez le cap si des valeurs bornées aident le débogage, mais sinus et cosinus fonctionnent aussi avec des angles non bornés.

Soient dL et dR les distances des roues gauche et droite depuis la mise à jour précédente, et b la distance effective entre les lignes de contact des roues.

Géométrie d'un robot à entraînement différentiel tournant le long d'un arc. Un point marqué CCI, le centre instantané de courbure, se tient à gauche avec des lignes de rayon en pointillés rejoignant le robot au début et à la fin du virage. Trois arcs concentriques balayent de la pose de départ à la pose d'arrivée : le trajet de la roue intérieure, le trajet du centre du robot en pointillés, et le trajet plus long de la roue extérieure. Une double flèche courbe entre les deux lignes de rayon marque le changement de cap delta thêta. Les formules indiquent que delta thêta égale la différence des deux distances de roues divisée par l'écartement des roues, et que la distance du centre est leur moyenne.
Les deux roues balayent le même angle autour du même centre, donc la différence de leurs parcours est le changement de cap, et leur moyenne est la distance parcourue par le robot. Télécharger le SVG

Convertissez les impulsions en mètres

Tout ce qui précède suppose que vous avez déjà dL et dR en mètres. Les encodeurs ne produisent pas des mètres ; ils produisent des impulsions. Une conversion se tient entre les deux, et la rater met à l’échelle chaque nombre que le reste de cette page calcule.

countsPerWheelTurn = PPR x edgesDecoded x gearRatio
metresPerCount     = (pi x wheelDiameter) / countsPerWheelTurn

edgesDecoded vaut 4 seulement si vous décodez les deux fronts des deux voies — la quadrature complète. Décoder le seul front montant d’une voie donne 1, et cela jette aussi l’information de sens, donc le robot ne peut pas distinguer une marche arrière d’une marche avant.

Encodeur et réducteur Impulsions par tour de roue Résolution sur une roue de 65 mm
N20, 7 PPR magnétique, 100:1 2,800 73 um
N20, 7 PPR magnétique, 50:1 1,400 146 um
11 PPR magnétique, réducteur 30:1 1,320 155 um
Disque optique 20 fentes, une voie, sur la roue 20 10.2 mm

La dernière ligne est celle qui surprend. Un disque à fentes fixé à la roue coûte peu, mais 10 mm de quantification signifient qu’un déplacement de 200 mm est connu à environ 5 %, et aucun étalonnage ne récupère une information que le capteur n’a jamais saisie. Le tutoriel sur les encodeurs en quadrature couvre tout le côté décodage.

La résolution est un plancher de précision, pas une promesse d’exactitude. Une impulsion de 73 µm ne signifie rien si le diamètre de roue est faux de 2 %.

Calculez le mouvement avant et angulaire

Le centre de l’essieu avance de la distance moyenne des roues :

dCenter = (dR + dL) / 2

Le cap change selon leur différence :

dTheta = (dR - dL) / b

Des distances égales font dTheta = 0. Des distances égales et opposées font dCenter = 0, donc le robot pivote sur place autour du milieu de l’essieu.

Pour de petites mises à jour, appliquer la translation au cap médian est précis et évite un cas particulier de ligne droite :

headingMid = theta + dTheta / 2
x = x + dCenter * cos(headingMid)
y = y + dCenter * sin(headingMid)
theta = theta + dTheta

Cette intégration au point médian est assez simple pour un contrôleur embarqué et se comporte bien quand la boucle tourne fréquemment. Une intégration exacte sur l’arc est utile pour de grands pas, mais elle ne répare pas de mauvaises mesures de roues.

Implémentez une mise à jour répétable

Gardez les totaux bruts des encodeurs et convertissez leurs différences en mètres. Ne remettez pas les compteurs matériels à zéro à chaque boucle ; des instantanés facilitent le bouclage et le diagnostic.

struct Pose { float x, y, theta; };

void updateOdometry(Pose& pose, float dLeft, float dRight, float trackWidth) {
  const float dCenter = 0.5f * (dLeft + dRight);
  const float dTheta = (dRight - dLeft) / trackWidth;
  const float headingMid = pose.theta + 0.5f * dTheta;

  pose.x += dCenter * cosf(headingMid);
  pose.y += dCenter * sinf(headingMid);
  pose.theta += dTheta;
}

Appelez ceci après lecture atomique des deux encodeurs. Les instantanés gauche et droit doivent représenter presque le même instant ; un long écart entre eux crée un braquage apparent pendant un mouvement rapide.

Si un autre sous-système a besoin de la vitesse, divisez les incréments par le temps écoulé mesuré. L’intégration de pose elle-même utilise la distance, donc la gigue du temps de boucle ne change pas directement l’estimée de distance parcourue.

Lisez les deux encodeurs sans perdre d’impulsions

La fonction ci-dessus est de l’arithmétique pure. L’alimenter correctement est là où l’odométrie embarquée casse vraiment, et trois détails portent presque tout le risque.

Instantané des deux compteurs au même instant. Les routines d’interruption continuent d’incrémenter pendant que votre boucle lit. Lire la gauche, faire du travail, puis lire la droite attribue à une seule roue le mouvement survenu entre-temps — que les équations interprètent fidèlement comme un virage.

Ne remettez jamais les compteurs matériels à zéro. Des totaux en roue libre plus une valeur précédente mémorisée donnent le delta, et ils laissent l’impulsion brute disponible pour le diagnostic.

Calculez le delta en arithmétique non signée. Le dépassement signé est un comportement indéfini en C++ ; la danse de transtypages ci-dessous est définie, ne coûte rien, et reste correcte à travers le bouclage.

volatile int32_t leftCount = 0, rightCount = 0;   // written only by the ISRs

void isrLeft()  { leftCount  += digitalRead(L_CHAN_B) ? +1 : -1; }
void isrRight() { rightCount += digitalRead(R_CHAN_B) ? +1 : -1; }

constexpr float METRES_PER_COUNT = (3.14159265f * 0.065f) / 2800.0f;   // 72.9 um
constexpr float TRACK_WIDTH      = 0.152f;                             // calibrated, not measured

void odometryTick(Pose& pose) {
  int32_t nowLeft, nowRight;
  noInterrupts();                      // one instant, both wheels
  nowLeft  = leftCount;
  nowRight = rightCount;
  interrupts();

  static int32_t prevLeft = 0, prevRight = 0;
  // Unsigned subtraction wraps in a defined way; the cast back recovers the
  // signed delta even across the 2^32 boundary.
  const int32_t dLeftCounts  = (int32_t)((uint32_t)nowLeft  - (uint32_t)prevLeft);
  const int32_t dRightCounts = (int32_t)((uint32_t)nowRight - (uint32_t)prevRight);
  prevLeft  = nowLeft;
  prevRight = nowRight;

  updateOdometry(pose,
                 dLeftCounts  * METRES_PER_COUNT,
                 dRightCounts * METRES_PER_COUNT,
                 TRACK_WIDTH);
}

À 73 µm par impulsion, un compteur signé 32 bits boucle après environ 157 km, donc sur un robot de bureau le bouclage est théorique. Cela coûte deux transtypages d’avoir raison quand même, et le même motif est obligatoire sur un compteur 16 bits, qui boucle après 2,4 m.

Déroulez un virage

Supposez que la roue gauche parcourt 0,20 m, la droite 0,30 m, et une voie de 0,25 m :

  • dCenter = 0,25 m
  • dTheta = (0,30 - 0,20) / 0,25 = 0,4 rad
  • depuis un cap nul, le cap médian est 0,2 rad
  • dx ≈ 0,25 cos(0,2) = 0,245 m
  • dy ≈ 0,25 sin(0,2) = 0,050 m

Le robot finit légèrement à gauche de son axe avant d’origine et pivoté d’environ 22,9°. Vérifier un calcul à la main comme celui-ci attrape les erreurs de signe et d’unité avant que le code atteigne le matériel.

Étalonnez les deux paramètres dominants

Le diamètre effectif des roues règle l’échelle de distance. Roulez une longue ligne droite mesurée, comparez les distances annoncée et réelle, et multipliez l’échelle de diamètre par réelle / annoncée. Étalonnez chaque côté si le robot courbe malgré des commandes égales.

La voie effective règle l’échelle de rotation. Commandez plusieurs rotations complètes sur place, mesurez le changement de cap réel avec une référence extérieure, et ajustez la voie. Des tours multiples amplifient l’erreur et facilitent la mesure. Le ripage des pneus signifie que la valeur effective peut différer de l’entraxe mesuré au mètre.

Gardez les deux paramètres configurables. Le sol, l’écrasement des pneus, la charge et l’usure peuvent les changer ; les modèles matériels ont besoin de boutons d’étalonnage.

Sachez à quelle vitesse l’erreur croît

L’étalonnage ne supprime pas l’erreur, il réduit le coefficient. Connaître la taille de ce qui reste dit combien de temps faire confiance à l’estimée avant que quelque chose d’extérieur doive la corriger.

Source d’erreur Taille d’une erreur réaliste Ce qu’elle vous coûte
Échelle de diamètre de roue 1 % 10 cm sur 10 m en ligne droite
Échelle de voie 2 % 7,2° après une rotation complète
Échelle de voie 2 % 8,7 cm d’erreur de fermeture sur un carré de 1 m
Ripage systématique des roues 2 % par pivot 0,9° par virage à 90°, 45° après 50 virages
Ripage aléatoire des roues 2 % par pivot environ 6° après 50 virages, croissant en racine carrée
Intégration au point médian à 50 Hz 0,04 rad par pas à 2 rad/s 0,007 %, environ 0,07 mm par mètre

Le chiffre du carré vient de l’intégration d’un carré de 1 m avec quatre pivots à 90° sur place et rien de faux sauf la voie. Tout le reste est exact, et le robot finit quand même à 8,7 cm de son départ.

Lisez la dernière ligne contre les deux premières. Les mathématiques d’intégration ne sont jamais votre erreur dominante. Elles sont trois ordres de grandeur sous les termes d’étalonnage et quatre sous le patinage, et c’est pourquoi monter la fréquence de boucle pour chasser la précision est la mauvaise optimisation — et pourquoi la page sur le patinage des roues compte plus pour votre pose finale que tout sur cette page.

Notez aussi que les erreurs systématiques et aléatoires se comportent très différemment. Un ripage systématique s’accumule linéairement et vaut d’être étalonné. Un aléatoire croît en racine carrée du nombre de virages, donc 50 virages coûtent environ 6° plutôt que 45 — agaçant, mais qu’aucun réglage de paramètres ne corrige.

Choisissez une fréquence de mise à jour

L’arc entre deux échantillons est approximé par une corde droite au cap médian, donc l’erreur dépend du changement de cap par pas. Pour un pas de dTheta radians, la corde est courte d’environ dTheta² / 24 :

Fréquence de mise à jour dTheta à 2 rad/s Erreur de corde En pratique
200 Hz 0.010 rad 0.0004% Inutile sur ces maths
100 Hz 0.020 rad 0.002% Gratuit si la boucle tourne déjà ici
50 Hz 0.040 rad 0.007% Défaut confortable
20 Hz 0.100 rad 0.04% Bien pour un robot d’intérieur lent
10 Hz 0.200 rad 0.17% Visible sur un pivot rapide
5 Hz 0.400 rad 0.67% Trop lent pour être cru en virage

Prenez 50 Hz et n’y pensez plus. Dépensez l’attention économisée à synchroniser les deux lectures d’encodeurs, ce qui à n’importe quelle fréquence compte plus que la fréquence elle-même : un décalage de 1 ms entre les instantanés gauche et droit pendant que les roues diffèrent de 0,5 m/s invente 0,5 mm de différence de roues à partir de rien, à chaque boucle.

Ajoutez un gyroscope sans confondre les repères

Un gyroscope fournit souvent un meilleur changement de cap à court terme que la différence entre encodeurs de roues. Vous pouvez utiliser le cap du gyroscope pour theta tout en gardant la distance moyenne des roues pour la translation. Appliquez la translation à la moyenne des caps gyro ancien et nouveau.

Cela ne rend pas la position absolue. Les gyroscopes ont un biais et la distance des roues patine toujours. Un capteur global — amers de caméra, localisation lidar, ou une autre référence extérieure — est nécessaire pour corriger la pose accumulée.

En terminologie ROS, une estimée locale continue appartient naturellement au repère odom. Un système de localisation peut actualiser map → odom sans forcer la transformée localement lisse odom → base_link à sauter.

Mesurez la dérive au lieu de la masquer

Faites trois tests répétables :

  1. Roulez avant et arrière jusqu’au départ.
  2. Tournez de plusieurs tours et revenez au cap d’origine.
  3. Roulez un grand carré et comparez la pose finale à la marque de départ.

Enregistrez les deltas d’encodeurs, la pose calculée, la tension de batterie et le sol. Répétez chaque test dans les deux sens. Une erreur d’échelle cohérente suggère l’étalonnage ; une erreur qui change avec le sens suggère le jeu ou une asymétrie ; une erreur qui change avec le sol suggère le patinage.

Dépannage

Symptôme Généralement Correctif
Le robot tourne à gauche, le cap diminue Signe ou ordre des roues inversé Inversez une fois, à la frontière où entrent les impulsions
Le mouvement droit change le cap Échelles de roues différentes, impulsions manquées, ou lectures non synchronisées Étalonnez chaque côté, puis instantané des deux compteurs ensemble
La rotation est systématiquement trop grande Voie effective trop petite Multipliez la voie par réel sur commandé
Le cap ne dérive qu’à vitesse Interruptions manquées en charge Raccourcissez les ISR, ou passez le décodage en matériel
Le carré ne se ferme jamais malgré l’étalonnage Patinage et erreur d’intégration qui s’accumulent Ajoutez une correction extérieure ; aucun paramètre ne corrige ceci
La position saute après de longues courses Bouclage de compteur traité en arithmétique signée Prenez le delta en non signé, puis retranstypez
Distance juste, cap faux Voie non étalonnée, ou un gyroscope qui lutte contre les encodeurs Étalonnez sur les rotations seules, distance déjà correcte
Parfait en simulation, faux au sol Le modèle ignore déformation des pneus, jeu et temporisation des capteurs Croyez les essais sur l’établi, pas le simulateur

Essayez les conséquences sur le braquage dans le simulateur de suiveur de ligne et comparez les estimées de mouvement local aux concepts de cartographie dans les fondamentaux SLAM. Pour la tâche qui dépend le plus directement de cette estimée — parcourir des rangées parallèles sur tout un sol — voyez la planification de couverture et le simulateur de couverture.

Explorer le graphe

Fait partie de ces réalisations

Les projets et parcours d'apprentissage qui incluent ce tutoriel.

Pour aller plus loin

Références