Android Articles Browser games My programs About
 

Ultrasonic distance sensor HC-SR04

Like any curious person, I was fascinated by the study of arduino. After purchasing a CPU was forced to think about the periphery. Starting from simple flashing LEDs. But this was not enough. And began designing toys. On old toy car jeep was electronic control system (with Arduino) of motor and turn the well. All motor connect with driver L293D chip, for save processor and to provide the necessary currents and voltages to the electric car motor. But on it I'm not stop and move next. So what is the robot-car, if this robot is no reference to the track. It was necessary to make the back data flow for search move track for robot car. Was bought ultrasonic sensor HC-SR04.

HC-SR04, to determine the direction of movement had to install it on an improvised swivel platform, to providing the scanning direction and the correct ways of driving. The first result give good data, auto-robot went quite cheerfully, independently choosing a path. However, a number of questions that made me think, how balanced the system is and how it interacts. Have been a number of primary test, that describe on this text. Immediately had to solve the question of the precision of the ultrasonic sensor.

For work with ultrasonic sensor under Arduino exist library NewPing.h. This library help to make measure of distance in inches and in centimeter. I have be test precision of this sensor on this text.

Have be collected statistic data of distance of same objects. And have be check precision of this ultrasonic sensor.

HC-SR04

Photo HC-SR04

On the Internet can be found pfoto with under mark «HC-SR04» but other electronic board construction or with different electronic circuit element. On this photo show the sensor model that I has describe on this review.

Software for plot graph, reproduce experiment

For collect statistic data ower COM-port I use Arduino SDK. For plot graph I using Open Office Calc, but you can use other table processor for it. For group data by sound delay at the first experiments I use OpenOffice Base (DB) and SQL (sketch 1). At the last experiment I wrote special program for Arduino, cause see that 2kB of memory has enought for process all statistic data up to 1 hour with non move object (sketch 2).

Arduino sketch 1. At the first time I using this code for Arduino. It's collect all «raw ping» data to serial port into PC row by row. At the start wait 5 second (blink LED). Then get lines with: number of measure, time stamp of measure (ms), distance (sound delay, ns). This is easy sketch, you should rework raw data for plot statistic graphic.

// Sonar statistic's

#include <NewPing.h>

#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void flashLed(int second) {
 for (int i=0;i<second*4;i++) {
  digitalWrite(13,HIGH);
  delay(250);
  digitalWrite(13,LOW);
  delay(250);
 }
}

void setup() {
 Serial.begin(9600); // Open serial monitor at 115200 baud to see ping results.
 Serial.println("Collecting ping's data. Wait 5 second then start...");

 pinMode(13, OUTPUT);
 flashLed(5);

 Serial.println("Start ping's... Number, Time after start , ping in microseconds");
}

unsigned long number=0; // number of probe
//*
void loop() {
 number++;
 if ( number != 0 ) {
   unsigned int echoTime = sonar.ping(); // microseconds
   unsigned long t=millis();
   Serial.print(number);Serial.print("\t");
   Serial.print(t);Serial.print("\t");
   Serial.print(echoTime);Serial.println();
   delay(50); //todo check optimal time. Recomended ower 50 ms, but print take time too.
 } else {
   Serial.println("End of numbers. Wait 5 min then again...");
   flashLed(5*60); // blink 5 min then again number=0
  }
}
//*/
// as in ping demo:
/*
void loop() {
  delay(50); // Wait 50ms between pings (about 20 pings/sec).
  unsigned int echoTime = sonar.ping(); // microseconds
  Serial.print("Ping cm,ns:\t");
  Serial.print(sonar.convert_cm(echoTime));
  Serial.print("\t");
  Serial.println(echoTime);
}
//*/

Sketch 2. After first experiment I was see that distance data is not too differend and can all store into small 1.5 kB of memory. This programm wait 5 second (blink LED) then show agregate statistic information on serial port. For plot graph you can get same table, sort by distance then plot graph. This program can make misstake after 1 hour of work, and can have forgot same data if the same object will be move and not enought memory (array cell) for store all data. This code will be write error message into console, if some data will be forgot.

// Sonar statistic's
// (C) www.AlexeyK.com 2016. This source code under GNU GPL v2 license.

#include <NewPing.h>

#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

void flashLed(int second) {
  for (int i=0;i<second*4;i++) {
    digitalWrite(13,HIGH);
    delay(250);
    digitalWrite(13,LOW);
    delay(250);
  }
}

void setup() {
  Serial.begin(9600); // Open serial monitor at 115200 baud to see ping results.
  Serial.println("Collecting ping's data. Wait 5 second then start...");

  pinMode(13, OUTPUT);
  flashLed(5);

  Serial.println("Start ping's...");
}

// ------ special statistic data 'processor' - group data into arduino ------

#define TArrayIndex unsigned int
// template: array size, value type: signed/unsigned int/long/float(floating point values not recomended), count type: unsigned int/long
template <int STAT_MAX_DATA, typename TValue,typename TCount> class MeasureMath {
  private:
    TCount MAX_COUNT;
  public:
    //#define MAX_COUNT 65535 // depends of TArrayIndex type: byte 255 or unsigned int 65535

    TValue stat_value[STAT_MAX_DATA]; // measure data
    TCount stat_count[STAT_MAX_DATA]; // measure count
    TArrayIndex stat_using; // using array size for stat_count/stat_value.
    unsigned long stat_start; // time of start data collected.
  MeasureMath()
  {
    stat_using=0;
    stat_start=0;
    MAX_COUNT=0;//std::numeric_limits<TCount>::max(); // required limits.h
    MAX_COUNT--; // owerflow for get max value.
    TArrayIndex testI=STAT_MAX_DATA+1; // check TArrayIndex: must can store value up to STAT_MAX_DATA+1
    if (testI<0 || testI!=STAT_MAX_DATA+1) Serial.println("ERROR: bad index type!");
  }

  void onNewMeasure(long value) // this method call for register new measurement.
  {
    int dataI;
    for (dataI=0; (dataI<stat_using) && (stat_value[dataI]!=value);dataI++); // search cell
    if (dataI==stat_using) { // not found - make new cell
      if (stat_using>=STAT_MAX_DATA) {
        Serial.println("ERROR: no memory for store new value!");
        return;
      }
      stat_value[stat_using]=value;
      stat_count[stat_using]=1;
      stat_using++;
      return;
    }
    if (stat_count[dataI]>=MAX_COUNT) {
      Serial.print("ERROR: owerflow for value ");Serial.print(value);Serial.println();
      return;
    }
    stat_count[dataI]++;
  }

  void resetStat() { // clear statistic data
    stat_using=0;
    stat_start=millis();
  }

  unsigned long getStatCountSumm() { // get all stored measurement count
    unsigned long s=0;
    for (TArrayIndex i=0;i<stat_using;i++) s+=stat_count[i];
    return s;
  }

  TValue getStatMin() { // find minimal value
    if (stat_using==0) {
      Serial.println("No data for find min value.");
      return 0;
    }
    TValue m=stat_value[0];
    for (TArrayIndex i=1;i<stat_using;i++) if (m>stat_value[i]) m=stat_value[i];
    return m;
  }

  TValue getStatMax() { // find maximal value
    if (stat_using==0) {
      Serial.println("No data for find max value.");
      return 0;
    }
    TValue m=stat_value[0];
    for (TArrayIndex i=1;i<stat_using;i++) if (m<stat_value[i]) m=stat_value[i];
    return m;
  }

  double getStatAverage() { // return average of all stored measurement
    if (stat_using==0) {
      Serial.println("No data for find average value.");
      return 0;
    }
    double sum=0, count=0;
    for (TArrayIndex i=0;i<stat_using;i++) {
      double cnt=stat_count[i];
      sum+=(double)stat_value[i] * cnt;
      count+=cnt;
    }
    //Serial.print("dbgAVG Count=");Serial.print(count);Serial.print(", sum=");Serial.println(sum);
    return sum/count;
  }

  double getStatStdE() { // math standard error of all collect data
    if (stat_using==0) {
      Serial.println("No data for find standard error.");
      return 0;
    }
    double average = getStatAverage();
    double sumSq=0, count=0;
    for (TArrayIndex i=0;i<stat_using;i++) {
      double cnt=stat_count[i];
      double a = sq(((double)stat_value[i]-average)) * cnt;
      sumSq+=a;
      count+=cnt;
    }
    double D = sumSq / count; // dispersion
    return sqrt(D); // standard error
  }

  void printStat() {
    //unsigned long timeSt=micros(); // for check CPU performance only. Or millis();
    TValue sMin=getStatMin();
    TValue sMax=getStatMax();
    unsigned long sCount=getStatCountSumm();
    double average=getStatAverage();
    double variance=getStatStdE();
    //unsigned long timeEnd=micros();
    Serial.print("Count: ");Serial.print(sCount); Serial.print(", min: ");Serial.print(sMin); Serial.print(", max: ");Serial.print(sMax); Serial.print(", average: ");Serial.print(average); Serial.print(", variance: ");Serial.print(variance);
    Serial.println();
    //Serial.print("Calculate time, ns: ");Serial.println(timeEnd-timeSt);
  }

  unsigned long getCollectTime() {
    return millis()-stat_start;
  }

  void printStatData() {
    Serial.print("Collecting data stored ");Serial.print(stat_using);Serial.print(" values, collect time ");Serial.print(getCollectTime());Serial.println(" ms, data count/values:");
    for (TArrayIndex i=0;i<stat_using;i++) {
       Serial.print(stat_count[i]);Serial.print("\t");Serial.println(stat_value[i]);
    }
  }

};

MeasureMath<220, unsigned int, unsigned int> mstat; // warning: with this MeasureMath on arduino - ower 16000 measure can make arifmetical misstake.

void loop() {
  unsigned int echoTime;
  for (int i=0;i<40;i++) {
    delay(50); // Wait 50ms between pings (about 20 pings/sec).
    echoTime = sonar.ping(); // microseconds
    mstat.onNewMeasure(echoTime);
  }
  Serial.print("Ping cm,ns:\t");
  Serial.print(sonar.convert_cm(echoTime));
  Serial.print("\t");
  Serial.println(echoTime);

  if (Serial.available()>0) {
    int cmd = Serial.read(); // int or byte or char?
    if (cmd=='r' || cmd =='R') {
      Serial.println("Reset stat data.");
      mstat.resetStat();
    }
  }

  mstat.printStat();
  mstat.printStatData();
}
The template MeasureMath can use not only for math length data, it can use for diffetent statistic data. The code on this page is under GNU GPL License v2.

As you can see, the Arduino UNO have enought memory (1.5kB) for make same non trivial math statistic with array of data (≈250 of element). Same words aboud programm performance. The more often data (distance) will be stored first cell of array that no often distance (cause probability theory work). Cause it the more data point will be seek faster for inserting into data array (for found position in array usualy need seek less that 1/2 cell of array). But after long time the array of data will be stored more different elements and math summary statistic will be required more time (average, standard deviation, etc.).

Measurements, data and graphics

1. At the begin I was check distance to tree surface (table), length 22.5 cm (this value is result of check by roulette from sensor circuit board). Collect 31212 measurements of 27 minutes 18 seconds. The sound delays was between from 1199 ns to 1219 ns, average delay is 1208.55 ns. Standard deviation is ± 2.624 ns. See graph 1, data in table 1.

graph, cm graph, ns

Graphic 1. Distribution time (length)

Table 1. The group data of measurement 1
nscmcount
119921.0350941
120321.105262558
120721.1754414186
121121.2456114146
121521.31579256
121921.3859625

2. At the second experiment was check distance to tree surface (table), distance 46 cm. Collect 40323 measurements of 36 minutes 12 seconds. The delay was from 2539 ns to 2587 ns, average delay was 2552.40 ns. Standard deviation was ± 6.153 ns. See graphic 2, data in table 2.

graph, cm graph, ns

Geaph 2. Distribution time (length)

Table 2. The group data of measurement 2
nscmcount
253944.543861
254344.61404111
254744.684216973
255144.7543924105
255544.824566516
255944.8947497
256344.964913
256745.0350940
257145.10526730
257545.175441436
257945.24561301
258345.315797
258745.385963

3. At the next was check distance to plastic bag: length 118.7 cm. Collect 8453 measurements of 8 minutes. The delay was from 5779 ns to 6127 ns, average delay was 5955.40 ns. Standard deviation was ± 51.691 ns. See graph 3, data in table 3.

graph, cm graph, ns

Graph 3. Distribution time (length)

(collapse)Table 3. The group data of measurement 3 (open)

Table 3. The group data of measurement 3
nscmcount
5779101.391
5783101.462
5787101.532
5807101.8814
5811101.954
5827102.238
5831102.3063
5835102.3799
5839102.4410
5847102.582
5851102.657
5855102.72236
5859102.79275
5863102.868
5875103.073
5879103.1488
5883103.21121
5887103.2813
5895103.421
5899103.4913
5903103.56182
5907103.63500
5911103.7078
5915103.772
5927103.98114
5931104.05464
5935104.12278
5939104.197
5947104.337
5951104.40270
5955104.471625
5959104.54936
5963104.6110
5971104.752
5975104.8259
5979104.89917
5983104.96546
5987105.0431
5991105.111
5999105.2554
6003105.32244
6007105.39167
6011105.462
6023105.679
6027105.74348
6031105.81167
6035105.8819
6047106.0911
6051106.16105
6055106.23126
6059106.303
6063106.371
6067106.441
6075106.5857
6079106.6577
6083106.7211
6095106.931
609910715
6103107.0727
6107107.145
6111107.211
6127107.493

4. At the next was check distance to cloth, length 67 cm. Collect 47586 measurements of 43 minutes 57 seconds. The delay was between from 4011 ns to 4779 ns, average delay was the 4114.44 ns. Standard deviation was ± 60.009 ns. See graph 4, data in table 4.

graph, ns graph, cm

Graph 4. Distribution time (length)

(collapse)Table 4. The group data of measurement 4 (open)

Table 4. The group data of measurement 4
nscmcount
401170.371
401570.4415
401970.5146
402370.5814
403170.721
403570.7911
403970.86684
404370.931465
404771625
405171.076
405571.141
405971.2150
406371.282064
406771.357168
407171.421964
407571.4944
407971.565
408371.636
408771.703025
409171.779286
409571.842935
409971.9125
410371.981
410772.0511
411172.12674
411572.191927
411972.261122
412372.336
412772.402
413172.476
413572.54329
413972.612967
414372.68498
414772.7538
415172.822
415572.893
415972.96431
416373.041112
416773.11704
417173.188
417973.322
418373.39106
418773.462959
419173.53885
419573.6016
419973.671
420373.743
420773.81146
421173.88730
421573.95425
421974.028
423174.2376
423574.30554
423974.37649
424374.4415
425574.657
425974.72212
426374.79232
426774.8660
427975.074
428375.14146
428775.21369
429175.2846
430775.56119
431175.63262
431575.7065
431975.771
433175.9820
433576.0570
433976.1238
435576.404
435976.4727
436376.5413
438776.962
441177.391
443177.742
443577.802
443977.881
445978.232
447978.581
448378.652
451179.142
453179.491
453579.563
455579.911
463981.391
465181.601
465981.744
467982.092
468382.161
4731833
477983.842

5. At the next was check distance to wall (concrete), distance 291 cm. The data collect some of outlier, cause I show statistic with outlier data and without outlier, (in the bracket wrote with outlier). Collect 2315 (raw 2320) of measurements ≈ 3 minutes. Delay of sound was between 16715 (raw 11107) ns and 16883 ns, average delay was 16763.05 (with outlier 16750.94) ns. Standard deviation ± 29.8355 ns (with outlier 262.29 ns). See graph 5 (without outlier), data in table 5; graph 6 with full data (with outlier).

graph, cm graph, ns

Graph 5. Distribution time (length), without first rows

graph

Graph 6. Distribution time (length), all data

(collapse)Table 5. The group data of measurement 5 (open)

Table 5. The group data of measurement 5
nscmcount
11107194.861
11111194.931
11135195.351
11179196.121
11187196.261
16715293.251
16719293.3224
16723293.39129
16727293.46201
16731293.53125
16735293.6012
16739293.671
16743293.744
16747293.81148
16751293.88358
16755293.95280
16759294.0263
16763294.095
16771294.2330
16775294.30202
16779294.37286
16783294.44133
16787294.5121
16791294.582
16799294.7251
16803294.7963
16807294.8649
16811294.9313
16815295.001
16823295.149
16827295.217
16831295.2813
16835295.356
16839295.421
16847295.563
16851295.6330
16855295.7022
16859295.7713
16863295.841
16875296.055
16879296.122
16883296.191

The standard deviation (error) is depends of distance and square of object (material). See result in table 6 of standard deviation.

Table 6. Standard deviation in all experiments
Length, cmAverage distance, nsStandard deviation, nsStandard deviation, cmComment
122.51208.552.624± 0.046tree
2462552.46.153± 0.108tree
3118.75955.451.691± 0.907plastic / bag
4674114.4460.009± 1.053cloth
529116763.05
(raw 16750.94)
29.836
(raw 262.29)
± 0.523
(raw ± 4,602)
concrete / wall

Distribution on first graph (1, 2) show that it is normal distribution, but other experiment (3 and 4) show that it is may be not normal distribution. Unfortunately, the feature averages converge to precission value work fine with normal distribution. With other distribution and discrete time intervall (4 ns), easy average value not enought for increase precission and get absolute best value of distance.

In the math rules for normally distributed data (in the Russian this rule call the rule of 3th Sigma, 3σ), every 370 measure (in the normal distribution) will be have more different between average value, gread that 3 standard deviation. Despite non normal distribution, in experiment 3, rules for normally distributed data is work: only in 9 of 8453 (0.1%) measurement have error ower 155 ns (± 2.7 cm).

This experiment was do in non moving object, without car motor noise, and some power surges. But realy work on moving car show that gread («unreal») error exist, and too often. The error found more often on non flat surface (small) objects. The moving car with ultrasonic sensor add new problems: the correct average of changing data in the fast moving, speed measure, noise and changing environment. See different betwen accuracy and precision for understanding whay average can not work for same data. For easy remove outlier I recomended use the median. The function for evaluate median distance exist in library NewPing::ping_median(it), the result of this function (in library) is nanosecond, you can transfer the nanosecod value into centimeters or inches.

Small object and background

If take object with small square for high detect error (square less that 5 cm2), and big background, then with collect too much statistic data can be see object and bacground together. On dotted graph 7 can see length (sound time, ns) from two object, without moving of object or sensor.

object and background HC-SR04

Graph 7.How see ultrasonuc sensor small object and flat background

On graph 7 can see backgroun on distance 215 cm, and small object before background (the big cloud of points) on distance 185 см, and one non real point (error) — it can be echo from background.

Depends of standard deviation from square and length

For check exist of depends standard deviations of measurement from length was be make addition experiment. Some square plastic peaces was moved between 20 cm up to 180 cm. The result of this experiment present on graph 8.

The graph of standard deviation depends from length to object, ultrasonic sensor, ns

Graph 8, depends ultrasonic sensor standard deviation by length and square in naniseconds (20-180 cm)

The data was too more noise and not enought data to make precission formuls for show depends standard deviation (error) from all parameters: length, square, material, ange of rotate square. But, if collect more data, it will be possible. At the result of experiment I make same formuls for show depend standard deviation from length (table 7), this formula able for distance between 20 cm to 160 cm.

Table 7. Function of standard deviation of ultrasonic sensor depends on length
Square, cm2Function, nsR2 (coefficient of determination)
112.50.00587*x-4.440.959
112.52.1571*exp(0.0004838*x)0.903
542.4872e-6*x^2-0.01333*x+27.3680.982
54f(x)=0.00773*x-7.5930.754
360.00628*x+0.9270.958
361.78845e-7*x^2+0.00442*x+4.6680.961

In this table text ^2 wrote for share this formul's to table processor (Open Office Calc); the better result is where great coefficient of determination. The often of depends on 20 to 160 cm is linear. But for experiment with 54cm2 square the depends is non linear. It can be describe as non linear square of object (as concave lens or concave mirrow).

The result: standard deviation (error) is depends from length to object, and shape of object.

Conclusions

Conducted a series of experiments in a stationary position on the measurement precision of distances to objects. The data seemed quite encouraging. According to these measurements, the sensor gave readings with an precision from 0.5 cm to 2-3 cm, at the depends of distance. But the stated range in datasheed at 4 meter seemed unconvincing. After 2 meter will start same big error in measure distance. Was be make graph of data, presentet in this text.

 
© www.AlexeyK.com, 2016