Check out the new projects site for A-i-S www.adventuresinsilicon.com

Sunday, December 20, 2009

Code below is for an Arduino Pro running at 16Mhz to sample a Maxsonic EZ0 Ultrasonic distance measuring device's PWM output.

This code is non-trivial as it takes 12 samples, determines the Interquartile Range of the dataset, then takes a mean of the Interquartile Range of the dataset and also calculates the Standard Deviation from the mean of that dataset.

The result is a robust measurement which has removed the influence of outliers in the data and can inform the user of the confidence of the single measurement number.

The code is generalised so that more accurate readings due to extra samples require only one line of code to be altered.
______________________________________________________________________

//Note - time to sample is not deterministic with this code, it will depend on the distance measured


int pin = 2; //pin revceiving PWM data from EZ0


void FindDistanceStdDev(int NUMOFSAMPLES);

void setup()
{
pinMode(pin, INPUT);
Serial.begin(9600);

}

void loop()
{

FindDistanceStdDev(12); //best to use number divisible by 4

delay(1);
}

//*********************************************************************************************************************
//
//
// Note: NUMOFSAPMLES should be divisible by 4
//*********************************************************************************************************************
void FindDistanceStdDev(int NUMOFSAMPLES)
{
float duration = 0.0f; //Variable to hold the delay in travel time of ultrasonic sound per sample (microseconds)
float distancefinal = 0.0f;

unsigned int count = 0; //variable to count samples
unsigned int CountSortInner = 0 ; //counter used to track inside loop for Bubble Sort
int SortCompleted = 0; //BOOL to track if bubblesort completed

float StdDevScratch = 0.0f;
const int SAMPLEDELAY = 1; //delay in millis between samples
float distancesamples[NUMOFSAMPLES]; //array to store samples in
float IQRScratch; //used for swaps inthe bubblesort
int IQRNUMOFSAMPLES = NUMOFSAMPLES/2;
float InterquartileArray[IQRNUMOFSAMPLES];
float mean = distancefinal;
float SamplesStdDevScratch[IQRNUMOFSAMPLES]; //array to hold intermediate values during calculations

// initialise array
for(count = 0; count < NUMOFSAMPLES; count++)
{
distancesamples[count] = 0;
}
count = 0;

//populate array with samples from the EZ0 hardware
count = 0;
for(count = 0; count < NUMOFSAMPLES; count++)
{
duration = 0;
duration = pulseIn(pin, HIGH);
distancesamples[count] = (duration/147.0f);
duration = 0;
delay(SAMPLEDELAY);
}

//DEBUG print sampled values
for(count = 0; count < NUMOFSAMPLES; count++)
{
Serial.print(" ");
Serial.print(distancesamples[count]);
Serial.print(" ");
}
Serial.print("\r\n");


//Cull array to leave only Interquartile Range
//Step 1 - Sort array
for(count = NUMOFSAMPLES - 1; count > 0; count--)
{
for(CountSortInner = 0; CountSortInner < count; CountSortInner++)
{
SortCompleted = 0;

if(distancesamples[CountSortInner] > distancesamples[CountSortInner+1])
{
IQRScratch = distancesamples[CountSortInner];
distancesamples[CountSortInner] = distancesamples[CountSortInner+1];
distancesamples[CountSortInner+1] = IQRScratch;
SortCompleted = 1;
}
}
if(SortCompleted = 0) break;

}

//DEBUG print out sorted values
for(count = 0; count < NUMOFSAMPLES; count++)
{
Serial.print(" ");
Serial.print(distancesamples[count]);
Serial.print(" ");
}
Serial.print("\r\n");

//Step 2 - populate new array by cutting the middle out of the sample array and pasting it into new array
int Temp = 0;
int TempStart = (NUMOFSAMPLES/4);
int TempEnd = (NUMOFSAMPLES - TempStart); //top and tail based on symmetrical array
for(count = TempStart; count < (TempStart + IQRNUMOFSAMPLES); count++)
{
InterquartileArray[Temp] = distancesamples[count];
Temp++;
}

for(count = 0; count < IQRNUMOFSAMPLES; count++)
{
Serial.print(" ");
Serial.print(InterquartileArray[count]);
Serial.print(" ");
}
Serial.print("\r\n");

//now average the samples to produce a mean
count = 0;
for(count = 0; count < IQRNUMOFSAMPLES; count++)
{
mean+=InterquartileArray[count];
}
mean = mean/IQRNUMOFSAMPLES;

//Compute the Standard Deviation of the IQR dataset
for(count = 0; count < IQRNUMOFSAMPLES; count++)
{
SamplesStdDevScratch[count] = (pow((InterquartileArray[count]-mean),2)); //take each sample away from the mean then square each result and save into a new array
}
//average these results to produce Std Dev
StdDevScratch = 0.0f;
for(count = 0; count < IQRNUMOFSAMPLES; count++)
{
StdDevScratch += SamplesStdDevScratch[count];
}
//divide by the number of samples
StdDevScratch = StdDevScratch / IQRNUMOFSAMPLES;
//square root this number
StdDevScratch = sqrt(StdDevScratch);

Serial.write("distance : ");
Serial.print(mean);
Serial.write(" inches to object with a Standard Deviation of ");
Serial.print(StdDevScratch);
Serial.write(" \r\n");
//print out the array of samples

}

Tuesday, December 15, 2009

Code below will let your Arduino poll a Maxsonic EZ0 distance sensor and read the PWM to intelligently report back the distance (uses multiple samples and calculates standard deviation as well to report of how confident you can be it got it right).

//Note - time to sample is not deterministic with this code, it will depend on the distance measured


int pin = 2; //pin revceiving PWM data from EZ0
const int NUMOFSAMPLES = 15; //number of samples to take
const int SAMPLEDELAY = 0; //delay in millis between samples
unsigned int count = 0; //variable to count samples

float duration = 0.0f; //Variable to hold the delay in travel time of ultrasonic sound per sample (microseconds)

float distancesample1 = 0.0f;
float distancesample2 = 0.0f;
float distancesample3 = 0.0f;
float distancefinal = 0.0f;


float distancesamples[NUMOFSAMPLES];

void setup()
{
pinMode(pin, INPUT);
Serial.begin(9600);

// initialise array
for(count = 0; count < NUMOFSAMPLES; count++)
{
distancesamples[count] = 0;
}
count = 0;
}

void loop()
{
//populate array with samples from the EZ0 hardware
count = 0;
for(count = 0; count < NUMOFSAMPLES; count++)
{
duration = 0;
duration = pulseIn(pin, HIGH);
distancesamples[count] = (duration/58.0f); //147us/inch or 58us/cm
duration = 0;
delay(SAMPLEDELAY);
}
//now average the samples to produce the final result
//Note no attempt to remove outliers yet
count = 0;
for(count = 0; count < NUMOFSAMPLES; count++)
{
distancefinal+=distancesamples[count];
}
distancefinal = distancefinal/NUMOFSAMPLES;

//Compute the Standard Deviation of the dataset
float mean = distancefinal;
float SamplesStdDevScratch[NUMOFSAMPLES]; //array to hold intermediate values during calculations
float StdDevScratch = 0.0f;

for(count = 0; count < NUMOFSAMPLES; count++)
{
SamplesStdDevScratch[count] = (pow((distancesamples[count]-mean),2.0f));
}
//now add these up
StdDevScratch = 0.0f;
for(count = 0; count < NUMOFSAMPLES; count++)
{
StdDevScratch += SamplesStdDevScratch[count];
}
//divide by the number of samples
StdDevScratch = StdDevScratch / NUMOFSAMPLES;
//square root this number
StdDevScratch = sqrt(StdDevScratch);

Serial.write("distance : ");
Serial.print(distancefinal);
Serial.write(" cm to object with a Standard Deviation of ");
Serial.print(StdDevScratch);
Serial.write(" \r\n");



delay(100);
}