How a thumb on a controller turns into two wheels spinning the right amount — explained step by step, with stuff you can drag.
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 basic robot has two drive motors — one for the left wheels, one for the right. So your code's whole job during driver control is to read the controller, do a little math, and hand each motor 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 the left & right 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 drive = -gamepad1.left_stick_y; // flip it, now up = forward = positive
From here on, forward is positive, the way a human expects.
Arcade drive needs just two ideas, and you can get both from one joystick:
y).x).double drive = -gamepad1.left_stick_y; // up/down = forward/back
double turn = gamepad1.left_stick_x; // left/right = turn
Now the magic. To go straight, both motors get the same power. To turn, one side speeds up and the other slows down. You get both at once with simple addition and subtraction:
double leftPower = drive + turn;
double rightPower = drive - turn;
Worked example. Driver pushes forward halfway and nudges right a little: drive = 0.5, turn = 0.2.
leftPower = 0.5 + 0.2 = 0.7
rightPower = 0.5 - 0.2 = 0.3
The left wheel spins faster than the right one → the robot drives forward while curving to the right.
Play with it. Drag the stick: up/down drives, left/right turns. Watch each motor's power and direction update on the robot.
double turn = gamepad1.right_stick_x;. All the rest of the math stays exactly the same.
double drive = -gamepad1.left_stick_y; // LEFT stick up/down
double turn = gamepad1.right_stick_x; // RIGHT stick left/right
Now try the two-stick version. The left stick only slides up/down (drive), the right stick only slides left/right (turn). Notice the robot does the exact same thing as the single-stick version — only your hands move differently.
A motor's power has to stay between -1 and +1. But add two numbers together and you can blow past that.
Example: drive forward full (drive = 1) and turn full (turn = 1): the left motor wants 1 + 1 = 2.0. A motor can't do 2.0 — it just maxes out at 1.0, and now the two sides are out of balance, so the robot won't move the way the driver expects.
The fix is called normalizing: if the bigger motor value is over 1, we shrink both of them by the same amount. They keep their proportions, so the robot still drives 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(drive) + Math.abs(turn), 1.0);
double leftPower = (drive + turn) / max;
double rightPower = (drive - turn) / 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 the stick fully into a corner never makes a motor read past 1.00.
Here's a complete, working arcade driver-control loop. This is real FTC Java you can drop into a TeleOp OpMode for a two-motor robot:
while (opModeIsActive()) {
// --- 1. read the stick (note the minus sign on drive!) ---
double drive = -gamepad1.left_stick_y; // forward / back
double turn = gamepad1.left_stick_x; // turn left / right
// --- 2. mix them into two motor powers ---
double max = Math.max(Math.abs(drive) + Math.abs(turn), 1.0);
double leftPower = (drive + turn) / max;
double rightPower = (drive - turn) / max;
// --- 3. send the power to the motors ---
leftMotor.setPower(leftPower);
rightMotor.setPower(rightPower);
}
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.rightMotor.setDirection(DcMotorSimple.Direction.REVERSE). Fix wrong-way wheels here, not in the math.turn from right_stick_x.gamepad1.setJoystickDeadzone(0.05f) if your robot creeps while idle.Master these steps and you understand the heart of every FTC driver-control program. Everything else — slow mode, button-controlled arms, even fancy four-wheel mecanum driving — is just more little bits of math added on top of this same loop.
// happy driving — go team 🐝