Skip to main content
Arduino

Return to Answer

Commonmark migration
Source Link

Average the distance

#Average the distance CreateCreate an array (dist, for now) and use it to find the average distance your sensor is from an object.

Average the averages

#Average the averages AsAs you have worked out, you need an array of values to determine if the distance is increasing. Bring in, again, the cyclic buffer.

#A possible solution:

A possible solution:

#Average the distance Create an array (dist, for now) and use it to find the average distance your sensor is from an object.

#Average the averages As you have worked out, you need an array of values to determine if the distance is increasing. Bring in, again, the cyclic buffer.

#A possible solution:

Average the distance

Create an array (dist, for now) and use it to find the average distance your sensor is from an object.

Average the averages

As you have worked out, you need an array of values to determine if the distance is increasing. Bring in, again, the cyclic buffer.

A possible solution:

Added further help
Source Link
CharlieHanson
  • 1.4k
  • 1
  • 11
  • 25

I also recommend replacing ALL const int <name> = <value> instances with #define <name> <value>.

#define MAX_DIST 5 // DEFINE is a useful tool!
int dist[MAX_DIST]; // create an array, no need to initialise.
double distAverage; // average distance, could be 'int' instead.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
// As you get the distance from your sensor, update 'dist' and
// then calculate the running average.
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAverage = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAverage += dist[x];
distAverage /= MAX_DIST;
#define MAX_DIST 5 // these could also be 'const int's
#define MAX_AVRG 3 // but my preference is this way
int dist[MAX_DIST]; // create an array, no need to initialise.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
doubleint distAvrg[MAX_DIST]; // create an array of averages.
int avrgIterator = 0; // wwwwoooOOOOooo ? Not this time.
// There's no single 'distAverage' anymore; the result dumps straight
//into 'distAvrg[avrgIterator]'...
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAvrg[avrgIterator] = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAvrg[avrgIterator] += dist[x];
distAvrg[avrgIterator] /= MAX_DIST;
avrgIterator++;
if (avrgIterator == MAX_AVRG) avrgIterator = 0;

#A possible solution:

** EDIT ** As pointed out by @EdgarBonet, double and float are 'expensive', in that they take up a lot of computing cycles to be dealt with. The Arduino website recommends avoiding them if you can. The alternative is to use scaled ints. For example, multiplying the return value from microsecondsToCentimetres() by 10 will give you the equivalent of one decimal place's worth of extra precision. No other code needs to change unless you're outputing the value on an LCD or similar and want it in centimeters. Dividing by 10 is conveniently the distance in millimeters, which is why I picked it, but there's no reason you can't scale by a different factor. Just be aware that there is a maximum (and mirrored minimum) value that int can hold, but I doubt you're going to hit it unless you scale by an unnecessarily high factor.

Let's say MAX_AVRG is 6. This gives five changes between distances to look at. The simplest function for determining an increase is:

bool increasing(int * DIST)
{
 int dir = 0;
 for (int x=1; x<MAX_AVRG; x++)
 {
 if (DIST[x] > DIST[x-1]) // change this to < for DECREASE
 dir++;
 else
 dir--;
 }
 if (dir > 0)
 return TRUE;
 else
 return FALSE;
}

The 'decrease' version is identical, save for the single commented line. The for loop starts at the second sample and checks against the previous sample, which is why x starts at 1, not 0 as 'usual'. The return value will be true if distAv has increased more times than it has decreased, and vice versa for decreasing().

Let's say distAve = { 1, 2, 1, 3, 4, 5 }. In the function:

distAve 1 2 1 3 4 5
 ^ ^ ^ ^ ^
start | | | | | dir = 0
increase -' | | | | dir = 1
decrease -' | | | dir = 0
increase -' | | dir = 1
increase -' | dir = 2
increase -' dir = 3
dir > 0 therefore INCREASE.

But what if distAve = { 5, 4, 3, 2, 1, 6 } ? If increasing() will now return false because there are four decreases vs one increase, except the final distance indicates that the sensed object has ended up at an increased distance than when it started.

int distance(int * DIST)
{
 int overallDistance = 0;
 for (int x=1; x<MAX_AVRG; x++)
 overallDistance += (DIST[x] - DIST[x-1]);
 return overallDistance;
}

Now you have an explicit value for the overall distance change.

I also recommend replacing ALL const int <name> = <value> instances with #define <name> <value>.

#define MAX_DIST 5 // DEFINE is a useful tool!
int dist[MAX_DIST]; // create an array, no need to initialise.
double distAverage; // average distance, could be 'int' instead.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
// As you get the distance from your sensor, update 'dist' and
// then calculate the running average.
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAverage = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAverage += dist[x];
distAverage /= MAX_DIST;
#define MAX_DIST 5 // 
#define MAX_AVRG 3 //
int dist[MAX_DIST]; // create an array, no need to initialise.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
double distAvrg[MAX_DIST]; // create an array of averages.
int avrgIterator = 0; // wwwwoooOOOOooo ? Not this time.
// There's no single 'distAverage' anymore; the result dumps straight
//into 'distAvrg[avrgIterator]'...
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAvrg[avrgIterator] = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAvrg[avrgIterator] += dist[x];
distAvrg[avrgIterator] /= MAX_DIST;
avrgIterator++;
if (avrgIterator == MAX_AVRG) avrgIterator = 0;
#define MAX_DIST 5
int dist[MAX_DIST]; // create an array, no need to initialise.
double distAverage; // average distance, could be 'int' instead.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
// As you get the distance from your sensor, update 'dist' and
// then calculate the running average.
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAverage = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAverage += dist[x];
distAverage /= MAX_DIST;
#define MAX_DIST 5 // these could also be 'const int's
#define MAX_AVRG 3 // but my preference is this way
int dist[MAX_DIST]; // create an array, no need to initialise.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
int distAvrg[MAX_DIST]; // create an array of averages.
int avrgIterator = 0; // wwwwoooOOOOooo ? Not this time.
// There's no single 'distAverage' anymore; the result dumps straight
//into 'distAvrg[avrgIterator]'...
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAvrg[avrgIterator] = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAvrg[avrgIterator] += dist[x];
distAvrg[avrgIterator] /= MAX_DIST;
avrgIterator++;
if (avrgIterator == MAX_AVRG) avrgIterator = 0;

#A possible solution:

** EDIT ** As pointed out by @EdgarBonet, double and float are 'expensive', in that they take up a lot of computing cycles to be dealt with. The Arduino website recommends avoiding them if you can. The alternative is to use scaled ints. For example, multiplying the return value from microsecondsToCentimetres() by 10 will give you the equivalent of one decimal place's worth of extra precision. No other code needs to change unless you're outputing the value on an LCD or similar and want it in centimeters. Dividing by 10 is conveniently the distance in millimeters, which is why I picked it, but there's no reason you can't scale by a different factor. Just be aware that there is a maximum (and mirrored minimum) value that int can hold, but I doubt you're going to hit it unless you scale by an unnecessarily high factor.

Let's say MAX_AVRG is 6. This gives five changes between distances to look at. The simplest function for determining an increase is:

bool increasing(int * DIST)
{
 int dir = 0;
 for (int x=1; x<MAX_AVRG; x++)
 {
 if (DIST[x] > DIST[x-1]) // change this to < for DECREASE
 dir++;
 else
 dir--;
 }
 if (dir > 0)
 return TRUE;
 else
 return FALSE;
}

The 'decrease' version is identical, save for the single commented line. The for loop starts at the second sample and checks against the previous sample, which is why x starts at 1, not 0 as 'usual'. The return value will be true if distAv has increased more times than it has decreased, and vice versa for decreasing().

Let's say distAve = { 1, 2, 1, 3, 4, 5 }. In the function:

distAve 1 2 1 3 4 5
 ^ ^ ^ ^ ^
start | | | | | dir = 0
increase -' | | | | dir = 1
decrease -' | | | dir = 0
increase -' | | dir = 1
increase -' | dir = 2
increase -' dir = 3
dir > 0 therefore INCREASE.

But what if distAve = { 5, 4, 3, 2, 1, 6 } ? If increasing() will now return false because there are four decreases vs one increase, except the final distance indicates that the sensed object has ended up at an increased distance than when it started.

int distance(int * DIST)
{
 int overallDistance = 0;
 for (int x=1; x<MAX_AVRG; x++)
 overallDistance += (DIST[x] - DIST[x-1]);
 return overallDistance;
}

Now you have an explicit value for the overall distance change.

Source Link
CharlieHanson
  • 1.4k
  • 1
  • 11
  • 25

The general idea of your code is fine, but it can be tweaked to make it more efficient.

Firstly, your function increasing(int p1, int p2, int p3) can be simplified to increasing(int *pingArray). This way you pass a reference to the function, rather than three ints. If you decide later that your pings array needs to hold more values you don't have to change the function arguements. However, if you're going to be experimenting with different lengths of the ping array you need to make sure you change the code within the function to reflect this. An easy way to stay on top of it is with #define:

#define MAX_PINGS 3 // Put this at the start of your code somewhere
int pings[MAX_PINGS]; // When you come to declare 'pings'
for (int x=0; x<MAX_PINGS; x++)
 pings[x] = -1; // Whatever each element is initialised to.
// Your 'increasing' function
// (... and the same for 'decreasing', if you decide to use it!)
bool increasing(int *pingArray)
{
 // CODE GOES HERE
 // and it doesn't matter how long pingArray / pings is
 // because you have 'MAX_PINGS' defined.
 return true;
}
// You call the 'increasing' function as you'd expect:
if (increasing(pings))
{ /* stuff */ }

I also recommend replacing ALL const int <name> = <value> instances with #define <name> <value>.

Now to the concentrating:

Your initial work is good but from what I've read on ultrasonic sensors, and from experience with any generic sensor, is that the readings aren't going to be perfect. The real world is a noisy place and you aren't going to get nice, accurate distance readings. Finding the average is a simple technique, but you will need to find an optimal solution yourself. Once you have your nice row of average values you can then determine if the distance is increasing or decreasing or neither.

Thus you have two steps:

#Average the distance Create an array (dist, for now) and use it to find the average distance your sensor is from an object.

#define MAX_DIST 5 // DEFINE is a useful tool!
int dist[MAX_DIST]; // create an array, no need to initialise.
double distAverage; // average distance, could be 'int' instead.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
// As you get the distance from your sensor, update 'dist' and
// then calculate the running average.
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAverage = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAverage += dist[x];
distAverage /= MAX_DIST;

I hope you understand the cyclic buffer principle - if you don't you can google it! A key point about the way the code above is constructed is the use of that #define MAX_DIST 5. You can change that single number to anything and your code will change along with it. You will discover that there is an optimum number; I can't tell you what it is (but I recommend it remains odd).

Now that you have a nice reliable distance measurement, you need to find if it is increasing or decreasing...

#Average the averages As you have worked out, you need an array of values to determine if the distance is increasing. Bring in, again, the cyclic buffer.

#define MAX_DIST 5 // 
#define MAX_AVRG 3 //
int dist[MAX_DIST]; // create an array, no need to initialise.
int distIterator = 0; // 'dist' will be a CYCLIC BUFFER. woooOOOOooo.
double distAvrg[MAX_DIST]; // create an array of averages.
int avrgIterator = 0; // wwwwoooOOOOooo ? Not this time.
// There's no single 'distAverage' anymore; the result dumps straight
//into 'distAvrg[avrgIterator]'...
dist[distIterator] = microsecondsToCentimeters(duration);
distIterator++;
if (distIterator == MAX_DIST) distIterator = 0;
distAvrg[avrgIterator] = 0.0;
for (int x=0; x<MAX_DIST; x++)
 distAvrg[avrgIterator] += dist[x];
distAvrg[avrgIterator] /= MAX_DIST;
avrgIterator++;
if (avrgIterator == MAX_AVRG) avrgIterator = 0;

Now you have an array of your averages. Winner.

From here you can create your increasing/decreasing function. I'll leave most of this up to you, but consider the following:

  • The contents of distAvrg may not be perfectly smooth;
  • The longer any of the arrays, the more reliable the average;
  • The longer the array, the slower the response of the 'machine';
  • Using int rather than double for distAvrg may help 'numb' some of the fluctuations in averages;
  • Because distAvrg is a cyclic buffer, when analysing it make sure you start at the correct element! HINT: If you add your increase/decrease function after all the code above rather than amongst it, avrgIterator will always point to the oldest average value;
  • If the distAverage array gets beyond three of four elements, you should definitely pass it by reference to the relevant functions; this is what I did in the first codeblock I wrote. It's beyond this answer to explain why you should do it other than "it's more efficient". This is a general rule for any large chunk of data, but you'll learn more as you go along.

The simplest algorithm for determining whether the distAvrgs are increasing or decreasing is to run through from oldest to newest and count how many increases there are and how many decreases there are:

int direction = 0;
for (x=oldest ... x=newest)
{
 if (x > x+1) direction++;
 if (x < x+1) direction--;
}

The above is pseudo-code - you'll have translate it yourself. You end up with a value of direction that is a positive number (increase), a negative number (decrease), or zero. The actual value of direction will help you determine the speed of the increase/decrease, if it's relevant.

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /