Go Down

Topic: linearizing sharp IR sensor graph (Read 3270 times) previous topic - next topic

ahhreeyell

Hello,

I have a Sharp IR sensor, and as you can see here, the relationship between the voltage produced and the distance sensed is logarithmic: http://www.danshope.com/blog/images/irSensor1.png
I want to make this relationship linear, i.e., the further the object is, the lower the voltage.
I've been looking at this page here: http://www.acroname.com/robotics/info/articles/irlinear/irlinear.html
I've also been playing around with code and wiring, trying to figure things out, but to no avail thus far. Here is what I have so far. Any suggestions?


int sensorvar = 0;
int led = 9;
int outputValue = 0;
int d = 0;
double linear = 0;

void setup(){
  pinMode(led, OUTPUT);
  Serial.begin(9600);
}

void loop(){

  sensorvar = analogRead(1);
  d = map(sensorvar, 0, 1023, 20, 150);
  linear = 1 / ( d + 0.42 );
  outputValue = map(linear, 20, 150, 0, 255);
  analogWrite(led, outputValue);
  Serial.print("sensor value:");
  Serial.println(linear);
   
    delay(500);

}

robtillaart

#1
Mar 27, 2011, 11:47 pm Last Edit: Mar 27, 2011, 11:50 pm by robtillaart Reason: 1
I wrote a library just to handle this: - http://arduino.cc/playground/Main/MultiMap -

my sample code - you need to update the arrays used as these are 20-150 cm,  I callibrated the arrays with a measure stick (steps of 10 cm) you might do in in steps of 1 cm. but they don't need to be equidistant

Code: [Select]

//
//    FILE: SharpDistanceSensor
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// PURPOSE: Demo SHARP 2Y0A02 F 9Y - distance sensor
//
// HISTORY:
// 0.1.00 - 2011-01-22 initial version
// 0.1.01 - 2011-01-24 improved multimap

// Released to the public domain
//
#define RED    4
#define GREEN  5

void setup()
{
 Serial.begin(19200);
 pinMode(RED, OUTPUT);
 pinMode(GREEN, OUTPUT);
 analogReference(EXTERNAL);
 
 Serial.println("Demo SHARP 2Y0A02 F 9Y - distance sensor");
 Serial.println();  
 Serial.println("TIME\tRAW\tCM");
 Serial.println();
}

void loop()
{
 long raw = sharpRawAvg();
 int mm = sharp2mm(raw);

 Serial.print(millis());
 Serial.print("\t");
 Serial.print(raw);
 Serial.print("\t");
 Serial.print(mm);
 Serial.print("\t");
 for (int i=50; i<mm;i+=10) Serial.print("]");
 
 if (mm > 750) digitalWrite(GREEN, HIGH);
 else digitalWrite(GREEN, LOW);
 if (mm < 850) digitalWrite(RED, HIGH);
 else digitalWrite(RED, LOW);
//  if (cm < 40)
//  {
//    digitalWrite(RED, HIGH);
//    delay(cm/2);
//    digitalWrite(RED, LOW);
//    delay(cm/2);
//  }
 Serial.println();
}

///////////////////////////////////////////////////////////////////////
//
// SAMPLE FUNCTIONS
//

// multisample version
// as the sensor is not very steady in its reading
// doing 128 samples smooths the reading.
unsigned int sharpRawAvg()
{
 unsigned long raw = 0;
 for (byte i=0; i<32; i++)
 {
   raw += analogRead(A0);
   //delay(1);
 }
 return raw/32;
}

// running average version
// use of 1024 == 10 bit shift
//    + configurable ~1/1000
//    + no floats
unsigned int sharpRawRA()
{
 static unsigned long value = 0;
 unsigned int a = 950;
 value = (a * value + (1024-a) * analogRead(A0)) /1024;
 return (unsigned int) value;
}


/*
TABLE OF MEASUREMENTS
CM    RAW      DELTA
150   91 91
140   97 6
130  105 8
120  113 8
110  124 9
100  135 11
90   147 12
80   164 17
70   185 21
60   218 33
50   255 37
40   317 62
30   408 91
20   506 98
*/


int sharp2mm(int val)
{
 int out[] = {1500,1400,1300,1200,1100,1000,900,800,700,600,500,400,300,200};
 
 // calibration 17-01
 // internal reference 5,00 Volt
 // factor 1.0
 // range = 416
 int in1[]  = { 90, 97,105,113,124,134,147,164,185,218,255,317,408,506};
 
 // (quick) calibration 24-01 (colder?)
 // internal reference 5,00 Volt
 // factor 1.0
 // range = 396
 int in2[] = { 87, 93,100,108,116,126,139,157,177,197,231,304,384,483};
 
 // (quick) calibration 24-01
 // external reference of ~3,07 V
 // factor 1,6243 * _in2[]
 // range = 644
 int in3[] = {141,151,162,178,193,205,229,256,287,320,375,494,624,785};
 
 return multiMap(val, in3, out, 14);
}

///////////////////////////////////////////////////////////////////////
//
// MULTILMAP FUNCTION
//
int multiMap(int val, int* _in, int* _out, int size)
{
 val = constrain(val, _in[0], _in[size-1]);
 
 // handle first value separately
 if (val == _in[0]) return _out[0];
 // search right interval
 int pos = 0;
 while(val > _in[pos] && pos < size) pos++;
 // interpolate
 return map(val, _in[pos-1], _in[pos], _out[pos-1], _out[pos]);
}

int sharp2cm2(int val)
{
 val = constrain(val, 90, 506);
 int _in[]  = {0,  90, 97,105,113,124,134,147,164,185,218,255,317,408,506};
 int _out[] = {0, 150,140,130,120,110,100, 90, 80, 70, 60, 50, 40, 30, 20};
 int size = 15;
 int pos = 0;
 
 while(val > _in[pos] && pos < size) pos++;

 return map(val, _in[pos-1], _in[pos], _out[pos-1], _out[pos]);
}
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

MarkT

Just for completeness:  that's not a logarithmic relationship, its a reciprocal one.
[ I won't respond to messages, use the forum please ]

robtillaart

Quote
Just for completeness:  that's not a logarithmic relationship, its a reciprocal one.

It is not reciprocal  - although the approximation formula is - I put measurements (see previous sketch)  in a spreadsheet and let it determine the formula but it allways misfit somewhere in the range. That's why I used multiMap(). And yes, it is an approximation too, but by adding extra points it can decrease the maximum error.


Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

johnwasser

It looks like the maximum value is near 512 which would be a voltage of about 2.5 volta.  You could get about 50% more resolution by connecting the ARef pin to 3.3v and selecting analogReference(EXTERNAL).  Then the max value would be closer to 775  (1024 * 2.5/3.3).
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

cpixip

... just for completeness (;)): the way the Sharp IR-sensor is working actually leads to a reciprocal relationship.

These sensors are based on triangulation, where a tiny IR light spot is "seen" by a position sensitive device from a slightly different perspective. The perspective shift induced by this on the PSD (position sensitive device) is in fact proportional to 1/z, with z being the distance of the object seen. This can be easily derived by drawing the basic geometry of such a setup on a piece of paper and utilizing similar triangles.

Deviations from the reciprocal relationship are due to the simple sensor design. Even so, these devices are much more precise than your usual ultrasound ranger; you can even use them for 3D scanning (I have attached an example of such a scan below this post).

Another remark: even so these sensors are rated at 30mA or so, they draw much more current when "fireing" - this can lead to heavy spikes on the power supply. It's a good idea to use an appropriate capacitor as close to the sensor as possible (see http://www.robotroom.com/DistanceSensor3.html for example).

Go Up