Robot travel in Straight Line

Project: Arduino robot with motors equipped with hall effect quadrature encoders.

Hobbyist Situation: Good hardware/electronics skills, just learning how to program.

Challenge: Creating a sketch that will allow the bot to travel in a straight line for some distance, turnaround and travel back to the start point. Great precision is not needed for this challenge; therefore, limited tracking (drift) back and forth across the straight line path is acceptable.

Current situation: The bot has been tested and it goes forward, backwards, turns etc. However, as expected, the bot cannot travel in straight line.

Concept: Keep it simple by having one motor serve as the speed master and the other as a speed slave, where the master motor speed is held constant and the slave motor speed is varied so as to match the master motor speed. In this situation Error = speed_Master - speed_Slave

This concept was presented at the web page below which uses RobotC for programming. My limited programming experience/skills do not allow me to simply translate RobotC to an Arduino sketch C which I am currently learning.

As explained at the above web page the difference between the master encoder and the slave encoder ticks can be used to determine error. Negative if slave has to slow down, positive if it has to speed up. If the motors moved at exactly the same speed, this value would be 0.

Another important aspect of the RobotC straight line code is it does not use floats or other data types that consume large amounts of microprocessor resources. It uses integers only and math that does not result in floats.

I have spent many hours on trying to come up with a way to get the bot to travel in a straight line using some aspect of PID that is not overly complex for this relatively simple task. It seems to me the concepts set out by the RobotC code offers the perfect solution.

Thank you for any advice or direction you may provide.


Two things that would be helpful for us to help you:

  • The sensors / motors you are using (Part Number or Datasheet)
  • Your code


I have tried the "master wheel" vs "slave wheel" method and found that my bot would travel in a slow arc. I instead opted for running each motor in a closed loop and operating towards a common "set point" of n counts from each encoder every 30 milliseconds. So every 30 msec the count from each motor was compared to the set point. The PWM to each motor was then adjusted by around 1% up or down which kept the bot running a straight line. I'm not sure how you would do this with an Arduino, I used a Parallax Propeller 32 bit 8 core controller. Doing very slight adjustments 33 times a second seemed to work very well for me. The bot's speed was controlled by changing the set point.

In Arduino C it would look something like this:

////task main()

#include <Servo.h>
//The powers we give to both motors. masterPower will remain constant while slavePower will change so that
//the right wheel keeps the same speed as the left wheel.
int masterPower = 30;
int slavePower = 30;

int SensorValue[2];
const int leftEncoder = 0;
const int rightEncoder = 1;

Servo motor[2];
const int leftServo = 0;
const int LeftServoPin = 4;
const int rightServo = 1;
const int RightServoPin = 5;

//Essentially the difference between the master encoder and the slave encoder. Negative if slave has
//to slow down, positive if it has to speed up. If the motors moved at exactly the same speed, this
//value would be 0.
int error = 0;

//'Constant of proportionality' which the error is divided by. Usually this is a number between 1 and 0 the
//error is multiplied by, but we cannot use floating point numbers. Basically, it lets us choose how much
//the difference in encoder values effects the final power change to the motor.
int kp = 5;

void setup() { ////
  //Reset the encoders.
  SensorValue[leftEncoder] = 0;
  SensorValue[rightEncoder] = 0;

//Repeat ten times a second.
void loop() ////while(true)
  //Set the motor powers to their respective variables.

  //This is where the magic happens. The error value is set as a scaled value representing the amount the slave
  //motor power needs to change. For example, if the left motor is moving faster than the right, then this will come
  //out as a positive number, meaning the right motor has to speed up.
  error = SensorValue[leftEncoder] - SensorValue[rightEncoder];

  //This adds the error to slavePower, divided by kp. The '+=' operator literally means that this expression really says
  //"slavePower = slavepower + error / kp", effectively adding on the value after the operator.
  //Dividing by kp means that the error is scaled accordingly so that the motor value does not change too much or too
  //little. You should 'tune' kp to get the best value. For us, this turned out to be around 5.
  slavePower += error / kp;

  //Reset the encoders every loop so we have a fresh value to use to calculate the error.
  SensorValue[leftEncoder] = 0;
  SensorValue[rightEncoder] = 0;

  //Makes the loop repeat ten times a second. If it repeats too much we lose accuracy due to the fact that we don't have
  //access to floating point math, however if it repeats to little the proportional algorithm will not be as effective.
  //Keep in mind that if this value is changed, kp must change accordingly.
  delay(100);  ////wait1Msec(100);

It looks like the sensor values are being updated in interrupt code. If the encoders are quadrature (A and B signals) there are lots of example of how to red them with an Arduino.

The problem: once you detect a deviation from the straight line, how would the robot find back to the intended line? Have you tried to draw a diagram of the robot movement, once it made a few steps away from the line? If you only make the other wheel catch up in revolution counts, the robot will continue to move on a parallel line, not on the original one.

As Due_unto mentioned, you must prevent the robot from running in a wiggly line at all, by a pretty fast feedback loop.

A simpler solution: use stepper motors, so that you can eliminate any motor speed difference. What remains then is wheel slippage or different diameters.