Caddy
A 2005 Roborodentia entry with vision and path planning capability
 All Data Structures Files Functions Variables Typedefs Macros Pages
robot_control.c
Go to the documentation of this file.
1 /*
2  * This file is part of Caddy.
3  *
4  * Caddy is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Caddy is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Caddy. If not, see <http://www.gnu.org/licenses/>.
16  */
18 #include "robot_control.h"
19 #include "line_tracking.h"
20 #include "ball_tracking.h"
21 #include "path_planning.h"
22 #include "motor_control.h"
23 #include "camera.h"
24 #include "servos.h"
25 #include "buttons.h"
26 #include "node_list.h"
27 #include "tether_ui.h"
28 #include "tweak_data.h"
29 #include "lcd_16x2.h"
30 #include "utility.h"
31 
32 // avr-libc
33 #include <string.h>
34 
35 #define BEAM_IGNORE_COUNT 6
36 #define CORRAL_COUNT 3
37 #define LIFT_DONE_COUNT 8
38 
39 //
40 // Static global variables
41 //
42 static bool checkedList[BALL_NODE_MAX];
43 
44 //
45 // Global variables
46 //
47 uint8_t botNode = START_NODE;
48 int8_t botHeading = START_HEADING;
49 uint8_t numUnreachedGoals = NUM_GOALS;
50 
51 static bool liftDown = false;
52 static uint8_t nextBallNodeNum = 0;
53 
57 static void moveServosToStart(void);
58 
70 static inline bool positionBot(void);
71 
72 static bool standardBallSearch( void );
73 static inline bool nodeCode0( void );
74 static inline bool nodeCode22( void );
75 static inline bool diagNodeCode(void);
76 static inline bool nodeCode37( void );
77 static inline void initBallSeeking(void);
78 
79 inline void runRoborodentiaCourse(void)
80 {
81  bool justTurned = false;
82  bool firstRun = true;
83 
84  // Known, fixed goals
85  addToGoalList(BONUS_BALL_1);
86  addToGoalList(BONUS_BALL_2);
87  addToGoalList(SENSOR_NODE);
88 
89  moveServosToStart();
90  initBallSeeking();
91 
92  updatePath();
93 
94  // run through first leg, skipping positionBot
95  junctionCode();
96  moveToJunction(1, justTurned);
97 
98 #if DEBUGGING
99  if (lcdMode == NAV_LCD_MODE)
100  {
101  lcdWriteStr(" ", 0, 0);
102  lcdWriteStr(" ", 1, 0);
103  lcdPrintDecU08(botNode, 1, 0);
104  lcdPrintDecS08(botHeading, 1, 3);
105  }
106 #endif
107 
108  // run through arena
109  while (pathList[pathListIndex + 1] != STOP_NODE)
110  {
111  junctionCode(); // ball search, bonous ball pickup, best path code
112 
113  justTurned = positionBot(); // turning, preparing for linetracking
114 
115  if (firstRun)
116  {
117  firstRun = false;
118  setServo(LIFT, LIFT_OPEN); // Lower lift, on first run, b/c skipping seek at node 21
119  msDelay(30);
120  nextBallNodeNum = 1;
121  liftDown = true;
122  }
123 
124 #if DEBUGGING
125  if (lcdMode == NAV_LCD_MODE)
126  {
127  lcdPrintDecS08(botHeading, 1, 3);
128  }
129 #endif
130 
131  moveToJunction(1, justTurned); // linetracking, ground ball pickup
132 
133 #if DEBUGGING
134  if (lcdMode == NAV_LCD_MODE)
135  {
136  lcdPrintDecU08(botNode, 1, 0);
137  }
138 #endif
139  }
140 
141  if (pathList[pathListIndex + 1] == STOP_NODE)
142  {
143  positionBot();
144  nestSequence();
145  }
146 }
147 
148 inline void initBallSeeking(void)
149 {
150  uint8_t i;
151  for (i = 0; i < ELEMENTSOF(checkedList); ++i)
152  {
153  checkedList[i] = false;
154  }
155 }
156 
157 void moveServosToStart(void)
158 {
159 #if DEBUGGING
160  lcdWriteStr("Servos ", 0, 6);
161 #endif
162  setServo(PAN, PAN_CENTER + panOffset);
163  setServo(TILT, TILT_FORWARD);
164  setServo(BOOM, BOOM_UP);
165  setServo(DOOR, DOOR_CLOSED);
166  myDelay(30);
167  setServo(LIFT, LIFT_UP);
168  myDelay(50);
169  disableServo(BOOM);
170  disableServo(DOOR);
171  disableServo(LIFT);
172 }
173 
174 inline bool positionBot(void)
175 {
176  bool justTurned = true;
177 
178  int8_t nextHeading = getNextHeading();
179  int8_t bradsToTurn = nextHeading - botHeading;
180 
181  // BB PICKUP CHECK
182  if (botNode == BONUS_BALL_1 && isInGoalList(BONUS_BALL_1))
183  {
184  bonusBallPickUpManeuver(BB1_HEADING, nextHeading);
185  removeFromGoalList(BONUS_BALL_1);
186  }
187  else if (botNode == BONUS_BALL_2 && isInGoalList(BONUS_BALL_2))
188  {
189  bonusBallPickUpManeuver(BB2_HEADING, nextHeading);
190  removeFromGoalList(BONUS_BALL_2);
191  }
192 
193  // TURN/STRAIGHT CHECK
194  else if (bradsToTurn != 0)
195  {
196  int8_t ticksToTurn;
197  switch ((int8_t) bradsToTurn)
198  {
199  case -128: // U-turn
200  if (botNode == 37)
201  {
202  moveToJunction(1, false);
203  tickWheels(20, 20, 255);
204  msDelay(0x50);
205  moveStraight(-20, 255);
206  }
207  //tankTurn(245, -58);
208  tickWheels(-29, 29, 250);
209  tankTurn(245, -58);
210  break;
211  case -105: // Hard Diagonal
212  tickWheels(28, 28, 250); //28
213  tractorTurn(255, -tempTweak4);
214  tankTurn(250, -70); //-80
215  break;
216  case 23: // Soft Diagonal
217  tractorTurn(255, 23); //23
218  break;
219  case -23:
220  tractorTurn(255, -28);
221  break;
222  case 105:
223  tickWheels(17, 17, 250);
224  tankTurn(250, 80); //80
225  break;
226  default:
227  // fixed ticks forward here?
228 
229  // convert brads to turn to ticks and turn
230  if (bradsToTurn < 0)
231  {
232  ticksToTurn = bradsToTurn + turnSubtract;
233  } else
234  {
235  ticksToTurn = bradsToTurn - turnSubtract;
236  }
237  tractorTurn(255, ticksToTurn);
238  break;
239  }
240  }
241  else
242  {
243  justTurned = false;
244  }
245 
246  if (botNode == SENSOR_NODE)
247  {
248  removeFromGoalList(SENSOR_NODE);
249  }
250 
251  // update botHeading
252  botHeading = nextHeading;
253 
254  // GB PICKUP CHECK: lower lift, if bot knows it will travel over ball
255  nextBallNodeNum = getNextBallNodeNum();
256  if (nextBallNodeNum != 0)
257  {
258  setServo(LIFT, LIFT_OPEN);
259  msDelay(30);
260  liftDown = true;
261  }
262 
263  return justTurned;
264 }
265 
266 inline void bonusBallPickUpManeuver(int8_t bbHeading, int8_t nextHeading)
267 {
268  // move forward (camera will be over junction at this point)
269  // May or may not need to move foward (requires testing)
270  // Some are fine without foward, some seem to need it
271  // Right now, only -32 case moves forward
272 
273  // rotate by (bbHeading - botHeading)
274  switch ((int8_t) (bbHeading - botHeading))
275  {
276  case 96:
277  // example of 96 brad rotation
278  tickWheels(28, 0, 255); // allows fluid motion (no overshoot correction)
279  tankTurn(255, 58); //58
280  break;
281  case -96:
282  tickWheels(0, 28, 255); // allows fluid motion (no overshoot correction)
283  tankTurn(255, -64); //-64
284  break;
285  case -32:
286  tickWheels(10, 10, 255); //10 Move bot forward a few ticks to make it correctly aligned
287  tickWheels(0, 32, 255); //28
288  break;
289  default:
290  fatalError("ERR: bonusBallPi", "ckUpManeuver: 1");
291  break;
292  }
293 
294  // Get in position
295  // setPose(BB_READY);
296  setServo(TILT, TILT_BACK);
297  setServo(LIFT, LIFT_BB_READY);
298  myDelay(40);
299 
300  // Drive up to the corner
301  forward(BOTH_MOTORS, 255);
302  msDelay(1000);
303  brake(BOTH_MOTORS);
304 
305  // Back and and move back into the corner (help align better)
306  moveStraight(-10, 255);
307  forward(BOTH_MOTORS, 255);
308  msDelay(400);
309  brake(BOTH_MOTORS);
310 
311  // Execute the pickup sequence
312  setServo(BOOM, BOOM_BB_GRAB);
313  myDelay(10);
314  setServo(TILT, TILT_BB_GRAB);
315  myDelay(10);
316  setServo(BOOM, BOOM_UP);
317  myDelay(10);
318  setServo(TILT, TILT_VERT + tiltOffset);
319  myDelay(10);
320  setServo(LIFT, LIFT_UP);
321 
322  // Back away from the corner
323  moveStraight(-18, 255);
324  setServo(TILT, TILT_FORWARD);
325 
326  disableServo(BOOM);
327  disableServo(LIFT);
328 
329  // Rotate by (nextHeading - bbHeading)
330  // (This should only be 32, -32, or -96)
331  switch ((int8_t) (nextHeading - bbHeading))
332  {
333  case 32:
334  tankTurn(250, 32);
335  break;
336  case -32:
337  tankTurn(250, -32);
338  break;
339  case -96:
340  tankTurn(250, -90);
341  break;
342  default:
343  // Error, this should only be 32, -32, or -96
344  fatalError("ERR: bonusBallPi", "ckUpManeuver: 2");
345  break;
346  }
347 }
348 
352 inline void moveToJunction(uint8_t numJunctions, bool justTurned)
353 {
354  bool onLine = true;
355  bool juncApproaching = false;
356  uint8_t juncCount = 0;
357 
358  uint8_t ignoreJuncCount;
359  if (!justTurned)
360  {
361  ignoreJuncCount = 3;
362  } else
363  {
364  ignoreJuncCount = 0;
365  }
366 
367  uint8_t pickingUp = false;
368  uint8_t pickingUpCount = 0;
369 
370  uint8_t ignoreBreakBeamCount = BEAM_IGNORE_COUNT;
371 
372  trackLineInit();
373 
374  // Linetrack, until bot is at junction or nest.
375  // If see ground ball, pickup it up and continue linetracking.
376  while (onLine)
377  {
378  while (lineStatsProcessed) ;
379 
380  analyzeLineStats();
381  adjustPWM();
382 
383  // CURRENT JUNCTION IGNORE
384  if (ignoreJuncCount > 0 && junctionY == 0)
385  {
386  ignoreJuncCount--;
387  }
388 
389  // JUNCTION CHECK
390  if (ignoreJuncCount == 0 && junctionY != 0)
391  {
392  if (junctionY < turnPoint)
393  {
394  juncApproaching = true;
395  } else if (juncApproaching)
396  {
397  juncApproaching = false;
398  juncCount++;
399 
400  // set botNode to next junction in pathList
401  do
402  {
403  pathListIndex++;
404  botNode = pathList[pathListIndex];
405  } while (!isJunction(botNode));
406 
407  // Break out of line tracking
408  if (juncCount >= numJunctions)
409  {
410  onLine = false;
411  }
412  }
413  }
414 
415  // STOP IGNORING BEAM CHECK
416  if (liftDown && ignoreBreakBeamCount != 0)
417  {
418  ignoreBreakBeamCount--;
419  }
420 
421  // BEGIN PICKUP CHECK
422  if (liftDown && ignoreBreakBeamCount == 0 && BREAK_BEAM_TRIGGERED)
423  {
425  setServo(LIFT, LIFT_CORRAL); // Perhaps raise it slowly if there are pick-up problems
426  msDelay(30);
427  trackLineInit();
428 
429  liftDown = false;
430  pickingUp = true;
431  pickingUpCount = 0;
432  }
433 
434  // COMPLETE/STOP LIFTING CHECK
435  if (pickingUp)
436  {
437  pickingUpCount++;
438 
439  if (pickingUpCount == CORRAL_COUNT)
440  {
442  setServo(LIFT, LIFT_UP);
443  trackLineInit();
444  }
445 
446  if (pickingUpCount == LIFT_DONE_COUNT)
447  {
448  pickingUp = false;
449 
450  // Set current botNode to node where this ball is
451  botNode = nextBallNodeNum;
452  removeFromGoalList(nextBallNodeNum);
453 
454  if (nextBallNodeNum == 1) // account for ball not found by camera prior to pickup
455  {
456  adjustNumKnownGoals(1);
457  }
458 
459  // Find correct pathListIndex
460  while (botNode != pathList[pathListIndex])
461  {
462  pathListIndex++;
463  }
464 
465  cameraStreamingOff(); // Turn off line tracking
466  disableServo(LIFT);
467  positionBot(); // In case we want to make a -128 brad turn after picking up ball
468  ignoreBreakBeamCount = BEAM_IGNORE_COUNT;
469  trackLineInit(); // Turn line tracking back on
470  }
471 
472  }
473  }
474 
476 
477  // Make sure lift is up (in case we missed a ball or incorrectly thought one was there)
478  if (liftDown)
479  {
480  brake(BOTH_MOTORS);
481 #if DEBUGGING
482  lcdWriteStr("No ball ", 0, 0);
483 #endif
484  setServo(LIFT, LIFT_UP); // Raise the lift
485  msDelay(700);
486  disableServo(LIFT);
487  liftDown = false;
488 
489  // correct goal state
490  removeFromGoalList(nextBallNodeNum);
491  numUnreachedGoals--;
492  adjustNumKnownGoals(-1);
493  }
494 }
495 
496 void nestSequence(void)
497 {
498  // line track, until NEST_BUTTON is pressed
499  trackLineInit();
500 
501  while (!justPressed(NEST_BUTTON))
502  {
503  if (!lineStatsProcessed)
504  {
505  analyzeLineStats();
506  adjustPWM();
507  }
508 
509  debounceButtons();
510  }
511 
512  brake(BOTH_MOTORS);
514  setServo(LIFT, LIFT_UP); // Turn lift on
515  msDelay(300);
516 
517  // Open door, back up, close door
518  setServo(DOOR, DOOR_OPEN);
519  moveStraight(-1, 255); // Back up to take pressure off button
520  brake(BOTH_MOTORS);
521  //myDelay(25); // Let balls roll out
522  msDelay(3000);
523  setServo(DOOR, DOOR_CLOSED); // Leaves door closed, so lift and door don't colide on power up.
524  //myDelay(10); // Wait for door to close
525  msDelay(1000);
526 
527  // Disable all servos
528  disableServo(PAN);
529  disableServo(TILT);
530  disableServo(BOOM);
531  disableServo(LIFT);
532  disableServo(DOOR);
533 }
534 
535 /*
536  * Searches for ground balls, picks-up bonous balls, and computes best path.
537  */
538 void junctionCode(void)
539 {
540  bool foundBall = false;
541 
542  switch (botNode)
543  {
544  case (0): // old virtual windowing look for ball 7 and 8
545  foundBall = nodeCode0();
546  break;
547  case (21): // Suppress standard seek, should already
548  break; // skip junction code at this node
549  case (22):
550  foundBall = standardBallSearch(); // standard seek and
551  foundBall |= nodeCode22(); // tilt look for ball 9, 11, and 12
552  break;
553  case (31): // rotate bot for diagonal ball search
554  case (34): // from any heading at node 31 or 34
555  foundBall = diagNodeCode();
556  break;
557  case (37): // seek for top balls from nest sensor
558  if (!allGoalsFound() && (!checkedList[13] || !checkedList[17]))
559  {
560  botNode = 35;
561  moveStraight(10, 255);
562  foundBall = diagNodeCode();
563  moveStraight(-10, 255);
564  botNode = 37;
565 
566  //pathList[pathListIndex--] = 36;
567  //pathList[pathListIndex--] = 35;
568  }
569  break;
570  default:
571  foundBall = standardBallSearch();
572  break;
573  }
574 
575  if (foundBall)
576  {
577  // clear checked list, if last ball found
578  if (allGoalsFound())
579  {
580  uint8_t i;
581  for (i = 0; i < NUM_BALL_NODES + 1; i++)
582  {
583  checkedList[i] = true;
584  }
585  }
586  updatePath();
587  printGoalList();
588  }
589 }
590 
591 
592 /*
593  * Returns true if a ball is found and the goal list is updated
594  */
595 bool standardBallSearch( void )
596 {
597  GraphNodeType curNode;
598  GraphNodeType nextNode;
599  uint8_t nextNodeNum;
600  int8_t lookDir = -1;
601  int8_t hallHeading = 0;
602  uint8_t ballDist = 0;
603  uint8_t uncheckedBalls[3][2];
604  uint8_t numUncheckedBalls = 0;
605  bool foundBall = false;
606  uint8_t i;
607 
608  bool stopped = false;
609  inSeekPosition = false;
610 
611  // Check for balls to the left, then to the right
612  for (i = 0; i < 2; i++)
613  {
614  ballDist = 0;
615  hallHeading = botHeading + lookDir * 64;
616  nextNodeNum = botNode;
617  numUncheckedBalls = 0;
618  // Continue traversing nodes to the left (or right) until you hit the end
619  while (nextNodeNum > 0)
620  {
621  getNode(nextNodeNum, &curNode);
622  nextNodeNum = getNodeAtHeading(&curNode, hallHeading);
623  if (nextNodeNum > 0)
624  {
625  getNode(nextNodeNum, &nextNode);
626  // Keep track of how far away we are from the bot's current node
627  ballDist += getCostToNode(&curNode, nextNodeNum);
628  if (isBallNode(nextNodeNum) && !checkedList[nextNodeNum])
629  {
630  uncheckedBalls[numUncheckedBalls][BALL_DIST] = ballDist;
631  uncheckedBalls[numUncheckedBalls][BALL_NODE_NUM] = nextNodeNum;
632  checkedList[nextNodeNum] = true;
633  numUncheckedBalls++;
634  }
635  }
636  }
637  // Set pan, tilt, hi-res, etc...
638 
639  if (numUncheckedBalls > 0)
640  {
641  stopped = true;
642  trackColorInit(lookDir);
643 
644  if (lookDir == -1)
645  {
646  foundBall |= cameraSeekLeft(uncheckedBalls, numUncheckedBalls);
647  } else if (lookDir == 1)
648  {
649  foundBall |= cameraSeekRight(uncheckedBalls, numUncheckedBalls);
650  }
651  }
652 
653  lookDir *= -1; // Look the other way the next time through
654  }
655 
656  if (stopped)
657  {
658  moveStraight(0xb, 255);
659  setServo(PAN, PAN_CENTER + panOffset);
660  setServo(TILT, TILT_FORWARD);
661  msDelay(600);
662  }
663 
664  // Returns true if one or more balls are found
665  return foundBall;
666 }
667 
668 
669 inline bool nodeCode0(void)
670 {
671  bool foundBall = false;
672 
673  // two virtual windows
674 
675  return foundBall;
676 }
677 
678 
679 inline bool nodeCode22()
680 {
681  bool foundBall = false; // Return value
682  uint8_t scanHeight = 4;
683  uint8_t y = 254;
684  uint8_t scanLimit = 1;
685  uint8_t foundBallNum = 0;
686 
687  if (botHeading != 0)
688  return false;
689 
690  trackColorInit(LOOK_UP);
691 
692  // scan from small ground distance to large ground distance
693  while ( y - scanHeight > scanLimit )
694  {
695  y -= scanHeight;
696  setVirtualWindow(1, y-scanHeight, 174, y);
697  if ( seeBall() )
698  {
699  foundBall = true;
700 
701  // find ball number of ball at this x
702  if( y > 148 )
703  foundBallNum = 9;
704  else if ( y > 50 )
705  foundBallNum = 11;
706  else
707  foundBallNum = 12;
708 
709  addToGoalList( foundBallNum );
710 
711  while ( seeBall() )
712  {
713  y -= scanHeight;
714  setVirtualWindow(1, y-scanHeight, 174, y);
715  }
716  }
717  }
718 
719  setServo(PAN, PAN_CENTER+panOffset);
720  setServo(TILT, TILT_FORWARD);
721  msDelay(300);
722 
723  return foundBall;
724 }
725 
726 
727 inline bool diagNodeCode(void)
728 {
729  bool foundBall = false;
730 
731  if( botHeading == N_WEST && (!checkedList[13] || !checkedList[17]) )
732  {
733  tankTurn(255,tempTweak3); // tank right
734  botHeading += 41;
735  foundBall = standardBallSearch();
736  botHeading -= 41;
737  tankTurn(255, -1*tempTweak3); // tank left
738  }
739  else if( botHeading != S_EAST && (!checkedList[14] || !checkedList[15]) )
740  {
741  tankTurn(255, -1*tempTweak3); // tank left
742  botHeading -= 41;
743  foundBall = standardBallSearch();
744  botHeading += 41;
745  tankTurn(255,tempTweak3); // tank right
746  }
747 
748  return foundBall;
749 }
750 
751 inline bool nodeCode37( void )
752 {
753  bool foundBall = false;
754 
755  // pass special values into cameraSeekLeft
756 
757  return foundBall;
758 }