Pages: [1]   Go Down
 Author Topic: linearizing sharp IR sensor graph  (Read 2932 times) 0 Members and 1 Guest are viewing this topic.
Offline
Full Member
Karma: 0
Posts: 158
 « on: March 27, 2011, 04:39:27 pm » Bigger Smaller Reset

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 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(){

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);

}
 Logged

Global Moderator
Netherlands
Offline
Shannon Member
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #1 on: March 27, 2011, 04:47:41 pm » Bigger Smaller Reset

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:
//
//    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
// doing 128 samples smooths the reading.
unsigned int sharpRawAvg()
{
unsigned long raw = 0;
for (byte i=0; i<32; i++)
{
//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]);
}
 « Last Edit: March 27, 2011, 04:50:56 pm by robtillaart » Logged

Rob Tillaart

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

0
Offline
Shannon Member
Karma: 200
Posts: 11694
Arduino rocks
 « Reply #2 on: March 27, 2011, 06:41:22 pm » Bigger Smaller Reset

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

[ I won't respond to messages, use the forum please ]

Global Moderator
Netherlands
Offline
Shannon Member
Karma: 212
Posts: 13531
In theory there is no difference between theory and practice, however in practice there are many...
 « Reply #3 on: March 28, 2011, 06:29:34 am » Bigger Smaller Reset

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.

 Logged

Rob Tillaart

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

Massachusetts, USA
Offline
Tesla Member
Karma: 201
Posts: 8695
 « Reply #4 on: March 28, 2011, 08:14:01 am » Bigger Smaller Reset

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

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Offline
Jr. Member
Karma: 0
Posts: 72
 « Reply #5 on: March 28, 2011, 09:25:06 am » Bigger Smaller Reset

... 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).
 3d_scan_04.jpg (41.25 KB, 400x230 - viewed 31 times.) Logged

 Pages: [1]   Go Up