Tutorial · Mittelstufe · 35 Min.

Mecanum-Radkinematik: Vier Räder, drei Achsen

Die vierzeilige inverse Kinematik einer Mecanumbasis aus einer 45°-Rolle herleiten, für Odometrie invertieren und die vierte Gleichung für Schlupf nutzen.

Eine Rolle entscheidet alles

Ein Mecanum-Rad vom Roboter nehmen und halten. Es dreht sich auf seiner Achse wie jedes Rad. Um seine Felge sitzen acht, neun tonnenförmige Rollen, jede auf eigener kleiner Achse, jede frei drehbar, und jede 45° zur Radachse gestellt.

Nun fragen, welche Kraft dieses Rad in den Boden bringen kann.

Entlang der Rollenrollrichtung kann es nicht schieben – die Rolle dreht sich bloß und nichts wird übertragen. Es kann nur entlang der Rollenachse schieben – die eine Richtung, in die die Rolle sich weigert zu rollen. Die Bodenkraft des Rads liegt also fest auf einer einzigen Bodenlinie, 45° zum Chassis – und kein Motordrehmoment ändert welche Linie.

Das ist der ganze Mechanismus. Alles unten ist Buchhaltung.

Ein einzelnes Mecanum-Rad von der Seite über einer Bodenlinie gezeichnet. Der Reifen ist mit Rollenbalken bei 45 Grad schraffiert, beschriftet Rollen frei drehbar, Achsen bei 45 Grad. Vom Aufstandsfleck auf dem Boden zeigt ein roter Pfeil 45 Grad nach unten rechts, beschriftet Bodenkraft F Wurzel zwei, gedeckelt von mü N. Ein cyanfarbener Pfeil zeigt waagerecht entlang des Bodens, beschriftet nützliches F, gezeichnet als Waagerechtkomponente des roten Pfeils, mit gestrichelter Linie, die das rechtwinklige Dreieck schließt, und Bogen, der den 45-Grad-Winkel markiert. Eine Bildunterschrift notiert, dass Reibung den ganzen 45-Grad-Vektor deckelt, aber nur sein Waagerechtanteil den Roboter treibt – die Nutzkraft pro Rad ist also mü N durch Wurzel zwei, etwa 71 Prozent eines normalen Rads.
Die gesamte Traktionsstrafe in einem Dreieck. Die Rolle überträgt Kraft nur entlang ihrer eigenen Achse – die Bodenkraft des Rads steckt also bei 45° fest: Reibung deckelt diesen ganzen Vektor, während nur seine Waagerechtkomponente den Roboter bewegt. SVG herunterladen

Die Zwangsbedingung, Rad für Rad

Den üblichen Roboterframe aufstellen: x vorwärts, y zur Roboterlinken, ω gegen den Uhrzeigersinn. Das ist die ROS-Konvention – alles andere heißt, jede je angefasste Bibliothek zu bekämpfen.

Rad i an Position (xᵢ, yᵢ) in diesem Frame legen und ihm eine Rollenhändigkeit sᵢ = ±1 geben, die sagt, auf welcher der zwei 45°-Linien seine Rollen liegen. Dann hängt die Felgendrehzahl vᵢ des Rads (in Metern pro Sekunde am Reifen, nicht Radiant) so mit dem Chassistwist zusammen:

vᵢ = vx + sᵢ·vy + (sᵢ·xᵢ − yᵢ)·ω

Drei Terme, und jeder verdient einen Satz:

  • vx – jedes Rad trägt voll zur Vorwärtsbewegung bei, egal welche Hand. Darum fühlt sich Vorwärts auf Mecanumbasis völlig normal an.
  • sᵢ·vy – Seitwärtsbewegung treibt halbe Räder so herum und halbe andersherum. Die Hand entscheidet, welche Hälfte.
  • (sᵢ·xᵢ − yᵢ)·ω – der Hebelarm. Beachte: nicht bloß die Spurweite; die Rollenhand steckt darin – weshalb ein Rad in der falschen Ecke Drehung wie Strafen ruiniert.

Von Felgendrehzahl zu dem, was man wirklich einem Motor befiehlt, durch den Radradius teilen: ωRad = vᵢ / r, in rad/s.

Vier Räder: die Matrix

Diese vier Zeilen stapeln und man hat eine 4×3-Matrix A, mit Chassistwist ξ = (vx, vy, ω):

v = A · ξ

Für die Standardanordnung – vorne links und hinten rechts eine Hand, vorne rechts und hinten links die andere – k = lx + ly schreiben, wobei lx der halbe Radstand ist (vorne zu hinten) und ly die halbe Spur (links zu rechts). Dann:

Rad vx vy ω
Vorne links 1 −1 −k
Vorne rechts 1 +1 +k
Hinten links 1 +1 −k
Hinten rechts 1 −1 +k

Was die vier Zeilen sind, die alle schreiben:

float fl = vx - vy - K * omega;
float fr = vx + vy + K * omega;
float rl = vx + vy - K * omega;
float rr = vx - vy + K * omega;

Gegen die vier Dinge prüfen, die man von einem Mecanumroboter bereits kennt:

Befehl Raddrehzahlen (VL, VR, HL, HR) Was man sieht
Vorwärts, vx = 1 1, 1, 1, 1 Alle vier gleichherum. Die Querteile heben sich auf.
Links strafen, vy = 1 −1, 1, 1, −1 Diagonalpaare gegenläufig. Das ist der Partystück.
CCW drehen, ω = 1 −k, k, −k, k Linke Seite zurück, rechte vorwärts – eine gewöhnliche Panzerdrehung.
Diagonal, vx = vy = 1 0, 2, 2, 0 Zwei Räder stehen völlig. Das merken.
Vier Draufsichten desselben Mecanum-Roboters mit Nase oben, jede zeigt, wohin die vier Räder für einen Befehl drehen, mit Raddrehzahlen als vorne links, vorne rechts, hinten links, hinten rechts, skaliert aufs schnellste Rad. Vorwärts: plus eins auf allen vier, alle Pfeile gleichherum, notiert als Querteile heben sich auf. Links strafen: minus eins, plus eins, plus eins, minus eins, mit gegenläufigen Diagonalpaaren und bernsteinfarbenem Pfeil nach links. Gegen Uhrzeigersinn drehen: minus eins, plus eins, minus eins, plus eins, linke Seite zurück und rechte vorwärts, mit bernsteinfarbenem Bogen. Diagonal: null, plus eins, plus eins, null, mit bernsteinfarbenem Pfeil vorwärts-links und Hinweis, dass zwei Räder alles tun, während zwei nichts tun.
Die vier Befehle, geradeaus aus der Matrix. Den letzten beachten: Eine Diagonale verlangt von zwei Rädern alles und von zweien nichts – woher die 71-%-Diagonaldecke kommt. SVG herunterladen

Das durchgerechnete Beispiel

Eine Basis mit lx = 75 mm, ly = 85 mm, 97-mm-Rädern (r = 48,5 mm), verlangt vx = 0,30 m/s, vy = 0,20 m/s, ω = 0,50 rad/s. Also k = 0,160 m.

Rad Rechnung Felgendrehzahl Motordrehzahl
Vorne links 0,30 − 0,20 − 0,160 × 0,50 0,020 m/s 0,41 rad/s (3,9 U/min)
Vorne rechts 0,30 + 0,20 + 0,160 × 0,50 0,580 m/s 11,96 rad/s (114 U/min)
Hinten links 0,30 + 0,20 − 0,160 × 0,50 0,420 m/s 8,66 rad/s (83 U/min)
Hinten rechts 0,30 − 0,20 + 0,160 × 0,50 0,180 m/s 3,71 rad/s (35 U/min)

Neunundzwanzigfache Drehzahl an einem Rad gegen ein anderes – aus einem Befehl, der nicht extrem aussieht. Ein Mecanumantrieb verbringt den Großteil seines Lebens damit, dass seine vier Motoren wild Verschiedenes tun – weshalb „alle bekommen dasselbe PWM und er fährt geradeaus“ hier kein Test ist, der etwas heißt.

Sättigung: skalieren, nie clippen

Mehr verlangen, als die Motoren haben, und man muss etwas aufgeben. Es gibt exakt zwei Wahlen und sie sind nicht gleichwertig.

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;
}

gegen das, was geschieht, wenn man nur constrain(fl, -255, 255) auf jeden Motor schreibt: Clippen. Clippen behält das Tempo und wirft die Richtung weg. 0,60 m/s vorwärts, 0,15 seitwärts und 0,8 rad/s Drall verlangen an einem Roboter, dessen Räder bei 0,50 m/s deckeln:

Kurs Vorwärts Drall Urteil
Befohlen 14,0° 0,60 m/s 0,80 rad/s —
Skalieren 14,0° 0,34 m/s 0,46 rad/s In jeder Achse langsamer, exakt wohin man zeigte
Clippen 5,6° 0,46 m/s 0,28 rad/s Schneller vorwärts, 8,4° neben Kurs, und 40 % weniger Drall

Das Fieseste: Clippen sieht bei den Befehlen, die Leute testen, gut aus. Eine reine Diagonale verlangt von zwei Rädern 2 und von zweien 0; die Nullen liegen bereits in der Grenze – Clippen und Skalieren erzeugen also identische Zahlen und der Fehler versteckt sich. Er zeigt sich später, unter einem Pfadfolger, als Roboter, der sich ohne findbaren Grund aus Kurven lehnt. Beide Begrenzer im selben Lauf beobachten und der Unterschied ist eine sichtbare Kurve.

Rückwärts: Odometrie

Vorwärtskinematik ist dieselbe Matrix andersherum gelesen. Aber A ist 4×3 – vier Messungen, drei Unbekannte –, also gibt es keine Inverse. Es gibt eine Kleinste-Quadrate-Lösung:

ξ = (Aᵀ A)⁻¹ Aᵀ v

Für die Standardanordnung wird AᵀA diagonal und das Ganze kollabiert zu drei Zeilen für einen Arduino:

vx    = (fl + fr + rl + rr) / 4.0;
vy    = (-fl + fr + rl - rr) / 4.0;
omega = (-fl + fr - rl + rr) / (4.0 * K);

Das jeden Tick durch den Roboterkurs integrieren und man hat Koppelnavigation:

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;

Was exakt so gut arbeitet wie die Annahme darunter – dass die Räder nicht rutschen. Auf Mecanumbasis ist Schlupf der Konstruktionspunkt des Rads – Querodometrie also als unzuverlässigste Zahl am Roboter behandeln.

Die vierte Gleichung, die niemand nutzt

Vier Gleichungen, drei Unbekannte. Diese überzählige Gleichung ist kein Abfall – sie ist die einzige Gratisdiagnose am ganzen Antriebsstrang.

Ist der Roboter starr und greift jedes Rad, können die vier Felgendrehzahlen nicht beliebig sein. Erste zwei Zeilen addieren und letzte zwei Zeilen addieren und beide ergeben 2·vx, also:

FL + FR = RL + RR

immer, exakt, für jede Bewegung, die ein starres Mecanumchassis ausführen kann. Also jeden Schleifendurchlauf das Residuum rechnen:

float residual = (fl + fr) - (rl + rr);   // should be ~0 forever

Ungleich null heißt: Die vier Encoder beschreiben eine Bewegung, die kein fester Körper ausführen kann. Ursachen, grob nach Wahrscheinlichkeit: ein Rad in der Luft, ein Rad auf nassem Fleck, eine blockierte Rolle, eine rutschende Kupplung, ein toter Encoderkanal – oder ein Rad in der falschen Ecke, was das Residuum groß und konstant statt stoßweise macht.

Ehrlich darüber, was es nicht sehen kann: Rutschen alle vier Räder gleich zugleich, bleibt das Residuum null und der Roboter gleitet seitwärts vom Kurs in totaler Stille. Diese Blindheit ist exakt, warum Mecanum-Odometrie leise statt laut scheitert – und warum ernsthafte Bauten eine IMU oder ungetriebene Messräder ergänzen statt den Antriebsencodern zu trauen.

Die zwei Folgen von 45°

Beide fallen aus der Matrix – und keine lässt sich wegkaufen.

Diagonalbewegung deckelt bei 1/√2. Ein 45°-Befehl legt vx + vy auf zwei Räder und 0 auf die anderen zwei – das beschäftigte Paar erreicht also die Drehzahlgrenze, wenn der Roboter erst bei V_MAX / √2 ≈ 71 % seiner Geradeaus-Topdrehzahl ist. Schnellere Motoren verschieben die Decke; sie beseitigen sie nicht.

Traktion deckelt ebenfalls bei 1/√2, aus anderem Grund. Die nützliche Antriebskraft F des Motors liegt entlang der Rollrichtung – die wirkliche Bodenkraft ist aber F·√2 entlang der Rollenachse, und Reibung deckelt die. Pro Rad ist die Nutzkraft also auf μN/√2 gedeckelt, und die Beschleunigungsdecke des Roboters lautet:

a_max = μ·g / √2

Bei μ = 0,85 sind das 5,9 m/s², wo derselbe Roboter auf normalen Rädern 8,3 hätte. Die Robotermasse kürzt sich aus beiden – Ballast hilft also nicht: eine Lektion, die exakt umgekehrt läuft zu einem Sumoroboter, wo Gewicht die ganze Strategie ist.

Ein Plot von Seitwärtsgeschwindigkeit gegen Vorwärtsgeschwindigkeit in Metern pro Sekunde, der jede Geschwindigkeit zeigt, die ein Mecanum-Roboter mit 0,60-Meter-pro-Sekunde-Rädern erreichen kann. Die erreichbare Menge ist eine gefüllte cyanfarbene Raute mit Ecken bei plus und minus 0,60 auf jeder Achse. Ein gestrichelter grauer Kreis mit Radius 0,60 ist zum Vergleich gezeichnet und beschriftet ohne Richtungsstrafe. Bernsteinfarbene Markierungen sitzen bei 0,60 Metern pro Sekunde vorwärts und 0,60 Metern pro Sekunde seitwärts auf den Rautenecken, und eine rote Markierung auf der Diagonalkante ist beschriftet 0,424 Meter pro Sekunde auf der Diagonalen.
Jede Geschwindigkeit, die der Roboter erreichen kann, Richtung für Richtung gelöst. Geradeaus und gerade seitwärts sind frei; die Diagonalkante der Raute ist der Ort, wo der Befehl 1/√2 kostet. Nur ein Antrieb ohne 45° darin bekommt den Kreis. SVG herunterladen

In Firmware

Die Geometrie an einen Ort legen und nie zweimal tippen lassen.

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
  }
};

lx und ly Radmitte zu Radmitte messen, dann halbieren – nicht die Chassisplatte, die meist breiter ist. Falsches k stoppt den Roboter nicht beim Fahren; es lässt Drehung und Translation ineinander bluten – ein reiner Drehbefehl kriecht also und reines Strafen kurvt. Kriecht dein Roboter beim Drehen auf der Stelle, k verdächtigen, bevor man den Code verdächtigt.

Fehlersuche

Symptom Wahrscheinliche Ursache Was zu prüfen ist
Geradeaus gut, Strafen kurvt und rotiert Ein Rad in der falschen Ecke Die X-Probe von oben; das Residuum wird groß und stabil sein
Straft gegen den Befehl Alle vier Räder gespiegelt Linkes Paar mit rechtem Paar tauschen
Drehen auf der Stelle versetzt auch k falsch, oder lx/ly am Chassis statt Rädern gemessen Radmitte zu Radmitte neu messen
Diagonale deutlich langsamer als geradeaus Die 1/√2-Decke – kein Defekt Nichts zu reparieren; darum herum entwerfen
Roboter lehnt sich unter Pfadfolger aus Kurven Pro-Motor-Clipping statt Skalieren Den ganzen Befehl am schwächsten Rad skalieren
Odometrie driftet seitwärts schnell, vorwärts in Ordnung Rollenschlupf, konstruktionsbedingt IMU für den Kurs, Messräder für die Position
Residuum spitzt nur auf einer Oberfläche Dieser Fleck ist glitschig oder eine Rolle blockiert Jede Rolle von Hand drehen

Weiter

Die Zahlen gegen einen Roboter laufen lassen, der rutschen kann: Der Mecanum-Antriebssimulator fährt exakt diese Gleichungen in ein Reibungsmodell – man kann Odometrie und Boden sich widersprechen sehen. Dann die Räder die gerechneten Drehzahlen wirklich halten lassen – geregelte Raddrehzahlregelung –, und „vorwärts“ auf dem Boden statt auf dem Roboter heißen lassen mit feldorientierter Fahrt.

Den Graphen erkunden

Teil dieser Bauprojekte

Projekte und Lernpfade, die dieses Tutorial enthalten.

Weiterführende Literatur

Quellen