Build a Raspberry Pi Robot Car: Simple Remote Control Tutorial
Learn how to build your own remote-controlled robot car using Raspberry Pi. Beginner-friendly tutorial covering chassis assembly, motor control, and web browser control from your phone or computer.
Keywords: raspberry pi robot car, diy robot car, raspberry pi robotics, remote control car raspberry pi, robot car tutorial, beginner robotics project
Build an impressive robot car you can control from your phone via web browser. Perfect introduction to robotics, programming, and electronics for beginners.
What You're Building
A simple robot car that:
- Drives forward, backward, left, and right
- Controlled via web browser from any device
- Live camera feed for remote driving
- Basic obstacle detection
- Great learning platform for robotics
Difficulty: ⭐⭐⭐ Intermediate Time Required: 3-4 hours Cost: $100-150
Shopping List
Core Components:
- Raspberry Pi 4 (4GB) – $45-55
- SanDisk 128GB microSD – $15-20
- Pi 4 Case – $8-10
Robot Parts:
- Robot car chassis kit with 4 motors – $35-50
- L298N motor driver board – $8-12
- Raspberry Pi camera module – $25-30
- HC-SR04 ultrasonic sensor – $5-8
- Jumper wires and breadboard – $10-15
- 18650 battery pack (2-cell) – $15-20
Total Cost: $106-158
Step-by-Step Build
Step 1: Assemble the Chassis
- Build the frame following kit instructions
- Mount the 4 motors in corners
- Attach wheels to motor shafts
- Install battery holder in center
- Mount Pi on top using standoffs
Step 2: Wire Everything Up
Motor Driver Connections:
L298N → Raspberry Pi
ENA → GPIO 18
IN1 → GPIO 24
IN2 → GPIO 23
ENB → GPIO 19
IN3 → GPIO 21
IN4 → GPIO 20
VCC → 5V
GND → GND
Motors to Driver:
Left Motors → OUT1, OUT2
Right Motors → OUT3, OUT4
Ultrasonic Sensor:
VCC → 5V
GND → GND
Trig → GPIO 16
Echo → GPIO 12
Power:
- Battery pack positive → L298N motor power input
- Battery pack negative → Pi GND and L298N GND
- Connect Pi to power via USB or GPIO
Step 3: Software Setup
Install Raspberry Pi OS:
- Use Raspberry Pi Imager
- Enable SSH and Wi-Fi in advanced options
- Flash to SD card and boot Pi
Install required packages:
sudo apt update && sudo apt upgrade -y
sudo apt install python3-pip python3-flask python3-opencv -y
pip3 install RPi.GPIO opencv-python flask --break-system-packages
sudo raspi-config # Enable camera interface
Step 4: Create the Robot Code
Main robot control script (robot_car.py):
from flask import Flask, render_template_string
import RPi.GPIO as GPIO
import cv2
import time
import threading
from datetime import datetime
app = Flask(__name__)
# Motor pins
ENA, IN1, IN2 = 18, 24, 23 # Left motors
ENB, IN3, IN4 = 19, 21, 20 # Right motors
# Sensor pins
TRIG, ECHO = 16, 12
# Setup GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup([ENA, IN1, IN2, ENB, IN3, IN4, TRIG], GPIO.OUT)
GPIO.setup(ECHO, GPIO.IN)
# PWM for speed control
left_motor = GPIO.PWM(ENA, 1000)
right_motor = GPIO.PWM(ENB, 1000)
left_motor.start(0)
right_motor.start(0)
# Camera setup
camera = cv2.VideoCapture(0)
class Robot:
def __init__(self):
self.speed = 50
def stop(self):
left_motor.ChangeDutyCycle(0)
right_motor.ChangeDutyCycle(0)
GPIO.output([IN1, IN2, IN3, IN4], GPIO.LOW)
def forward(self):
left_motor.ChangeDutyCycle(self.speed)
right_motor.ChangeDutyCycle(self.speed)
GPIO.output([IN1, IN3], GPIO.HIGH)
GPIO.output([IN2, IN4], GPIO.LOW)
def backward(self):
left_motor.ChangeDutyCycle(self.speed)
right_motor.ChangeDutyCycle(self.speed)
GPIO.output([IN1, IN3], GPIO.LOW)
GPIO.output([IN2, IN4], GPIO.HIGH)
def left(self):
left_motor.ChangeDutyCycle(self.speed)
right_motor.ChangeDutyCycle(self.speed)
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.HIGH)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
def right(self):
left_motor.ChangeDutyCycle(self.speed)
right_motor.ChangeDutyCycle(self.speed)
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.HIGH)
def get_distance(self):
GPIO.output(TRIG, True)
time.sleep(0.00001)
GPIO.output(TRIG, False)
start_time = time.time()
while GPIO.input(ECHO) == 0:
start_time = time.time()
while GPIO.input(ECHO) == 1:
end_time = time.time()
distance = (end_time - start_time) * 17150
return round(distance, 2)
robot = Robot()
# Web interface HTML
HTML_TEMPLATE = '''
<!DOCTYPE html>
<html>
<head>
<title>Robot Car Control</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
font-family: Arial;
text-align: center;
background: #2c3e50;
color: white;
margin: 0;
padding: 20px;
}
.container { max-width: 600px; margin: 0 auto; }
h1 { color: #3498db; margin-bottom: 30px; }
.controls {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
max-width: 300px;
margin: 30px auto;
}
.btn {
background: #3498db;
border: none;
color: white;
padding: 20px;
border-radius: 10px;
font-size: 18px;
cursor: pointer;
transition: background 0.3s;
}
.btn:hover { background: #2980b9; }
.btn:active { background: #21618c; }
.stop { background: #e74c3c !important; }
.stop:hover { background: #c0392b !important; }
.speed-control {
margin: 20px 0;
padding: 20px;
background: rgba(255,255,255,0.1);
border-radius: 10px;
}
.slider {
width: 100%;
margin: 10px 0;
height: 6px;
border-radius: 3px;
background: #34495e;
outline: none;
}
.status {
margin: 20px 0;
padding: 15px;
background: rgba(255,255,255,0.1);
border-radius: 10px;
}
.camera-feed {
margin: 20px 0;
padding: 20px;
background: rgba(255,255,255,0.1);
border-radius: 10px;
}
#camera {
max-width: 100%;
border-radius: 10px;
background: #34495e;
}
</style>
</head>
<body>
<div class="container">
<h1>🤖 Robot Car Control</h1>
<div class="camera-feed">
<h3>📹 Camera Feed</h3>
<img id="camera" src="/video" width="400" height="300" alt="Camera Loading...">
</div>
<div class="controls">
<div></div>
<button class="btn" onclick="move('forward')">↑</button>
<div></div>
<button class="btn" onclick="move('left')">←</button>
<button class="btn stop" onclick="move('stop')">STOP</button>
<button class="btn" onclick="move('right')">→</button>
<div></div>
<button class="btn" onclick="move('backward')">↓</button>
<div></div>
</div>
<div class="speed-control">
<h3>Speed Control</h3>
<input type="range" class="slider" id="speed" min="20" max="100" value="50"
onchange="setSpeed(this.value)">
<p>Speed: <span id="speedValue">50</span>%</p>
</div>
<div class="status">
<h3>Robot Status</h3>
<p>Distance: <span id="distance">-- cm</span></p>
<p>Last Update: <span id="time">--:--:--</span></p>
</div>
</div>
<script>
function move(direction) {
fetch('/move/' + direction);
}
function setSpeed(speed) {
document.getElementById('speedValue').textContent = speed;
fetch('/speed/' + speed);
}
function updateStatus() {
fetch('/status')
.then(response => response.json())
.then(data => {
document.getElementById('distance').textContent = data.distance + ' cm';
document.getElementById('time').textContent = data.time;
});
}
// Update status every 2 seconds
setInterval(updateStatus, 2000);
// Keyboard controls
document.addEventListener('keydown', function(event) {
switch(event.key) {
case 'ArrowUp': case 'w': case 'W': move('forward'); break;
case 'ArrowDown': case 's': case 'S': move('backward'); break;
case 'ArrowLeft': case 'a': case 'A': move('left'); break;
case 'ArrowRight': case 'd': case 'D': move('right'); break;
case ' ': move('stop'); event.preventDefault(); break;
}
});
document.addEventListener('keyup', function(event) {
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'W', 'a', 'A', 's', 'S', 'd', 'D'].includes(event.key)) {
move('stop');
}
});
</script>
</body>
</html>
'''
@app.route('/')
def index():
return render_template_string(HTML_TEMPLATE)
@app.route('/move/<direction>')
def move(direction):
if direction == 'forward':
robot.forward()
elif direction == 'backward':
robot.backward()
elif direction == 'left':
robot.left()
elif direction == 'right':
robot.right()
elif direction == 'stop':
robot.stop()
# Auto-stop after 0.5 seconds for safety
if direction != 'stop':
threading.Timer(0.5, robot.stop).start()
return 'OK'
@app.route('/speed/<int:speed>')
def set_speed(speed):
robot.speed = max(20, min(100, speed))
return 'OK'
@app.route('/status')
def status():
return {
'distance': robot.get_distance(),
'time': datetime.now().strftime('%H:%M:%S')
}
@app.route('/video')
def video():
def generate():
while True:
ret, frame = camera.read()
if ret:
_, jpeg = cv2.imencode('.jpg', frame)
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + jpeg.tobytes() + b'\r\n')
time.sleep(0.1)
return app.response_class(generate(),
mimetype='multipart/x-mixed-replace; boundary=frame')
if __name__ == '__main__':
try:
print("Starting Robot Car Server...")
print("Access at: http://robotcar.local:5000")
app.run(host='0.0.0.0', port=5000, debug=False)
except KeyboardInterrupt:
print("Shutting down...")
finally:
robot.stop()
GPIO.cleanup()
camera.release()
Step 5: Test Your Robot
Run the robot:
python3 robot_car.py
Control your robot:
- Open web browser on phone or computer
- Go to:
http://robotcar.local:5000 - Use arrow buttons or WASD keys to drive
- Watch live camera feed
- Adjust speed slider as needed
Basic Troubleshooting
Robot won't move:
- Check battery connections
- Verify GPIO wiring
- Test motors directly with battery
Can't access web interface:
- Check Pi IP address:
hostname -I - Try IP address instead:
http://192.168.1.50:5000 - Ensure Pi is connected to Wi-Fi
Camera not working:
- Enable camera:
sudo raspi-config→ Interface → Camera - Check camera connection
- Restart Pi
Simple Upgrades
Auto-Start on Boot
Create service file:
sudo nano /etc/systemd/system/robotcar.service
Add:
[Unit]
Description=Robot Car
After=network.target
[Service]
ExecStart=/usr/bin/python3 /home/pi/robot_car.py
WorkingDirectory=/home/pi
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
Enable service:
sudo systemctl enable robotcar.service
sudo systemctl start robotcar.service
Basic Obstacle Avoidance
Add this to your robot code:
def auto_drive(self):
distance = self.get_distance()
if distance > 20: # 20cm threshold
self.forward()
time.sleep(0.5)
else:
self.backward()
time.sleep(0.3)
self.right()
time.sleep(0.5)
self.stop()
What's Next?
Once your basic robot works, you can:
- Add more sensors (gyroscope, GPS)
- Implement line following
- Add voice control
- Create autonomous missions
- Build obstacle courses
Cost vs. Commercial Options
Your DIY Robot: $100-150 one-time Commercial RC cars: $50-200 (no camera/programming) Educational robot kits: $200-500+
Your advantages:
- Learn programming and electronics
- Completely customizable
- Web-based control from any device
- Foundation for advanced robotics
Frequently Asked Questions
Can I use a different Raspberry Pi model?
Yes! Pi 3 B+ works fine, Pi Zero 2 W works but slower camera streaming.
How long does the battery last?
Typically 1-2 hours depending on usage and battery capacity.
Can I control it from outside my home?
Yes, set up VPN or port forwarding (advanced topic).
What if I mess up the wiring?
No permanent damage likely - just double-check connections and try again.
Conclusion
You've built a working robot car! This project teaches:
- Basic robotics and motor control
- Web development and remote control
- Electronics and circuit building
- Problem-solving and debugging
Next steps: Experiment with the code, add new features, and most importantly - have fun driving your creation!
Ready to build your robot car? This beginner-friendly project is perfect for learning robotics fundamentals!
