Loading...
Pages: [1] 2   Go Down
Author Topic: SHARP IR Distance Sensor - GP2D120XJ00F  (Read 2835 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 66
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
« Last Edit: June 08, 2011, 10:38:52 am by Blue_Boy » Logged

0
Offline Offline
Faraday Member
**
Karma: 12
Posts: 2857
ruggedcircuits.com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:

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

Quote
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.

Quote
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
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 66
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: June 08, 2011, 12:22:06 pm by Blue_Boy » Logged

Left Coast, CA (USA)
Online Online
Brattain Member
*****
Karma: 279
Posts: 15316
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Logged

Netherlands
Offline Offline
Tesla Member
***
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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 - http://arduino.cc/playground/Main/MultiMap -
it interpolates linear between points of the graph. More points means less error more processing time.

Hopes this helps,
Rob

« Last Edit: June 08, 2011, 03:08:41 pm by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 66
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Netherlands
Offline Offline
Tesla Member
***
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.


Quote
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:

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

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

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

New Hampshire
Offline Offline
God Member
*****
Karma: 13
Posts: 776
There are 10 kinds of people, those who know binary, and those who don't.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html

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.
Logged


Left Coast, CA (USA)
Online Online
Brattain Member
*****
Karma: 279
Posts: 15316
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
/*
  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
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 71
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

The one jraskell posted (http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html) 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... smiley-wink
« Last Edit: June 08, 2011, 07:46:37 pm by cpixip » Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 66
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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



Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 66
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 - http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html.
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.

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 ++;
  }
« Last Edit: June 10, 2011, 03:51:01 pm by Blue_Boy » Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 71
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Blue_Boy - nice that it worked out that easy... smiley-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:

Code:
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
Code:
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....
Logged

0
Offline Offline
Jr. Member
**
Karma: 0
Posts: 66
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?

Code:
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();
    }
  }
Logged

Netherlands
Offline Offline
Tesla Member
***
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


check - http://www.arduino.cc/playground/Main/Statistics - or - http://arduino.cc/playground/Main/RunningAverage -
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -

Pages: [1] 2   Go Up
Print
 
Jump to: