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
}
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
}
Comments