How a thumb on a controller turns into four motors that can drive, spin, and strafe — explained step by step, with stuff you can drag.
// companion to the two-motor arcade explainer · this one is the 4-wheel mecanum version
When a driver moves a joystick during a match, your robot doesn't understand "go forward." It only knows how to do one thing: spin each motor at some power.
A mecanum robot has four drive motors — one at each corner. Your code's whole job during driver control is to read the controller, do a little math, and hand each of the four motors a power number. Read → math → set power, about 50 times a second.
// This loop runs over and over while the match is on
while (opModeIsActive()) {
// 1. read the controller
// 2. do the math (that's what this guide is about)
// 3. tell all four motors how hard to spin
}
A joystick reports two numbers, and each one is always between -1 and +1.
gamepad1.left_stick_x → left/right. All the way left is -1, all the way right is +1.gamepad1.left_stick_y → up/down.Resting in the middle, both numbers are 0. Push it part-way and you get part-way numbers like 0.5 or -0.3. Try it — drag the stick below and watch the numbers.
Move the stick to see the values change.
left_stick_y goes negative, not positive! The controller measures Y upside-down. Notice it above: up = -1, down = +1.
That's backwards from how we think ("up should mean forward, forward should be positive"). The fix is one tiny minus sign — we flip it the moment we read it:
double y = -gamepad1.left_stick_y; // flip it, now up = forward = positive
From here on, y means "forward is positive," the way a human expects.
A normal wheel can only roll forward and back. A mecanum wheel is different: around its rim it has a bunch of little rollers set at a 45° angle.
Because of that angle, when the motor spins a mecanum wheel, the wheel doesn't just push straight — it also pushes diagonally, like a slanted conveyor belt.
Spin one wheel and it scoots the robot diagonally. That's useless alone — but put four of them at the corners, each angled in a clever pattern, and their diagonal pushes can add up or cancel out.
Mix them one way → the diagonals combine into straight forward. Mix them another way → they combine into a pure sideways slide (that's strafing). Mix them a third way → the robot spins in place.
Arcade drive had two ideas. Mecanum has three, because now we can slide sideways too:
y — drive forward / back → left stick up/downx — strafe left / right → left stick left/rightrx — turn / rotate → right stick left/rightdouble y = -gamepad1.left_stick_y; // forward / back
double x = gamepad1.left_stick_x; // strafe
double rx = gamepad1.right_stick_x; // turn
Each of the four wheels gets its own recipe of pluses and minuses. Don't memorize them — just notice every wheel uses the same three numbers, only the signs change:
frontLeft = y + x + rx;
backLeft = y - x + rx;
frontRight = y - x - rx;
backRight = y + x - rx;
Worked example. Drive forward a bit while strafing right: y = 0.5, x = 0.5, rx = 0.
frontLeft = 0.5 + 0.5 + 0 = 1.0
backLeft = 0.5 - 0.5 + 0 = 0.0
frontRight = 0.5 - 0.5 - 0 = 0.0
backRight = 0.5 + 0.5 - 0 = 1.0
Only two wheels spin — and they're the ones on one diagonal. That diagonal push sends the robot gliding forward-and-to-the-right.
Play with it. Left stick drives and strafes, right stick turns. Watch the four motors and the robot's movement arrow update live.
A motor's power has to stay between -1 and +1. But add three numbers together and you can easily blow past that.
Example: drive forward full (y = 1) and strafe full (x = 1): the front-left wheel wants 1 + 1 = 2.0. A motor can't do 2.0 — it just maxes out at 1.0, and now the four wheels are out of balance, so the robot drifts off in the wrong direction.
The fix is called normalizing: if the biggest wheel value is over 1, we shrink all four by the same amount. They keep their proportions, so the robot still glides the right direction — just safely under the limit.
// Find the biggest power we're asking for (but never less than 1)
double max = Math.max(Math.abs(y) + Math.abs(x) + Math.abs(rx), 1.0);
double frontLeftPower = (y + x + rx) / max;
double backLeftPower = (y - x + rx) / max;
double frontRightPower = (y - x - rx) / max;
double backRightPower = (y + x - rx) / max;
max is like splitting a pizza: no matter how much everyone wants, you share the same whole pizza fairly so nobody gets more than 100%. The slices stay in the same ratio.
The robot above already does this — that's why pushing both sticks all the way never makes a wheel read past 1.00.
Here's a complete, working mecanum driver-control loop. This is real FTC Java you can drop into a TeleOp OpMode for a four-motor robot:
while (opModeIsActive()) {
// --- 1. read the sticks (note the minus sign on y!) ---
double y = -gamepad1.left_stick_y; // forward / back
double x = gamepad1.left_stick_x; // strafe
double rx = gamepad1.right_stick_x; // turn
// --- 2. mix them into four wheel powers ---
double max = Math.max(Math.abs(y) + Math.abs(x) + Math.abs(rx), 1.0);
double frontLeftPower = (y + x + rx) / max;
double backLeftPower = (y - x + rx) / max;
double frontRightPower = (y - x - rx) / max;
double backRightPower = (y + x - rx) / max;
// --- 3. send the power to the four motors ---
frontLeft.setPower(frontLeftPower);
backLeft.setPower(backLeftPower);
frontRight.setPower(frontRightPower);
backRight.setPower(backRightPower);
}
Things to remember:
-gamepad1.left_stick_y, or up will drive you backward.max) so no motor is asked for more than it can give.frontLeft.setDirection(DcMotorSimple.Direction.REVERSE). If forward makes the robot spin or strafe, a motor direction is wrong — fix it here, not in the math.double x = gamepad1.left_stick_x * 1.1; to even it out.gamepad1.setJoystickDeadzone(0.05f) if the robot creeps while idle.This same loop is the heart of every mecanum driver-control program. Fancier features — slow mode, button-controlled arms, even field-centric driving (where "forward" always means away from the driver, no matter which way the robot faces) — are just more bits of math added on top of these three numbers.
// happy strafing — go team 🐝