Go Down

Topic: NewPing Library: HC-SR04, SRF05, SRF06, DYP-ME007, Parallax PING))) - v1.7 (Read 387071 times)previous topic - next topic

robtillaart

#30
May 18, 2012, 07:44 pmLast Edit: May 11, 2013, 08:08 pm by robtillaart Reason: 1
@teckel
Thanks for the explanation!, good point as I did not investigate the temperature in it.

Quote
yet the humidity, pressure, altitude, etc. makes no difference in the speed (only the temperature affects the speed).

Humidity does make a difference, too. Furthermore the effect of temperature and humidity depends on the frequency of the sound.

see this publication for the math details - http://www.rane.com/pdf/ranenotes/Enviromental_Effects_on_the_Speed_of_Sound.pdf - (updated link)

To get a optimal pulseIn you should play with Timer1 as it can count in HW in steps of 1/16 us. That's as precise as Arduino gets. Use an IRQ to get the edges of the pulse and do a diff on the countervalues. See some of my experiments in this thread - http://arduino.cc/forum/index.php/topic,96971.45.html -
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

teckel

@teckel
Thanks for the explanation!, good point as I did not investigate the temperature in it.

Quote
yet the humidity, pressure, altitude, etc. makes no difference in the speed (only the temperature affects the speed).

Humidity does make a difference, too. Furthermore the effect of temperature and humidity depends on the frequency of the sound.

You're correct with humidity, I misspoke.  I meant to say at some point that humidity makes a slight difference.  You could add humidity into the calculations, but most of that could be discounted in the margin of error of the sensor itself.  And, the distance the sensor can read makes humidity mostly trivial under most situations.

I guess my point was really that using close whole integers is probably good enough for most purposes.  If not, use the ping function which outputs the time in microseconds and do your own calculations using a temperature sensor and optionally even a humidity sensor if your readings need to be that precise.  But, if your readings really need to be that precise, you probably should use a different ranging sensor.

With all this said, I do plan on looking further into this overhead delay and possibly adding a constant in the calculations if it's found that this delay is a constant.  I need to build a testing jig with distance measurements to accurately test multiple sensors in a very controlled manor. But, I'm not even going to do that until the pulseIn is rewritten as that could have an effect on these readings.  I could also add a microsecond to cm and inch conversion where you could also specify the temperature from a temperature sensor.  I do plan on sticking with using integers for basic conversions to save space and speed.  If more precise values can be verified, I'll add those to the code as an option for those who are already using floating point calculations in their sketch or don't have a code space issue.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

robtillaart

wrt floating point, keep the code in the integer/long range. Optionally do the math in millimeters (multiply all with 10) and bring it back to cm in the end (divide by 10) That gives higher precision without the overhead of the float. Did that in one of the conversion variations (prev post).

wrt Humidity, we discussed a year(?) ago measuring the depth of a water pit (well?). There the humidity was close to 100% so it affected the ping sensor readings to the max, so in the end the working allways depends on the context

Quote
I could also add a microsecond to cm and inch conversion where you could also specify the temperature from a temperature sensor.  I do plan on sticking with using integers for basic conversions to save space and speed.

imho set the temperature seperately to choose the divider to use from a small lookup table. Temp does not change that often, so conversion time will be minimized, no need to do the lookup agaiin and again.

(snippet, you get the idea)
Code: [Select]
`byte table[] = { ...,57,58,59,60, ...}div = 58; // defaultrnd = div/2;  //rnd is short for roundingsetTemperature(int F)  {  F = F/20;  div = table[F];  rnd = div/2;}unsigned int convert_cm(unsigned int echoTime){  return (echoTime + rnd)/ div;}`

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

teckel

wrt floating point, keep the code in the integer/long range. Optionally do the math in millimeters (multiply all with 10) and bring it back to cm in the end (divide by 10) That gives higher precision without the overhead of the float. Did that in one of the conversion variations (prev post).

wrt Humidity, we discussed a year(?) ago measuring the depth of a water pit (well?). There the humidity was close to 100% so it affected the ping sensor readings to the max, so in the end the working allways depends on the context

This is what the ping function is for, so you can do your own calculations based on microseconds as you see fit.  The ping_cm and ping_in are designed to be fast, lite and "good enough" for most situations.  After all, these kinds of sensors are typically used to provide "too close", and "clear ahead" readings, not really exact measurements.  If I could make an accurate calculation, I would.  It's simply not possible to be accurate to the mm, so a calculation like that would be pointless.  It's silly to have a ping_mm function if it's off by 15mm, no?  My goal is to be accurate to the cm, which with these sensors and how pulseIn works will be a real challenge.  mm resolution is a pipe dream, I suggest a laser sensor if that's what you need.

Basically, if you need some unique calculation that's accurate down to the mm, use the ping function and real-world calibration from your own unique sensor.  But, be warned.  Even if your formula is accurate, and you account for temperature and humidity, your result will still be off by as much as a cm or even more as there's other problems with how the readings are timed and the margin of error in the sensor itself.

And once again, this is why I made the statement that I did in my code, that I'm using integers and if you want to use something else, change the numbers.  Or better yet, use the ping function and use your own conversion function for your needs.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

terryking228

Tim, This is an approach that could perhaps be taken more often with libraries in general: Expose relevant internal (previously private) variables for the user who wishes to do additional calculations.

Maybe the library could even be structured so that if a user does that, the code for further/final calculations in the "usual" application is not compiled?

I'm still semi-illiterate about library details and conventions so this is just conjecture.
Regards, Terry King terry@yourduino.com  - Check great prices, devices and Arduino-related boards at http://YourDuino.com
HOW-TO: http://ArduinoInfo.Info

robtillaart

@Terry
Every published library is open, so users can allways "roll their own" variation of the library. I think Tim does a very good job keeping the lib as simple as possible because that means small footprint and performance most of the time. My additions and ideas is just searching the "edge of the possible"

@Tim
Code: [Select]
`////    FILE: newPing.cpp//  AUTHOR: Tim Eckel//    DATE: 2012-05-xx// VERSION: 1.1//// PUPROSE: new implementation of ping)))// LINK: http://...// HISTORY:// 1.0: ...// 1.1: ...`
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

terryking228

Hi, Rob..
You are always  (cut-paste) on the "edge of the possible"

Many Arduino users have no idea of what the source of a library looks like, or why it has that structure, or how to force it to recompile. They are happy (and capable) using a documented library.  But maybe if they could access the library functions at both a lower and higher level it would be useful. Maybe.

I'm not sure what side of the divide I'm on, or even where it is...

I really glad you guys are digging into this.
Regards, Terry King terry@yourduino.com  - Check great prices, devices and Arduino-related boards at http://YourDuino.com
HOW-TO: http://ArduinoInfo.Info

teckel

Tim, This is an approach that could perhaps be taken more often with libraries in general: Expose relevant internal (previously private) variables for the user who wishes to do additional calculations.

Maybe the library could even be structured so that if a user does that, the code for further/final calculations in the "usual" application is not compiled?

I'm still semi-illiterate about library details and conventions so this is just conjecture.

Terry, that's exactly how I see it.  I think the primary goal of the NewPing library is to measure the ping/echo time and distance as easily, quickly and accurately as possible.  But, when we get into advanced distance conversion, the requirement to consider temperature and to a lesser extent humidity take it outside the scope of an ultrasonic/ping library as other sensors are required.  For those with this need, the NewPing library provides the user with the low-level ping/echo timing.  And those with this need and knowledge of what other sensors are needed should already know the equation they need to use.

Not that I'm going to wash my hands of this and off-load it.  There's just more primary issues, like making pulseIn event-driven and a generally more accurate echo time sensor first.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

teckel

@Terry
Every published library is open, so users can allways "roll their own" variation of the library. I think Tim does a very good job keeping the lib as simple as possible because that means small footprint and performance most of the time. My additions and ideas is just searching the "edge of the possible"

@Tim
Code: [Select]
`////    FILE: newPing.cpp//  AUTHOR: Tim Eckel//    DATE: 2012-05-xx// VERSION: 1.1//// PUPROSE: new implementation of ping)))// LINK: http://...// HISTORY:// 1.0: ...// 1.1: ...`

Thanks for the suggestions!

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

teckel

Thanks to Terry King and YourDuino I did a round of range testing with 6 ultrasonic/ping sensors and the results were not as I expected.  I tested with 3 different models, (4) SF04, (1) SRF05, (1) SRF06.  The testing environment was in an air conditioned room at 75 degrees.  I tested at distances of 5cm, 10cm, 25cm, 50cm, and 100cm.  Longer distances were not tested in this round.  I believe the accuracy within 100cm or important as that's within the primary measurement distance.  Beyond 100cm it's not as much of an issue in most situations.  Once the pulseIn echo code is reworked, I'll do round 2 of testing out to a further distance.

The good news is that the sensors were all very consistent.  With all the SF04 and SRF05 generating nearly identical results (makes sense as the PCBs are nearly identical).  The SRF06 uses different components and yields slightly different measurements as a result.  It does appear to be a slightly better sensor (makes less clicking noise, hardly no lag, and results closer to actual speed of sound).  The maximum distance of the SRF06 seemed to be slightly lower, but that will be answered in round 2 of testing.

The results don't fit at all with the speed of sound calculations previously being used.  For the SF04 and SRF05 sensors, the calculation from microseconds to cm is:
cm = (microseconds + 37) / 48

For the SRF06 sensor, the calculation is:
cm = (microseconds + 5) / 51

Using these calculations, I could accurately measure any distance in the 5cm to 100cm range with all 6 sensors.  Albeit, not even close to speed of sound at this temperature, which should be right around cm = microseconds / 58.  If it was simply a long sensor lag, the additional microseconds added in the equation would be much higher, which they're not.

I have two theories as to why this is happening:
1) The pulseIn function is simply way off.  It's not even measuring time, but counting processor cycles, so there's a good possibility the time conversion is wrong.
2) Because I'm using port registers the code is faster.  And, since the formulas used to compute pulseIn are not based on port register manipulation but using the known slow higher-level digitalWrite command that's where the error is.  This doesn't seem likely as while digitalWrite is slow, it's not THAT slow.

At this point, I think diving into the pulseIn code and replacing it is a wise choice.  Then at least I'll have some other information to figure out the source of the time discrepancy.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

teckel

Rewrote the pulseIn to use micros() instead of loop counting.  With that change, I got accurate data that matches exactly with the speed of sound.  So, the problem was with pulseIn.  However, I'm not sure if the problem is how it calculates time (counting processor cycles) or it has to do with the time it takes for the sensor to send out a ping.  In any case, it doesn't matter as I've completely re-written the code to use simple timing which works great.  There's not yet an option for the ping to be event driven.  But, it is much faster and more accurate now.

I should be releasing v1.2 later tonight.

Tim

My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

terryking228

Quote
Rewrote the pulseIn to use micros() instead of loop counting.  With that change, I got accurate data that matches exactly with the speed of sound.  So, the problem was with pulseIn.

Beautiful, Tim.  You have challenged lots of assumptions with this and it's really good news that you are understanding what's happening. We'll all be listening for your next round!

Regards, Terry King terry@yourduino.com  - Check great prices, devices and Arduino-related boards at http://YourDuino.com
HOW-TO: http://ArduinoInfo.Info

robtillaart

Quote
Rewrote the pulseIn to use micros() instead of loop counting.

Be aware that micros() always round its value to a multiple of 4.

Counting the loops in pulsein works quite well, however if there are interrupt handled during this it will definitely give a wrong (too low) reading. This could (partially) explain why you need a smaller divisor in your formulas.
This "trick" is used especially to be able to read pulselengths smaller than 4 micros() precission.

Hope to see the code soon!

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

teckel

#43
May 25, 2012, 12:20 amLast Edit: May 25, 2012, 08:42 am by teckel Reason: 1

Quote
Rewrote the pulseIn to use micros() instead of loop counting.

Be aware that micros() always round its value to a multiple of 4.

Counting the loops in pulsein works quite well, however if there are interrupt handled during this it will definitely give a wrong (too low) reading. This could (partially) explain why you need a smaller divisor in your formulas.
This "trick" is used especially to be able to read pulselengths smaller than 4 micros() precission.

Hope to see the code soon!

Let's put it this way.  The pulseIn function is from 460 to 7120 microseconds off because of the way ultrasonic sensors work.  Basically, the echo port state takes that long to settle and pulseIn counts that time.  So, I think I'll settle for only 4 microseconds off. ;-)

I may release the code in the source both ways so if you want to try it the other way, you can uncomment it.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My Projects: https://dogblocker.com & https://baconorbeer.com
My beer: Great Lakes Brewing Co. Lake Erie Monster

teckel

#44
May 25, 2012, 08:53 amLast Edit: May 25, 2012, 05:41 pm by teckel Reason: 1
New in version 1.2:

Lots of code clean-up thanks to Adruino Forum members. Rebuilt the ping timing code from scratch, ditched the pulseIn code as it doesn't give correct results (at least with ping sensors). The NewPing library is now VERY accurate and the code was simplified as a bonus. Smaller and faster code as well. Fixed some issues with very close ping results when converting to inches. All functions now return 0 only when there's no ping echo (out of range) and a positive value for a successful ping. This can effectively be used to detect if something is out of range or in-range and at what distance. Now compatible with Arduino 0023.