Detect Ball with NXT Ultrasonic Sensor

Hey friends, a little off-topic here, but I can’t find another forum online where to post this. I have an NXT ultrasonic sensor, and I’d like to detect my red ball from the kit with my robot about twenty centimeters away. The range is inaccurate, sometimes getting values of 20, sometimes 30 or 10 centimeters from the same position. Does anyone know what I can do to detect the ball accurately? I’m using Python and BrickPi3. This might be hardware or software related, I don’t know.
Thanks very much!

How many readings are you taking?
Have you plotted std deviation vs number of readings?
I have found that most personal robotics sensors benefit greatly from taking 10 to 50 measurements in a loop with a small time delay between measurements, and then using the average.

import numpy as np

    NUM_SAMPLES = 50

    print("\n\nPlace Carl at known distance from wall")
    print("Taking Measurement")
    for i in range(NUM_SAMPLES):
    measuredDistance = np.average(mDl)
    correctedDistance = adjustReadingInMMForError(measuredDistance)
    print("average of %d measured_readings: %.1f mm = adjusted_distance: %.1f mm" % (len(mDl),measuredDistance,correctedDistance))

# Can also get all the stats easily with numpy e.g.:
#    c_ave_reading = np.average(c_readings)
#    c_max_reading = np.amax(c_readings)
#    c_min_reading = np.amin(c_readings)
#    c_std_reading = np.std(c_readings)

1 Like

Interesting. I will try that out, just placing a sleep of 0.02 will do, right? Do you think I need to do any other changes for hardware or in my code, or should this be accurate enough to detect a five centimeter ball in height and width?
Thanks for your help!

You can play around with the sleep time - one of the HC-SR04 Ultrasonic ranger datasheets suggests:

we suggest to use over 60ms measurement cycle, in order to prevent trigger signal to the echo signal.

That would suggest 0.06 or more sleep delay, which I am guessing is based on the maximum distance reading time plus the trigger pulse time.

My example was for a laser time of flight sensor - speed of light vs speed of sound is why the shorter delay can be used.

As for detecting the ball consistently, I don’t know what real accuracy and consistency to expect. The beam width of the HC-SR04 Ultrasonic sensor is around 15 degrees or something like 5 cm wide at 20 cm away, computed as 20cm * tangent( 1/2 beam width which is 7.5 deg). If the width of the ball is about 5 cm in diameter and round, it means the distance is varying, the sound reflection content is varying, the amount of the ball in the beam is varying. (Manufacturers like to spec as sensor against a huge, flat, sound reflecting wall but robots use the sensor against small, non-flat, less reflective objects.

One technique I use to find closest object and the angle to it is to sweep the distance sensor around to find the minimum average distance, but it takes a while using multiple directions and multiple sensor readings.

You asked:

The real world of robots is never accurate. A robot must plan for inaccurate information, and have a plan for detecting errors and correcting errors continually.

1 Like

That is a very good idea. How many times do you usually turn past an object to detect the minimum value? I’d suggest about three times (six in all - left, right x 3).

Also, a very good statement, though which methods are common when trying to correct errors. Would it be in my example when the robot detects the ball turn left in the robot’s place for 0.5 seconds at speed 50? Or would it be more sensor-based rather than strict value given to the robot by me? Such as, turn right and left to detect the minimum data, stop when this data is detected again, then move forward, stop when the ball is 10 cm away, lower arm, and go forward for 1 second blindly (sensor is covered by arm - my bobcat:).
WIN_20191028_19_36_21_Pro WIN_20191028_19_36_27_Pro WIN_20191028_19_36_41_Pro

@PolarBear1234, I think you are getting the ideas, and there are many ways to treat each potential problem, and only you know what constraints the bot and you are under.

Some ways to plan for inaccurate info:

  • determine expected sensor performance
    – what is maximum distance walls are detected
    – what is maximum distance balls are detected (at 0 deg to heading, not near a wall)
    – what is off heading angle where ball starts/stops being detected
    – what is measurement error (+/- n cm) at various distances to ball (0 deg to heading)
  • Never “believe” (or act) on a single datum
    – Confirm at least once “something” detected is a ball
    – If distance to ball is believed to be X, only move X / (1- (2 times sensor error rate) toward
    the ball, or half the distance to your “put the bucket down” point, and check the distance again

Some ways to detect errors:

  • Confirm using apriori knowledge
    – If moved Y cm toward ball X cm away, if new measurement is greater than (X - Y + Error rate) or
    less than (X - Y - Error rate) , then take new measurement because something is wrong
    – Based on ball detection angle limit, scan left till ball not detected, then scan right till ball not detected, if it is a ball the angle to the minimum distance should be roughly equal to half angle of the left and right disappeared headings.

Some ways to correct errors continually

  • (Identify the worst error that could happen, and how to detect it), have a planned reaction
    – If you are up against a time limit, then possibly the worse error is to think you have a ball when you don’t, so might need to add a sensor to detect ball in “bucket”, either light sensor or contact sensor, or does the nonsense range reported when a ball is in the bucket differ from the range with no ball in the bucket?
    – if knocking the ball off the “tee” is the worst error, determine a way to know the ball has been knocked off the tee, and change the “get ball from tee into bucket method” to “push the ball to the wall and use the wall to grab the ball method”

I wonder if the sensor was raised little or lowered a little, could the sensor provide ball distance or at least ball detection when the “bucket” is down?

If you can’t measure heading changes, you need to determine a reliable means of turning a smidge left or right - you mention 0.5s at speed 50 - might determine what is the shortest time at what speed to get at least three reliable on ball distance measurements between the left and right off ball measurements.

Real robotics is harder than most people realize, and it is all about thinking and trying, thinking and trying. You can’t test quality in, but you can test the quality. If you don’t know the quality, you can’t succeed.

1 Like

Thank you so much for the super-detailed reply! :+1:
So I’m trying to find out the minimum and maximum ball distances so I can head for the minimum ones. I’m trying to turn my robot left until it passes the ball, then right, do that four more times. If I want to set up my Ultrasonic Sensor on Port 4 to make a list and for starters print the two miminum and two maximum values, would I use a list and a for loop? I’ve already setup my code this way:

import brickpi3
import time

BP = brickpi3.BrickPi3()

BP.set_sensor_type(BP.PORT_4, NXT_ULTRASONIC)

while True:
    BP.set_motor_power(BP.PORT_B, 50)
    BP.set_motor_power(BP.PORT_C, -50)
    #Detect the ball first (robot comes from the right) and stop there
    while True:
            value = BP.get_sensor(BP.PORT_4)
            print(value)                         # print the distance in CM
            if value <= 53:
        except brickpi3.SensorError as error:
    BP.set_motor_power(BP.PORT_B + BP.PORT_C, 0)
    #Now the "data sweep" begins
    #Turn left so that it passes the ball
    BP.set_motor_power(BP.PORT_B, 50)
    BP.set_motor_power(BP.PORT_C, -50)
    #Now turn past the ball to the right, then to the left, and repeat this four times
    counter = 5
    while counter != 0:
        BP.set_motor_power(BP.PORT_B, -50)
        BP.set_motor_power(BP.PORT_C, 50)
        #This is the data for detecting when to turn
        value = BP.get_sensor(BP.PORT_4)
        #This is the "sweep data"
        [sweep_data] = BP.get_sensor(BP.PORT_4)
        if value >= 50:
            BP.set_motor_power(BP.PORT_B + BP.PORT_C, 0)
        #Now turn in the other direction and try again!
        BP.set_motor_power(BP.PORT_B, -50)
        BP.set_motor_power(BP.PORT_C, 50)
        [sweep_data] = BP.get_sensor(BP.PORT_4)
        if value >= 50:
            BP.set_motor_power(BP.PORT_B + BP.PORT_C, 0)
        counter - 1

So that’s what I got. I’m missing something important now, how would I get the two or three minimum / maximum values of the ball so I know which one’s are right? How would the code beneath this while loop look like with the sweep_data list?
Thanks again very much for your help!