16 from pathlib
import Path
19 from evo
import main_ape
20 from evo.tools
import file_interface
21 import matplotlib.pyplot
as plt
22 from rosbag2_py._info
import Info
as Bag2Info
24 from itertools
import cycle
26 from .
import timem_results
27 from .exceptions
import ScriptError
29 DIR_NAME_PATTERN =
'benchmark_([0-9]+)_particles_output'
30 SUPPORTED_TOPIC_NAMES = (
'/pose',
'/amcl_pose')
31 EVO_RESULTS_FILE_NAME =
'evo_results.zip'
32 TERMINAL_OUTPUT_LOG_FILE_NAME =
'output.log'
36 metadata = Bag2Info().read_metadata(str(bag_path),
'')
37 for topic_name
in SUPPORTED_TOPIC_NAMES:
39 topic_info.topic_metadata.name == topic_name
40 for topic_info
in metadata.topics_with_message_count
44 f
'Estimate pose topic was not found in bag file, expected names: {SUPPORTED_TOPIC_NAMES}'
49 evo_results_path = dir_path / EVO_RESULTS_FILE_NAME
50 if evo_results_path.exists():
52 bag_path = dir_path /
'rosbag'
54 arg_parser = main_ape.parser()
55 args = arg_parser.parse_args(
59 '/odometry/ground_truth',
62 str(evo_results_path.resolve()),
70 return file_interface.load_res_file(zip_file_path).stats
78 r'\[.*\] \[INFO\] \[([0-9]*\.[0-9]*)\] \[amcl\]: Particle filter update iteration stats: '
79 +
r'[0-9]* particles [0-9]* points - ([0-9]*\.[0-9]*)ms'
82 with open(output_file_path,
'r')
as f:
84 if not pattern.match(line):
87 latency = pattern.match(line).group(2)
88 latencies_seq.append(float(latency) * 1e-3)
91 'latency_min': min(latencies_seq),
92 'latency_max': max(latencies_seq),
93 'latency_mean': sum(latencies_seq) / len(latencies_seq),
94 'latency_median': sorted(latencies_seq)[len(latencies_seq) // 2],
101 'latency_median': 0.0,
120 for dir
in results_path.iterdir():
123 m = re.match(DIR_NAME_PATTERN, dir.name)
126 particles.append(int(m[1]))
127 timem_results_output = timem_results.read_timem_output(dir)
129 timem_results.get_timem_metrics_values(timem_results_output)
130 if timem_results_output
134 cpu_usage.append(cpu)
136 evo_results_path = dir / EVO_RESULTS_FILE_NAME
138 ape_rmse.append(stats[
"rmse"])
139 ape_max.append(stats[
"max"])
140 ape_mean.append(stats[
"mean"])
141 ape_std.append(stats[
"std"])
142 ape_median.append(stats[
"median"])
143 terminal_output_path = dir / TERMINAL_OUTPUT_LOG_FILE_NAME
145 latency_min.append(latency_stats[
'latency_min'])
146 latency_max.append(latency_stats[
'latency_max'])
147 latency_mean.append(latency_stats[
'latency_mean'])
148 latency_median.append(latency_stats[
'latency_median'])
153 'particles_n': particles,
154 'peak_rss': peak_rss,
155 'cpu_usage': cpu_usage,
156 'ape_rmse': ape_rmse,
157 'ape_mean': ape_mean,
159 'ape_median': ape_median,
161 'latency_min': latency_min,
162 'latency_max': latency_max,
163 'latency_mean': latency_mean,
164 'latency_median': latency_median,
167 .set_index(
'particles_n')
173 arg_parser = argparse.ArgumentParser(
174 description=
'Script to compare parameterized run results of beluga with another'
175 ' implementation (e.g. nav2_amcl, another beluga version, etc).'
177 arg_parser.add_argument(
182 help=
'Folder with parameterized benchmark results',
185 arg_parser.add_argument(
190 help=
'Label for a series',
194 arg_parser.add_argument(
197 help=
'Use log scale on y axis',
200 default_plots = [
'cpu_usage',
'peak_rss',
'ape_rmse',
'latency_median']
201 arg_parser.add_argument(
205 default=default_plots,
206 help=
'List of plots to generate. Default: ' +
', '.join(default_plots),
209 arg_parser.add_argument(
213 help=
'Instead of plotting, save all the results to a csv file',
216 args = arg_parser.parse_args()
218 assert len(args.series) == len(args.label),
'Number of series and labels must match'
236 for label, series
in zip(args.label, args.series)
239 full_table = series[0].filter(items=[])
242 full_table = pd.concat([full_table, s], axis=1)
243 print(
'Saving to CSV file: ', args.save_csv)
244 full_table.to_csv(args.save_csv)
249 .filter(items=args.plot_names)
250 .add_prefix(label +
'_')
251 for label, series
in zip(args.label, args.series)
254 print(
'Plotting data...')
256 marker_gen = cycle(
'o^sDvP*')
260 color=next(color_gen),
261 marker=next(marker_gen),
265 for other_series
in series[1:]:
269 color=next(color_gen),
270 marker=next(marker_gen),
274 for ax
in plt.gcf().axes:
278 ax.grid(
True, which=
"both", ls=
"-")
279 ax.legend(fontsize=
'small', loc=
'upper left', bbox_to_anchor=(1.01, 1))
280 current_bounds = ax.get_position().bounds
281 new_bounds = (0.05, *current_bounds[1:])
282 ax.set_position(new_bounds)
283 current_ylimits = ax.get_ylim()
285 min(0.0, current_ylimits[0]),
286 max(0.0, current_ylimits[1]),
288 ax.set_ylim(new_limits)