Hi again on the tests for the HC-SR04...
I have fixed the errors spotted by Tim (thanks once again) in the sketch and even managed to work it all without float variables ( ); and redone the tests with the assistance of my youngest daughter :%
As per my measurements the facts regarding the HC-SR04 are:
- FOV (full cone): horizontal ~20º, vertical ~13º
- Spatial resolution (full cone): ~0.6-1.4º
- Range: tested from 5 to 200 cm
- Accuracy: relative error ~0/5% (-1.3 ±4.6 %); absolute error ~-0.5/-1.5 cm (-0.4 ±1.2 cm).
- Precision: standard deviation ~0.1/0.5 cm (0.3 ±0.6 cm)
Worse in all regards than the standard specifications.
The related files are uploaded:
- test report: Box
- spreadsheets: Box, Box, Box
- sketch (also copied below): Box
I have worked on the results and included some charts. This one, f.eg., shows that using the mode as an estimator of the distance is not much better than the average once outlliers are removed (which is quite easier to do than calculating the mode): Box (Sorry - I still do not know how to post an image...)
Once the physical features of the sensor are reasonably clear, the issue is how to best use the readings. There are some suggestions in the forums/fora regarding this matter which, of course, recommend repeating the readings, but also using the mode instead of the average. I rather disagree with the latter: calculating the mode is computationally expensive and the average is very simple to compute and unskewed once the data is filtered. Here is my alternative proposal for "on the flight" calculations:
-
set a predefined maximum estimate of the deviation (f.eg. 1.5 cm or 3% of the value of the measurement, or both);
-
read N times the distance (suggested N= 20, or at least 10);
-
on the flight, calculate the summation of the readings (summation ? summation + reading), the main range (max, min) and the second largest range (max* = second largest reading, min* = second lowest reading);
-
once the N readings are completed:
-
if the largest range (max-min) does not exceed the predefined deviation, then provide the average as an estimator of the distance: distance_estimate ? summation/N;
-
else:
if the second largest range (max*, min*) still exceeds the predefined deviation, then the reading/measurement is incorrect and therefore cancelled (no reading is returned);
else, remove max and min from the readings sample and provide the resulting average as the estimator of the distance: distance_estimate ? (summation-max-min)/(N-2).
Regards,
/*
* Trials with Ultrasonic sensors
* R01: Two US sensors HC-SR04
* R02: One LCD Serial
* R03: One US sensor; the reading is done directly, without <Ultrasonic.h>
* Source of sensor interface code: http://www.arduino.cc/en/Tutorial/Ping
* Control of max/min distance range also introduced
* R04: Two US sensors - not tyet crossed for reading...
* Temperature correction for air speed
* R05: Testing of the US sensors
* Only one sensor at the time, output by serial display (PC, no LCD), capture of readings by CoolTerm
* Arduino board: mini Pro
* Number of readings and time interval can be controlled
* No control of distance range
* R06:
* Typo on sound speed ... corrected(!)
* Number of time intervals is reduced to 1 and 100 milliseconds
* All variabls are now int/long...! :-}
*/
//
// Definitions
//
// US sensor pin definitions:
int pingPin = 9; // trigger pin in the US sensor
int pongPin = 8; // data pin in the US sensor
// Other variables
#define NR 10 // number of repetition of readings under each test condition
#define tempAir 24 // Celsius - of course, can/should be changed!
long unsigned soundSpeed; // approx 340000 mm/s -> long is ok
long unsigned distance[NR]; // array with the readings of the repeated measurements, mm; precision of the readings is not beyond mm, so further digits are just false precision;
// long because of the operations involved.
int AT[2]={1,100}; // time interval between consecutive readings, milliseconds
//
// Setup
//
void setup()
{
Serial.begin(9600);
soundSpeed = 331300+606*tempAir; // mm/s; source: wikipedia
pinMode(pingPin, OUTPUT);
pinMode(pongPin, INPUT);
}
//
// Loop
//
void loop()
{
for (int i=0; i<2; i++) // repetition with different reading intervals
{
for (int j=0; j<NR; j++)
{
distance[j] = soundSpeed/100; // speed is in mm/s, duration in microseconds: 6 zeroes need to be removed, 2 go out here, the bañance later
distance[j] = distance[j]*durCalc(pingPin, pongPin); // distance (mm) = time (total sound travelling distance, microseconds) * speed (mm/s)
distance[j] = distance[j]/20000; // I remove the remaing 4 zeros here, plus the effect of two-way travel of the sound; this care with the zeros is because of size limitations of long
}
// print results as a batch:
Serial.print(" NR = ");
Serial.println(NR);
Serial.print(" AT = ");
Serial.print(AT[i]);
Serial.println(" milli seconds ");
for (int j=0; j<NR; j++)
{
Serial.print(" reading[ ");
Serial.print(j);
Serial.print("]= ");
Serial.println(distance[j]);
}
// ready for next loop:
delay(250);
}
}
//
// Functions
//
unsigned long durCalc(int pinI, int pinO)
{
// The PING is triggered by a HIGH pulse of 10 or more microseconds.
// Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
digitalWrite(pinI, LOW);
delayMicroseconds(2);
digitalWrite(pinI, HIGH);
delayMicroseconds(12);
digitalWrite(pinI, LOW);
//delay(10);
// The pongPin is used to read the signal from the PING))): a HIGH
// pulse whose duration is the time (in microseconds) from the sending
// of the ping to the reception of its echo off of an object.
return(pulseIn(pinO, HIGH, 35000)); // microseconds of (total) sound travel;
}