Tutorial · Intermediário · 35 min
Cinemática da roda Mecanum: quatro rodas, três eixos
Derive a cinemática inversa de uma base Mecanum a partir de um rolete a 45°, inverta-a para odometria e use a equação extra para detectar derrapagem.
Um rolete decide tudo
Retire uma roda Mecanum do robô e segure-a. Ela gira no eixo como qualquer roda. Ao redor de sua borda ficam oito ou nove roletes em forma de barril, cada um em seu pequeno eixo, cada um livre para girar e cada um posicionado a 45° em relação ao eixo da roda.
Agora pergunte qual força essa roda pode aplicar no piso.
Ela não pode empurrar ao longo da direção de rolamento do rolete — o rolete simplesmente gira e nada é transmitido. Ela só pode empurrar ao longo do eixo do rolete, a única direção na qual o rolete se recusa a girar. Portanto, a força da roda no chão fica travada em uma única linha no piso, a 45° em relação ao chassi, e nenhuma quantidade de torque do motor muda qual linha é essa.
Esse é todo o mecanismo. Tudo abaixo é apenas registro matemático.
A restrição, uma roda por vez
Defina o referencial usual do robô: x para a frente, y para a esquerda do robô, ω no sentido anti-horário. Essa é a convenção do ROS, e usar qualquer outra significa lutar contra todas as bibliotecas que você vier a usar.
Coloque a roda i na posição (xᵢ, yᵢ) nesse referencial e atribua a ela um sentido de roletes sᵢ = ±1, que indica em qual das duas linhas de 45° eles estão. Então a velocidade periférica da roda vᵢ (em metros por segundo no pneu, não em radianos) se relaciona com o movimento do chassi por:
vᵢ = vx + sᵢ·vy + (sᵢ·xᵢ − yᵢ)·ω
São três termos, e cada um merece uma frase:
- vx — todas as rodas contribuem totalmente para o movimento para a frente, qualquer que seja o sentido de seus roletes. É por isso que avançar em uma base Mecanum parece completamente normal.
- sᵢ·vy — o movimento lateral faz metade das rodas girarem em um sentido e metade no outro. O sentido decide qual metade.
- (sᵢ·xᵢ − yᵢ)·ω — o braço de momento. Observe que não é apenas a bitola; o sentido dos roletes está dentro dele, e é por isso que uma roda no canto errado estraga tanto a rotação quanto o deslocamento lateral.
Para obter o comando de cada motor a partir da velocidade da roda — a cinemática inversa — divida pelo raio da roda: ωwheel = vᵢ / r, em rad/s.
Quatro rodas: a matriz e a cinemática inversa
Empilhe essas quatro linhas e você terá uma matriz 4 × 3 A, com o vetor de movimento do chassi ξ = (vx, vy, ω):
v = A · ξ
Para o arranjo padrão — dianteira esquerda e traseira direita com um sentido de roletes, dianteira direita e traseira esquerda com o outro — escreva k = lx + ly, em que lx é metade do entre-eixos (da frente para trás) e ly é metade da bitola (da esquerda para a direita). Então:
| Roda | vx | vy | ω |
|---|---|---|---|
| Dianteira esquerda | 1 | −1 | −k |
| Dianteira direita | 1 | +1 | +k |
| Traseira esquerda | 1 | +1 | −k |
| Traseira direita | 1 | −1 | +k |
Estas são as quatro linhas que todo mundo escreve:
float fl = vx - vy - K * omega;
float fr = vx + vy + K * omega;
float rl = vx + vy - K * omega;
float rr = vx - vy + K * omega;
Confira isso contra as quatro coisas que você já sabe que um robô Mecanum faz:
| Comando | Velocidades das rodas (FL, FR, RL, RR) | O que se vê |
|---|---|---|
| Para a frente, vx = 1 | 1, 1, 1, 1 | Todas as quatro no mesmo sentido. As partes laterais se cancelam. |
| Deslocamento para a esquerda, vy = 1 | −1, 1, 1, −1 | Pares diagonais se opõem. Este é o movimento característico. |
| Giro anti-horário, ω = 1 | −k, k, −k, k | Lado esquerdo para trás, lado direito para a frente — um giro de tanque. |
| Movimento diagonal, vx = vy = 1 | 0, 2, 2, 0 | Duas rodas param completamente. Guarde essa ideia. |
O exemplo numérico
Uma base com lx = 75 mm, ly = 85 mm, rodas de 97 mm (r = 48,5 mm), solicitada a operar com vx = 0,30 m/s, vy = 0,20 m/s, ω = 0,50 rad/s. Portanto, k = 0,160 m.
| Roda | Conta | Velocidade da roda | Velocidade do motor |
|---|---|---|---|
| Dianteira esquerda | 0,30 − 0,20 − 0,160 × 0,50 | 0,020 m/s | 0,41 rad/s (3,9 rpm) |
| Dianteira direita | 0,30 + 0,20 + 0,160 × 0,50 | 0,580 m/s | 11,96 rad/s (114 rpm) |
| Traseira esquerda | 0,30 + 0,20 − 0,160 × 0,50 | 0,420 m/s | 8,66 rad/s (83 rpm) |
| Traseira direita | 0,30 − 0,20 + 0,160 × 0,50 | 0,180 m/s | 3,71 rad/s (35 rpm) |
Uma roda gira vinte e nove vezes mais rápido que outra, a partir de um comando que não parece extremo. Um trem de rodas Mecanum passa a maior parte da vida com seus quatro motores fazendo coisas muito diferentes, e é por isso que “todos recebem o mesmo PWM e ele anda em linha reta” não é um teste que signifique alguma coisa aqui.
Saturação: aplique uma escala, nunca recorte
Peça mais do que os motores podem fornecer e será preciso abrir mão de alguma coisa. Existem exatamente duas escolhas, e elas não são equivalentes.
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;
}
Isso é diferente do que acontece se você simplesmente escrever constrain(fl, -255, 255) em cada motor: recorte. O recorte mantém a velocidade e descarta a direção. Peça 0,60 m/s para a frente, 0,15 lateral e 0,8 rad/s de giro em um robô cujas rodas chegam no máximo a 0,50 m/s:
| Orientação | Para a frente | Giro | Veredito | |
|---|---|---|---|---|
| Comandado | 14,0° | 0,60 m/s | 0,80 rad/s | — |
| Escala | 14,0° | 0,34 m/s | 0,46 rad/s | Mais lento em todos os eixos, exatamente na direção indicada |
| Recorte | 5,6° | 0,46 m/s | 0,28 rad/s | Mais rápido para a frente, 8,4° fora da direção solicitada e 40% menos giro |
A parte mais traiçoeira é que o recorte parece bem funcionar nos comandos que as pessoas testam. Um movimento diagonal puro pede 2 às duas rodas e 0 às outras duas; os zeros já estão dentro do limite, então recorte e escala produzem números idênticos e o defeito se esconde. Ele aparece depois, sob um seguidor de caminho, como um robô que se inclina para fora das curvas sem que ninguém descubra o motivo. Veja os dois limitadores na mesma execução e a diferença é uma curva que você pode observar.
Odometria: a cinemática direta
A cinemática direta é a mesma matriz lida ao contrário. Mas A é 4 × 3 — quatro medições, três incógnitas — portanto não existe uma inversa matricial. Existe uma solução por mínimos quadrados:
ξ = (Aᵀ A)⁻¹ Aᵀ v
Para o arranjo padrão, AᵀA sai diagonal e tudo se reduz a três linhas que você pode colocar em um Arduino:
vx = (fl + fr + rl + rr) / 4.0;
vy = (-fl + fr + rl - rr) / 4.0;
omega = (-fl + fr - rl + rr) / (4.0 * K);
Integre esses valores usando a orientação do robô a cada ciclo e você terá navegação estimada:
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;
Isso funciona tão bem quanto a suposição sob a qual se baseia — que as rodas não estão derrapando. Em uma base Mecanum, a derrapagem é inerente ao projeto da roda, portanto trate a odometria lateral como o número menos confiável do robô.
A quarta equação que ninguém usa
Quatro equações, três incógnitas. Essa equação extra não é desperdício — é o único diagnóstico gratuito de todo o trem de rodas.
Se o robô é rígido e todas as rodas estão em contato firme com o piso, as quatro velocidades das rodas não podem ser arbitrárias. Some as duas primeiras linhas da matriz e as duas últimas, e ambas dão 2·vx, portanto:
FL + FR = RL + RR
sempre, exatamente, para qualquer movimento que um chassi Mecanum rígido possa executar. Calcule o resíduo a cada ciclo:
float residual = (fl + fr) - (rl + rr); // should be ~0 forever
Um valor diferente de zero significa que os quatro encoders estão descrevendo um movimento que nenhum objeto sólido pode fazer. Causas, em ordem aproximada de probabilidade: uma roda fora do chão, uma roda sobre uma área molhada, um rolete travado, um acoplamento deslizando, um canal de encoder sem sinal — ou uma roda instalada no canto errado, o que torna o resíduo grande e constante, em vez de intermitente.
Seja honesto sobre o que ele não consegue ver: se todas as quatro rodas derraparem da mesma forma ao mesmo tempo, o resíduo continuará em zero e o robô deslizará lateralmente para fora da trajetória solicitada em total silêncio. Essa cegueira é exatamente a razão pela qual a odometria Mecanum falha de forma silenciosa em vez de ruidosa, e a razão pela qual projetos sérios adicionam uma IMU ou rodas mortas sem alimentação em vez de confiar nos encoders da tração.
As duas consequências de 45°
As duas decorrem da matriz, e nenhuma pode ser eliminada.
O movimento diagonal é limitado a 1/√2. Um comando a 45° coloca vx + vy em duas rodas e 0 nas outras duas, portanto o par ocupado atinge o limite de velocidade quando o robô está se movendo a apenas V_MAX / √2 ≈ 71% de sua velocidade máxima em linha reta. Motores mais rápidos elevam o teto; não o removem.
A tração também é limitada a 1/√2, por outro motivo. A força útil F do motor fica ao longo da direção de rolamento, mas a força real no chão é F·√2 ao longo do eixo do rolete, e o atrito limita essa força. Assim, por roda, a força útil é limitada a μN/√2, e o teto de aceleração do robô é:
a_max = μ·g / √2
Com μ = 0,85, isso dá 5,9 m/s², enquanto o mesmo robô com rodas comuns teria 8,3. A massa do robô cancela nos dois casos, portanto lastro não ajuda — uma lição que vai exatamente na direção oposta à de um robô sumo, no qual o peso é toda a estratégia.
No firmware
Coloque a geometria em um único lugar e nunca a deixe ser digitada duas vezes.
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
}
};
Meça lx e ly de centro de roda a centro de roda e depois divida por dois — não na placa do chassi, que normalmente é mais larga. Errar k não impede o robô de andar; faz a rotação e a translação se misturarem, de modo que um comando de giro puro também faz o robô avançar e um deslocamento lateral puro faz curva. Se o robô avança enquanto gira no lugar, suspeite de k antes de suspeitar do código.
Solução de problemas
| Sintoma | Causa provável | O que verificar |
|---|---|---|
| Para a frente está bom, mas o deslocamento lateral faz curva e rotação | Uma roda no canto errado | A troca indicada acima; o resíduo será grande e constante |
| Desliza para o lado oposto ao comando | Todas as quatro rodas espelhadas | Troque o par esquerdo pelo direito |
| Girar no lugar também produz translação | k errado, ou lx/ly medidos no chassi em vez das rodas | Meça de centro de roda a centro de roda novamente |
| O movimento diagonal é muito mais lento que o para a frente | O teto de 1/√2 — não é uma falha | Nada a corrigir; projete em torno dele |
| O robô se inclina para fora das curvas sob um seguidor de caminho | Recorte por motor em vez de escala | Aplique um fator de escala ao comando inteiro com base na roda mais rápida |
| A odometria deriva muito para os lados, mas fica boa para a frente | Derrapagem dos roletes, funcionando como projetado | IMU para a orientação, rodas mortas para a posição |
| O resíduo dispara apenas em uma superfície | A área está escorregadia, ou um rolete está travado | Gire todos os roletes à mão |
Próximo passo
Execute os números em um robô que possa derrapar: o simulador de tração Mecanum usa exatamente estas equações em um modelo de atrito, para que você possa ver a odometria e o piso discordarem. Depois faça as rodas realmente manterem as velocidades que você calculou — controle de velocidade das rodas em malha fechada — e faça “para a frente” significar para a frente no piso, e não apenas no robô, com tração orientada pelo campo.
Explore o grafo
Faz parte destas montagens
Projetos e caminhos de aprendizagem que incluem este tutorial.
Leitura complementar