Many TVs and other equipment are controlled by infrared remotes. When you point the remote at the TV and press the channel-up button, it changes the channel for you. Pressing the button makes the remote flash a pattern of infrared light at the TV. Infrared light is not visible to humans, but the TV’s infrared receiver detects the flashing light pattern. The TV’s microcontroller decodes this pattern and uses that information to change the channel for you.
The infrared light (IR LED) and receiver work great as "flashlights" and "eyes" for your ActivityBot. The IR LEDs shine forward, and the IR receivers detect reflections off of nearby obstacles.
The Propeller microcontroller receives reflection data from the IR receivers and uses it to make navigation decisions. In this way, your ActivityBot can roam around obstacles without first bumping into them.
In this activity, you will build and test the infrared object sensor system to make sure they detect objects in front of the ActivityBot. Just like the whisker circuits, there is one on the right and one on the left so the ActivityBot can determine which way to turn in relation to the obstacle.
In this case, each side needs two electronic components: an LED that rapidly flashes infrared light, and an infrared receiver to detect a reflection of that infrared light bouncing off of objects nearby.
(2) IR LEDs
(2) IR receivers
(2) 1 k-ohm resistors (brown-black-red)
(2) 220-ohm resistors (red-red-brown)
The infrared LEDs are designed to sit in the standoff tubes a certain way. The flat spot on the LED dome matches a flat ledge inside the tube. Holes on the bottom of the tube are labeled for the anode (+) and cathode (-) leads.
To direct the IR LED’s light, just like a flashlight beam, we’ll use an IR LED standoff (longer tube) and shield (shorter tube). The style of the standoff and light shield may vary, but they still fit together the same way.
The IR LED’s cathodes are connected to the D/A0 and D/A1 sockets on the Activity Board WX. These two special sockets can output a variable voltage. This voltage can be increased to make the IR LEDs dimmer, for closer range detection. (They can even be tested at different voltages—levels of dimness—to get a rough idea of the object’s distance, but that's for another activity...)
The test code displays the state of each IR detector:
Before continuing, it is VERY important make sure that both sides can reliably detect objects. Otherwise, your robot won't be able to navigate, and you won't know if your circuits or your code is the problem.
It's important to make sure both IR detectors work properly before continuing.
What the IR Sensors Can't See
Remember, the IR sensor system is looking for reflected infrared light. Light-colored objects reflect infrared light well, while dark-colored objects absorb infrared light instead of reflecting it. So, if your IR sensors can't see your black shoes, or a black plastic wastebasket, don't worry, that is normal!
First, int variables irLeft and irRight are declared to hold the IR detector output states. Then, inside main, the code sets P26 and P27 low to make sure that the D/A terminals are both providing 0 V to the IR LED cathodes.
The IR receivers are designed to only send detect signals if they see infrared light that flashes on/off at 38,000 times per second (38 kHz). So, in the main function’s while loop, freqout(11, 1, 38000) sends sends high/low signals that repeat 38000 times per second to the IR LED’s anode. This makes it flash on/off at that rate.
Next, irLeft = input(10) saves the IR receiver’s output in the irLeft variable. If the IR receiver’s output is high, meaning no IR reflection detected, input(10) returns a 1. If the receiver’s output is low, meaning reflected IR was detected, intput(10) returns a 0. The same process repeats for the right IR detector with freqout(1, 1, 38000) and irRight = input(2).
/* Test IR Detectors.c */ #include "simpletools.h" // Include simpletools int irLeft, irRight; // Global variables int main() // main function { low(26); // D/A0 & D/A1 to 0 V low(27); while(1) // Main loop { freqout(11, 1, 38000); // Left IR LED light irLeft = input(10); // Check left IR detector freqout(1, 1, 38000); // Repeat for right detector irRight = input(2); print("%c irLeft = %d, irRight = %d", // Display detector states HOME, irLeft, irRight); pause(100); // Pause before repeating } }
After the two infrared detection results are stored in irLeft and irRight, print(%c irLeft = %d, irRight = %d”, HOME, irLeft, irRight) does several things. The %c makes it send the HOME value (1) to SimpleIDE Terminal. That sends the cursor to the top-left position. After that, irLeft = %d, irRight = %d displays irLeft = followed by the 1 or 0 that irLeft stores, then irRight = followed by the 1 or 0 that irRight stores. Last, pause(100) keeps the rate that the SimpleIDE Terminal is updated down in the 10 times per second neighborhood.
Infrared — It means “below red”, and it refers to the fact that the light’s frequency is below that of red on the color spectrum.
Filtering — The IR receiver has a filter built in that makes it look for infrared that flashes on/off 38000 times per second (38 kHz). This allows it to differentiate between infrared coming from the TV remote and other IR sources such as halogen and incandescent lamps as well as sunlight streaming in through a nearby window.
Sunlight — Even though the IR receiver filters for sunlight, direct sunlight can often swamp the IR LED’s signal. Try to keep it indoors and out of any sunlight streaming in through windows.
The P26 and P27 lines are currently in use providing 0 V to the IR LED cathodes, so we cannot use their LEDs for indicating the IR detector states like we did with whiskers. So, let’s build some LEDs on the breadboard for indicators.
Parts Required
(2) 220 Ω resistors (red-red-brown)
(2) Red LEDs
(2) jumper wires (shown in black below)
Next, modify the code to turn a given light on if its IR receiver returns 0, or off if it returns 1.
You can further modify your IR detection code from Try This so that it sounds an alarm if it detects IR interference from the environment. This is a useful test to find out if nearby fluorescent lights are sending out signals that are interfering with your detector. The key is to not emit any infrared with the IR LEDs. If the receivers still send a low signal, there must be IR interference coming from another source. If interference is detected, make the piezospeaker get your attention with a series of six 50 ms, 4 kHz chirps separated by six 50 ms pauses. You can use a TV remote to test this.
With your IR object sensors built and tested, you are ready to make your ActivityBot roam and avoid obstacles without bumping into them.
The IR Roaming code example makes the ActivityBot go forward until it detects an obstacle. If it sees an obstacle on the left, it will turn right.
Likewise, if the ActivityBot detects an obstacle on the right, it will turn left. If it sees obstacles on both left and right, the ActivityBot will back up.
Not all obstacles are visible.
Many black objects will absorb infrared light instead of reflecting it. If in doubt, use your LED indicator program from the Build and Test the IR Detectors "Try This" section. The LEDs will show you if the object is visible to the IR object detectors.
This example is almost identical to Test IR Detectors.c from Build and Test the IR Sensor Circuits. The code changes are:
/* IR Roaming.c Use IR LEDs and IR receivers to detect obstacles while roaming. */ #include "simpletools.h" // Library includes #include "abdrive360.h" int irLeft, irRight; // IR variables int main() // main function { low(26); // D/A0 & D/A1 to 0 V low(27); drive_setRampStep(12); // Max step 12 ticks/s every 20 ms while(1) { freqout(11, 1, 38000); // Check left & right objects irLeft = input(10); freqout(1, 1, 38000); irRight = input(2); if(irRight == 1 && irLeft == 1) // No obstacles? drive_rampStep(128, 128); // ...full speed ahead else if(irLeft == 0 && irRight == 0) // Left & right obstacles? drive_rampStep(-128, -128); // ...full speed reverse else if(irRight == 0) // Just right obstacle? drive_rampStep(-128, 128); // ...rotate left else if(irLeft == 0) // Just left obstacle? drive_rampStep(128, -128); // ...rotate right } }
The drive_rampStep function is designed to be used in loops. After our call to drive_setRampStep(12), if a loop calls drive_rampStep(128, 128) it will increase speed 12 ticks/second at a time toward a final 128 ticks per second every time it’s called. If it’s calling 50 times per second (20 ms pauses), it will take 11 repeated calls (11/50ths of a second) to get to full speed. After the ActivityBot reaches full speed, repeated calls to drive_rampStep(128, 128) don’t change the speed any because it’s already there. If the loop then starts repeating drive_rampStep(-128, -128) calls, it’ll take 22/50ths of a second (getting close to half a second) to ramp from full speed forward to full speed reverse.
The if…else if…else if…else statement is what uses the irLeft and irRight detection result values for navigation. For example, if(irRight == 1 && irLeft == 1) it means that no objects are detected, so drive_rampStep(128, 128) sends the ActivityBot a 12 ticks/second step toward 128 ticks per second if it both wheels aren’t already running at that speed. If the ActivityBot sees an obstacle on it’s right, the else if(irRight == 0) condition becomes true, so each time through the loop, it takes 12 ticks/second steps toward rotating left in place every 50th of a second. Rotating in in place is achieved with the left wheel turning at full speed backwards at -128 ticks/second and the right turning at 128 ticks/second forward.
As the ActivityBot rotates left to avoid the right obstacle, the obstacle will disappear from the right detector’s view. At that point, the detection states will go back to irLeft == 1 and irRight == 1, which will make the first if statement true again, and drive_rampStep(128, 128) will start ramping back to full speed forward.
A pause between while(1) loop repetitions is not needed because the drive_rampStep function delays 1/50th of a second before returning.
drive_setRampStep — This function sets the maximum change in speed that drive_rampStep can cause every 1/50 of a second. The abdrive360 library defaults to 4, which means that the largest change in speed allowed is 4 ticks/second every 50th of a second. Although that default makes drive_ramp and drive_rampStep maneuvers nice and smooth, it’s not enough change in a small amount of time to detect and avoid obstacles at full speed. So, drive_setRampStep(12) triples the speed change allowed every 50th of a second. Not quite as smooth, but responsive enough to avoid the obstacles.
You can reduce the roaming speeds by changing the 128's in the program to lower values.
Increasing the value used in the drive_setRampStep function call to a higher value will make the robot more responsive, but also more twitchy. Reducing the value will make it smoother, but at some point, it won’t respond quickly enough and it will run into obstacles.
Links
[1] http://learn.parallax.com/activitybot/software-and-programming
[2] http://learn.parallax.com/propeller-c-set-simpleide/update-your-learn-folder