Using a Light Sensor to Stop at a Line
|
Video
Creating a Line to Detect
Before we worry too much about any sort of programming or setup, we need to find or create a nice environment for the robot to detect differences in light shades. In the examples, black electrical tape forms dark lines on the surface of a large whiteboard. A setup similar to this would have the advantage of easy customization and high contrast, but any surface with large color differences between different sections will do. The bigger the contrast, the more easily the robot will tell the difference between surfaces, and the less errors the robot will make. You may want to place the robot on top of each different color patch in Debug mode to get a rough idea of the value that the light sensor returns for the terrain. If the values are close together, even within 10-20 of each other, the robot may have problems distinguishing between the two and a different pair of surfaces should be used.
Configuring the Light Sensor
In a new file for RobotC, open the Motors and Sensors Setup window and configure the shield and motors. Then tab over to Analog 0-5, rename anlg0 to "lightSensor," and change it's type to "Light Sensor."
Once you're done with that, go to Digital 0-13 and rename dgtl3 to "lightLED" while changing its type to "Digital Out"
Once you are done with the setup window, RobotC should produce a bock of code that looks something like this:
#pragma config(CircuitBoardType, typeCktBoardUNO) #pragma config(PluginCircuitBoard, typeShieldDFRobotMotor) #pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0) #pragma config(Sensor, anlg0, lightSensor, sensorReflection) #pragma config(Sensor, dgtl3, lightLED, sensorDigitalOut) #pragma config(Motor, motor_5, rightServo, tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl5, dgtl4) #pragma config(Motor, motor_6, leftServo, tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl6, dgtl7) //*!!Code automatically generated by 'ROBOTC' configuration wizard !!*// |
Programming the robot
For this task, we want the robot to drive forward and stop at a dark line.
Using what we've learned in previous lessons, the first thing that comes to mind is to use an infinite while() loop with if() and else statements to keep the robot going until the robot detects the line.
Because the light sensor produces a wide range of values, we're going to need to put the operator > to denote that the light sensor is looking for a value that is above 60 (i.e. fairly dark).
if (SensorValue[lightSensor] > 60) //when the light sensor detects dark, presumably the line (Value may need to be adjusted based on the darkness of the surface and the line) { // Stop motor[leftServo]=0; motor[rightServo]=0; } else { //Drive Forward motor[leftServo]=100; motor[rightServo]=100; } |
Putting it in task main() so produces something like this:
task main() { while(true) //repeat indefinitely { if (SensorValue[lightSensor] > 60) //when the light sensor detects dark, presumably the line (Value may need to be adjusted based on the darkness of the surface and the line) { // Stop motor[leftServo]=0; motor[rightServo]=0; } else { //Drive Forward motor[leftServo]=100; motor[rightServo]=100; } } } |
There is one small problem with using this method to program the robot: if the line the robot is trying to detect is too thin, the robot will overrun the line (since the robot motors coast a bit from momentum even when set to speed 0) and the light sensor will pick up the lighter color again, meaning that the robot will "stutter" and pause for a little bit when it detects the line, then start up again once the sensor is past it.
To solve this issue (and make the code a little simpler), we're going to change the condition in the while() statement.
while (SensorValue[lightSensor] < 60) // while the light sensor is detecting light ground, not the line (value may need to be adjusted based on the darkness of your surfaces) { motor[leftServo]=100; motor[rightServo]=100; } //when no longer detecting a light surface, stop motor[leftServo]=0; motor[rightServo]=0; |
Notice that we have changed the greater than (>) operator to a less than (<) operator. This is because the goal is now to keep the robot driving (thus the while() loop) while the surface is light and permanently stopping when it is no longer so, unlike the previous program, which continually evaluated whether the surface was light or dark and started or stopped the motors accordingly.
Insert this code into the task main, and you come out with:
#pragma config(CircuitBoardType, typeCktBoardUNO) #pragma config(PluginCircuitBoard, typeShieldDFRobotMotor) #pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0) #pragma config(Sensor, anlg0, lightSensor, sensorReflection) #pragma config(Sensor, dgtl3, lightLED, sensorDigitalOut) #pragma config(Motor, motor_5, rightServo, tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl5, dgtl4) #pragma config(Motor, motor_6, leftServo, tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl6, dgtl7) //*!!Code automatically generated by 'ROBOTC' configuration wizard !!*// task main() { while (SensorValue[lightSensor] < 60) // while the light sensor is detecting light ground, not the line (value may need to be adjusted based on the darkness of your surfaces) { //Drive Forward motor[leftServo]=100; motor[rightServo]=100; } //when no longer detecting a light surface, stop motor[leftServo]=0; motor[rightServo]=0; } |
Extra Help: Using the Debugger to Read Sensor Values
The RobotC debugging program is a useful little tool that allows you to do a variety of things to monitor the process of a program. One of these features is the ability to display the values read by the various sensors that have been configured in the program. By now you have noticed that every time you compile and download a program, an annoying little window that you have to close like this comes up:
This is the primary debugging window. It allows the user to start and stop the program at will, and go command by command through the program, both of which can be useful for discovering bugs in the program. Let's leave it be for now, however. Instead let's go to Robot -> Debugger Windows and make sure that the bar that says Sensors has a checked box next to it. (if it doesn't, just click it and it will come up).
Now, look down at the box under the main coding area, labeled Sensors. You should see two rows that each display the data for a sensor, in this case, the data for the light sensor and the light sensor's LED. The window displays the port number (index location) of the sensor, it's name, its type, and most importantly, the value it is currently reading. Our sensor is currently positioned over the whiteboard, and is reading 43 as its value.
Once you have taken the reading of the light area, you will then want to proceed to read the value of the darker line (and any other surfaces the robot may need to transverse and detect, or in some cases, not detect) by moving the light sensor to the center of the line. It is important that you read the darkest value on the line, since the values on the border of the line (where the sensor is also picking up parts of the lighter region) tend to be much lower. In our case, we read a massive 194 for our black electrical tape lines.
In theory, the best threshold is right smack in the middle of the two values, in this case, somewhere between 110 and 115. Our threshold, 60 is a little on the low side because we noticed that the line did not have extremely high values for very long, and so we lowered the threshold to make sure the robot would detect the line every time, while the light values on the plain whiteboard were constantly in the 40-45 range so we had no worries of the white accidentally tripping off the sensor and stopping the robot before the line.
For a little extra amusement, you can see what values other objects return when placed under the light sensor


