3 * @brief Implementation of the A* algorithm. Main functions.
4 * @author Jose Maria Martin Laguna <jmmartin@etud.insa-toulouse.fr>
6 * @note The author asumes that the reader knows how A* algorithm
7 * works. In this documentation only the implementation is
8 * explained. More information "Principles of Robot Motion" by
9 * H. Choset&others (Algorithm 24, p531)
14 #define _ISOC99_SOURCE /* To have INFINITY */
15 #define _XOPEN_SOURCE /* To have M_SQRT2 etc. */
16 #include "aalgorithm.h"
17 #include "pathqueue.h"
27 #define SQRT2 M_SQRT2 /**< @brief Root square of two.*/
28 #define WALL_COST 1000 /**< Cost of jumping a wall. */
32 * @name A* Main Structure
35 static GraphMapCell graph[MAP_HEIGHT][MAP_WIDTH]; /**< @brief Graph structure used in A*. */
38 #ifndef CONFIG_PP_USES_RBTREE
39 static GraphMapCell *graph_queue; /**< Pointer to the first cell in the list */
41 static struct rb_root graph_queue; /**< rbtree root */
43 #define __compiler_offsetof(a,b) __builtin_offsetof(a,b) /* For GCC 4 */
44 #ifdef __compiler_offsetof
45 #define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
47 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
51 * container_of - cast a member of a structure out to the containing structure
52 * @ptr: the pointer to the member.
53 * @type: the type of the container struct this is embedded in.
54 * @member: the name of the member within the struct.
57 #define container_of(ptr, type, member) ({ \
58 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
59 (type *)( (char *)__mptr - offsetof(type,member) );})
63 static void pushNodeInOrder(GraphMapCell *cell);
64 float cFunction(int x1, int y1, int x2, int y2);
65 void calculateMapHeuristic(int xgoal, int ygoal);
66 float calculateCost(int x1, int y1, int x2, int y2);
67 void initGraphStructure(void);
68 int getPathLength(int xstart, int ystart, int xgoal, int ygoal);
71 void init_bitmap(bitmap_dim bitmap[], int bitmap_ab, int bitmap_af, int bitmap_w2);
72 bool check_bot_position_straight(int x_0, int y_0, bitmap_dim bitmap_straight[], int angle);
73 bool check_bot_position_across(int x_0, int y_0, bitmap_dim bitmap_across[], int angle);
74 bool check_bot_position(int x_0, int y_0, bitmap_dim bitmap_par[], int angle);
76 void init_bitmap_straight(bitmap_dim_straight bitmap_straight[]);
77 bool check_bot_position_streight(int x_0, int y_0, bitmap_dim_straight bitmap_straight[], int angle);
78 //int get_angle_between_cells(int x_0, int y_0, int x_1, int y_1);
79 //int get_round_actual_angle();
81 void init_bitmap_straight(bitmap_dim_straight bitmap_straight[]){
83 bitmap_straight[0].x_min=-BITMAP_AB;
84 bitmap_straight[0].x_max=BITMAP_AF;
85 bitmap_straight[0].y_min=-BITMAP_W2;
86 bitmap_straight[0].y_max=BITMAP_W2;
88 bitmap_straight[1].x_min=-BITMAP_W2;
89 bitmap_straight[1].x_max=BITMAP_W2;
90 bitmap_straight[1].y_min=-BITMAP_AB;
91 bitmap_straight[1].y_max=BITMAP_AF;
93 bitmap_straight[2].x_min=-BITMAP_AF;
94 bitmap_straight[2].x_max=BITMAP_AB;
95 bitmap_straight[2].y_min=-BITMAP_W2;
96 bitmap_straight[2].y_max=BITMAP_W2;
98 bitmap_straight[3].x_min=-BITMAP_W2;
99 bitmap_straight[3].x_max=BITMAP_W2;
100 bitmap_straight[3].y_min=-BITMAP_AF;
101 bitmap_straight[3].y_max=BITMAP_AB;
105 bool check_bot_position_streight(int x_0, int y_0, bitmap_dim_straight bitmap_straight[], int angle){
107 if (angle==0){ //angle must be in range 90-360
115 for(x=x_0-bitmap_straight[angle].x_min; x<=x_0+bitmap_straight[angle].x_max;x++){
116 for(y=y_0-bitmap_straight[angle].y_min; y<=y_0+bitmap_straight[angle].y_max;y++){
118 ShmapSetCellFlag(x,y,MAP_FLAG_PATH_SHAPE);
120 if (!ShmapIsFreeCell(x,y)){ //1 no obstacle,-1 no in map, 0 otherwise
130 ShmapSetCellFlag(x,x+q,MAP_FLAG_PATH_SHAPE);
132 // int get_angle_between_cells(int x_0, int y_0, int x_1, int y_1){
136 // angle= atan2((double)(y_1-y_0),(double)(x_1-x_0)); //angle in range -pi, pi
137 // angle=(angle*360)/(2*pi()); //angle in deg.
139 // return (int) angle;
142 // int get_round_actual_angle(){
147 // robot_get_est_pos(&x,&y,&phi);
150 // phi_int=phi_int-(phi_int % 45);
156 * Returns X and Y index of a graph cell identified by a pointer.
158 * @param[in] c Pointer to the cell in question.
159 * @param[out] x Cell's X index.
160 * @param[out] y Cell's Y index.
162 void GraphCell2XY(GraphMapCell *c, int *x, int *y)
164 *y = (c - &graph[0][0]) / MAP_WIDTH;
165 *x = (c - &graph[0][0]) % MAP_WIDTH;
170 int aAlgorithm_shape(double xstart_real,double ystart_real, double xgoal_real, double ygoal_real, GraphMapCell **original_path)
173 * prioritySet : Set of nodes to process order by heuristic distance
175 #ifndef CONFIG_PP_USES_RBTREE
178 graph_queue = RB_ROOT;
181 * nbest : Best node to reach the goal
183 GraphMapCell *nbest, *start, *goal, *contig;
185 int xcontig, ycontig, xstart, ystart, xgoal, ygoal;
186 struct map *map = ShmapIsMapInit();
189 bitmap_dim_straight bitmap_straight[4];
191 init_bitmap_straight(bitmap_straight);
194 // Transform real data in cell data
195 ShmapPoint2Cell(xstart_real, ystart_real, &xstart, &ystart, &valid);
198 start = &graph[ystart][xstart];
199 ShmapPoint2Cell(xgoal_real, ygoal_real, &xgoal, &ygoal, &valid);
202 goal = &graph[ygoal][xgoal];
206 /// <li> Init graph structure with default values.
207 initGraphStructure();
208 /// <li> Calculate Heuristic distances.
209 calculateMapHeuristic(xgoal, ygoal);
211 /// <li> Initial values : put start node in queue.
213 start->f = start->g + start->h;
214 start->backpointer = start;
215 pushNodeInOrder(start);
217 /// <li> Expand nbest :for all x E Star(nbest) that are not in C
219 //printf("*cntr %d \n", cntr);
221 /// <li> REPEAT until the queue is empty.
225 /// <li> Pick nbest from prioritySet and remove
226 #ifndef CONFIG_PP_USES_RBTREE
228 graph_queue = graph_queue->next;
230 nbest = container_of(rb_first(&graph_queue), GraphMapCell, node);
231 rb_erase(&nbest->node, &graph_queue);
234 GraphCell2XY(nbest, &x, &y);
235 /* map->cells[y][x].detected_obstacle = 255; */
236 /* usleep(100000); */
238 /// <li> add to processed
239 nbest->processed = 1;
241 /// <li> IF nbest == goal ; EXIT
246 /// <li> Expand nbest :for all x E Star(nbest) that are not in C
247 const struct { int x; int y; int angle;} neighbours[] = { { 0, 1, 90}, /*{ 1, 1},*/ { 1, 0, 0}, /*{-1, 1},*/
248 {-1, 0, 180}/*, {-1,-1}*/, { 0,-1, 270}/*, { 1,-1}*/};
250 for (i=0; i< sizeof(neighbours)/sizeof(neighbours[0]); i++) {
251 xcontig = x + neighbours[i].x;
252 ycontig = y + neighbours[i].y;
254 if(ShmapIsCellInMap(xcontig, ycontig) && (graph[ycontig][xcontig].processed == 0)){
255 double cost = cFunction(x, y, xcontig, ycontig);
256 contig = &graph[ycontig][xcontig];
257 /// <li> IF is not in Priorityset add to prioritySet.
258 if (!contig->in_queue) {
259 // if(ShmapIsFreeCell(xcontig, ycontig)) {
260 // // add to prioritySet.
261 // contig->g=nbest->g + cost;
262 // contig->f = contig->g + contig->h;
263 // contig->backpointer=nbest;
264 // pushNodeInOrder(contig);
266 //int angle=get_angle_between_cells(x,y,xcontig,ycontig);
267 if(check_bot_position_streight(xcontig,ycontig,bitmap_straight,neighbours[i].angle)){
268 // add to prioritySet.
269 contig->g=nbest->g + cost;
270 contig->f = contig->g + contig->h;
271 contig->backpointer=nbest;
272 pushNodeInOrder(contig);
275 } else if (nbest->g + cost < contig->g) {
276 /// <li> ELSE update backpointer to point to nbest.
277 contig->backpointer=nbest;
278 contig->g = nbest->g + cost;
283 #ifndef CONFIG_PP_USES_RBTREE
284 } while (graph_queue);
286 } while (!RB_EMPTY_ROOT(&graph_queue));
293 // Clear previous path
294 for (y=0;y<MAP_HEIGHT;y++){
295 for(x=0;x<MAP_WIDTH;x++){
296 map->cells[y][x].flags &= ~(MAP_FLAG_PATH | MAP_FLAG_START | MAP_FLAG_GOAL);
301 /* Construct forward list for the found path. */
305 while (cell != start) {
306 cell->backpointer->next = cell;
307 cell = cell->backpointer;
309 GraphCell2XY(cell, &x, &y);
310 map->cells[y][x].flags |= MAP_FLAG_PATH;
313 map->cells[ystart][xstart].flags |= MAP_FLAG_START | MAP_FLAG_PATH;
314 map->cells[ygoal][xgoal].flags |= MAP_FLAG_GOAL | MAP_FLAG_PATH;
316 *original_path = start;
323 * @name A* Main function
328 * @brief Search the shortest path between two points
329 * @param xstart_real Coordonate X of the start point
330 * @param ystart_real Coordonate Y of the start point
331 * @param xgoal_real Coordonate X of the goal point
332 * @param ygoal_real Coordonate Y of the goal point
333 * @param original_path [out] Pointer to the first map cell of the
334 * path is stored here. Use
335 * GraphMapCell.next to traverse the found
338 * @return -1 if the goal is not founded, the number of points to the goal is founded on success.
340 * This is an implementation of A* Algorithm (Algorithm 24, p531)
341 * defined in the book "Principles of Robot Motion" by
345 int aAlgorithm(double xstart_real,double ystart_real, double xgoal_real, double ygoal_real, GraphMapCell **original_path)
348 * prioritySet : Set of nodes to process order by heuristic distance
350 #ifndef CONFIG_PP_USES_RBTREE
353 graph_queue = RB_ROOT;
356 * nbest : Best node to reach the goal
358 GraphMapCell *nbest, *start, *goal, *contig;
360 int xcontig, ycontig, xstart, ystart, xgoal, ygoal;
361 struct map *map = ShmapIsMapInit();
365 // Transform real data in cell data
366 ShmapPoint2Cell(xstart_real, ystart_real, &xstart, &ystart, &valid);
369 start = &graph[ystart][xstart];
370 ShmapPoint2Cell(xgoal_real, ygoal_real, &xgoal, &ygoal, &valid);
373 goal = &graph[ygoal][xgoal];
377 /// <li> Init graph structure with default values.
378 initGraphStructure();
379 /// <li> Calculate Heuristic distances.
380 calculateMapHeuristic(xgoal, ygoal);
382 /// <li> Initial values : put start node in queue.
384 start->f = start->g + start->h;
385 start->backpointer = start;
386 pushNodeInOrder(start);
388 /// <li> Expand nbest :for all x E Star(nbest) that are not in C
390 //printf("*cntr %d \n", cntr);
392 /// <li> REPEAT until the queue is empty.
396 /// <li> Pick nbest from prioritySet and remove
397 #ifndef CONFIG_PP_USES_RBTREE
399 graph_queue = graph_queue->next;
401 nbest = container_of(rb_first(&graph_queue), GraphMapCell, node);
402 rb_erase(&nbest->node, &graph_queue);
405 GraphCell2XY(nbest, &x, &y);
406 /* map->cells[y][x].detected_obstacle = 255; */
407 /* usleep(100000); */
409 /// <li> add to processed
410 nbest->processed = 1;
412 /// <li> IF nbest == goal ; EXIT
417 /// <li> Expand nbest :for all x E Star(nbest) that are not in C
418 const struct { int x; int y; } neighbours[] = { { 0, 1}, { 1, 1}, { 1, 0}, {-1, 1},
419 {-1, 0}, {-1,-1}, { 0,-1}, { 1,-1}};
421 for (i=0; i< sizeof(neighbours)/sizeof(neighbours[0]); i++) {
422 xcontig = x + neighbours[i].x;
423 ycontig = y + neighbours[i].y;
425 if(ShmapIsCellInMap(xcontig, ycontig) && (graph[ycontig][xcontig].processed == 0)){
426 double cost = cFunction(x, y, xcontig, ycontig);
427 contig = &graph[ycontig][xcontig];
428 /// <li> IF is not in Priorityset add to prioritySet.
429 if (!contig->in_queue) {
430 if(ShmapIsFreeCell(xcontig, ycontig)) {
431 // add to prioritySet.
432 contig->g=nbest->g + cost;
433 contig->f = contig->g + contig->h;
434 contig->backpointer=nbest;
435 pushNodeInOrder(contig);
437 } else if (nbest->g + cost < contig->g) {
438 /// <li> ELSE update backpointer to point to nbest.
439 contig->backpointer=nbest;
440 contig->g = nbest->g + cost;
445 #ifndef CONFIG_PP_USES_RBTREE
446 } while (graph_queue);
448 } while (!RB_EMPTY_ROOT(&graph_queue));
455 // Clear previous path
456 for (y=0;y<MAP_HEIGHT;y++){
457 for(x=0;x<MAP_WIDTH;x++){
458 map->cells[y][x].flags &= ~(MAP_FLAG_PATH | MAP_FLAG_START | MAP_FLAG_GOAL);
463 /* Construct forward list for the found path. */
467 while (cell != start) {
468 cell->backpointer->next = cell;
469 cell = cell->backpointer;
471 GraphCell2XY(cell, &x, &y);
472 map->cells[y][x].flags |= MAP_FLAG_PATH;
475 map->cells[ystart][xstart].flags |= MAP_FLAG_START | MAP_FLAG_PATH;
476 map->cells[ygoal][xgoal].flags |= MAP_FLAG_GOAL | MAP_FLAG_PATH;
478 *original_path = start;
487 * @name A* related functions
488 * This functions have a meaning in theorical A*.
493 * @brief Calculates the lenght of edge connecting two points
494 * @param x1 Coordonate X of the first cell
495 * @param y1 Coordonate Y of the first cell
496 * @param x2 Coordonate X of the second cell
497 * @param y2 Coordonate Y of the second cell
498 * @return Lenght of edge
501 float cFunction(int x1, int y1, int x2, int y2)
504 //if(GETMAPPOS(x2, y2)== MAP_WALL) c = WALL_COST;
505 if(!ShmapIsFreeCell(x2,y2)) c = WALL_COST;
506 else if( (x1==x2) || (y1 == y2) ) c=1.0;
516 * @brief Calculates Map Heuristic values.
517 * @param xgoal Coordonate X of the goal
518 * @param ygoal Coordonate Y of the goal
520 * This function calculates the map heuristic values (the distance to
521 * the goal that is supoused to be the shortest).
523 * The chosen method has been the euclidean distance. The distance
524 * between a point of the grid \f$(x,y)\f$ and the
525 * goal\f$(x_(goal),y_(goal))\f$ is
526 * \f$\sqrt{(x_(goal)-x)^2+(y_(goal)-y)^2}\f$.
528 * Another method explained in the book is coded but not used.It is
529 * called Brushfire algorithm. (section 4.3.2 of the book)
532 void calculateMapHeuristic(int xgoal, int ygoal)
535 for(i=0;i<MAP_HEIGHT;i++){
536 for (j=0;j<MAP_WIDTH;j++){
537 graph[i][j].h=sqrt(abs(xgoal-j)*abs(xgoal-j)+abs(ygoal-i)*abs(ygoal-i));
541 /* This is another way to compute heuristic without SQRT but it don't work properly */
544 NodeQueue * toProcess = malloc(sizeof(NodeQueue));
545 int xcontig, ycontig;
546 float h; // Heuristic distance of cell
547 float newh; // New Heuristic distance of cell
549 graph[ygoal][xgoal].h=0;
550 graph[ygoal][xgoal].processed=1;
552 pushNode(toProcess, xgoal, ygoal);
553 while(!queueIsEmpty(toProcess)){
554 //printPrioritySet(toProcess);
555 // Pop the first node in the queue
556 popNode(toProcess,&n);
557 graph[n.y][n.x].processed=1;
558 // Take node's heuristic distance
559 h = graph[n.y][n.x].h;
561 // Cross contiguous cells
562 for (xcontig = n.x-1 ; xcontig <= n.x + 1 ; xcontig++){
563 for (ycontig = n.y - 1 ; ycontig <= n.y + 1 ; ycontig++){
564 if(ShmapIsCellInMap(xcontig, ycontig) && (graph[ycontig][xcontig].processed==0)) {
565 //Add node to priority queue
566 if ((!isInQueue(toProcess,xcontig,ycontig))) pushNode(toProcess,xcontig,ycontig);
567 // Update heuristic if [ (Heuristic not set) OR [oldHeuristic is higher than newer)]
568 newh= h + calculateCost(n.x, n.y, xcontig, ycontig);
569 if( ( (graph[ycontig][xcontig].h < 0) || (graph[ycontig][xcontig].h > newh )) )
570 graph[ycontig][xcontig].h=newh ;
577 // Clear proccessed flag
578 for(i=0;i<MAP_HEIGHT;i++){
579 for (j=0;j<MAP_WIDTH;j++){
580 graph[i][j].processed=0;
588 * @brief Calculates the cost of moving from the first cell to the second cell.
589 * @param x1 Coordonate X of the first cell
590 * @param y1 Coordonate Y of the first cell
591 * @param x2 Coordonate X of the second cell
592 * @param y2 Coordonate Y of the second cell
594 * @warning Cells must be contiguous
596 float calculateCost(int x1, int y1, int x2, int y2){
598 if( (x1==x2) || (y1 == y2) ) return 1.0;
606 * @name A* Implementation Functions
607 * This functions have not a meaning in A* algorithm, but are needed for implementation.
612 * @brief Init Graph structure with default values
615 void initGraphStructure(void){
618 GraphMapCell g = { .h = -1, .g = -1, .f = INFINITY, .backpointer = NULL,
619 .processed = false, .in_queue = false, .next = NULL };
621 //Initialisation of graph structure
622 for(i=0;i<MAP_HEIGHT;i++){
623 for (j=0;j<MAP_WIDTH;j++){
630 * @brief Push the cell c in the queue ordered by heuristic value of the cell
634 * @todo Use some tree based data structure for this. -- Sojka
637 void pushNodeInOrder(GraphMapCell *c)
641 #ifndef CONFIG_PP_USES_RBTREE
642 GraphMapCell *current, *last;
646 // Put the node in right position
649 while (current!=NULL){
650 if (c->f < current->f ) {
651 if (last==NULL) graph_queue = c;
657 current=current->next;
660 // the node goes in the last position
665 struct rb_node **new = &(graph_queue.rb_node), *parent = NULL;
666 /* Figure out where to put new node */
668 GraphMapCell *this = container_of(*new, GraphMapCell, node);
672 new = &((*new)->rb_left);
674 new = &((*new)->rb_right);
677 /* Add new node and rebalance tree. */
678 rb_link_node(&c->node, parent, new);
679 rb_insert_color(&c->node, &graph_queue);