# Making a robot go straight using encoder motors and micro-controller (Arduino)

Now I have a simple chassis with two wheels which are powered by encoder motors. I connected Arduino Uno with the chassis and made a simple program to just count the numbers of signals (in simple terms) from each of the encoder motors.

Now theoretically when the chassis moves straight both readings should be exactly the same (I've checked - They are!). And when one motor is faster that certain reading should be more.

So I made a simple program that keeps a check on these encoder readings and when the difference exceeds a certain value it shuts off one motor until the values don't become equal and then turn on that motor.

Now theoretically the chassis should go in a straight line but it doesn't and performs weird stuff. (Code is attached below).

Now sometimes the code gets stuck in one of the while loops. I've also tinkered with my code a lot. For example instead of while(left_counter != right_counter) I've even done while ((left_counter - right_counter) > Certain_Range

The only problem I can think of is that it takes time for the motors to turn off and in that time the interrupts are wreaking havoc. (I'm new to micro controller programming and stuff although I have quite a lot of experience in C++ etc.)

Please can any one explain why isn't this approach working in keeping the motor straight?

Code (Arduino UNO):

int motor_vcc_left = 12; //LEFT TIRE - BLUE
int pull_up_left = 3; //LEFT TIRE - GREEN

int motor_vcc_right = 11; //RIGHT TIRE - ORANGE
int pull_up_right = 2; //RIGHT TIRE - BLUE

int encoder_vcc_pin = 7;

volatile int left_counter = 0;
volatile int right_counter = 0;

void setup() {
pinMode(motor_vcc_left, OUTPUT);
pinMode(motor_vcc_right, OUTPUT);

pinMode(encoder_vcc_pin, OUTPUT);
digitalWrite(encoder_vcc_pin, HIGH);

pinMode(3, INPUT_PULLUP); //LEFT TIRE

pinMode(2, INPUT_PULLUP); //RIGHT TIRE

digitalWrite(motor_vcc_left, HIGH);
digitalWrite(motor_vcc_right, HIGH);
}

void loop() {
if ((left_counter - right_counter) > 10) {
while (left_counter != right_counter) {
digitalWrite(motor_vcc_left, LOW);
}
digitalWrite(motor_vcc_left, HIGH);
} else if ((right_counter - left_counter) > 10) {
while (right_counter != left_counter) {
digitalWrite(motor_vcc_right, LOW);
}
digitalWrite(motor_vcc_right, HIGH);
}
}

left_counter++;
}

right_counter++;
}

• If everything were perfectly symmetrical, and if the wheels had perfect contact with a perfect surface, then it might be possible to drive in a straight line by counting ticks. – Chu Aug 15 '15 at 18:35
• But by my code won't the robot straighten itself if it deviates from the path? The question I'm asking is what is the problem with my approach? – Saim Salman Aug 15 '15 at 18:39
• How does it know it's deviated from the required path? The main problem is that the wheels slide and the tick counters have no way of knowing. The robot needs a method of checking its absolute position and orientation frequently. – Chu Aug 15 '15 at 18:49
• Path control is not as easy as it appears. Go-to-goal is probably more practical, where the exact path is not controlled. – Chu Aug 15 '15 at 18:55
• I get that sliding can be a problem but shouldn't this still work when you have a certain type of surface where the slipping is extremely low. And through the clicks can't one know which wheel is going faster and essentially make it go on the straight path. Initially this code seems to work but then it starts to go left again and again. I actually wanted to somehow control it's path. So essentially I wanted to make sure that if my robot can go straight and can take 90 degree turns then I can easily make it follow a certain path. – Saim Salman Aug 15 '15 at 18:56

Simply turning the motors on and off won't get you far when it comes to driving straight. Motors and the robot itself have some inertia, so they won't just stop in place when you switch them off. Much better approach would be to implement a PID controller, or at least proportional part of it.

How proportional controller works?

• First you measure how many ticks you get in a certain, constant period, i.e. 10 ms, for each of wheels.
• You compare the result, with desired target value, let's say 40 ticks.
• When measured value is greater than the target you slow down motor a bit, when it's smaller you speed it up.

On Arduino, where you control a motor speed by PWM it looks like:

void loop() {
if(micros() - t > PERIOD) {
error = target - ticks;
newPWM = error * proportionalGain;
runMotor(newPWM);
t = micros();
}
}


This example is for only one motor, but I hope you get the general idea. You will need to tune PERIOD and proprtionalGain yourself. Remember, that valid PWM range for Arduino is 0-255.

Problems you will probably encounter Even when you have your motor running at the exactly same speed, you may still observe that your robot will turn. Here are few possible reasons: - Unequal wheels diameters - Uneven mass distribution in the robot (more mass on one wheel than on another) - Misalignment of wheels Here's a great paper, on a method called UMBmark developed by Johann Borenstein, that helps to counter these errors: http://www-personal.umich.edu/~johannb/Papers/paper60.pdf

• I'm using the following library:playground.arduino.cc/Code/PIDLibaryBasicExample It has the PID implemented. The problem I'm facing I don't know what are the input, output and setpoint. Now it's picking an analogue input and giving a PWM output. So will my input be a certain speed and setpoint a certain speed too? – Saim Salman Aug 17 '15 at 20:06
• Or will the ticks of Motor_1 be the setpoint of Motor_2 and likewise for the other motor? – Saim Salman Aug 17 '15 at 20:11
• @GeorgeAdams You want to contrl speed, so input will be ticks per your choosen period, set point will be ticks per period you want to get, and output will be PWM. Use two PID controllers, one per each wheel. To have more meaningful values, I usually scale my input and output to be in range of -100:100, be it's your choice. – mactro Aug 18 '15 at 14:39

Using encoder feedback will offer slight improvement over just turning the motors on but will not achieve perfect tracking of a straight line over a long distance. That would require some other sort of feedback mechanism. The following assumes that you are only looking for minor improvements.

As to your code, I would lose the extra states introduced by the while loops and do something like:

#define DEAD_BAND 10

void loop() {
if ((left_counter - right_counter) > DEAD_BAND) {
// We are pointing too far to the right. Turn right motor on, left motor off.
digitalWrite(motor_vcc_right, HIGH);
digitalWrite(motor_vcc_left, LOW);
}
else if ((right_counter - left_counter) > DEAD_BAND) {
// We are pointing too far to the left. Turn left motor on, right motor off.
digitalWrite(motor_vcc_right, LOW);
digitalWrite(motor_vcc_left, HIGH);
}
else {
// Everything seems cool. Continue psuedo-straight.
digitalWrite(motor_vcc_right, HIGH);
digitalWrite(motor_vcc_left, HIGH);
}
}


Further improvement: The above has some weaknesses. Once we accumulate some error and are pointing to the right, we correct the count by angling to the left a bit more. However, we still may have moved laterally and/or have corrected at a different turning radius than that of the error source.

• I took this exact approach for an old project in high school :) – Houston Fortney Aug 16 '15 at 1:12