Click here to read Part 1 of this article >
In Part 1, we built a custom acrylic base board to mount NEMA 17 stepper motors to the chassis. We then attached the Arduino Uno 3 and the motor shield to the acrylic base and installed the motor shield library. In Part 2, we will be adding the rest of the parts required for the robot to function, such servo and Laser Range Finder (LRF), and writing a program that allows the robot to move around autonomously.
Please refer back to How To Make Your Own Robot and How To Make Your Own Robot (Part 2) where we showed a simple and easy way to build your own wheeled robot from scratch using Arduino UNO R3. In this article, we will further improve the functionality of the robot by making it mobile and adding laser range finder (LRF) to allow the robot to detect objects and measure distances.
The robot is designed to:
The hardware required for this Robot are available in many electronics shop, we provide examples of shops in the Hardware list.
Install and test the servo
The next step is to install a servo for panning. First screw the two small plates to the servo base, and then screw it to the acrylic base as shown in Figure 1 and Figure 2. Install aluminum mounting to the top of the servo using 2 screws.
Figure 1. Attach servo to acrylic base using 2 small plates.
Figure 2. And attach aluminum mounting to servo.
Connect the connector from servo to motor shield as shown in Figure 3. There are 2 servo connectors in motor shield labeled “servo1” and “servo2”. Use servo1 for this connection (the one on the outer side). Please be careful not to reverse the connection.
Figure 3. Connection between servo and motor shield.
Now we are ready to run the code, upload the code below to Arduino. There is no need to install a new library. The required library (Servo.h) is already included with the Arduino IDE program. The servo will use Digital pin 10 (servo 1) or you may use pin 11 if the servo is connected to servo 2 in motor shield.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
//*********************************************************************************************** #include <Servo.h> Servo panMotor; // servo for laser range finder (lrf) scanning int pos = 0; // variable to store the servo position const int a = 1000; void setup() { panMotor.attach(10); } // Attach Servo for scanning to pin 10 void loop() { // *************************** Scan Left *********************************** panMotor.write(180); // 90 degree delay(a); panMotor.write(135); // 45 degree delay(a); // ************************** Scan Right ********************************** panMotor.write(45); // 45 degree delay(a); panMotor.write(0); // 90 degree delay(a); // ************************* Neutral position ***************************** panMotor.write(90); delay(a); } //************************************************************************************************** |
The code is very simple; it scans from left to right and back to original position.
The following video shows how the servo works:
Laser Range Finder Product Document – Parallax
Parallax Laser Range Finder (LRF) Module is a distance-measuring instrument that uses laser technology to calculate the distance to a targeted object. Distance is calculated by optical triangulation using simple trigonometry between the centroid of laser light, camera, and object. Optimal measurement ranges 6–48 inches (15–122 cm) with an accuracy error <5% (average 3%).
Hardware installation is simple. Simply drill 2 holes to match with LRF position then screw the LRF into the aluminum mounting using plastic spacer (see Figure 5).
Figure 4. Connect LRF cable to motor shield.
Figure 5. LRF attached to aluminum mounting
Due to the max optimal measurement 122 cm of the LRF, we need to bend forward the aluminum mounting a little bit so that the range will always be less than 120 cm (Figure 6).
Figure 6. Bend the aluminum mounting forward, so that the distance from laser to floor less than 120 cm
Connect the cable connector to the LRF exactly as shown in Figure 7. GND goes to Ground, VCC goes to 5V, SOUT goes to pin 8, and SIN goes to pin 9.
Figure 7. LRF connection to motor shield
Now that we have installed and connected the LRF, let’s upload the code! Again, there is no need to install library. We will use SoftwareSerial.h which is already included in Arduino IDE.
The following code is originally from the sample code with some modification, converting distance data from string into integer. What it does is to measure distance to the object in front of the sensor and print the result. We will use serial monitor to display the result.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
// ************************************************************************************************************************************************** #include <SoftwareSerial.h> #define rxPin 8 // Serial input (connects to the LRF's SOUT pin) #define txPin 9 // Serial output (connects to the LRF's SIN pin) #define ledPin 13 // Most Arduino boards have an on-board LED on this pin #define BUFSIZE 16 // Size of buffer int lrfDataInt; SoftwareSerial lrfSerial = SoftwareSerial(rxPin, txPin); void setup() // Set up code called once on start-up { // *************************************** setup for LRF *********************************************** pinMode(ledPin, OUTPUT); pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); digitalWrite(ledPin, LOW); // turn LED off Serial.begin(9600); while (!Serial); // Wait until ready Serial.println("\n\nParallax Laser Range Finder"); lrfSerial.begin(9600); Serial.print("Waiting for the LRF..."); delay(2000); // Delay to let LRF module start up lrfSerial.print('U'); // Send character while (lrfSerial.read() != ':'); delay(10); // Short delay lrfSerial.flush(); // Flush the receive buffer Serial.println("Ready!"); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor } // ****************************************** main loop ************************************************ void loop() // Main code, to run repeatedly { lrf(); } // ****************************************** end main loop ********************************************* void lrf() { lrfSerial.print('R'); // Send command digitalWrite(ledPin, HIGH); // Turn LED on while LRF is taking a measurement char lrfData[BUFSIZE]; // Buffer for incoming data int lrfDataInt1; int lrfDataInt2; int lrfDataInt3; int lrfDataInt4; int offset = 0; // Offset into buffer lrfData[0] = 0; // Clear the buffer while(1) { if (lrfSerial.available() > 0) // If there are any bytes available to read, then the LRF must have responded { lrfData[offset] = lrfSerial.read(); // Get the byte and store it in our buffer if (lrfData[offset] == ':') // If a ":" character is received, all data has been sent and the LRF is ready to accept the next command { lrfData[offset] = 0; // Null terminate the string of bytes we just received break; } // Break out of the loop offset++; // Increment offset into array if (offset >= BUFSIZE) offset = 0; // If the incoming data string is longer than our buffer, wrap around to avoid going out-of-bounds } } lrfDataInt1 = ( lrfData[5] -'0'); lrfDataInt2 = ( lrfData[6] -'0'); lrfDataInt3 = ( lrfData[7] -'0'); lrfDataInt4 = ( lrfData[8] -'0'); lrfDataInt = (1000*lrfDataInt1)+ (100*lrfDataInt2)+(10*lrfDataInt3) + lrfDataInt4; Serial.print("Distance = "); // The lrfData string should now contain the data returned by the LRF, so display it on the Serial Monitor Serial.println(lrfDataInt); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor digitalWrite(ledPin, LOW); // Turn LED off delay(1000); } //************************************************************************************************************************************************* |
Here is the result shown in serial monitor. All measurement are in mm.
Install OLED display
First, let’s install the OLED plastic case into the acrylics base (see Figure 9), then connect the cable (which comes with the OLED Display) to the display. For connection to motor shield, cut the cable from other jst connector then solder red cable to 5V, black cable to Ground, yellow cable to pin SDA, and green cable to pin SCL. Just to make sure see also the back of OLED display.
Figure 8. Connection between OLED and motor shield.
Once we have attached OLED at the base and connected the cables, now we are ready for some coding.
First, make sure that the library, SeeedOLED.h, has already been installed. Then, upload the following code to Arduino. This code uses function oled1 which will be used in the final coding later on. Basically, it displays number from 100 to 109.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//***************************************************************************************************************************************** #include <Wire.h> #include <SeeedOLED.h> int distanceFwd; void setup() { Wire.begin();} void loop() { int i = 0; for (i; (i < 10); i ++) { distanceFwd = 100 + i; oled1(); delay(1000); } } void oled1() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(3,3); SeeedOled.putString("Forward :"); SeeedOled.setTextXY(5,9); SeeedOled.putNumber(distanceFwd); } //***************************************************************************************************************************************** |
When the program runs properly it will display as shown in the following video:
So now we have installed all the hardware and tested the individual devices. Let’s put it all together and build a smart laser robot that can move around autonomously. The final code will do the following:
Copy the following code and upload to Arduino:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
//********************************************************************************************************************* #include <Wire.h> #include <Adafruit_MotorShield.h> #include "utility/Adafruit_MS_PWMServoDriver.h" #include <SoftwareSerial.h> #include <Servo.h> #include <SeeedOLED.h> #define ledPin 13 #define BUFSIZE 16 #define rxPin 8 // Serial input (connects to the LRF's SOUT pin) #define txPin 9 // Serial output(connects to the LRF's SIN pin) SoftwareSerial lrfSerial = SoftwareSerial(rxPin, txPin); // Size of buffer (in bytes) for incoming data // Create the motor shield object with the default I2C address Adafruit_MotorShield AFMS = Adafruit_MotorShield(); // Connect a stepper motor with 200 steps per revolution (1.8 degree) Adafruit_StepperMotor *myMotor1 = AFMS.getStepper(200, 1); // motor port #1 (M1 and M2) Adafruit_StepperMotor *myMotor2 = AFMS.getStepper(200, 2); // motor port #2 (M3 and M4) Servo panMotor; // servo for laser range finder (lrf) scanning int leftDistance1; int leftDistance2; int rightDistance1; int rightDistance2; int maxDistance; int angleTurn; int directions; int distanceFwd; const int a = 30; //*********************************************************************************start set up ************************** void setup() { Serial.begin(9600); // set up Serial library at 9600 bps panMotor.attach(10); // Attach Servo for scanning to pin 10 AFMS.begin(); // create with the default frequency 1.6KHz myMotor1->setSpeed(100); // Set stepmotor1 speed at 100 rpm myMotor2->setSpeed(100); // Set stepmotor2 speed at 100 rpm pinMode(ledPin, OUTPUT); pinMode(rxPin, INPUT); // Input pin for LRF pinMode(txPin, OUTPUT); // Output pin for LRF digitalWrite(ledPin, LOW); // turn LED off Serial.begin(9600); while (!Serial); // Wait until ready lrfSerial.begin(9600); Serial.print("Waiting for the LRF..."); delay(2000); // Delay to let LRF module start up lrfSerial.print('U'); // Send character while (lrfSerial.read() != ':'); delay(10); // Short delay lrfSerial.flush(); // Flush the receive buffer Serial.println("Ready!"); Serial.flush(); // Wait for all bytes to be transmitted to the Serial Monitor panMotor.write(90); delay(a); } //******************************************************************* start Loop ***************************************** void loop() { distanceFwd = lrf(); maxDistance = distanceFwd; oled1(); if (distanceFwd > 700) { Motor(500,1);} else if (distanceFwd > 400) { Motor(200,1);} else // if path is blocked { checkTurn(); turn();} } //***************************************************************** check turn function ******************************** void checkTurn() { digitalWrite(ledPin, HIGH); // ************************** Scan Left *********************************** panMotor.write(180); delay(a); leftDistance1 = lrf(); panMotor.write(135); delay(a); leftDistance2 = lrf(); oled(); // *************************** Scan Right ********************************* panMotor.write(45); delay(a); rightDistance2 = lrf(); panMotor.write(0); delay(a); rightDistance1 = lrf(); oled(); panMotor.write(90); digitalWrite(ledPin, LOW); // ************************************ Turn Left ************************ maxDistance = leftDistance1; angleTurn = 100; directions = 0; if (maxDistance <= leftDistance2) {angleTurn = 50; maxDistance = leftDistance2; directions = 0; } //*********************************** Turn Right *********************** if (maxDistance <= rightDistance2) {angleTurn = 50; maxDistance = rightDistance2; directions = 1; } if (maxDistance <= rightDistance1) {angleTurn = 100; maxDistance = rightDistance1; directions = 1; } // ******************************* Turn Back****************************** if ((leftDistance1 < 300) && (rightDistance1 <300) && (distanceFwd <300)) {angleTurn = 200; directions = 3; } } //************************************************ turn function ********************************************************* void turn() { rightDistance1 = 0; rightDistance2 = 0; leftDistance1 = 0; leftDistance2 = 0; if (directions == 0) // turn left { Motor(angleTurn,3);} if (directions == 1) // turn right { Motor(angleTurn,4);} if (directions == 3) // turn back { Motor(angleTurn,4);} } //*************************************** Stepper Motor function **************************************************** void Motor(int x,int y) { int i = 0; for ( i; (i < x); i ++) { if (y == 1) // move forward {myMotor1->step(1, FORWARD, SINGLE); myMotor2->step(1, BACKWARD, SINGLE);} if (y == 2) // move backward {myMotor1->step(1, BACKWARD, SINGLE); myMotor2->step(1, FORWARD, SINGLE);} if (y == 3) // move left { myMotor1->step(1, FORWARD, SINGLE); myMotor2->step(1, FORWARD, SINGLE);} if (y == 4) // move right { myMotor1->step(1, BACKWARD, SINGLE); myMotor2->step(1, BACKWARD, SINGLE);} } } //*********************************************************************** LRF function ******************************* long lrf() { lrfSerial.print('R'); // Send command digitalWrite(ledPin, HIGH); // Turn LED on while LRF is taking a measurement char lrfData[BUFSIZE]; // Buffer for incoming data int lrfDataInt1; int lrfDataInt2; int lrfDataInt3; int lrfDataInt4; int lrfDataInt; int offset = 0; // Offset into buffer lrfData[0] = 0; // Clear the buffer while(1) { if (lrfSerial.available() > 0) { lrfData[offset] = lrfSerial.read(); if (lrfData[offset] == ':') { lrfData[offset] = 0; break;} offset++; if (offset >= BUFSIZE) offset = 0; } } lrfDataInt1 = ( lrfData[5] -'0'); lrfDataInt2 = ( lrfData[6] -'0'); lrfDataInt3 = ( lrfData[7] -'0'); lrfDataInt4 = ( lrfData[8] -'0'); lrfDataInt = (1000*lrfDataInt1)+ (100*lrfDataInt2)+(10*lrfDataInt3) + lrfDataInt4; Serial.flush(); digitalWrite(ledPin, LOW); return lrfDataInt; } //********************************************************* Oled function ************************************************ void oled() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(0,0); SeeedOled.putString("Left 1:"); SeeedOled.setTextXY(0,12); SeeedOled.putNumber(leftDistance1); SeeedOled.setTextXY(2,0); SeeedOled.putString("Left 2:"); SeeedOled.setTextXY(2,12); SeeedOled.putNumber(leftDistance2); SeeedOled.setTextXY(4,0); SeeedOled.putString("Right 1:"); SeeedOled.setTextXY(4,12); SeeedOled.putNumber(rightDistance1); SeeedOled.setTextXY(6,0); SeeedOled.putString("Right 2:"); SeeedOled.setTextXY(6,12); SeeedOled.putNumber(rightDistance2); } void oled1() { SeeedOled.clearDisplay(); //clear the screen and set start position to top left corner SeeedOled.setNormalDisplay(); //Set display to Normal mode SeeedOled.setPageMode(); //Set addressing mode to Page Mode SeeedOled.setTextXY(3,3); SeeedOled.putString("Forward :"); SeeedOled.setTextXY(5,9); SeeedOled.putNumber(distanceFwd); } //*********************************************************************************************************************************** |
Figure 9. Final look with OLED attached to acrylic base.
In the previous articles, How To Make Your Own Robot and How To Make Your Own Robot (Part 2), we have created a simple wheeled robot using stepper motors. This time we improved the functionality of the robot we created before by adding laser range finder (LRF) and making it mobile. I have wanted to create something that does measuring for us. The laser sensor, in this case, allows the robot to detect and avoid object(s) and obtain more accurate distance data. There are many other applications of laser robot. You can come up with your own fun projects using the laser sensor.
We will make something even cooler next time. So stay tuned!