Simple Raspberry Pi Obstacle Avoidance Car Project

DIY car projects have always been highly attractive, and we’ve previously created an Arduino smart robot car. Now, we’ll undertake a similar project using Raspberry Pi. This is a simple and fun project primarily equipped with basic obstacle avoidance functionality. You just need to prepare some basic components and a car chassis, then easily control the car by combining technologies such as GPIO, Python programming, and network communication.

Table of Contents

What Do You Need

Before starting the Raspberry Pi obstacle avoidance car project, make sure you have the following key components ready:

  • Choose a Raspberry Pi with suitable performance, ensuring it has enough processing power and GPIO pins to meet the project requirements.
  • Purchase a car frame that you like; typically, it will include all the necessary components.
  • Use the L298N module to control the car’s motors.
  • Use the HC-SR04 ultrasonic module to measure the distance between the car and obstacles.
  • Prepare basic components such as a battery, jumper wires, cables, and a screwdriver; these are essential for assembling and connecting the hardware.

In the schematic diagram below for the Raspberry Pi obstacle avoidance car frame, each wheel needs to have a motor installed and connected to the L298N module. The Raspberry Pi acts as the main control unit, responsible for controlling the L298N module and receiving data from the HC-SR04.

Raspberry Pi obstacle avoidance car frame

Raspberry Pi GPIO Concept

As a powerful single-board computer, one of the unique features of the Raspberry Pi is its General Purpose Input/Output (GPIO) functionality. Through programming, users can configure these pins as input or output to communicate with other electronic components. Pins can be configured in output mode to generate high or low logic levels, or in input mode to receive logic signals.

Before proceeding with programming configurations, it’s essential to understand the numbering scheme of Raspberry Pi pins. Physical pins are numbered from 1 to 40, left to right and top to bottom. However, different coding schemes are typically used when writing programs. For C language, you need to import the wiringPi library; for Python, the RPi.GPIO library is required. It’s important to note that these two libraries use different pin numbering schemes. Taking the RPi.GPIO library as an example, the physical pin number 38 is labeled as BCM.20, while the physical pin number 40 is labeled as BCM.21.

Raspberry Pi GPIO

The main code excerpts involving GPIO under the Python program are as follows:

				
					import RPi.GPIO as GPIO  # Import the GPIO module
GPIO.setmode(GPIO.BCM)  # Set the numbering mode to BCM
GPIO.setwarnings(False)  # Turn off warnings
GPIO.setup(1, GPIO.IN)  # Set pin 1 (BCM numbering) as an input channel
GPIO.setup(2, GPIO.OUT)  # Set pin 2 as an output channel
value = GPIO.input(1)  # Read the input value of channel 1 (0 / GPIO.LOW / False or 1 / GPIO.HIGH / True)
GPIO.output(2, GPIO.HIGH)  # Set the output of channel 2 to a high logic level (0 / GPIO.LOW / False or 1 / GPIO.HIGH / True)
GPIO.cleanup()  # Clean up channel resources
				
			

Raspberry Pi Car Frame Assembly

After purchasing the car frame, it usually comes with an assembly guide, which is a relatively straightforward process. Using the provided mounting brackets to secure each motor, the assembly is essentially complete.

Here is an image of the assembled car frame (including L298N and HC-SR04):

Assembled Raspberry Pi obstacle avoidance car

When assembling the car frame, ensure that each motor is correctly installed, paying attention to the orientation of the motors to prevent issues during operation. Additionally, make sure the screws are oriented correctly to facilitate subsequent operations, such as wiring.

HC-SR04 Ultrasonic Sensor Module

HC-SR04 ultrasonic module

This project utilizes the HC-SR04 as the ultrasonic sensor module for the Raspberry Pi obstacle avoidance car. Although it has limited detection range, it has become a common choice for obstacle avoidance car projects due to its high accuracy and quick response.

The sensor module operates with a supply voltage of 5V. During operation, a high-level square wave of at least 10 microseconds needs to be applied to the Trig pin. This triggers the module to emit eight 40kHz ultrasonic waves while causing the level of the Echo pin to transition from 0 to 1. When the ultrasonic waves return and are received, the level of the Echo pin changes from 1 to 0. By recording this duration ‘t,’ the distance can be calculated using the formula: Distance = 340 * t / 2.

				
					import RPi.GPIO as GPIO
import time
class UltrasonicModule(object):
    '''Ultrasonic distance measurement module'''
    def __init__(self, trig_pin, echo_pin):
        self.trig_pin = trig_pin
        self.echo_pin = echo_pin
        self.pins_for_motor = [5, 6, 13, 19, 21, 22, 23, 24]  # These pins are occupied by the motor module
        self.pins_available = [12, 16, 17, 18, 20, 25, 26, 27]
    def setup(self):
        '''Pin initialization'''
        if (self.trig_pin not in self.pins_for_motor) and (self.trig_pin in self.pins_available):
            print("trig_pin is valid")
        else:
            print("trig_pin is invalid")
            return False
        if (self.echo_pin not in self.pins_for_motor) and (self.echo_pin in self.pins_available):
            print("echo_pin is valid")
        else:
            print("echo_pin is invalid")
            return False
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
        GPIO.setup(self.trig_pin, GPIO.OUT, initial=GPIO.LOW)  # Initialize trigger pin to low level
        GPIO.setup(self.echo_pin, GPIO.IN)
        return True
    def get_distance(self):
        '''Polling method to obtain distance'''
        GPIO.output(self.trig_pin, GPIO.HIGH)
        time.sleep(0.000015)
        GPIO.output(self.trig_pin, GPIO.LOW)
        while not GPIO.input(self.echo_pin):
            pass
        t1 = time.time()  # Start timing
        while GPIO.input(self.echo_pin):
            pass
        t2 = time.time()
        distance = (t2 - t1) * 340 / 2  # Calculate distance based on the speed of sound propagation
        return distance

				
			

L298N Motor Module

L298N motor module

As shown in the above diagram, the three terminals at the bottom left corner of the module are designated for 12V power input, GND, and 5V output, providing electrical support to the circuit. Additionally, on the left and right sides, there are two sets of terminals each. OUT1 and OUT2 are used to control the left motor, while OUT3 and OUT4 control the right motor.

The combination controlling the left motor consists of ENA, IN1, and IN2. ENA is the enable pin, and IN1 and IN2 are used to control the input logic of OUT1 and OUT2. Similarly, the control combination for the right motor is composed of ENB, IN3, and IN4, used to control OUT3 and OUT4.

The truth table for the left control combination is provided in the table below:

ENAIN1IN2Motor Status
0//Stop
100Idle
110Forward Rotation
101Reverse Rotation
111Idle

In addition, by providing PWM waves to the enable pin, you can easily adjust the motor’s speed. Typically, an L298N module is used to control two motors, each mounted on the left and right sides of the car. However, if the car is equipped with four motors, you may consider parallel connecting the two motors on one side, allowing one L298N module to manage all four motors simultaneously. Of course, if the battery supply is sufficient, you can also choose to use two L298N modules. When connecting the motors, ensure not to mistakenly connect the two motors on one side to guarantee their consistent movement direction.

The logic for the car’s movement is straightforward. When both motors rotate forward simultaneously, the car moves forward; conversely, when both rotate backward simultaneously, the car moves backward. If the left motor stops while the right motor rotates forward, the car turns left. Conversely, if the right motor stops while the left motor rotates forward, the car turns right.

This configuration and control logic make the car’s movement easy to understand. Through simple motor control, it achieves basic movements such as forward, backward, left turn, and right turn.

				
					import RPi.GPIO as GPIO
import time
import sys
class MotorModule(object):
    '''Motor control module'''
    def __init__(self):
        # Pins for enable and control
        self.enable_pins = [5, 6, 13, 19]
        self.control_pins = [21, 22, 23, 24]
        # Pins for four directions
        self.right_ahead_pin = self.control_pins[3]
        self.right_back_pin = self.control_pins[2]
        self.left_ahead_pin = self.control_pins[1]
        self.left_back_pin = self.control_pins[0]
        # Initialize pins
        self.setup()
    def setup(self):
        '''Pin initialization'''
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)
        # Initialize control pins to low level
        for pin in self.control_pins:
            GPIO.setup(pin, GPIO.OUT)
            GPIO.output(pin, GPIO.LOW)
        # Initialize enable pins to high level
        for pin in self.enable_pins:
            GPIO.setup(pin, GPIO.OUT)
            GPIO.output(pin, GPIO.HIGH)
    def ahead(self, second_value=0):
        '''Car moves forward'''
        self.setup()
        GPIO.output(self.right_ahead_pin, GPIO.HIGH)
        GPIO.output(self.left_ahead_pin, GPIO.HIGH)
        if second_value != 0:
            time.sleep(second_value)
            self.stop()
    def left(self, second_value=0):
        '''Car moves left'''
        self.setup()
        GPIO.output(self.right_ahead_pin, GPIO.HIGH)
        if second_value != 0:
            time.sleep(second_value)
            self.stop()
    def right(self, second_value=0):
        '''Car moves right'''
        self.setup()
        GPIO.output(self.left_ahead_pin, GPIO.HIGH)
        if second_value != 0:
            time.sleep(second_value)
            self.stop()
    def rear(self, second_value=0):
        '''Car moves backward'''
        self.setup()
        GPIO.output(self.right_back_pin, GPIO.HIGH)
        GPIO.output(self.left_back_pin, GPIO.HIGH)
        if second_value != 0:
            time.sleep(second_value)
            self.stop()
    def stop(self):
        '''Stop the car'''
        for pin in self.control_pins:
            GPIO.output(pin, GPIO.LOW)
				
			

Setting up the Control Main Program

After the distance measurement and motor control modules are debugged, combine them with the main body of the car to form the final product as shown below. You can use a power bank to supply power to the Raspberry Pi, while multiple dry batteries provide power to the motors.

Completely assembled Raspberry Pi obstacle avoidance car

Next, we need to write a main program to start the car and implement a simple obstacle avoidance function. My goal is to make the car move straight and continuously detect the distance of obstacles in front. When approaching an obstacle, the program will make the car turn right at a certain angle and then continue moving forward.

Raspberry Pi obstacle avoidance car workflow

				
					#!/usr/bin/python3
"""
Simple script to control a car with ultrasonic obstacle avoidance using Raspberry Pi GPIO.
"""
import RPi.GPIO as GPIO
import sys
import time
import Ultrasonic_Module as Ul
import Motor_Module as Mo
### Module Initialization
ultrasonic = Ul.Ultrasonic_Module(25, 26)  # Create an ultrasonic module object, specifying trigger pin and echo pin
motor = Mo.Motor_Module()  # Create a motor module object
print("######### Initiating the ultrasonic module... ##########")
# Initialize the ultrasonic module
if not ultrasonic.setup():
    print("Ultrasonic setup failed, program stopped.")
    exit()
print("######### Initiating the motor module... ##########")
motor.setup()  # Initialize the motor module
### Initially, the car is in the forward state
print("The car starts running...")
motor.ahead()
### Loop to check distance
distance = 10
limited_distance = 0.05  # Minimum obstacle distance
time_rear = 0.8  # Duration for moving backward
time_right = 1.2  # Duration for turning right
try:
    while True:
        distance = ultrasonic.getdistance()  # Get the current distance
        if distance <= limited_distance:
            print("Run into an obstacle")
            motor.stop()  # Stop the motor
            motor.rear(time_rear)  # Move backward for a period
            motor.right(time_right)  # Turn right for a period
            motor.ahead()  # Resume forward state
except KeyboardInterrupt:
    motor.stop()  # Stop the motor on keyboard interrupt
    print("\nStop the car")
    # Clean up GPIO resources
    GPIO.cleanup()

				
			

Ending

Finally, connect to the Raspberry Pi via SSH to execute the aforementioned main program file and start the program, successfully running the car.

However, this project only uses one sensor located at the front of the car, making it unable to recognize obstacles from the side, potentially causing the car to get stuck. Additionally, due to the heavy load of the four motors, the battery life of the dry battery pack is insufficient, and the speed of the car is not optimal. TechSparks recommends purchasing a larger capacity lithium battery.

You Might Be Interested

raspberry pi autostart
How to Auto Start Programs on Raspberry Pi

Automating program startup on the Raspberry Pi can be achieved through various methods. Editing the “/etc/rc.local” file or using desktop applications, while simpler, may not

raspberry pi
What is Raspberry Pi?

Raspberry Pi, a revolutionary single-board computer introduced by the Raspberry Pi Foundation, has become a global sensation, initially designed for educational purposes. With its integrated

Scroll to Top