Joystick to Differential Drive (Arduino)


When you are building robots, one of the most common chassis you find are the ones that use Differential Drive. This means they have a motor per wheel, might be threaded, two wheels and a caster, 4 wheels, etc.

differential drive details
differential drive details

One way of driving this differential drive robots is sending PWM signals to each motor. But how do you convert for example the signals from a Joystick, (or any Cartesian pair of coordinates, like a direction vector) to the specific order for each wheel?

That is where this function is required. It receives a pair of cartesian x, and y coordinates, and calculates the differential drive output (right and left wheel commands) you will need to send to the motors.

This function will generate an output as can be seen in the following image:

Cartesian to Differencial Drive
Cartesian to Differencial Drive

LeftMotorOutput and RightMotorOutput will hold the values you should send to the right and left motors.

You can also configure the limits of x, y, and right and left motors by using the 4 constants defined on top of the code, this is handy to adapt the function to your specific scenario.

#define DDRIVE_MIN -100 //The minimum value x or y can be.
#define DDRIVE_MAX 100  //The maximum value x or y can be.
#define MOTOR_MIN_PWM -255 //The minimum value the motor output can be.
#define MOTOR_MAX_PWM 255 //The maximum value the motor output can be.

int LeftMotorOutput; //will hold the calculated output for the left motor
int RightMotorOutput; //will hold the calculated output for the right motor

void DifferentialDriveService::CalculateTankDrive(float x, float y)
 float rawLeft;
 float rawRight;
 float RawLeft;
 float RawRight;

 // first Compute the angle in deg
 // First hypotenuse
 float z = sqrt(x * x + y * y);

// angle in radians
 float rad = acos(abs(x) / z);

// Cataer for NaN values
 if (isnan(rad) == true) {
 rad = 0;

// and in degrees
 float angle = rad * 180 / PI;

// Now angle indicates the measure of turn
 // Along a straight line, with an angle o, the turn co-efficient is same
 // this applies for angles between 0-90, with angle 0 the co-eff is -1
 // with angle 45, the co-efficient is 0 and with angle 90, it is 1

float tcoeff = -1 + (angle / 90) * 2;
 float turn = tcoeff * abs(abs(y) - abs(x));
 turn = round(turn * 100) / 100;

// And max of y or x is the movement
 float mov = max(abs(y), abs(x));

// First and third quadrant
 if ((x >= 0 && y >= 0) || (x < 0 && y < 0))
 rawLeft = mov; rawRight = turn;
 rawRight = mov; rawLeft = turn;

// Reverse polarity
 if (y < 0) {
 rawLeft = 0 - rawLeft;
 rawRight = 0 - rawRight;

// Update the values
 RawLeft = rawLeft;
 RawRight = rawRight;

// Map the values onto the defined rang
 RightMotorOutput = map(rawRight, DDRIVE_MIN, DDRIVE_MAX, MOTOR_MIN_PWM, MOTOR_MAX_PWM);


If you find this code snippet useful, need help to make it work, or to understand it, please leave a comment and we can talk about it!