Tutorial · Intermedio · 35 min
Cinematica delle ruote Mecanum: quattro ruote, tre assi
Da un rullo a 45° ricava quattro equazioni di cinematica inversa per una base Mecanum, poi usa l'equazione ridondante per odometria e slittamento.
Un rullino determina tutto
Togli una ruota Mecanum dal robot e tienila in mano. Ruota sul proprio asse come qualsiasi ruota. Sul bordo ci sono otto o nove rulli a forma di barile, ciascuno sul proprio piccolo asse, ciascuno libero di ruotare e ciascuno inclinato a 45° rispetto all’asse della ruota.
Ora chiediti quale forza la ruota può esercitare sul pavimento.
Non può spingere lungo la direzione di rotazione del rullino: il rullino ruota semplicemente e non trasmette nulla. Può spingere soltanto lungo l’asse del rullino, l’unica direzione lungo la quale il rullino si rifiuta di ruotare. La forza della ruota sul terreno è quindi bloccata su un’unica retta del pavimento, a 45° rispetto al telaio, e nessuna quantità di coppia motrice cambia quale sia quella retta.
Questo è l’intero meccanismo. Tutto il resto è contabilità.
Il vincolo, una ruota alla volta
Imposta il solito sistema di riferimento del robot: x in avanti, y verso sinistra del robot, ω in senso antiorario. Questa è la convenzione ROS; usarne un’altra significa combattere con ogni libreria che toccherai.
Metti la ruota i nella posizione (xᵢ, yᵢ) di questo sistema di riferimento e assegna alla sua mano del rullino sᵢ = ±1, che indica lungo quale delle due rette a 45° si allineano i rulli. La velocità perimetrale vᵢ della ruota (in metri al secondo sulla gomma, non in radianti) è allora legata alla velocità del telaio da:
vᵢ = vx + sᵢ·vy + (sᵢ·xᵢ − yᵢ)·ω
Tre termini, ciascuno degno di una frase:
- vx — ogni ruota contribuisce pienamente al movimento in avanti, qualunque sia la sua mano. Ecco perché l’avanti su una base Mecanum risulta del tutto normale.
- sᵢ·vy — il movimento laterale aziona metà delle ruote in una direzione e metà nell’altra. La mano decide quale metà.
- (sᵢ·xᵢ − yᵢ)·ω — il braccio di momento. Nota che non dipende soltanto dalla carreggiata: la mano del rullino è dentro l’espressione, ed è per questo che una ruota nell’angolo sbagliato rovina anche la rotazione oltre alla traslazione laterale.
Per passare dalla velocità perimetrale a ciò che comandi davvero al motore, dividi per il raggio della ruota: ωwheel = vᵢ / r, in rad/s.
Quattro ruote: la matrice
Impila queste quattro righe e ottieni una matrice 4 × 3 A, con la velocità del telaio ξ = (vx, vy, ω):
v = A · ξ
Per la disposizione standard — anteriore sinistra e posteriore destra con una mano, anteriore destra e posteriore sinistra con l’altra — scrivi k = lx + ly, dove lx è metà dell’interasse (dall’avanti al posteriore) e ly è metà della carreggiata (da sinistra a destra). Quindi:
| Ruota | vx | vy | ω |
|---|---|---|---|
| Anteriore sinistra | 1 | −1 | −k |
| Anteriore destra | 1 | +1 | +k |
| Posteriore sinistra | 1 | +1 | −k |
| Posteriore destra | 1 | −1 | +k |
Sono le quattro righe che tutti scrivono:
float fl = vx - vy - K * omega;
float fr = vx + vy + K * omega;
float rl = vx + vy - K * omega;
float rr = vx - vy + K * omega;
Verifica con le quattro cose che sai già fare a un robot Mecanum:
| Comando | Velocità delle ruote (FL, FR, RL, RR) | Cosa osservi |
|---|---|---|
| Avanti, vx = 1 | 1, 1, 1, 1 | Tutte e quattro nello stesso verso. Le componenti laterali si annullano. |
| Traslazione a sinistra, vy = 1 | −1, 1, 1, −1 | Le coppie diagonali si oppongono. È l’effetto caratteristico. |
| Rotazione antioraria, ω = 1 | −k, k, −k, k | Lato sinistro indietro, lato destro avanti: una normale rotazione su posto. |
| Diagonale, vx = vy = 1 | 0, 2, 2, 0 | Due ruote si fermano completamente. Tienilo a mente. |
L’esempio calcolato
Una base con lx = 75 mm, ly = 85 mm e ruote da 97 mm (r = 48.5 mm) riceve il comando vx = 0.30 m/s, vy = 0.20 m/s, ω = 0.50 rad/s. Quindi k = 0.160 m.
| Ruota | Calcolo | Velocità perimetrale | Velocità del motore |
|---|---|---|---|
| Anteriore sinistra | 0.30 − 0.20 − 0.160 × 0.50 | 0.020 m/s | 0.41 rad/s (3.9 rpm) |
| Anteriore destra | 0.30 + 0.20 + 0.160 × 0.50 | 0.580 m/s | 11.96 rad/s (114 rpm) |
| Posteriore sinistra | 0.30 + 0.20 − 0.160 × 0.50 | 0.420 m/s | 8.66 rad/s (83 rpm) |
| Posteriore destra | 0.30 − 0.20 + 0.160 × 0.50 | 0.180 m/s | 3.71 rad/s (35 rpm) |
Ventinove volte la velocità su una ruota rispetto a un’altra, da un comando che non sembra estremo. Una trazione Mecanum passa quasi tutto il tempo con i suoi quattro motori che fanno cose radicalmente diverse, ed è per questo che «assegnano tutti lo stesso PWM e va dritto» non è una prova utile in questo contesto.
Saturazione: scala, non troncare
Se chiedi più di quanto possano fornire i motori, devi rinunciare a qualcosa. Ci sono esattamente due scelte e non sono equivalenti.
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;
}
Rispetto a ciò che accade se scrivi soltanto constrain(fl, -255, 255) su ogni motore: troncare. Il troncamento conserva la velocità e scarta la direzione. Chiedi 0.60 m/s in avanti, 0.15 laterali e 0.8 rad/s di rotazione a un robot le cui ruote raggiungono al massimo 0.50 m/s:
| Orientamento | Avanti | Rotazione | Verdetto | |
|---|---|---|---|---|
| Richiesto | 14.0° | 0.60 m/s | 0.80 rad/s | — |
| Scala | 14.0° | 0.34 m/s | 0.46 rad/s | Più lento su ogni asse, esattamente nella direzione indicata |
| Troncamento | 5.6° | 0.46 m/s | 0.28 rad/s | Più veloce in avanti, 8.4° fuori rotta e il 40% di rotazione in meno |
La parte più insidiosa è che il troncamento sembra corretto sui comandi con cui le persone fanno le prove. Una diagonale pura chiede 2 a due ruote e 0 alle altre due; gli zeri sono già entro il limite, quindi troncamento e scala producono numeri identici e il bug si nasconde. Compare più avanti, sotto un robot seguilinea, come un robot che esce dalle curve senza una ragione che nessuno riesca a trovare. Osserva entrambi i limitatori nella stessa esecuzione: la differenza è una curva visibile.
In senso inverso: odometria
La cinematica diretta è la stessa matrice letta al contrario. Ma A è 4 × 3 — quattro misurazioni, tre incognite — quindi non esiste un’inversa. Esiste una soluzione ai minimi quadrati:
ξ = (Aᵀ A)⁻¹ Aᵀ v
Per la disposizione standard, AᵀA risulta diagonale e tutto si riduce a tre righe che puoi caricare su un Arduino:
vx = (fl + fr + rl + rr) / 4.0;
vy = (-fl + fr + rl - rr) / 4.0;
omega = (-fl + fr - rl + rr) / (4.0 * K);
Integrale rispetto all’orientamento del robot a ogni passo e ottieni una stima per integrazione:
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;
Funziona esattamente quanto bene funziona l’ipotesi sottostante, cioè che le ruote non slittino. Su una base Mecanum, lo slittamento è il punto di funzionamento previsto della ruota, quindi considera l’odometria laterale il dato meno attendibile del robot.
La quarta equazione che nessuno usa
Quattro equazioni, tre incognite. L’equazione ridondante non è uno spreco: è l’unica diagnostica gratuita dell’intera trazione.
Se il robot rigido è stabile e ogni ruota aderisce, le quattro velocità perimetrali non possono essere arbitrarie. Somma le prime due righe e le ultime due righe della matrice: entrambe danno 2·vx, quindi:
FL + FR = RL + RR
sempre, esattamente, per qualsiasi moto che un telaio Mecanum rigido possa eseguire. Calcola quindi il residuo a ogni ciclo:
float residual = (fl + fr) - (rl + rr); // should be ~0 forever
Un valore diverso da zero significa che i quattro encoder descrivono un moto impossibile per un oggetto solido. Possibili cause, in ordine approssimativo di probabilità: una ruota sollevata da terra, una ruota su una zona bagnata, un rullino bloccato, un accoppiamento che slitta, un canale encoder morto — oppure una ruota montata nell’angolo sbagliato, che rende il residuo grande e costante anziché discontinuo.
Sii onesto su ciò che non può vedere: se tutte e quattro le ruote slittano nello stesso modo e nello stesso istante, il residuo resta a zero e il robot scivola lateralmente fuori rotta in totale silenzio. Questa cecità è il motivo per cui l’odometria Mecanum fallisce in silenzio anziché rumorosamente e per cui le costruzioni serie aggiungono un IMU o ruote passive non motorizzate invece di fidarsi degli encoder della trazione.
Le due conseguenze dei 45°
Entrambe derivano dalla matrice e nessuna si può eliminare.
Il movimento diagonale è limitato a 1/√2. Un comando a 45° impone vx + vy a due ruote e 0 alle altre due, quindi la coppia impegnata raggiunge il limite di velocità quando il robot si muove soltanto a V_MAX / √2 ≈ 71% della sua velocità massima in rettilineo. Motori più veloci alzano il limite; non lo eliminano.
Anche la trazione è limitata a 1/√2, ma per un motivo diverso. La forza motrice utile F del motore è lungo la direzione di rotazione, ma la forza effettiva al terreno è F·√2 lungo l’asse del rullino, e l’attrito limita quella. Per ogni ruota la forza utile è quindi limitata a μN/√2 e il limite di accelerazione del robot è:
a_max = μ·g / √2
Con μ = 0.85 è 5.9 m/s², mentre lo stesso robot su ruote semplici avrebbe 8.3. La massa del robot si cancella in entrambi i casi, quindi la zavorra non aiuta: una lezione opposta a quella del robot sumo, per il quale il peso è l’intera strategia.
Nel firmware
Metti la geometria in un solo punto e non permettere mai che venga digitata due volte.
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
}
};
Misura lx e ly da centro ruota a centro ruota, poi dimezza: non sulla piastra del telaio, che di solito è più larga. Se sbagli k, il robot continua a muoversi, ma rotazione e traslazione si contaminano: un comando di sola rotazione deriva e una traslazione laterale curva. Se il robot deriva mentre ruota sul posto, sospetta di k prima del codice.
Risoluzione dei problemi
| Sintomo | Causa probabile | Cosa controllare |
|---|---|---|
| L’avanti funziona, la traslazione laterale curva e ruota | Una ruota è nell’angolo sbagliato | Il controllo X descritto sopra; il residuo sarà grande e stabile |
| Si sposta lateralmente nel verso opposto al comando | Tutte e quattro le ruote sono specchiate | Scambia la coppia sinistra con quella destra |
| Ruotando sul posto si sposta anche lateralmente | k è sbagliato, oppure lx/ly sono stati misurati sul telaio anziché sulle ruote | Rimisura da centro ruota a centro ruota |
| Il movimento diagonale è molto più lento dell’avanti | Limite 1/√2: non è un guasto | Niente da correggere; progetta tenendolo conto |
| Il robot esce dalle curve sotto un robot seguilinea | Troncamento per motore invece che scala | Scala l’intero comando in base alla ruota peggiore |
| L’odometria deriva rapidamente lateralmente, mentre regge in avanti | Slittamento dei rulli, come previsto | IMU per l’orientamento, ruote passive per la posizione |
| Il residuo presenta picchi solo su una superficie | Quella zona è scivolosa, oppure un rullo è bloccato | Gira ogni rullo a mano |
Prossimo passo
Esegui i calcoli su un robot che può slittare: il simulatore della trazione Mecanum applica queste precise equazioni a un modello di attrito, così puoi osservare l’odometria e il pavimento che divergono. Poi fai in modo che le ruote mantengano davvero le velocità calcolate con il controllo in anello chiuso della velocità delle ruote e fai in modo che «avanti» significhi avanti sul terreno, non sul robot, con la trazione orientata al campo.
Esplora il grafo
Fa parte di queste costruzioni
Progetti e percorsi di apprendimento che includono questo tutorial.
Per approfondire