[PYTHON] Make an autonomous driving robot car with Raspberry Pi3 B + and ultrasonic distance sensor HC-SR04

Introduction

The elements that make up an autonomous vehicle in the real world are own vehicle position estimation, object recognition, action judgment, and vehicle body control. It is possible to implement all of these four elements based on a radio-controlled model, but it is not easy to easily estimate the position of the own vehicle, and this time the goal is low-speed driving, so there is almost no need for vehicle body control. Therefore, this time, I would like to recognize the object in front with the ultrasonic distance sensor and easily realize the action judgment to avoid it with the autonomous driving robot car based on the radio control.

environment

Robot car overall configuration

A PWM signal is sent from the Raspberry Pi to a commercially available radio-controlled model to control the accelerator and steering. The three ultrasonic distance sensors in front are used to recognize the object in front and judge the behavior by turning the steering wheel to the left and right.

Parts used

**-Radio Control: Tamiya 1/10 XB Series No.199 NSX (TT-02 Chassis) ** Anything is OK for the main body, but I still want to attach the body and fit various hardware neatly, so I recommend a larger one. Also, as I will explain later, this time I wanted to run at low speed, so I think it was easier to control the speed if it was not a 7.2V system.

**-Battery: Tamiya 7.2V Custom Pack ** It was okay to align the voltage from 7.2V of the main body with a linear regulator and share it with the power supply of the Raspberry Pi, but since it was easier to use the mobile battery for the power supply of the Raspberry Pi side, this time the power supply of the Raspberry Pi side and the radio control main body are independent I was allowed to.

**-Speed Controller: HiLetgo 5A Mini DC Motor PWM Speed Controller 3V-35V ** This is the one that bothered me the most. The motor is controlled by PWM of 7.2V, but with the method via the controller this time, it seems that the duty ratio alone cuts off at a certain ratio on the low speed side, so I used it because it is not possible to obtain low speed. Moreover, since it is rectified in this circuit, reverse rotation does not occur. It seems possible if you use two and correspond to both positive and negative sides, but this time it is only forward rotation.

**-Single board computer: Raspberry Pi3 B + ** There are no particular precautions. It can be used as it is. OS ver.

**-Battery for Raspberry Pi: Poweradd Ombar ijuice 3350mAh Mobile Battery ** Any kind is cheap and OK. The larger the capacity, the better, but I chose this size in consideration of the layout.

**-Raspberry Pi Switch: Audio Fan USB Type A ** For the switch that activates the Raspberry Pi. When this is turned on, it will enter the standby state.

**-Infrared remote control module: IR HX1838 ** It is used as a trigger to start driving from standby.

**-Ultrasonic Distance Sensor: HC-SR04 + Acrylic Mount Angle ** It is a sensor for recognizing obstacles in front, left and right.

**-Hard fixing jig: MOLATE Straight plate Flat repair plate 96 * 19mm Thickness 2.6mm ** When making this robot car, how to lay out the hardware is the most difficult. I attached this plate by putting it between the screws that were originally attached to the main body, or by drilling screw holes.

Hardware implementation

**-Wiring ** For the overall configuration, I referred to this site created based on the same type of radio control. The final overall wiring is shown in the last figure.

**-Ultrasonic sensor ** The basic usage of ultrasonic waves is [this site](http://make.bcde.jp/raspberry-pi/%e8%b6%85%e9%9f%b3%e6%b3%a2%e8% b7% 9d% e9% 9b% a2% e3% 82% bb% e3% 83% b3% e3% 82% b5hc-sr04% e3% 82% 92% e4% bd% bf% e3% 81% 86 /) I used it as a reference. Both 5V and 3.3V are used to use 3 ultrasonic sensors. It is stated that when using a 5V output, it is necessary to step down to 3.3V using a resistor, but it is used directly as it is (5V can be used as it is without any problem). I saw an article saying, but it was possible to operate for the time being). After the fact, it was discovered that if I thought I had obtained the exact same three items, only one would be different.

**-Infrared remote control ** I used the infrared remote controller while referring to this site to trigger the movement of the robot car. I didn't have time to spend here, so I decided to start the operation by pressing any button on the remote control. See the source code below for details. The overall configuration of the hardware wiring is shown in the figure below.

Raspberry Pi setup

**-Network settings for Raspberry Pi and PC ** The necessary settings on the Raspberry Pi side are to first connect to the Internet and make an environment where you can download the necessary software, and you need to set the server for SSH connection. Of course, development is done in an environment where the server Raspberry Pi and the client PC can use Wi-fi, but when debugging is required in a place where there is no wide Wi-fi environment for operation test, Raspberry Pi is used. An ad hoc connection is required to connect the PC directly wirelessly. For ad hoc connection, I referred to this site. **-Raspberry Pi startup settings ** Add the following description to /etc/rc.local to set the program /home/pi/nsx.py to run on python3 when the radio control and Raspberry Pi are turned on and the Raspberry Pi starts as it is. As a caveat, you need to pass the password (hogehoge in this case) and the full path because you need to run sudo in your environment.

:/etc/rc.local

echo "hogehoge" | sudo -u pi /user/bin/python3 /home/pi/nsx.py

Software implementation

**-Servo control by ServoBraster ** I used Servo Blaster to send command values to the speed controller and steering servo by PWM signal. Depending on the duty ratio of the PWM signal, 150 is transmitted to the center of neutral and a value of 100 to 200 is transmitted to adjust the opening. Since this value differs depending on the servo, it is necessary to determine the value to be used for control while implementing it. Also, for some reason, the value at the limit of around 150 did not receive a detailed command, and there was a dead zone between 140 and 150 and between 160 and 150, so there is a limit to low speed driving due to the accelerator opening. Therefore, an additional speed controller HiLetgo is used to narrow down the current value.

**-Distance measurement with an object using an ultrasonic distance sensor ** It seems that the maximum distance accuracy is about several centimeters, but as a caveat, if the reflection of ultrasonic waves cannot be measured correctly, the measurement will not be completed endlessly and you will not be able to get out of the While Loop, so we have introduced an Abort timer. When Abort is performed, the value measured last time is used as it is. I tried various methods, such as taking the average of a few steps, but compromised the accuracy of the values because the simple method gave the most stable behavior.

**-Infrared remote control start switch ** I used lilc for infrared transmission and reception of Raspberry Pi. The installation procedure etc. is as shown in this site that I referred to, but the notes are the description of the following config file. Specify dtoverlay as gpio-ir, gpio_pin and specify GPIO pin. Here, 20 GPIO pins (= board pin No. 38) are used.

:/boot/config.txt

# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi
#dtparam=gpio_in_pin=20
#dtparam=gpio_in_pull=up
dtoverlay=gpio-ir,gpio_pin=20
dtoverlay=gpio-irtx,gpio_pin=20

Based on the above, the entire source code is as follows.

:~/nsx.py

import subprocess as sp #Run Servo Blaster with sudo
import os
import sys
from time import sleep
import time 
import RPi.GPIO as GPIO #For Raspberry GPIO

#Value definition for execution
dirServo = "PiBits/ServoBlaster/user"
dirHome = "/home/pi"
passwd = ("hogehoge").encode() #Password for sudo execution

#Ultrasonic distance sensor parameters
trigCenter = 19
echoCenter = 18
trigLeft = 15
echoLeft = 16
trigRight = 7
echoRight = 8

#Execution (initialization) of Servo Blaster
def resetSystem(dirServo, passwd, dirHome):
    os.chdir(dirHome)
    os.chdir(dirServo)
    sp.run(("sudo", "./servod"), input=passwd)
    os.chdir(dirHome)
    print("Reset System")

#Steering and accelerator (motor command value) are neutralized at the same time
def setNeutral():
    stNeutral = "echo 1=150 > /dev/servoblaster"
    accNeutral = "echo 2=150 > /dev/servoblaster"    
    os.system(stNeutral)
    os.system(accNeutral)
    print("Set Neutral")

#Neutralization of steering unit
def setStNeutral():
    stNeutral = "echo 1=150 > /dev/servoblaster"
    os.system(stNeutral)
    print("Set Steer Neutral")

#Neutralization of the accelerator alone
def setAccNeutral():
    accNeutral = "echo 2=150 > /dev/servoblaster"    
    os.system(accNeutral)
    #print("Set Accel Neutral")

#Forward accelerator Max command value
def setFwdMax():
    fwdMax = "echo 2=100 > /dev/servoblaster"
    os.system(fwdMax)
    #print("Set fwdMax")

#Left steering Max command value
def setLeftMax():
    leftMax = "echo 1=100 > /dev/servoblaster"
    os.system(leftMax)
    print("Set leftMax")

#Left steering Min command value (not used this time)
def setLeftMin():
    leftMin = "echo 1=140 > /dev/servoblaster"
    os.system(leftMin)
    print("Set leftMin")

#Right steering Max command value
def setRightMax():
    rightMax = "echo 1=200 > /dev/servoblaster"
    os.system(rightMax)
    print("Set rightMax")

#Right steering Min command value (not used this time)
def setRightMin():
    rightMin = "echo 1=160 > /dev/servoblaster"
    os.system(rightMin)
    print("Set rightMin")

#Distance measurement with ultrasonic distance sensor
def getDistance(TRIG, ECHO, abortTime):
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(TRIG, GPIO.OUT)
    GPIO.setup(ECHO, GPIO.IN)
    GPIO.output(TRIG, GPIO.LOW)
    time.sleep(0.01)

    GPIO.output(TRIG, True)
    time.sleep(0.00001)
    GPIO.output(TRIG, False)
    
    startTime = time.time()
    while GPIO.input(ECHO) == 0:
        signaloff = time.time()
        
        if ((signaloff - startTime) > abortTime):
            distance = -1
            return distance

    while GPIO.input(ECHO) == 1:
        signalon = time.time()

    timepassed = signalon - signaloff
    distance = timepassed * 17000
    
    return distance
    
    GPIO.cleanup()

#Infrared remote control reception
def getRemoteSignal(flagState):
    boardNo = 38
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(boardNo, GPIO.IN)
  
    try: 
        if (GPIO.LOW  ==  GPIO.input(boardNo)):
            
            if (flagState == False):
                flagState = True
                print("Switch ON !!!!!!!!")

            elif (flagState == True):
                flagState = False
                print("Switch OFF !!!!!!!!")

            time.sleep(0.5)

    except:
        GPIO.cleanup()
    
    return flagState

#Accelerator control (simple forward and stop control by forward distance measurement only)
def getAccControl(currDist, setDist):

    flagStop = 0
    
    if (currDist < setDist) and (currDist > -1):
        
        setNeutral()
        flagStop = 1

    else:
        setFwdMax()
    
    return flagStop

#Steering control (simple control by distance measurement only)
def getStControl(currDistRight, setDistRight, currDistLeft, setDistLeft, flagStop):
    statusRight = (currDistRight < setDistRight) and (currDistRight > -1)
    statusLeft =  (currDistLeft < setDistLeft) and (currDistLeft > -1)

    if (flagStop == 1):

        setStNeutral()

    elif (statusRight and statusLeft):

        setStNeutral()
    
    elif (statusRight):

        setLeftMin()

    elif (statusLeft):

        setRightMin()

    else:
        setStNeutral()

#Avoiding error values of ultrasonic distance sensor
def getTakeOverValue(distLeft, distRight):

    global prevDistLeft
    global prevDistRight

    if (distLeft == -1):
        distLeft = prevDistLeft

    prevDistLeft = distLeft

    if (distRight == -1):
        distRight = prevDistRight

    prevDistRight = distRight

    return (distLeft, distRight)


def main():

    #Infrared remote control reception status flag initialization
    global flagState
    flagState = False
    
    #Initialization of the previous value of the distance to the left and right objects
    global prevDistRight
    global prevDistLeft
    prevDistRight = 0
    prevDistLeft = 0

    #Calib value of ultrasonic distance sensor
    centerThreshold = 30 #The front is set short to stop
    rightThreshold = 70 #Only the right sensor has a different type (lot ??), so the left and right values are different.
    leftThreshold = 60

    #Put in standby and set parameters to neutral
    resetSystem(dirServo, passwd, dirHome) 
    setNeutral()
    
    while True:
        
        #Checking the reception status of the infrared remote controller (at the start of operation)
        flagState = getRemoteSignal(flagState)
        
        if (flagState == True):        

            #Distance measurement with all ultrasonic distance sensors
            distCenter = getDistance(trigCenter, echoCenter, 0.1)
            distLeft = getDistance(trigLeft, echoLeft, 0.1)
            distRight = getDistance(trigRight, echoRight, 0.3)
           
            #Alternate value generation when the ultrasonic distance sensor returns an error
            distLeft, distRight = getTakeOverValue(distLeft, distRight)
            
            #Stop command value generation based on the distance to the object in front
            flagStop = getAccControl(distCenter, centerThreshold)

            #Steering command value generation based on the distance to the left and right objects
            getStControl(distRight, rightThreshold, distLeft, leftThreshold, flagStop)
            
            #Check the reception status of the infrared remote controller
            flagState = getRemoteSignal(flagState)
            
        else:
            setNeutral()

if __name__ == "__main__":
    main()

At the end

What did you use this radio control for? I used it as a ring pillow carrying robot car to carry rings at weddings. Probably the first in the world.

Link

  1. I made an internet radio control with Raspberry Pi 2 and 1/10 RC of Tamiya

  2. [Use ultrasonic distance sensor (HC-SR04)](http://make.bcde.jp/raspberry-pi/%e8%b6%85%e9%9f%b3%e6%b3%a2%e8 % b7% 9d% e9% 9b% a2% e3% 82% bb% e3% 83% b3% e3% 82% b5hc-sr04% e3% 82% 92% e4% bd% bf% e3% 81% 86 /

  3. Control the remote control using Raspberry Pi 3 Model B + and lilc 1

  4. How to create an RPI Ad-Hoc network

  5. ServoBlaster

Recommended Posts

Make an autonomous driving robot car with Raspberry Pi3 B + and ultrasonic distance sensor HC-SR04
MQTT RC car with Arduino and Raspberry Pi
Make an umbrella reminder with Raspberry Pi Zero W
Make an air conditioner integrated PC "airpi" with Raspberry Pi 3!
Make a simple CO2 incubator using Raspberry PI and CO2 sensor (MH-Z14A)
Make a thermometer with Raspberry Pi and make it viewable with a browser Part 4
Make a Kanji display compass with Raspberry Pi and Sense Hat
Let's make an IoT shirt with Lambda, Kinesis, Raspberry Pi [Part 1]
Make a wireless LAN Ethernet converter and simple router with Raspberry Pi
Get GrovePi + sensor value with Raspberry Pi and store it in kintone
Pet monitoring with Rekognition and Raspberry pi
Improved motion sensor made with Raspberry Pi
Use PIR motion sensor with raspberry Pi
Make a wash-drying timer with a Raspberry Pi
Operate an oscilloscope with a Raspberry Pi
Create a car meter with raspberry pi
Make a thermometer with Raspberry Pi and make it visible on the browser Part 3
I tweeted the illuminance of the room with Raspberry Pi, Arduino and optical sensor
[For beginners] I made a motion sensor with Raspberry Pi and notified LINE!
Make a monitoring device with an infrared sensor
Raspberry + am2302 Measure temperature and humidity with temperature and humidity sensor
Get temperature and humidity with DHT11 and Raspberry Pi
GPS tracking with Raspberry Pi 4B + BU-353S4 (Python)