Programing the robot to use the light sensor to find light

From ROBOTC API Guide
< Tutorials‎ | Arduino Projects/Mobile Robotics/VEX
Revision as of 17:32, 8 August 2012 by Dwest (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
ArduinoArduino Tutorials and Guided ProjectsVEX + Arduino, Mobile Robotics Platform → Programing the robot to use the light sensor to find light

A fun way to use the VEX light sensors is to write a program which will make it turn towards the direction of strongest light, effectively driving towards the light. This means you will even be able to guide the robot around with a flashlight!

Configuring RobotC

The first thing we need to do is configure RobotC for our light sensors. Open up Robot > Motors and sensors setup, choose the Analog 0-5 tab, and then configure anlg0 as rightLight and anlg1 as leftLight. The type for both should be set to Light Sensor.

Also, configure the push button (digital port 2) as a touch sensor and call it 'button'.

Afterwards, your configuration code should look like this:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  rightLight,     sensorReflection)
#pragma config(Sensor, anlg1,  leftLight,      sensorReflection)
#pragma config(Sensor, dgtl2,  button,         sensorTouch)
#pragma config(Motor,  servo_10,        rightServo,    tmotorServoContinuousRotation, openLoop, reversed, IOPins, dgtl10, None)
#pragma config(Motor,  motor_11,        leftServo,     tmotorServoContinuousRotation, openLoop, IOPins, dgtl11, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

The nature of light

The inherent problem with anything that senses light is that every room has different lighting. Human eyes are exceptional at accounting for this change, but for a robot, a room that seems well lit to us could be either very dark, or very light. Therefore, we won't be able to compare a value as we usually do, e.g. something like this:

if (SensorValue[leftLight] > 120) {
  //Do something
}

will not work. That is because any given light value could be taken as either light or dark, depending on the room that the robot is in.

What to do?

Don't panic, all is not lost. We can use a programming technique called calibration to keep the readings the same in different light levels. This will also compensate for nasty differences between the sensors, a common problem with photoresistors.

Programming

To calibrate the sensors, we first need to find the value for 'dark' and the value for 'light'. We are going to have our program start with this 'calibration mode', and then leave to go to the main program when the button has been pressed.

We can make our lives easier by automating much of the calibration process - basically, all we do is save the highest and lowest values that both sensors encounter during the calibration. Therefore, during the calibration we just need to make sure that, at least once, the robot is in the darkest place that it will be during the duration of the program, and also the lightest place. So, if you plan to guide the robot with a flashlight, you would need to have the flashlight both shining on and off the robot before the program begins.

Here's the code:

  int leftValue = 0;
  int rightValue = 0;
 
  int leftLow = 1000; //High number so the < comparison will work (we don't want the low value to always be 0!)
  int rightLow = 1000; //Same deal here.
  int leftHigh = 0;
  int rightHigh = 0;
 
  // Auto-calibration ended by button press. The button will begin the light following.
  while(!SensorValue[button]) {
    leftValue = SensorValue[leftLight];
    rightValue = SensorValue[rightLight];
 
    //Get low and high values for the left sensor
    if(leftValue > leftHigh) {
      leftHigh = leftValue;
    } else if(leftValue < leftLow) {
      leftLow = leftValue;
    }
 
    //Get low and high values for the right sensor
    if(rightValue > rightHigh) {
      rightHigh = rightValue;
    } else if(rightValue < rightLow) {
      rightLow = rightValue;
    }

So, we have the values for 'dark' and 'light'. We will use these values in the main loop to establish a common scale of data which we can use for easy line following. Basically, we will convert the light sensor's range of values (e.g. 89 to 874) to something more manageable - a scale of 0 to 20, with 0 being dark and 20 being bright. Since we did the calibration, we actually have values for dark and light. This calculation is called 'mapping'. This is the code:

while(true)
{
  //Maps both sensors to a scale of 0-20. 20 was chosen so the calculation doesn't overflow (use a number larger than 16 bits)
  leftValue = (SensorValue[leftLight] - leftLow) * 20 / (leftHigh - leftLow);
  rightValue = (SensorValue[rightLight] - rightLow) * 20 / (rightHigh - rightLow);
}

Now, think about what we want the robot to do. We want it to steer towards the light by looking at which sensor (left or right) is receiving more light. Another way of putting this is saying we want to find the difference between the two sensors. Since the mathematical operation for difference is subtraction, we can get a very useful value by simply subtracting:

//Find the difference in light values
int shadeDiff = (leftValue - rightValue);

This operation will equal zero if the light is right in the middle, it will be positive if the light is on the right side, and negative if the light is on the left side. Since this is true, we can now simply add this value to the right servo's power, and subtract it from the left servo's power. We will also need to multiply it by some factor to change how much it effects the steering (a small factor, like 1, will make it effect the steering only a little bit. A large factor means it will respond more sensitively).

//Run the motors
motor[leftServo] = 40 - (shadeDiff * factor);
motor[rightServo] = 40 + (shadeDiff * factor);

Now, we have our complete program for following the light:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  rightLight,     sensorReflection)
#pragma config(Sensor, anlg1,  leftLight,      sensorReflection)
#pragma config(Sensor, dgtl2,  button,         sensorTouch)
#pragma config(Motor,  servo_10,        rightServo,    tmotorServoContinuousRotation, openLoop, reversed, IOPins, dgtl10, None)
#pragma config(Motor,  motor_11,        leftServo,     tmotorServoContinuousRotation, openLoop, IOPins, dgtl11, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
 
task main()
{
  int leftValue = 0;
  int rightValue = 0;
 
  int leftLow = 1000; //Arbitrary high numberso the < comparison will work (we don't want the low value to always be 0!)
  int rightLow = 1000; //Same deal here.
  int leftHigh = 0;
  int rightHigh = 0;
 
  // Auto-calibration ended by button press. The button will begin the light following.
  while(!SensorValue[button]) {
    leftValue = SensorValue[leftLight];
    rightValue = SensorValue[rightLight];
 
    //Get low and high values for the left sensor
    if(leftValue > leftHigh) {
      leftHigh = leftValue;
    } else if(leftValue < leftLow) {
      leftLow = leftValue;
    }
 
    //Get low and high values for the right sensor
    if(rightValue > rightHigh) {
      rightHigh = rightValue;
    } else if(rightValue < rightLow) {
      rightLow = rightValue;
    }
  }
 
  int factor = 8; //This value worked for us however you might want to change it for your robot/flashlight intensity.
 
  // Light following loop
  while(true)
  {
    //Maps both sensors to a scale of 0-20. 20 was chosen so the calculation doesn't overflow (use a number larger than 16 bits)
    leftValue = (SensorValue[leftLight] - leftLow) * 20 / (leftHigh - leftLow);
    rightValue = (SensorValue[rightLight] - rightLow) * 20 / (rightHigh - rightLow);
 
    //Find the difference in light values
    int shadeDiff = (leftValue - rightValue);
 
    //Run the motors
    motor[leftServo] = 40 - (shadeDiff * factor);
    motor[rightServo] = 40 + (shadeDiff * factor);
 
    //If the loop cycles too quickly, we get calculation errors. Therefore we regulate the speed by waiting a bit.
    wait1Msec(10);
  }
}