Interactive simulatorIntermediate

Mecanum Drive Simulator: Strafe, Slip and Drift

Drive a holonomic robot sideways round a square, take the grip away, and watch its encoders keep insisting it worked. Then fit one wheel backwards.

Category
Autonomous Robots
Time
20–40 min
Platform
Browser · Arduino · ESP32
Mecanum Drive Simulator: Strafe, Slip and Drift technical schematicBASE_LINKVxVyωzFOUR WHEELS · THREE DEGREES OF FREEDOMROLLERS AT 45° MAKE AN X

01 / Start here

Introduction

A mecanum wheel can only push at 45 degrees, which is what lets it move sideways and what makes it let go. This lab solves the real force balance against a friction limit, so slip is something the physics does rather than something the lab draws — and it integrates the encoders separately from the floor, so you can watch the robot's own estimate walk away from the truth with nothing on board able to tell.

Live lab / Holonomic motion

Mecanum drive simulator

Drive a square sideways without ever turning, then take the grip away and watch the encoders keep insisting it worked. Fit one wheel backwards and the same code drives a spiral.

Browser native
This lab drives a four-wheel mecanum robot through a manoeuvre and draws three paths: the one commanded, the one the floor actually saw, and the one the wheel encoders reported. Beside it, the four wheel rim speeds against time with the motor's free speed marked. Use the controls below to change the command, the floor and how the wheels are fitted.

Left: a top-down view of the floor. The dashed outline is the manoeuvre as commanded, the solid line is where the robot actually went, and the dotted line is where its own encoders believe it went. The robot is drawn at its final pose, with each wheel's roller hand hatched — on a correctly built base those hatches point at the centre and make an X. Right: the four wheel rim speeds against time, with the motor's free speed marked. A trace sitting flat on that line is a wheel that has run out of motor; slip shows up on the left instead, as the gap between the solid line and the dotted one.

  • Commanded manoeuvre
  • Where the robot went
  • Where the encoders say
  • Motor free speed
Try it

There is nothing to start — the whole manoeuvre is re-run as you drag any control. Step through the five presets to see the four ways a holonomic base stops doing what it was told.

The command
The robot and the floor

The robot drives the square sideways and comes home.

Missed home by
0 mm
Odometry drift
0 mm
Heading change
0.0°
Ground lost to slip
0 mm
Command clipped
0%
Encoder disagreement
0.000

Every action has a button above. If you prefer the keyboard, focus the plots and press P for the next preset, M for the next manoeuvre, O to switch between robot- and field-oriented, W to fit the front-left wheel backwards and back, R to reset and F for full screen; sliders respond to the arrow keys.

Controls

Every control replays the whole manoeuvre on input, so the paths track a slider as you drag it. There is nothing to start.

Control What it changes
Preset Five worked situations: one that works, and four ways it stops working
Manoeuvre What the driver asks for — a square by strafing, a square by turning, a diagonal dash, or driving while spinning
Stick speed / Stick spin How hard the command is pushed
Stick frame Robot-oriented, or field-oriented with an IMU
Over the limit Scale the whole command by the worst wheel, or clip each wheel on its own
Wheel top speed The motors’ free speed at the rim — a hard ceiling, slipping or not
Floor grip μ Polished concrete is around 0.8; a dusty gym floor or spilt lubricant is 0.25
Wheel fit All four correct, or the front-left one fitted backwards

The left panel is the floor. Dashed is the manoeuvre as commanded, solid is where the robot actually went, dotted is where its own encoders believe it went. The right panel is the four wheel rim speeds against time, with the motor’s ceiling marked. A trace sitting flat on that line is a wheel that has run out of motor; slip shows up in the left panel instead, as the gap between the solid line and the dotted one.

Theory

A mecanum wheel can only put force into the floor along its rollers’ axles. Every other direction, the roller simply turns and nothing is transmitted. Those axles sit at 45° to the wheel, so one wheel’s ground force is pinned to a single line on the floor and no amount of motor torque moves it.

Four of those lines, alternating hand, sum to anything. Write the wheel’s rim speed against the chassis twist ξ = (vx, vy, ω) and you get one row per wheel:

vᵢ = vx + sᵢ·vy + (sᵢ·xᵢ − yᵢ)·ω        sᵢ = ±1, the roller hand

Stack the four rows into a matrix A and the whole drivetrain is v = A·ξ. Three consequences, and the lab exists to make each one visible:

Four equations, three unknowns. A rigid chassis whose wheels are all gripping cannot produce arbitrary rim speeds — the extra equation says FL + FR = RL + RR, always. The residual of that check is a free slip detector you can compute from encoders alone. It fires on asymmetric slip — one wheel off the ground, one wheel on a wet patch, one wheel in the wrong corner — and is completely blind when all four slip together, which is why mecanum odometry degrades quietly.

Traction costs 1/√2. The motor’s useful drive force runs along the rolling direction, but the actual ground force is √2 times that, pointed along the roller axle — and friction caps the whole vector. So the per-wheel limit is μN/√2 and the robot’s acceleration ceiling is:

a_max = μ·g / √2

At μ = 0.85 that is 5.9 m/s² against 8.3 m/s² for the same robot on plain wheels. The mass cancels out of both, so ballast changes nothing — the opposite of a pushing robot, where weight is the whole strategy.

Diagonals cost 1/√2 too. A 45° command puts vx + vy onto two wheels and zero onto the other two, so the busy pair saturates while the robot is only moving at V_MAX / √2. Faster motors move that ceiling; nothing removes it.

The full derivation, including the odometry inverse, is in mecanum wheel kinematics.

Algorithm

Each 5 ms step, in this order. The two pose integrations at the end are deliberately separate — that separation is the odometry lesson.

  1. Read the stick for this point in the manoeuvre.
  2. Rotate into the robot frame if field-oriented: vx' = vx·cos θ + vy·sin θ, vy' = −vx·sin θ + vy·cos θ. Robot-oriented skips this entirely.
  3. Inverse kinematicsv = A·ξ, using the matrix the firmware believes in, which is always the correct one. A mis-fitted wheel is not something firmware can know about.
  4. Limit — scale the whole vector by the worst wheel, or clip each wheel on its own.
  5. Wheel lag — each rim chases its target with a 0.1 s first-order response, because a velocity loop is good, not instant.
  6. What the wheels demand — least-squares forward kinematics on the rim speeds, using the matrix of the robot that actually got built.
  7. Solve for the forces that would make the chassis match, then apply two ceilings in order: the motor’s (there was only ever so much torque) and the floor’s (μN/√2 per wheel).
  8. Whatever the floor refuses spins the rim instead. That surplus accelerates the wheel away from its commanded speed, which is wheelspin, and it is the only symptom of slip an encoder can ever report.
  9. Integrate the truth from the forces that were actually applied.
  10. Integrate the odometry from the rim speeds, through the firmware’s matrix — because the encoder is bolted to the wheel, not to the floor.

Nothing in there looks for slip or inserts it. Slip is step 7 refusing a force, and the drift you see is step 10 never finding out.

Guided experiments

Each one isolates a single variable. Run them in order; they build.

1. The thing you bought mecanum wheels for

Load A square that closes. The robot drives forward a metre, right a metre, back and left, and comes home 17 mm from where it started with the heading readout never moving off 0.0°. Now switch the manoeuvre to square, turning at the corners — the same square, driven the way a differential-drive robot has to. It closes too, to 4 mm. Remember that both of them work here.

What to observe: the strafing square translates in two axes and spends no heading at all doing it. No differential drive can attempt that.

2. Take the floor away

Drag floor grip from 0.85 down to 0.25, keeping the strafing square. Then do the same to the turning square.

Manoeuvre Heading at μ = 0.85 Heading at μ = 0.25
Square by strafing 0.0° 0.0°
Square by turning 0.2° tens of degrees, and sometimes a full reversal

What to observe: watch the heading, not the distance. Both robots slide and both miss home; the strafing one misses while still pointing exactly where it started, because it never commanded a rotation in the first place. The turning one loses its heading and then drives its next leg in the wrong direction, which is how a small slip becomes a large error. Every spin is a fresh chance to lose the plot, and the holonomic base simply does not take them.

Distances in this experiment move around between runs at different settings, and that is the honest answer rather than a bug: a robot sliding on all four wheels is a stick-slip system and does not land twice in the same place. Heading does not have that problem.

3. Watch the encoders lie

Load Same square, greasy floor and look at the two lines on the left panel. The dotted odometry line drives a tidy square; the solid line does not. The robot finishes 225 mm from home while its own encoders put it 133 mm somewhere else, after 1.27 m of ground passed under the wheels without moving the robot at all.

Now watch the Encoder disagreement readout. It stays at 0.000 the whole time.

What to observe: the free residual check — FL + FR should equal RL + RR on any rigid chassis — catches one wheel slipping and is completely blind to all four slipping together. That blindness is exactly why mecanum odometry fails quietly. Put an IMU on it, or dead wheels, and stop trusting the drive encoders for position.

4. The √2 ceiling

Load Diagonal dash. Stick speed is 0.60 m/s and so is the wheel top speed, so the command should be achievable. It is not: the command is clipped for 91% of the run and the robot tops out at 0.424 m/s — which is 0.60/√2 to three decimal places, reached exactly.

What to observe: on the right panel, two traces sit flat at zero while two are pinned at the ceiling. A 45° command puts vx + vy on two wheels and nothing on the other two, so the busy pair saturates at V_MAX / √2. Raise the wheel top speed and the ceiling moves with it — the 71% never changes.

5. Scale versus clip

Switch the manoeuvre to drive while spinning, push stick spin to 240 °/s, and flip Over the limit between the two settings. The path changes shape.

What to observe: scaling gives up speed on every axis and keeps the direction. Clipping keeps the forward speed and loses 27% of the rotation — the robot turns through 127° over the run instead of 92° — so it curls differently than it was asked to. Then go back to the diagonal dash and try the same switch: nothing happens, because the idle wheels were already inside the limit. That is how this bug survives testing.

6. Fit a wheel backwards

Load One wheel fitted backwards, or press W on the plots. Nothing about the code has changed.

All four correct Front-left mirrored
Missed home by 17 mm over a metre
Heading change 0.0° tens of degrees
Ground lost to slip 15 mm several metres
Encoder disagreement 0.000 ≈ 0.28

What to observe: the robot is no longer doing anything like what it was told, and this time the residual readout does fire — large and steady rather than bursty. Note which column has exact numbers and which does not: with one wheel fighting the other three the robot is sliding continuously, so its end pose is chaotic. The residual is the number that survives, which is exactly why it is the one worth putting on a telemetry line. That is the one symptom you can read on a real robot with no IMU and no external reference. Look at the drawn robot too: the mirrored corner’s hatching points the wrong way, and the four roller lines no longer make an X.

7. Field-oriented, in one keypress

Load Field-oriented while spinning, then press O to switch back to robot-oriented and O again to return.

What to observe: robot-oriented, a forward stick held while spinning draws a rosette: the robot travels 2.34 m of path and ends up 0.30 m from where it started — 13% of its effort went anywhere. Field-oriented, the same stick travels 2.03 m and ends 2.03 m away: a dead straight line, 100%, with the body spinning the whole way along it. Six lines of trigonometry separate the two, and it is the difference between a robot a human can drive and one nobody can.

What you should observe

Pulling the seven together:

  • Forward is free; sideways and diagonal are not. Every wheel contributes fully to forward motion. A diagonal uses half of them.
  • Two root-two penalties, from the same 45°. Speed on a diagonal, and traction in every direction. Neither can be bought off with bigger motors.
  • Mass cancels. The acceleration ceiling is μg/√2 whatever the robot weighs, which is the opposite of a pushing robot.
  • Holonomic beats differential when the floor is bad, because the spins are where the error comes from.
  • Odometry is the price. Not a tuning problem, not a better-encoder problem — the wheel slipping is how the drivetrain works.

Taking it to hardware

In this lab On a real robot
A grip slider One floor, and a number you discover by pushing the robot with a luggage scale
Wheels reach their commanded speed with a 0.1 s lag Only if you built a velocity loop. Open-loop, the ratios drift with the battery
Perfect, instantaneous odometry integration 50–100 Hz, with encoder quantisation and a micros() rollover waiting for you
Identical motors 3–8% apart out of the box, and further apart warm
A clean μ for the whole floor Dust, a join in the vinyl, a cable — local grip varies more than you expect
Wheel fit is a dropdown Wheel fit is four bolts and the X check, and you will get it wrong once

The lab is worth the twenty minutes mostly for experiment 6. Fitting a wheel backwards costs nothing here and half a day on the bench, and it is the failure you are most likely to build.

Source code

The whole drivetrain is these two functions. Geometry lives in exactly one place.

const float LX = 0.075f;      // half the wheelbase, m (wheel centre to wheel centre / 2)
const float LY = 0.085f;      // half the track, m
const float K  = LX + LY;
const float R  = 0.0485f;     // wheel radius, m
const float V_MAX = 0.60f;    // rim speed the motors can actually hold, m/s

// Inverse kinematics + saturation. vx forward, vy LEFT, omega counter-clockwise.
void driveMecanum(float vx, float vy, float omega) {
  float w[4] = {
    vx - vy - K * omega,      // front left
    vx + vy + K * omega,      // front right
    vx + vy - K * omega,      // rear left
    vx - vy + K * omega,      // rear right
  };

  float peak = 0;
  for (int i = 0; i < 4; i++) peak = max(peak, fabsf(w[i]));
  if (peak > V_MAX) {                    // SCALE, never clip
    float s = V_MAX / peak;
    for (int i = 0; i < 4; i++) w[i] *= s;
  }

  for (int i = 0; i < 4; i++) wheelLoop[i].target = w[i];   // hand to the velocity loops
}

// Forward kinematics for odometry, and the free diagnostic.
float updateOdometry(const float rim[4], float dt) {
  float vx    = ( rim[0] + rim[1] + rim[2] + rim[3]) * 0.25f;
  float vy    = (-rim[0] + rim[1] + rim[2] - rim[3]) * 0.25f;
  float omega = (-rim[0] + rim[1] - rim[2] + rim[3]) * 0.25f / K;

  theta += omega * dt;
  poseX += (vx * cosf(theta) - vy * sinf(theta)) * dt;
  poseY += (vx * sinf(theta) + vy * cosf(theta)) * dt;

  // Four equations, three unknowns. On any rigid, gripping chassis this is zero.
  // Large and steady => a wheel is in the wrong corner. Bursty => something slipped.
  return (rim[0] + rim[1]) - (rim[2] + rim[3]);
}

The derivation of those four rows, and why the residual works, is in mecanum wheel kinematics. Making wheelLoop[i].target mean something is closed-loop wheel velocity control. Building the thing is the mecanum omnidirectional robot.

Hardware checklist

Components

  • Four mecanum wheels, two of each hand — never four the same
  • Four gearmotors, one per wheel, each independently reversible
  • Four quadrature encoders, if you want the wheels to hold their ratios
  • A flat, hard, clean floor, which is not negotiable

Explore the graph

Where this simulator is used

The projects, learning paths, and tutorials that build on this lab.

Continue building

Download resources

Use these on-page references while working through the project. Downloadable project bundles will be added only after their source and version are published.

Common questions

Frequently asked questions

Why does my mecanum robot spin when I ask it to strafe?

Almost always one wheel is in the wrong corner. A mecanum set has two left-handed and two right-handed wheels, and the handedness has to alternate around the chassis. Put one in the wrong place and its force vector points across the strafe instead of into it, so a sideways command comes out as a slow curve with a spin in it. Forward still looks fine, which is what makes it so confusing. Load the one wheel fitted backwards preset: a square that closed to 17 mm ends up more than a metre away and tens of degrees rotated, with no code change at all. The number to trust there is the encoder disagreement, which goes from 0.000 to about 0.28 — a robot that is sliding on every wheel is a stick-slip system, so exactly where it stops is not a prediction, on the bench or here. The five-second check on a real robot: stand over it, look straight down, follow the rollers you can see on top of each wheel — those four lines should aim at the middle and draw an X.

Why is my mecanum robot slower going diagonally?

Because a 45 degree command asks two wheels for everything and the other two for nothing. Set the manoeuvre to diagonal dash and watch the wheel speed plot: two traces sit flat on zero while two are pinned at the ceiling. Your top diagonal speed is therefore the top wheel speed divided by root two, about 71 percent of your straight-line speed, and faster motors move that ceiling without removing it. It is geometry, not a tuning problem.

Why is mecanum odometry so bad?

Because the encoder is bolted to the wheel and the wheel is not the floor. On a mecanum base the rollers slip by design — that is how sideways motion works — so every millimetre the floor refuses goes into the encoder as travel the robot never made. Load the greasy preset and drive the strafing square: the robot finishes 225 mm from home, its own dead reckoning is 133 mm out, and 1.27 m of ground went under the wheels without moving the robot. Nothing on board can tell. Worse, the residual check that catches one slipping wheel goes silent when all four slip together, so the error accumulates in complete silence. This is why serious mecanum builds add an IMU for heading and unpowered dead wheels for position.

Should I scale the whole command or clip each wheel?

Scale. Clipping each motor with a constrain() keeps the speed and throws the direction away. Set the manoeuvre to drive while spinning, push the stick spin up to 240 degrees per second and switch the limiter: scaling holds the commanded heading and slows every axis together, clipping holds the forward speed and quietly gives up more than a quarter of the rotation. The trap is that on a pure diagonal the two limiters produce identical numbers, because the idle wheels are already inside the limit — so the bug passes the test everybody runs and shows up later as a robot that leans out of corners.

Does a heavier robot get better traction on mecanum wheels?

Not for acceleration. More mass buys proportionally more friction and proportionally more inertia, and they cancel exactly: the acceleration ceiling is mu times g over root two regardless of what the robot weighs. That is the opposite of a sumo robot, where weight is the whole strategy, because there the load being pushed is external. Ballast on a mecanum base buys you nothing but a slower motor response and a flatter battery.

Is this what a real mecanum robot does?

The kinematics are exact — the same four-row matrix any real implementation uses, and the odometry is its least-squares inverse. The dynamics solve a genuine force balance each step with two ceilings in order, the motor's and the floor's, and slip is the surplus being refused rather than anything scripted. What it does not model is roller-by-roller contact as each roller enters and leaves the ground, the small vertical ripple that causes, or weight transfer under acceleration. So it gets the engineering answers right — the root-two penalties, where odometry goes, what a backwards wheel does — and runs a little smoother than a real robot on a real floor. One honest caveat: once every wheel is sliding, the model is a Coulomb stick-slip system, and the exact final pose stops being a repeatable number even though the lab itself is perfectly deterministic. Slip distance, the encoder residual and every gripping case are solid; a fully sliding run tells you the size of the problem, not where the robot will stop.

Further reading

References

Authoritative sources for going deeper than this simulator's bounded educational model.