Caddy
A 2005 Roborodentia entry with vision and path planning capability
 All Data Structures Files Functions Variables Typedefs Macros Pages
path_planning.c
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  */
17 #include "path_planning.h"
18 #include "robot_control.h"
19 #include "servos.h"
20 #include "node_list.h"
21 #include "lcd_16x2.h"
22 #include "permutation.h"
23 #include "utility.h"
24 
25 // avr-libc
26 #include <string.h>
27 #include <stdlib.h>
28 
29 #define NO_PARENT 0xff
30 #define MAX_COST 0xff
31 
32 typedef struct SearchNode
33 {
34  uint8_t parent;
35  uint8_t pathCost;
36  bool visited;
38 
39 typedef struct PathListNode
40 {
41  uint8_t nodeNum;
42  struct PathListNode *nextNode;
44 
45 //
46 // Static global variables
47 //
48 static SearchNodeType searchSpace[NUM_NODES];
49 uint8_t numKnownGoals = NUM_FIXED_GOALS;
50 
51 //
52 // Global variables
53 //
54 uint8_t pathList[MAX_PATH_LIST_SIZE];
55 uint8_t pathListIndex = 0;
56 
57 // initialized in initBotGlobals
58 static uint8_t goalList[NUM_GOALS];
59 static uint8_t goalListSize = 0;
60 
61 static inline uint8_t uniformCostSearch( uint8_t startNode, uint8_t goalNode /*, SEARCH_NODE searchSpace[]*/ );
62 static inline PathListNodeType * addNodeByCost(PathListNodeType *head, uint8_t newNodeNum /*, SEARCH_NODE searchSpace[]*/ );
63 static inline void recostructPath( uint8_t startNode, uint8_t goalNode /*, SEARCH_NODE searchSpace[]*/ );
64 static inline uint8_t updatePathTo( uint8_t nodeNum );
65 static inline void freeList(PathListNodeType *head);
66 static inline PathListNodeType * freeNode(PathListNodeType *head);
67 
68 // returns distance of path found
69 uint8_t updatePath(void)
70 {
71  uint8_t startCost[NUM_GOALS];
72  uint8_t goalCost[NUM_GOALS][NUM_GOALS];
73  uint8_t stopCost[NUM_GOALS];
74 
75  uint8_t tempGoalList[NUM_GOALS];
76 
77  uint8_t curPerm[NUM_GOALS];
78  uint8_t bestPerm[NUM_GOALS];
79  uint8_t bestCost;
80 
81  uint8_t i;
82  uint8_t j;
83  uint8_t tempCost;
84 
85  // find distances between goal nodes and start/stop nodes
86  for (i = 0; i < goalListSize; i++)
87  {
88  startCost[i] = uniformCostSearch(botNode, goalList[i]);
89  stopCost[i] = uniformCostSearch(goalList[i], STOP_NODE);
90  }
91 
92  // find distances between every goal node
93  for (i = 0; i < goalListSize; i++)
94  {
95  for (j = 0; j <= i; j++)
96  {
97  if (i == j)
98  goalCost[i][j] = 0;
99  else
100  {
101  tempCost = uniformCostSearch(goalList[i], goalList[j]);
102  goalCost[i][j] = tempCost;
103  goalCost[j][i] = tempCost;
104  }
105  }
106  }
107 
108  // initialize list for permutations
109  for (i = 0; i < goalListSize; i++)
110  {
111  curPerm[i] = i;
112  }
113 
114  bestCost = MAX_COST;
115 
116  // make sure goalListSize > 0
117  if (goalListSize == 0)
118  {
119  // set pathList to STOP_NODE
120  return updatePathTo(STOP_NODE);
121  }
122 
123  do
124  {
125  // Find distance of this permutation
126  tempCost = startCost[curPerm[0]];
127  for (i = 0; i < goalListSize - 1; i++)
128  tempCost += goalCost[curPerm[i]][curPerm[i + 1]];
129  tempCost += stopCost[curPerm[i]];
130 
131  // save tempCost and perm, if this permutation is better
132  if (tempCost < bestCost)
133  {
134  bestCost = tempCost;
135  memcpy(bestPerm, curPerm, goalListSize * sizeof(*bestPerm));
136  }
137 
138  } while (generateNextPermutation(curPerm, curPerm + goalListSize));
139 
140  // Re-order goalList according to bestPerm.
141  memcpy(tempGoalList, goalList, goalListSize * sizeof(*tempGoalList));
142  for (i = 0; i < goalListSize; i++)
143  goalList[i] = tempGoalList[bestPerm[i]];
144 
145  // Replace current projectedPath with newly found best path
146  pathListIndex = MAX_PATH_LIST_SIZE - 1;
147  uniformCostSearch(goalList[goalListSize - 1], STOP_NODE);
148  recostructPath(goalList[goalListSize - 1], STOP_NODE);
149  for (i = goalListSize - 1; i > 0; i--)
150  {
151  uniformCostSearch(goalList[i - 1], goalList[i]);
152  recostructPath(goalList[i - 1], goalList[i]);
153  }
154  uniformCostSearch(botNode, goalList[0]);
155  recostructPath(botNode, goalList[0]);
156 
157  //printPathList();
158 
159  return bestCost;
160 }
161 
162 inline void addToGoalList(uint8_t nodeNum)
163 {
164  if (!isInGoalList(nodeNum))
165  {
166  goalList[goalListSize++] = nodeNum;
167  numKnownGoals++;
168  }
169 }
170 
171 bool removeFromGoalList(uint8_t nodeNum)
172 {
173  int8_t i = findValue(goalList, goalListSize, nodeNum);
174  if (i == -1)
175  {
176  numUnreachedGoals--;
177  numKnownGoals++;
178  return false;
179  }
180 
181  for (; i < goalListSize - 1; i++)
182  {
183  goalList[i] = goalList[i + 1];
184  }
185  goalListSize--;
186  numUnreachedGoals--;
187 
188  return true;
189 }
190 
191 inline void adjustNumKnownGoals( int8_t adjustment )
192 {
193  numKnownGoals += adjustment;
194 }
195 
196 inline bool allGoalsFound(void)
197 {
198  return numKnownGoals >= NUM_GOALS;
199 }
200 
201 inline bool isInGoalList(uint8_t nodeNum)
202 {
203  return findValue(goalList, goalListSize, nodeNum) != -1;
204 }
205 
206 inline void printGoalList(void)
207 {
208 #if DEBUGGING
209  uint8_t i;
210 
211  lcdWriteStr(" ", 0, 0);
212 
213  if (goalListSize == 0)
214  {
215  lcdWriteStr("(empty)", 0, 0);
216  } else
217  {
218  for (i = 0; i < goalListSize; i++)
219  {
220  lcdPrintDecU08(goalList[i], 0, 3 * i);
221  }
222  }
223 #endif
224 }
225 
226 // returns 0 if no ball is in goalList
227 uint8_t getNextBallNodeNum(void)
228 {
229  uint8_t i = 1;
230  uint8_t nodeNum = pathList[pathListIndex + i];
231  while (isBallNode(nodeNum))
232  {
233  if (isInGoalList(nodeNum))
234  {
235  return nodeNum;
236  }
237  i++;
238  nodeNum = pathList[pathListIndex + i];
239  }
240 
241  return 0;
242 }
243 
249 inline int8_t getNextHeading(void)
250 {
251  GraphNodeType nextNode; // info about nodes adjacent to botNode
252  int8_t nextNodeIndex; // nextNode offset to nextBotNode
253  int8_t nextHeading; // absolute direction to nextBotNode
254 
255  // get absolute direction of nextBotNode from node list
256  getNode(botNode, &nextNode);
257  nextNodeIndex = findValue(nextNode.adjNodes,
258  nextNode.numAdjNodes,
259  pathList[pathListIndex + 1]);
260 
261  // get next heading or report error
262  if (nextNodeIndex == -1)
263  {
264  fatalError("pathList error", "");
265  }
266  nextHeading = nextNode.adjHeadings[nextNodeIndex];
267 
268  return nextHeading;
269 }
270 
271 
272 // returns distance of path found
273 uint8_t updatePathTo(uint8_t nodeNum)
274 {
275  uint8_t tempCost = uniformCostSearch(botNode, nodeNum);
276  pathListIndex = MAX_PATH_LIST_SIZE - 1;
277  recostructPath(botNode, nodeNum);
278  return tempCost;
279 }
280 
281 // fills searchSpace with parent information
282 // returns distance of path found
283 inline uint8_t uniformCostSearch(uint8_t startNode, uint8_t goalNode)
284 {
285  GraphNodeType nodeData;
286  uint8_t n;
287 
288  PathListNodeType *visitList = NULL;
289  uint8_t curNodeNum = startNode;
290  uint8_t curCost = 0;
291 
292  // init searchSpace
293  uint8_t i;
294  for (i = 0; i < NUM_NODES; i++)
295  {
296  searchSpace[i].parent = NO_PARENT;
297  searchSpace[i].pathCost = MAX_COST;
298  searchSpace[i].visited = false;
299  }
300 
301  searchSpace[startNode].pathCost = 0;
302  searchSpace[startNode].visited = true;
303  while (curNodeNum != goalNode)
304  {
305  // explore successors, if haven't visited yet
306  getNode(curNodeNum, &nodeData);
307  for (i = 0; i < nodeData.numAdjNodes; i++)
308  {
309  n = nodeData.adjNodes[i];
310  // if not visited and not found yet, add to visit list
311  if (!searchSpace[n].visited && searchSpace[n].pathCost == MAX_COST)
312  {
313  searchSpace[n].parent = curNodeNum;
314  searchSpace[n].pathCost = curCost + nodeData.adjCosts[i];
315  visitList = addNodeByCost(visitList, n /*, searchSpace*/);
316  }
317  }
318 
319  // visit next node w/ lowest cost, if one exists
320  if (visitList == NULL )
321  {
322  fatalError("uniformCostSearc", "error ");
323  return MAX_COST; // b/c path not found
324  }
325  else
326  {
327  // get info of and remove first node of visitList
328  curNodeNum = visitList->nodeNum;
329  curCost = searchSpace[curNodeNum].pathCost;
330  searchSpace[curNodeNum].visited = true;
331  visitList = freeNode(visitList);
332  }
333  }
334 
335  freeList(visitList);
336 
337  return searchSpace[goalNode].pathCost;
338 }
339 
340 PathListNodeType * addNodeByCost(PathListNodeType *head, uint8_t newNodeNum)
341 {
342  PathListNodeType *current = NULL;
343  PathListNodeType *next = NULL;
344  PathListNodeType *newNode = NULL; // node being inserted
345 
346  // allocate space for new node
347  newNode = (PathListNodeType *) malloc(sizeof(PathListNodeType));
348  newNode->nodeNum = newNodeNum;
349  newNode->nextNode = NULL;
350 
351  //add to empty list
352  if (head == NULL )
353  {
354  return newNode;
355  }
356 
357  //add to front of list
358  if (searchSpace[head->nodeNum].pathCost > searchSpace[newNodeNum].pathCost)
359  {
360  next = head;
361  head = newNode;
362  head->nextNode = next;
363  }
364 
365  //add after head
366  else
367  {
368  current = head;
369  next = head->nextNode;
370 
371  while (next != NULL )
372  {
373  if (searchSpace[next->nodeNum].pathCost >= searchSpace[newNodeNum].pathCost)
374  break;
375 
376  current = current->nextNode;
377  next = next->nextNode;
378  }
379 
380  newNode->nextNode = next;
381  current->nextNode = newNode;
382  }
383 
384  return head;
385 }
386 
387 
388 // fills pathList in reverse order (from goalNode to startNode)
389 // PRE: searchSpace has been filled with parent info
390 // pathListIndex points to the first position to fill
391 // pathList is large enough to prevent pathListIndex from reaching below zero
392 // POST: pathListIndex points to first node in pathList
393 inline void recostructPath(uint8_t startNode, uint8_t goalNode)
394 {
395  pathList[pathListIndex] = goalNode;
396  while (goalNode != startNode)
397  {
398  goalNode = searchSpace[goalNode].parent;
399  pathListIndex--;
400  pathList[pathListIndex] = goalNode;
401  }
402 }
403 
404 // Frees all nodes in list at head
405 inline void freeList(PathListNodeType *head)
406 {
407  while (head != NULL)
408  {
409  head = freeNode(head);
410  }
411 }
412 
413 // Frees node at head and returns pointer to next node
414 inline PathListNodeType * freeNode(PathListNodeType *head)
415 {
416  PathListNodeType *temp = NULL;
417  temp = head;
418  head = head->nextNode;
419  free(temp);
420  return head;
421 }