NewPing Library: HC-SR04, SRF05, SRF06, DYP-ME007, Parallax PING))) - v1.7

some ideas about accuracy...

i have just gotten my sr04 and played a lithe with it. i intend to use it to measure oil level in a tank. so accuracy is quite important.
after reading on the web there are two main points that seem to influence the accuracy of the sensor and both are not accounted for in the newping library:

  • one is temperature. it would be nice to be able to set the temperature that newping uses for the conversion to cm/inces. depending on the temperaure the error can easily be in the range of 10% if temperature is not accounted for.

  • the other one is the which echo is received and evaluated. if i do repeated measurements i notice that the results will be relatively near around two or tree that are themselves further apart. this looks like it is consistent with some articles that say this error comes from reading the second or third echo and not the first and should be especialy noticeable in the short range.

so to be able to make more accurate measurements i started reading 10 or 50 pings, iterating over all of them and throwing away all that are equal to the largest and all that are equal to the smallest and then averaging the rest. if there are non available after throwing the smallest and largest away i take the smallest. the conversion to cm is then done in float.

this seems to give very accurate and reproduceable results. doing this repeatedly in a range of 1cm to 1m gives identical results up to at least 1mm.

maybe a 'slow scan' option could add this to the newping library for applications where fast response times are not needed.

justme1968:
some ideas about accuracy...

i have just gotten my sr04 and played a lithe with it. i intend to use it to measure oil level in a tank. so accuracy is quite important.
after reading on the web there are two main points that seem to influence the accuracy of the sensor and both are not accounted for in the newping library:

  • one is temperature. it would be nice to be able to set the temperature that newping uses for the conversion to cm/inces. depending on the temperaure the error can easily be in the range of 10% if temperature is not accounted for.

  • the other one is the which echo is received and evaluated. if i do repeated measurements i notice that the results will be relatively near around two or tree that are themselves further apart. this looks like it is consistent with some articles that say this error comes from reading the second or third echo and not the first and should be especialy noticeable in the short range.

so to be able to make more accurate measurements i started reading 10 or 50 pings, iterating over all of them and throwing away all that are equal to the largest and all that are equal to the smallest and then averaging the rest. if there are non available after throwing the smallest and largest away i take the smallest. the conversion to cm is then done in float.

this seems to give very accurate and reproduceable results. doing this repeatedly in a range of 1cm to 1m gives identical results up to at least 1mm.

maybe a 'slow scan' option could add this to the newping library for applications where fast response times are not needed.

  1. Because you need an outside thermometer to measure temperature, it was decided not to overly complicate the NewPing library. However, NewPing IS designed to adjust with temperature. Simply use the ping() method which gives you a time. Then, do your own calculation of distance based on your thermometer attached to the Arduino. In almost all situations, the default distance works well for average indoor temperatures. But, if you need accuracy, you're going to need additional hardware and then (of course) you're going to need to do your own calculation. But, NewPing will work with this situation just fine.

  2. This isn't a problem with the library, nor can it be fixed in a library. This is fixed by following the instructions in my sample sketches. Basically, if you ping a second time too quickly after a previous ping, you can get an echo. This can't be fixed by the library as it's only reading what the sensor outputs, which is a digital signal (the library has no amplitude information). The solution is to simply heed my warnings in my sketches. Which is to not ping too quickly. Your pings shouldn't be more frequently than around once every 29ms. If you're in a highly sonicly reflective environment, or your sensor is overly sensitive, you may need to increase this amount. If it's not something fast moving (probably like in your case) ping once every second or two even. That way, you can be assured that you're not reading an echo. NewPing doesn't control the speed of the pings YOU do. If you want to make it slow, just make it slow. Maybe I'm missing something?

Tim

Hi Teckel,

did investigate the duration2cm() function here - http://arduino.cc/forum/index.php/topic,165860.0.html -
might be useful in your library.

robtillaart:
Hi Teckel,

did investigate the duration2cm() function here - http://arduino.cc/forum/index.php/topic,165860.0.html -
might be useful in your library.

I tried something similar in the past, but honestly it's not worth the extra cycles adding this type of code adds. Basically, it's pointless to try to make the math more accurate than the sensor is. Ultrasonic sensors are not very accurate anyway. Also, there's actually an argument as to the speed of sound (everyone likes to use a different number). This is why I settled on an integer and the value that I default to. It provides very accurate distance results in typical indoor temperatures and does so very fast using very little program space.

Instead of getting into this battle, I avoid it. Just use the ping() method which returns a ping time. Then, use whatever math you want (based on temperature or whatever). If you want to use a fraction and want to use a different method to calculate this (like the above linked example) that's fine also.

Keep in mind that NewPing is an ultrasonic ping library, not a floating point, math, or temperature library. I'm sure you can find or develop a library that allows you to divide a floating point number without using floating point math. This would be then just included in your sketch along with NewPing to calculate a distance at whatever accuracy level you desired. But keep in mind that the sensor isn't that accurate anyway (only about 1cm). So all this effort may be wasted anyway.

Tim

The fact that the sensors are not accurate is imho no argument to use math that adds additional errors. The relative errors add up.
That said, I agree with you that most applications do not need this level of accuracy.

Thanks,

robtillaart:
The fact that the sensors are not accurate is imho no argument to use math that adds additional errors. The relative errors add up.
That said, I agree with you that most applications do not need this level of accuracy.

Thanks,

If you want to use floating point values, more power to you, go for it! NewPing FULLY supports doing this. YOU have the ability to change the US_ROUNDTRIP_CM and US_ROUNDTRIP_IN values to whatever you want, or use ping() and do any other calculations you wish factoring in temperature, altitude, humidity, etc.

The fact is, my integer values are typically more accurate than when people try to over complicate things by using floating point values in attempts to get more accurate values. But, if you're in search for the holy grail of ultrasonic measurement, use ping() and apply your magic formula.

This is NOT something that will be added to NewPing as it's outside the scope of the library to be any more accurate than it is now. Because, to be more accurate you'd need a thermometer, and apply your own formula. So, while this is a fine topic, it shouldn't be here as it will never be added to NewPing. Heck, there's debate on the speed of sound, I'm just staying out of the fight.

Topic closed.

yes. i do think you missed something.

if i run the new ping sampe code i get a minimum error of 1cm or 2cm even in an absolutely stable setup, if i change the calculation to float the error is a little bit smaller but still in the range of 1cm. and both will give no stable values. the results fluctuate with ever line on the console.

if i use my 'slow' method to measure i get an error that is in the range of 1mm and absolutely stable values. no fluctuation. repeated measurements give the same distance again and again.

this is a huge difference for my application. and now it makes sense to appy the temperature correction.

if you say that this is not your intendet usage scenario that is fine with me. but to say the sensors are inherently so bad that nothing can be done ist just plain wrong.

justme1968:
yes. i do think you missed something.

if i run the new ping sampe code i get a minimum error of 1cm or 2cm even in an absolutely stable setup, if i change the calculation to float the error is a little bit smaller but still in the range of 1cm. and both will give no stable values. the results fluctuate with ever line on the console.

if i use my 'slow' method to measure i get an error that is in the range of 1mm and absolutely stable values. no fluctuation. repeated measurements give the same distance again and again.

this is a huge difference for my application. and now it makes sense to appy the temperature correction.

if you say that this is not your intendet usage scenario that is fine with me. but to say the sensors are inherently so bad that nothing can be done ist just plain wrong.

Maybe you need to turn on rounding in the header file? #define ROUNDING_ENABLED true

I get stable values with no fluctuations with my library without any modifications with all the sensors I have.

The math doesn't change from sensor reading to sensor reading. So, if the sensor is outputting the exact same ping time, the exact same distance calculation will occur. With this said, it does depend on how it's rounded. By default, NewPing truncates the distance calculation. So if the distance is say 50.95cm it could flip between 50 and 51cm. This has to do with the margin of error of the sensor, which is around 0.33cm. With the rounding enabled, the same situation as shown above would yield a stable 51cm.

I think you've confused the accuracy of the sensors and the method of time to distance calculation. But, in all cases, you can simply just use the ping() method which just gives you the ping time. Then, you can do whatever logic you wish to convert this to a distance. The ping_cm() and ping_in() methods are meant to be a down and dirty, fast, and tight way of getting accurate results. This works for 99.9% of people best. But, if you're in the 0.01% that must use a different formula and use a thermometer to get more accurate results, NewPing is already set to work for you, just use the ping() method.

Basically, what I'm saying is that you should use the ping() method and use your own math. NewPing is not the place to debate the speed of sound (which is yet another debate that 0.01% of people want to get into). I'm not getting into this and won't be changing NewPing making it bigger and more complex for 0.01% of people. One of the primary goals of NewPing is to be smaller and faster than other ping libraries. While adding rounding and temperature calculations could make it ever so slightly more accurate, it's simply not within the scope of the project, and what the ping() method is exactly designed for. So please, use ping() and this can be the last I hear of time to distance calculations, it's an old, dead, and boring topic that I don't wish to debate further.

Tim

I'm trying to use an SR04 with the single-pin method, but I can't get it to work. I've tried it with two different identical looking sensors purchased from Amazon and eBay. This is on an Arduino Uno (might be a SainSmart recreation but it looks like an authentic one, not sure.

What troubleshooting steps can I try?

chambo622:
I'm trying to use an SR04 with the single-pin method, but I can't get it to work. I've tried it with two different identical looking sensors purchased from Amazon and eBay. This is on an Arduino Uno (might be a SainSmart recreation but it looks like an authentic one, not sure.

What troubleshooting steps can I try?

Start with supplying your sketch, schematic, and exactly what your results are. "can't get it to work" is too vague to diagnose over the internet with no other information.

Tim

really new to arduino and coding, so I am trying to figure out this library. I have been searching and trying things out on my own, but haven't see any examples of using the 2 commands the way I am attempting. I think I have figured it out... at least it appears this snippet of my code is working.
What I am trying to do with this is return the center reading (median) out of 15. using convert_in to convert it to inches. I figure returning 15 readings, choosing the median one is the middle between the high and low readings.
Is this correct?

centerDist = (sonar.convert_in(sonar.ping_median(15)));
//median ping (out of 15), distance in inches and assign to centerDist on sensor named "sonar"

I run it and it seems to give me a very accurate reading, in inches. Is there anything wrong with this format?

MetalEd222:
really new to arduino and coding, so I am trying to figure out this library. I have been searching and trying things out on my own, but haven't see any examples of using the 2 commands the way I am attempting. I think I have figured it out... at least it appears this snippet of my code is working.
What I am trying to do with this is return the center reading (median) out of 15. using convert_in to convert it to inches. I figure returning 15 readings, choosing the median one is the middle between the high and low readings.
Is this correct?

centerDist = (sonar.convert_in(sonar.ping_median(15)));
//median ping (out of 15), distance in inches and assign to centerDist on sensor named "sonar"

I run it and it seems to give me a very accurate reading, in inches. Is there anything wrong with this format?

That's correct. That command will return the median distance measurement in inches out of 15 pings. With that many iterations, it should give very stable results.

If you find that the distance is slightly off, it's because of the convert_in() math, which by default simply divides the ping microseconds by 146. For example, the following code will give the same result:

centerDist = sonar.ping_median(15) / 146;

If you find that you need more accurate conversion from ping time to distance and wish to create a calculation based on temperature readings, you can replace the division by 146 (or the convert_in()) to whatever calculation you wish based on temperature, altitude, humidity, or whatever level of accuracy you wish. The convert_in() is designed to be fast, create small code, not require additional sensors, and be fairly accurate for almost all users. If you need higher accuracy, you can use whatever calculations you wish with NewPing.

Tim

A common problem with using NewPing is getting a `__vector_7' error during compile. What this error means is that you're using two libraries that are both trying to use timer 2. The timer 2 stuff is only in NewPing for the timer interrupt method ping_timer(). If you're just using the standard ping(), ping_in(), ping_cm(), or ping_median() methods, NewPing is not using timer 2. However, the compiler is not smart enough to know that you're not using those interrupts. So you'll get this error if any other library is also using timer 2.

There's an easy solution as long as you're not using ping_timer(). Change the NewPing.cpp program and comment out the section that calls timer 2. Find this section at around lines 210 - 216 in version 1.5:

#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
ISR(TIMER4_OVF_vect) {
#else
ISR(TIMER2_COMPA_vect) {
#endif
	if(intFunc) intFunc(); // If wrapped function is set, call it.
}

Comment it all out like this:

/*
#if defined (__AVR_ATmega32U4__) // Use Timer4 for ATmega32U4 (Teensy/Leonardo).
ISR(TIMER4_OVF_vect) {
#else
ISR(TIMER2_COMPA_vect) {
#endif
	if(intFunc) intFunc(); // If wrapped function is set, call it.
}
*/

That's it! Also, if the conflict is with the tone library, you can use a different tone library that doesn't use timer 2. I've created a couple tone replacement libraries that not only use timer 1 instead of timer 2 to avoid a conflict, they also have many other advantages:

NewTone - About 1,200 bytes smaller code size than the standard tone library. Faster execution time. Exclusive use of port registers for fastest and smallest code. Higher quality sound output than tone library. Plug-in replacement for Tone. Uses timer 1 which may free up conflicts with the tone library.

toneAC - Nearly twice the volume (because it uses two out of phase pins in push/pull fashion). Higher quality (less clicking). Capability of producing higher frequencies (even if running at a lower clock speed). Nearly 1.5k smaller compiled code. Bug fixes (standard tone library can generate some odd and unpredictable results). Can set not only the frequency but also the sound volume. Less stress on the speaker so it will last longer and sound better.

In addition, version 1.6 of the NewPing library (currently under development) will include a setting that allows you to easily turn off the timer 2 stuff if you're not using the ping_timer() method.

If, however you are using the ping_timer() method you still may have options.

First, do you really need to use the ping_timer() method? Many people incorrectly assume that if they're using multiple sensors they must use the ping_timer() method shown in my 15 sensor example. That's simply not the case. It's best to do it that way for "multitasking" reasons. But, there's no reason why you can't just ping() multiple sensors like in the NewPingExample sketch.

Secondly, maybe you can use a different library that's causing the timer 2 conflict. I've shown two other tone libraries above that use timer 1 instead of timer 2. But, if the timer 2 conflict is with a different library, there's still a chance that you have another option. For example, maybe the timer 2 conflict is with an LED dimmer library. Try to find another LED dimmer library that uses timer 1 instead. While this may not be an option for every library, there are many libraries out there so it's worth looking into.

Tim

Hi Tim,

This is great information! Many people have no idea about timers and conflicts.

Can you include this in the NewPing documentation, or point to this post there? I KNOW I'll need this in the future..

terryking228:
Hi Tim,

This is great information! Many people have no idea about timers and conflicts.

Can you include this in the NewPing documentation, or point to this post there? I KNOW I'll need this in the future..

I have this post linked from the opening post in this thread. Also, I created Wiki pages for this as well as another common question here: Google Code Archive - Long-term storage for Google Code Project Hosting.

Tim

Thanks, Tim! I linked to your WIKI pages from the ArduinoInfo.Info WIKI here:
http://arduino-info.wikispaces.com/UltraSonicDistance

Please use code tags (# button above the smileys)
Please press CTRL-T in the IDE to autoformat the layout before copying, makes it far more readable and debugable,

Thank you

// walkerForwardComplete.pde - Two servo walker.
// Complete code with obstacle avoidance
// (c) Kimmo Karvinen & Tero Karvinen http://BotBook.com
// Updated by Joe Saavedra, 2010
#include <Servo.h>

Servo frontServo;
Servo rearServo;
/* Servo motors - global variables */
int centerPos = 90;
int frontRightUp = 72;
int frontLeftUp = 108;
int backRightForward = 75;
int backLeftForward = 105;
int walkSpeed = 150; // How long to wait between steps in milliseconds
int centerTurnPos = 81;
int frontTurnRightUp = 63;
int frontTurnLeftUp = 117;
int backTurnRightForward = 66;
int backTurnLeftForward = 96;

/* Ping distance measurement - global variables */
int pingPin = 4;
long int duration, distanceInches;
long distanceFront=0; //cm
int startAvoidanceDistance=20; //cm

long microsecondsToInches(long microseconds)
{
return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
return microseconds / 29 / 2;
}

long distanceCm(){
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);

pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);

distanceInches = microsecondsToInches(duration);
return microsecondsToCentimeters(duration);
}

void center()
{
frontServo.write(centerPos);
rearServo.write(centerPos);
}

void moveForward()
{
frontServo.write(frontRightUp);
rearServo.write(backLeftForward);
delay(125);
frontServo.write(centerPos);
rearServo.write(centerPos);
delay(65);
frontServo.write(frontLeftUp);
rearServo.write(backRightForward);
delay(125);

frontServo.write(centerPos);
rearServo.write(centerPos);
delay(65);
}

void moveBackRight()
{
frontServo.write(frontRightUp);
rearServo.write(backRightForward-6);
delay(125);
frontServo.write(centerPos);
rearServo.write(centerPos-6);
delay(65);
frontServo.write(frontLeftUp+9);
rearServo.write(backLeftForward-6);
delay(125);

frontServo.write(centerPos);
rearServo.write(centerPos);
delay(65);
}

void moveTurnLeft()
{
frontServo.write(frontTurnRightUp);
rearServo.write(backTurnLeftForward);
delay(125);
frontServo.write(centerTurnPos);
rearServo.write(centerTurnPos);
delay(65);
frontServo.write(frontTurnLeftUp);
rearServo.write(backTurnRightForward);
delay(125);

frontServo.write(centerTurnPos);
rearServo.write(centerTurnPos);
delay(65);
}

void setup()
{
frontServo.attach(2);
rearServo.attach(3);
pinMode(pingPin, OUTPUT);
}

void loop()
{
distanceFront=distanceCm();
if (distanceFront > 1){ // Filters out any stray 0.00 error readings
if (distanceFront<startAvoidanceDistance) {
for(int i=0; i<=8; i++) {
moveBackRight();
delay(walkSpeed);
}
for(int i=0; i<=10; i++) {
moveTurnLeft();
delay(walkSpeed);
}
}
else {
moveForward();
delay(walkSpeed);
}
}
}

Moderator edit: Please don't use copy for forum - just cut and paste between code tags.

Was there a question? AWOL

Good day

Sorry I am new to this forum and Arduino. With regards to the insect bot code I posted. The question was, how do I addapt the code to use a 4 pin ultrasonic sensor.

Kind regards

Kevin

Hi,

first - big thanks for this fantastic library. I used it in two projects so far. Works perfectly.
With my new project I am pushing the Arduino Mega to its limits. I need to drive two steppers - that's the time critical part.
When I enable the pings (using the timer from the 15 sensor example) they sometimes start to stutter as it can't keep the step frequency. As far as I can see it's when the distance to ping is high. If I hold my hand in front of the sensor it's much better.

I went through all the rest of my code. No delays (all implemented as FSM) and no unnecessary complicated calculations.

So I thought it might be possible to get out the last bit of performance with direct addressing of the pins (http://forum.arduino.cc/index.php/topic,46896.0.html) and with an alternative solution on the micros delays in newPing::ping_trigger()
But it is above my capabilities. I assume if it would be simple it would be in the library XD

Does someone see an “easy” to implement approach? Or am I on the wrong track at all?

Thanks
Robert