SHARP IR Distance Sensor - GP2D120XJ00F

Hey Guys,
Let me pick your brain for a minute here.
I'm having some trouble getting useful stuff out of this Sharp GP2D120XJ00F.
The datasheet for this thing is here: http://www.sparkfun.com/datasheets/Sensors/Infrared/GP2D120XJ00F_SS.pdf
I'm connecting it to an Arduino UNO, and it runs on the 5V power line.
I've got a few questions about this thing.

  • Can somebody talk me trough "linearising" the readings from this sensor? (I've read up a bit, but it makes little sense to me)
  • The data sheet mentions one should add a 10 (or more) uF bypass capacitor beween Vcc and Ground.
    I used a 33uF, 10V tantalium capacitor, but this got pretty hot pretty quick, so I must be doing something wrong.
  • Does anybody have any idea if it is at all possible to use two of these close together, or is it necesarry to (physically) seperate the sensors?

Thanks in advance,
Jim

The simplest way to linearize is to use a lookup table. Declare an array of 256 bytes (say) and for byte 0, store the distance that represents an analog reading of 0 (analogRead() >> 2, so we only look at 256 bytes). For byte 1 of the array, store the distance that represents an analog reading of 1, and so on.

Then, to map a reading to distance:

distance = lookupArray[analogRead(IR_PIN) >> 2];

I used a 33uF, 10V tantalium capacitor, but this got pretty hot pretty quick, so I must be doing something wrong.

Any chance you put it in backwards? Tantalum capacitors are polarized and only go in one way.

Does anybody have any idea if it is at all possible to use two of these close together, or is it necesarry to (physically) seperate the sensors?

Should be possible, but I'll let others weigh in.

--
The Rugged Circuits Yellowjacket: 802.11 WiFi module with ATmega328P microcontroller, only 1.6" x 1.2", bootloader

So the lookup table - this poses another problem - how does one get the values to store in this lookup table.
The graph is not quite precise enough, and doing it by hand / experimentation seems a long and arduous process.
I found this info - http://luckylarry.co.uk/arduino-projects/arduino-using-a-sharp-ir-sensor-for-distance-calculation/
But I could use some extra background information on the idea, I don't quite understand it enough to modify this to get linear 0 -> 1024 values out of my sensor.

About the cap - it was a ceramic one then, because it wasn't polarized.

Blue_Boy:
About the cap - it was a ceramic one then, because it wasn't polarized.

A 33ufd ceramic cap? Are you sure? It's hard to believe you found a non-polarized cap of that size. You have made some kind of error in either the cap you selected or how you wired it up. Caps don't get warm or hot if used properly.

Lefty

Can somebody talk me trough "linearising" the readings from this sensor? (I've read up a bit, but it makes little sense to me)

Worked with another sharp distance sensor and wrote the multimap() function just to do that. See - Arduino Playground - MultiMap -
it interpolates linear between points of the graph. More points means less error more processing time.

Hopes this helps,
Rob

I'll upload a picture of that cap in a bit.
About the multimap - that looks great!
Only problem is, it takes around 300 millis, best case scenario to run the code.
I have to run it twice, and also at least 24 times a second.
So, that solution will not be fast enough.

Only problem is, it takes around 300 millis, best case scenario to run the code.

Where does this 300 millis come from?
In my indicative test I did 10.000 worst case calls in ~1 seconds so 24 times per second would take approx 2.5 millis.....

(from the playground muiltimap article)
Performance
In a small test with an input array of 14 elements, 10.000 worst case calls took 1003 millis, 10.000 best case calls took 358 millis so on average 1361/2 = 680 micros per call. This performance is most affected by the size of the array it has to search, so the general advice is keep the array as small as possible. Note these numbers are only indicative.

Ihave to run it twice, and also at least 24 times a second.

Can you explain why you have to make 24++ measurements?
Can you tell more about your project?

If you need max speed the lookupcode proposed by RuggedCircuits in reply #1 is fastest. The # entries in the array can be as low or large (max 1024) as you want but multiples of makes the simplest math. You can even use 2 (or more) lookuparrays - one with bigger steps for the flat part and one with more steps for the dynamic part. You get something like:

int distance(int reading)
{
  if (reading < 256) return lookup1[reading>>1];  
  if reading < 512) return lookup2[reading>>2];  
  return lookup3[reading>>4]; 
}

in fact you might combine these three arrays into one (by using an offset)

int distance(int reading)
{
  if (reading < 256) return lookup1[reading>>1];  
  if reading < 512) return lookup1[reading>>2 + 128];  
  return lookup1[reading>>4 + 192]; 
}

The following is a much more efficient linearization function than that other link:

With a little effort, you can get that formula to work entirely with ints (or longs, depending on the kind of resolution you're looking for, but the resolution on those sharp sensors isn't all that great either way) and it'll run very fast.

Here is a sketch that I played around with to try and linearize the IR output. It's been awhile sense I used it so I don't recall my success with it, but it is something that might give you a starting point for further development.

/*
  IR distance Analog input, serial output
 
 Reads an analog input pin from a sharp IR2D120 analog IR sensor, prints the results to the serial monitor.
 

*/

// These constants won't change.  They're used to give names
// to the pins used:
const int analogInPin = A0;  // Analog input pin that the IR sensor is attached to

int R;
int scaled;
int avg;
int sensorValue;

void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(9600); 
}

void loop() {
  // read the analog in value 10 times to get a stable average reading:
  avg = 0;
  for (int i=0; i <= 9; i++){
      avg = avg + analogRead(analogInPin);
   } 
  sensorValue = avg / 10;  

 R = (29140/ (sensorValue + 5)) -1; // R is millimeters
 
 if (R > 350) {   // clamp value for out of range readings
    R = 350;}
    
 if (R < 50) {
    R= 50;}

  Serial.print("sensor = " );                       
  Serial.print(sensorValue);      
  Serial.print("\t output = ");      
  Serial.println(R);   
  delay(200);                     
}

Lefty

  • just a short comment to all the "linearizing functions" mentioned:

The one jraskell posted (Linearizing Sharp Ranger Data | Acroname) is the only one to use if you want to do it right. All Sharp IR-distance sensors function by the way of triangulation and if you do the math, you will end up with a formula distance = const / ( Measurement + offset).

Note that this is an inverse law, not a linear functional relationship. So it is generally not a good idea to use linearized methods for getting distance values out of such a sensor. By the way - for many applications, for example object avoidance, you do not need the distance in cm or inches. You can directly work with the raw measurements.

These Sharp sensors are quite fascinating devices - with two servos, you can even turn them into a slow 3D scanner (see attached image). They easily outperform ultrasound devices in terms of resolution.

One additional (important) note: these Sharp sensors are power hungry. The datasheet mentions a rather low current - but this is only the time averaged current consumption. The peak current these sensors are drawing when they are firing is immense! So in order to get good results, it helps to have a big capacitor close to the IR-sensors power connectors. Be sure to connect it with the right polarity, otherwise, things might get explosive...

Ideally, one pairs this big capacitor with a small capacitor to block high frequency noise as well (Google or any other search engine is certainly your friend here). And be sure to sample the output voltage with the appropriate delay after triggering a measurement... :wink:

3d_scan_04.jpg

1 Like

Hey Guys,
Here's an image of the capacitor, and the way I hooked it up.
(except for the fact that the capacitor I used did not seem to have a polarity)
I'll tinker a bit with the code you provided me with, and will keep you posted.
Thanks,
Jim

Ok, Here's a little update.
Got me some new capacitors, hooked up a big one and a small one (for HF filtering) and this seems to work.
I used the formulas found here - Linearizing Sharp Ranger Data | Acroname.
I more or less wrapped my head around the theory.
But, when I use the "R = (2914 / (V + 5)) - 1" as proposed at the end of the page, I get almost perfect distance readings in cm.
I would like to have them as a value from 0 - 254 though, but since I'm using floating point values, I don't think the map function is working out quite right.
Here's my code.

unsigned long previousMillis;
unsigned long currentMillis;

int sensorPin = A0;
float sensorValue = 0;
unsigned long sensorValueTotal = 0;
unsigned long numberOfSteps = 0;
float voltage;

void setup() 
  {
  Serial.begin(9600);
  }

void loop() 
  {
  currentMillis = millis();
  if (currentMillis - previousMillis > 40)
    {
    sensorValue = sensorValueTotal / numberOfSteps;
    sensorValue = (2914 / (sensorValue + 5)) - 1;
    sensorValue = map(sensorValue, 3, 40, 0, 255);
    Serial.println(sensorValue);
    //Serial.println(numberOfSteps);
    
    numberOfSteps = 0;
    sensorValueTotal = 0;
    previousMillis = millis();
    }
  
  sensorValueTotal = sensorValueTotal + analogRead(sensorPin);
  numberOfSteps ++;
  }

Hi Blue_Boy - nice that it worked out that easy... :wink:

Have a look at the reference to the map-function (http://arduino.cc/en/Reference/Map) - pretty easy to use, I think.

As the reference mentions in the appendix, the library version is working with longs; if you want to use your floats, either do an explict cast to long, or, even better, simply implement the formula given in the appendix yourself, with floats. Here's one possibility:

float myMap(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

As you want to map to 0-254, your call should read x_new = myMap(x,lowest_distance,highest_distance,0,254);

Note that you might want to reject too large and to small distance values coming from the Sharp-sensor, as these units have a limited operating range.

As for your capacitor - this is indeed a tantal one, and it has a polarity. Sometimes, there's a really tiny dot or "plus" on one of the legs, sometimes a single leg is slightly longer bend away from the main body of the capacitor. That's the plus pole....

Hey Guys,
Here's my code.
The analog reads from inputs 0 and 1 are the IR rangers.
At the top of the range I get severe jitter though.
Are they just that imprecise, or is it a problem in my code?
Like suggested, I connected the rangers to capacitors to stabilize the voltage.
Also, I take many readings and average them out. Should that not make them more stable?
So - any suggestions?

unsigned long previousMillis;
unsigned long currentMillis;

int numberOfSteps = 0;
int sensorValue[6];
long sensorValueTotal[6];

float tempFloat;

// Set calibrate to 255 to disable calibration
int calibrate = 255 ;
int sensorTop[] =    {25500, 27000, 510, 530, 530, 480};
int sensorBottom[] = {3500, 3500, 610, 650, 630, 600};

byte dataStart = 0xFF;

void setup() 
  {
  Serial.begin(9600);
  }

void loop() 
  {
  numberOfSteps ++;
  currentMillis = millis();
  
  for (int i = 0; i < 6; i++)
    {
    sensorValueTotal[i] = sensorValueTotal[i] + analogRead(i);
    }
  
  if (currentMillis - previousMillis > 40)
    {
    if (calibrate == 255) Serial.print(dataStart);
    
    for (int i = 0; i < 6; i++)
      {
      tempFloat = sensorValueTotal[i] / numberOfSteps;
      sensorValue[i] = (int) tempFloat;
      sensorValueTotal[i] = 0;
      
      // IR Ranger specifics
      if (i < 2)
        {
        tempFloat = (float) sensorValue[i];
        tempFloat = (2914 / (tempFloat + 5)) - 1;
        tempFloat = tempFloat  * 1000;
        sensorValue[i] = (int) tempFloat;
        }
      
      if (calibrate == i) Serial.println(sensorValue[i]);
      if (calibrate == 255)
        { 
        /// Only map to 254 because 255 is start byte!
        sensorValue[i] = map(sensorValue[i], sensorBottom[i], sensorTop[i], 0, 254);
        sensorValue[i] = constrain(sensorValue[i], 0, 254); 
        Serial.print((byte)sensorValue[i]);
        }
      }
    
    numberOfSteps = 0;
    previousMillis = millis();
    }
  }

check - Arduino Playground - Statistics - or - Arduino Playground - RunningAverage -

Actually, the measurements of a Sharp IR distance sensor are quite stable over time. If you notice jitter in the readings, it is most probably caused by a not sufficiently stable power supply. As I already remarked, these device draw a lot of current when they are firing. Be sure to have sufficient and good capacitors as close to the sensor as possible (ideally a big one for the large power spike, plus a 10nF one in parallel for the high-frequency components).

One the other hand, due to the principle these sensors work with (triangulation), they have a much greater resolution at short distances than on distances farther away. Very similar to human 3D vision which works best at close distances. Any noise you have in the system is basically magnified (distance-wise) if you are working at the upper edge of the distance range.

Note also that these IR sensors tend to work at distances greater than specified, but these measurements might not be as stable as those taken in the specified range.

In working with Sharp IR distance sensors, it is most important that you get the power supply to the sensors right and that you decouple these sensors sufficiently from the rest of your circuit, especially the Arduino. You might want to compare the readings you get with readings when you power the IR sensor with a separate battery.

As already mentioned, a critical thing is to make sure you are measuring a value that is within the standard distance range of the IR sensor.

If you look at the datasheet that shows voltage output Vs distance of sensed object there are two key items to see. First the response is non-linear within the valid mesurement range (therefore a need for a linearizing calculation of the raw value) and second that below the minimum range and above the maximum range one gets 'illogical' and unstable voltage values. So one's program should try and keep track or figure out if the measurement is valid or not before using it.

Lefty

Thanks for the info guys.
Some more information.

I have linearized the output from the sensor(s).
However, the raw values also fluctuate over time.

The measuring distance (25 cm) should be in range of the sensors capabilities (30 cm).

And I have capacitors in place to stabilize the voltage.
(One big and one small, based on the information in the data sheet)
There are other sensors on the same 5 volt line (flex sensors).
So, if my voltage supply was not stable enough for the IR Rangers,
(or destabilized by these sensors) would that not cause readings for the flex sensors to also jitter?

I was thinking that it maybe had something to do with the frequency at which I read them.
The datasheet http://www.sparkfun.com/datasheets/Sensors/Infrared/GP2D120XJ00F_SS.pdf specifies
a "timing chart". I cannot make much sense out of it, but does this imply I cannot read the voltage all the time?
Does the voltage from the sensor need to stabilize before it's ready to be read again?

If I read the timing chart - page 6 - correctly, a reading (worst case) is available after 38.3 + 9.6 + 5.0 ms = 52.9 ms => 18.9 times per second.

So, I should only read the sensor 18 times per second at most?