Introduction: Autonomous RC Car
With the rise of self-driving, autonomous cars today, I decided to take on the challenge of making one of my own. This project also served as my capstone project in my Engineering Design and Development and Robotics classes and received an award for best autonomous vehicle at a high school STEM competition.
Instead of starting from scratch, I opted to use an RC car that we already had and paired it with a RedBoard Arduino Uno board. I chose the Arduino due to its relative ease of use and programming.
For those wondering, this car has a Redcat Racing 03061 Splash-Resistant ESC with a brushed motor. The ESC was already programmed using the controller that came with the car. I have not tested this with a brushless motor since we do not have one on hand, but anyone is welcome to try this project with a brushless motor.
In brief summary, this car collects data from (5) HC-SR04 Ultrasonic sensors. This data goes back to the Arduino, where it makes decisions on how to move. The Arduino then controls the steering servo and motor accordingly. The program uses the standard Arduino servo library to do so, and no additional libraries are necessary.
The car is capable of variable speed control via a potentiometer and backing up from a wall when it hits one. In addition, the car can correct itself if it drifts too close to a wall by easing itself away.
Step 1: Parts List
Disclaimer: I am not including the parts needed for the car itself, only the additional parts beyond the car. An ESC, motor, chassis, battery, etc. will all be necessary for this.
You will need:
(1) Arduino Uno - knockoffs will work just fine
(1) Breadboard - for this project, I took the +/- rail from one breadboard and used another, smaller breadboard. Any size will do.
(5) HC-SR04 Ultrasonic Sensors
(1) Potentiometer - used to control the speed of the car
(20) Female-Male Dupont wires - I highly recommend having more to use as extenders for other wires if needed
Soldering Iron with solder
Arduino Power Supply - in this case, I used (6) 1.2v AA batteries wired in series. External phone and tablet power banks like this one will also work well when plugged into the USB port.
Tape, hot glue, and/or any other items used to fasten items together
(1) Toggle Switch (optional -- I use it to turn the Arduino on and off)
Step 2: Position the Sensors
First, you will want to correctly position and fasten the sensors. I have (1) sensor facing forward, (2) sensors angled about 45 degrees, and (2) sensors on the sides of the car. I 3D printed mounting brackets for the sides and front, and used hot glue to fasten the angled front sensors since hot glue is non-conductive. The mounting brackets for the sides and front can be downloaded and 3D printed.
Step 3: Add the Breadboard and Potentiometer
Next, you'll want to add in the breadboard and speed-controlling potentiometer before you begin wiring. This is where I used a small breadboard and the +/- from another breadboard due to space on the car's body, but a standard breadboard will also do just fine.
Step 4: Wire Everything
This one is probably the biggest step, and one wrong wire can cause the car to not function properly. Refer to the Fritzing diagram above for extra guidance.
Start by connecting the 5v pin of your Arduino to the positive rail on the breadboard and the GND pin of your Arduino to the negative rail of the breadboard.
Next, wire up the sonar sensors. The HC-SR04 sensors have each of their four pins labeled. They are:
VCC -- 5v power
Trig -- trigger to send an ultrasonic pulse out
Echo -- receiving pin that measures the duration of the pulse
GND - ground pin
Use female-male Dupont wires for this. Each of the VCC pins should be connected to the positive breadboard rail, and each of the GND pins should be connected to the negative breadboard rail. I used extra female-male Dupont wires as extenders for this part since I had an issue with some of the wires not being long enough.
Next, wire the Trig and Echo pins into the Arduino. These will be connected to the digital pins of the Arduino as such:
Front Center Sensor:
Trig -- pin 6
Echo -- pin 7
Left Side Sensor:
Trig -- 4
Echo -- 5
Right Side Sensor:
Trig -- 2
Echo -- 3
Front Left Sensor:
Trig -- 10
Echo -- 11
Front Right Sensor:
Trig -- 9
Echo -- 8
Next, wire the steering servo, motor ESC, and speed control potentiometer.
First, start with the steering servo. The servo on my car had red, orange, and brown wires. The colors may vary a little bit, but they will all be wired similarly:
Brown wire (ground) -- connect to negative breadboard rail
Red wire (5v power) -- connect to 5v breadboard rail
Orange wire (signal) -- connect to pin 13 on your Arduino
The ESC -- or Electronic Speed Controller -- that controls the motor is wired very similarly. In this case, the wires are white, red, and black.
White (signal) -- Connect to pin 12 on your Arduino
Red (5v) -- do NOT connect to anything. Due to a surge of electricity that flows backward when the motor stops, the 5v should not be connected. You could fry a USB port or, possibly, your Arduino.
Black (ground) -- connect to negative breadboard rail
Finally, wire the potentiometer that you put on your breadboard earlier. Small numbers are likely printed on it somewhere. It should be wired as:
1 (left pin) -- connect to negative breadboard rail
2 (middle pin) -- connect to pin A0 on your Arduino
3 (right pin) -- connect to positive breadboard rail
The wiring will look very messy, so if you want to do some wire management, now would be the time to do it.
Step 5: Powering the Arduino
Next, you will want to set up a power solution for the Arduino. Two separate power sources are used in this project: the battery for the car, and the battery for the Arduino. In this case, I used (6) 1.2v rechargeable AA batteries wired in series. Portable cell phone power banks will also work, just make sure to have a cable that plugs into your Arduino's USB port (such as mini-USB).
Please note that 9v batteries will NOT work with this project. Due to the way that 9v batteries are designed, the voltage is sufficient to run the Arduino, but the current coming out of the battery will cause it to die in no time. I also had issues with random reboots on the 9v battery.
If you choose to use the solution that I used, you will need:
(6) AA batteries (alkaline batteries work fine as well)
AA battery holders for all (6) batteries. This one would work great and doesn't even require you to use a soldering iron. For the supply that I made, I daisy-chained (3) two-battery holders together as pictured, soldered the positive/negative wires together, took the DC power plug from a 9v battery adapter, and soldered it to the end positive and negative wires. I then soldered a power switch in series with the power supply for ease of turning the Arduino on and off. This is completely optional.
Step 6: Upload the Arduino Program
Next, you will need to upload the program to the Arduino. Download the program here, and upload it to your Arduino through the Arduino IDE.
For those of you who might look into modifying the code, I have included some pseudocode explaining what each part does.
EDIT 9/25/18 - I added a second program to make it drive in the middle of two walls. I have not had the chance to try out the code due to not having access to the car, but feel free to experiment with it.
Step 7: Plug Everything in and Turn It On
Finally, you will need to plug everything in. First, connect the car battery to the car and turn on your ESC. The ESC should beep, indicating that it is ready to be "armed" by the Arduino. Next, power up the Arduino. The ESC should beep three times, and the wheels should begin turning. If the ESC beeps, but the wheels do not begin turning, turn the potentiometer to the right to increase the speed. If the car is moving too fast, turn the potentiometer to the left.
If the potentiometer works the opposite of the way it should, you can flip the positive and negative wires to solve this.
The video shows the car working, how to change the speed, and the order to turn it on in.

Participated in the
Microcontroller Contest
8 Comments
Question 2 years ago on Step 7
my motor is not tuning i am using romeo BLE V2 boad
[code]
/* Autonomous RC Car
* 5 ultrasonic sensors
* servo to steer
* rear motor to drive forward and reverse
*/
#include <Servo.h>
const int echo = A3; //Pins for readFront() - Sonar on front
const int trig = A2;
const int echo2 = 13; //Pins for readLeft() - Left side
const int trig2 = 12;
const int echo3 = 3; //Pins for readRight() - Right side
const int trig3 = 2;
const int echo4 = 11; //Pins for readFrontLeft() - Front left side
const int trig4 = 10;
const int echo5 = A1; //Pins for readFrontRight() - Front right side
const int trig5 = A0;
int distance; //Necessary vars for sonar readings, setting speeds, etc.
int distance2;
int distance3;
int distance4;
int distance5;
const int motorFoward = 5;
const int motorReverse = 4;
Servo motor; //Create ESC drive motor 'motor' and steering servo 'steer'
Servo steer;
int speedRead; //Vars necessary to control the speed of the motor via the ESC.
int speedRemap;
int forwardSpeed;
int reverseSpeed;
int speedCenter = 1500;
const int neutral = 90; //Steering servo positions for straight, left turns, and right turns. These can be changed and are set in degrees.
const int steerRight = 140;
const int steerLeft = 40;
const int frontThreshold = 30; //Thresholds for the different 'types' of sonar sensors -- front, front side, and side. These can be changed and are set in centimeters.
const int frontSideThreshold = 25;
const int sideThreshold = 20;
void setup() {
//Serial.begin(9600); //Begins//Serial Monitor for debugging
pinMode(echo, INPUT); //Configuring pins for sonar modules
pinMode(trig, OUTPUT);
pinMode(echo2, INPUT);
pinMode(trig2, OUTPUT);
pinMode(echo3, INPUT);
pinMode(trig3, OUTPUT);
pinMode(echo4, INPUT);
pinMode(trig4, OUTPUT);
pinMode(echo5, INPUT);
pinMode(trig5, OUTPUT);
motor.attach("(5, 4)", 255, 255); //Attaches motor to pin 5 enable, pin 4 control
steer.attach(9); //Attaches steering servo to pin 9
//Serial.println("MOTOR ATTACHED"); //Prints a message for debug
delay(500);
motor.writeMicroseconds(forwardSpeed); //Starts the car moving forward
}
void readFront(unsigned long duration) { //Function called to read in front of car
digitalWrite(trig, LOW);
delayMicroseconds(3);
digitalWrite(trig, HIGH);
delayMicroseconds(10);
digitalWrite(trig, LOW);
duration = pulseIn(echo, HIGH, 100000);
distance = (duration / 2) / 29.1;
return (distance);
}
void readLeft(unsigned long leftDuration) { //Function called to read left side of car
digitalWrite(trig2, LOW);
delayMicroseconds(3);
digitalWrite(trig2, HIGH);
delayMicroseconds(10);
digitalWrite(trig2, LOW);
leftDuration = pulseIn(echo2, HIGH, 40000);
distance2 = (leftDuration / 2) / 29.1;
return (distance2);
}
void readRight(unsigned long rightDuration) { //Function called to read right side of car
digitalWrite(trig3, LOW);
delayMicroseconds(3);
digitalWrite(trig3, HIGH);
delayMicroseconds(10);
digitalWrite(trig3, LOW);
rightDuration = pulseIn(echo3, HIGH, 40000);
distance3 = (rightDuration / 2) / 29.1;
return (distance3);
}
void readFrontLeft(unsigned long frontLeftDuration) { //Function called to read front left of car
digitalWrite(trig4, LOW);
delayMicroseconds(3);
digitalWrite(trig4, HIGH);
delayMicroseconds(10);
digitalWrite(trig4, LOW);
frontLeftDuration = pulseIn(echo4, HIGH, 87500);
distance4 = (frontLeftDuration / 2) / 29.1;
return (distance4);
}
void readFrontRight(unsigned long frontRightDuration) { //Function called to read front right of car
digitalWrite(trig5, LOW);
delayMicroseconds(3);
digitalWrite(trig5, HIGH);
delayMicroseconds(10);
digitalWrite(trig5, LOW);
frontRightDuration = pulseIn(echo5, HIGH, 87500);
distance5 = (frontRightDuration / 2) / 29.1;
return (distance5);
}
void rightTurn() { //Function called to execute a short, right turn to get away from a wall
steer.write(steerRight);
motor.writeMicroseconds(forwardSpeed);
delay(50);
steer.write(neutral);
//Serial.println("RIGHT TURN");
}
void leftTurn() { //Function called to execute a short, left turn to get away from a wall
steer.write(steerLeft);
motor.writeMicroseconds(forwardSpeed);
delay(50);
steer.write(neutral);
//Serial.println("LEFT TURN");
}
void longLeftTurn() { //Function called to execute a long, left turn when a wall is detected by the front sensor. Should be approximately 90 degrees.
steer.write(steerLeft);
motor.writeMicroseconds(forwardSpeed);
delay(475);
steer.write(neutral);
//Serial.println("LONG LEFT");
}
void longRightTurn() { //Function called to execute a long, right turn when a wall is detected by the front sensor. Should be approximately 90 degrees.
steer.write(steerRight);
motor.writeMicroseconds(forwardSpeed);
delay(475);
steer.write(neutral);
//Serial.println("LONG RIGHT");
}
void frontLeftTurn() { //Function called to execute a 45 degree right turn when the front left sensor is triggered.
steer.write(steerLeft);
motor.writeMicroseconds(forwardSpeed);
delay(90);
steer.write(neutral);
//Serial.println("FRONT LEFT TURN");
}
void frontRightTurn() { //Function called to execute a 45 degree right turn when the front left sensor is triggered.
steer.write(steerRight);
motor.writeMicroseconds(forwardSpeed);
delay(90);
steer.write(neutral);
//Serial.println("FRONT RIGHT TURN");
}
void readSidesAndFront() { //Function called to determine if any short or 45 degree turns are needed. I made this a separate function since this is called three times in the loop, and the code
readFrontLeft(0); //became unnecessarily long. It reads all of the sensors except the front sensor and determines whether or not any minor corrections are necessary.
//Serial.print("Front Left: ");
//Serial.println(distance4);
delay(3);
readFrontRight(0);
//Serial.print("Front Right: ");
//Serial.println(distance5);
delay(3);
readLeft(0);
//Serial.print("Left: ");
//Serial.println(distance2);
delay(3);
readRight(0);
//Serial.print("Right: ");
//Serial.println(distance3);
if (distance3 < distance2 && distance3 != 0 && distance2 != 0) {
readLeft(0);
delay(3);
readRight(0);
//Serial.print("Left: ");
//Serial.println(distance2);
if (distance3 < distance2 && distance3 != 0 && distance2 != 0) {
leftTurn();
}
else {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
}
else if (distance2 < distance3 && distance2 != 0 && distance3 != 0) {
readLeft(0);
delay(3);
readRight(0);
//Serial.print("Right: ");
//Serial.println(distance3);
if (distance2 < distance3 && distance2 != 0 && distance3 != 0) {
rightTurn();
}
else {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
}
else if (distance5 <= 4 && distance5 != 0) {
steer.write(steerRight);
motor.writeMicroseconds(speedCenter);
delay(1000);
motor.writeMicroseconds(reverseSpeed);
//Serial.println("Reversing");
delay(950);
motor.writeMicroseconds(speedCenter);
steer.write(neutral);
}
else if (distance5 < frontSideThreshold && distance5 != 0) {
readFrontRight(0);
//Serial.print("Front Right: ");
//Serial.println(distance5);
if (distance5 < frontSideThreshold && distance5 != 0) {
readFrontRight(0);
//Serial.print("Front Right: ");
//Serial.println(distance5);
if (distance5 < frontSideThreshold && distance5 != 0) {
frontLeftTurn();
}
else {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
}
else {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
}
else if (distance4 < 4 && distance4 != 0) {
steer.write(steerLeft);
motor.writeMicroseconds(speedCenter);
delay(1000);
motor.writeMicroseconds(reverseSpeed);
//Serial.println("Reversing");
delay(950);
motor.writeMicroseconds(speedCenter);
steer.write(neutral);
}
else if (distance4 < frontSideThreshold && distance4 != 0) {
readFrontLeft(0);
//Serial.print("Front Left: ");
//Serial.println(distance4);
if (distance4 < frontSideThreshold && distance4 != 0) {
readFrontLeft(0);
//Serial.print("Front Left: ");
//Serial.println(distance4);
if (distance4 < frontSideThreshold && distance4 != 0) {
frontRightTurn();
}
else {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
}
else {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
}
}
void loop() {
speedRead = analogRead(A0); //Reads the potentiometer, remaps the values, and creates a forward and reverse speed that are both controlled by the potentiometer. This was
speedRemap = map(speedRead, 0, 1023, 0, 500); //implemented to accommodate the car battery discharging, since you can write a higher signal to a lower power battery to get the same speed
forwardSpeed = speedCenter + speedRemap; //as a lower signal with a higher power battery.
reverseSpeed = speedCenter - speedRemap;
//Serial.print("Forward Speed: ");
//Serial.println(forwardSpeed);
//Serial.print("Reverse Speed: ");
//Serial.println(reverseSpeed);
steer.write(neutral);
motor.writeMicroseconds(forwardSpeed);
digitalWrite(trig, LOW);
digitalWrite(trig2, LOW);
digitalWrite(trig3, LOW);
digitalWrite(trig4, LOW);
digitalWrite(trig5, LOW);
readFront(0);
//Serial.print("Forward Reading: "); //Here is an example of the stacked if/else statements I mentioned before. I had issues with the car randomly turning even when no object was present.
//Serial.println(distance); //This was due to noise in the sonar readings. Here, the four if statements within each other essentially quadruple check to make sure that an obstacle
delay(8); //is present before turning. This has a negligible impact on reading performance, and I used a similar structure for the readSidesAndFront() function above.
if (distance < frontThreshold && distance != 0) {
readFront(0);
//Serial.print("Forward Reading: ");
//Serial.println(distance);
delay(8);
if (distance < frontThreshold && distance != 0) {
readFront(0);
//Serial.print("Forward Reading: ");
//Serial.println(distance);
delay(8);
if (distance < frontThreshold && distance != 0) {
readFront(0);
//Serial.print("Forward Reading: ");
//Serial.println(distance);
delay(8);
if (distance > frontThreshold || distance == 0) {
motor.writeMicroseconds(forwardSpeed);
steer.write(neutral);
}
else { //Here is the code for the passage of the quadruple check, indicating an obstacle. Below, the car reads the two sides and decides which one has more room. If one sensor
readRight(0); //returns an error (0), the car turns in that direction due to errors being returned for wide open spaces.
readLeft(0);
if (distance2 > distance3 && distance2 != 0) {
longLeftTurn();
}
else if (distance3 > distance2 && distance3 != 0) {
longRightTurn();
}
else if (distance2 == 0) {
longLeftTurn();
}
else if (distance3 == 0) {
longRightTurn();
}
readFront(0); //Code for backing up. If the front sonar is too close to the wall (less than 5cm), the car will pause, then back up and carry on with normal function.
if (distance <= 5 && distance != 0) {
motor.writeMicroseconds(speedCenter);
delay(1000);
motor.writeMicroseconds(reverseSpeed);
//Serial.println("Reversing");
delay(950);
motor.writeMicroseconds(speedCenter);
steer.write(neutral);
}
}
}
else {
readSidesAndFront(); //Here, if the multiple checks for a wall in front fail at any point, the car reads the rest of the sensors for minor adjustments. It calls the function mentioned above.
}
}
else {
readSidesAndFront();
}
}
else {
readSidesAndFront();
}
}
[/code]
Mdara Vee
vee.mdara@icloud.com
2 years ago
Hi there! Finally i found a good tutorial that almost fits my needs. By almost i wanted to ask, is there any possible way to still use the reciver along with the arduino sensors so you actually have control of the car with the remote it came with and still have the anti crash features?
Thanks!
4 years ago
hey, can anybody tell me the exact or full name of car because i got different car for this project by mistake and from where we can buy this car.
thanks
Reply 2 years ago
It looks like a la traxx rally car or a tamiya euro truck
Question 4 years ago
In my case the car is only steering away from the wall, if the distance to the wall is about 2cm or less.
If the distance is larger than that, the car is steering towards the wall, instead of driving straight.
Does anybody know a solution to this problem?
Thanks
Question 4 years ago
Hey, i had a couple of questions and was wondering if anyone could answer it.
Q1) I wanted to know how i could increase the distance from where it's supposed to turn away from the wall, for eg, right now it avoids the wall from a distance of 5 cm, how can i increase that to 10 or 15. Thanks.
Q2) Since the distance will be increased, is there any way to make it turn away from the wall without stopping for it to be much smoother.
Q3) is there any way to make it run in the centre of 2 walls, just like lane driving, so it runs with equal distance on each side, basically a combination of the features asked about in question 1 and 2.
thanks, any help would be appreciated.
Answer 4 years ago
Q1) To change the thresholds of the sensors, you can change the numbers of the threshold variables. You can find them in lines 45-47 of the code.
Q2) The car should turn smoothly away from the wall as it is. Personally, I didn't have issues with my car, but there could be some obscure issue that's hiding somewhere that is causing it to stop. I don't really have an answer for this one.
Q3) I'm working on changing some code right now to make it run between two walls. I will try and upload it once I am finished with it.
EDIT: I uploaded the code but haven't had a chance to try it out yet. Feel free to experiment and change it as needed.
5 years ago
Very cool, thanks for sharing!!