Last Updated: 10/11/2021
Project 2 - Arduino Robot Car
Projects >> Project 2 - Arduino Robot Car
Arduino Robot Car
This started out as a simple project to build a Robot Car for a "Makers Club" with th idea that I wanted those between 7 and 70+ to be abe to build the car with as few tools and skills as possible while allowing those who wanted to learn more to be able to learn some basics of programming and gain an understanding of how an Arduino works.
I had a lot of fun building my car but along the way I did come acros various issues and pitfalls that I thought I would share on this page to help others avoid some simple issues.
The project has been divided into sections as the project is ongoing, but if you want to skip to certain parts of the page just click on the lnnks below.
Makers Club Project Overview and Issues for younger maker
Buying a Kit, what to watch out for and guide prices
Arduino Object Detection Overview
Example1: Arduino code and video tutorial: First version using a single servo mounted sensor
Upgrades v1: Change to battery holder
Version 1
Ugrade to Version 2
Makers Club Project Overview
As part of a "Makers club" project with my church I wanted to help young and old alike to take part in building a couple of robot cars, either line following or obstacle avoidance types.
The issues I faced was that I would have a mixture of ages and abilities and so I wanted to be able to have a series of projects that would allow different ages and abilities to take part including those with no programming ability at all. It would also limit the tools that could be used to build the project, soldering irons and electric drills are not the best things to be used by youngsters in a crowded environment, at least according to the dreaded risk assessments.
I also wanted the project to be affordable to as many as possible so that if individuals wanted to have a go at building one at home they could. So the first part of the project was which of the many kits available would be the best to start with.
If you type into "2WD robot car" or "4WD robot car" Ebay or some other selling site such as Amazon or Aliexpress there seem to be a couple of types that are very common so I ordered a 2WD and 4WD version to see if they would be suitable. As I was ordering two kits I just ordered a complete kit and a chassis kit as I would be able to swap the Arduino and sensors between the kits.
Once the kits arrived I started by building the 2WD version first as it was cheaper. This version only has a single base plate and it very quickly became obvious that to get the motor driver, Arduino, battery box and ultrasonic sensor to fit in the space available was going to be a tight squeeze. Not impossible but it would make the job a bit trickier. It also became obvious at this point that this was a generic kit of parts and that the holes in the base plate did not line up with the components so I would need to drill some holes. Not a problem in the workshop but it did raise some issues for youngsters. On the version I bought (cheap), the motors also needed the wires soldered onto the motors, again a simple job for anyone who can solder but it was a point to note.
Apart from the soldering and need for drilling everything else could be put together with a screwdriver and built up quite nicely. The 2WD version uses a castor wheel as a third wheel, not a problem in itself but it would mean that care was needed on the balance of the car. Also as the castor is only 25mm diameter I was concerned about it's ability to run on uneven floors.
The second build was the 4WD version, again on my cheaper version the wires needed to be soldered to the motors. Learning from the previous model I made the wires from a twin red/black flex and made the wires much longer than the supplied version. The wires need to be soldered before you put the car together. As with the 2WD version the actual assembly just requires a screwdriver.
The 4WD version I bought has two baseplates that meant I could slide the battery box between the layers and have much more space on the top deck to place items. However, as with the 2WD on this cheaper version the holes did not line up with the Arduino, motor driver or the sensor assembly so once again it looked like drilling would be required, something I really wanted to avoid.
The drilling problem gave me a bit of a problem, not only the thought of seven year olds with electric drills on a difficult material that could easily crack, but also once drilled components would be fixed in place and I wanted the ability to alter the project week by week so that we could learn how to use different sensors and control methods.
The solution came in the form of self adhesive Velcro strip. It was just a case of sticking it to the base plate and under components and was so strong I could lift the whole car by holding the Velcroed on components. Probably not a good idea, but showed me how secure it was. I now had a car that apart from soldering the motor leads could be built with little more than a screwdriver and a pair of scissors and could have components added, removed or repositioned as required.
Buying a Kit
This section deals with buying a kit, what to look out for as well as some guide prices. From the project overview it was decided that a 4WD (4 Wheel Drive) version was required so these are the kits I looked at.
It is important to note that spending more money does not always get you a better kits so below are some points to look out for.
Below is a basic kit with an indication of the parts.
As well as the kit you will require a computer of some type, Windows, Mac, Linux, Rasperry PI to program the Arduino. The computer does not need to have a high specification. If it runs the operating system is powerful enough.
Essential Items to make the car move.
Chassis Plates: NOTE the kit pictured only has one plate, look for a kit with two plates giving a top and lower deck. The car will be stronger and have more room for equipment to be fitted.
Motors and Wheels. NOTE in this kit the red/black wires centre rights need to be soldered to the motors.
Arduino: This is the brain of the robot. The one pictured is a Chinease clone board but they work exactly the same.
Motor Driver board: The board that sends the power to the motors.
Sensor Shield: makes life much easier whe attaching items. Approx £4 if bought seperately.
Dupont wires. You can never have enough of these, they make it very easy to plug modules together without soldering. Approx £6 for 80 on Ebay..
Battery box: NOTE this is a 4 x AA battery box. Look for kits with 5 x AA or 6 x AA, otherwise you will need a seperate supply for the Arduino. Bigger battery box £4 on Ebay.
Sensors to make the car more interactive
.
Ultrasonic sensor: Required for obstacle avoidance.
Servo and servo bracket. Enable the Ultrasonic sensor to rotate.
Line Following Sensors: Some kits come with these sensors, nice to have to enable more variety in building the project. Approx £5 on Ebay of bought seperately.
This next kit is slightly more advanced.(Guide price under £40)
Extras:
2 x Chassis plates for a strionger model.
Lots of Dupont wires.
Bluetooth module: Allows car to be controlled from a phone. £4 if bought seperately..
IR sensor: Allows the remote supplied to drive the car. £5 if bought seperately.
Slightly better quality Ulrasonic sensor bracket.
battery charging module. Allows the batteries to be recharged through the USB connector.
Battery boxes are designed to use 3.7v recharagable 18650 batteries.
NOTE Motor leads still need to be soldered.
The kits I ordered for the club ( £24 on aliexpress.com).
Motors have soldered leads.
Battery box holds 6 x AA and has barrel plug fitted for the Arduino.
Better quality Ultrasonic sensor mount.
Extra stand off bolts (The brass parts below the motor board). Allows more flexibility when fitting items.
The two kits arrived about 2 weeks later, very well packed and nothing missing. Motors do have the leads soldered as per the picture.
The only unexpected part was that the battery holder doesn't take AA batteries. Instead it is made for 2 x 18650 3.7v batteries.so an extra cost is about £7 for some batteries and a charger from Ebay.
Other kits.
Although many times the price of kits does not always mean you are getting a better kit, I did notice a kit on Amazon called ELEGOO Smart Robot Car Kit V4.0 for £78.
Although more expensive, the camera on it's own is about £10. Also Elegoo are a branded product and if you look at the connections they are all plug in types with pre made cables and comes with a camera, tools and assembly instructions. I would expect a kit like that to assemble without any issues with some after sale support via email.
Building the Car
Now you have the kit it's time to put it together.
I will work through the components and try and explain what they do and show how to connect them together.
In each section there will be some software code to test that the item is working. This requires NO programming ability.
I will also work though the code seperately for those who want to learn how to program the car for themselves.
Motors
As can be seen in the photos below, the motors are clamped in place by a couple of tabs that fit into the baseplate.
NOTE: If wires need soldering to the motors, do it before fitting.
WARNING: Do not overtighten or you will crack the brackets.
Chassis
Use the brass spacers to join the two bases together as in the picture below. Try to spread them out to screate the best support. Again do not overtighten.
Arduino
Now we come to the brains of the kit, the Arduino.
At the core of the Arduino UNO is a small microprocessor, it may not look much, but this is a mini computer that hs been made as user friendly as possible for those who want to control projects.Although not as powerful as your modern PC it is more than capable of running all sorts of projectsand is fast enough to run our car with room to spare.It has been developed specifically for projects that need to receive data from sensors, process it and then send commands out to attached components.
Although the Arduino UNO is often seen as the beginners Arduino, it doesn't mean it lacks power. It can be used to run most home projects from our robot car to to garden irrigations systems, weather stations, home automation and just about anything else you can imagine so anything learnt in this project can be applied to other projects.
The Arduino is fully programmable and has it's own software, the Arduino IDE, that has been developed to be as user friendly as possible. The new program is uploaded to the board by simply plugging in the USB cable and pressing the upload button.
How to download the Arduino IDE
Basics of using the Arduino IDE
Testing the Arduino, the Blink sketch.
Below is a picture of a typical Chinese copy Arduino (copies are legal) and on the left is a sensor shield that plugs on top of it. This makes it easier to attach components to the Arduino.
The two boards push together as shown, be careful not to bend the pins.
Motor Driver
Now we come to the board that the Arduino will use to control the directions of the motors and for the first time we need to start doing some wiring.
So first identify the motor driver board, it should look something like this.
You will now need to attach the board, the Arduino and battery box to your car. If you want to avoid drilling just use self adhesive velcro as shown below. Even though the surface is not smooth it still is enough to hold the board in place. I did the same with the battery box and the Arduino. Obviously if your kit has screw holes in the right place then use them.
Once you have the items in place it's time to wire it all up.
The diagram below shows how to wire the car up
The four wires from the motor driver go to the sensor shield pins 8 - 11 on the terminal on the "S" (signal) line.
The 5th wire just goes to ground (any pin with a "G" or GND symbol.
.
NOTE: Make sure the connectors are between the two pins at the end of the row on the motor driver board.
For the initial test the Arduino will get it's power from the USB cable
Download the code from the L298N basic circuit tutorial to test your wiring.
At this point your motors should have turned and you can check your wiring.
For further control Example 2 on the L298N tutorial allows you to easily create a pattern for the car to follow. You can alter the function order at the bottom of the sketchto give your car a series of instructions in any order and time interval that you wish.
HC-SR04 Ultrasonic Distance Sensor
This is the unit that will allow the car to sense objects in the way of the car.. The sensor is connected as in the diagram below.
Once you have connected the sensor as shown above you can download the Example 1 code from HC-SR04 Ultrasonic Sensor
If you want to learn how to program the sensor follow the video tutorial, otherwise just download the code and upload it to your Arduino. Open the Serial monitor and move your hand in front of the sensor to make sure it is working.
Servo
The servo is attached to a bracket at the front of the car that has the HC-SR04 Ultrasonic Distance Sensor mounted to it. Befofre attaching the servo to the bracket run the Example 2: Servov2set90.ino sketch to test the servo and set the servo in a central position.
The wiring diagram is shown below.
If you want to know more about how to program the servo follow the video tutorial on the Servo lesson.
Putting it all together
At this point in the project we have the ability to control the cars direction using the motors, we can sense an objects distance from the car using the HC-SR04 Ultrasonic Distance Sensor and we can move the sensor in different directions by attaching it to the servo bracket.
If you have followed the tutorials above you will have the following pins in use:
Pins | Component |
6,7 | HC-SR04 Ultrasonic Distance Sensor |
8,9,10,11 |
L298N Motor Driver Board |
12 |
Servo Control |
My Car at this stage is shown below, I had to swap the Arduino and L298N Motor Driver board positions as otherwise the wires from the HC-SR04 Ultrasonic Distance Sensor to the Arduino sensor shield did not reach so my idea of attaching the components with velocro has worked out well for now.
The car is now ready for final programming, but based on the lessons so far will need to work within the limitations of only having a single speed for forwards or reverse as well as a single speed cornering ability.
The next section is a video tutorial on the issues of obeject detection and explains the issues that we will face at this point. It is not required to finish the car as the final code will be avaialble for download below the tutorial..
Arduino Object Detection Overview
It's very easy to rush into writing code without thinking through how things work physically. As such our code can work great in theory, yet fail when it is used in a real world scenario.
So the first part to designing our code is to understand the issues and limitations around the components and sensors we are using.
The components we are using are a drive system built around the L298N motor driver board.
We also have a HC-SR04 Ultrasonic Distance Sensor that is mounted on a Servo to enable us to point the sensor in different directions.
Diagram 1
The HC-SR04 Sensor has a detection angle of about 15 degrees. You can test this using the test sketch on the HC-SRO4 Ultrasonic Distance Sensor page on the Digital Town website.
The issue is that the sensor in a static position only covers the full width of the car at a distance of 400mm, below that distance it would be quite easy for something like a chair leg to fall into the un-sensed region.
Also we would have no idea at this point if the object is directly in front of us or to one side so would have no way of working out the best way to turn to avoid the item
So using a single fixed sensor we would need to sense an object at around 600mm to allow us time to slow down or stop and then turn the sensor to the left and then the right to try and work out a clear path.
We could do this by turning the car to the left and seeing if the distance reading increased or decreased and then do the same turning to the right. Although this method could be made to work it would probably be best to stop the car and then turn the motors on one side forwards and the other side backwards to force the car to skid round on the spot until it found a route without an obstacle and still runs the risk of an object not being detected in the un-sensed region. As such turning the sensor using the servo is probably the best option.
Diagram 2
The normal solution provided for us is to mount the HC -SR04 Ultrasonic Distance sensor onto a servo mount so that is can be turned.
The issue with moving the servo is that it takes time so to loop round the three positions would probably take about 1 second. It doesn't sound long, but our car may have moved quite a distance in that time.
However, the advantage is that the width of the car is now sensed to a distance of 200mm and if we stop the car and check each position in turn we will know which side the obstacle is on and have a better idea of how to turn to avoid it.
Diagram 3
In an ideal world situation it would be nice to be able to take more readings as shown in this next diagram. The car can now sense objects at about 100mm, however, as it would take about 2 seconds to work backwards and forwards through the 7 positions we would need to make sure the car slowed down as soon as any position picked up an object. It's a similar dilemma to Diagram 2, but with greater accuracy on finding an avoiding route.
Problems identified
The problems faced by Diagram 2 and 3 is down to the speed of the servo rather than the speed of the Distance Sensor.
Diagram 4
In this diagram we see 3 sensors in fixed positions. Because they don't need to move the only limitation to how fast we can cycle through them is literally down to the speed of sound, making sure that any echo has returned before firing the next sensor. In reality we could easily cycle through them in 100 milliseconds (0.1 seconds) and may even be able to drop down to 20 milliseconds (0.02 seconds) so this technique would give us very fast sensing speeds combined with the ability to sense objects down to about 100mm as well as knowing the approximate position of the object to allow us to change the cars direction.
It is why this type of solution is used in so many cars for reversing sensors as can be seen in the bumper image that shows 4 sensors that have overlapping fields of coverage.
Solutions
So now the issues and limitations of the hardware are understood what is the best solution.
In a perfect world it would be great to be able to have fixed sensors that could be cycled through quickly as in the reversing sensor on a car. However, the reversing sensor does have something our car does not, it has a driver who can steer the car away from an obstacle.
For our car, not only do we need to find the obstacle, but then find the best direction to manoeuvre around and away from it.
I think the ideal solution would be using three sensors in static positions as in diagram 4, but having the centre sensor still mounted on the servo bracket so that if the car is struggling to avoid a big obstacle, maybe a wall that is blocking all sensors, it could stop and turn the centre sensor to find the best route out and it is a technique I will try at a later date.
However, as our kits tend to come with a single distance sensor the best solution will be to cycle through a small number of points, probably between 3 and 5 at about 5 degree intervals to keep the process fast, maybe 4 is the best number based on the car reversing sensor set up. Then if the car gets stuck, once again stop it and move the sensor through a larger range of positions until it finds the best escape route. It is this solution that we will look to program first.
Example 1: RobotCarSingleSensorv1.ino
Video code tutorial
Click to Download code:RobotCarSingleSensorv1.ino
Basic Obstacle avoiding Robot Car sketch based on previous lessons, watch the video for full explanation.
/* 28/08/2021
RobotcarSingleSensorv1
Sketch Goal
To create a sketch that combines the L298N motor driver, a servo and a HC-SR04 Ultrasonic Distance sensor
to create a basic obstacle avoiding car.
This sketch will use elements learnt from:
L298Nv2BasicControlFunctions.ino at http://www.digitaltown.co.uk/components2L298NMotorDriver.php
HCSR04UltrasonicDistanceSensorv2Function.ino at http://www.digitaltown.co.uk/components3HCSR04UltrasonicSensor.php
Servov1.ino at http://www.digitaltown.co.uk/components4servo.php
Sketch will also use Arrays, the lessons on this can be found at http://www.digitaltown.co.uk/lesson11Arrays.php
For wiring diagrams see http://www.digitaltown.co.uk/project2RobotCar.php#Building
Arduino UNO
pins used:
6 Echo HC-SR04 Ultrasonic Distance Sensor
7 Trigger HC-SR04 Ultrasonic Distance Sensor
8 L298N Motor Driver Board pin IN1
9 L298N Motor Driver Board pin IN2
10 L298N Motor Driver Board pin IN3
11 L298N Motor Driver Board pin IN4
12 Servo control
for this version the sensor will move 7 degrees either side of the centre position
to take a distance reading as well as the centre position.
If the car gets stuck or objects are too close the car will stop and take a set of
readings at 10 degree intervals and work out the best route based on the return values.
*/
//Global variables
//Servo
const int myServoPin = 12;//constant integer that will not change
//L298N Pins
const int leftForwardPin = 10;
const int leftReversePin = 11;
const int rightForwardPin = 8;
const int rightReversePin = 9;
//HC-SR04 Ultrasonic Distance Sensor pins
const int triggerPin = 7;
const int echoPin = 6;
//Othe global variables
//To start with the car will stop if it gets closer than 400mm to an object
const int minimumDistance = 400;
//Array holding the 3 positions the servo will move to at 7 degree intervals while the car is moving.
int runningScanPositions[3] = {83, 90, 97};
//Array holding the 13 positions the servo will move to while the car is stopped at 10 degree intervals.
int stoppedScanPositions[13] = {30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150};
//Integer that will hold the current position of the servo
int scanPosition;
//The array that will hold the distances of the car when running
int scanRunDistances[3];
//The array that will hold the distances of the car when stopped
int scanStopDistances[13];
int okToDrive = 0;//0 = stop 1 = no obstacle detected
//include the servo library
#include "Servo.h"
//Create a servo object. In this example we will call the servo "myServo".
Servo myServo; // create servo object to control a servo
//Motor functions from L298Nv2BasicControlFunctions.ino
//function to make the left motors run forwards
void forwardRight() {
Serial.println("left motor forwards, turn right");
digitalWrite(leftForwardPin, HIGH);
//Making the pin LOW means we turn off the 5v and the pin returns to 0v
digitalWrite(leftReversePin, LOW);
//Right motor stopped, both pins low
digitalWrite(rightForwardPin, LOW);
digitalWrite(rightReversePin, LOW);
}
//function to make the left motors run in reverse
void reverseRight() {
Serial.println("Reverse left motor, reverse right");
digitalWrite(leftForwardPin, LOW);
digitalWrite(leftReversePin, HIGH);
//Right motor stopped, both pins low
digitalWrite(rightForwardPin, LOW);
digitalWrite(rightReversePin, LOW);
}
//function to make the right motors run forwards
void forwardLeft() {
Serial.println("right motor forwards, forwards left");
digitalWrite(rightForwardPin, HIGH);
//Making the pin LOW means we turn off the 5v and the pin returns to 0v
digitalWrite(rightReversePin, LOW);
//left motor stopped, both pins low
digitalWrite(leftForwardPin, LOW);
digitalWrite(leftReversePin, LOW);
}
//function to make the right motors run in reverse
void reverseLeft() {
Serial.println("Reverse right motor, reverse left");
digitalWrite(rightForwardPin, LOW);
digitalWrite(rightReversePin, HIGH);
//left motor stopped, both pins low
digitalWrite(leftForwardPin, LOW);
digitalWrite(leftReversePin, LOW);
}
//this function calls both stop functions together
void stopMotors() {
Serial.println("Stop motors");
digitalWrite(rightForwardPin, LOW);
digitalWrite(rightReversePin, LOW);
digitalWrite(leftForwardPin, LOW);
digitalWrite(leftReversePin, LOW);
}
//calls both forward functions
void driveForward() {
Serial.println("Both motors forwards");
//Both motors forwards pins HIGH
digitalWrite(rightForwardPin, HIGH);
digitalWrite(rightReversePin, LOW);
digitalWrite(leftForwardPin, HIGH);
digitalWrite(leftReversePin, LOW);
}
//calls both reverse functions
void driveReverse() {
Serial.println("Both motors reverse");
//Both motors reverse pins HIGH
digitalWrite(rightForwardPin, LOW);
digitalWrite(rightReversePin, HIGH);
digitalWrite(leftForwardPin, LOW);
digitalWrite(leftReversePin, HIGH);
}
//End of L298N functions
//Distance function from HCSR04UltrasonicDistanceSensorv2Function.ino
//It receives the pins of the sensor and send back the distance
int getDistanceHCSSR04(int funcTriggerPin, int funcEchoPin) {
//An unsigned long is needed because the numbers produced can be much larger
//than an int can hold (max value 32,767)
//Local variables
unsigned long duration; //time taken for echo
int myDistance;
//make sure Trigger is low to start
digitalWrite(funcTriggerPin, LOW);
//A tiny delay 2 millionths of a second
delayMicroseconds(2);
// Sets the trigPin HIGH (ACTIVE) for 10 microseconds
digitalWrite(funcTriggerPin, HIGH);
//pin stays HIGH (5v) for 10 microseconds to trigger a pulse
delayMicroseconds(10);
//Pin taken LOW (0v) after pulse triggered ready for next pulse
digitalWrite(funcTriggerPin, LOW);
duration = pulseIn(funcEchoPin, HIGH);
myDistance = duration * 0.34 / 2;
return myDistance;
}
//This will find the best direction to move the car in when stopped
//Once found it will generate a short turn manoeuvre
void findStoppedBestDirection() {
Serial.println("Doing Full Scan");
//Local variables
int steerDelay = 10;//alter this variable to turn more or less
//stores the best direction found
int bestDirection = 0;
//Leeps track of the longest distance found in this scan routine
int longestDistance = 0;
//Move through the array of scan positions
for (scanPosition = 0; scanPosition < 13; scanPosition++) {
myServo.write(stoppedScanPositions[scanPosition]);
//the delay must be long enough to allow the servo to move to it's next location before it takes a distance reading.
//Otherwise it will try to take a reading while still moving.
delay(250);
//Put the Answer in the array
scanStopDistances[scanPosition] = getDistanceHCSSR04(triggerPin, echoPin);
//this "if" added due to odd results giving a value of 20000+, possibly because of the surfaces I had surrounding the car
//Possible echo as it's when something is close
if(scanStopDistances[scanPosition] > 5000){
scanStopDistances[scanPosition] = 0;
}
Serial.println(scanPosition);
Serial.println(scanStopDistances[scanPosition]);
//if the distance just scanned is greater than the best distance so far change the value to the new value
if (scanStopDistances[scanPosition] > longestDistance) {
//Update the longest distance
longestDistance = scanStopDistances[scanPosition];
//update the bestDirection
bestDirection = scanPosition;
}
}
//At this point the scanning process is over
//If the longest distance found was less than 200mm (so no good route out found), try a short reverse
//trying 200 as 100 caused crashes...couldn't brake in time
if (longestDistance < 200) {
//reverse for 0.3 seconds
driveReverse();
delay(300);
stopMotors();
} else { //otherwise decent escape route found turn to the direction
//if a route has been found turn towards it.
Serial.print("Best direction: ");
Serial.println(bestDirection);
//the switch statement does a turn with an appropriate delay based on the best direction angle
switch (bestDirection) {
case 0://60 degrees right
forwardRight();
delay(steerDelay * 60); //delay based on angle as will take longer to turn
break;
case 1: //50 degrees right
forwardRight();
delay(steerDelay * 50);
break;
case 2: //40 degrees right
forwardRight();
delay(steerDelay * 40);
break;
case 3: //30 degrees right
forwardRight();
delay(steerDelay * 30);
break;
case 4: //20 degrees right
forwardRight();
delay(steerDelay * 20);
break;
case 5: //10 degrees right
forwardRight();
delay(steerDelay * 10);
break;
case 6://centre
driveForward();
delay(20);
break;
case 7://10 degrees left
forwardLeft();
delay(steerDelay * 10);
break;
case 8://20 degrees left
forwardLeft();
delay(steerDelay * 20);
break;
case 9://30 degrees left
forwardLeft();
delay(steerDelay * 30);
break;
case 10://40 degrees left
forwardLeft();
delay(steerDelay * 40);
break;
case 11://50 degrees left
forwardLeft();
delay(steerDelay * 50);
break;
case 12://60 degrees left
forwardLeft();
delay(steerDelay * 60);
break;
default:
stopMotors();
Serial.println("steering error");
break;
}
}
//Once the function is complete the code returns to the main loop.
}
void setup() {
// Serial.begin(9600) starts serial communication.
Serial.begin(9600);
//Send script name
Serial.println("RobotCarSingleSensorv1...");
Serial.println(" ");
//L298N Pins
Serial.println("L298N Pins set");
pinMode(leftForwardPin, OUTPUT);
pinMode(leftReversePin, OUTPUT);
pinMode(rightForwardPin, OUTPUT);
pinMode(rightReversePin, OUTPUT);
//HC-SR04 Ultrasonic Distance Sensor pins
Serial.println("HC-SR04 Ultrasonic Distance Sensor pins set");
pinMode(triggerPin, OUTPUT); //sends signal
pinMode(echoPin, INPUT); //receives signal
//Servo
//Servo object attached to a pin
myServo.attach(myServoPin);
Serial.println("Testing Servo");
myServo.write(30);
delay(500);
myServo.write(150);
delay(500);
myServo.write(90);
delay(500);
Serial.println("Servo Ready");
//now proceed to the main loop
}
void loop() {
okToDrive = 1; //set to Drive
//run through the three scanning positions that are used while the vehicle is in motion
for (scanPosition = 0; scanPosition < 3; scanPosition++) {
//Move to the next servo position
myServo.write(runningScanPositions[scanPosition]);
//wait 0.1 secons for the servo to move (only moving a short distance so short wait)
delay(100);
//take a disance reading
scanRunDistances[scanPosition] = getDistanceHCSSR04(triggerPin, echoPin);
Serial.println(scanRunDistances[scanPosition]);
//check if the distance scanned has seen an obstacle nearby
if (scanRunDistances[scanPosition] < minimumDistance) {
//If something is blocking set okToDrive to 0... the car will stop.
okToDrive = 0;
Serial.println("Normal path blocked");
}
}
//if obstacles detected do full scan.
if (okToDrive < 1) {
//Stop the motors
stopMotors();
//use the scan function to find the best position to go in
findStoppedBestDirection();
} else {//if no obstacle is found... okTodrive = 1
//keep going until obstacle found
driveForward();
}
}
Upgrades v1 A Change of Battery
As with all projects, once you start to use something you quickly find any weaknesses.
The main weakness I found was in the battery department. 6v was just not enough to power all 4 motors and run the Arduino. I therefore added a 5 x AA battery holder that gave 7.5v and found that worked very well although I would advise using rechargable batteries as it quickly works it's way though standard AA batteries.
This also meant swapping the batteries to the back of the car as the leads seemed to be a bit shorter.
You will see from the photos that I soldered a lead from the power input to the motor board to a barrel plug that takes the power to the Arduino.
Upgrades v2 TB6612FNG motor controller, 3 x Ultrasonic sensors, servo removal, addition of seperate 9v battery
This was quite a substantial Upgrade.
The first version worked but was slow in changing direction or finding a new route. It was also heavy on batteries.
v2 Upgrades
TB6612FNG motor controller: Better battery life combined with PWM motor control to allow for more subtle changes in direction. The TB6612FNG is a more more energy efficient motor controller meaning it can get more out of the batteries.
3 x Ultrasonic Sensors: This removed the need for the servo and meant I could check distances much faster without having to wait for the servo to move.
Better Ultrasonic Sensor Brackets: Sensors fit much better and easier to attach to the car.
Seperate 9v battery: The Arduino seemed to have some weird moments when everything was plugged in, this semed to come down to the power drain and voltage drop on the batteries so I installed a seperate 9v rechargable battery just for the Arduino.
New code: Allows for more subtle changes of direction so it swerves out of the way of an obect and then continues on course rather than stopping and making a sharp turn.PWM means turns are gradual but also the car slows down on one set of motors gradually rather than just stopping.
The new wiring diagram
Example 2: RobotCar3SensorsPWMTB6612FNGv2.ino
Click to Download code:RobotCar3SensorsPWMTB6612FNGv2.ino
New components required a rewrite of the code. This uses PWM for motor control giving gradual speed and direction changes.
It also removes the servo code and has 3 ultrasonic sensors.
/* RobotCar3SensorsPWMTB6612FNGv2.ino
Mod to change turns, turns too much in v1
This car uses 3 ultrasonic sensors
Motor controller is TB6612FNG
PWM to control speed
This worked but seemed to have weird issues with the sensors not registering correctly...PWM pulsen() conflict?
Issue fixed by having seperate battery for the arduino
This version uses all the main usable pins
Pins
2 Echo right
3 trigger right
4 Echo centre
5 PWM left
6 PWM right
7 Trigger centre
8 rightForwardPin
9 rightReversePin
10 leftForwardPin
11 leftReversePin
12 Echo left
13 Trigger left
*/
//TB6612FNG pins
/*
const int leftForwardPin = 10;
const int leftReversePin = 11;
const int rightForwardPin = 9;
const int rightReversePin = 8;
const int rightPWMPin = 5;
const int leftPWMPin = 6;
*/
//reconfigured after reversing bottom plate due to crash
const int leftForwardPin = 11;
const int leftReversePin = 10;
const int rightForwardPin = 8;
const int rightReversePin = 9;
const int rightPWMPin = 6;
const int leftPWMPin = 5;
//HCSR04 Ultrasonic sensors
const int triggerLeft = 13;
const int echoLeft = 12;
const int triggerCentre = 7;
const int echoCentre = 4;
const int triggerRight = 3;
const int echoRight = 2;
//set drive condition
int pwmSpeed; //0 = stop 255 is full ahead
int leftSpeed;
int rightSpeed;
int pwmAdjuster; //stores direction differential
unsigned long speedTimer;
//canning sensors
int scanDistances[3];//holds the distances scanned 0 = left, 1 = centre 2 = right
int scanDirection = 0;//0 = 0, 1, 2 1 = 2,1,0
int scanPosition = 1;//hold the step scans have got to
//speed of sound is 343m/s = 0.003 seconds per metre
//for sound to travel 20 meters = 0.06 seconds...safe gap between triggering pulses = 60 milliseconds
const int scanGap = 100;//changed to 120, seemed to stop odd readings
unsigned long scanTime;
int goBackwards;
void doScans() {
int getDistance;
if (millis() > scanTime) {
switch (scanPosition) {
case 0:
getDistance = getDistanceHCSSR04(triggerLeft, echoLeft);
if (getDistance > 1000) {
getDistance = 1000;
}
if (getDistance < 10000) {
scanDistances[scanPosition] = getDistance;
}
break;
case 1:
getDistance = getDistanceHCSSR04(triggerCentre, echoCentre);
if (getDistance > 1000) {
getDistance = 1000;
}
if (getDistance < 10000) {
scanDistances[scanPosition] = getDistance;
}
break;
case 2:
getDistance = getDistanceHCSSR04(triggerRight, echoRight);
if (getDistance > 1000) {
getDistance = 1000;
}
if (getDistance < 10000) {
scanDistances[scanPosition] = getDistance;
}
break;
default:
Serial.println("scan error default");
break;
}
if (scanDirection < 1) { //if scanning left to right
scanPosition++;
if (scanPosition > 1) {
scanDirection = 1;//change the scanning direction
}
} else { //scanning right to left
scanPosition--;
if (scanPosition < 1) {
scanDirection = 0;//change the scanning direction
}
}
scanTime = millis() + scanGap;
}
}
//Distance function from HCSR04UltrasonicDistanceSensorv2Function.ino
//It receives the pins of the sensor and send back the distance
int getDistanceHCSSR04(int funcTriggerPin, int funcEchoPin) {
//An unsigned long is needed because the numbers produced can be much larger
//than an int can hold (max value 32,767)
//Local variables
unsigned long duration; //time taken for echo
int myDistance;
//make sure Trigger is low to start
digitalWrite(funcTriggerPin, LOW);
//A tiny delay 2 millionths of a second
delayMicroseconds(2);
// Sets the trigPin HIGH (ACTIVE) for 10 microseconds
digitalWrite(funcTriggerPin, HIGH);
//pin stays HIGH (5v) for 10 microseconds to trigger a pulse
delayMicroseconds(10);
//Pin taken LOW (0v) after pulse triggered ready for next pulse
digitalWrite(funcTriggerPin, LOW);
duration = pulseIn(funcEchoPin, HIGH);
myDistance = duration * 0.34 / 2;
return myDistance;
}
void setup() {
Serial.begin(9600);
Serial.println("RobotCar3SensorsPWMTB6612FNGv2");
//Motor driver pins
pinMode(leftForwardPin, OUTPUT);
pinMode(leftReversePin, OUTPUT);
pinMode(rightForwardPin, OUTPUT);
pinMode(rightReversePin, OUTPUT);
pinMode(rightPWMPin, OUTPUT);
pinMode(leftPWMPin, OUTPUT);
digitalWrite(leftForwardPin, HIGH);
digitalWrite(leftReversePin, LOW);
digitalWrite(rightForwardPin, HIGH);
digitalWrite(rightReversePin, LOW);
//ultrasonic pins
pinMode(triggerLeft, OUTPUT);
pinMode(echoLeft, INPUT);
pinMode(triggerCentre, OUTPUT);
pinMode(echoCentre, INPUT);
pinMode(triggerRight, OUTPUT);
pinMode(echoRight, INPUT);
//allow 3 sec to get initial readings and to let go of car
speedTimer = millis() + 3000;
}
unsigned long testTimer;
void loop() {
doScans();
if (millis() > testTimer) {
Serial.print(scanDistances[0]);
Serial.print(" : ");
Serial.print(scanDistances[1]);
Serial.print(" : ");
Serial.println(scanDistances[2]);
Serial.print("pwmSpeed: ");
Serial.println(pwmSpeed);
Serial.print("leftSpeed: ");
Serial.println(leftSpeed);
Serial.print("rightSpeed: ");
Serial.println(rightSpeed);
testTimer = millis() + 500;
}
if (millis() > speedTimer) {
//speedTimer = millis() + 3;//larger number slows direction changes
speedTimer = millis() + 6;//larger number slows direction changes
pwmSpeed = scanDistances[1] - 255;//This should cause car to decelerate below 500mm
if (pwmSpeed > 255) {
pwmSpeed = 255;//above 500 mm go flat out
}
if (scanDistances[0] < 200 && scanDistances[1] < 200 && scanDistances[2] < 200) { //stuck
goBackwards = 1; //try reverse spin
}
//v2 mod
//if (scanDistances[0] < 800 || scanDistances[2] < 800) {
if (scanDistances[0] < 500 || scanDistances[2] < 500) {//don't react until closer
if (scanDistances[0] > scanDistances[2]) {
pwmAdjuster++;
} else {
pwmAdjuster--;
}
} else {
pwmAdjuster = 0;
}
if (pwmAdjuster > 200) {
pwmAdjuster = 200;
}
if (pwmAdjuster < -200) {
pwmAdjuster = -200;
}
leftSpeed = pwmSpeed + pwmAdjuster;
rightSpeed = pwmSpeed - pwmAdjuster;
if (leftSpeed > 255) {
leftSpeed = 255;
}
if (rightSpeed > 255) {
rightSpeed = 255;
}
if (leftSpeed < 120) {
leftSpeed = 0;
}
if (rightSpeed < 120) {
rightSpeed = 0;
}
if (leftSpeed == 0 && rightSpeed == 0) {
goBackwards = 1;
}
if (goBackwards < 1) {//forwards
digitalWrite(leftForwardPin, HIGH);
digitalWrite(leftReversePin, LOW);
digitalWrite(rightForwardPin, HIGH);
digitalWrite(rightReversePin, LOW);
analogWrite(leftPWMPin, leftSpeed);
analogWrite(rightPWMPin, rightSpeed);
} else {//stuck manouvere
digitalWrite(leftForwardPin, LOW);
digitalWrite(leftReversePin, HIGH);
digitalWrite(rightForwardPin, LOW);
digitalWrite(rightReversePin, HIGH);
analogWrite(leftPWMPin, 200);
analogWrite(rightPWMPin, 200);
delay(200); //reverse
digitalWrite(leftForwardPin, HIGH);
digitalWrite(leftReversePin, LOW);
delay(800); //spin
analogWrite(leftPWMPin, 0);
analogWrite(rightPWMPin, 0);
goBackwards = 0;
speedTimer = millis() + 1000;
}
}
}
Additional Resource Links
Don't forget to use the Reference in you Arduino IDE and look at the Control Structures and Comparison Operators for more help on this lesson.Comments
This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site. To add a comment or ask a question please email the address in this image: and use Project 2 - Arduino Robot Car as a reference.