Highly Accurate driver for the Sharp 2D120X proximity sensor

Here is my version. Let me know if you find any issue. Otherwise have fun!
Here is the work flow:
-Take 10 readings at a time into an arraylist
-Sort the arraylist asc order
-Then eliminate low/high extreme values.
-Then I avg the middle 6 values. If the avg is out of manufacture’s specs return min or max respectively.


//-------------------------------------------------------------------------------------
//              GHI Electronics, LLC
//               Copyright (c) 2009
//               All rights reserved
//-------------------------------------------------------------------------------------
//  Modifications:
//-------------------------------------------------------------------------------------
//  Hai Nguyen
//  09/2010 Added GetDistanceCmWithFilter() and quickSort() in attempt to
//  eliminate possible invalid readings
//          
//-------------------------------------------------------------------------------------

/*

 * You can use this file if you agree to the following:

 *

 * 1. This header can't be changed under any condition.

 *    

 * 2. This is a free software and therefore is provided with NO warranty.

 * 

 * 3. Feel free to modify the code but we ask you to provide us with

 *	  any bugs reports so we can keep the code up to date.

 *

 * 4. This code may ONLY be used with GHI Electronics, LLC products.

 *

 * THIS SOFTWARE IS PROVIDED BY GHI ELECTRONICS, LLC ``AS IS'' AND 

 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 

 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 

 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL 

 * GHI ELECTRONICS, LLC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 

 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 

 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ORT 

 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 

 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  

 *

 *	Specs are subject to change without any notice

 */



using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace GHIElectronics.NETMF.FEZ
{
    public static partial class FEZ_Components
    {
        public class DistanceDetector : IDisposable
        {
            AnalogIn adc;
            float Y0 = 0;
            float X0 = 0;
            float Y1 = 0;
            float X1 = 0;
            float C = 0;

            public enum SharpSensorType : byte
            {
                GP2Y0A21YK = 0,
                GP2D120 = 1,
            }

            public void Dispose()
            {
                adc.Dispose();
            }

            public DistanceDetector(FEZ_Pin.AnalogIn pin, SharpSensorType type)
            {
                adc = new AnalogIn((AnalogIn.Pin)pin);
                adc.SetLinearScale(0, 330);
                switch (type)
                {
                    case SharpSensorType.GP2Y0A21YK:
                        Y0 = 10;
                        X0 = 315;
                        Y1 = 80;
                        X1 = 30;
                        break;

                    case SharpSensorType.GP2D120:
                        Y0 = 3;
                        X0 = 315;
                        Y1 = 30;
                        X1 = 30;
                        break;
                }

                C = (Y1 - Y0) / (1 / X1 - 1 / X0);
            }

            public float GetDistance_cm()
            { 
                return C / ((float)adc.Read() + (float).001) - (C / X0) + Y0; 
            }

            public float GetDistanceCmWithFilter()
            {
                const int ARRAY_SIZE = 10;
                float[] array2Sort = new float[ARRAY_SIZE];

                //make 10 readings                
                for (int i = 0; i < ARRAY_SIZE; i++)
                    array2Sort[i] = GetDistance_cm();

                //sort the values in ascending order
                quickSort(0, ARRAY_SIZE - 1, ref array2Sort); 

                //elimate lowest extremes and highest extremes. calculate avg for the middle 6 values
                float avg = 0;
                for (int i = 2; i < 8; i++) 
                    avg += array2Sort[i];

                avg = avg / 6;

                //check for valid ranges within manufacture's specs.
                if (avg < Y0) return Y0;
                if (avg > Y1) return Y1;

                return avg;
            }

            public void quickSort(int left, int right, ref float[] array2Sort)
            {
              int leftHolder = left;
              int rightHolder = right;
              float pivot = array2Sort[left];

              while( left < right )
              {
                while( (array2Sort[right] >= pivot) && (left < right) )   
                  right--;

                if( left != right )    {
                  array2Sort[left] = array2Sort[right];
                  left++;
                }

                while( (array2Sort[left] <= pivot) && (left < right) )   
                  left++;

                if( left != right )    {
                  array2Sort[right] = array2Sort[left];
                  right--;
                }
              }

              array2Sort[left] = pivot;
              pivot            = left;
              left             = leftHolder;
              right            = rightHolder;

              if (left < pivot) quickSort(left, (int)pivot - 1, ref array2Sort);
              if (right > pivot) quickSort((int)pivot + 1, right, ref array2Sort); 
            }
        }//quickSort

    }

}


Unfortunately Hai, the GHI class’s sensor readings have never had anything to do with reality :frowning: My class implements pretty much everything you have there except mfr specs, however my culling of variables outside of range is done with standard deviation so you only end up with middle values and culling if it’s needed.

Dear All,

I updated the code to eleminate possible performance hicup with quick sort. Note: the code was written on a non testable laptop so, it is possible that it may contain systax errors. I will test it once i get home and update it as needed

-Hai

Unfortunately it’s still based on the GHI code, so it’ll be about as accurate and useful as a catapult made out of wet noodles :frowning:

I think my standard deviation cleanup will be quicker than your code too sorry - it doesn’t run if it doesn’t need to.

MarkH,

It is POSSIBLE your code runs much faster and error-free. however, which is better still a question. I’ve never claim it is my code nor this is faster than yours. I modified GHI code to suit my need and shared what I got. I will let people decide.

-Thanks!

Hi Hai,

Mark came up with a VERY slick driver. I’d go with it. Not to say anything bad about your driver. ;D

Hai, i’m not saying anything against your driver, the issue is with the GHI driver. We spent a long time on chat trying to get some meaningful readings out of it.

See this image:

This is the standard GHI driver with 10 sample averaging, then the green line is a 5pt moving average. This plot is starting at 30cm, moving down to 5cm in 5cm steps. At no point is the distance measurement the same as the actual distance. This is why i wrote my driver from scratch which uses a 6th degree polynomial function to get the distance from the voltage. If you look at the datasheet for these sensors, the voltage curve is not linear, or exponential. You can’t correct it the way that GHI are trying to.

I will use yours instead :-). but you still holding up driver for GP2Y0A21YK. do you have an ETA for it?

I’m hoping to get to it this weekend. It takes 5-6hrs to do. I was going to do it last night but then Gus posed the challenge for the CDC driver and it was bugging me on how to do it so i spent 7hrs doing that instead.

I updated my library to make it even more accurate.

Here is a video of it in use:

Wow. Now that is very accurate.

Holy crap Mark, that is AWESOME! That is some sick accuracy. I will use your driver 100% on my rosvem project! Very very nice! :clap:

Thanks guys, i’ll have video for the other sensors, i just need to record the audio so i can make the videos complete.

I’ll be interested to see how people go in real world tests. My tests are done to the datasheet, which is against a piece of kodak paper.

I don’t think anyone in the hobbyist world has managed to convert these sensors to distance before (i looked very hard), never mind with this sort of accuracy. Maybe i’m just weird haha.

Not that accurate for sure. We used zones in the Basic stamp and arduino world for that stuff.

Now these sensors can be used for more advanced robotics (example: production robot model using x - y - z axes to track a object and pick it up)

Mark
Did you have chance to mount multiple of these sharp sensors side by side and test to see if they still deliver accurate result?. I have bad experience with Maxbotix sensors that caused me lots of frustration. when mounted side by side (with is very common usage). they interfere one another.

Thanks!

I was using two of them without any difficulty on my test rig…

at an avg of 14 milliseconds per reading sample, that may be an issue for fast moving projects like traxass tempede… which relies on multiple sensors (2 sensors = 28milliseconds) at one instance to determine the next move.


milliseconds between readings: 14
30.186396103504102
milliseconds between readings: 15
29.180461518060337
milliseconds between readings: 14
29.959312001341868
milliseconds between readings: 14
29.258945721215696
milliseconds between readings: 14
29.485963343778895
milliseconds between readings: 14
30.768258921550387
milliseconds between readings: 15
30.401793655091478
milliseconds between readings: 15
30.638236196733395
milliseconds between readings: 15
30.589309212734499
milliseconds between readings: 15
29.70815439005937
milliseconds between readings: 14
29.197297487037076
milliseconds between readings: 13
30.740963736298863
milliseconds between readings: 14
30.884793782117686
milliseconds between readings: 15

just a thought.

Turn off moving averaging, and moving averages. I was performing 10 sample averaging in 6ms.

“at an avg of 14 milliseconds per reading sample, that may be an issue for fast moving projects like traxass tempede… which relies on multiple sensors (2 sensors = 28milliseconds) at one instance to determine the next move.”

Could you read the two sensors asynchronously, instead of doing 1 after the other?

This would still allow for 14ms - with a ms or two extra likely.The code would be more complicated, possibly needing a blocking mechanism - is a ManualResetEvent available in .NETMF? - until both (or more) results come in.

Or would there be interference between the sensors (IR or electrical) if they were to operate simultaneously?