SHARP IR Distance Sensor - GP2D120XJ00F

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?

actually, reading of the sensor value should occur at a precise time after you trigger the measurement. See the datasheet on how the voltage varies over time, there's a diagram somewhere. That time is the 38 msec robtillaart mentioned. You should not have any jitter in that timing. And yes, sampling with a higher frequency does not make too much sense, provided your powersupply is clean.

Could you explain what you mean by "triggering the measurement"?
There's only a power, ground and "voltage to read" line coming out of this sensor.

Maybe the problem is still my power line, though.
I put the capacitors right next to the Arduino, and then wired all my sensors "behind" that.
(IR Rangers and Flex sensors)

oops... - sorry, I mixed up the Sharp IR distance sensors with the Sharp IR dust sensor (the GP2Y1010AU0F for example). The later is being triggered and has to be read out at a specific time after the trigger. Sorry for this confusion....

The Sharp IR distance sensors are indeed outputting the data continously, but that reading does change only at discrete time points.

Back to the issue of power line spikes: I would try to place the capacitors as close as possible to the sensor, not the Arduino. If you google, you will find people soldering a condensator directly to the pins of the IR sensor (for example: http://www.robotfreak.de/blog/en/robotics/how-to-improve-sharp-ir-sensors/450).

You might also try to power sensor and Arduino with different voltage supplies for a test (batteries for example).

Basically, if the power is not stable enough, the noise spikes created by the sensor kick through to the Arduino, especially the Aref voltage which the DAC uses to digitize the analog voltages. If that happens, the readings of the sensor will get noisy, even so the voltage the sensor is delivering is constant.

Time averaging will stabilize the readings, in any case.

Hey,
When I just check the RAW values from the sensors,
jitter seems pretty minimal now.
I added an extra capacitor, adding even more
does not seem to improve the situation.

The sensor response is still not ideal however.
I do a time-averaged read of the sensors,
but improvements are possible.

I guess some smarter filtering could help.

(Analog pin 0 and 1 are connected to the IR rangers)
(The flex sensors a quite stable now)

/* PuppetMaster V5 Code                                */
/* Jim Bollansée | jim@jimboproductions.be   */
/* Creative Commons Attribution ShareAlike    */
/* GameHUB 2011                                           */

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 = (float) sensorValueTotal[i];
      tempFloat = tempFloat / 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();
    }
  }

FYI, in my case adding just the capacitor onto the sensor power pins (directly on the sensor, not Arduino) did not reduce the spikes in measured distance readings much. But adding a 10ohm resistor between Arduino +5V power and the capacitor improved things a lot. Here are the standard deviations of measured distance (about 10cm from sensor, measured every 7ms):

plain sensor connected to Arduino +5V/GND: 0.27cm standard deviation, mainly consisting of ca 1cm spikes occuring ca 20 times per sec

with 100uF cap on sensor power pins: 0.15cm

with 100uF cap+10ohm resistor: 0.03cm