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.
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 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.



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):



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



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



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:
| ENA | IN1 | IN2 | Motor Status |
|---|---|---|---|
| 0 | / | / | Stop |
| 1 | 0 | 0 | Idle |
| 1 | 1 | 0 | Forward Rotation |
| 1 | 0 | 1 | Reverse Rotation |
| 1 | 1 | 1 | Idle |
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.



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.



#!/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


The article provides a detailed overview of essential files in the Raspberry Pi file system, covering their functions and significance in system operation. It explores


This tutorial streamlines Raspberry Pi’s auto-connect feature by editing the wpa_supplicant.conf file, allowing users to specify preferred Wi-Fi networks. By customizing network configurations, it ensures


This collaborative project seamlessly integrates the Raspberry Pi 4B and STM32 to create a sophisticated Intelligent Access Control System. Leveraging the strengths of each component,


In this comprehensive tutorial, you’ve explored how to build a compact weather station using a Raspberry Pi and the versatile BME280 sensor. The BME280 offers


This tutorial guides precision drone landing with Raspberry Pi, covering ROS Kinetic, MAVROS, and PX4 Autopilot setup. It explains Gazebo config, showcasing landmark recognition via


In this Raspberry Pi DHT22 Temperature & Humidity Sensor Project, a cost-effective and reliable DHT22 sensor is used to measure real-time temperature and humidity readings.


This tutorial outlines transmitting Raspberry Pi video frames to a PC using the UDP protocol for remote monitoring. UDP is preferred for speed over data


This tutorial provides a concise guide to establishing SPI communication between a Raspberry Pi and an SSD1306 OLED display. It covers SPI principles, Raspberry Pi