Hi there. My HC-SR04 works fine at some distances, but when something is too far away, it reads a negative distance. Is this supposed to happen? Is there a way I can get rid of those negatives?
Are you signed variables for distance, or unsigned?
I've had the HC-SR04 do some similar things, either the target is too far away or is absorbing or deflecting the sonar pulses. For most applications, you don't want to look too far away with the HC-SR04 depending on who's datasheet you go by, the max range value seems to vary a bit. Let's say the max range is around 200 to 400 centimeters.
Depending on what else you might have connected to your controller, you might be also getting some electrical noise, electromagnetic disturbance or power fluctuations. There are ways to help with all of that though. I find that putting electrolytic capacitors between my V+ and ground rails. You can also connect another at the sensor itself.
Connecting some ceramic caps to your DC motors if you're using any helps to reduce some of the brush noise, as does using twisted pair wire for the motors themselves. I had to use all of these on my current robot to reduce interference to my sensor and the servo that rotates it.
The next bit deals directly with the sensor itself, and I'm not sure if it will be useful in your application or not, it was in my case. I wanted my robot to take measurements to the front and 45 degrees to either side. If the distance to the front is less than 30cm, it evaluates the left and right distances and turns in the direction of the higher number. Then it looks around again. If there are no obstructions in front of it, it moves forward until there is.
What I was finding in my testing was that in a very large area, or an area with complex geometry IE things with round edges, very sharp corners or things like stuffed animals or fabric the robot would "Get lost" and either stutter a lot or constantly be turning when there were no obstructions.
I realized this was because the sensor was returning weird numbers. It took too long to detect the echo, or it didn't detect an echo at all. This is easily solved by adding a couple of "if" statements to the code, before you evaluate the number returned by the sensor. You have to decide an upper and lower limit for your sensor. Since my robot reacts only when an obstacle is 30cm or closer, I decided my upper limit was fine at 45cm. and the lower limit is 1cm.
So to rule out any unusable numbers..
if (range <= 0)
{
range == 1;
}
if (range >= 46)
{
range == 45;
}
After you pass the rangefinders numbers through a similar filter, you don't have to worry about those odd numbers.
The other thing you need to make sure of before doing the above is that you are handling the raw data from the sensor properly. Below is a portion of the code that I borrowed elsewhere and it returns a measurement in centimeters.
digitalWrite(Trig, LOW); //Make sure ultrasound is off
delayMicroseconds(2);
digitalWrite(Trig, HIGH); //Send ping
delayMicroseconds(10); //Ping must remain high for 10ms to send a proper signal out.
digitalWrite(Trig, LOW); //stop ping
duration = pulseIn(Echo, HIGH); //Listen for the echo
distance = (duration/2) / 29.1; //I am told this is the appropriate math to convert the measurement to CM
return distance;
If you're using your rangefinder for another application such as simply taking a distance measurement and displaying that on a small screen or over serial, you may want to look into another rangefinder. There are a few different variations of ultrasonic rangefinder but I think they all have about the same accuracy until you get into the higher dollar "Narrow angle" rangefinders. For better accuracy and longer distance it might be better to go with a laser rangefinder. This particular one seems to be starting to gain popularity and I know there are guys here that are using them as I've stumbled across a lot of threads that mention them.
danno629:
Is this supposed to happen?
Depends on your code.
This is a code I'm using with it. Should be simple. If it's below a certain distance, it turns an led on, but I'm getting negative values.
int trigPin=3;
int echoPin=2;
void setup()
{
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(13, OUTPUT);
}
void loop()
{
int duration, distance;
digitalWrite(trigPin, HIGH);
delay(1);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance=(duration/2) / 29.1;
Serial.print(distance);
Serial.println("cm");
delay(100);
if (distance<10)
{
digitalWrite(13, HIGH);
}
else
{
digitalWrite(13, LOW);
}
}
At 1 millisec your pulse duration delay may be too long, try 10 Microseconds (0.01 millisec).
FWIW in your setup section, u should probably also write the trigPin LOW to avoid unwanted fluctuation at the start.
int trigPin=3;
int echoPin=2;
void setup()
{
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(13, OUTPUT);
// add this line.
digitalWrite(trigPin, LOW);
}
void loop()
{
int duration, distance;
digitalWrite(trigPin, HIGH);
// delay(1);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance=(duration/2) / 29.1;
Serial.print(distance);
Serial.println("cm");
delay(100);
if (distance<10)
{
digitalWrite(13, HIGH);
}
else
{
digitalWrite(13, LOW);
}
}
danno629:
This is a code I'm using with it. Should be simple. If it's below a certain distance, it turns an led on, but I'm getting negative values.
...
duration = pulseIn(echoPin, HIGH);
...
The pulseIn function has a timeout of 1 second and returns the result in microseconds, so the possible result range is 0...1000000, but you have defined "duration" as 'int', so the range for duration is −32768...32767 only.
So if you should try to assign (example only) a value from pulseIn like 32770 to 'duration', then duration would contain a negative value of -3 after doing so.
So if you want to avoid negative values for duration, you would either have:
- make the data type of 'duration' big enought to take a value of 1000000, i.e. define it "long" instead "int"
- make pulseIn a shorter timeout
The pulseIn function can take the timeout as a third parameter, so perhaps use in your code:
duration = pulseIn(echoPin, HIGH,32760);
That way, pulseIn will never return a value bigger than 32760, so it fits into an 'int'.
And of couse you should fix the triggering code as aisc already told: Tigger for 10 microseconds, and also pull the pin LOW before measuring:
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH,32760);
Hi,
I have done a movie with detailed example here:
www.electronics-freak.com
Enter the site and go to
Arduino -> lessons -> lesson 6
Enjoy
Roee
danno629:
This is a code I'm using with it.
I'm on my way out, but I'll test that when I get back later today.
roeebloch:
I have done a movie with detailed example
Without watching 15 minutes of video, does that actually address the OP's problem of negative values?