NVIDIA / jetson-gpio

A Python library that enables the use of Jetson's GPIOs
MIT License
915 stars 259 forks source link

Troubles with HC-SR04 control #18

Closed TNemes-3141 closed 5 years ago

TNemes-3141 commented 5 years ago

Hello,

I want to contorl the HC-SR04 ultrasonic sensor with the Jetson Nano's GPIO pins. It needs a 0.01ms Trigger signal and sends an Echo signal as soon as the sonic wave arrives. Here is my simple code to do that:

# Import libraries
import RPi.GPIO as GPIO`
import time`

# GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)
print("Distance measurement in process")

# Assign GPIO Pins
GPIO_TRIGGER = 19
GPIO_ECHO = 21

# Set direction of GPIO Pins (IN / OUT)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)

# Start the sensor
GPIO.output(GPIO_TRIGGER, GPIO.LOW)
print("Sensor is starting!")
time.sleep(2)

# Set Trigger to HIGH
GPIO.output(GPIO_TRIGGER, GPIO.HIGH)

# Set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, GPIO.LOW)

#Save start time
while GPIO.input(GPIO_ECHO) == GPIO.LOW:
    StartTime = time.time()

# Save arrival time
while GPIO.input(GPIO_ECHO) == GPIO.HIGH:
    StopTime = time.time()

# Difference between start and arrival
TimeElapsed = StopTime - StartTime
distance = (TimeElapsed * 34300) / 2
print("Distance: ", distance)

GPIO.cleanup()

Here is my problem: It gets stuck in the first 'while'-loop, which means that the Echo signal never comes. I use a voltage translator to connect the sensor to the Jetson. I remember that I connected the 5V power supply pin first and then connected the Ground-pin, could that be the problem? Or my other assumption is related to the pin numbering definition. The numbers I entered in the definition are the ones that I can read from my Jetson Nano board. Are they right with the BCM-Mode? I also tested the state of the pins in a seperate program and I have different results when I type import Jetson.GPIO as GPIO and import RPi.GPIO as GPIO. Can someone tell me where the problem could be originated?

Thanks for the help in advance!

swarren commented 5 years ago

If you're using the pin numbers from the connector (1..40) then you want BOARD mode. Both BOARD and BCM mode numbering should work identically between Jetson.GPIO and a Raspberry Pi running RPi.GPIO.

One possible issue is that the incoming pulse arrives so quickly, and lasts for such a short time, that software polling can't react fast enough to see it. To work around this, you could try using the GPIO event APIs rather than polling the pin value using GPIO.input().

TNemes-3141 commented 5 years ago

Thanks for the reply, in the meantime, I fixed the error myself as I found that my pin numbering was wrong. Now the sensor returns values, but they're complete trash. Here are e.g. the results for measuring in front of a flat wall which is 1m away (time between each measurement is 1s):

154.4cm 124.3cm 0.1cm 0.1cm 97.0cm 56.7cm 75.9cm 245.1cm

Any idea what can cause this? I read that the sonic wave is spreading in a degree of 15° and so other surrounding objects can interfere with the results, but with a completely blank wall, this is obviously not the case. Is the error in my code or the sensor? Can it be that the receiver of the sensor measures other sonic waves than ultrasonic ones, too? Would it help to shield the sensors emitter and receiver outwards to 3cm or so so only the waves hit the receiver that come from the direction the sensor is facing to?

swarren commented 5 years ago

It could simply be timing (software scheduling) inaccuracies; a 1m range will yield a ~6ms pulse, which is a very short time to measure using high-level user-space software.

TNemes-3141 commented 5 years ago

You might be right about that, in the meantime, I discovered that the sensor is best for measuring distances between 2 - 3m. What was that "GPIO event APIs" you talked about?

swarren commented 5 years ago

The event-based APIs are described at https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/. Search for "Interrupts and Edge detection".

svkumar18 commented 5 years ago

Can you tell me which pins you used for Trig and Echo on jetson Nano?

swarren commented 5 years ago

Can we close this bug now? Thanks.

swarren commented 5 years ago

Since this issue does not describe an actual bug in Jetson.GPIO, I am closing it.

armandgabriel commented 3 years ago

Hello @Totemi1324

How did you manage to fix the first while looping forever, I'm running in debugger and it seems is just recording StartTime and then runs another loop.

Thank you in advance, Kind regards, Armand

TNemes-3141 commented 3 years ago

Hey Armand,

The problem with the HC-SR04 itself was solved by the link provided by @swarren . There, you find example code for wait_for_edge() function, which monitors the pin for a specified amount of time and if the state changes, returns a true:

channel = GPIO.wait_for_edge(channel, GPIO_RISING, timeout=5000)
# GPIO_RISING if you look for Low->High, GPIO_FALLING for High->Low
if channel is None:
    print('Timeout occurred')
else:
    print('Edge detected on channel', channel)

This is much more efficient and safe than using while-loops, simply because it doesn't use the CPU that much and you can specify a timeout (in ms) so infinite loops are eliminated. With this, I not only fixed the measuring, but the results were more accurate than before.

I don't know what your use case is, but, if I can give you some advice, don't use ultrasonic sensors with other than Arduino boards. RaspPi and Jetson Nano have a whole operating system running in the background, so GPIO signals are highly inaccurate. Other than that, ultrasonic has a low range. So I didn't stick with ultrasonic for my application, but switched to LiDAR, which is far more accurate, has a greater range and can be used with I2C or SPI in order to get precise communication. It's even smaller than ultrasonic sensors. I recommend this one: https://de.aliexpress.com/item/4000074204979.html You can find good libraries for calling the sensor as well: https://github.com/simondlevy/VL53L1X

oerni21 commented 3 years ago

@Totemi1324 Hello, great discussion. Can you share the whole code for using the HC-SR04? It would be such a help.

Thank you very much!

TNemes-3141 commented 3 years ago

@Totemi1324 Hello, great discussion. Can you share the whole code for using the HC-SR04? It would be such a help.

Thank you very much!

Hey @oerni21 , well, the whole code is already shared in my initial question. I would advise you to change the while-loops for saving the start and arrival times to the edge detection function in my previous reply. Other than that not much has changed. I hope it helps!

oerni21 commented 3 years ago

Hallo @Totemi1324 , thanks for the fast responde. Did you change something in the lines "GPIO.setup"? And did you just replace 'channel' with 'GPIO_ECHO'?

Thank you

TNemes-3141 commented 3 years ago

Hallo @Totemi1324 , thanks for the fast responde. Did you change something in the lines "GPIO.setup"? And did you just replace 'channel' with 'GPIO_ECHO'?

Thank you

No and no. The setup lines stay the same, since the Trigger pin must still be an output pin and the Echo pin still an input pin. As for the channel variable, you can copy-paste the code posted by me as is, it doesn't need any further changes. Just try executing the program for yourself and, if you have questions, please look first in the API documentation linked by @swarren .

spok7 commented 2 years ago

Here's some code for those of us who want a copy-pastable solution:

import RPi.GPIO as GPIO
import time

# assign pins
GPIO_TRIGGER = 19
GPIO_ECHO = 21

# configure board and pins
GPIO.setmode(GPIO.BOARD) # GPIO Mode (BOARD / BCM)
GPIO.setup(GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(GPIO_ECHO, GPIO.IN)

# start sensor
GPIO.output(GPIO_TRIGGER, GPIO.LOW)
time.sleep(2)

# send pulse
GPIO.output(GPIO_TRIGGER, GPIO.HIGH)
time.sleep(0.00001)
GPIO.output(GPIO_TRIGGER, GPIO.LOW)

# receive response and calculate distance
if GPIO.wait_for_edge(GPIO_ECHO, GPIO.RISING, timeout=5000):
    StartTime = time.time()
    if GPIO.wait_for_edge(GPIO_ECHO, GPIO.FALLING, timeout=5000):
        StopTime = time.time()
        TimeElapsed = StopTime - StartTime
        distance = (TimeElapsed * 34300) / 2
        print("Distance: ", distance)
    else:
        print('Falling edge timeout occurred')
else:
    print('Rising edge timeout occurred')

GPIO.cleanup()