Let's Make Robots! | RobotShop

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.

Hey, I was just sketching on the "servo-pointed-sensor" stuff.  I would like a simple easy-to-reproduce design that uses a small servor to rotate sensors (Ultrasonic and IR) over 360 degrees. Some cool but expensive robots use a gear system or a continuous-rotation servo with limit switches.  

How about the sensor block sits atop a pencil-sized vertical shaft and the servo arms push and pull on a thin cord wrapped around the shaft and permanently attached at one pint so it doesn't 'creep'.

These sensors don't have real sharp horizontal resolution and reasonable accuracy is fine.

I bet someone has done this. Pointers? Comments?

I want a design that I can show that anyone with real simple materials and a trip to the hardware store or Fa Da Te (DIY in Italian) can make. 

I bet the 38 KHz IR could be made pretty sharp resolution with a simple lens and slit, though. 

 

 

 

 

I understand that because these receivers are only looking for IR light modulated at 38 KHz they are less susceptible to outside interference. I was wondering if there are also other advantages over a plain old IR Phototransistors like used in Mr General's Compound Eye or a QRD1114  

The IR phototransistors work at short distances. The IR demodulators work as far as 8 feet. All you need to get such a great distance is a powerful IR LED. Since the LED is pulsated for short times, you can have a very low resistance in series or even none and the LED will survive and will give up lots of light. At close distances you can make the pulse bursts less than 10 miliseconds, but at long distances it's a must. Make sure you're using 5mm LEDs not 3mm for long distances. I was experimenting with both sizes and saw a big difference with the same 220 ohm resistor in series with both.

Thank you

Anyone have one of these and working on updating to 38KHz operation??

I have some coming from Dagu, but will take a while to get here from YourDuino.com to me in Italy.  I think they will sell for about $10 in the LMR shop. Maybe we can all figure out an easy way to upgrade these to 38KHz and longer distances.. they certainly have lots of LEDs...    Here's the schematic.  Looks like it wouldn't be that hard to update it for 38KHz. 

We have the receivers here: http://arduino-direct.com/sunshop/index.php?l=product_detail&p=210

Wire into anything with a piece of this: http://arduino-direct.com/sunshop/index.php?l=product_detail&p=185

Anybody have time to try this out?? 

 

Regards, Terry King

...On the Mediterranean in Italy

terry@yourduino.com

 

Ro-Bot-X,

   Thanks for a great post!  This is exactly what I've been looking for.  I ran out and grabbed one of these sensors from the dreaded "Shack" to try it out.  I hooded my IR LED with some shrink wrap and set it forward of the sensor.  I get about 8 inches for 38 kHz at 5V.  I tried varying the duty cycle, but I didn't see much difference in the sensitivity.  Then, I tried to change the period.  At 34 kHz and at 44 kHz, I get no reaction at 50% duty cycle.  Is it just a matter of aligning the emitter and receiver?  I was hoping to take readings at various period lengths to determine distance.  I'd love to have the sensor trigger at 4-5 cm and another sensor to trigger at 7-8 cm.  Any help appreciated.

Thanks!

EDIT:

   I may have my problem.  I see that your code includes a delay between write/reads:

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

Thanks for trying out this sensor. I think some sensors (different manufacturers) may be less sensitive than others. The Panasonic PNA4601M sensor definitely behaves this way. I have an old TSOP4838 that also works, but there are many TSOP sensors that work at 38kHz and are not sensitive to other frequencies. To get 35.7kHz you have to set delayMicroseconds(14) in the IR38Write function, to get 41.6kHz set delayMicroseconds(12). Sensor alignment should not matter, because the LED emits a cone of light and the receiver also has a detection cone. More importantly is  to use a light color material as the object when testing (white paper for example). 

Oh, and the delay() function in Arduino expects miliseconds as argument. 100 miliseconds is just to have a break between sensor measurements.

 

In the circuit above the servo's black wire is connected to + instead of -

I guess you're right, I moved the servo a bit and the wire moved with it and I didn't check back. Thanks for noticing, I'll fix it.

Hello Everyone!

   My question is regarding the code above. Has it been verified to work with a TSOP 4838 38KHz receiver? Or has any one tried it and was successful? If it's verified to work with a 4838, then if anyone can please point me to the right direction... I have two IR receivers on hand; one is a 4838 and another one is a PNA4602m I salvaged from a broken toy. The 4602m works just fine with the code and circuit specified above but the 4838 doesn't. I might be missing something... I just bought the 4838 off Ebay, Im beginning to think it's defective since the 4602 works perfect. I tried different alterations on the code and the circuit like changing the half cycle delay lower and higher; or changing the supply voltage/resistance from the minimum to maximum based on the 4838's datasheet. Any opinion or comment is very welcome. Thanks in advance! : )