Sunday, October 2, 2016

Raspberry Pi & Arch Linux - Day 9 - Code that controls the distance sensor



Let us go back to the distance sensor again. We haven't spent too much time on it.

For our project, we use the Ultrasonic Ranging Module HC - SR04. It is cheap and easy to work with.  For more details about the sensor, here are two useful links:
The first link is highly recommended.

The sensor has four pins: (1)Ground;(2)Vcc;(3)Trig;(4)Echo. The first two pins are easy to understand. (1) need to be connect to ground and (2) provides the power to the sensor. 5V is in the working voltage range so we can connect the power pin of raspberry pi to the sensor directly. 

(3) is the input pin of the sensor. Once the sensor receives the input pulse, it sends out ultrasonic wave. Let T denote the time between the sensor sends out the wave and the time it receives the echo. After the sensor receives the echo, it will generate an output pulse in the Echo pin that lasts T (s). Therefore, the distance is 0.5 * speed_of_sound * T.

Before we talk about the code,  we should point out that the original structure mentioned in the previous posts is not strong enough to hold the sensor when we connect the wire to the sensor. If the sensor is not strictly locked, the measures will have too much noise and will not be accurate enough to provide useful information. So we need to make some enhancements and here is the result:




The code is not hard to write if we only measure the distance once. The problem is that we want to create a kind of "radar" that can scan the space in front of the car. It means that the sensor need to continuously measure the distance and send back the results. Let us assume that we have a function called measure_once which allows us to perform a single measure. If we simply put this function in a while loop, it will not work because in that case the sensor can no longer send back the results. 

Fortunately, there is one solution to this problem: Coroutine.

For more details about Coroutine in Python, I highly recommend the online tutorial: 
A Curious Course on Coroutines and Concurrency by David Beazley. The version of Python used in the tutorial is 2.7 but almost everything can work directly in Python 3. It seems that python 3 has a package called asyncio dedicated to the tasks and coroutines. It maybe a good idea to check the official documentation later.

Here is the first version of the code: 


import RPi.GPIO as gpio
import time

#gpio.setmode(gpio.BCM)
gpio.setmode(gpio.BOARD)



class DistanceSensor:
    SUCC    = 'SUCC'
    INIT    = 'INIT'
    TIMEOUT = 'TIMEOUT'
    FAIL    = 'FAIL'

    def __init__(self, pin_echo=None, pin_trig=None, unit='m'):
        assert pin_echo is not None and pin_trig is not None
        self._pin_echo = pin_echo
        self._pin_trig = pin_trig

        gpio.setup(pin_echo, gpio.IN, pull_up_down=gpio.PUD_DOWN)
        gpio.setup(pin_trig, gpio.OUT, initial=0)
        time.sleep(1.)

        self._pulse = 0.00001
        self._status = DistanceSensor.INIT
        self._latest_measure = (None, DistanceSensor.INIT)

    def measure_once(self):
        """
        measure the distance once. The returnd value is (distance, status)
        """

        # send out the signal
        gpio.output(self._pin_trig, 1)
        time.sleep(self._pulse)
        gpio.output(self._pin_trig, 0)

        start = time.time()
        _start = start

        while gpio.input(self._pin_echo) == 0: # no echo signal received
            if time.time() - _start > 1.:
                self._status = DistanceSensor.TIMEOUT 
                break
            start = time.time()

        if self._status == DistanceSensor.TIMEOUT:
            return None, self._status

        while gpio.input(self._pin_echo) == 1: # receiving the echo signal
            stop = time.time()


        distance_m = 170. * (stop - start)

        # sanity check
        if distance_m < 0.04 or distance_m > 4:
            self._status = DistanceSensor.FAIL
            distance_m = None

        else:
            self._status = DistanceSensor.SUCC

        return distance_m, self._status

    def measure(self, delay=0.1):
        """
        Measure the distance several times. The Measure function will return a coroutine.
        """
        try:
            while True:
                msg = (yield)
                self._latest_measure = self.measure_once()
                time.sleep(delay)
        except GeneratorExit:
            print('disconnect to the distance sensor')
            pass


                    

if __name__ == '__main__':
    echo = 18
    trig = 16

    distance_sensor = DistanceSensor(pin_echo=echo, pin_trig=trig)

    input('press any key to start')

    #print(distance_sensor.measure_once())
    observer = distance_sensor.measure(repeat=30)
    next(observer)
    
    res = []

    for msg in ['m','m','m','stop']:
        observer.send(msg)
        res.append(distance_sensor._latest_measure)

    observer.close()

    for item in res:
        print(item)
                


--END--

No comments:

Post a Comment