Let's Make Robots!

How to use infrared receiver sensors for collision avoidance

Use a 38kHz remote control receiver sensor for detecting objects and avoid collision.

Do you want to know how to use cheap infrared receiver sensors to detect objects? How to build a Single or Dual proximity sensor and determine fast if you have objects or a clear path ahead or on the sides? Then read on, my friends, read on!

When I started to build hobby robots, I wanted to start from the basics and go up to the fancy stuff. I never got good enough at the fancy stuff (I lack advanced programming skills), but I learned the basics (took me some years to do it, as there was not too much info on how to do it with AVR micros). There are good books to learn robotics, but almost all of them are for PIC microcontrollers. Because at the time the hardware programmer was expensive (about $100) and I was supposed to buy the PIC Basic Pro ($100) to be able to do the projects in the books, I wasn't too eager to start using PICs. One of the books I read that helped me A LOT in understanding the basics of robot functioning was the text from Parallax.com called "Robotics with the BOE-Bot". I never owned a BOE-Bot or a BasicStamp for that matter, but the book is written for students to learn robotics and the principles are true for any robot. After I was introduced to AVR microcontrollers and Bascom-AVR I wrote the code in Bascom for almost every experiment in that text book. Then I found Arduino, and imediately I could adapt the code to work with the new board.

Nowadays almost nobody is using one of the cheapest methods to detect objects, avoid obstacles or find the range to the detected objects. Why? Perhaps because you need more than one pin from the microcontroller and a few more lines of code than needed when you use a Sharp IR (infrared) or Ping US (ultrasonic) sensor. And it bugs me because you almost never use the full potential of the Sharp sensor, you always set a treshold and you treat objects that are further away as non existent and detect only objects that are closer. Basically you are using it as a digital On/Off sensor with the treshold adjustable in the code. The more experienced builders are using the Sharp sensors or the Ping sensors, so that example is followed by the beginners, who are not aware that there are other methods for object detection.

So I decided to make this tutorial about using the regular IR modulated receiver sensor mostly found in TVs and VCRs. I will use some information from the text book mentioned above (read Chapter 7: Navigating with IR headlights) with Arduino code for exemplification.

Ok, so what is that sensor? How does it work? How can we use it for our purpose? Let's see:

The most popular sensor found in the US is made by Panasonic and the part number is PNA4601M. It may come with or without a metal shield that you need to connect to the ground. You will find even a cheaper sensor made by Vishay, the TSOP4838 sensor. I prefer this sensor over the Panasonic one because I can use it with low voltage systems (3.3V) directly. The sensor is an infrared remote control signal demodulator and it is sensitive only to infrared light that is modulated in the 38kHz frequency. What does that mean? It means that the light is turned on and off 38000 times per second. The sensor filters out any other frequency or straight IR light. You can find sensors that are sensitive to other frequencies, from 36 up to 56kHz. But we'll talk about that later. The sensor will output a High signal unless it detects a proper modulated light when it will output a Low signal as long as the light is on.

If the sensor only detects IR light modulated in the 38kHz frequency, how can we give it to it? Well, we will use an IR LED that we will turn on and off 38000 times per second. We will aim the LED towards the front of the robot and expect that the modulated light will go out and bounce off an object and come back to be detected by the sensor (also facing forward). Easy.

How do we connect the sensor and the LED to Arduino (or any other mocrocontroller)? The sensor looks like a rectangular capsule with a little dome on one face and 3 leads. If you hold the sensor with the dome towards you and the leads down, the first lead from the left is Signal, the middle lead is Ground, the right lead is Positive. You need to connect the Signal lead to any digital pin on the Arduino board, the Ground to the GND pin and the Positive to the 5V pin. The LED has a long lead that is the anode which you will connect to a series resistor of 220 ohm then the resistor to a digital pin on Arduino. The short lead is the cathode, which you will connect to the ground on Arduino. That's it.

Now how do we code it? First, we need to declare the pins where we connected the sensor and the LED, before the setup() function. While we are at it, let's also declare the Digital 13 LED:

#define IRsensorPin 9
#define IRledPin 10
#define D13ledPin 13 

We need to create a function that will modulate the LED 38000 times per second for about 10 miliseconds. To find out the time for the LED to be On and Off we divide 1 second by 38000 and we get 0.000026 seconds, or 0.026 miliseconds, or 26 microseconds. We will turn the LED On for half that and Off for the other half of the time, resulting a 50% duty cycle. Further, we will divide 10 miliseconds by 0.026 miliseconds to find out how many times we need to repeat flickering the LED and we get 384.

 

void IR38Write() {
  for(int i = 0; i <= 384; i++) {
    digitalWrite(IRledPin, HIGH);
    delayMicroseconds(13);
    digitalWrite(IRledPin, LOW);
    delayMicroseconds(13);
  }
}

Nice. Now we write our setup() function where we set the pin mode for the LED to be output, since all Arduino pins are set to input by default. We set the pin Low so the LED does not output any light until needed. We also set the Digital13 LED to output so we can see when an object was detected.

void setup(){
  pinMode(IRledPin, OUTPUT);
  digitalWrite(IRledPin, LOW);
  pinMode(D13ledPin, OUTPUT);
  digitalWrite(D13ledPin, LOW);
}

 

In the loop() function we flicker the IR LED and check the sensor for any reflection. If a reflection was detected, we turn on the D13 LED, if not, we turn it off. Instead of turning on and off the D13 LED you can drive or stop your robot.

void loop(){
  IR38Write();
  if (digitalRead(IRsensorPin)==LOW){
    digitalWrite(D13ledPin, HIGH);
  } else {
    digitalWrite(D13ledPin, LOW);
  }
  delay(100);
}

 

That's it. Now we can play with our hand in front of the sensor and see how far the sensor detects it. But what do we do if the distance is too far? Or too close? How do we adjust the detection range (by range I mean the interval of distances starting from close to the sensor and all the way to where the sensor stops detecting the object)? There are 2 methods: a hardware method and a software method. Let's talk about each.

The hardware method of adjusting the detection distance. Remember we used a resistor in series with the IR LED? We add a 5 kilo-ohms potentiometer in series with them. We can then adjust the pot until the sensor detects the object at the distance we want. If we need a closer range, we replace the resistor with a smaller value, like 100 ohms and adjust the pot until we get the desired distance.

The software method. Remember we split those 26 microseconds in 2 equal values to get a 50% on/off duty cycle? We can decrease the On time and increase the Off time to keep the same 26 microseconds, resulting a duty cycle of less than 50%. The LED will shine with a smaller power, thus reducing the detection distance. If we need to increase the distance, we increase the On time.

Getting more advanced. With a sigle sensor, we can detect any object in front of the robot in a 20-45 degree cone, depending on the LEDs lense (see the data sheet for your particular LED). The sensor has a very large detection cone that covers about 120 degrees all around the sensor. If we want to reduce the detection cone we need to place a tube in front of the sensor so it detects only the light that comes directly in front of it.  We can mount the sensor and the LED on a servo and look left and right when we detect an object in front of the robot, to see where we can safely turn the robot and continue driving. With this method we get a whole 180 degrees range in front of the robot where we can detect objects.

Here is the code for a scanning IR Proximity sensor:

#include <Servo.h>
#define IRsensorPin 9
#define IRledPin 10
#define ServoPin 11
#define D13ledPin 13 
#define Front 90
#define LeftSide 0
#define RightSide 180

 

byte ObjectDetected=0;

Servo PanServo;

void IR38Write() {
  for(int i = 0; i <= 384; i++) {
    digitalWrite(IRledPin, HIGH);
    delayMicroseconds(13);
    digitalWrite(IRledPin, LOW);
    delayMicroseconds(13);
  }
}

 

void setup(){
  pinMode(IRledPin, OUTPUT);
  digitalWrite(IRledPin, LOW);
  PanServo.attach(ServoPin);
  PanServo.write(Front);
  pinMode(D13ledPin, OUTPUT);
  digitalWrite(D13ledPin, LOW);
  
  // drive forward
}

 

void loop(){

  ObjectDetected=0;
  IR38Write();  // flicker the LEDs
  if (digitalRead(IRsensorPin)==LOW){ //check the sensor, if high
    // stop the robot, object in front
    PanServo.write(LeftSide); // look to the left
    delay(100); // wait for the servo to get there
    IR38Write(); // flicker the LEDs
    if (digitalRead(IRsensorPin)==LOW){ //check the sensor
      ObjectDetected=1; // object on the left
    }
    PanServo.write(RightSide); // look to the right
    delay(200);  // wait for the servo to ghet there
    IR38Write(); // flicker the LEDs
    if (digitalRead(IRsensorPin)==LOW){ // check the sensor
      ObjectDetected=2;  // object on the right
    }
  }
  // now we can decide where to go by looking at the ObjectDetected value
  switch (ObjectDetected){
    case 1:
      // turn right, object detected on the left
      break;
    case 2:
      // turn left, object detected on the right
      break;
  }
  PanServo.write(Front); // look ahead
  delay(100); // wait for the servo to get there
  // drive forward
}

 

You need of course to add your code for driving and turning the robot instead of the comments.

Another method is to use 2 sensors or 2 IR LEDs, one for detecting objects on the Left, one for the Right. This method is fast, but it doesn't cover the whole 180 degrees range like the sensor on the servo. But most of the times is good enough, asuming you can find a pair of LEDs with a wide emiting cone (45 or even 60 degrees). You can get the entire 180 degrees by using 2 sensors mounted on the left and right side, angled outwards (like this: /   \  but aim for about 30 degrees, the slashes look like about 60 degrees) and use 5-6 LEDs in parallel spread around to send the light in all directions. You flicker all the LEDs at the same time, then you check the Left sensor, flicker the LEDs again and check the Right sensor. If you detect on the left, turn right, if you detect on the right, turn left, if you detect in both directions, that means there is an object in the front of the robot. If you decide to use 2 LEDs, mount one on the Left, and one on the Right, with the sensor in the center facing forward. Flicker the Left LED, check the sensor, flicker the Right LED, check the sensor again. This is called a Dual IR Proximity Sensor.

Here is the code for a Dual proximity sensor:

#define IRsensorPin 9
#define LeftIRledPin 10
#define RightIRledPin 11
#define D13ledPin 13 

byte ObjectDetected=0;

void IR38Write(byte IRledPin) {
  for(int i = 0; i <= 384; i++) {
    digitalWrite(IRledPin, HIGH);
    delayMicroseconds(13);
    digitalWrite(IRledPin, LOW);
    delayMicroseconds(13);
  }
}

void setup(){
  pinMode(LeftIRledPin, OUTPUT);
  digitalWrite(LeftIRledPin, LOW);
  pinMode(RightIRledPin, OUTPUT);
  digitalWrite(RightIRledPin, LOW);
  pinMode(D13ledPin, OUTPUT);
  digitalWrite(D13ledPin, LOW);
  // drive forward
}

 

void loop(){
  ObjectDetected=0;
  IR38Write(LeftIRledPin); // flicker the left LED
  if (digitalRead(IRsensorPin)==LOW){ // check the sensor
    ObjectDetected=1; //object on the left
  }
  IR38Write(RightIRledPin); // flicker the right LED
  if (digitalRead(IRsensorPin)==LOW){ // check the sensor
    ObjectDetected=ObjectDetected+2; // object on the right
  }
  // now we can decide where to go by looking at the ObjectDetected value
  switch (ObjectDetected){
    case 0:
      //drive forward, no onject detected
      break;
    case 1:
      // turn right, object detected on the left
      break;
    case 2:
      // turn left, object detected on the right
      break;
    case 3:
      // turn around, object detected in the front
      break;
  }
  delay(100);
}

 

One more thing, the text book explains that if we change the frequency from 38kHz to several higher values, (40, 42, 44kHz) by making the delayMicroseconds(13) smaller (both of them!), we make the sensor less sensitive and thus determine the distance range where the object is at that moment. For example, if at 38kHz the object is detected at 20 inches, at 40kHz it will be detected say at 16 inches, at 42kHz at 10 inches, at 44kHz at 4 inches. You need to actually determine what are the exact values for your setup, but you can determine if the object is far, close, closer or right in front of your robot.

Oh yeah, let's not forget that the IR light gets reflected differently by different colored objects, for instance white reflects more than black, so the actual distance to the object greatly depends on the color of the object.

That's about it, I think I mentioned everything that was important. I will add pictures with the schematic another day, now I'm toasted. Let me know if I need to explain anything in more detail.

 

Update:

I forgot to mention something very important: you have to make sure no IR light escapes the LED sideways and backwards. Use a shrink tube and make sure you squeze it at the leads. Even with a shrink tube on my LED, the IR sensor still picked up some light. I had to add another larger shrink tube over it to completely block sideways light.

You can use the same sensor for remote control or object detection, or, you can choose 2 sensors that work at a different frequency, say 38kHz for the remote and 56kHz for the object detection. You can have 2 or more robots use the object detection sensor to talk to each other, exchange commands or other info. Detect the lenght of the IR signal to switch between obstacle detection and communication. The communication protocol has a start bit that is about 2.4~2.5 miliseconds. In the examples above, the IR signal is 10 miliseconds. Make it 2 miliseconds, then look for signals that are greater than 2.2 miliseconds to switch to receiving communication bits.

Thanks for taking the time to read this!

Cheers!

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

I am glad you took the time to share this with the masses.

*NOTE* may I suggest you consider adding a resistor in parallel with your 5k pot if you plan on using that method over hardware? It would give the 5k pot more adjustment, as a parallel resistor would bring the total resistance down to a more reasonable range. I would imagine anything over about 1k or so would pretty much kill off the IR LED output. I don't know this from practice mind you.

Thanks birdmun, 

it took me a whole evening writing this and I ended up exhausted. Did not had enough energy to make the schematic in Fritzing yet.

I never used a resistor in parallel with my pot as I usually set it for the biggest detecting distance I need and then I adjust the duty cycle to find how far the object might be. But I do that only if I need to get close to that object for instance to pick it up. If I want to avoid obstacles, I just use the 50% duty cycle and adjust the pot to a convenient distance. Most of the times a 1k pot worked for me, a 5k pot worked too, but as you said, with less adjusting area. I'll change the writeup to say 1k pot instead of 5k.

Great tutorial! This is a easy, cheap, and useful sensor that should get more mention.

If anyone is interested they can see videos of my Boe-Bot running with Dual IR Proximity Sensors here. I have an example running a course with fixed distance and following my hand using multiple modulation frequencies. The Boe-bot text is an awesome resource, and I like that you went through it with an AVR =)

Thank you very much for writing this. I was sub-consicously looking for that. I just want to point out that some people just love the Ping or Sharp sensor`s look. Their look is sorta cute but I agree that home-made IR rangefinders look more hardcore. Thanks again!

Great, added to [to do list].

I will try with a 555 as emitter... Adjusting the range by changing the duty cycle or the frequency.

Thanks a lot!

My robot works also great with it's homemade IR sensor. I really recommended to make your own instead of buying an expensive sharp one. If anyone is interested I can share my Picbasic code..

Be sure the receiver can't see the IR light directly. Place a (metal/black) barrier between them or put a piece of shrink tube around the led.(also makes the IR beam narrower)

Another tip is to place the led more on the front and the receiver little more back. As the IR light also will shines through the back of the receiver.

My sensor is done!

The soldering is crap but it works!

It works great! Thnk you soooo much!

you just made my day :-) I was looking for a simple cliff detaction sensor form my new project. Sharp is good but to big and i really don't need to know the distance. Well done!

Hey Thanks, Ro-Bot-X

Not only a good How-To but a good Why-it-Works.  

Not to get too Spamish, but you can make 5 of these for $3 with these:

http://arduino-direct.com/sunshop/index.php?l=product_detail&p=208

http://arduino-direct.com/sunshop/index.php?l=product_detail&p=210

And LMR members get 5%plus5% to the site if you sign up and send an email to me; terry@yourduino.com

I'll do some experiments with this... 

Great writeup job.  LMR may be collaborating in WIKI stuff and this could all end up where people can find it.. For now, my How-To stuff is here: http://arduino-direct.com/sunshop/index.php?l=product_detail&p=210

Regards, Terry

 

Wow Terry, you have good prices for the IR LEDs and receivers. I need to order a pack to do some tests and come up with a little board... I am already in the process of designing a board that can be mounted on a servo with a single sensor and another that can be used either separate or as a shield on Arduino, but tests must be done first.