writeup.tex (19127B)
1 \documentclass[a4paper,12pt]{article} 2 3 \usepackage{tabularx} 4 \usepackage{geometry} 5 \usepackage{pgfplots} 6 \usepackage{titling} 7 \usepackage{titlesec} 8 \usepackage[english]{babel} 9 \usepackage[hidelinks]{hyperref} 10 \usepackage{listings} 11 \usepackage{graphicx} 12 \usepackage{float} 13 14 \graphicspath{ {./images} } 15 16 \titleformat{\section} {\large\bfseries} {} {0em} {}[\titlerule] 17 \titleformat{\subsection} {\bfseries} {} {0em} {}[\titlerule] 18 \geometry{a4paper,total={170mm,257mm},left=25mm,right=25mm} 19 \setlength{\parindent}{0pt} 20 \setlength{\parskip}{10pt} 21 22 \pgfplotsset{width=15cm,compat=1.9} 23 24 \author{Lucas Standen} 25 \title{Robotics report} 26 27 28 \begin{document} 29 \maketitle 30 \newpage 31 \section{How does the robot work?} 32 I designed my system to repeatedly poll the sensors, and adjusting the motors based on the input, I made functions called \lstinline{stepMove} and \lstinline{stepTurn} which each moved or turned the robot a small amount. These function ran in a very short amount of time, allowing them to be called as often as possible, letting me read the sensors more often. This aloud me to make my robot react to changes, such as going off course or touching the barcode, a lot faster than if I halted the program while the robot moved. Thanks to this I didn't have to worry about distances or angles in my code, as I could tell the robot to keep moving until something happened. 33 34 The sensors were polled by a function called \lstinline{getState}, which would be called as often as possible and updated the readings of the sensors in the robot, and updated the state of the motors depending on how long they had be turned on for. My step function worked by storing a time stamp of when they were called (using \lstinline{millis}), then every time the get state function was called, I could see if it had been over 19 milliseconds since the motors had been told to move, if it had been, I could turn them off. The reason I did this was to avoid the need to turn the motors off in my behaviour functions and line following functions, as having that in them, would have made them more complex than they needed to be. 35 36 \subsection{What went well?} 37 I was happy with the robots attraction and avoidance behaviours, they both worked very well, and were reliable. The LED state functioned as expected, flashing repeatedly, this worked in a similar way to the motors, checking when the last call was made to flash the LED, allowing the code to spend more time on the sensors readings than the state of the LED. 38 39 I was also happy with my use of a state \lstinline{struct} to hold all of the robots internal values, thanks to this my robot used almost no global variables. I declared it as \lstinline{static} inside of the \lstinline{getState} function, this meant that I could reassign the state variable in my \lstinline{loop} function, while not losing data, such as LED flash state. 40 41 A final thing that I was proud of in my code was my debug macros, I wrote a macro that aloud entire code segments to be toggled, in a neat and clean way. I used this a lot over my code to get readings from all the sensors. It also is used to enable testing upon starting the robot. 42 43 \subsection{What didn't go well?} 44 45 I found my robot can sometimes get stuck while reading barcodes, if it did not enter them straight on, it would think that it needs to turn. This was partially fixed by adding an error correction, that would try to detect when this happened and turn in the opposite direction, to line the robot up with the barcode. This did help, however it still got stuck sometimes. 46 47 \section{Comparing my code to the design brief} 48 \noindent\begin{minipage}{\textwidth} 49 \subsection{Working with Light Dependant Resistors} 50 When the robot starts up, and has been compiled with DEBUG enabled, it will start a testing program, this program will perform all tests on the LDRs. The code that does so is presented here. This code will read 10 values from each sensor and output the readings, it will also write the values to EEPROM. I think this code properly achieves the target of testing so I would say it is worth 70\% of the maximum mark for this section. 51 52 \begin{verbatim} 53 void 54 testLDR() 55 { 56 SEprintf("Testing LDRS\n"); 57 for (int i = 0; i < 2; i++) { 58 SEprintf("Please turn on the lights, and place the robot 59 on a %s surface (press button A to continue)\n", colors[i]); 60 61 waitButton(BUTTONA, HIGH); 62 reading r = readSensorsMean(TESTREADINGS); 63 64 if (strcmp(colors[i], "black") == 0) { 65 EEPROM.put(6, (uint16_t)r.left + 100); 66 EEPROM.put(10,(uint16_t)r.mid + 100); 67 EEPROM.put(14, (uint16_t)r.right + 100); 68 } else { 69 EEPROM.put(4, (uint16_t)r.left); 70 EEPROM.put(8,(uint16_t)r.mid); 71 EEPROM.put(12, (uint16_t)r.right); 72 } 73 74 SEprintf("Test complete, please turn off the lights 75 (press button A to continue)\n"); 76 77 waitButton(BUTTONA, HIGH); 78 readSensorsMean(TESTREADINGS); 79 } 80 SEprintf("finished testing LDRS, please check the values are expected 81 for lighting conditions\n"); 82 } 83 84 \end{verbatim} 85 \end{minipage} 86 87 \begin{center} 88 \textit{These lines have been wrapped, so this code will not compile.} 89 \end{center} 90 91 Note the use of the \lstinline{SEprintf} function, this is one of many helper functions I made. It uses \lstinline{snprintf} from the C stdlib adn then prints it using the Arduino's \lstinline{Serial} class. This makes it equivalent to the C \lstinline{printf} function. 92 93 \subsection{Light source following and obstacle detection} 94 \noindent\begin{minipage}{\textwidth} 95 To make the robot follow the line, I made a move function that checks the current state of the LDRs and will adjusts the course accordingly. 96 97 \begin{verbatim} 98 void 99 move(state *s) 100 { 101 /* small adjustments */ 102 if (s->right && s->mid) 103 stepTurn(RGT, s); 104 else if (s->left && s->mid) 105 stepTurn(LFT, s); 106 107 /* large adjustments */ 108 else if (s->right) { 109 s->prevDir = RGT; 110 stepTurn(RGT, s); 111 } else if (s->left) { 112 s->prevDir = LFT; 113 stepTurn(LFT, s); 114 115 /* drive straight, nominal */ 116 } else if (s->mid) { 117 for (int i = 0; i < 20; i++) 118 stepMove(FWD, s); 119 } else { 120 /* try to recover from not being on the line by moving in the prevDir 121 only updated on large turns */ 122 while (!s->mid) { 123 s = getState(); 124 stepTurn(s->prevDir,s); 125 } 126 } 127 } 128 \end{verbatim} 129 \end{minipage} 130 131 \noindent\begin{minipage}{\textwidth} 132 To make the robot stop and wait when an obstacle, I simply have this code at the top of my loop function, if an obstacle is seen, the code will just keep on going to the top of the loop function. The \lstinline{flash} function will cause the LED to flash. 133 134 \begin{verbatim} 135 void 136 loop() 137 { 138 state *s = getState(); 139 140 /* do nothing while there is an obstacle */ 141 while (isObstacle()) { 142 flash(YELLOW_LED, s); 143 return; 144 } 145 .... 146 \end{verbatim} 147 \end{minipage} 148 149 \noindent\begin{minipage}{\textwidth} 150 The barcode reading function compares the time that the robot was last on a barcode from the current time and if it is longer than a certain time, then a barcode has been passed. 151 152 \begin{verbatim} 153 void 154 checkBarcode(state *s) 155 { 156 long t = millis(); 157 if (s->timeStamp > 500) { 158 /* long barcode */ 159 if ((t - s->timeStamp) > 3000) 160 runBehave(&stepAvoidanceBehave); 161 162 /* short barcode */ 163 else if ((t - s->timeStamp) > 800) 164 runBehave(&stepAttractionBehave); 165 } 166 s->timeStamp = t; 167 } 168 \end{verbatim} 169 \end{minipage} 170 171 \noindent\begin{minipage}{\textwidth} 172 The avoidance and attraction behaviours are both very similar, just having inverted directions, they work by comparing the raw values of the LDRs, meaning they should work in many light conditions. 173 174 \begin{verbatim} 175 void 176 stepAvoidanceBehave(state *s) 177 { 178 flash(RED_LED, s); 179 DBG({ 180 /* WARNING this causes the program to stutter when left on, 181 not too bad for debugging but very bad for general use! */ 182 SEprintf("Avoid: %d, %d, %d\n", s->rawleft, s->rawmid, s->rawright); 183 }); 184 185 /* left is noticably larger */ 186 if (s->rawleft > s->rawright + 50) 187 stepTurn(RGT,s); 188 /* right is noticably larger */ 189 else if (s->rawright > s->rawleft + 50) 190 stepTurn(LFT,s); 191 else 192 stepMove(FWD, s); 193 } 194 195 196 void 197 stepAttractionBehave(state *s) 198 { 199 flash(GREEN_LED, s); 200 DBG({ 201 SEprintf("Attract: %d, %d, %d\n", s->rawleft, s->rawmid, s->rawright); 202 }); 203 204 /* left is noticably larger */ 205 if (s->rawleft > s->rawright + 50) 206 stepTurn(LFT,s); 207 /* right is noticably larger */ 208 else if (s->rawright > s->rawleft +50) 209 stepTurn(RGT,s); 210 else 211 stepMove(FWD, s); 212 } 213 214 void 215 runBehave(void (*stepBehave)(state *)) 216 { 217 while (true) { 218 state *s = getState(); 219 stepBehave(s); 220 } 221 } 222 \end{verbatim} 223 \end{minipage} 224 225 For this section I believe my code was effective and defensive, I included debug statements, didn't rely on global variables and split my program into manageable functions. While testing I found it didn't always work perfectly on edge cases, like the robot coming onto the line from an angle, because of this I believe my code is worth 60\% of the total marks for this section. 226 227 \noindent\begin{minipage}{\textwidth} 228 \subsection{Robot states} 229 The robots states were handled through a \lstinline{flash} function that could cause any one LED to flash at a given point. The code was effective and worked nicely, however the \lstinline{flash} and \lstinline{getState} functions, have to be called very frequently to properly flash the LEDs. Due to this I believe this code is worth 65\% of the maximum mark for the section. 230 231 \begin{verbatim} 232 void 233 flash(int pin, state *s) 234 { 235 switch (pin) { 236 case GREEN_LED: s->gstate = millis(); break; 237 case YELLOW_LED: s->ystate = millis(); break; 238 case RED_LED: s->rstate = millis(); break; 239 } 240 } 241 242 void 243 stepFlash(int pin) 244 { 245 /* we define this a static to preserve its value across function calls */ 246 static uint8_t flashCount; 247 248 if (flashCount < FLASHTIME) 249 digitalWrite(pin, HIGH); 250 else if (flashCount > FLASHTIME / 2) 251 digitalWrite(pin, LOW); 252 253 /* this value wraps which is by design, this will cause the flashing to reset */ 254 flashCount++; 255 } 256 257 void 258 updateFlash(state *s) 259 { 260 /* turn off the leds if they arent ment to be on anymore */ 261 if (millis() - s->gstate > FLASHTIME) { 262 s->gstate = 0; 263 digitalWrite(GREEN_LED, LOW); 264 } if (millis() - s->ystate > FLASHTIME) { 265 s->ystate = 0; 266 digitalWrite(YELLOW_LED, LOW); 267 } if (millis() - s->rstate > FLASHTIME) { 268 s->rstate = 0; 269 digitalWrite(RED_LED, LOW); 270 } 271 272 /* turn on the leds if needed */ 273 if (s->gstate) stepFlash(GREEN_LED); 274 if (s->ystate) stepFlash(YELLOW_LED); 275 if (s->rstate) stepFlash(RED_LED); 276 } 277 \end{verbatim} 278 \end{minipage} 279 280 \subsection{EEPROM} 281 \noindent\begin{minipage}{\textwidth} 282 My code only relied on the black calibration value, along with the motor values, so not all the values were read. I wrote a simple program called calibrate that could however fill all values, and perform simple calibration of the LDR's (although the \lstinline{testLDRs} function was more effective at finding proper values). I believe, as I achieved the brief of filling in all the values and reading them too, this code is worth 70\% of the available marks for this section. 283 284 \begin{center} 285 \textit{Code for reading values from EEPROM} 286 \end{center} 287 \begin{verbatim} 288 .... 289 290 /* let the user attach the battery, attaching the battery 291 while code is running caused strange behaviour */ 292 293 delay(3000); 294 setupRobot(); 295 296 /* servo speed values */ 297 EEPROM.get(0, SERVOA_ZERO); 298 EEPROM.get(1, SPEEDA); 299 EEPROM.get(2, SERVOB_ZERO); 300 EEPROM.get(3, SPEEDB); 301 302 .... 303 304 /* read the calibrated values that should be in EEPROM, 305 these values will be made by the calibrate program */ 306 307 EEPROM.get(6, LDRA_SWITCH); 308 EEPROM.get(10, LDRB_SWITCH); 309 EEPROM.get(14, LDRC_SWITCH); 310 .... 311 \end{verbatim} 312 \end{minipage} 313 \newpage 314 \noindent\begin{minipage}{\textwidth} 315 \begin{center} 316 \textit{Code for setting default values into EEPROM} 317 \end{center} 318 \begin{verbatim} 319 /* repeatedly changes calibration values for each ldr 320 until returning consistant data */ 321 322 while (isLDRBright(LDRA)) LDRA_SWITCH++; 323 while (isLDRBright(LDRB)) LDRB_SWITCH++; 324 while (isLDRBright(LDRC)) LDRC_SWITCH++; 325 326 SEprintf("%d, %d, %d\n\n", LDRA_SWITCH + 100, 327 LDRB_SWITCH + 100, LDRC_SWITCH + 100); 328 329 EEPROM.put(6, LDRA_SWITCH + 100); 330 EEPROM.put(10, LDRB_SWITCH + 100); 331 EEPROM.put(14, LDRB_SWITCH + 100); 332 333 /* setting robots values, my robots values for fwd motion, and turns */ 334 335 EEPROM.put(0, (uint8_t)SERVOA_ZERO); 336 EEPROM.put(1, (uint8_t)SPEEDA); 337 EEPROM.put(2, (uint8_t)SERVOB_ZERO); 338 EEPROM.put(3, (uint8_t)SPEEDB); 339 \end{verbatim} 340 \end{minipage} 341 342 343 \section{Testing} 344 \begin{table}[H] 345 \resizebox{\linewidth}{!}{ 346 \begin{tabular}{|| l | p{0.6\linewidth} | p{0.4\linewidth} ||} 347 \hline 348 \textbf{Test} & \textbf{Method} & \textbf{Result} \\ 349 \hline 350 \hline 351 Push-button left & 352 I made a function called \lstinline{testButton} repeatedly asks the user to press the button, counting how many times the program receives the button press, the user can compare this to how many times the button was pressed. & 353 Pass \\ 354 \hline 355 Push-button right & 356 My \lstinline{testButton} function is requires an argument of the pin number, meaning a button on any pin could be tested. & 357 Pass \\ 358 \hline 359 Servo left & 360 I made a \lstinline{testServo} function, it spins the servo at its default speed for 10 seconds, the user can see if this acts as expected. & 361 Pass \\ 362 \hline 363 Servo right & 364 Like with the \lstinline{testButton} function, any value can be passed into this function, allow the user to test the other servo. & 365 Pass \\ 366 \hline 367 IR transmitter \& receiver & 368 I made a function called \lstinline{testIR} that expects the user to hold an item in front of the sensor and takes multiple readings, checking if the value outputted was consistent for more than 8 times out of 10. & 369 Pass \\ 370 \hline 371 LDRs & 372 I made a function called \lstinline{testLDRS} that asks the user to place the robot on different colours and in different lighting conditions, then takes many readings, the user can then compares these to expected values for the input. & 373 Pass \\ 374 \hline 375 \hline 376 Object avoidance & 377 To test the object avoidance, I simply used the robot repeatedly, in a variety of lighting conditions, and confirmed that the robot stops and that the orange LED turns on. I ran this test 10 times, with the object \~10cm away from the robot. & 378 8 / 10 Passed. 2 times resulted in robot entering attraction behaviour due to detecting shadow from object \\ 379 \hline 380 Line following & 381 To test the line following, I placed the robot on the start of the line, and turned it on, I then saw if the robot was able to follow a straight black line. I repeated this 10 times. & 382 7 / 10 Passed. The robot sometimes has issues when travelling over barcodes if it doesn't go straight on, as this causes it to act as if it is on the line diagonally. \\ 383 \hline 384 Barcode reading & 385 To test the barcode reading, I let the robot run over the 2 types of barcode 10 times each, and recorded how many times it was able to determine the correct type of barcode. For this test I repeated it if the robot failed to follow the line, as this was to test if the robot could read the barcode, not follow the line. & 386 9 / 10 Passed for long barcode. 387 8 / 10 Passed for short barcode. 388 All the times this test failed, was because the robot was detecting the other kind of barcode, this is most likely due to the reading being based on time since last line, causing a slow travel to detect as the long barcode, and vice versa. \\ 389 \hline 390 Light attraction and avoidance & 391 To test the light attraction and avoidance, I modified my program to put the robot straight into these modes, I then used my phones flashlight to see if the robot did the expected behaviour. I repeated this 10 times for each behaviour and assessed it based on how well it worked. & 392 All 10 times were consistent for each behaviour, however I found while the robot was turning that it wasn't very smooth, I am not sure why this is, as turning in the line following behaviour was smooth, this did not effect its ability to move towards or away from the light. \\ 393 \hline 394 \end{tabular} 395 } 396 \caption{\centering{\textit{All tests were performed in a well lit room, using lines and barcodes provided, unless otherwise stated}}} 397 \end{table} 398 399 \begin{center} 400 \begin{tikzpicture} 401 \begin{axis}[ 402 title=Graph showing LDR readings on a white background with the room bright, 403 ymin=0, ymax=600, 404 ] 405 \addplot[ 406 color=blue, 407 mark=square, 408 ] 409 coordinates { 410 (0,223) 411 (1,226) 412 (2,225) 413 (3,215) 414 (4,233) 415 (5,270) 416 (6,304) 417 (7,321) 418 (8,323) 419 (9,308) 420 }; 421 \addplot[ 422 color=red, 423 mark=square, 424 ] 425 coordinates { 426 (0,182) 427 (1,187) 428 (2,189) 429 (3,183) 430 (4,191) 431 (5,218) 432 (6,243) 433 (7,256) 434 (8,259) 435 (9,248) 436 }; 437 \addplot[ 438 color=green, 439 mark=square, 440 ] 441 coordinates { 442 (0,214) 443 (1,219) 444 (2,220) 445 (3,217) 446 (4,228) 447 (5,255) 448 (6,278) 449 (7,290) 450 (8,290) 451 (9,292) 452 }; 453 \legend{LDR A, LDR B, LDR C} 454 \end{axis} 455 \end{tikzpicture} 456 457 \begin{tikzpicture} 458 \begin{axis}[ 459 title=Graph showing LDR readings on a white background with the room dark, 460 ymin=0, ymax=50, 461 ] 462 \addplot[ 463 color=blue, 464 mark=square, 465 ] 466 coordinates { 467 (0,11) 468 (1,11) 469 (2,11) 470 (3,11) 471 (4,11) 472 (5,11) 473 (6,11) 474 (7,11) 475 (8,11) 476 (9,11) 477 }; 478 \addplot[ 479 color=red, 480 mark=square, 481 ] 482 coordinates { 483 (0,15) 484 (1,14) 485 (2,14) 486 (3,15) 487 (4,15) 488 (5,15) 489 (6,16) 490 (7,15) 491 (8,15) 492 (9,15) 493 }; 494 \addplot[ 495 color=green, 496 mark=square, 497 ] 498 coordinates { 499 (0,8) 500 (1,8) 501 (2,8) 502 (3,8) 503 (4,9) 504 (5,9) 505 (6,9) 506 (7,9) 507 (8,9) 508 (9,9) 509 }; 510 \legend{LDR A, LDR B, LDR C} 511 \end{axis} 512 \end{tikzpicture} 513 514 \begin{tikzpicture} 515 \begin{axis}[ 516 title=Graph showing LDR readings on a black background with the room bright, 517 ymin=0, ymax=200, 518 ] 519 \addplot[ 520 color=blue, 521 mark=square, 522 ] 523 coordinates { 524 (0,110) 525 (1,108) 526 (2,102) 527 (3,101) 528 (4,105) 529 (5,112) 530 (6,118) 531 (7,119) 532 (8,114) 533 (9,104) 534 }; 535 \addplot[ 536 color=red, 537 mark=square, 538 ] 539 coordinates { 540 (0,77) 541 (1,89) 542 (2,86) 543 (3,86) 544 (4,93) 545 (5,99) 546 (6,104) 547 (7,105) 548 (8,99) 549 (9,90) 550 551 }; 552 \addplot[ 553 color=green, 554 mark=square, 555 ] 556 coordinates { 557 (0,88) 558 (1,101) 559 (2,99) 560 (3,99) 561 (4,105) 562 (5,111) 563 (6,116) 564 (7,116) 565 (8,111) 566 (9,103) 567 }; 568 \legend{LDR A, LDR B, LDR C} 569 \end{axis} 570 \end{tikzpicture} 571 572 \begin{tikzpicture} 573 \begin{axis}[ 574 title=Graph showing LDR readings on a black background with the room dark, 575 ymin=0, ymax=10, 576 ] 577 \addplot[ 578 color=blue, 579 mark=square, 580 ] 581 coordinates { 582 (0,1) 583 (1,1) 584 (2,1) 585 (3,1) 586 (4,1) 587 (5,1) 588 (6,1) 589 (7,1) 590 (8,1) 591 (9,1) 592 593 }; 594 \addplot[ 595 color=red, 596 mark=square, 597 ] 598 coordinates { 599 (0,2) 600 (1,2) 601 (2,2) 602 (3,2) 603 (4,2) 604 (5,2) 605 (6,2) 606 (7,2) 607 (8,2) 608 (9,2) 609 }; 610 \addplot[ 611 color=green, 612 mark=square, 613 ] 614 coordinates { 615 (0,0) 616 (1,0) 617 (2,0) 618 (3,0) 619 (4,0) 620 (5,0) 621 (6,0) 622 (7,0) 623 (8,0) 624 (9,0) 625 }; 626 \legend{LDR A, LDR B, LDR C} 627 \end{axis} 628 \end{tikzpicture} 629 \end{center} 630 631 632 \end{document}