Sure, a robot that cooks you dinner or can walk the dog is great, but before our robotics skills can run, they must walk (or indeed, roll). So let’s start with the basics and build the "Somewhat Autonomous Machine", affectionately known as SAM.
In the maker space, there’s not a lot cooler than robotics, but if you’re just starting out where do you even begin? Robotics is a very broad term; traditional sci-fi has us envision bi-pedal android type robots that can be easily confused with humans, but the reality is that a robot can be a very simple machine. A true robot, in fact, is anything your imagination can envision.
The boss here at DIYODE Magazine said we needed a robot. I agreed and so produced my plans for an epic, tank track-driven, flame-throwing robot destined to destroy all other robots in its path. We discussed it and came to a compromise. It would be the same robot as my original design just without the tank tracks, or the flame thrower, or the path of destruction for other robots. What we did agree on was the need to create an initial platform that could serve as a base for understanding the fundamentals of robotics.
The truth is that robots are designed to do tasks that we do not want to do, to go where we cannot, or operate in environments that present danger. They are designed to be as efficient and productive as possible in doing that designated task. For our robot the task here is simple: roam around the room and don’t hit the walls, chairs, feet or the dog.
Fundamentally any robot exists as a system. The system is a set of inputs and outputs with a decision making process in the middle. Arguably you could set your robot on a predefined pathway assuming nothing will go outside of your control. However, this could very easily end in a result that could damage your robot or yourself (the author was attacked several times during the making of this article due to a preprogrammed robot that was having new code uploaded on the workbench, which lurched it into action). We need to ensure that we have a series of feedback loops in place to be sure that the task is carried out correctly.
INPUTS
Any system or robot will require various inputs to be evaluated before making decisions. Inputs can be simple or advanced, from pressure, air speed, temperature, angle, G-force or anything else you can measure. In this case, we are using distance as our input metric. We could archive this through several means, but we will be using a cost-effective and easily obtainable ultrasonic transducer.
Process
The robot needs a brain; it needs to be able to take the information from the inputs, evaluate it and then perform an action of outputs, based upon that decision. This could be as simple as a yes/no logic, such as “do X until Y happens, then stop” or it could be a much more complex fuzzy logic system that will make various decisions based on several inputs or system states.
Output
The system needs to do something. This is the output, and it could be as simple as turning on an LED (or activating a flame-thrower). You may then evaluate this output (as an input) and determine if the action has happened. The process will continue to run, monitor, evaluate and loop.
As you can see from the diagram below, the system follows a very simple strategy for obstacle avoidance. This is designed to provide very simple functionality to demonstrate the principles in this project. However they're very "yes or no" binary decisions.
What we have described here are feedback loops. Anything that goes out must come back. Feedback loops serve to give us constant evaluation and defined processes. Traditional logic is very concrete. States only exist as true or false, high or low, 0 or 1; but fuzzy logic concerns itself with degrees of truth. For example, how do we define tall or short? It could be as simple as “if something is bigger than a certain number, then it is tall”. But tall will be relative to what is short, within a defined pattern set. We need to understand the natural environment that our robot exists in and determine how it should react given a set of inputs.
THE BROAD OVERVIEW
This robot is quite simple but still requires some pre-planning. Upon power up the robot will drive itself forward. It does this by switching both sets of drive motors in a particular direction. As the code loops, it uses the ultrasonic sensor to send out a pulse to which it measures the distance of any object in front. Once an object is detected, it stops the robot and enters a conditional section of code that sends it in the opposite direction, then manoeuvres to switch its direction. Once complete it reverts to the original routine and moves in a forward direction until it encounters another obstacle.
HOW IT WORKS
THE UNO BRAIN: In this instance, the Arduino UNO is our microprocessor of choice, as it is a simple, open source and a low-cost microprocessor. It also has the power required for many of our functions.
ULTRASONIC DISTANCE SENSOR: Our input device for this build is the HC-SR04 Ultrasonic module. This is one of the most simple and cost-effective methods for measuring distance; however, it comes with some caveats. The Ultrasonic works by sending out a timed soundwave well beyond what the human ear can hear. If there is an object in front of the sound wave, it is reflected off the object. This bounceback is received by the unit and the time taken for this to occur is then calculated using the speed of sound, to work out how far away the object is.
So how do we calculate the distance? The speed of sound is 0.034cm/µs or 340m/s so if the object is 10cm away it will take 294microseconds. However, what you get back from the echo pin will be double that number, due to the time it takes for the sound wave to return to the unit. To get the distance in centimetres, we need to multiply the received travel time value from the echo pin by 0.034 and then divide it by 2. By using this calculation against the known speed of sound, we effectively gain a distance measurement.
PROBLEMS: The Ultrasonic modules work well when you have a large surface to reflect off, and the unit is perpendicular to the object. If the object is smaller, it will need to be closer before the sound wave will reflect and can be calculated correctly. If the unit is at an angle, then the reflection does not bounce back at all, and it will often require the robot to be incredibly close before it receives the information it requires to calculate the distance.[1]
L298N Motor Controller
For this project, we will be using the L298N Dual H Bridge package. In this instance, it is the duinotech, XC4492, which is an inexpensive module that makes utilisation incredibly simple. It operates as a two-channel device, which can let us operate the left and right pairs of motors. You can easily use two or four motors. There are versions of the L298N modules that have two chips allowing you to create four channels; in the case of our robot this would enable independent four wheel drive. However, for our current purpose this would be overkill, so we will use two channels and enable skid-steer or tank track-style direction changes.
The L298N works by sending a PWM signal (from 0-255) to the enable pin of the channel you are using. It then specifies the direction required by sending the two other pins high, low or a combination of both. This is repeated for the second channel, thus requiring six outputs from the UNO to control the motors.
On top of the standard VCC and ground, the L298N has a second voltage input VMS, which is used to supply the power for the motors. Each channel requires three inputs: the ENA and the IN1 & IN2 in the case of motor A.
MOTOR OUTPUTS: This project could easily be built with two or four motors driving the unit. The 4WD is not truly independent; it could be achieved by using a four-channel motor driver, then providing a sensor on each wheel for detecting slippage. Power could be removed or decreased to that wheel and apply more power to other wheels.
For this build, we will be using basic DC hobby motors. Each motor is driven by the control module with simple DC. They are connected to a small plastic gear box, which is connected to each wheel.
THE BUILD
Parts Required: | Jaycar | Altronics |
---|---|---|
1 x Arduino UNO | XC4410 | Z6240 |
1 x Ultrasonic Module | XC4442 | Z6322 |
1 x L298n Motor Control Module | XC4492 | Z6343 |
1 x Chassis & Motor Kit | KR3162 | K1092 |
1 x 8AA Battery Holder | PH9209 | S5034 |
8 x AA Batteries | SB2333 | S4955B |
We have also created a 3D printable bracket to cradle the Arduino and Motor Control module, as well as mount the ultrasonic sensors. This isn't essential for operation, so don't worry if you don't have a 3D printer. However it does make setup easier.
No breadboard is required, but you'll need prototyping jumper wires for most of the connections. Depending on your 4-wheel chassis kit, you may also need to solder wires to your motors. They usually come with everything you need already supplied, but in various states of completion. A few minutes with a soldering iron will solve any issue.
THE 3D PRINTABLE BRACKET
The 4-wheel chassis you're using may not be the same of ours, or you may build your own entirely. For this reason, we created a simple 3D-printable caddy. This caddy holds the Arduino UNO and motor controller so there's no strain on the wiring. Since there's not really much strain on any components here, you can simple use double-sided tape to mount the caddy to the 4-wheel chassis.
More importantly however, it provides a secure mount for the ultrasonic sensors. Since these provide our only sensory interface to the real world in our basic code, it's important that they're mounted correctly. If they're pointing up or down, you'll have problems with getting ultrasonic reflections back and your robot won't know when to stop (and will basically keep trying to go forward until the batteries go flat).
We have provided the .stl file for 3D printing in the digital resources along with the code. Feel free to modify this however you like. It's very likely that Part 2 of this system will have a very different caddy too, to support whatever system we add next.
BUILDING THE CIRCUIT
There are no external discrete components required. If you are using our printed chassis, we recommended fitting the US module first, as different printers will handle vertical holes differently. We would recommend using support structures, then clean, and file where appropriate. Connect the trig pin from the US to pin 11, and echo to pin 3. VCC to 5V and GND to GND on the UNO.
DUAL MOTOR DRIVER | UNO |
ENA | 10 |
IN1 | 9 |
IN2 | 8 |
ENB | 5 |
IN3 | 7 |
IN4 | 6 |
trig | 11 |
echo | 3 |
US GND | UNO GND |
L298N GND | UNO GND |
Using jumpers connect the following from the UNO to the L298N.Connect the other GND to the ground on the L298N board.
The motor’s rotation will be dependent on the polarity of the power applied. We colour-coded each channel when soldering the wires on. If using two motors then connect each one to the motor to A and B screw terminals. If you are using four motors, then connect each pair as per the diagram [2], before connecting to the screw terminals.
The system will power up on as little as 6V; however, we would not recommend it. During testing, we found that once the power was applied to the motors, the board logic would become erratic and inconsistent. Applying 12V (8xAA) solved all these issues instantly.
If your L298N board has the 5V_EN jumper I would recommend removing it while connecting power. This will essentially disable the motor outputs. Connect your GND wire from the power source to the GND screw terminal pin on the L298N board. Then connect positive power to the VMS terminal; you will also need to connect this power to the Vin on the UNO. The unit should power up if you removed the 5V_EN jumper then reconnect it - but be warned; the motors will power up straight away!
It would be quite simple to add a small switch to the enable pins on the motor controller, or inline with the power supply to the Arduino. Either would make it easy to halt all movement.
THE CODE
The code is quite simple. Once compiled it should be ready to go. We have provided you with sam_v1.ino in the digital resources. Load the sketch onto your UNO board.
While you don't really need to know precisely what we're doing in the code, our goal with SAM is to help you understand precisely what's happening. For that reason, we'll take you through some of the important parts of the code. Understanding the code will also help you with debugging should your SAM not work perfectly first go, as most pins are explicitely noted in the code too.
Firstly we need to set up the input and output required, for the ultrasonic sensor and the motor driver. The only variables you need to change are ob_distance, which is the distance at which the robot will stop and turn. The next is motorSpeed. This is a value of 0-255, with 0 being 0% and 255 being 100%. This will be dependent on your power and motors, so be sure to adjust as required.
//Setup the Ultrasonic Sensor
int trigPin = 11;
int echoPin = 3;
//Setup Motordriver
//Motor A
int ENA = 10; // MCU PWM Pin 10 to ENA
int IN1 = 9; // MCU Digital Pin 9 to IN1
int IN2 = 8; // MCU Digital Pin 8 to IN2
//Motor B
int ENB = 5; // MCU PWM Pin 5 to ENB
int IN3 = 7; // MCU Digital pin 7 to IN3
int IN4 = 6; // MCU Digital pin 6 to IN4
// defines variables
long duration;
int ob_distance = 20;//distance in cm
int motorSpeed = 75;//0-255
Next is the setup routine. This sets up the motor driver channels and the ultrasonics pins for input and output.
void setup() {
pinMode(ENA, OUTPUT); //Set all the L298n Pin
to output
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
pinMode(trigPin, OUTPUT); // trigPin as output
pinMode(echoPin, INPUT); // echoPin as input
}
Next, we have a series of functions that the loop will run to make the system work. Firstly we have the code required to run the ultrasonic module. The pingSonar() function returns an integer, which is the distance in centimetres to the object detected. This works by pulsing the trigPin then reading the echoPin. This returns a long (duration), which we then multiply by 0.034 (speed of sound) and divide by 2, allowing for the round trip (since we're reading the echo of the original signal).
int pingSonar() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
return duration * 0.034 / 2;
}
Next, we have five functions relating to motor control. These functions, forward(), backward(), turnLeft(), turnRight() will enable the motors to turn on and perform the action as they are called. They will continue to be on until you either give the L298N a new movement command, or you call the stopAll() function.
void forward() {
digitalWrite(IN1, HIGH); // Turn HIGH motor A
digitalWrite(IN2, LOW);
analogWrite(ENA, motorSpeed);
digitalWrite(IN3, HIGH); // turn HIGH motor B
digitalWrite(IN4, LOW);
analogWrite(ENB, motorSpeed);
}
Finally, we have the main loop code. In this case, we have made it as simple as possible using only a single if statement. Once the robot is on, the forward() function is called in the loop.
The “if” statement calls the pingSonar() function, which returns an integer providing the current distance to any object in front of the sensor. If the value is equal to or less than the given threshold in the ob_distance constant, it executes a series of commands. The robot stops, waits 1 second, calls the backward() function, which due to the delay will reverse for 2 seconds. It then stops before calling the turnLeft() function, which makes the left motors reverse while the right go forward for 1 second, before stopping again.
The pingSonar is called again to check the distance before continuing forward, reversing and turning again.
void loop()
{
if (pingSonar() < ob_distance) {
stop_all();
delay(1000);
backward();
delay(2000);
stop_all();
delay(1000);
turnLeft();
delay(1000);
stop_all();
}else
forward();
}
WHERE TO FROM HERE?
This project provides the simplest implementation of an obstacle-avoiding robot that we could develop. It serves as a great base to experiment with the code, and build in more intelligence to the system. Currently, it performs a simple sequence based upon a single decision, so what about creating a random decision on whether it will turn left or right? What about accelerating the motors up to speed rather than just running them on or off?
ACCELERATION CODE:
for (int x = 0; x < 256; x++)
{
analogWrite(ENA, x);
analogWrite(ENB, x);
delay(20);
}
for (int y = 255; y >= 0; --y)
{
analogWrite(ENA, y);
analogWrite(ENB, y);
delay(20);
}
This is a very basic system, using relatively accessible parts. We’ll be building on these fundamentals in future issues, to explore more of the awesome world that is robotics.
Part 2: SAM v1.1: Upgraded Senses
Part 3: SAM v1.2: Radar-Like Ultrasonic Scanning
Part 4: SAM v1.3: Setting SAM Free
Part 5: SAM v1.4: Talk Back with SAM