Tutoriel · Intermédiaire · 35 min de lecture
Cinématique des roues mecanum : quatre roues, trois axes
Dérivez la cinématique inverse d'une base mecanum depuis un galet à 45°, inversez-la pour l'odométrie, et la quatrième équation détecte le patinage.
Un galet décide de tout
Démontez une roue mecanum du robot et tenez-la. Elle tourne sur son essieu comme toute roue. Autour de sa jante se tiennent huit ou neuf galets en tonneau, chacun sur son petit essieu, chacun libre de tourner, et chacun à 45° de l’essieu de la roue.
Demandez maintenant quel effort cette roue peut mettre dans le sol.
Elle ne peut pas pousser le long de la direction de roulement du galet — le galet tourne et rien n’est transmis. Elle ne peut que pousser le long de l’essieu du galet, la seule direction où le galet refuse de rouler. Donc l’effort au sol de la roue est verrouillé sur une seule ligne au sol, à 45° du châssis, et aucun couple moteur ne change laquelle.
Voilà tout le mécanisme. Tout ce qui suit est de la comptabilité.
La contrainte, roue par roue
Posez le repère robot usuel : x vers l’avant, y vers la gauche du robot, ω antihoraire. C’est la convention ROS, et en utiliser une autre signifie se battre contre chaque bibliothèque jamais touchée.
Posez la roue i en position (xᵢ, yᵢ) dans ce repère, et donnez-lui une main de galet sᵢ = ±1 disant laquelle des deux lignes à 45° ses galets suivent. Alors la vitesse de jante vᵢ de la roue (en mètres par seconde au pneu, pas en radians) se relie à la torsion du châssis par :
vᵢ = vx + sᵢ·vy + (sᵢ·xᵢ − yᵢ)·ω
Trois termes, et chacun vaut une phrase :
- vx — chaque roue contribue pleinement au mouvement avant, quelle que soit sa main. C’est pourquoi l’avant sur une base mecanum semble parfaitement normal.
- sᵢ·vy — le mouvement latéral entraîne la moitié des roues dans un sens et l’autre moitié dans l’autre. La main décide quelle moitié.
- (sᵢ·xᵢ − yᵢ)·ω — le bras de levier. Notez que ce n’est pas juste la voie ; la main du galet est dedans, et c’est pourquoi une roue dans le mauvais coin ruine la rotation autant que le ripage.
Pour passer de la vitesse de jante à ce que vous commandez vraiment à un moteur, divisez par le rayon de roue : ωroue = vᵢ / r, en rad/s.
Quatre roues : la matrice
Empilez ces quatre lignes et vous avez une matrice 4 × 3 A, avec la torsion du châssis ξ = (vx, vy, ω) :
v = A · ξ
Pour la disposition standard — avant-gauche et arrière-droite d’une main, avant-droite et arrière-gauche de l’autre — écrivez k = lx + ly où lx est la moitié de l’empattement (avant à arrière) et ly la moitié de la voie (gauche à droite). Alors :
| Roue | vx | vy | ω |
|---|---|---|---|
| Avant gauche | 1 | −1 | −k |
| Avant droite | 1 | +1 | +k |
| Arrière gauche | 1 | +1 | −k |
| Arrière droite | 1 | −1 | +k |
Soit les quatre lignes que tout le monde écrit :
float fl = vx - vy - K * omega;
float fr = vx + vy + K * omega;
float rl = vx + vy - K * omega;
float rr = vx - vy + K * omega;
Vérifiez-la contre les quatre choses que vous savez déjà que fait un robot mecanum :
| Commande | Vitesses des roues (AVG, AVD, ARG, ARD) | Ce que vous voyez |
|---|---|---|
| Avant, vx = 1 | 1, 1, 1, 1 | Les quatre pareilles. Les parties latérales s’annulent. |
| Ripage gauche, vy = 1 | −1, 1, 1, −1 | Les paires diagonales s’opposent. C’est le tour de magie. |
| Pivot antihoraire, ω = 1 | −k, k, −k, k | Côté gauche en arrière, côté droit en avant — un virage char ordinaire. |
| Diagonale, vx = vy = 1 | 0, 2, 2, 0 | Deux roues s’arrêtent complètement. Retenez cela. |
L’exemple calculé
Une base avec lx = 75 mm, ly = 85 mm, roues de 97 mm (r = 48,5 mm), à qui on demande vx = 0,30 m/s, vy = 0,20 m/s, ω = 0,50 rad/s. Donc k = 0,160 m.
| Roue | Calcul | Vitesse de jante | Vitesse moteur |
|---|---|---|---|
| Avant gauche | 0.30 − 0.20 − 0.160 × 0.50 | 0.020 m/s | 0.41 rad/s (3.9 rpm) |
| Avant droite | 0.30 + 0.20 + 0.160 × 0.50 | 0.580 m/s | 11.96 rad/s (114 rpm) |
| Arrière gauche | 0.30 + 0.20 − 0.160 × 0.50 | 0.420 m/s | 8.66 rad/s (83 rpm) |
| Arrière droite | 0.30 − 0.20 + 0.160 × 0.50 | 0.180 m/s | 3.71 rad/s (35 rpm) |
Vingt-neuf fois la vitesse sur une roue contre une autre, pour une commande qui n’a pas l’air extrême. Une transmission mecanum passe l’essentiel de sa vie avec ses quatre moteurs faisant des choses follement différentes, et c’est pourquoi « elles reçoivent toutes la même PWM et ça roule droit » n’est pas un test qui signifie quoi que ce soit ici.
Saturation : mise à l’échelle, jamais d’écrêtage
Demandez plus que n’ont les moteurs et il faut abandonner quelque chose. Il y a exactement deux choix et ils ne sont pas équivalents.
float peak = max(max(fabs(fl), fabs(fr)), max(fabs(rl), fabs(rr)));
if (peak > V_MAX) { // SCALE: keep the direction, lose the speed
float s = V_MAX / peak;
fl *= s; fr *= s; rl *= s; rr *= s;
}
contre la chose qui arrive si vous écrivez juste constrain(fl, -255, 255) sur chaque moteur : l’écrêtage. L’écrêtage garde la vitesse et jette la direction. Demandez 0,60 m/s avant, 0,15 latéral et 0,8 rad/s de pivot sur un robot dont les roues plafonnent à 0,50 m/s :
| Cap | Avant | Pivot | Verdict | |
|---|---|---|---|---|
| Commandé | 14.0° | 0.60 m/s | 0.80 rad/s | — |
| Échelle | 14.0° | 0.34 m/s | 0.46 rad/s | Plus lent sur chaque axe, exactement où vous pointiez |
| Écrêtage | 5.6° | 0.46 m/s | 0.28 rad/s | Plus vite avant, 8,4° hors cap, et 40 % de pivot en moins |
Le plus vicieux est que l’écrêtage semble bien sur les commandes testées. Une diagonale pure demande 2 à deux roues et 0 aux deux autres ; les zéros sont déjà dans la limite, donc écrêtage et échelle produisent des nombres identiques et le bogue se cache. Il apparaît plus tard, sous un suiveur de trajectoire, comme un robot qui s’écarte des virages sans raison trouvable. Regardez les deux limiteurs sur la même course et la différence est une courbe visible.
En arrière : l’odométrie
La cinématique directe est la même matrice lue dans l’autre sens. Mais A fait 4 × 3 — quatre mesures, trois inconnues — donc il n’y a pas d’inverse. Il y a une solution aux moindres carrés :
ξ = (Aᵀ A)⁻¹ Aᵀ v
Pour la disposition standard, AᵀA sort diagonale et le tout s’effondre en trois lignes mettables sur un Arduino :
vx = (fl + fr + rl + rr) / 4.0;
vy = (-fl + fr + rl - rr) / 4.0;
omega = (-fl + fr - rl + rr) / (4.0 * K);
Intégrez cela via le cap du robot à chaque tic et vous avez de l’estime :
float dt = (now - last) * 1e-6f;
theta += omega * dt;
x += (vx * cosf(theta) - vy * sinf(theta)) * dt;
y += (vx * sinf(theta) + vy * cosf(theta)) * dt;
Ce qui marche exactement aussi bien que l’hypothèse dessous — que les roues ne patinent pas. Sur une base mecanum, le patinage est le principe de la roue, donc traitez l’odométrie latérale comme le nombre le moins fiable du robot.
La quatrième équation que personne n’utilise
Quatre équations, trois inconnues. Cette équation en trop n’est pas du gaspillage — c’est le seul diagnostic gratuit de toute la transmission.
Si le robot est rigide et que chaque roue accroche, les quatre vitesses de jantes ne peuvent pas être arbitraires. Additionnez les deux premières lignes et les deux dernières de la matrice et les deux font 2·vx, donc :
FL + FR = RL + RR
toujours, exactement, pour tout mouvement qu’un châssis mecanum rigide peut exécuter. Calculez donc le résidu à chaque boucle :
float residual = (fl + fr) - (rl + rr); // should be ~0 forever
Non nul signifie que les quatre encodeurs décrivent un mouvement qu’aucun objet solide ne peut faire. Causes, par ordre approximatif de probabilité : une roue en l’air, une roue sur une flaque, un galet grippé, un accouplement qui patine, une voie d’encodeur morte — ou une roue montée dans le mauvais coin, ce qui rend le résidu grand et constant plutôt que par à-coups.
Soyez honnête sur ce qu’il ne peut pas voir : si les quatre roues patinent pareil en même temps, le résidu reste à zéro et le robot glisse latéralement hors cap dans un silence total. Cette cécité est exactement pourquoi l’odométrie mecanum échoue discrètement au lieu de bruyamment, et pourquoi les montages sérieux ajoutent une IMU ou des roues folles instrumentées plutôt que de croire les encodeurs de traction.
Les deux conséquences du 45°
Les deux tombent de la matrice, et aucune ne s’achète.
Le mouvement diagonal est limité à 1/√2. Une commande à 45° met vx + vy sur deux roues et 0 sur les deux autres, donc la paire occupée atteint la limite de vitesse quand le robot ne roule qu’à V_MAX / √2 ≈ 71 % de sa vitesse de pointe en ligne droite. Des moteurs plus rapides déplacent le plafond ; ils ne le suppriment pas.
La traction est limitée à 1/√2 aussi, pour une autre raison. L’effort moteur utile F se tient le long de la direction de roulement, mais l’effort réel au sol vaut F·√2 le long de l’essieu du galet, et le frottement plafonne cela. Donc par roue l’effort utile est plafonné à μN/√2, et le plafond d’accélération du robot est :
a_max = μ·g / √2
Sur μ = 0,85 cela fait 5,9 m/s², là où le même robot sur roues simples aurait 8,3. La masse du robot s’annule des deux côtés, donc le lest n’aide pas — une leçon exactement opposée à un robot sumo, où le poids est toute la stratégie.
En micrologiciel
Mettez la géométrie à un endroit et ne la laissez jamais taper deux fois.
struct Mecanum {
float lx, ly, r; // half wheelbase, half track, wheel radius (m)
float k() const { return lx + ly; }
// twist -> four wheel angular velocities, rad/s
void inverse(float vx, float vy, float w, float out[4]) const {
out[0] = (vx - vy - k() * w) / r; // FL
out[1] = (vx + vy + k() * w) / r; // FR
out[2] = (vx + vy - k() * w) / r; // RL
out[3] = (vx - vy + k() * w) / r; // RR
}
// four measured wheel angular velocities -> twist, plus the residual
float forward(const float in[4], float &vx, float &vy, float &w) const {
float fl = in[0] * r, fr = in[1] * r, rl = in[2] * r, rr = in[3] * r;
vx = (fl + fr + rl + rr) * 0.25f;
vy = (-fl + fr + rl - rr) * 0.25f;
w = (-fl + fr - rl + rr) * 0.25f / k();
return (fl + fr) - (rl + rr); // the free diagnostic
}
};
Mesurez lx et ly de centre de roue à centre de roue, puis divisez par deux — pas la plaque du châssis, généralement plus large. Se tromper de k n’empêche pas le robot de rouler ; cela fait saigner rotation et translation l’une dans l’autre, donc une commande de pivot pur rampe et un ripage pur courbe. Si votre robot rampe en pivotant sur place, suspectez k avant le code.
Dépannage
| Symptôme | Cause probable | Quoi vérifier |
|---|---|---|
| Avant bien, ripage qui courbe et pivote | Une roue dans le mauvais coin | Le contrôle X ci-dessus ; le résidu sera grand et stable |
| Ripe dans le sens opposé à la commande | Les quatre roues en miroir | Permutez la paire gauche avec la paire droite |
| Pivoter sur place translate aussi | k faux, ou lx/ly mesurés sur le châssis au lieu des roues | Remesurez de centre de roue à centre de roue |
| Diagonale bien plus lente qu’avant | Le plafond 1/√2 — pas une panne | Rien à corriger ; concevez avec |
| Le robot s’écarte des virages sous suiveur de trajectoire | Écrêtage par moteur au lieu de mise à l’échelle | Mettez toute la commande à l’échelle par la pire roue |
| Odométrie qui dérive vite en latéral, bien en avant | Patinage des galets, fonctionnement nominal | IMU pour le cap, roues instrumentées pour la position |
| Résidu qui pique sur un seul sol | Cette zone glisse, ou un galet est grippé | Tournez chaque galet à la main |
Suite
Faites tourner les nombres contre un robot qui peut patiner : le simulateur de traction mecanum enfonce ces équations exactes dans un modèle de frottement, pour voir l’odométrie et le sol en désaccord. Puis faites tenir aux roues les vitesses calculées — commande de vitesse des roues en boucle fermée — et faites signifier « avant » avant sur le sol plutôt que sur le robot avec la commande orientée terrain.
Explorer le graphe
Fait partie de ces réalisations
Les projets et parcours d'apprentissage qui incluent ce tutoriel.
Pour aller plus loin