Let's Make Robots!

How to use infrared receiver sensors for collision avoidance


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.
caleb's picture

thanks man !

Dan M's picture

This is a well thought-out page. I will collect it to send people here when they have questions about this subject.

Good job.

 

Ro-Bot-X's picture

Thanks Dan!

vishurockssrivastava's picture
This is one of the best tutorials that I read. Great job.
Dannyv's picture

This part:

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

however I don't know this programming language, I see this loop repeats 384 times, but the tsop will see this as continuous noise and bock this.. Try to change that in:

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

 

so it repeats only 5 times, what should be enough.

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! : )

 

Ro-Bot-X's picture

It should work with TSOP4838, I've tested it some time ago. There are 2 things that might not let it work. First, the length of the burst pulse, in the code I used 10ms, perhaps you can shorten it to about 2-3ms. Also, the receiver may need to be shielded from reflected IR light going out from the back of the LEDs or it will switch to low regardless if there is an object in front or not. If the receiver does not switch to low at all, it may be defective.

Thanks for the tips. It seems that it is defective or I got the wrong part. I checked the impedance accross pins 1 and 3; I should be getting 30Kohms according to the datasheet, I'm reading close to 100K. I tried shortening the burst length to 2 and 3ms (int i = 0; i <= 115; i++) and (int i = 0; i <= 77; i++), still no response from the receiver (doesn't go low). I'll try to get another one and give that seller a - feedback if he doesn't do something about it. : )

Ok so I've got a new set of TSOP4838 receivers but I'm still getting the same results like I did with the first receiver I got (it still wouldn't go low). And again if I switch it with the other module which I believe is a PNA4602m receiver, the proximity sensor works on about 5-6 inbches. I bought a total of 5 TSOP4838s and all of them are doing the same thing. They can't be all bad... Plus I purchase them from a different store than the first one. After some experementations, I found out that if I use the remote for my Sharp TV, the TSOP4838 is able to pick it up; so with the code and the circuit exactly as above, it would flicker the LED. I did a little research and it seems that Sharp uses the same 38KHz carrier frequency on their remotes. I can't think of any reason why the TSOP4838 would detect an IR signal coming from the remote and not with the generated one (as indicated above). It also seems that the 4838 requires a shorter burst time; which I also did try with several different cycles but still no effect. It just doesn't make sense to me. I'm sure I'm missing something. So please if anyone can shed some light, I'm very grateful!

Ro-Bot-X's picture

Looks like either your IR LED is of a different light length (it has to be 940nm) or there is something fishy about the connection. Hard to say... I had limited success when using 3mm IR LEDs but it worked great with 5mm LEDs and if I lower the resistance to about 22 ohms I can detect a wall at a couple of meters away. TSOP4838 is what I'm using and it works perfectly, so if you got new sensors and still does not work, it has to be something else. 

To test the sensor even without the microcontroller, you can tie the cathode of a red or green LED to the signal (output) pin of the sensor and the anode through a 220 resistor to the Vcc pin. When the output pin goes low, the LED will light on. If the sensor works with the remote and it doesn't with the IR LED from the microcontroller then look for the problem there. Either the LED or the code is not quite right. You don't specify what kind of microcontroller you use, I have assumed you're using an Arduino, but if you set the fuses yourself, perhaps there might be a problem there (for instance you have divide by 8 checked so the code does not run at the proper clock rate). Also, if you use a breadboard, there might be a bad connection in the breadboard itself, many had problems with that, including me.