Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
process_data.py
1 #! /usr/bin/env python3
2 # Copyright 2022 Joshua Wallace
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import math
17 import os
18 import pickle
19 
20 import matplotlib.pylab as plt
21 from nav2_msgs.action import ComputePathToPose
22 from nav2_msgs.msg import Costmap
23 from nav_msgs.msg import Path
24 import numpy as np
25 import seaborn as sns
26 from tabulate import tabulate
27 
28 
29 def getPaths(results: list[ComputePathToPose.Result]) -> list[Path]:
30  paths = []
31  for result in results:
32  for path in result:
33  paths.append(path.path)
34  return paths
35 
36 
37 def getTimes(results: list[ComputePathToPose.Result]) -> list[float]:
38  times = []
39  for result in results:
40  for time in result:
41  times.append(time.planning_time.nanosec / 1e09 + time.planning_time.sec)
42  return times
43 
44 
45 def getMapCoordsFromPaths(paths: list[Path], resolution: float) -> list[list[float]]:
46  coords = []
47  for path in paths:
48  x = []
49  y = []
50  for pose in path.poses:
51  x.append(pose.pose.position.x / resolution)
52  y.append(pose.pose.position.y / resolution)
53  coords.append(x)
54  coords.append(y)
55  return coords
56 
57 
58 def getPathLength(path: Path) -> float:
59  path_length = 0.0
60  x_prev = path.poses[0].pose.position.x
61  y_prev = path.poses[0].pose.position.y
62  for i in range(1, len(path.poses)):
63  x_curr = path.poses[i].pose.position.x
64  y_curr = path.poses[i].pose.position.y
65  path_length = path_length + math.sqrt(
66  (x_curr - x_prev) ** 2 + (y_curr - y_prev) ** 2
67  )
68  x_prev = x_curr
69  y_prev = y_curr
70  return path_length
71 
72 
73 def plotResults(costmap: Costmap, paths: list[Path]) -> None:
74  coords = getMapCoordsFromPaths(paths, costmap.metadata.resolution)
75  data = np.asarray(costmap.data)
76  data.resize(costmap.metadata.size_y, costmap.metadata.size_x)
77  data = np.where(data <= 253, 0, data)
78 
79  plt.figure(3)
80  ax = sns.heatmap(data, cmap='Greys', cbar=False)
81  for i in range(0, len(coords), 2):
82  ax.plot(coords[i], coords[i + 1], linewidth=0.7)
83  plt.axis('off')
84  ax.set_aspect('equal', 'box')
85  plt.show()
86 
87 
88 def averagePathCost(
89  paths: list[Path], costmap: Costmap,
90  num_of_planners: int) -> list[list[float]]:
91  coords = getMapCoordsFromPaths(paths, costmap.metadata.resolution)
92  data = np.asarray(costmap.data)
93  data.resize(costmap.metadata.size_y, costmap.metadata.size_x)
94 
95  average_path_costs: list[list[float]] = []
96  for i in range(num_of_planners):
97  average_path_costs.append([])
98 
99  k = 0
100  for i in range(0, len(coords), 2):
101  costs = []
102  for j in range(len(coords[i])):
103  costs.append(data[math.floor(coords[i + 1][j])][math.floor(coords[i][j])])
104  average_path_costs[k % num_of_planners].append(sum(costs) / len(costs))
105  k += 1
106 
107  return average_path_costs
108 
109 
110 def maxPathCost(paths: list[Path], costmap: Costmap, num_of_planners: int) -> list[list[float]]:
111  coords = getMapCoordsFromPaths(paths, costmap.metadata.resolution)
112  data = np.asarray(costmap.data)
113  data.resize(costmap.metadata.size_y, costmap.metadata.size_x)
114 
115  max_path_costs: list[list[float]] = []
116  for i in range(num_of_planners):
117  max_path_costs.append([])
118 
119  k = 0
120  for i in range(0, len(coords), 2):
121  max_cost = 0
122  for j in range(len(coords[i])):
123  cost = data[math.floor(coords[i + 1][j])][math.floor(coords[i][j])]
124  if max_cost < cost:
125  max_cost = cost
126  max_path_costs[k % num_of_planners].append(max_cost)
127  k += 1
128 
129  return max_path_costs
130 
131 
132 def main() -> None:
133 
134  print('Read data')
135  with open(os.getcwd() + '/results.pickle', 'rb') as f:
136  results = pickle.load(f)
137 
138  with open(os.getcwd() + '/planners.pickle', 'rb') as f:
139  planners = pickle.load(f)
140 
141  with open(os.getcwd() + '/costmap.pickle', 'rb') as f:
142  costmap = pickle.load(f)
143 
144  paths = getPaths(results)
145  path_lengths_list = []
146 
147  for path in paths:
148  path_lengths_list.append(getPathLength(path))
149  path_lengths = np.asarray(path_lengths_list)
150  total_paths = len(paths)
151 
152  path_lengths.resize((int(total_paths / len(planners)), len(planners)))
153  path_lengths = path_lengths.transpose()
154 
155  times = np.asarray(getTimes(results))
156  times.resize((int(total_paths / len(planners)), len(planners)))
157  times = np.transpose(times)
158 
159  # Costs
160  average_path_costs = np.asarray(averagePathCost(paths, costmap, len(planners)))
161  max_path_costs = np.asarray(maxPathCost(paths, costmap, len(planners)))
162 
163  # Generate table
164  planner_table = [
165  [
166  'Planner',
167  'Average path length (m)',
168  'Average Time (s)',
169  'Average cost',
170  'Max cost',
171  ]
172  ]
173 
174  for i in range(0, len(planners)):
175  planner_table.append(
176  [
177  planners[i],
178  np.average(path_lengths[i]),
179  np.average(times[i]),
180  np.average(average_path_costs[i]),
181  np.average(max_path_costs[i]),
182  ]
183  )
184 
185  # Visualize results
186  print(tabulate(planner_table))
187  plotResults(costmap, paths)
188 
189 
190 if __name__ == '__main__':
191  main()