Using a light sensor to find light

From ROBOTC API Guide
< Tutorials‎ | Arduino Projects/Mobile Robotics/BoeBot
Revision as of 15:37, 16 October 2012 by Jwatson (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

A fun way to use the phototransistors with the BoeBot 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 it around with a flashlight!

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

Afterwards, your configuration code should look like this:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(PluginCircuitBoard, typeShieldParallaxBoeBot)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  leftLight,      sensorReflection)
#pragma config(Sensor, anlg1,  rightLight,     sensorReflection)
#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               !!*//

The Formula

The inherent problem with anything that senses light is that every room has different lighting. Human eyes are exceptional adapting to these differences, but to a robot, a room that seems well lit 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. Thankfully, we can use the magic Zero Justified Normalized Differential Shade calculation (Yes, it's really called that) to determine some relevant data about light levels.

But how does it work?

We cannot determine if the room is 'light' or 'dark' because those values are subject to the location they are in. What we can do, however, is find the side of the robot where the light is the brightest, and also how much brighter it is than the other side. We can then use this value on our drive motors in order to steer towards the light.

The calculation

The Zero Justified Normalized Differential Shade calculation can be written as:

noframe

This algorithm will give us a percentage (ranging from -100% to +100% of how far the brightest light is relative to the center. For example, if the light is a bit to the left, we will get a value of -20 or something similar. If the light is far to the right, we will get a value of around +50. We can then use these values to make the robot steer towards the light.

Steering towards the light

Since the percentage we get from the formula is positive if the light is on the right and negative if the light is on the left, then we want to add the value to the left servo and subtract the value from the right servo, as a number minus a negative is of course an addition.

We want to perform these operations on a base value - that is, the speed the motors should go when the formula returns zero (when the light is directly ahead). Let's leave that at 20.

Programming

With all that figured, we get this program:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(PluginCircuitBoard, typeShieldParallaxBoeBot)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  leftLight,      sensorReflection)
#pragma config(Sensor, anlg1,  rightLight,     sensorReflection)
#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               !!*//
 
task main()
{
  while(true)
  {
    int shadeDiff = ((SensorValue[rightLight] * 100) / (SensorValue[leftLight] + SensorValue[rightLight]) - (100 / 2)) * 2;
 
    motor[leftServo] = 20 + shadeDiff;
    motor[rightServo] = 20 - shadeDiff;
 
    //If the loop cycles too quickly, the debugger can return incorrect values. (this is not a bug)
    //adding a small delay solves this problem
    wait1Msec(10);
  }
 
}

Try it out with a flashlight in a shady area. The robot should steer towards the light. Keep in mind the sensors are not ranged - they will not be able to seek out light sources at a distance. They will only be able to detect the difference in light level of their immediate area.

Modification

You may notice that the robot does not react as much as you want it to when there is a significant light difference. To change this, standard practice is to multiply the result by a factor - e.g. 1.2 or 0.5. Since the RobotC Arduino UNO firmware cannot support decimals, we will instead incorporate the factor into the formula, so instead of returning a range of -100 to 100, it will return a range of -80 to 80, say, or -150 to 150. To do this, we will add another variable called 'factor' and replace the instances of '100'.

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(PluginCircuitBoard, typeShieldParallaxBoeBot)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  leftLight,      sensorReflection)
#pragma config(Sensor, anlg1,  rightLight,     sensorReflection)
#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               !!*//
 
task main()
{
  while(true)
  {
    int factor = 120; //This value worked for us however you might want to change it for your robot/flashlight intensity.
    int shadeDiff = ((SensorValue[rightLight] * factor) / (SensorValue[leftLight] + SensorValue[rightLight]) - (factor / 2)) * 2;
 
    motor[leftServo] = 20 + shadeDiff;
    motor[rightServo] = 20 - shadeDiff;
 
    //If the loop cycles too quickly, we get calculation errors. Therefore we regulate the speed by waiting a bit.
    wait1Msec(10);
  }
 
}