In Issue 3 we built a basic obstacle-avoidance robot named SAM (Somewhat Autonomous Machine). This month we’ll continue this project to give him some more intelligence and a lot more personality.
SAM’s original brief was to build a basic autonomous robot that could sense an obstacle, react and then continue on its way. SAM achieved this objective, but we now have a new brief; be more aware of its surroundings and add some more functionality.
In last issue, we did solve the problem of basic obstacle avoidance, the resulting solution was quite abrupt. He would sense something and make an immediate retreat before turning around. This got us off the ground and used some simple code to achieve the desired result; however, there were some obvious issues. While he never hit anything (OK, almost never), the avoidance was very crude; we need to design a much better way for SAM to side-step an object and not lose his momentum.
HOW IT WORKS
ULTRASONICS
One of the main issues we faced after SAM’s inception was that while he worked very well at undertaking his mission, he was not particularly smart when it came to driving. The only input was a very simple question: “is there something in front of me and how close is it?” And his response was “if it’s close enough, then I’m going to turn and run in a different direction”.
The two issues SAM faces is that he does not always drive particularly straight, nor is he aware of his surroundings. We can combat this in a number of ways: if we were using him outdoors we could implement a GPS unit that will provide a constant heading, allowing SAM to use his ultrasonic sensor to avoid things in front of him; or we could use a magnetometer to give us a heading to follow, but the magnetic interference from the motors can cause inconsistent readings. Alternatively, we could add a pan motor and allow him to move the ultrasonic scanner from side to side, although this would be quite complex to engineer and code.
A simple and cost effective solution is to add two more ultrasonic sensors. We will set this up as a polled operation, and it will continue to scan the surrounding environment to see if there are any objects nearby.
30° V 45° V 90°
This revision has placed two additional ultra sonic transducers on SAM’s sides, to enable him to see what is to each side of him, and to allow him to side-step objects rather than stop, back up and turn around. We tested 30, 45 and 90-degree angles and found that the 45 seemed to give the best compromise. Be mindful that to detect an object it needs to be parallel to the ultra sonic sensor to reflect the sound waves back. We have included some variables that can be changed to help refine this process.
CONTROL LOGIC
SAM’s original logic tree was simple: if an object gets with 20cm then stop, wait, back up, wait, turn, wait, then go forward. While this met the brief, it is fairly simple in its logic. The new brief we received was to make him avoid objects by navigating around them. The logic remains the same for the obstacle avoidance, with his front facing sensor, but we have the addition of the two extra sensors. Using these we scan each side individually and react based upon an obstacle being present outside of the range of the main sensor. We then use this information to slow one set of the motors on the chassis enabling the turn. This is achieved in a very simple way. The main loop now has three conditions: the first is a simple “if” statement for the object in front, which triggers the stop and reverse. The next condition is a while loop. This loop checks the left-hand sensor, and if anything is within the set range, then the right-hand pair of wheels are slowed down to 25%, thus instigating a side-step arc around the object. If it does not detect anything, it checks the right-hand sensor, repeating the while loop if required, before looping back to the first statement. Each while loop is governed by the distance to the object, so while ever the ultrasonic sensor in question finds an object too close, it will continue to make adjustments.
while (pingLeftSonar() <=
objectDistanceLeft) {
Serial.print("L:");Serial.
println(pingLeftSonar());
slowLeft();
}
while (pingRightSonar() <=
objectDistanceRight) {
Serial.print("R:");Serial.
println(pingRightSonar());
slowRight();
}
The above code calls the pingLeftSonar() during each while loop. The function returns the current distance that the ultrasonic sensor is reading. If it is less or equal to the defined distance variable, we call the slowLeft function, which will slow the motors and cause SAM to make a side-step or turn. The slowLeft function (below) is identical to the forward function with the exception of the analogWrite (ENA, motorSpeed) line. In this line, we have used a simple multiple to slow the motor down – 0.25 will reduce the motor to 25% of the motorSpeed variable.
The other addition to all of these functions is the printByte function, which we will discuss next. This is repeated for slowRight function, but the multiplier is added to the analogWrite (ENB), thus slowing the alternate motor pair on the chassis.
//slow Left
void slowLeft() {
printByte(hmmmL);
Serial.println("SL");
digitalWrite(IN1, HIGH); // Turn HIGH motor A
digitalWrite(IN2, LOW);
analogWrite(ENA, motorSpeed * 0.25);
digitalWrite(IN3, HIGH); // turn HIGH motor B
digitalWrite(IN4, LOW);
analogWrite(ENB, motorSpeed);
}
LED MATRIX
To add in a bit more personality, we have added an 8x8 LED matrix array, which gives SAM an injection of personality. We have programmed him with a series of expressions – happy, sad, annoyed and indifferent, and each expression is triggered in the various drive functions. So when he is moving forward, we call the happy icon. The matrix can be thought of as a grid of LEDs. Now if we were to connect all of them directly to the Uno it would take up 64 I/O lines, which is more than the Uno has available! However, in this case, the matrix also contains a matrix driver. The MAX7219 (part of the 72XX series) is a serial interfaced, eight-digit encoder. This makes the setup and execution of 8x8 icons easy.
The LED matrix can be thought of as a series of eight rows of 8 LEDs. We feed the data to the matrix one byte at a time. Each byte represents 8 bits, each one being an LED on or off.
To declare our icons or emotions, we use an array of 8 bytes, where each byte contains 8 bits. Each byte here is represented by a hexadecimal (base 16) value. If you converted them to a decimal (base 10), you would get a normal number, but if you convert them to base 2 or binary, then you will get a series of bits. Each one of these bytes represents a row in the matrix. The first four rows are blank, the fifth row contains the first byte of data that will change the state of the LED row.
byte grin[8] = {0x0, 0x0, 0x0, 0x0, 0x81, 0x42, 0x3c, 0x0 };
0x81 = 10000001
To send this to the matrix-encoding chip we send it one byte at a time, and each time we index the row that we want it to be. To do this, we use a simple for loop, and loop through it eight times, since as we have eight rows.
void printByte(byte character [])
{
int i = 0;
for (i = 0; i < 8; i++)
{
lc.setRow(0, i, character[i]);
}
}
If you wish to make your icons or patterns check out this generator web app project at GitHub.
THE BUILD
ORIGINAL SAM PARTS LIST FROM ISSUE #3:
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 |
PARTS LIST FOR v1.1:
ADDITIONAL Parts Required: | Jaycar | Altronics |
---|---|---|
2 x Ultrasonic Modules | XC4442 | Z6322 |
1 x 8x8 LED Matrix Module | XC4499 | Z6362 |
1 x Updated 3D Printed Caddy | - | - |
BUILDING THE CIRCUIT
If you have already built the first version of SAM, then the hook up is quite simple. We have two additional ultrasonic sensors to add to the chassis, in addition to the 8x8 LED matrix.
As the unit is based on the UNO R3, we will be very close to having every GPIO pin used by the end of this project. As such we have run out of GND and 5V pinouts. A simple solution is to use a small breadboard which has power rails to expand the connection points available.
The chassis has been modified to allow the additional pair of Ultrasonic sensors and have them set off at a 45° angle relative to the main sensor. We have also raised the main sensor up high to allow for the LED matrix underneath. Please note that due to the addition of the extra ultrasonics and the angle they sit on, it is now very difficult to program the Arduino Uno while it is attached to the chassis (the USB port is obscured). For this reason, make sure you have the jumper on the motherboard off, and program the UNO before you attach it. The LED matrix is a very snug fit if you are using the module. Due to the encoder IC, you will find the unit just goes past flush on the print. You may find bending the input pins will also help to ensure clearance. The two additional ultrasonic sensors are much easier to work with if you place them upside down, and they will work just as effectively in either orientation.
THE CODE / SETUP / OTHER
SAM includes the addition of three new functions: 1) to deliver our emotions to SAM’s LED matrix; and 2) to change the motors speed to allow them to side-step any objects. To facilitate the LED matrix, we need to include a new library that is specifically designed to work with the MAX7219 and MAX7221 (https://github.com/wayoda/LedControl). This has been included in the download package. Please extract and place in the libraries folder of your Arduino IDE installation.
In setting SAM up, you may need to fine tune the sensitivity settings. You can do this by adjusting the three object distance variables. These are integers and are representative of centimetres. SAM V1 had the objectDistance variable set at 20cm. In testing we found that the longer distance would prevent the side sensor from triggering so, we have set the defaults at 15cm for the front and 30cm for the side pods. This should mean that any object off to the left or right should trigger the function to make him side-step the object before the main sensor causes him to halt. This is very dependent on the range and angle of the object itself.
// defines variables
long duration;
int objectDistance = 15; //distance in cm
int objectDistanceRight = 30; // distance in cm
int objectDistanceLeft = 30; // distance in cm
int motorSpeed = 128; // 0-255
Note: make sure the batteries are fresh. Low batteries may result in false signals with the ultrasonic sensors.
WHERE TO FROM HERE?
We are starting to reach the physical limits of the Arduino Uno, with only three GPIO pins left to use. We will address this in the next iteration. However, the software provided with SAM is quite simple. Currently, the code is very block-driven; it checks a sensor and immediately implements a function. This means that it will always favour an object on the left before it evaluates an object to the right. A more intelligent way to address this would be to get the data from all three sensors and evaluate them based on values and tests before implementing an action for avoidance. What if there is an object left and right? SAM could evaluate the distance and adjust the speed multiplier on the fly, to create smaller movements and keep on track.
Part 3: SAM v1.2: Radar-Like Ultrasonic Scanning