# Difference between revisions of "Tutorials/Arduino Projects/Mobile Robotics/BoeBot/Using encoders to make turns"

## Contents

Another powerful way to use the encoder is to use it to allow your robot to turn through a certain number of degrees. We have previously turned a certain amount by doing something like this:

 ```motor[leftServo] = -20; motor[rightServo] = 20;   wait1Msec(500);   motor[leftServo] = 0; motor[rightServo] = 0;```

As you may have noticed, not only can this be very inconsistent, but the only way to tell how many degrees the robot has turned through is by a trial-and-error method, which will have to be redone when on a different surface or even at a different battery level.

### Three ways of using encoders to make turns

Now that the robot is equipped with encoders, we have a number of options regarding how to go about making turns.

1. We can turn the robot until one wheel's encoder has ticked far enough, and hope the other wheel has gone the same distance. If you notice that your wheels tend to go roughly the same speed (A good indication of this is how straight your robot went without the benefit of proportional control) then this might be the best option as it is the simplest. However, many motors have manufacturing tolerances which means the motors may well go at different speeds when the same power is applied.
2. We can control each wheel independently throughout the turn - that is, we separately monitor each wheel for when its encoder has passed the tick point. This will result in a robot that has turned exactly the correct angle, however if the robots have slightly different speeds, one wheel will be turning after the other has stopped, leading to a slight change in position of the robot.
3. We can use proportional control. This is rather complex and since the BoeBot's servos are very accurate, we really don't need this for our application.

For this lesson, we are going to choose option 2 as it is best suited for our purposes, and a slight displacement of the robot at the end of each turn should not concern us too much.

## The Test

Just like in the previous lesson where we found the ratio of encoder ticks to inches traveled, we are going to find the ratio of encoder ticks to degrees turned. To do this, we will perform an experiment of sorts. It is possible to determine this ratio with some trigonometry however that is beyond the scope of this lesson, and since our robot will always be the same size an experimental method like this is both easier and simpler.

### The Program

The first thing we need to do is write a program which will make the robot turn through a preset number of encoder ticks. We will turn for 8 encoder ticks to get enough motion to negate slop but not enough to be too large for the protractor. We will also control each motor independently so that in the end we will have the most accurate turn.

 ```//used to store the count of the motor clicks int leftEncoderCount = 0; int rightEncoderCount = 0;   //a background task used to monitor the encoders and count the clicks task encoderTask() { //get the state of the encoders so that we know when they have changed // for example if the last state was high and the current state is low // or vise-versa, then we know that the encoder incremented another click. int lastStateLeft = SensorValue[leftEncoder]; int lastStateRight = SensorValue[rightEncoder];   while(true) {   //get the current state of the left encoder int state = SensorValue[leftEncoder];   //has the left encoder changed states if (lastStateLeft != state) { //if so, then increment the leftEncoderCount by 1 leftEncoderCount++;   // then store the current state as the last state so that we can keep monitoring every change lastStateLeft = state; }   //repeat for the right encoder.   state = SensorValue[rightEncoder]; if (lastStateRight != state) { rightEncoderCount++; lastStateRight = state; }   //we add a pause between updates to keep from hogging the CPU and // so that we debounce the encoders. wait1Msec(10); } }   task main() { //Start the encoder monitoring task, which will run in the background. StartTask(encoderTask);   //Reset encoders leftEncoderCount = 0; rightEncoderCount = 0;   //Perform a point turn to the left. We will use lower power values for more accuracy. motor[leftServo] = -20; motor[rightServo] = 20;   //Since the wheels may go at slightly different speeds due to manufacturing tolerances, etc., //we need to test both encoders and control both motors separately. This may result in one motor //going for longer than another but it will ultimately result in a much more accurate turn. while(rightEncoderCount < 8 || leftEncoderCount < 8) { if(rightEncoderCount > 8) {motor[rightServo] = 0;} if(leftEncoderCount] > 8) {motor[leftServo] = 0;} } }```

### The Experiment

To use this code, the first thing we need to do is place the robot on a markable surface (e.g. whiteboard, a large sheet of paper, etc.) and mark a line perpendicular to the robot, straight through its center, as seen in the below image:

Before you run the program, position both wheels so that each encoder is exactly over a slot. This will help synchronize the encoders as we want as precise an experiment as possible. Now, run the program.

Next, mark another perpendicular line in the same manner as you did before, only relative to the robot's new position.

Finally, use a protractor to measure the angle formed between these two lines, and that will determine the angle through which the robot turned.

### The Results

Our tests resulted in an angle of around 119 degrees. It is highly recommended that you perform the test a few times for accuracy purposes.

## Writing a function

Now that we know the angle our robot turned through with 8 encoder ticks, it will be simple to find the ratio of encoder ticks to degrees turned.

If the robot turned 119 degrees in 8 encoder ticks, then the ratio of encoder ticks to degrees turned must be

8 / 119 ≈ 0.067 encoder ticks per degree.

We can multiply this ratio by the desired amount of degrees we wish to turn, and that will output the amount of encoder ticks needed. Since the Arduino UNO with RobotC firmware cannot handle floating-point (decimal) operations, we will express this as:

Encoder ticks needed = (8 * degrees to turn) / 119.

This is exactly the same operation however we multiply to get as large a number as possible before we complete a division. Keep in mind we are bound to lose an amount of accuracy because of the lack of floating-point division, and the low resolution of the encoders.

Now, all we need to do is convert the previously used code into a function with two parameters (degrees to turn and speed) using the formula we just calculated. We will write two functions, one for turning left, one for turning right. While it would be possible to use only one function, it can get confusing and complex, and when calling the function you will have to remember the direction of rotation. Having two functions makes it much clearer, easier, and more useful, even though there might be a few more lines of code.

 ```void turnLeftDeg(int degrees, int power) { //Reset encoders leftEncoderCount = 0; rightEncoderCount = 0;   //Determine tickGoal int tickGoal = (8 * degrees) / 119;   //Start the motors in a left point turn. motor[leftServo] = -1 * power; motor[rightServo] = power;   //Since the wheels may go at slightly different speeds due to manufacturing tolerances, etc., //we need to test both encoders and control both motors separately. This may result in one motor //going for longer than another but it will ultimately result in a much more accurate turn. while(rightEncoderCount < tickGoal || leftEncoderCount < tickGoal) { if(rightEncoderCount > tickGoal) {motor[rightServo] = 0;} if(leftEncoderCount > tickGoal) {motor[leftServo] = 0;} } //Make sure both motors stop at the end of the turn. motor[leftServo] = 0; motor[rightServo] = 0; }```

To turn right, we just change the function accordingly:

 ```void turnRightDeg(int degrees, int power) { //Reset encoders leftEncoderCount = 0; rightEncoderCount = 0;   //Determine tickGoal int tickGoal = (8 * degrees) / 119;   //Start the motors in a left point turn. motor[leftServo] = power; motor[rightServo] = -1 * power;   //Since the wheels may go at slightly different speeds due to manufacturing tolerances, etc., //we need to test both encoders and control both motors separately. This may result in one motor //going for longer than another but it will ultimately result in a much more accurate turn. while(rightEncoderCount < tickGoal || leftEncoderCount < tickGoal) { if(rightEncoderCount > tickGoal) {motor[rightServo] = 0;} if(leftEncoderCount > tickGoal) {motor[leftServo] = 0;} } //Make sure both motors stop at the end of the turn. motor[leftServo] = 0; motor[rightServo] = 0; }```

### Test Program

Finally, we are going to test these functions by making the robot do four turns which should make it end up back at the starting position. All we need to do is put both functions in the program, then call them from within task main(). We also need to include the encoder monitoring task constructed earlier.

 ```#pragma config(CircuitBoardType, typeCktBoardUNO) #pragma config(PluginCircuitBoard, typeShieldParallaxBoeBot) #pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0) #pragma config(Sensor, dgtl2, rightEncoder, sensorDigitalHighImpedance) #pragma config(Sensor, dgtl3, leftEncoder, sensorDigitalHighImpedance) #pragma config(Motor, servo_10, leftServo, tmotorServoContinuousRotation, openLoop, IOPins, dgtl10, None) #pragma config(Motor, servo_11, rightServo, tmotorServoContinuousRotation, openLoop, reversed, IOPins, dgtl11, None) //*!!Code automatically generated by 'ROBOTC' configuration wizard  !!*//   //used to store the count of the motor clicks int leftEncoderCount = 0; int rightEncoderCount = 0;   //a background task used to monitor the encoders and count the clicks task encoderTask() { //get the state of the encoders so that we know when they have changed // for example if the last state was high and the current state is low // or vise-versa, then we know that the encoder incremented another click. int lastStateLeft = SensorValue[leftEncoder]; int lastStateRight = SensorValue[rightEncoder];   while(true) {   //get the current state of the left encoder int state = SensorValue[leftEncoder];   //has the left encoder changed states if (lastStateLeft != state) { //if so, then increment the leftEncoderCount by 1 leftEncoderCount++;   // then store the current state as the last state so that we can keep monitoring every change lastStateLeft = state; }   //repeat for the right encoder.   state = SensorValue[rightEncoder]; if (lastStateRight != state) { rightEncoderCount++; lastStateRight = state; }   //we add a pause between updates to keep from hogging the CPU and // so that we debounce the encoders. wait1Msec(10); } }   void turnRightDeg(int degrees, int power) { //Reset encoders leftEncoderCount = 0; rightEncoderCount = 0;   //Determine tickGoal int tickGoal = (8 * degrees) / 119;   //Start the motors in a left point turn. motor[leftServo] = power; motor[rightServo] = -1 * power;   //Since the wheels may go at slightly different speeds due to manufacturing tolerances, etc., //we need to test both encoders and control both motors separately. This may result in one motor //going for longer than another but it will ultimately result in a much more accurate turn. while(rightEncoderCount < tickGoal || leftEncoderCount < tickGoal) { if(rightEncoderCount > tickGoal) {motor[rightServo] = 0;} if(leftEncoderCount > tickGoal) {motor[leftServo] = 0;} } //Make sure both motors stop at the end of the turn. motor[leftServo] = 0; motor[rightServo] = 0; }   void turnLeftDeg(int degrees, int power) { //Reset encoders leftEncoderCount = 0; rightEncoderCount = 0;   //Determine tickGoal int tickGoal = (8 * degrees) / 119;   //Start the motors in a left point turn. motor[leftServo] = -1 * power; motor[rightServo] = power;   //Since the wheels may go at slightly different speeds due to manufacturing tolerances, etc., //we need to test both encoders and control both motors separately. This may result in one motor //going for longer than another but it will ultimately result in a much more accurate turn. while(rightEncoderCount < tickGoal || leftEncoderCount < tickGoal) { if(rightEncoderCount > tickGoal) {motor[rightServo] = 0;} if(leftEncoderCount > tickGoal) {motor[leftServo] = 0;} } //Make sure both motors stop at the end of the turn. motor[leftServo] = 0; motor[rightServo] = 0; }   task main() { //Start the encoder monitoring task, which will run in the background. StartTask(encoderTask);   //Turn right 90 degrees with power 30 turnRightDeg(90,30);   //Wait a bit so the robot's momentum does not effect the next turn wait1Msec(500);   turnLeftDeg(50,40); wait1Msec(500); turnRightDeg(160,20); wait1Msec(500); turnLeftDeg(200,30); }```