0

I'm building a ground proximity warning system with an ultrasonic sensor, a raspberry pi, and python. I've got a loop thats checking the distance, and based on that distance I'd like a callout to be read.

Intended functionality: Shown in youtube video Boeing 737 cockpit landing (listen to the automatic callouts - sorry Airbus fans ;) ).

Intended functionality explained: As a height is reached, play a mp3 sound once, but only if the distance is getting closer, not farther away.

Python code for the ultrasonic sensor:

#! /usr/bin/python
import RPi.GPIO as GPIO
import time

def checkdist():
    GPIO.output(16, GPIO.HIGH)
    time.sleep(0.000015)
    GPIO.output(16, GPIO.LOW)
    while not GPIO.input(18):
        pass
    t1 = time.time()
    while GPIO.input(18):
        pass
    t2 = time.time()
    return (t2-t1)*340/2

GPIO.setmode(GPIO.BOARD)
GPIO.setup(16,GPIO.OUT,initial=GPIO.LOW)
GPIO.setup(18,GPIO.IN)
time.sleep(2)
os.system('mpg123 -q PWSOnline.mp3 &')
try:
    while True:
        print('Distance: %0.2f m' %checkdist())
        time.sleep(0.1)
except KeyboardInterrupt:
    GPIO.cleanup()

What I'd like to do:

  • The ultrasound sensor distance value tends to jump around especially at larger distances, so some way to get the average of the last 0.5 seconds?

  • Play the callout only if the distance is getting closer, not farther away - so I guess it would need to see the average change over 1 second (whether decreasing or increasing)?

  • Play the callout once.

The questions:

  • What's the best way to go about triggering a function once while inside a loop (and not having the main loop wait for the function to finish - threading, right)?
  • How do I deduce the average of the last second in while loop?
Michał
  • 868
  • 1
  • 10
  • 36
  • It is bad to run loops and wait for the distance. Instead you can use tornado to run an I/O loop and the code will trigger once distance is less. – binu.py Jul 21 '17 at 21:17
  • Which sensor is it? `HCSR04`? If so, it has a max of 13' (~400cm) and a minimum iirc of ~1cm (<0.5"). To do what you want (wait without blocking) is to farm out an async process of some sort. Although I code Python at work, I don't on my Pis and I've never used async within that language so I can't help with specifics. – stevieb Jul 21 '17 at 21:24
  • Just a thought for the "moving average" functionality: use a `deque` with `maxlen` set: `last_few_samples = collections.deque(maxlen=100)`. Set the `maxlen` to a value that gets you about 0.5s of samples and then take the average: `avg = sum(last_few_samples)/len(last_few_samples)` – Scott Colby Jul 21 '17 at 21:39
  • @ScottColby, looks like a step towards where I want to go - but I will have to read up on deque on my end. I'm just wondering how late it will be to get the exact value, especially if you're descending like 10cm per second. Maybe it would be possible to get the rate of change over the last second and predict when a value will be reached, so the callout is exactly on point? – Michał Jul 21 '17 at 21:47
  • @binu.py Doesn't Tornado (and other event looping technology) just run a loop itself? – jpmc26 Jul 22 '17 at 03:25
  • @Michał sounds like you want to take a [finite derivative](https://en.wikipedia.org/wiki/Finite_difference). Check out [this question](https://stackoverflow.com/a/9877279/600882), perhaps? This would basically be a linear approximation of the future height. If you need better than that, perhaps [Taylor series](https://en.wikipedia.org/wiki/Taylor_series) is where you want to look. – Scott Colby Jul 22 '17 at 09:09
  • 1
    @Michał also, to answer the question you posed in your comment better, the time it takes to get to the "exact" value depends on the number of samples you're using. The more samples, the longer it'll take but the less noisy the measurement will be. The fewer samples, the opposite. I'm basically advocating for a sort of [moving average](https://en.wikipedia.org/wiki/Moving_average) here. – Scott Colby Jul 22 '17 at 09:11
  • @ScottColby now you got me thinking back to years ago when I was asking my high school math and science teachers "When will I ever need this in real life?" :D Regarding the sampling rate - I don't think it can be a fixed rate - since we still have to wait for the ultrasound to send a sound and receive it again, that time it takes varies from height - which further delays getting the height at an exact point of time. – Michał Jul 22 '17 at 10:29
  • 1
    Here's my take on this: I think what you want is some method of either multithreading or multiprocessing (tornado/scoop/etc.) where one thread/process is running a loop to get the current distance to "ground" and report it to the main thread, and another (could possibly be done in main, or farmed out to another) that averages the last few measurements, compares it to the previous average(s), and when `currentAverage - (previousAverage - currentAverage) <= nextAnnounceHeight` is True, start your height announcement. But take it one step at a time, coming here with the whole project won't work. – 3D1T0R Jul 23 '17 at 00:00

0 Answers0