FTC · Java · Arcade Drive

One Stick, Two Motors

How a thumb on a controller turns into two wheels spinning the right amount — explained step by step, with stuff you can drag.

1

The big idea

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
}
2

What a joystick actually gives you

A joystick reports two numbers, and each one is always between -1 and +1.

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.

up down left right
drag me
left_stick_x0.00
left_stick_y0.00

Move the stick to see the values change.

⚠ The #1 gotcha every rookie hits Push the stick UP and 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.

3

Two ideas: drive and turn

Arcade drive needs just two ideas, and you can get both from one joystick:

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;
Why + on one side and - on the other? Think about turning right. You want the left wheel to push harder (so we add) and the right wheel to ease off (so we subtract). The robot pivots right. Flip the turn the other way and it pivots left. These same two lines handle every direction automatically.

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.

fwd back
up/down = drive
left/right = turn
▲ FRONT ▲
leftPower
0
rightPower
0
spins forward spins backward
drive0.00
turn0.00
leftPower = drive + turn = 0.00
rightPower = drive - turn = 0.00
Many FTC teams split it across two sticks Some drivers like drive on the left stick and turn on the right stick. The only change is one line — read turn from the other stick: 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.

fwd back
LEFT stick
drive (up/down only)
RIGHT stick
turn (left/right only)
▲ FRONT ▲
leftPower
0
rightPower
0
spins forward spins backward
drive0.00
turn0.00
leftPower = drive + turn = 0.00
rightPower = drive - turn = 0.00
4

One last problem: motors can't go past 100%

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;
In plain English Dividing by 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.


The whole thing — copy/paste cheat sheet

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:

  • Always flip the drive value. -gamepad1.left_stick_y, or up will drive you backward.
  • Always normalize (divide by max) so no motor is asked for more than it can give.
  • Motor directions matter. The two motors face opposite ways on the robot, so one usually needs to be reversed in setup: rightMotor.setDirection(DcMotorSimple.Direction.REVERSE). Fix wrong-way wheels here, not in the math.
  • One stick or two? Single-stick arcade (shown here) feels like a video game. Split arcade puts turn on the right stick — same math, just read turn from right_stick_x.
  • Deadzone. Sticks rarely sit at a perfect 0, so the SDK ignores tiny wiggles for you. You can widen it with 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 🐝