16 from datetime
import datetime
19 from pathlib
import Path
23 from lattice_generator
import LatticeGenerator
25 import matplotlib.pyplot
as plt
29 logging.basicConfig(level=logging.INFO)
30 logger = logging.getLogger(__name__)
33 def handle_arg_parsing():
35 Handle the parsing of arguments.
40 An object containing all parsed arguments
43 parser = argparse.ArgumentParser(
44 description=
"Generate motionprimitives for Nav2's State Lattice Planner"
49 default=
'./config.json',
50 help=
'The config file containing the ' 'parameters to be used',
55 default=
'./output.json',
56 help=
'The output file containing the ' 'trajectory data',
61 default=
'./visualizations',
62 help=
'The output folder where the '
63 'visualizations of the trajectories will be saved',
66 return parser.parse_args()
69 def create_heading_angle_list(minimal_set_trajectories: dict) -> list:
71 Create a sorted list of heading angles from the minimal trajectory set.
75 minimal_set_trajectories: dict
76 The minimal spanning set
81 A sorted list of heading angles
84 heading_angles = set(minimal_set_trajectories.keys())
85 return sorted(heading_angles, key=
lambda x: (x < 0, x))
88 def read_config(config_path) -> dict:
90 Read in the user defined parameters via JSON.
95 Path to the config file
100 Dictionary containing the user defined parameters
103 with open(config_path)
as config_file:
104 config = json.load(config_file)
109 def create_header(config: dict, minimal_set_trajectories: dict) -> dict:
111 Create a dict containing all the fields to populate the header with.
116 The dict containing user specified parameters
117 minimal_set_trajectories: dict
118 The minimal spanning set
123 A dictionary containing the fields to populate the header with
127 'version': constants.VERSION,
128 'date_generated': datetime.today().strftime(
'%Y-%m-%d'),
129 'lattice_metadata': {},
133 for key, value
in config.items():
134 header_dict[
'lattice_metadata'][key] = value
136 heading_angles = create_heading_angle_list(minimal_set_trajectories)
137 adjusted_heading_angles = [
138 angle + 2 * np.pi
if angle < 0
else angle
for angle
in heading_angles
141 header_dict[
'lattice_metadata'][
'heading_angles'] = adjusted_heading_angles
147 output_path: Path, minimal_set_trajectories: dict, config: dict
150 Write the minimal spanning set to an output file.
155 The output file for the json data
156 minimal_set_trajectories: dict
157 The minimal spanning set
159 The dict containing user specified parameters
162 output_dict = create_header(config, minimal_set_trajectories)
164 trajectory_start_angles = list(minimal_set_trajectories.keys())
166 heading_angle_list = create_heading_angle_list(minimal_set_trajectories)
167 heading_lookup = {angle: idx
for idx, angle
in enumerate(heading_angle_list)}
170 for start_angle
in sorted(trajectory_start_angles, key=
lambda x: (x < 0, x)):
172 for trajectory
in sorted(
173 minimal_set_trajectories[start_angle], key=
lambda x: x.parameters.end_angle
177 traj_info[
'trajectory_id'] = idx
178 traj_info[
'start_angle_index'] = heading_lookup[
179 trajectory.parameters.start_angle
181 traj_info[
'end_angle_index'] = heading_lookup[
182 trajectory.parameters.end_angle
184 traj_info[
'left_turn'] = bool(trajectory.parameters.left_turn)
185 traj_info[
'trajectory_radius'] = trajectory.parameters.turning_radius
186 traj_info[
'trajectory_length'] = round(
187 trajectory.parameters.total_length, 5
189 traj_info[
'arc_length'] = round(trajectory.parameters.arc_length, 5)
190 traj_info[
'straight_length'] = round(
191 trajectory.parameters.start_straight_length
192 + trajectory.parameters.end_straight_length,
195 traj_info[
'poses'] = trajectory.path.to_output_format()
197 output_dict[
'primitives'].append(traj_info)
200 output_dict[
'lattice_metadata'][
'number_of_trajectories'] = idx
202 with open(output_path,
'w')
as output_file:
203 json.dump(output_dict, output_file, indent=
'\t')
206 def save_visualizations(
207 visualizations_folder: Path, minimal_set_trajectories: dict
210 Draw the visualizations for every trajectory and save it as an image.
214 visualizations_folder: Path
215 The path to the folder for where to save the images
216 minimal_set_trajectories: dict
217 The minimal spanning set
221 visualizations_folder.mkdir(exist_ok=
True)
223 for start_angle
in minimal_set_trajectories.keys():
225 for trajectory
in minimal_set_trajectories[start_angle]:
226 plt.plot(trajectory.path.xs, trajectory.path.ys,
'b')
230 left_x, right_x = plt.xlim()
231 left_y, right_y = plt.ylim()
233 output_path = visualizations_folder /
'all_trajectories.png'
234 plt.savefig(output_path)
237 for start_angle
in minimal_set_trajectories.keys():
239 angle_in_deg = np.rad2deg(start_angle)
241 if start_angle < 0
or start_angle > np.pi / 2:
244 for trajectory
in minimal_set_trajectories[start_angle]:
245 plt.plot(trajectory.path.xs, trajectory.path.ys,
'b')
246 plt.xlim(left_x, right_x)
247 plt.ylim(left_y, right_y)
251 output_path = visualizations_folder / f
'{angle_in_deg}.png'
252 plt.savefig(output_path)
256 if __name__ ==
'__main__':
258 args = handle_arg_parsing()
259 config = read_config(args.config)
262 lattice_gen = LatticeGenerator(config)
263 minimal_set_trajectories = lattice_gen.run()
264 print(f
'Finished Generating. Took {time.time() - start} seconds')
266 write_to_json(args.output, minimal_set_trajectories, config)
267 save_visualizations(args.visualizations, minimal_set_trajectories)