├── .gitignore ├── README.md ├── launch └── bringup.launch.py ├── media ├── bfs.webm ├── bfs_b.gif └── playground.png ├── package.xml ├── pathplanners ├── __init__.py ├── breadthfirstsearch.py ├── dijkstra.py └── map.py ├── resource └── pathplanners ├── rviz └── visualizer.rviz ├── setup.cfg ├── setup.py └── test ├── test_copyright.py ├── test_flake8.py └── test_pep257.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | pathplanners/__pycache__/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Path-Planning-Algorithms-with-ROS2 2 | 3 |
4 | Table of Contents 5 |
    6 |
  1. About
  2. 7 |
  3. Description of Algorithms
  4. 8 |
  5. Using this Project
  6. 9 |
10 |
11 | 12 | 13 | **ToDo:** 14 | 15 | - Potential function for the complex algo 16 | - Explore neighbors 17 | - Takes a node and returns all its neighbor 18 | - Should this be encapsulated in base Algorithm class? 19 | 20 | 22 | 23 | 24 | 25 | ## About 26 | 27 | ### Design aspects of system 28 | 29 | Preliminary data such as map with obstacle, initial and goal position, common to all algorithms will be provided with node - `map_node` 30 | 31 | Each algorithms can be executed using its own python node. 32 | 33 | Complex algorithms which requires functionalities present in simpler can be derived easily as the codebase it OOP based. 34 | 35 |

36 | PlayGround 37 |

38 |

39 | 40 |

41 | 42 | 43 | **bringup.launch.py** 44 | 45 | ```python 46 | Launches rviz2 for visualization of algorithms and 47 | map_node which provided the preliminary data 48 | ``` 49 | 50 | **map_node** 51 | 52 | ```python 53 | Has the preliminary map defined. 54 | Publishes 55 | Map with obstacles 56 | initial and goal pose embedded within the map 57 | 58 | map data is first stored in 2d matrix represented as x, y 59 | 60 | Instead of adjacency list, the preliminary map is transmitted. 61 | 62 | Serves service `get_map` requested by individual algorithms for preliminary map. 63 | Since this wont be changing over time and will be required only once service has been used, 64 | instead of topic. 65 | ``` 66 | 67 | **algo_nodes** 68 | 69 | ```python 70 | Waits for the GetMap service and requests the service for map with obstacles with initial and goal pose. 71 | Instead of using sync service, using a flag to wait for the response. 72 | 73 | Service callback method converts the data into 2D matrix for manipulation. 74 | 75 | Within the timer loop BFS traversal is performed and each node is marked visited denoted in the map itself. 76 | After each iteration of master loop color of visited cells is increased in spectrum for visualization. 77 | 78 | Path is back tracked using `parent` attribute and path is generated. 79 | ``` 80 | 81 | 82 | ### Color Scheme of CostMap: 83 | 84 | ```python 85 | -128 to -2 => RED to YELLOW 86 | -1 => GREY 87 | 0 => BLACK 88 | 1 to 98 => BLUE to RED 89 | 99 => CYAN 90 | 100 => PINK 91 | 101 to 127 => GREEN 92 | ``` 93 | 94 | ```python 95 | YELLOW => START POINT 96 | BLUE => END POINT 97 | GREEN => GENERATED PATH 98 | Explored area has a gradient from YELLOW to BLUE for each iteration. 99 | ``` 100 | 101 | ## Algorithms 102 | 103 | ### Fundamentals 104 | 105 |

106 | Breadth First Search 107 |

108 | 109 |

110 | 111 |

112 | 113 | 114 | ```python 115 | Explores the neighbor nodes first, before moving to the next level of neighbors. 116 | Utilizes queue to store the node yet to be visited. 117 | ``` 118 |

119 | Depth First Search 120 |

121 | 122 | ```python 123 | Plunges depth first into a graph without regard for which edge it take next 124 | until it cannot go further at which point it backtracks and continues. 125 | ``` 126 | 127 |

128 | Minimal Spanning Tree 129 |

130 | 131 | 132 | 133 | ### Grid-based search algorithms 134 | - dijkstra 135 | - greedy 136 | - A* 137 | - D* 138 | - Uniform Cost Search 139 | - Best-First Searching 140 | - A* 141 | - Bidirectional A* 142 | - Anytime Repairing A* 143 | - Learning Real-time A* (LRTA*) 144 | - Real-time Adaptive A* (RTAA*) 145 | - Lifelong Planning A* (LPA*) 146 | - Dynamic A* (D*) 147 | - D* Lite 148 | - Anytime D* 149 | - Dynamic Window Approach. 150 | 151 | ### Sampling-based search algorithms 152 | - Rapidly-Exploring Random Trees RRT 153 | - RRT* 154 | - Probabilistic Roadmap (PRM) 155 | - RRT 156 | - RRT-Connect 157 | - Extended-RRT 158 | - Dynamic-RRT 159 | - RRT* 160 | - Informed RRT* 161 | - RRT* Smart 162 | - Anytime RRT* 163 | - Closed-Loop RRT* 164 | - Spline-RRT* 165 | - Fast Marching Trees (FMT*) 166 | - Batch Informed Trees (BIT*) 167 | 168 | ### Potential Field Algorithms 169 | 170 | - Artificial Potential Field (APF) 171 | 172 | ### Cell Decomposition Algorithms: 173 | 174 | - Voronoi Diagrams 175 | - Visibility Graphs: 176 | 177 | ## Optimal Control Algorithms: 178 | 179 | - Trajectory Optimization 180 | - Model Predictive Control (MPC) 181 | 182 | ### Search-Based Algorithms: 183 | 184 | -Probabilistic Graph Search 185 | - Anytime Algorithm 186 | 187 | ### Sampling-Based Hybrid Methods: 188 | 189 | - PRM* (Probabilistic Roadmap Star) 190 | - Informed RRT* 191 | 192 | ## Using this Project 193 | 194 | Move into your workspace's src folder 195 | ``` 196 | cd ~/ros2_ws/src 197 | ``` 198 | Clone the project 199 | ``` 200 | git clone 201 | ``` 202 | Build the project. 203 | ``` 204 | cd ~/ros2_ws && colcon build 205 | ``` 206 | 207 | Launch rviz2 and map_node using bringup launch file 208 | ``` 209 | ros2 launch pathplanners bringup.launch.py 210 | ``` 211 | 212 | To see individual algorithms in action, run individual scripts. 213 | ``` 214 | ros2 run pathplanners 215 | ``` 216 | 217 | Executable list 218 | 219 | ```python 220 | - bfs 221 | ``` 222 | Replace these with to run the specific algorithm 223 | 224 | 225 | ## SiteMap 226 | 227 | ```python 228 | ├── launch 229 | │   └── bringup.launch.py 230 | │ 231 | ├── media 232 | │   ├── bfs.gif 233 | │   ├── bfs.webm 234 | │   └── playground.png 235 | │ 236 | ├── pathplanners 237 | │   ├── breadthfirstsearch.py 238 | │   ├── __init__.py 239 | │   ├── map.py 240 | │ 241 | ├── rviz 242 | │   └── visualizer.rviz 243 | │ 244 | ├── README.md 245 | │ 246 | ├── package.xml 247 | ├── setup.cfg 248 | └── setup.py 249 | 250 | ``` -------------------------------------------------------------------------------- /launch/bringup.launch.py: -------------------------------------------------------------------------------- 1 | import os 2 | from ament_index_python.packages import get_package_share_directory 3 | from launch import LaunchDescription 4 | from launch_ros.actions import Node 5 | 6 | 7 | def generate_launch_description(): 8 | 9 | rviz_config = os.path.join(get_package_share_directory('pathplanners'), 10 | 'rviz', 'visualizer.rviz') 11 | 12 | rviz2_launcher = Node( 13 | package='rviz2', 14 | executable='rviz2', 15 | name='rviz2', 16 | output='screen', 17 | arguments=['-d', rviz_config] 18 | ) 19 | 20 | ref_map_launcher = Node( 21 | package='pathplanners', 22 | executable='map_node', 23 | ) 24 | 25 | algo_map_launcher = Node( 26 | package='pathplanners', 27 | executable='bfs', 28 | ) 29 | 30 | static_transform_publisher = Node( 31 | package='tf2_ros', 32 | executable='static_transform_publisher', 33 | name='static_transform_publisher', 34 | namespace='', 35 | output='screen', 36 | arguments=['0.0', '0.0', '0.0', '0.0', '0.0', '0.0', 'map', 'algo_map'] 37 | ) 38 | ld = LaunchDescription() 39 | 40 | ld.add_action(rviz2_launcher) 41 | ld.add_action(ref_map_launcher) 42 | ld.add_action(static_transform_publisher) 43 | # ld.add_action(algo_map_launcher) 44 | 45 | 46 | return ld -------------------------------------------------------------------------------- /media/bfs.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maker-ATOM/Path-Planning-Algorithms/d4353017ead9b72df8c05ceb5a2bb6b877069e6f/media/bfs.webm -------------------------------------------------------------------------------- /media/bfs_b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maker-ATOM/Path-Planning-Algorithms/d4353017ead9b72df8c05ceb5a2bb6b877069e6f/media/bfs_b.gif -------------------------------------------------------------------------------- /media/playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maker-ATOM/Path-Planning-Algorithms/d4353017ead9b72df8c05ceb5a2bb6b877069e6f/media/playground.png -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pathplanners 5 | 0.0.0 6 | TODO: Package description 7 | aditya 8 | TODO: License declaration 9 | 10 | rclpy 11 | 12 | ament_copyright 13 | ament_flake8 14 | ament_pep257 15 | python3-pytest 16 | 17 | 18 | ament_python 19 | 20 | 21 | -------------------------------------------------------------------------------- /pathplanners/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maker-ATOM/Path-Planning-Algorithms/d4353017ead9b72df8c05ceb5a2bb6b877069e6f/pathplanners/__init__.py -------------------------------------------------------------------------------- /pathplanners/breadthfirstsearch.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | from collections import deque 4 | 5 | import rclpy 6 | from rclpy.node import Node 7 | 8 | from nav_msgs.msg import OccupancyGrid 9 | from custom_interfaces.msg import AdjacencyList, Neighbors, Point 10 | from std_msgs.msg import Empty 11 | 12 | from nav_msgs.srv import GetMap 13 | 14 | 15 | class BFS(Node): 16 | 17 | def __init__(self): 18 | 19 | # constants 20 | self.FREE_SPACE = -1 21 | self.START = -2 22 | self.GOAL = 1 23 | self.TRAVERSAL_COLOR_INCREMENT = 0.104 24 | self.FINAL_COLOR = -127 25 | self.TRAVERSAL_COLOR_UPDATE = 98 26 | 27 | # Flag instead of sync service call 28 | self.received_data = False 29 | 30 | # transmitted on topic 31 | self.map_data = GetMap.Response() 32 | 33 | # start and end points 34 | self.initial_point = Point() 35 | self.goal_point = Point() 36 | 37 | # map 38 | self.map = [] 39 | self.parent_map = [] 40 | 41 | self.queue = [] 42 | 43 | self.color = self.START 44 | 45 | self.goal_reached = False 46 | 47 | # Node setup 48 | super().__init__('bfs_traversal') 49 | self.traversal_pub = self.create_publisher(OccupancyGrid, 'algo_viz', 10) 50 | self.get_map_service = self.create_client(GetMap, 'get_map') 51 | 52 | # timer to call master_callback repetitively 53 | self.timer_period = 0.005 # seconds 54 | self.timer = self.create_timer(self.timer_period, self.master_callback) 55 | 56 | # Wait until service is available 57 | while not self.get_map_service.wait_for_service(timeout_sec=1.0): 58 | self.get_logger().warn('Service not available, waiting again...') 59 | 60 | # Request for initial goal pose 61 | request = GetMap.Request() 62 | self.get_map_future = self.get_map_service.call_async(request) 63 | self.get_map_future.add_done_callback(self.get_map_callback) 64 | 65 | def get_map_callback(self, future): 66 | self.received_data = True 67 | 68 | self.map_data = future.result() 69 | self.map_data.map.header.frame_id = 'algo_map' 70 | 71 | # extract height and width of map 72 | height = self.map_data.map.info.height 73 | width = self.map_data.map.info.width 74 | 75 | map_list = list(self.map_data.map.data) 76 | 77 | # generate the 2D array 78 | 79 | for j in range(0, width): 80 | row = [] 81 | for i in range(0, height): 82 | row.append(map_list[width*j + i]) 83 | if row[-1] == self.GOAL: 84 | self.goal_point.x = i 85 | self.goal_point.y = j 86 | if row[-1] == self.START: 87 | self.initial_point.x = i 88 | self.initial_point.y = j 89 | self.map.append(row) 90 | 91 | # initial_point to queue 92 | self.queue = deque([self.initial_point]) 93 | 94 | # set parent map 95 | self.parent_map = [[-1] * height for _ in range(width)] 96 | 97 | # required to generate the map 98 | self.path_node = self.goal_point 99 | 100 | self.get_logger().info("Received map data") 101 | self.get_logger().info(f"Initial pose => x: {self.initial_point.x}, y: {self.initial_point.y}") 102 | self.get_logger().info(f"Goal pose => x: {self.goal_point.x}, y: {self.goal_point.y}") 103 | 104 | def master_callback(self): 105 | 106 | # direction vector to generate neighbors 107 | dx = [0, 1, 0, -1] 108 | dy = [1, 0, -1, 0] 109 | 110 | if self.received_data: 111 | # do traversal 112 | 113 | if len(self.queue) > 0: 114 | # get recent node and mark it as visited 115 | node = self.queue.popleft() 116 | # self.map[node.y][node.x] = int(self.color) 117 | 118 | for i in range(0, 4): 119 | # generate new neighbors 120 | neighbor = Point() 121 | neighbor.x = node.x + dx[i] 122 | neighbor.y = node.y + dy[i] 123 | 124 | # neighbor is traversable free space 125 | if self.map[neighbor.y][neighbor.x] == self.FREE_SPACE: 126 | 127 | # add to queue 128 | self.queue.append(neighbor) 129 | # update the map indicating neighbor visited 130 | self.map[neighbor.y][neighbor.x] = int(self.color) 131 | 132 | # parent of neighbor is node 133 | self.parent_map[neighbor.y][neighbor.x] = node 134 | 135 | # if any of the neighbor is goal pose 136 | elif self.map[neighbor.y][neighbor.x] == self.GOAL: 137 | self.received_data = False 138 | self.goal_reached = True 139 | 140 | # parent of neighbor is node 141 | self.parent_map[neighbor.y][neighbor.x] = node 142 | 143 | 144 | self.color -= self.TRAVERSAL_COLOR_INCREMENT 145 | # uneven transition 146 | if self.color < self.FINAL_COLOR: 147 | self.color = self.TRAVERSAL_COLOR_UPDATE 148 | 149 | elif self.goal_reached: 150 | self.get_logger().info("Goal Reached",once=True) 151 | 152 | try: 153 | # get parent node to generate path 154 | path_element = self.parent_map[self.path_node.y][self.path_node.x] 155 | except: 156 | self.goal_reached = False 157 | self.get_logger().info("Path Generated") 158 | 159 | # program should not exit but else does it has to do? 160 | exit() 161 | 162 | # update map with path 163 | self.map[self.path_node.y][self.path_node.x] = 101 164 | 165 | # node is path-element 166 | self.path_node = path_element 167 | 168 | 169 | self.map_data.map.data = [] 170 | 171 | # convert 2D array to single list 172 | for list in self.map: 173 | self.map_data.map.data.extend(list) 174 | 175 | # publish data 176 | self.traversal_pub.publish(self.map_data.map) 177 | 178 | def main(args=None): 179 | rclpy.init(args=args) 180 | 181 | bfs_node = BFS() 182 | 183 | rclpy.spin(bfs_node) 184 | 185 | bfs_node.destroy_node() 186 | rclpy.shutdown() 187 | 188 | 189 | if __name__ == "__main__": 190 | main() -------------------------------------------------------------------------------- /pathplanners/dijkstra.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | from collections import deque 4 | 5 | import rclpy 6 | from rclpy.node import Node 7 | 8 | from nav_msgs.msg import OccupancyGrid 9 | from custom_interfaces.msg import AdjacencyList, Neighbors, Point 10 | from std_msgs.msg import Empty 11 | 12 | from nav_msgs.srv import GetMap 13 | 14 | 15 | class BFS(Node): 16 | 17 | def __init__(self): 18 | 19 | # constants 20 | self.FREE_SPACE = -1 21 | self.START = -2 22 | self.GOAL = 1 23 | self.TRAVERSAL_COLOR_INCREMENT = 0.104 24 | self.FINAL_COLOR = -127 25 | self.TRAVERSAL_COLOR_UPDATE = 98 26 | 27 | # Flag instead of sync service call 28 | self.received_data = False 29 | 30 | # transmitted on topic 31 | self.map_data = GetMap.Response() 32 | 33 | # start and end points 34 | self.initial_point = Point() 35 | self.goal_point = Point() 36 | 37 | # map 38 | self.map = [] 39 | self.parent_map = [] 40 | 41 | self.queue = [] 42 | 43 | self.color = self.START 44 | 45 | self.goal_reached = False 46 | 47 | # Node setup 48 | super().__init__('bfs_traversal') 49 | self.traversal_pub = self.create_publisher(OccupancyGrid, 'algo_viz', 10) 50 | self.get_map_service = self.create_client(GetMap, 'get_map') 51 | 52 | # timer to call master_callback repetitively 53 | self.timer_period = 0.005 # seconds 54 | self.timer = self.create_timer(self.timer_period, self.master_callback) 55 | 56 | # Wait until service is available 57 | while not self.get_map_service.wait_for_service(timeout_sec=1.0): 58 | self.get_logger().warn('Service not available, waiting again...') 59 | 60 | # Request for initial goal pose 61 | request = GetMap.Request() 62 | self.get_map_future = self.get_map_service.call_async(request) 63 | self.get_map_future.add_done_callback(self.get_map_callback) 64 | 65 | def get_map_callback(self, future): 66 | self.received_data = True 67 | 68 | self.map_data = future.result() 69 | self.map_data.map.header.frame_id = 'algo_map' 70 | 71 | # extract height and width of map 72 | height = self.map_data.map.info.height 73 | width = self.map_data.map.info.width 74 | 75 | map_list = list(self.map_data.map.data) 76 | 77 | # generate the 2D array 78 | 79 | for j in range(0, width): 80 | row = [] 81 | for i in range(0, height): 82 | row.append(map_list[width*j + i]) 83 | if row[-1] == self.GOAL: 84 | self.goal_point.x = i 85 | self.goal_point.y = j 86 | if row[-1] == self.START: 87 | self.initial_point.x = i 88 | self.initial_point.y = j 89 | self.map.append(row) 90 | 91 | # initial_point to queue 92 | self.queue = deque([self.initial_point]) 93 | 94 | # set parent map 95 | self.parent_map = [[-1] * height for _ in range(width)] 96 | 97 | # required to generate the map 98 | self.path_node = self.goal_point 99 | 100 | self.get_logger().info("Received map data") 101 | self.get_logger().info(f"Initial pose => x: {self.initial_point.x}, y: {self.initial_point.y}") 102 | self.get_logger().info(f"Goal pose => x: {self.goal_point.x}, y: {self.goal_point.y}") 103 | 104 | def master_callback(self): 105 | 106 | # direction vector to generate neighbors 107 | dx = [0, 1, 0, -1] 108 | dy = [1, 0, -1, 0] 109 | 110 | if self.received_data: 111 | # do traversal 112 | 113 | if len(self.queue) > 0: 114 | # get recent node and mark it as visited 115 | node = self.queue.popleft() 116 | # self.map[node.y][node.x] = int(self.color) 117 | 118 | for i in range(0, 4): 119 | # generate new neighbors 120 | neighbor = Point() 121 | neighbor.x = node.x + dx[i] 122 | neighbor.y = node.y + dy[i] 123 | 124 | # neighbor is traversable free space 125 | if self.map[neighbor.y][neighbor.x] == self.FREE_SPACE: 126 | 127 | # add to queue 128 | self.queue.append(neighbor) 129 | # update the map indicating neighbor visited 130 | self.map[neighbor.y][neighbor.x] = int(self.color) 131 | 132 | # parent of neighbor is node 133 | self.parent_map[neighbor.y][neighbor.x] = node 134 | 135 | # if any of the neighbor is goal pose 136 | elif self.map[neighbor.y][neighbor.x] == self.GOAL: 137 | self.received_data = False 138 | self.goal_reached = True 139 | 140 | # parent of neighbor is node 141 | self.parent_map[neighbor.y][neighbor.x] = node 142 | 143 | 144 | self.color -= self.TRAVERSAL_COLOR_INCREMENT 145 | # uneven transition 146 | if self.color < self.FINAL_COLOR: 147 | self.color = self.TRAVERSAL_COLOR_UPDATE 148 | 149 | elif self.goal_reached: 150 | self.get_logger().info("Goal Reached",once=True) 151 | 152 | try: 153 | # get parent node to generate path 154 | path_element = self.parent_map[self.path_node.y][self.path_node.x] 155 | except: 156 | self.goal_reached = False 157 | self.get_logger().info("Path Generated") 158 | 159 | # program should not exit but else does it has to do? 160 | exit() 161 | 162 | # update map with path 163 | self.map[self.path_node.y][self.path_node.x] = 101 164 | 165 | # node is path-element 166 | self.path_node = path_element 167 | 168 | 169 | self.map_data.map.data = [] 170 | 171 | # convert 2D array to single list 172 | for list in self.map: 173 | self.map_data.map.data.extend(list) 174 | 175 | # publish data 176 | self.traversal_pub.publish(self.map_data.map) 177 | 178 | def main(args=None): 179 | rclpy.init(args=args) 180 | 181 | bfs_node = BFS() 182 | 183 | rclpy.spin(bfs_node) 184 | 185 | bfs_node.destroy_node() 186 | rclpy.shutdown() 187 | 188 | 189 | if __name__ == "__main__": 190 | main() -------------------------------------------------------------------------------- /pathplanners/map.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import rclpy 4 | from rclpy.node import Node 5 | from nav_msgs.msg import OccupancyGrid 6 | from nav_msgs.srv import GetMap 7 | 8 | class MetaData(Node): 9 | 10 | def __init__(self): 11 | # Node setup 12 | super().__init__('map_pub') 13 | self.map_pub = self.create_publisher(OccupancyGrid, 'map_viz', 10) 14 | self.srv = self.create_service(GetMap, 'get_map', self.get_map_callback) 15 | 16 | # timer to call master_callback repetitively 17 | self.timer_period = 0.02 # seconds 18 | self.timer = self.create_timer(self.timer_period, self.master_callback) 19 | 20 | # initial start pose 21 | self.initial_point_x = 8 22 | self.initial_point_y = 41 23 | 24 | # goal pose 25 | self.goal_point_x = 41 26 | self.goal_point_y = 8 27 | 28 | # 2D array of the map 29 | rows = 50 30 | cols = 50 31 | 32 | data_array = [[0 for _ in range(rows)] for _ in range(cols)] 33 | 34 | for i in range(1, rows - 1): 35 | data_array[i][1:-1] = [-1] * (cols - 2) 36 | 37 | 38 | data_array[int(rows/3)][-34:-1] = [0] * 33 39 | data_array[int(2 * rows/3)][1:34] = [0] * 33 40 | 41 | """ 42 | data_array = [ 43 | [0,0],[1,0] . . . 44 | [1,0] . 45 | . . 46 | . . 47 | . . . . . [49,49] 48 | ] 49 | """ 50 | 51 | # playground map 52 | self.map_data = OccupancyGrid() 53 | self.map_data.header.frame_id = 'map' 54 | self.map_data.info.resolution = 1.0 55 | self.map_data.info.width = rows 56 | self.map_data.info.height = cols 57 | self.map_data.info.origin.position.x = 0.0 58 | self.map_data.info.origin.position.y = 0.0 59 | self.map_data.info.origin.position.z = 0.0 60 | self.map_data.info.origin.orientation.x = 0.00 61 | self.map_data.info.origin.orientation.y = 0.00 62 | self.map_data.info.origin.orientation.z = 0.00 63 | self.map_data.info.origin.orientation.w = 1.00 64 | 65 | # Assign a value to a specific cell based on init anf goal pose 66 | data_array[self.initial_point_y][self.initial_point_x] = -2 67 | data_array[self.goal_point_y][self.goal_point_x] = 1 68 | 69 | # convert 2D array to single list 70 | for list in data_array: 71 | self.map_data.data.extend(list) 72 | 73 | 74 | def get_map_callback(self, request, response): 75 | # generated ref map 76 | self.get_logger().info(f"{request}") 77 | response.map = self.map_data 78 | self.get_logger().info("Now this looks like a job for me!") 79 | 80 | return response 81 | 82 | def master_callback(self): 83 | # publish data 84 | self.map_pub.publish(self.map_data) 85 | 86 | def main(args=None): 87 | rclpy.init(args=args) 88 | 89 | metadata_node = MetaData() 90 | rclpy.spin(metadata_node) 91 | 92 | metadata_node.destroy_node() 93 | rclpy.shutdown() 94 | 95 | 96 | if __name__ == "__main__": 97 | main() -------------------------------------------------------------------------------- /resource/pathplanners: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maker-ATOM/Path-Planning-Algorithms/d4353017ead9b72df8c05ceb5a2bb6b877069e6f/resource/pathplanners -------------------------------------------------------------------------------- /rviz/visualizer.rviz: -------------------------------------------------------------------------------- 1 | Panels: 2 | - Class: rviz_common/Displays 3 | Help Height: 78 4 | Name: Displays 5 | Property Tree Widget: 6 | Expanded: 7 | - /Grid1 8 | - /Map1 9 | - /Map2 10 | Splitter Ratio: 0.5 11 | Tree Height: 659 12 | - Class: rviz_common/Selection 13 | Name: Selection 14 | - Class: rviz_common/Tool Properties 15 | Expanded: 16 | - /2D Goal Pose1 17 | - /Publish Point1 18 | Name: Tool Properties 19 | Splitter Ratio: 0.5886790156364441 20 | - Class: rviz_common/Views 21 | Expanded: 22 | - /Current View1 23 | Name: Views 24 | Splitter Ratio: 0.5 25 | - Class: rviz_common/Time 26 | Experimental: false 27 | Name: Time 28 | SyncMode: 0 29 | SyncSource: "" 30 | Visualization Manager: 31 | Class: "" 32 | Displays: 33 | - Alpha: 0.20000000298023224 34 | Cell Size: 1 35 | Class: rviz_default_plugins/Grid 36 | Color: 160; 160; 164 37 | Enabled: true 38 | Line Style: 39 | Line Width: 0.029999999329447746 40 | Value: Lines 41 | Name: Grid 42 | Normal Cell Count: 0 43 | Offset: 44 | X: 25 45 | Y: 25 46 | Z: 0 47 | Plane: XY 48 | Plane Cell Count: 50 49 | Reference Frame: 50 | Value: true 51 | - Alpha: 1 52 | Class: rviz_default_plugins/Map 53 | Color Scheme: costmap 54 | Draw Behind: false 55 | Enabled: true 56 | Name: Map 57 | Topic: 58 | Depth: 5 59 | Durability Policy: Volatile 60 | Filter size: 10 61 | History Policy: Keep Last 62 | Reliability Policy: Reliable 63 | Value: /algo_viz 64 | Update Topic: 65 | Depth: 5 66 | Durability Policy: Volatile 67 | History Policy: Keep Last 68 | Reliability Policy: Reliable 69 | Value: /algo_viz_updates 70 | Use Timestamp: false 71 | Value: true 72 | - Alpha: 0.20000000298023224 73 | Class: rviz_default_plugins/Map 74 | Color Scheme: costmap 75 | Draw Behind: false 76 | Enabled: true 77 | Name: Map 78 | Topic: 79 | Depth: 5 80 | Durability Policy: Volatile 81 | Filter size: 10 82 | History Policy: Keep Last 83 | Reliability Policy: Reliable 84 | Value: /map_viz 85 | Update Topic: 86 | Depth: 5 87 | Durability Policy: Volatile 88 | History Policy: Keep Last 89 | Reliability Policy: Reliable 90 | Value: /map_viz_updates 91 | Use Timestamp: false 92 | Value: true 93 | Enabled: true 94 | Global Options: 95 | Background Color: 48; 48; 48 96 | Fixed Frame: map 97 | Frame Rate: 30 98 | Name: root 99 | Tools: 100 | - Class: rviz_default_plugins/Interact 101 | Hide Inactive Objects: true 102 | - Class: rviz_default_plugins/MoveCamera 103 | - Class: rviz_default_plugins/Select 104 | - Class: rviz_default_plugins/FocusCamera 105 | - Class: rviz_default_plugins/Measure 106 | Line color: 128; 128; 0 107 | - Class: rviz_default_plugins/SetInitialPose 108 | Covariance x: 0.25 109 | Covariance y: 0.25 110 | Covariance yaw: 0.06853891909122467 111 | Topic: 112 | Depth: 5 113 | Durability Policy: Volatile 114 | History Policy: Keep Last 115 | Reliability Policy: Reliable 116 | Value: /initialpose 117 | - Class: rviz_default_plugins/SetGoal 118 | Topic: 119 | Depth: 5 120 | Durability Policy: Volatile 121 | History Policy: Keep Last 122 | Reliability Policy: Reliable 123 | Value: /goal_pose 124 | - Class: rviz_default_plugins/PublishPoint 125 | Single click: true 126 | Topic: 127 | Depth: 5 128 | Durability Policy: Volatile 129 | History Policy: Keep Last 130 | Reliability Policy: Reliable 131 | Value: /clicked_point 132 | Transformation: 133 | Current: 134 | Class: rviz_default_plugins/TF 135 | Value: true 136 | Views: 137 | Current: 138 | Class: rviz_default_plugins/Orbit 139 | Distance: 62 140 | Enable Stereo Rendering: 141 | Stereo Eye Separation: 0.05999999865889549 142 | Stereo Focal Distance: 1 143 | Swap Stereo Eyes: false 144 | Value: false 145 | Focal Point: 146 | X: 25.316682815551758 147 | Y: 24.91035270690918 148 | Z: -0.024869605898857117 149 | Focal Shape Fixed Size: false 150 | Focal Shape Size: 0.05000000074505806 151 | Invert Z Axis: false 152 | Name: Current View 153 | Near Clip Distance: 0.009999999776482582 154 | Pitch: -1.5697963237762451 155 | Target Frame: 156 | Value: Orbit (rviz_default_plugins) 157 | Yaw: 4.714000225067139 158 | Saved: ~ 159 | Window Geometry: 160 | Displays: 161 | collapsed: true 162 | Height: 888 163 | Hide Left Dock: true 164 | Hide Right Dock: true 165 | QMainWindow State: 000000ff00000000fd0000000400000000000001560000031efc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000003d0000031e000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f0000031efc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d0000031e000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000003a10000003efc0100000002fb0000000800540069006d00650000000000000003a1000002fb00fffffffb0000000800540069006d00650100000000000004500000000000000000000003a10000031e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000 166 | Selection: 167 | collapsed: false 168 | Time: 169 | collapsed: false 170 | Tool Properties: 171 | collapsed: false 172 | Views: 173 | collapsed: true 174 | Width: 929 175 | X: 82 176 | Y: 27 177 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [develop] 2 | script_dir=$base/lib/pathplanners 3 | [install] 4 | install_scripts=$base/lib/pathplanners 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | import os 3 | from glob import glob 4 | 5 | package_name = 'pathplanners' 6 | 7 | setup( 8 | name=package_name, 9 | version='0.0.0', 10 | packages=find_packages(exclude=['test']), 11 | data_files=[ 12 | ('share/ament_index/resource_index/packages', 13 | ['resource/' + package_name]), 14 | ('share/' + package_name, ['package.xml']), 15 | (os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*'))), 16 | (os.path.join('share', package_name, 'rviz'), ['rviz/visualizer.rviz']), 17 | 18 | ], 19 | install_requires=['setuptools'], 20 | zip_safe=True, 21 | maintainer='aditya', 22 | maintainer_email='makersatom@gmail.com', 23 | description='TODO: Package description', 24 | license='TODO: License declaration', 25 | tests_require=['pytest'], 26 | entry_points={ 27 | 'console_scripts': [ 28 | "map_node = pathplanners.map:main", 29 | "bfs = pathplanners.breadthfirstsearch:main" 30 | "dijkstra = pathplanners.dijkstra:main" 31 | ], 32 | }, 33 | ) 34 | -------------------------------------------------------------------------------- /test/test_copyright.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_copyright.main import main 16 | import pytest 17 | 18 | 19 | # Remove the `skip` decorator once the source file(s) have a copyright header 20 | @pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') 21 | @pytest.mark.copyright 22 | @pytest.mark.linter 23 | def test_copyright(): 24 | rc = main(argv=['.', 'test']) 25 | assert rc == 0, 'Found errors' 26 | -------------------------------------------------------------------------------- /test/test_flake8.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_flake8.main import main_with_errors 16 | import pytest 17 | 18 | 19 | @pytest.mark.flake8 20 | @pytest.mark.linter 21 | def test_flake8(): 22 | rc, errors = main_with_errors(argv=[]) 23 | assert rc == 0, \ 24 | 'Found %d code style errors / warnings:\n' % len(errors) + \ 25 | '\n'.join(errors) 26 | -------------------------------------------------------------------------------- /test/test_pep257.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_pep257.main import main 16 | import pytest 17 | 18 | 19 | @pytest.mark.linter 20 | @pytest.mark.pep257 21 | def test_pep257(): 22 | rc = main(argv=['.', 'test']) 23 | assert rc == 0, 'Found code style errors / warnings' 24 | --------------------------------------------------------------------------------