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 :
xpointe vers l’avant depuis la pose de départ.ypointe à 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.
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 mdTheta = (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 mdy ≈ 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 :
- Roulez avant et arrière jusqu’au départ.
- Tournez de plusieurs tours et revenez au cap d’origine.
- 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.
- ProjetConstruire un Micromouse : solveur de labyrinthe
- ProjetConstruire un robot de couverture qui balaie une pièce
- ProjetConstruire un robot éviteur d'obstacles : capter, balayer
- ProjetConstruire un rover GPS à waypoints qui roule tout seul
- ProjetConstruire un suiveur de ligne : du simulateur à la piste
- Parcours d'apprentissageCommande des moteurs pour robots : un parcours d'apprentissage
- Parcours d'apprentissageSuivi de trajectoire pour robots : un parcours d'apprentissage
Pour aller plus loin