% % 2D path planning example on 50x50 grid with obstacles % % Yuri Malitsky (ynm@cs.brown.edu), Chad Jenkins (cjenkins@cs.brown.edu) % Modified: September 27, 2010 % % based on Wikipedia A* entry % % call as a function with 1 argument for the search algorithm % % select search algorithm based on first input argument % 1 - Depth first search (no backtracking) % 2 - Breadth first search % 3 - Dijkstra's algorithm % 4 - greedy best-first search % 5 - A* search: heuristic: euclidean distance to goal (default) % % second and third input arguments specify 2D goal location % default: (12, 22) % % fourth and fifth input arguments specify 2D start location % default: (25, 25) % % try: % > pathplan(1) % > pathplan(2) % > pathplan(3) % > pathplan(4) % > pathplan(5) % % > pathplan(1,25,25,12,22) % > pathplan(2,25,25,12,22) % > pathplan(3,25,25,12,22) % > pathplan(4,25,25,12,22) % > pathplan(5,25,25,12,22) % % > pathplan(1,25,25,40,25) % > pathplan(2,25,25,40,25) % > pathplan(3,25,25,40,25) % > pathplan(4,25,25,40,25) % > pathplan(5,25,25,40,25) % % > pathplan(1,40,25,25,25) % > pathplan(2,40,25,25,25) % > pathplan(3,40,25,25,25) % > pathplan(4,40,25,25,25) % > pathplan(5,40,25,25,25) % function [output_args] = pathplan(algorithm,goal_x,goal_y,start_x,start_y) hold off; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % GLOBALS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % size of map size_x = 50; % size of the map along the x-axis size_y = 50; % size of the map along the y-axis size_t = size_x*size_y; % number of nodes in the map axis([1 size_x 1 size_y]) % keeps track of information about the search closedset = zeros(size_t,1); % set of nodes already evaluated came_from = [ ]; % the map of navigated nodes openset = [ ]; % set of tentative nodes % format: [node index, g_score, h_score, f_score] % node index: index of the current node % g_score: distance from start along optimal path % h_score: estimate of distance to goal node % f_score: estimated distance from start to goal % toggle display of intermediate information during search display_visited_nodes = 1; display_intermediate_set_parents = 0; display_live = 0; found = 0; % flag for visiting goal node % output variables for optimal path stat_distance = 0; % optimal distance from start to goal stat_memory = 0; % maximum amount of memory used to find the solution stat_steps = 0; % number of steps needed to find solution %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % HELPER FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % convert the (x,y) coordinate to a single index function [ out_index ] = coord_to_index( x, y ) out_index = ((y-1)*size_x + (x-1))+1; end % convert the position index to an (x,y) coordinate function [ out_x, out_y ] = index_to_coord( index ) index = index-1; out_x = mod( index, size_x )+1; out_y = floor( index / size_x )+1; end function [dist] = heuristic_distance( s, e ) [s_x, s_y] = index_to_coord(s); [e_x, e_y] = index_to_coord(e); delta_x = abs(s_x - e_x); delta_y = abs(s_y - e_y); dist = sqrt(delta_x*delta_x + delta_y*delta_y); end function [index] = shift_node( current_index, step_x, step_y ) [x,y] = index_to_coord( current_index ); index = coord_to_index( x+step_x, y+step_y ); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % HANDLE FUNCTION PARAMETERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % set default algorithm if (~exist('algorithm')) algorithm = 5; end % set goal location if (~exist('goal_x')) G(1) = 12; else G(1) = goal_x; end if (~exist('goal_y')) G(2) = 22; else G(2) = goal_y; end % set start location if (~exist('start_x')) S(1) = 25; else S(1) = start_x; end if (~exist('start_y')) S(2) = 25; else S(2) = start_y; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % SET THE MAP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % draw grid environment rectangle('Position',[1,1,size_x,size_y],'FaceColor',[0 .4 0]); hold on; % "water" obstacles oxs = 7; oys = 25; oxw = 10; oyw = 5; rectangle('Position',[oxs,oys,oxw,oyw],'FaceColor','b'); for x = oxs:(oxs+oxw) for y = oys:(oys+oyw) closedset( coord_to_index(x,y) ) = 1; end end oxs = 15; oys = 20; oxw = 2; oyw = 5; rectangle('Position',[oxs,oys,oxw,oyw],'FaceColor','b'); for x = oxs:(oxs+oxw) for y = oys:(oys+oyw) closedset( coord_to_index(x,y) ) = 1; end end oxs = 7; oys = 15; oxw = 10; oyw = 5; rectangle('Position',[oxs,oys,oxw,oyw],'FaceColor','b'); for x = oxs:(oxs+oxw) for y = oys:(oys+oyw) closedset( coord_to_index(x,y) ) = 1; end end oxs = 7; oys = 30; oxw = 3; oyw = 100; rectangle('Position',[oxs,oys,oxw,oyw],'FaceColor','b'); for x = oxs:(oxs+oxw) for y = oys:(oys+oyw) closedset( coord_to_index(x,y) ) = 1; end end % start and goal locations plot(S(1),S(2),'m.','MarkerSize',25); plot(G(1),G(2),'r.','MarkerSize',25); drawnow; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % PERFORM SEARCH %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% node_start = coord_to_index( S(1), S(2) ); node_goal = coord_to_index( G(1), G(2) ); % add the start node into the list of nodes to explore openset = [ node_start, ... 0, ... heuristic_distance( node_start, node_goal ), ... 0 + heuristic_distance( node_start, node_goal ) ]; % while there are nodes still in the tentative node list while ( size(openset,1) > 0 & found == 0 ) % keep track of statistics about the search stat_memory = max(stat_memory, size(openset,1) ); stat_steps = stat_steps + 1; % dequeue node current being visited, the node differs based on the % search algorithm being used: % DFS: node most recently added to the list of tentative nodes % BFS: node placed earliest in the list of tentative nodes % Dijkstra: node with lowest path distance to start node % greedy best-first: node with lowest straight line distance to goal % A*: node with lowest (path distance to start + straight line % distance to goal) current_index = 1; if( algorithm == 1 ) current_index = size(openset,1); end % sort nodes based on exploration priority, this is based on f_score % for A*, h_score for greedy best-first, and g_score for Dijkstra if algorithm == 4 || algorithm == 5 openset = sortrows( openset, 4 ); elseif algorithm == 3 openset = sortrows( openset, 2 ); end node_current = openset(current_index, 1); % current node index dist_current = openset(current_index, 2); % distance from the start % remove current node from list of tentative nodes if algorithm == 1 openset = openset(1:size(openset,1)-1,:); else openset = openset(2:size(openset,1),:); end % mark that the current node has been visited closedset(node_current) = 1; % if goal is found, our search is over if ( node_current == node_goal) found = 1; stat_distance = dist_current; break; end if (display_visited_nodes) [disp_x, disp_y] = index_to_coord(node_current); plot(disp_x, disp_y,'.','Color',[0.1 0.9 0.1]); if( display_live == 1 ) drawnow; end end % scan all the neighbors of the current node and add them to the list % of tentative nodes that need to be visited in the future for i = -1:1 for j = -1:1 node_neighbor = shift_node( node_current, i, j ); [c_x, c_y] = index_to_coord(node_current); % make sure neighbores are among the 4-connected nodes and % within the map if ( ~((i == 0) & (j == 0)) ... % enforce not current & (abs(i+j)==1) ... % enforce 4-connected & (c_x+i > 0) ... % boundary check & (c_y+j > 0) ... & (c_x+i <= size_x) ... & (c_y+j <= size_y) ) % skip this neighbor if it has been visited previously if ( closedset(node_neighbor) == 0 ) % distance along path through current visit node tentative_g_score = dist_current + sqrt(i*i + j*j); % check if this neighbor is already in the tentative set node_index = find( openset(:,1) == node_current ); % if this is the first time we are seeing this node, % add it to the tentative list. Otherwise if have found % a shorter path to the node, update the nodes ancestry if ( isempty(node_index) ) openset = [openset; [node_neighbor, tentative_g_score, 0, 0]]; node_index = size(openset,1); update_distance = 1; elseif ( tentative_g_score < openset(node_index,2) ) if( algorithm == 3 || algorithm == 4 || algorithm == 5 ) update_distance = 1; end else update_distance = 0; end % update the neighbor's path routing, path % distance, and exploration priority if (update_distance) if (display_visited_nodes) [disp_x_n, disp_y_n] = index_to_coord(node_neighbor); plot(disp_x_n, disp_y_n,'.','Color',[0.7 0.5 0]); end; if (display_intermediate_set_parents) plot(disp_x, disp_y,'.','Color',[.6 0 .6]); plot([c_x ;c_x+i],[c_y; c_y+j],'-'); end % update routing, meaning the best parent that %leads to this node came_from(node_neighbor) = node_current; if( algorithm == 5 ) openset(node_index,3) = heuristic_distance( node_neighbor, node_goal ); openset(node_index,4) = tentative_g_score + heuristic_distance( node_neighbor, node_goal ); elseif( algorithm == 4 || algorithm == 3 ) openset(node_index,3) = 0; openset(node_index,4) = heuristic_distance( node_neighbor, node_goal ); end end % if goal is found, our search is over if ( node_neighbor == node_goal & (algorithm == 1)) came_from(node_neighbor) = node_current; found = 1; stat_distance = tentative_g_score; break; end end end end end %if (display_visited_nodes) % [disp_x, disp_y] = index_to_coord(node_current); % plot(disp_x, disp_y,'.','Color',[0 0.9 0]); % if( display_live ) % drawnow; % end %end end % trace path from goal back to start current = node_goal; planpath=[]; stat_distance = 0; while (current ~= node_start) [c_x,c_y] = index_to_coord( current ); planpath = [planpath; [c_x, c_y]]; current = came_from( current ); stat_distance = stat_distance + 1; end [c_x,c_y] = index_to_coord( current ); planpath = [planpath; [c_x, c_y]]; % display path figure(1); plot(planpath(:,1),planpath(:,2),'y-','LineWidth',4); plot(S(1),S(2),'m.','MarkerSize',25); plot(G(1),G(2),'r.','MarkerSize',25); axis([1 size_x 1 size_y]) stat_distance stat_memory stat_steps %planpath end