diff --git a/.rosinstall b/.rosinstall
index 43cc7e9b8..17d75d6c7 100644
--- a/.rosinstall
+++ b/.rosinstall
@@ -18,6 +18,11 @@
uri: https://github.com/ignc-research/stable-baselines3
version: master
+- git:
+ local-name: ../forks/marl/stable-baselines3
+ uri: https://github.com/ignc-research/stable-baselines3
+ version: marl
+
- git:
local-name: ../forks/navigation/local_planner/teb
uri: https://github.com/rst-tu-dortmund/teb_local_planner
diff --git a/HPP/model/model.py b/HPP/model/model.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/HPP/record.py b/HPP/record.py
new file mode 100644
index 000000000..193803975
--- /dev/null
+++ b/HPP/record.py
@@ -0,0 +1,16 @@
+from arena_marl.marl_agent.utils.supersuit_utils import MarkovVectorEnv_patched
+
+
+def store_data():
+ """
+ Store data in a csv file
+ """
+
+
+class Env_Recorder(MarkovVectorEnv_patched):
+ """
+ A wrapper for the environment that records the state and action
+ """
+
+ def __init__(self):
+ super(Env_Recorder, self).step(self, actions)
diff --git a/HPP/train.py b/HPP/train.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/arena_bringup/launch/marl_start_flatland.launch b/arena_bringup/launch/marl_start_flatland.launch
new file mode 100644
index 000000000..7dacf4f69
--- /dev/null
+++ b/arena_bringup/launch/marl_start_flatland.launch
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/arena_bringup/launch/marl_start_training.launch b/arena_bringup/launch/marl_start_training.launch
new file mode 100644
index 000000000..5b27d17e0
--- /dev/null
+++ b/arena_bringup/launch/marl_start_training.launch
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/arena_bringup/launch/start_arena_flatland.launch b/arena_bringup/launch/start_arena_flatland.launch
index 2aac3af96..e026cb07b 100755
--- a/arena_bringup/launch/start_arena_flatland.launch
+++ b/arena_bringup/launch/start_arena_flatland.launch
@@ -38,7 +38,7 @@
-
+
diff --git a/arena_bringup/launch/sublaunch_training/marl_fake_localization.launch b/arena_bringup/launch/sublaunch_training/marl_fake_localization.launch
new file mode 100644
index 000000000..e3d5ed46c
--- /dev/null
+++ b/arena_bringup/launch/sublaunch_training/marl_fake_localization.launch
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/arena_bringup/launch/sublaunch_training/marl_single_env_training.launch b/arena_bringup/launch/sublaunch_training/marl_single_env_training.launch
new file mode 100644
index 000000000..4a0eb1add
--- /dev/null
+++ b/arena_bringup/launch/sublaunch_training/marl_single_env_training.launch
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/arena_bringup/rviz/nav_LP_2.rviz b/arena_bringup/rviz/nav_LP_2.rviz
new file mode 100644
index 000000000..efb888592
--- /dev/null
+++ b/arena_bringup/rviz/nav_LP_2.rviz
@@ -0,0 +1,1039 @@
+Panels:
+ - Class: rviz/Displays
+ Help Height: 84
+ Name: Displays
+ Property Tree Widget:
+ Expanded:
+ - /Map1
+ - /obstacles1
+ - /Plan Manager1/subgoal1
+ - /Tracking-Visuals1/TrackedPersons1
+ - /Tracking-Visuals1/TrackedPersons1/History as line1
+ - /Tracking-Visuals1/TrackedGroups1
+ - /AIO1/Dynamic Scan1
+ - /Agent path taken1
+ Splitter Ratio: 0.4374079406261444
+ Tree Height: 357
+ - Class: rviz/Selection
+ Name: Selection
+ - Class: rviz/Tool Properties
+ Expanded:
+ - /2D Pose Estimate1
+ - /2D Nav Goal1
+ - /Publish Point1
+ Name: Tool Properties
+ Splitter Ratio: 0.4285709857940674
+ - Class: rviz/Views
+ Expanded:
+ - /Current View1
+ Name: Views
+ Splitter Ratio: 0.5
+ - Class: rviz/Time
+ Experimental: false
+ Name: Time
+ SyncMode: 0
+ SyncSource: LaserScan
+Preferences:
+ PromptSaveOnExit: false
+Toolbars:
+ toolButtonStyle: 2
+Visualization Manager:
+ Class: ""
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /visualizer/path
+ Name: Path_Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/TF
+ Enabled: true
+ Frame Timeout: 15
+ Frames:
+ All Enabled: false
+ base_link:
+ Value: true
+ chassis_link:
+ Value: true
+ front_fender_link:
+ Value: true
+ front_laser:
+ Value: true
+ front_laser_mount:
+ Value: true
+ front_left_wheel_link:
+ Value: true
+ front_mount:
+ Value: true
+ front_right_wheel_link:
+ Value: true
+ imu_link:
+ Value: true
+ map:
+ Value: true
+ mid_mount:
+ Value: true
+ navsat_link:
+ Value: true
+ odom:
+ Value: true
+ rear_fender_link:
+ Value: true
+ rear_left_wheel_link:
+ Value: true
+ rear_mount:
+ Value: true
+ rear_right_wheel_link:
+ Value: true
+ Marker Alpha: 1
+ Marker Scale: 1
+ Name: TF
+ Show Arrows: true
+ Show Axes: true
+ Show Names: true
+ Tree:
+ map:
+ odom:
+ base_link:
+ chassis_link:
+ front_fender_link:
+ {}
+ front_left_wheel_link:
+ {}
+ front_right_wheel_link:
+ {}
+ imu_link:
+ {}
+ mid_mount:
+ front_mount:
+ front_laser_mount:
+ front_laser:
+ {}
+ rear_mount:
+ {}
+ navsat_link:
+ {}
+ rear_fender_link:
+ {}
+ rear_left_wheel_link:
+ {}
+ rear_right_wheel_link:
+ {}
+ Update Interval: 0
+ Value: true
+ - Alpha: 1
+ Class: rviz/RobotModel
+ Collision Enabled: false
+ Enabled: true
+ Links:
+ All Links Enabled: true
+ Expand Joint Details: false
+ Expand Link Details: false
+ Expand Tree: false
+ Link Tree Style: Links in Alphabetic Order
+ base_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ chassis_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ front_fender_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ front_laser:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ front_laser_mount:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ front_left_wheel_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ front_mount:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ front_right_wheel_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ imu_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ mid_mount:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ navsat_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ rear_fender_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ rear_left_wheel_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ rear_mount:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ rear_right_wheel_link:
+ Alpha: 1
+ Show Axes: false
+ Show Trail: false
+ Value: true
+ Name: RobotModel
+ Robot Description: robot_description
+ TF Prefix: ""
+ Update Interval: 0
+ Value: true
+ Visual Enabled: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: false
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /move_base_simple/goal
+ Unreliable: false
+ Value: false
+ Enabled: true
+ Name: robot
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /cadrl/goal_path_marker
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /cadrl/other_agents_markers
+ Name: MarkerArray
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: false
+ Name: cadrl
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 0.699999988079071
+ Class: rviz/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: true
+ Name: Map
+ Topic: /map
+ Unreliable: false
+ Use Timestamp: false
+ Value: true
+ - Alpha: 0.699999988079071
+ Class: rviz/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: false
+ Name: Global_costmap
+ Topic: /move_base/global_costmap/costmap
+ Unreliable: false
+ Use Timestamp: false
+ Value: false
+ - Alpha: 0.699999988079071
+ Class: rviz/Map
+ Color Scheme: costmap
+ Draw Behind: false
+ Enabled: false
+ Name: Local_costmap
+ Topic: /move_base/local_costmap/costmap
+ Unreliable: false
+ Use Timestamp: false
+ Value: false
+ Enabled: true
+ Name: Map
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 255; 0; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: teb
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 0; 0
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/TebLocalPlannerROS/local_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 85; 0; 127
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: mpc
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/MpcLocalPlannerROS/local_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 0; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.029999999329447746
+ Name: dwa
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/DWAPlannerROS/local_plan
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: Local plan
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 170; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: teb
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 0; 0
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/TebLocalPlannerROS/global_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 170; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: mpc
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/MpcLocalPlannerROS/global_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 170; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.029999999329447746
+ Name: dwa
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/DWAPlannerROS/global_plan
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: Global Plan
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 0.30399999022483826
+ Min Value: 0.30399999022483826
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/LaserScan
+ Color: 255; 0; 0
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: LaserScan
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 8
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /scan
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ Enabled: true
+ Name: Sensors
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /action
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Group
+ Displays: ~
+ Enabled: true
+ Name: obstacles
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /cadrl/goal_path_marker
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /other_agents_markers
+ Name: MarkerArray
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: true
+ Name: crowdnav
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 25; 255; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Lines
+ Line Width: 0.029999999329447746
+ Name: global_path
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /vis_global_path
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 85; 170; 255
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.30000001192092896
+ Name: local_traj
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /vis_path_timed_astar
+ Unreliable: false
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /vis_goal
+ Name: goal
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /planning_vis/subgoal
+ Name: subgoal
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /vis_landmarks_
+ Name: landmarks
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /vis_triangle
+ Name: triangulation_map
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: occmap_local_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/occupancy
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: occmap_dynamic_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/occupancy_dynamic
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: occmap_static_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/occupancy_static
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: esdfmap_local_fused
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/esdf
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: esdfmap_static_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/esdf_static
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ Enabled: true
+ Name: Plan Manager
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 255; 25; 0
+ Enabled: false
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Shape: Axes
+ Topic: /goal
+ Unreliable: false
+ Value: false
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /time_space/vis_visted_samples
+ Name: samples
+ Namespaces:
+ {}
+ Queue Size: 1
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /planning_vis/goal
+ Name: Marker
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 25; 255; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Lines
+ Line Width: 0.029999999329447746
+ Name: Path
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /globalPlan
+ Unreliable: false
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /planning_vis/subgoal
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 255; 25; 0
+ Enabled: true
+ Head Length: 0.30000001192092896
+ Head Radius: 0.10000000149011612
+ Name: Pose
+ Queue Size: 10
+ Shaft Length: 1
+ Shaft Radius: 0.05000000074505806
+ Shape: Arrow
+ Topic: /subgoal
+ Unreliable: false
+ Value: true
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Class: spencer_tracking_rviz_plugin/TrackedPersons
+ Color: 130; 130; 130
+ Color map offset: 0
+ Color transform: SRL Tracking Colors
+ Delete after no. cycles: 5
+ Enabled: true
+ Excluded person IDs: ""
+ Font color: 255; 255; 255
+ Font color style: Same color
+ Font scale: 2
+ History as line:
+ Line width: 0.05000000074505806
+ Value: true
+ History size: 10
+ Included person IDs: ""
+ Min. history point distance: 0.4000000059604645
+ Missed alpha: 1
+ Name: TrackedPersons
+ Occlusion alpha: 1
+ Queue Size: 10
+ Render covariances:
+ Line width: 0.10000000149011612
+ Value: false
+ Render detection IDs: false
+ Render history: false
+ Render person visual: true
+ Render track IDs: true
+ Render track state: false
+ Render velocities: true
+ Show DELETED tracks: false
+ Show MATCHED tracks: true
+ Show MISSED tracks: true
+ Show OCCLUDED tracks: true
+ Style:
+ Line width: 0.05000000074505806
+ Scaling factor: 1
+ Value: Bounding boxes
+ Topic: /pedsim_visualizer/tracked_persons
+ Unreliable: false
+ Value: true
+ Z offset:
+ Use Z position from message: false
+ Value: 0
+ - Alpha: 0.699999988079071
+ Class: spencer_tracking_rviz_plugin/TrackedGroups
+ Color: 130; 130; 130
+ Color map offset: 0
+ Color transform: Flat
+ Connect group members: true
+ Enabled: true
+ Excluded group IDs: ""
+ Excluded person IDs: ""
+ Font color: 255; 255; 255
+ Font color style: Same color
+ Font scale: 2
+ Global history size: 5
+ Group ID Z offset: 2
+ Included group IDs: ""
+ Included person IDs: ""
+ Name: TrackedGroups
+ Occlusion alpha: 1
+ Queue Size: 10
+ Render group IDs:
+ Hide IDs of single-person groups: false
+ Value: true
+ Render history: false
+ Single-person groups in constant color: false
+ Style:
+ Line width: 0.05000000074505806
+ Scaling factor: 1
+ Value: Simple
+ Topic: /pedsim_visualizer/tracked_groups
+ Tracked persons topic: /pedsim_visualizer/tracked_persons
+ Unreliable: false
+ Value: true
+ Z offset:
+ Use Z position from message: false
+ Value: 0
+ Enabled: true
+ Name: Tracking-Visuals
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/LaserScan
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: LaserScan
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.03999999910593033
+ Style: Flat Squares
+ Topic: /scan_new
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /all_in_one_planner/all_in_one_action_trajectory_vis
+ Name: Planner Selection over Path
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /all_in_one_planner/all_in_one_action_vis
+ Name: Planner Selection
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/LaserScan
+ Color: 252; 233; 79
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: Dynamic Scan
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.3499999940395355
+ Style: Flat Squares
+ Topic: /all_in_one_planner/scan_dynamic
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /all_in_one_planner/vis_subgoal
+ Name: Subgoal
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: true
+ Name: AIO
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /viz_agent_pose
+ Name: Agent path taken
+ Namespaces:
+ agent: true
+ Queue Size: 100
+ Value: true
+ Enabled: true
+ Global Options:
+ Background Color: 0; 0; 0
+ Default Light: true
+ Fixed Frame: map
+ Frame Rate: 30
+ Name: root
+ Tools:
+ - Class: rviz/MoveCamera
+ - Class: rviz/Interact
+ Hide Inactive Objects: true
+ - Class: rviz/Select
+ - Class: rviz/SetInitialPose
+ Theta std deviation: 0.2617993950843811
+ Topic: /initialpose
+ X std deviation: 0.5
+ Y std deviation: 0.5
+ - Class: rviz/SetGoal
+ Topic: /move_base_simple/goal
+ - Class: rviz_plugins/Goal3DTool
+ Topic: manual_goal
+ - Class: rviz/PublishPoint
+ Single click: true
+ Topic: /clicked_point
+ Value: true
+ Views:
+ Current:
+ Angle: -7.859952449798584
+ Class: rviz/TopDownOrtho
+ Enable Stereo Rendering:
+ Stereo Eye Separation: 0.05999999865889549
+ Stereo Focal Distance: 1
+ Swap Stereo Eyes: false
+ Value: false
+ Invert Z Axis: false
+ Name: Current View
+ Near Clip Distance: 0.009999999776482582
+ Scale: 40.95969772338867
+ Target Frame:
+ X: 0.17317672073841095
+ Y: -2.2967824935913086
+ Saved: ~
+Window Geometry:
+ Displays:
+ collapsed: true
+ Height: 1367
+ Hide Left Dock: true
+ Hide Right Dock: false
+ QMainWindow State: 000000ff00000000fd00000004000000000000016a00000266fc0200000005fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000003be0000025d00000198000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000003d00000266000000c900ffffff000000010000010f00000270fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000002800000270000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004a00000003efc0100000002fb0000000800540069006d00650000000000000004a0000004f300fffffffb0000000800540069006d00650100000000000004500000000000000000000009b8000004fd00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+ Selection:
+ collapsed: false
+ Time:
+ collapsed: false
+ Tool Properties:
+ collapsed: false
+ Views:
+ collapsed: false
+ Width: 2488
+ X: 72
+ Y: 27
\ No newline at end of file
diff --git a/arena_bringup/rviz/nav_MARL.rviz b/arena_bringup/rviz/nav_MARL.rviz
new file mode 100644
index 000000000..82f8bb367
--- /dev/null
+++ b/arena_bringup/rviz/nav_MARL.rviz
@@ -0,0 +1,1602 @@
+Panels:
+ - Class: rviz/Displays
+ Help Height: 78
+ Name: Displays
+ Property Tree Widget:
+ Expanded:
+ - /Sensors1
+ - /obstacles1
+ - /obstacles1/dynamic1/01
+ - /obstacles1/dynamic1/11
+ - /obstacles1/dynamic1/21
+ - /obstacles1/dynamic1/31
+ - /obstacles1/dynamic1/41
+ - /obstacles1/dynamic1/51
+ - /obstacles1/dynamic1/61
+ - /obstacles1/dynamic1/71
+ - /obstacles1/dynamic1/81
+ - /obstacles1/dynamic1/91
+ - /crowdnav1/Marker1
+ - /Plan Manager1/goal1
+ - /Plan Manager1/subgoal1
+ - /MARL1
+ - /MARL1/robot_11
+ - /MARL1/robot_21/my_robot1
+ - /MARL1/robot_21/Goal Pose1
+ - /MARL1/robot_31
+ - /MARL1/robot_31/my_robot1
+ - /MARL1/robot_31/Goal Pose1
+ - /MARL1/robot_41
+ - /MARL1/robot_51
+ - /MARL1/robot_61/my_robot1
+ - /MARL1/robot_61/Goal Pose1
+ - /MARL1/robot_71/my_robot1
+ - /MARL1/robot_71/Goal Pose1
+ - /MARL1/robot_81/my_robot1
+ - /MARL1/robot_81/Goal Pose1
+ Splitter Ratio: 0.4374079406261444
+ Tree Height: 814
+ - Class: rviz/Selection
+ Name: Selection
+ - Class: rviz/Tool Properties
+ Expanded:
+ - /2D Pose Estimate1
+ - /2D Nav Goal1
+ - /Publish Point1
+ Name: Tool Properties
+ Splitter Ratio: 0.4285709857940674
+ - Class: rviz/Views
+ Expanded:
+ - /Current View1
+ Name: Views
+ Splitter Ratio: 0.5
+ - Class: rviz/Time
+ Experimental: false
+ Name: Time
+ SyncMode: 0
+ SyncSource: ""
+Preferences:
+ PromptSaveOnExit: true
+Toolbars:
+ toolButtonStyle: 2
+Visualization Manager:
+ Class: ""
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /visualizer/path
+ Name: Path_Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/layer/static
+ Name: static_layer
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 0.699999988079071
+ Class: rviz/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: true
+ Name: Map
+ Topic: /map
+ Unreliable: false
+ Use Timestamp: false
+ Value: true
+ - Alpha: 0.699999988079071
+ Class: rviz/Map
+ Color Scheme: map
+ Draw Behind: false
+ Enabled: false
+ Name: Global_costmap
+ Topic: /move_base/global_costmap/costmap
+ Unreliable: false
+ Use Timestamp: false
+ Value: false
+ - Alpha: 0.699999988079071
+ Class: rviz/Map
+ Color Scheme: costmap
+ Draw Behind: false
+ Enabled: false
+ Name: Local_costmap
+ Topic: /move_base/local_costmap/costmap
+ Unreliable: false
+ Use Timestamp: false
+ Value: false
+ Enabled: true
+ Name: Map
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /cadrl/goal_path_marker
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /cadrl/other_agents_markers
+ Name: MarkerArray
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: false
+ Name: cadrl
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 255; 0; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: teb
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 0; 0
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/TebLocalPlannerROS/local_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 85; 0; 127
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: mpc
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/MpcLocalPlannerROS/local_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 0; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.029999999329447746
+ Name: dwa
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/DWAPlannerROS/local_plan
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: Local plan
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 170; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: teb
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 0; 0
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/TebLocalPlannerROS/global_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 170; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.05000000074505806
+ Name: mpc
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/MpcLocalPlannerROS/global_plan
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 0; 170; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.029999999329447746
+ Name: dwa
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /move_base/DWAPlannerROS/global_plan
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: Global Plan
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 0.30399999022483826
+ Min Value: 0.30399999022483826
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/LaserScan
+ Color: 255; 0; 0
+ Color Transformer: FlatColor
+ Decay Time: 0
+ Enabled: true
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: LaserScan
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 8
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /scan
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: true
+ Enabled: true
+ Name: Sensors
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /action
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_00
+ Name: 0
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_01
+ Name: 1
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_02
+ Name: 2
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_03
+ Name: 3
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_04
+ Name: 4
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_05
+ Name: 5
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_06
+ Name: 6
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_07
+ Name: 7
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_08
+ Name: 8
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_09
+ Name: 9
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_10
+ Name: 10
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_11
+ Name: 11
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_12
+ Name: 12
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_13
+ Name: 13
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_14
+ Name: 14
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_15
+ Name: 15
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_16
+ Name: 16
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_17
+ Name: 17
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_18
+ Name: 18
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_19
+ Name: 19
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_20
+ Name: 20
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_21
+ Name: 21
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_22
+ Name: 22
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_23
+ Name: 23
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_circle_static_24
+ Name: 24
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: true
+ Name: static
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_00
+ Name: 0
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_01
+ Name: 1
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_02
+ Name: 2
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_03
+ Name: 3
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_05
+ Name: 4
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_06
+ Name: 5
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_07
+ Name: 6
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_08
+ Name: 7
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_09
+ Name: 8
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_04
+ Name: 9
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_10
+ Name: 10
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_11
+ Name: 11
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_12
+ Name: 12
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_13
+ Name: 13
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_15
+ Name: 14
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_05
+ Name: 15
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_16
+ Name: 16
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_17
+ Name: 17
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_18
+ Name: 18
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_19
+ Name: 19
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_20
+ Name: 20
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_21
+ Name: 21
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_22
+ Name: 22
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_23
+ Name: 23
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_24
+ Name: 24
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_25
+ Name: 25
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_26
+ Name: 26
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_27
+ Name: 27
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_28
+ Name: 28
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_29
+ Name: 29
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_30
+ Name: 30
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_31
+ Name: 31
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_32
+ Name: 32
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_33
+ Name: 33
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_34
+ Name: 34
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_35
+ Name: 35
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_36
+ Name: 36
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_37
+ Name: 37
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_38
+ Name: 38
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /flatland_server/debug/model/obstacle_dynamic_with_traj_39
+ Name: 39
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: true
+ Name: dynamic
+ Enabled: true
+ Name: obstacles
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /cadrl/goal_path_marker
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /other_agents_markers
+ Name: MarkerArray
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ Enabled: true
+ Name: crowdnav
+ - Class: rviz/Group
+ Displays:
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 25; 255; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Lines
+ Line Width: 0.029999999329447746
+ Name: global_path
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /vis_global_path
+ Unreliable: false
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 85; 170; 255
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Billboards
+ Line Width: 0.30000001192092896
+ Name: local_traj
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /vis_path_timed_astar
+ Unreliable: false
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /vis_goal
+ Name: goal
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /planning_vis/subgoal
+ Name: subgoal
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /vis_landmarks_
+ Name: landmarks
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /vis_triangle
+ Name: triangulation_map
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: occmap_local_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/occupancy
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: occmap_dynamic_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/occupancy_dynamic
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: occmap_static_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/occupancy_static
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: esdfmap_local_fused
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/esdf
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ - Alpha: 1
+ Autocompute Intensity Bounds: true
+ Autocompute Value Bounds:
+ Max Value: 10
+ Min Value: -10
+ Value: true
+ Axis: Z
+ Channel Name: intensity
+ Class: rviz/PointCloud2
+ Color: 255; 255; 255
+ Color Transformer: Intensity
+ Decay Time: 0
+ Enabled: false
+ Invert Rainbow: false
+ Max Color: 255; 255; 255
+ Min Color: 0; 0; 0
+ Name: esdfmap_static_only
+ Position Transformer: XYZ
+ Queue Size: 10
+ Selectable: true
+ Size (Pixels): 3
+ Size (m): 0.009999999776482582
+ Style: Points
+ Topic: /sdf_map/esdf_static
+ Unreliable: false
+ Use Fixed Frame: true
+ Use rainbow: true
+ Value: false
+ Enabled: true
+ Name: Plan Manager
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /time_space/vis_visted_samples
+ Name: samples
+ Namespaces:
+ {}
+ Queue Size: 1
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /planning_vis/goal
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Buffer Length: 1
+ Class: rviz/Path
+ Color: 25; 255; 0
+ Enabled: true
+ Head Diameter: 0.30000001192092896
+ Head Length: 0.20000000298023224
+ Length: 0.30000001192092896
+ Line Style: Lines
+ Line Width: 0.029999999329447746
+ Name: Path
+ Offset:
+ X: 0
+ Y: 0
+ Z: 0
+ Pose Color: 255; 85; 255
+ Pose Style: None
+ Queue Size: 10
+ Radius: 0.029999999329447746
+ Shaft Diameter: 0.10000000149011612
+ Shaft Length: 0.10000000149011612
+ Topic: /globalPlan
+ Unreliable: false
+ Value: true
+ - Class: rviz/Marker
+ Enabled: true
+ Marker Topic: /planning_vis/goal
+ Name: Marker
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 117; 80; 123
+ Enabled: true
+ Head Length: 0.5
+ Head Radius: 0.10000000149011612
+ Name: Pose
+ Queue Size: 10
+ Shaft Length: 3
+ Shaft Radius: 0.05000000074505806
+ Shape: Arrow
+ Topic: /sim_1/goal
+ Unreliable: false
+ Value: true
+ - Class: rviz/TF
+ Enabled: true
+ Frame Timeout: 15
+ Frames:
+ All Enabled: false
+ eval_sim_robot1_base_footprint:
+ Value: true
+ eval_sim_robot1_laser_link:
+ Value: true
+ eval_sim_robot1_odom:
+ Value: true
+ eval_sim_robot2_base_footprint:
+ Value: true
+ eval_sim_robot2_laser_link:
+ Value: true
+ eval_sim_robot2_odom:
+ Value: true
+ eval_sim_robot3_base_footprint:
+ Value: true
+ eval_sim_robot3_laser_link:
+ Value: true
+ eval_sim_robot3_odom:
+ Value: true
+ eval_sim_robot4_odom:
+ Value: true
+ eval_sim_robot5_odom:
+ Value: true
+ eval_sim_robot6_odom:
+ Value: true
+ eval_sim_robot7_odom:
+ Value: true
+ eval_sim_robot8_odom:
+ Value: true
+ map:
+ Value: true
+ sim_1_robot1_base_footprint:
+ Value: true
+ sim_1_robot1_laser_link:
+ Value: true
+ sim_1_robot1_odom:
+ Value: true
+ sim_1_robot2_base_footprint:
+ Value: true
+ sim_1_robot2_laser_link:
+ Value: true
+ sim_1_robot2_odom:
+ Value: true
+ sim_1_robot3_base_footprint:
+ Value: true
+ sim_1_robot3_laser_link:
+ Value: true
+ sim_1_robot3_odom:
+ Value: true
+ sim_1_robot4_odom:
+ Value: true
+ sim_1_robot5_odom:
+ Value: true
+ sim_1_robot6_odom:
+ Value: true
+ sim_1_robot7_odom:
+ Value: true
+ sim_1_robot8_odom:
+ Value: true
+ Marker Alpha: 1
+ Marker Scale: 1
+ Name: TF
+ Show Arrows: true
+ Show Axes: true
+ Show Names: true
+ Tree:
+ map:
+ eval_sim_robot1_odom:
+ eval_sim_robot1_base_footprint:
+ eval_sim_robot1_laser_link:
+ {}
+ eval_sim_robot2_odom:
+ eval_sim_robot2_base_footprint:
+ eval_sim_robot2_laser_link:
+ {}
+ eval_sim_robot3_odom:
+ eval_sim_robot3_base_footprint:
+ eval_sim_robot3_laser_link:
+ {}
+ eval_sim_robot4_odom:
+ {}
+ eval_sim_robot5_odom:
+ {}
+ eval_sim_robot6_odom:
+ {}
+ eval_sim_robot7_odom:
+ {}
+ eval_sim_robot8_odom:
+ {}
+ sim_1_robot1_odom:
+ sim_1_robot1_base_footprint:
+ sim_1_robot1_laser_link:
+ {}
+ sim_1_robot2_odom:
+ sim_1_robot2_base_footprint:
+ sim_1_robot2_laser_link:
+ {}
+ sim_1_robot3_odom:
+ sim_1_robot3_base_footprint:
+ sim_1_robot3_laser_link:
+ {}
+ sim_1_robot4_odom:
+ {}
+ sim_1_robot5_odom:
+ {}
+ sim_1_robot6_odom:
+ {}
+ sim_1_robot7_odom:
+ {}
+ sim_1_robot8_odom:
+ {}
+ Update Interval: 0
+ Value: true
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /eval_sim/flatland_server/debug/model/robot1
+ Name: my_robot
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /eval_sim/robot1/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_1
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /eval_sim/flatland_server/debug/model/robot2
+ Name: my_robot
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /eval_sim/robot2/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_2
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /eval_sim/flatland_server/debug/model/robot3
+ Name: my_robot
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /eval_sim/robot3/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_3
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/robot4
+ Name: my_robot
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /sim_1/robot4/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_4
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/robot5
+ Name: my_robot
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /sim_1/robot6/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_5
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/obstacle__random_dynamic_06
+ Name: my_robot
+ Namespaces:
+ "": true
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /sim_1/robot6/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_6
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/robot7
+ Name: my_robot
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /sim_1/robot7/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_7
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/robot8
+ Name: my_robot
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /sim_1/robot8/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_8
+ - Class: rviz/Group
+ Displays:
+ - Class: rviz/MarkerArray
+ Enabled: true
+ Marker Topic: /sim_1/flatland_server/debug/model/robot4
+ Name: my_robot
+ Namespaces:
+ {}
+ Queue Size: 100
+ Value: true
+ - Alpha: 1
+ Axes Length: 1
+ Axes Radius: 0.10000000149011612
+ Class: rviz/Pose
+ Color: 0; 255; 0
+ Enabled: true
+ Head Length: 0.10000000149011612
+ Head Radius: 0.15000000596046448
+ Name: Goal Pose
+ Queue Size: 10
+ Shaft Length: 0.5
+ Shaft Radius: 0.029999999329447746
+ Shape: Arrow
+ Topic: /sim_1/robot4/goal
+ Unreliable: false
+ Value: true
+ Enabled: true
+ Name: robot_9
+ Enabled: true
+ Name: MARL
+ Enabled: true
+ Global Options:
+ Background Color: 0; 0; 0
+ Default Light: true
+ Fixed Frame: map
+ Frame Rate: 30
+ Name: root
+ Tools:
+ - Class: rviz/MoveCamera
+ - Class: rviz/Interact
+ Hide Inactive Objects: true
+ - Class: rviz/Select
+ - Class: rviz/SetInitialPose
+ Theta std deviation: 0.2617993950843811
+ Topic: /initialpose
+ X std deviation: 0.5
+ Y std deviation: 0.5
+ - Class: rviz/SetGoal
+ Topic: /move_base_simple/goal
+ - Class: flatland_viz/SpawnModel
+ - Class: rviz_plugins/Goal3DTool
+ Topic: goal
+ - Class: flatland_viz/PauseSim
+ - Class: rviz_plugins/TaskGenerator
+ - Class: rviz/PublishPoint
+ Single click: true
+ Topic: /clicked_point
+ Value: true
+ Views:
+ Current:
+ Angle: -1.569939136505127
+ Class: rviz/TopDownOrtho
+ Enable Stereo Rendering:
+ Stereo Eye Separation: 0.05999999865889549
+ Stereo Focal Distance: 1
+ Swap Stereo Eyes: false
+ Value: false
+ Invert Z Axis: false
+ Name: Current View
+ Near Clip Distance: 0.009999999776482582
+ Scale: 38.79127883911133
+ Target Frame:
+ X: 2.8734543323516846
+ Y: -0.42915862798690796
+ Saved: ~
+Window Geometry:
+ Displays:
+ collapsed: false
+ Height: 1043
+ Hide Left Dock: false
+ Hide Right Dock: false
+ QMainWindow State: 000000ff00000000fd0000000400000000000002b4000003b9fc0200000005fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000005e80000039500000198000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000003b9000000c900ffffff000000010000010f00000270fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000002800000270000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004a00000003efc0100000002fb0000000800540069006d00650000000000000004a0000004f300fffffffb0000000800540069006d00650100000000000004500000000000000000000004c6000003b900000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+ Selection:
+ collapsed: false
+ Time:
+ collapsed: false
+ Tool Properties:
+ collapsed: false
+ Views:
+ collapsed: false
+ Width: 1920
+ X: 0
+ Y: 0
\ No newline at end of file
diff --git a/arena_bringup/rviz/nav_jackal.rviz b/arena_bringup/rviz/nav_jackal.rviz
index 29ef5766a..aebb5f98c 100755
--- a/arena_bringup/rviz/nav_jackal.rviz
+++ b/arena_bringup/rviz/nav_jackal.rviz
@@ -5,14 +5,13 @@ Panels:
Property Tree Widget:
Expanded:
- /robot1
- - /robot1/my_robot1
- /robot1/Goal Pose1
- /Global Plan1/teb1
- /Sensors1/LaserScan1
- /obstacles1
- /obstacles1/dynamic1/301
Splitter Ratio: 0.4374079406261444
- Tree Height: 787
+ Tree Height: 475
- Class: rviz/Selection
Name: Selection
- Class: rviz/Tool Properties
@@ -1252,18 +1251,18 @@ Visualization Manager:
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.009999999776482582
- Scale: 46.09282302856445
+ Scale: 25.905818939208984
Target Frame:
- X: 3.297379970550537
- Y: 2.1553478240966797
+ X: 11.80865478515625
+ Y: 8.243291854858398
Saved: ~
Window Geometry:
Displays:
collapsed: false
- Height: 1016
+ Height: 704
Hide Left Dock: false
Hide Right Dock: false
- QMainWindow State: 000000ff00000000fd00000004000000000000029e0000039efc0200000005fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000003be0000025d00000198000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d0000039e000000c900ffffff000000010000010f00000270fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000002800000270000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004a00000003efc0100000002fb0000000800540069006d00650000000000000004a0000004f300fffffffb0000000800540069006d00650100000000000004500000000000000000000004940000039e00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
+ QMainWindow State: 000000ff00000000fd0000000400000000000001ab00000266fc0200000005fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000003be0000025d00000198000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d00000266000000c900ffffff000000010000010f00000270fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000002800000270000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004a00000003efc0100000002fb0000000800540069006d00650000000000000004a0000004f300fffffffb0000000800540069006d006501000000000000045000000000000000000000035d0000026600000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Time:
@@ -1272,6 +1271,6 @@ Window Geometry:
collapsed: false
Views:
collapsed: false
- Width: 1848
+ Width: 1294
X: 72
Y: 27
diff --git a/arena_marl/CMakeLists.txt b/arena_marl/CMakeLists.txt
new file mode 100755
index 000000000..7fd10c43d
--- /dev/null
+++ b/arena_marl/CMakeLists.txt
@@ -0,0 +1,213 @@
+cmake_minimum_required(VERSION 3.0.2)
+project(arena_marl)
+
+## Compile as C++11, supported in ROS Kinetic and newer
+# add_compile_options(-std=c++11)
+
+## Find catkin macros and libraries
+## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
+## is used, also find other catkin packages
+find_package(catkin REQUIRED COMPONENTS
+ geometry_msgs
+ nav_msgs
+ roscpp
+ rospy
+ sensor_msgs
+ tf2
+ tf2_geometry_msgs
+ tf2_ros
+ flatland_msgs
+)
+
+## System dependencies are found with CMake's conventions
+# find_package(Boost REQUIRED COMPONENTS system)
+
+
+## Uncomment this if the package has a setup.py. This macro ensures
+## modules and global scripts declared therein get installed
+## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
+catkin_python_setup()
+
+################################################
+## Declare ROS messages, services and actions ##
+################################################
+
+## To declare and build messages, services or actions from within this
+## package, follow these steps:
+## * Let MSG_DEP_SET be the set of packages whose message types you use in
+## your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
+## * In the file package.xml:
+## * add a build_depend tag for "message_generation"
+## * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
+## * If MSG_DEP_SET isn't empty the following dependency has been pulled in
+## but can be declared for certainty nonetheless:
+## * add a exec_depend tag for "message_runtime"
+## * In this file (CMakeLists.txt):
+## * add "message_generation" and every package in MSG_DEP_SET to
+## find_package(catkin REQUIRED COMPONENTS ...)
+## * add "message_runtime" and every package in MSG_DEP_SET to
+## catkin_package(CATKIN_DEPENDS ...)
+## * uncomment the add_*_files sections below as needed
+## and list every .msg/.srv/.action file to be processed
+## * uncomment the generate_messages entry below
+## * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)
+
+## Generate messages in the 'msg' folder
+# add_message_files(
+# FILES
+# Message1.msg
+# Message2.msg
+# )
+
+## Generate services in the 'srv' folder
+# add_service_files(
+# FILES
+# Service1.srv
+# Service2.srv
+# )
+
+## Generate actions in the 'action' folder
+# add_action_files(
+# FILES
+# Action1.action
+# Action2.action
+# )
+
+## Generate added messages and services with any dependencies listed here
+# generate_messages(
+# DEPENDENCIES
+# geometry_msgs# nav_msgs# sensor_msgs# tf2_geometry_msgs
+# )
+
+################################################
+## Declare ROS dynamic reconfigure parameters ##
+################################################
+
+## To declare and build dynamic reconfigure parameters within this
+## package, follow these steps:
+## * In the file package.xml:
+## * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
+## * In this file (CMakeLists.txt):
+## * add "dynamic_reconfigure" to
+## find_package(catkin REQUIRED COMPONENTS ...)
+## * uncomment the "generate_dynamic_reconfigure_options" section below
+## and list every .cfg file to be processed
+
+## Generate dynamic reconfigure parameters in the 'cfg' folder
+# generate_dynamic_reconfigure_options(
+# cfg/DynReconf1.cfg
+# cfg/DynReconf2.cfg
+# )
+
+###################################
+## catkin specific configuration ##
+###################################
+## The catkin_package macro generates cmake config files for your package
+## Declare things to be passed to dependent projects
+## INCLUDE_DIRS: uncomment this if your package contains header files
+## LIBRARIES: libraries you create in this project that dependent projects also need
+## CATKIN_DEPENDS: catkin_packages dependent projects also need
+## DEPENDS: system dependencies of this project that dependent projects also need
+catkin_package(
+# INCLUDE_DIRS include
+# LIBRARIES arena_local_planner_drl
+# CATKIN_DEPENDS geometry_msgs nav_msgs roscpp rospy sensor_msgs tf2 tf2_geometry_msgs tf2_ros
+# DEPENDS system_lib
+)
+
+###########
+## Build ##
+###########
+
+## Specify additional locations of header files
+## Your package locations should be listed before other locations
+include_directories(
+# include
+ scripts
+ ${catkin_INCLUDE_DIRS}
+)
+
+## Declare a C++ library
+# add_library(${PROJECT_NAME}
+# src/${PROJECT_NAME}/arena_local_planner_drl.cpp
+# )
+
+## Add cmake target dependencies of the library
+## as an example, code may need to be generated before libraries
+## either from message generation or dynamic reconfigure
+# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
+## Declare a C++ executable
+## With catkin_make all packages are built within a single CMake context
+## The recommended prefix ensures that target names across packages don't collide
+# add_executable(${PROJECT_NAME}_node src/arena_local_planner_drl_node.cpp)
+
+## Rename C++ executable without prefix
+## The above recommended prefix causes long target names, the following renames the
+## target back to the shorter version for ease of user use
+## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
+# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")
+
+## Add cmake target dependencies of the executable
+## same as for the library above
+# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
+
+## Specify libraries to link a library or executable target against
+# target_link_libraries(${PROJECT_NAME}_node
+# ${catkin_LIBRARIES}
+# )
+
+#############
+## Install ##
+#############
+
+# all install targets should use catkin DESTINATION variables
+# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html
+
+## Mark executable scripts (Python etc.) for installation
+## in contrast to setup.py, you can choose the destination
+catkin_install_python(PROGRAMS
+# scripts/env/flatland_gym_env.py
+ DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+)
+
+## Mark executables for installation
+## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
+# install(TARGETS ${PROJECT_NAME}_node
+# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
+# )
+
+## Mark libraries for installation
+## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
+# install(TARGETS ${PROJECT_NAME}
+# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
+# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
+# )
+
+## Mark cpp header files for installation
+# install(DIRECTORY include/${PROJECT_NAME}/
+# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
+# FILES_MATCHING PATTERN "*.h"
+# PATTERN ".svn" EXCLUDE
+# )
+
+## Mark other files for installation (e.g. launch and bag files, etc.)
+# install(FILES
+# # myfile1
+# # myfile2
+# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
+# )
+
+#############
+## Testing ##
+#############
+
+## Add gtest based cpp test target and link libraries
+# catkin_add_gtest(${PROJECT_NAME}-test test/test_arena_local_planner_drl.cpp)
+# if(TARGET ${PROJECT_NAME}-test)
+# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
+# endif()
+
+## Add folders to be run by python nosetests
+# catkin_add_nosetests(test)
diff --git a/arena_marl/marl_agent/__init__.py b/arena_marl/marl_agent/__init__.py
new file mode 100755
index 000000000..e69de29bb
diff --git a/arena_marl/marl_agent/envs/__init__.py b/arena_marl/marl_agent/envs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/arena_marl/marl_agent/envs/pettingzoo_env.py b/arena_marl/marl_agent/envs/pettingzoo_env.py
new file mode 100644
index 000000000..572365e4a
--- /dev/null
+++ b/arena_marl/marl_agent/envs/pettingzoo_env.py
@@ -0,0 +1,390 @@
+"""PettingZoo Environment for Single-/Multi Agent Reinforcement Learning"""
+import numpy as np
+import rospy
+from time import sleep
+from typing import List, Tuple, Dict, Any, Union, Callable
+from gym import spaces
+from pettingzoo import *
+from pettingzoo.utils import from_parallel, to_parallel
+from warnings import warn
+
+import supersuit as ss
+
+from arena_marl.marl_agent.utils.supersuit_utils import MarkovVectorEnv_patched
+from arena_navigation.arena_local_planner.learning_based.arena_local_planner_drl.rl_agent.training_agent_wrapper import (
+ TrainingDRLAgent,
+)
+from task_generator.task_generator.marl_tasks import get_MARL_task
+from flatland_msgs.srv import StepWorld, StepWorldRequest
+
+# from marl_agent.utils.supersuit_utils import *
+# from rl_agent.utils.supersuit_utils import MarkovVectorEnv_patched
+
+
+def env_fn(**kwargs: Dict[str, Any]): # -> VecEnv:
+ """
+ The env function wraps the environment in 3 wrappers by default. These
+ wrappers contain logic that is common to many pettingzoo environments.
+ We recommend you use at least the OrderEnforcingWrapper on your own environment
+ to provide sane error messages. You can find full documentation for these methods
+ elsewhere in the developer documentation.
+ """
+ env = FlatlandPettingZooEnv(**kwargs)
+ env = from_parallel(env)
+ env = ss.pad_action_space_v0(env)
+ env = ss.pad_observations_v0(env)
+ env = to_parallel(env)
+ env = MarkovVectorEnv_patched(env, black_death=True)
+ return env
+
+
+class FlatlandPettingZooEnv(ParallelEnv):
+ """The SuperSuit Parallel environment steps every live agent at once."""
+
+ def __init__(
+ self,
+ num_agents: int,
+ agent_list_fn: Callable[[int, str, str, str, str], List[TrainingDRLAgent]],
+ PATHS: dict,
+ ns: str = None,
+ task_mode: str = "staged",
+ max_num_moves_per_eps: int = 1000,
+ agent_list_kwargs: Dict[str, Any] = None,
+ ) -> None:
+ """Initialization method for the Arena-Rosnav Pettingzoo Environment.
+
+ Args:
+ num_agents (int): Number of possible agents.
+ agent_list_fn (Callable[ [int, str, str, str, str], List[TrainingDRLAgent] ]): Initialization function for the agents. \
+ Returns a list of agent instances.
+ ns (str, optional): Environments' ROS namespace. There should only be one env per ns. Defaults to None.
+ task_mode (str, optional): Navigation task mode for the agents. Modes to chose from: ['random', 'staged']. \
+ Defaults to "random".
+ max_num_moves_per_eps (int, optional): Maximum number of moves per episode. Defaults to 1000.
+
+ Note:
+ These attributes should not be changed after initialization:
+ - possible_agents
+ - action_spaces
+ - observation_spaces
+ """
+ self._ns = "" if ns is None or not ns else f"{ns}/"
+ self._is_train_mode = rospy.get_param("/train_mode")
+ self.metadata = {}
+
+ self.agent_list: List[TrainingDRLAgent] = agent_list_fn(
+ num_agents, ns=ns, **(agent_list_kwargs or {})
+ )
+
+ self.agents = []
+ # list containing the unique robot namespaces
+ # used as identifier
+ self.possible_agents = [a._robot_sim_ns for a in self.agent_list]
+ self.agent_name_mapping = dict(
+ zip(self.possible_agents, list(range(len(self.possible_agents))))
+ )
+ self.agent_object_mapping = dict(zip(self.possible_agents, self.agent_list))
+ self.terminal_observation = {}
+
+ self._validate_agent_list()
+
+ # task manager
+ self.task_manager = get_MARL_task(
+ ns=ns,
+ mode=task_mode,
+ robot_ids=[a._robot_sim_ns for a in self.agent_list],
+ PATHS=PATHS,
+ )
+
+ # service clients
+ if self._is_train_mode:
+ self._service_name_step = f"{self._ns}step_world"
+ self._sim_step_client = rospy.ServiceProxy(
+ self._service_name_step, StepWorld
+ )
+
+ self._max_num_moves = max_num_moves_per_eps
+ self.action_provided, self.curr_actions = False, {}
+
+ def observation_space(self, agent: str) -> spaces.Box:
+ """Returns specific agents' observation space.
+
+ Args:
+ agent (str): Agent name as given in ``self.possible_agents``.
+
+ Returns:
+ spaces.Box: Observation space of type _gym.spaces_.
+ """
+ return self.agent_object_mapping[agent].observation_space
+
+ def action_space(self, agent: str) -> spaces.Box:
+ """Returns specific agents' action space.
+
+ Args:
+ agent (str): Agent name as given in ``self.possible_agents``.
+
+ Returns:
+ spaces.Box: Action space of type _gym.spaces_.
+ """
+ return self.agent_object_mapping[agent].action_space
+
+ def _validate_agent_list(self) -> None:
+ """Validates the agent list.
+
+ Description:
+ Checks if all agents are named differently. That means each robot adresses its own namespace.
+ """
+ assert len(self.possible_agents) == len(
+ set(self.possible_agents)
+ ), "Robot names and thus their namespaces, have to be unique!"
+
+ def reset(self) -> Dict[str, np.ndarray]:
+ """Resets the environment and returns the new set of observations (keyed by the agent name)
+
+ Description:
+ This method is called when all agents reach an end criterion. End criterions are: exceeding the \
+ max number of steps per episode, crash or reaching the end criterion.
+ The scene is then reseted.
+
+ Returns:
+ Dict[str, np.ndarray]: Observations dictionary in {_agent name_: _respective observations_}.
+ """
+ self.agents, self.num_moves, self.terminal_observation = (
+ self.possible_agents[:],
+ 0,
+ {},
+ )
+
+ # reset the reward calculator
+ for agent in self.agents:
+ self.agent_object_mapping[agent].reward_calculator.reset()
+
+ # reset the task manager
+ self.task_manager.reset()
+ # step one timestep in the simulation to update the scene
+ if self._is_train_mode:
+ self._sim_step_client()
+
+ # get first observations for the next episode
+ observations = {
+ agent: self.agent_object_mapping[agent].get_observations()[0]
+ for agent in self.agents
+ }
+
+ self.action_provided, self.curr_actions = False, {}
+ return observations
+
+ def step(
+ self, actions: Dict[str, np.ndarray]
+ ) -> Tuple[
+ Dict[str, np.ndarray],
+ Dict[str, float],
+ Dict[str, bool],
+ Dict[str, Dict[str, Any]],
+ ]:
+ """Simulates one timestep and returns the most recent environment information.
+
+ Description:
+ This function takes in velocity commands and applies those to the simulation.
+ Afterwards, agents' observations are retrieved from the current timestep and \
+ the reward is calculated. \
+ Proceeding with the ``RewardCalculator`` processing the observations and detecting certain events like \
+ if a crash occured, a goal was reached. Those informations are returned in the '*reward\_info*' \
+ which itself is a dictionary. \
+ Eventually, dictionaries containing every agents' observations, rewards, done flags and \
+ episode information is returned.
+
+ Args:
+ actions (Dict[str, np.ndarray]): Actions dictionary in {_agent name_: _respective observations_}.
+
+ Returns:
+ Tuple[ Dict[str, np.ndarray], Dict[str, float], Dict[str, bool], Dict[str, Dict[str, Any]], ]: Observations, \
+ rewards, done flags and episode informations dictionary.
+
+ Note:
+ Done reasons are mapped as follows: __0__ - episode length exceeded, __1__ - agent crashed, \
+ __2__ - agent reached its goal.
+ """
+ # If a user passes in actions with no agents, then just return empty observations, etc.
+ if not actions:
+ self.agents = []
+ return {}, {}, {}, {}
+
+ # actions
+ for agent in self.possible_agents:
+ if agent in actions:
+ self.agent_object_mapping[agent].publish_action(actions[agent])
+ else:
+ noop = np.zeros(shape=self.action_space(agent).shape)
+ self.agent_object_mapping[agent].publish_action(noop)
+
+ # fast-forward simulation
+ self.call_service_takeSimStep()
+ self.num_moves += 1
+
+ merged_obs, rewards, reward_infos = {}, {}, {}
+
+ for agent in actions:
+ # observations
+ merged, _dict = self.agent_object_mapping[agent].get_observations()
+ merged_obs[agent] = merged
+
+ # rewards and infos
+ reward, reward_info = self.agent_object_mapping[agent].get_reward(
+ action=actions[agent], obs_dict=_dict
+ )
+ rewards[agent], reward_infos[agent] = reward, reward_info
+
+ # dones & infos
+ dones, infos = self._get_dones(reward_infos), self._get_infos(reward_infos)
+
+ # remove done agents from the active agents list
+ self.agents = [agent for agent in self.agents if not dones[agent]]
+
+ for agent in self.possible_agents:
+ # agent is done in this episode
+ if agent in dones and dones[agent]:
+ self.terminal_observation[agent] = merged_obs[agent]
+ infos[agent]["terminal_observation"] = merged_obs[agent]
+ # agent is done since atleast last episode
+ elif agent not in self.agents:
+ if agent not in infos:
+ infos[agent] = {}
+ infos[agent]["terminal_observation"] = self.terminal_observation[agent]
+
+ return merged_obs, rewards, dones, infos
+
+ def apply_action(self, actions: np.ndarray) -> None:
+ """_summary_
+
+ Args:
+ action (np.ndarray): _description_
+
+ Returns:
+ _type_: _description_
+ """
+ if self.action_provided:
+ warn(
+ "Disobeyed method order. Called 'apply_action' multiple times without retrieving states."
+ )
+
+ # If a user passes in actions with no agents, then just return empty observations, etc.
+ if not actions:
+ self.agents = []
+ return {}, {}, {}, {}
+
+ # actions
+ for agent in self.possible_agents:
+ if agent in actions:
+ self.agent_object_mapping[agent].publish_action(actions[agent])
+ else:
+ noop = np.zeros(shape=self.action_space(agent).shape)
+ self.agent_object_mapping[agent].publish_action(noop)
+
+ self.num_moves += 1
+ self.action_provided, self.curr_actions = True, actions
+
+ def get_states(
+ self,
+ ) -> Tuple[
+ Dict[str, np.ndarray], Dict[str, float], Dict[str, bool], Dict[str, Any]
+ ]:
+ assert self.action_provided, "No actions provided"
+ merged_obs, rewards, reward_infos = {}, {}, {}
+
+ for agent in self.curr_actions:
+ # observations
+ merged, _dict = self.agent_object_mapping[agent].get_observations()
+ merged_obs[agent] = merged
+
+ # rewards and infos
+ reward, reward_info = self.agent_object_mapping[agent].get_reward(
+ action=self.curr_actions[agent], obs_dict=_dict
+ )
+ rewards[agent], reward_infos[agent] = reward, reward_info
+
+ # dones & infos
+ dones, infos = self._get_dones(reward_infos), self._get_infos(reward_infos)
+
+ # remove done agents from the active agents list
+ self.agents = [agent for agent in self.agents if not dones[agent]]
+
+ for agent in self.possible_agents:
+ # agent is done in this episode
+ if agent in dones and dones[agent]:
+ self.terminal_observation[agent] = merged_obs[agent]
+ infos[agent]["terminal_observation"] = merged_obs[agent]
+ # agent is done since atleast last episode
+ elif agent not in self.agents:
+ if agent not in infos:
+ infos[agent] = {}
+ infos[agent]["terminal_observation"] = self.terminal_observation[agent]
+
+ return merged_obs, rewards, dones, infos
+
+ @property
+ def max_num_agents(self):
+ return len(self.agents)
+
+ def call_service_takeSimStep(self, t: float = None):
+ """Fast-forwards the simulation.
+
+ Description:
+ Simulates the Flatland simulation for a certain amount of seconds.
+
+ Args:
+ t (float, optional): Time in seconds. When ``t`` is None, time is forwarded by ``step_size`` s \
+ (ROS parameter). Defaults to None.
+ """
+ request = StepWorldRequest() if t is None else StepWorldRequest(t)
+
+ try:
+ response = self._sim_step_client(request)
+ rospy.logdebug("step service=", response)
+ except rospy.ServiceException as e:
+ rospy.logdebug(f"step Service call failed: {e}")
+
+ def _get_dones(self, reward_infos: Dict[str, Dict[str, Any]]) -> Dict[str, bool]:
+ """Extracts end flags from the reward information dictionary.
+
+ Args:
+ reward_infos (Dict[str, Dict[str, Any]]): Episode information from the ``RewardCalculator`` in \
+ {_agent name_: _reward infos_}.
+
+ Returns:
+ Dict[str, bool]: Dones dictionary in {_agent name_: _done flag_}
+
+ Note:
+ Relevant dictionary keys are: "is_done", "is_success", "done_reason"
+ """
+ return (
+ {agent: reward_infos[agent]["is_done"] for agent in self.agents}
+ if self.num_moves < self._max_num_moves
+ else {agent: True for agent in self.agents}
+ )
+
+ def _get_infos(
+ self, reward_infos: Dict[str, Dict[str, Any]]
+ ) -> Dict[str, Dict[str, Any]]:
+ """Extracts the current episode information from the reward information dictionary.
+
+ Args:
+ reward_infos (Dict[str, Dict[str, Any]]): Episode information from the ``RewardCalculator`` in \
+ {_agent name_: _reward infos_}.
+
+ Returns:
+ Dict[str, Dict[str, Any]]: Info dictionary in {_agent name_: _done flag_}
+
+ Note:
+ Relevant dictionary keys are: "is_done", "is_success", "done_reason"
+ """
+ infos = {agent: {} for agent in self.agents}
+ for agent in self.agents:
+ if reward_infos[agent]["is_done"]:
+ infos[agent] = reward_infos[agent]
+ elif self.num_moves >= self._max_num_moves:
+ infos[agent] = {
+ "done_reason": 0,
+ "is_success": 0,
+ }
+ return infos
diff --git a/arena_marl/marl_agent/utils/__init__.py b/arena_marl/marl_agent/utils/__init__.py
new file mode 100755
index 000000000..e69de29bb
diff --git a/arena_marl/marl_agent/utils/action_collector.py b/arena_marl/marl_agent/utils/action_collector.py
new file mode 100755
index 000000000..7c95eea27
--- /dev/null
+++ b/arena_marl/marl_agent/utils/action_collector.py
@@ -0,0 +1,68 @@
+import rospy
+import numpy as np
+
+from tf.transformations import *
+from geometry_msgs.msg import Twist
+from gym import spaces
+
+
+class ActionCollector:
+ def __init__(self):
+ self.v_max_ = 0.8
+ self.w_max_ = 1.2
+ self.action_library = {
+ 0: {"linear": 0.0, "angular": -self.w_max_},
+ 1: {"linear": self.v_max_, "angular": 0.0},
+ 2: {"linear": 0.0, "angular": self.w_max_},
+ 3: {"linear": self.v_max_, "angular": self.w_max_ / 2},
+ 4: {"linear": self.v_max_, "angular": -self.w_max_ / 2},
+ 5: {"linear": 0.0, "angular": 0.0},
+ }
+ self.N_DISCRETE_ACTIONS = len(self.action_library)
+ self.action_space = spaces.Discrete(self.N_DISCRETE_ACTIONS)
+
+ def get_action_space(self):
+ return self.action_space
+
+ def get_cmd_vel(self, action_id):
+ vel_msg = Twist()
+ vel_msg.linear.x = self.action_library[action_id]["linear"]
+ vel_msg.angular.z = self.action_library[action_id]["angular"]
+ return vel_msg
+
+
+if __name__ == "__main__":
+ action_collector = ActionCollector()
+ print(action_collector.get_cmd_vel(1))
+
+ box = spaces.Box(low=3.0, high=4, shape=(2, 2))
+ print(box)
+ box.seed(4)
+ for _ in range(1):
+ print(box.sample())
+
+ min_position = 0
+ max_position = 10
+ max_speed = 2
+ goal_position = 0.5
+ low = np.array([min_position, -max_speed])
+ high = np.array([max_position, max_speed])
+ action_space = spaces.Discrete(3) # action space
+ observation_space = spaces.Box(low, high) #
+ print("*" * 10)
+ print(observation_space)
+ for _ in range(2):
+ print(observation_space.sample())
+
+ observation_space = spaces.Tuple(
+ (
+ spaces.Box(low=0, high=10, shape=(10,), dtype=np.float32),
+ spaces.Box(low=-10, high=0, shape=(3 + 2,), dtype=np.float32),
+ )
+ )
+ print("2" * 10)
+ print(observation_space.sample())
+ print(type(observation_space.sample()))
+
+ reward = spaces.Discrete(4)
+ print(type(reward.sample()))
diff --git a/arena_marl/marl_agent/utils/debug.py b/arena_marl/marl_agent/utils/debug.py
new file mode 100755
index 000000000..0349734d2
--- /dev/null
+++ b/arena_marl/marl_agent/utils/debug.py
@@ -0,0 +1,16 @@
+import time
+from functools import wraps
+
+
+def timeit(f):
+ @wraps(f)
+ def timed(*args, **kw):
+
+ ts = time.time()
+ result = f(*args, **kw)
+ te = time.time()
+
+ print("func:%r args:[%r, %r] took: %2.4f sec" % (f.__name__, args, kw, te - ts))
+ return result
+
+ return timed
diff --git a/arena_marl/marl_agent/utils/observation_collector.py b/arena_marl/marl_agent/utils/observation_collector.py
new file mode 100755
index 000000000..7181ed443
--- /dev/null
+++ b/arena_marl/marl_agent/utils/observation_collector.py
@@ -0,0 +1,300 @@
+#! /usr/bin/env python
+from typing import Tuple
+
+from numpy.core.numeric import normalize_axis_tuple
+import rospy
+import random
+import numpy as np
+from collections import deque
+
+import time # for debuging
+import threading
+
+# observation msgs
+from sensor_msgs.msg import LaserScan
+from geometry_msgs.msg import Pose2D, PoseStamped, Twist
+from nav_msgs.msg import Path
+from rosgraph_msgs.msg import Clock
+from nav_msgs.msg import Odometry
+
+# message filter
+import message_filters
+
+# for transformations
+from tf.transformations import *
+
+from gym import spaces
+import numpy as np
+
+
+class ObservationCollector:
+ def __init__(
+ self,
+ ns: str,
+ num_lidar_beams: int,
+ lidar_range: float,
+ ):
+ """a class to collect and merge observations
+
+ Args:
+ num_lidar_beams (int): [description]
+ lidar_range (float): [description]
+ """
+ """SET A METHOD TO EXTRACT IF MARL IS BEIN REQUESTED"""
+ MARL = rospy.get_param("num_robots") > 1
+
+ self.ns = ns
+ if ns is None or ns == "":
+ self.ns_prefix = ""
+ else:
+ self.ns_prefix = (
+ "/" + ns + "/" if not ns.endswith("/") else "/" + ns
+ )
+
+ # define observation_space
+ self.observation_space = ObservationCollector._stack_spaces(
+ (
+ spaces.Box(
+ low=0,
+ high=lidar_range,
+ shape=(num_lidar_beams,),
+ dtype=np.float32,
+ ),
+ spaces.Box(low=0, high=10, shape=(1,), dtype=np.float32),
+ spaces.Box(
+ low=-np.pi, high=np.pi, shape=(1,), dtype=np.float32
+ ),
+ )
+ )
+
+ self._laser_num_beams = num_lidar_beams
+
+ self._clock = Clock()
+ self._scan = LaserScan()
+ self._robot_pose = Pose2D()
+ self._robot_vel = Twist()
+ self._subgoal = Pose2D()
+ self._globalplan = np.array([])
+
+ # subscriptions
+ self._scan_sub = rospy.Subscriber(
+ f"{self.ns_prefix}scan",
+ LaserScan,
+ self.callback_scan,
+ tcp_nodelay=True,
+ )
+ self._robot_state_sub = rospy.Subscriber(
+ f"{self.ns_prefix}odom",
+ Odometry,
+ self.callback_robot_state,
+ tcp_nodelay=True,
+ )
+
+ # self._clock_sub = rospy.Subscriber(
+ # f'{self.ns_prefix}clock', Clock, self.callback_clock, tcp_nodelay=True)
+
+ # when using MARL listen to goal directly since no intermediate planner is considered
+ goal_topic = (
+ f"{self.ns_prefix}goal" if MARL else f"{self.ns_prefix}subgoal"
+ )
+ self._subgoal_sub = rospy.Subscriber(
+ f"{goal_topic}", PoseStamped, self.callback_subgoal
+ )
+ self._globalplan_sub = rospy.Subscriber(
+ f"{self.ns_prefix}globalPlan", Path, self.callback_global_plan
+ )
+
+ # synchronization parameters
+ self._first_sync_obs = (
+ True # whether to return first sync'd obs or most recent
+ )
+ self.max_deque_size = 10
+ self._sync_slop = 0.1
+
+ self._laser_deque = deque()
+ self._rs_deque = deque()
+
+ def get_observation_space(self):
+ return self.observation_space
+
+ def get_observations(self):
+ # try to retrieve sync'ed obs
+ laser_scan, robot_pose = self.get_sync_obs()
+ if laser_scan is not None and robot_pose is not None:
+ # print("Synced successfully")
+ self._scan = laser_scan
+ self._robot_pose = robot_pose
+ # else:
+ # print("Not synced")
+ if len(self._scan.ranges) > 0:
+ scan = self._scan.ranges.astype(np.float32)
+ else:
+ scan = np.zeros(self._laser_num_beams, dtype=float)
+
+ rho, theta = ObservationCollector._get_goal_pose_in_robot_frame(
+ self._subgoal, self._robot_pose
+ )
+ merged_obs = np.hstack([scan, np.array([rho, theta])])
+
+ obs_dict = {
+ "laser_scan": scan,
+ "goal_in_robot_frame": [rho, theta],
+ "global_plan": self._globalplan,
+ "robot_pose": self._robot_pose,
+ }
+
+ self._laser_deque.clear()
+ self._rs_deque.clear()
+ return merged_obs, obs_dict
+
+ @staticmethod
+ def _get_goal_pose_in_robot_frame(
+ goal_pos: Pose2D, robot_pos: Pose2D
+ ) -> Tuple[float, float]:
+ y_relative = goal_pos.y - robot_pos.y
+ x_relative = goal_pos.x - robot_pos.x
+ rho = (x_relative ** 2 + y_relative ** 2) ** 0.5
+ theta = (
+ np.arctan2(y_relative, x_relative) - robot_pos.theta + 4 * np.pi
+ ) % (2 * np.pi) - np.pi
+ return rho, theta
+
+ def get_sync_obs(self):
+ laser_scan = None
+ robot_pose = None
+
+ # print(f"laser deque: {len(self._laser_deque)}, robot state deque: {len(self._rs_deque)}")
+ while len(self._rs_deque) > 0 and len(self._laser_deque) > 0:
+ laser_scan_msg = self._laser_deque.popleft()
+ robot_pose_msg = self._rs_deque.popleft()
+
+ laser_stamp = laser_scan_msg.header.stamp.to_sec()
+ robot_stamp = robot_pose_msg.header.stamp.to_sec()
+
+ while abs(laser_stamp - robot_stamp) > self._sync_slop:
+ if laser_stamp > robot_stamp:
+ if len(self._rs_deque) == 0:
+ return laser_scan, robot_pose
+ robot_pose_msg = self._rs_deque.popleft()
+ robot_stamp = robot_pose_msg.header.stamp.to_sec()
+ else:
+ if len(self._laser_deque) == 0:
+ return laser_scan, robot_pose
+ laser_scan_msg = self._laser_deque.popleft()
+ laser_stamp = laser_scan_msg.header.stamp.to_sec()
+
+ laser_scan = self.process_scan_msg(laser_scan_msg)
+ robot_pose, _ = self.process_robot_state_msg(robot_pose_msg)
+
+ if self._first_sync_obs:
+ break
+
+ # print(f"Laser_stamp: {laser_stamp}, Robot_stamp: {robot_stamp}")
+ return laser_scan, robot_pose
+
+ def callback_clock(self, msg_Clock):
+ self._clock = msg_Clock.clock.to_sec()
+ return
+
+ def callback_subgoal(self, msg_Subgoal):
+ self._subgoal = self.process_subgoal_msg(msg_Subgoal)
+ return
+
+ def callback_global_plan(self, msg_global_plan):
+ self._globalplan = ObservationCollector.process_global_plan_msg(
+ msg_global_plan
+ )
+ return
+
+ def callback_scan(self, msg_laserscan):
+ if len(self._laser_deque) == self.max_deque_size:
+ self._laser_deque.popleft()
+ self._laser_deque.append(msg_laserscan)
+
+ def callback_robot_state(self, msg_robotstate):
+ if len(self._rs_deque) == self.max_deque_size:
+ self._rs_deque.popleft()
+ self._rs_deque.append(msg_robotstate)
+
+ def callback_observation_received(
+ self, msg_LaserScan, msg_RobotStateStamped
+ ):
+ # process sensor msg
+ self._scan = self.process_scan_msg(msg_LaserScan)
+ self._robot_pose, self._robot_vel = self.process_robot_state_msg(
+ msg_RobotStateStamped
+ )
+ self.obs_received = True
+ return
+
+ def process_scan_msg(self, msg_LaserScan: LaserScan):
+ # remove_nans_from_scan
+ self._scan_stamp = msg_LaserScan.header.stamp.to_sec()
+ scan = np.array(msg_LaserScan.ranges)
+ scan[np.isnan(scan)] = msg_LaserScan.range_max
+ msg_LaserScan.ranges = scan
+ return msg_LaserScan
+
+ def process_robot_state_msg(self, msg_Odometry):
+ pose3d = msg_Odometry.pose.pose
+ twist = msg_Odometry.twist.twist
+ return self.pose3D_to_pose2D(pose3d), twist
+
+ def process_pose_msg(self, msg_PoseWithCovarianceStamped):
+ # remove Covariance
+ pose_with_cov = msg_PoseWithCovarianceStamped.pose
+ pose = pose_with_cov.pose
+ return self.pose3D_to_pose2D(pose)
+
+ def process_subgoal_msg(self, msg_Subgoal):
+ return self.pose3D_to_pose2D(msg_Subgoal.pose)
+
+ @staticmethod
+ def process_global_plan_msg(globalplan):
+ global_plan_2d = list(
+ map(
+ lambda p: ObservationCollector.pose3D_to_pose2D(p.pose),
+ globalplan.poses,
+ )
+ )
+ return np.array(list(map(lambda p2d: [p2d.x, p2d.y], global_plan_2d)))
+
+ @staticmethod
+ def pose3D_to_pose2D(pose3d):
+ pose2d = Pose2D()
+ pose2d.x = pose3d.position.x
+ pose2d.y = pose3d.position.y
+ quaternion = (
+ pose3d.orientation.x,
+ pose3d.orientation.y,
+ pose3d.orientation.z,
+ pose3d.orientation.w,
+ )
+ euler = euler_from_quaternion(quaternion)
+ yaw = euler[2]
+ pose2d.theta = yaw
+ return pose2d
+
+ @staticmethod
+ def _stack_spaces(ss: Tuple[spaces.Box]):
+ low = []
+ high = []
+ for space in ss:
+ low.extend(space.low.tolist())
+ high.extend(space.high.tolist())
+ return spaces.Box(np.array(low).flatten(), np.array(high).flatten())
+
+
+if __name__ == "__main__":
+
+ rospy.init_node("states", anonymous=True)
+ print("start")
+
+ state_collector = ObservationCollector("sim1/", 360, 10)
+ i = 0
+ r = rospy.Rate(100)
+ while i <= 1000:
+ i = i + 1
+ obs = state_collector.get_observations()
+
+ time.sleep(0.001)
diff --git a/arena_marl/marl_agent/utils/reward.py b/arena_marl/marl_agent/utils/reward.py
new file mode 100755
index 000000000..d4b53cb16
--- /dev/null
+++ b/arena_marl/marl_agent/utils/reward.py
@@ -0,0 +1,472 @@
+"""Reward Calculator for DRL"""
+import numpy as np
+import scipy.spatial
+
+from geometry_msgs.msg import Pose2D
+from typing import Dict, Tuple, Union
+
+from rl_agent.utils.reward import RewardCalculator
+
+class RewardCalculator(RewardCalculator):
+ def __init__(
+ self,
+ robot_radius: float,
+ safe_dist: float,
+ goal_radius: float,
+ rule: str = "rule_00",
+ extended_eval: bool = False,
+ ):
+ """A facotry class for reward calculation. Holds various reward functions.
+
+ An overview of the reward functions can be found under:
+ https://github.com/ignc-research/arena-rosnav/blob/local_planner_subgoalmode/docs/DRL-Training.md#reward-functions
+
+ Possible reward functions: "_rule_00_", "_rule_01_", "_rule_02_", "_rule_03_", "_rule_04_"
+
+ Args:
+ robot_radius (float): Robots' radius in meters.
+ safe_dist (float): Robots' safe distance in meters.
+ goal_radius (float): Radius of the goal.
+ rule (str, optional): The desired reward function name. Defaults to "rule_00".
+ extended_eval (bool, optional): Extended evaluation mode. Defaults to False.
+ """
+ self.curr_reward = 0
+ # additional info will be stored here and be returned alonge with reward.
+ self.info = {}
+ self.robot_radius = robot_radius
+ self.goal_radius = goal_radius
+ self.last_goal_dist = None
+ self.last_dist_to_path = None
+ self.last_action = None
+ self.safe_dist = robot_radius + safe_dist
+ self._extended_eval = extended_eval
+
+ self.kdtree = None
+
+ self._cal_funcs = {
+ "rule_00": RewardCalculator._cal_reward_rule_00,
+ "rule_01": RewardCalculator._cal_reward_rule_01,
+ "rule_02": RewardCalculator._cal_reward_rule_02,
+ "rule_03": RewardCalculator._cal_reward_rule_03,
+ "rule_04": RewardCalculator._cal_reward_rule_04,
+ }
+ self.cal_func = self._cal_funcs[rule]
+
+ def reset(self) -> None:
+ """Resets variables related to the episode."""
+ self.last_goal_dist = None
+ self.last_dist_to_path = None
+ self.last_action = None
+ self.kdtree = None
+
+ def _reset(self) -> None:
+ """Resets variables related to current step."""
+ self.curr_reward = 0
+ self.info = {}
+
+ def get_reward(
+ self,
+ laser_scan: np.ndarray,
+ goal_in_robot_frame: Tuple[float, float],
+ *args,
+ **kwargs
+ ) -> Tuple[float, Dict[str, Union[str, int, bool]]]:
+ """Returns reward and info to the gym environment.
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ goal_in_robot_frame (Tuple[float, float]): Position (rho, theta) of the goal in the robot frame (polar coordinate).
+
+ Returns:
+ Tuple[float, Dict[str, Union[str, int, bool]]]: Tuple of calculated rewards for the current step, \
+ and the reward information dictionary.
+ """
+ self._reset()
+ self.cal_func(self, laser_scan, goal_in_robot_frame, *args, **kwargs)
+ return self.curr_reward, self.info
+
+ def _cal_reward_rule_00(
+ self,
+ laser_scan: np.ndarray,
+ goal_in_robot_frame: Tuple[float, float],
+ *args,
+ **kwargs
+ ):
+ """Reward function: '_rule\_00_'
+
+ Description:
+ "rule_00" incorporates the most instinctive characteristics for learning navigation into its \
+ reward calculation. The reward function is made up of only 4 summands, namely the success \
+ reward, the collision reward, the danger reward and the progress reward. Similar reward functions \
+ were utilized in numerous research projects and produced promising results. Thus, this \
+ rule is chosen to be the basis for further experiments with extended versions of it. \
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ goal_in_robot_frame (Tuple[float, float]): Position (rho, theta) of the goal in the robot frame (polar coordinate).
+ """
+ self._reward_goal_reached(goal_in_robot_frame)
+ self._reward_safe_dist(laser_scan, punishment=0.25)
+ self._reward_collision(laser_scan)
+ self._reward_goal_approached(
+ goal_in_robot_frame, reward_factor=0.3, penalty_factor=0.4
+ )
+
+ def _cal_reward_rule_01(
+ self,
+ laser_scan: np.ndarray,
+ goal_in_robot_frame: Tuple[float, float],
+ *args,
+ **kwargs
+ ):
+ """Reward function: '_rule\_01_'
+
+ Description:
+ This reward function extends "rule 00" by adding a penalty factor that affects the current \
+ reward like an abstract fuel consumption factor. In principle, a certain penalty is applied \
+ for each action taken depending on the velocity and thus imposes a severer punishment for \
+ dissipated driving.
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ goal_in_robot_frame (Tuple[float, float]): Position (rho, theta) of the goal in the robot frame (polar coordinate).
+ """
+ self._reward_distance_traveled(
+ kwargs["action"], consumption_factor=0.0075
+ )
+ self._reward_goal_reached(goal_in_robot_frame, reward=15)
+ self._reward_safe_dist(laser_scan, punishment=0.25)
+ self._reward_collision(laser_scan, punishment=10)
+ self._reward_goal_approached(
+ goal_in_robot_frame, reward_factor=0.3, penalty_factor=0.4
+ )
+
+ def _cal_reward_rule_02(
+ self,
+ laser_scan: np.ndarray,
+ goal_in_robot_frame: Tuple[float, float],
+ *args,
+ **kwargs
+ ):
+ """Reward function: '_rule\_02_'
+
+ Description:
+ Previous reward functions required only basic information from the simulation. For this rule, \
+ which builds on the reward function "rule 01", we introduced the assessment of the progress \
+ regarding the global plan. The additional summand essentially rewards the agent for following \
+ the global plan. It was implemented in order to test the effect of including the global plan in \
+ the reward calculation. \
+ Since "rule 02" shares almost the same reward function composition as "rule 01", similar performance \
+ was expected to some extent. The desired behavior for this agent was to learn faster and \
+ to drive more goal-oriented than the agent of "rule 01", as this rule was provided the global plan. \
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ goal_in_robot_frame (Tuple[float, float]): Position (rho, theta) of the goal in the robot frame (polar coordinate).
+ """
+ self._reward_distance_traveled(
+ kwargs["action"], consumption_factor=0.0075
+ )
+ self._reward_following_global_plan(
+ kwargs["global_plan"], kwargs["robot_pose"]
+ )
+ self._reward_goal_reached(goal_in_robot_frame, reward=15)
+ self._reward_safe_dist(laser_scan, punishment=0.25)
+ self._reward_collision(laser_scan, punishment=10)
+ self._reward_goal_approached(
+ goal_in_robot_frame, reward_factor=0.3, penalty_factor=0.4
+ )
+
+ def _cal_reward_rule_03(
+ self,
+ laser_scan: np.ndarray,
+ goal_in_robot_frame: Tuple[float, float],
+ *args,
+ **kwargs
+ ):
+ """Reward function: '_rule\_03_'
+
+ Description:
+ The base of this rule is made up of summands from "rule 00". The two extra factors were \
+ introduced in order to further leverage the global plan information for reward generation. \
+ One that rewards the following of the global path and one for valuing the agents’ action - \
+ positively, when it approaches the global plan - negatively when the robot distances itself \
+ from the path. \
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data. \
+ goal_in_robot_frame (Tuple[float, float]): Position (rho, theta) of the goal in the robot frame (polar coordinate). \
+ """
+ self._reward_following_global_plan(
+ kwargs["global_plan"], kwargs["robot_pose"], kwargs["action"]
+ )
+ if laser_scan.min() > self.safe_dist:
+ self._reward_distance_global_plan(
+ kwargs["global_plan"],
+ kwargs["robot_pose"],
+ reward_factor=0.2,
+ penalty_factor=0.3,
+ )
+ else:
+ self.last_dist_to_path = None
+ self._reward_goal_reached(goal_in_robot_frame, reward=15)
+ self._reward_safe_dist(laser_scan, punishment=0.25)
+ self._reward_collision(laser_scan, punishment=10)
+ self._reward_goal_approached(
+ goal_in_robot_frame, reward_factor=0.3, penalty_factor=0.4
+ )
+
+ def _cal_reward_rule_04(
+ self,
+ laser_scan: np.ndarray,
+ goal_in_robot_frame: Tuple[float, float],
+ *args,
+ **kwargs
+ ):
+ """Reward function: '_rule\_04_'
+
+ Description:
+ This reward function extends "rule 03" with an additional term that punishes the agent for \
+ abruptly changing the direction. Previous test runs, conducted right after the implementation, \
+ evidenced that although the agent performed well on different tasks, the robot tended to drive \
+ in tail motion. It was aimed to adjust this behavior by including this additional penalty term. \
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ goal_in_robot_frame (Tuple[float, float]): Position (rho, theta) of the goal in the robot frame (polar coordinate).
+ """
+ self._reward_abrupt_direction_change(kwargs["action"])
+ self._reward_following_global_plan(
+ kwargs["global_plan"], kwargs["robot_pose"], kwargs["action"]
+ )
+ if laser_scan.min() > self.safe_dist:
+ self._reward_distance_global_plan(
+ kwargs["global_plan"],
+ kwargs["robot_pose"],
+ reward_factor=0.2,
+ penalty_factor=0.3,
+ )
+ else:
+ self.last_dist_to_path = None
+ self._reward_goal_reached(goal_in_robot_frame, reward=15)
+ self._reward_safe_dist(laser_scan, punishment=0.25)
+ self._reward_collision(laser_scan, punishment=10)
+ self._reward_goal_approached(
+ goal_in_robot_frame, reward_factor=0.3, penalty_factor=0.4
+ )
+
+ def _reward_goal_reached(
+ self, goal_in_robot_frame: Tuple[float, float], reward: float = 15
+ ):
+ """Reward for reaching the goal.
+
+ Args:
+ goal_in_robot_frame (Tuple[float, float], optional): Position (rho, theta) of the goal in the robot frame (polar coordinate).
+ reward (float, optional): Reward amount for reaching the goal. Defaults to 15.
+ """
+ if goal_in_robot_frame[0] < self.goal_radius:
+ self.curr_reward = reward
+ self.info["is_done"] = True
+ self.info["done_reason"] = 2
+ self.info["is_success"] = 1
+ else:
+ self.info["is_done"] = False
+
+ def _reward_goal_approached(
+ self,
+ goal_in_robot_frame=Tuple[float, float],
+ reward_factor: float = 0.3,
+ penalty_factor: float = 0.5,
+ ):
+ """Reward for approaching the goal.
+
+ Args:
+ goal_in_robot_frame ([type], optional): Position (rho, theta) of the goal in the robot frame (polar coordinate). Defaults to Tuple[float, float].
+ reward_factor (float, optional): Factor to be multiplied when the difference between current distance to goal and the previous one is positive. \
+ Defaults to 0.3.
+ penalty_factor (float, optional): Factor to be multiplied when the difference between current distance to goal and the previous one is negative. Defaults to 0.5.
+ """
+ if self.last_goal_dist is not None:
+ # goal_in_robot_frame : [rho, theta]
+
+ # higher negative weight when moving away from goal
+ # (to avoid driving unnecessary circles when train in contin. action space)
+ if (self.last_goal_dist - goal_in_robot_frame[0]) > 0:
+ w = reward_factor
+ else:
+ w = penalty_factor
+ reward = w * (self.last_goal_dist - goal_in_robot_frame[0])
+
+ # print("reward_goal_approached: {}".format(reward))
+ self.curr_reward += reward
+ self.last_goal_dist = goal_in_robot_frame[0]
+
+ def _reward_collision(self, laser_scan: np.ndarray, punishment: float = 10):
+ """Reward for colliding with an obstacle.
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ punishment (float, optional): Punishment amount for collisions. Defaults to 10.
+ """
+ if laser_scan.min() <= self.robot_radius:
+ self.curr_reward -= punishment
+
+ if not self._extended_eval:
+ self.info["is_done"] = True
+ self.info["done_reason"] = 1
+ self.info["is_success"] = 0
+ else:
+ self.info["crash"] = True
+
+ def _reward_safe_dist(
+ self, laser_scan: np.ndarray, punishment: float = 0.15
+ ):
+ """Reward for undercutting safe distance.
+
+ Args:
+ laser_scan (np.ndarray): 2D laser scan data.
+ punishment (float, optional): Punishment amount. Could be applied in consecutive timesteps. \
+ Defaults to 0.15.
+ """
+ if laser_scan.min() < self.safe_dist:
+ self.curr_reward -= punishment
+
+ if self._extended_eval:
+ self.info["safe_dist"] = True
+
+ def _reward_not_moving(
+ self, action: np.ndarray = None, punishment: float = 0.01
+ ):
+ """Reward for not moving.
+
+ Args:
+ action (np.ndarray, optional): Array of shape (2,). First entry, linear velocity. \
+ Second entry, angular velocity. Defaults to None.
+ punishment (float, optional): Punishment for not moving. Defaults to 0.01.
+
+ Note:
+ Only applies half of the punishment amount when angular velocity is larger than zero.
+ """
+ if action is not None and action[0] == 0.0:
+ self.curr_reward -= (
+ punishment if action[1] == 0.0 else punishment / 2
+ )
+
+ def _reward_distance_traveled(
+ self,
+ action: np.array = None,
+ punishment: float = 0.01,
+ consumption_factor: float = 0.005,
+ ):
+ """Reward for driving a certain distance. Supposed to represent "fuel consumption".
+
+ Args:
+ action (np.array, optional): Array of shape (2,). First entry, linear velocity. \
+ Second entry, angular velocity. Defaults to None.
+ punishment (float, optional): Punishment when action can't be retrieved. Defaults to 0.01.
+ consumption_factor (float, optional): Factor for the weighted velocity punishment. Defaults to 0.005.
+ """
+ if action is None:
+ self.curr_reward -= punishment
+ else:
+ lin_vel = action[0]
+ ang_vel = action[1]
+ reward = (lin_vel + (ang_vel * 0.001)) * consumption_factor
+ self.curr_reward -= reward
+
+ def _reward_distance_global_plan(
+ self,
+ global_plan: np.array,
+ robot_pose: Pose2D,
+ reward_factor: float = 0.1,
+ penalty_factor: float = 0.15,
+ ):
+ """Reward for approaching/veering away the global plan.
+
+ Description:
+ Weighted difference between prior distance to global plan and current distance to global plan.
+
+ Args:
+ global_plan (np.array): Array containing 2D poses.
+ robot_pose (Pose2D): Robot position.
+ reward_factor (float, optional): Factor to be multiplied when the difference between current \
+ distance to global plan and the previous one is positive. Defaults to 0.1.
+ penalty_factor (float, optional): Factor to be multiplied when the difference between current \
+ distance to global plan and the previous one is negative. Defaults to 0.15.
+ """
+ if global_plan is not None and len(global_plan) != 0:
+ curr_dist_to_path, idx = self.get_min_dist2global_kdtree(
+ global_plan, robot_pose
+ )
+
+ if self.last_dist_to_path is not None:
+ if curr_dist_to_path < self.last_dist_to_path:
+ w = reward_factor
+ else:
+ w = penalty_factor
+
+ self.curr_reward += w * (
+ self.last_dist_to_path - curr_dist_to_path
+ )
+ self.last_dist_to_path = curr_dist_to_path
+
+ def _reward_following_global_plan(
+ self,
+ global_plan: np.array,
+ robot_pose: Pose2D,
+ action: np.array = None,
+ dist_to_path: float = 0.5,
+ ):
+ """Reward for travelling along the global plan.
+
+ Args:
+ global_plan (np.array): Array containing 2D poses.
+ robot_pose (Pose2D): Robot position.
+ action (np.array, optional): action (np.ndarray, optional): Array of shape (2,). First entry, linear velocity. \
+ Second entry, angular velocity. Defaults to None.
+ dist_to_path (float, optional): Minimum distance to the global path. Defaults to 0.5.
+ """
+ if (
+ global_plan is not None
+ and len(global_plan) != 0
+ and action is not None
+ ):
+ curr_dist_to_path, idx = self.get_min_dist2global_kdtree(
+ global_plan, robot_pose
+ )
+
+ if curr_dist_to_path <= dist_to_path:
+ self.curr_reward += 0.1 * action[0]
+
+ def get_min_dist2global_kdtree(
+ self, global_plan: np.array, robot_pose: Pose2D
+ ) -> Tuple[float, int]:
+ """Calculates minimal distance to global plan using kd-tree-search.
+
+ Args:
+ global_plan (np.array): Array containing 2D poses.
+ robot_pose (Pose2D): Robot position.
+
+ Returns:
+ Tuple[float, int]: Distance to the closes pose and index of the closes pose.
+ """
+ if self.kdtree is None:
+ self.kdtree = scipy.spatial.cKDTree(global_plan)
+
+ dist, index = self.kdtree.query([robot_pose.x, robot_pose.y])
+ return dist, index
+
+ def _reward_abrupt_direction_change(self, action: np.array = None):
+ """Applies a penalty when an abrupt change of direction occured.
+
+ Args:
+ action (np.array, optional): Array of shape (2,). First entry, linear velocity. \
+ Second entry, angular velocity. Defaults to None.
+ """
+ if self.last_action is not None:
+ curr_ang_vel = action[1]
+ last_ang_vel = self.last_action[1]
+
+ vel_diff = abs(curr_ang_vel - last_ang_vel)
+ self.curr_reward -= (vel_diff ** 4) / 2500
+ self.last_action = action
diff --git a/arena_marl/marl_agent/utils/sb3agent_format_check.py b/arena_marl/marl_agent/utils/sb3agent_format_check.py
new file mode 100644
index 000000000..e4d688774
--- /dev/null
+++ b/arena_marl/marl_agent/utils/sb3agent_format_check.py
@@ -0,0 +1,39 @@
+from typing import Type
+
+from stable_baselines3.common.torch_layers import BaseFeaturesExtractor
+from torch.nn.modules.module import Module
+
+from arena_navigation.arena_local_planner.learning_based.arena_local_planner_drl.rl_agent.model.base_agent import BaseAgent, PolicyType
+
+
+def check_format(cls: Type[BaseAgent]):
+ assert isinstance(cls.type, PolicyType), "Type has to be of type 'PolicyType'!"
+
+ if cls.features_extractor_class:
+ assert issubclass(
+ cls.features_extractor_class, BaseFeaturesExtractor
+ ), "Feature extractors have to derive from 'BaseFeaturesExtractor'!"
+
+ if cls.features_extractor_kwargs:
+ assert (
+ type(cls.features_extractor_kwargs) is dict
+ ), "Features extractor kwargs have to be of type 'dict'!"
+
+ if cls.net_arch:
+ assert (
+ type(cls.net_arch) is list
+ ), "Network architecture kwargs have to be of type 'list'!"
+ for entry in cls.net_arch:
+ assert (
+ type(entry) is dict or type(entry) is int
+ ), "Network architecture entries have to be of either type 'list' or 'dict'!"
+ if type(entry) is dict:
+ assert "pi" in entry or "vf" in entry, (
+ "net_arch dictionaries have to contain either 'pi' or 'vf'"
+ "for the respective network head!"
+ )
+
+ if cls.activation_fn:
+ assert issubclass(
+ cls.activation_fn, Module
+ ), "Activation functions have to be taken from torch!"
diff --git a/arena_marl/marl_agent/utils/supersuit_utils.py b/arena_marl/marl_agent/utils/supersuit_utils.py
new file mode 100644
index 000000000..e2290ccbf
--- /dev/null
+++ b/arena_marl/marl_agent/utils/supersuit_utils.py
@@ -0,0 +1,99 @@
+from functools import partial
+from typing import Callable
+
+import numpy as np
+import rospy
+
+# from stable_baselines3.common.vec_env import VecNormalize
+from supersuit.vector import ConcatVecEnv, MarkovVectorEnv
+
+
+class MarkovVectorEnv_patched(MarkovVectorEnv):
+ """Patched environment wrapper which creates the correct API for vector environments. Dones for dead agents are returned as True instead as False."""
+
+ def step(self, actions):
+ agent_set = set(self.par_env.agents)
+ act_dict = {
+ agent: actions[i]
+ for i, agent in enumerate(self.par_env.possible_agents)
+ if agent in agent_set
+ }
+ observations, rewards, dones, infos = self.par_env.step(act_dict)
+
+ # adds last observation to info where user can get it
+ if all(dones.values()):
+ for agent, obs in observations.items():
+ infos[agent]["terminal_observation"] = obs
+
+ rews = np.array(
+ [rewards.get(agent, 0) for agent in self.par_env.possible_agents],
+ dtype=np.float32,
+ )
+ # we changed the default value to true instead of false
+ dns = np.array(
+ [dones.get(agent, True) for agent in self.par_env.possible_agents],
+ dtype=np.uint8,
+ )
+ infs = [infos.get(agent, {}) for agent in self.par_env.possible_agents]
+
+ if all(dones.values()):
+ observations = self.reset()
+ else:
+ observations = self.concat_obs(observations)
+ assert (
+ self.black_death or self.par_env.agents == self.par_env.possible_agents
+ ), "MarkovVectorEnv does not support environments with varying numbers of active agents unless black_death is set to True"
+ return observations, rews, dns, infs
+
+
+def vec_env_create(
+ env_fn: Callable,
+ agent_list_fn: Callable,
+ num_robots: int,
+ task_mode: str,
+ num_cpus: int,
+ num_vec_envs: int,
+ PATHS: dict,
+ agent_list_kwargs: dict,
+ max_num_moves_per_eps: int,
+):
+ """Function which vectorizes a given environment function in multiple parallel environments.
+
+ Args:
+ env_fn (Callable): Function that initializes an environment with wrappers
+ agent_list_fn (Callable): Object containing the program arguments
+ robot_model (str): Name of the robot model
+ num_robots (int): Number of robots in the environment
+ task_mode (str): Navigation task mode
+ num_cpus (int): Maximal number of CPUs to use (Currently only process is used anyhow)
+ num_vec_envs (int): Number of parallel environments to spawn
+ PATHS (dict): Dictionary which holds hyperparameters for the experiment
+
+ Returns:
+ SB3VecEnvWrapper: Vectorized environments following the SB3 VecEnv API. Each each robot in an environment \
+ poses as an environment in the vector.
+ """
+ import supersuit.vector.sb3_vector_wrapper as sb3vw
+
+ env_list_fns = [
+ partial(
+ env_fn,
+ ns=f"sim_{i}",
+ num_agents=num_robots,
+ task_mode=task_mode,
+ agent_list_fn=agent_list_fn,
+ PATHS=PATHS,
+ agent_list_kwargs=agent_list_kwargs,
+ max_num_moves_per_eps=max_num_moves_per_eps,
+ )
+ for i in range(1, num_vec_envs + 1)
+ ]
+ env = env_list_fns[0]()
+ action_space = env.action_space
+ observation_space = env.observation_space
+ metadata = env.metadata
+
+ num_cpus = min(num_cpus, num_vec_envs)
+ rospy.init_node("train_env", disable_signals=False, anonymous=True)
+ vec_env = ConcatVecEnv(env_list_fns, observation_space, action_space)
+ return sb3vw.SB3VecEnvWrapper(vec_env)
diff --git a/arena_marl/marl_agent/utils/supersuit_utils_experimental.py b/arena_marl/marl_agent/utils/supersuit_utils_experimental.py
new file mode 100644
index 000000000..1ddc3302c
--- /dev/null
+++ b/arena_marl/marl_agent/utils/supersuit_utils_experimental.py
@@ -0,0 +1,81 @@
+class SharedArray_patched(SharedArray):
+ def __setstate__(self, state):
+ (self.shared_arr, self.dtype, self.shape) = state
+ self._set_np_arr()
+
+class ProcConcatVec_patched(ProcConcatVec):
+ def __init__(self, vec_env_constrs, observation_space, action_space, tot_num_envs, metadata):
+ self.observation_space = observation_space
+ self.action_space = action_space
+ self.num_envs = num_envs = tot_num_envs
+ self.metadata = metadata
+
+ self.shared_obs = SharedArray_patched((num_envs,) + self.observation_space.shape, dtype=self.observation_space.dtype)
+ act_space_wrap = SpaceWrapper(self.action_space)
+ self.shared_act = SharedArray_patched((num_envs,) + act_space_wrap.shape, dtype=act_space_wrap.dtype)
+ self.shared_rews = SharedArray_patched((num_envs,), dtype=np.float32)
+ self.shared_dones = SharedArray_patched((num_envs,), dtype=np.uint8)
+
+ pipes = []
+ procs = []
+ for constr in vec_env_constrs:
+ inpt, outpt = mp.Pipe()
+ constr = gym.vector.async_vector_env.CloudpickleWrapper(constr)
+ proc = mp.Process(
+ target=async_loop, args=(constr, inpt, outpt, self.shared_obs, self.shared_act, self.shared_rews, self.shared_dones)
+ )
+ proc.start()
+ outpt.close()
+ pipes.append(inpt)
+ procs.append(proc)
+
+ self.pipes = pipes
+ self.procs = procs
+
+ num_envs = 0
+ env_nums = self._receive_info()
+ idx_starts = []
+ for pipe, cnum_env in zip(self.pipes, env_nums):
+ cur_env_idx = num_envs
+ num_envs += cnum_env
+ pipe.send(cur_env_idx)
+ idx_starts.append(cur_env_idx)
+
+ assert num_envs == tot_num_envs, f"num_envs={num_envs} und tot_num_envs={tot_num_envs}"
+ self.idx_starts = idx_starts
+
+class call_wrap_patched:
+ def __init__(self, fn, data, i):
+ self.fn = fn
+ self.data = data
+ self.i = i
+
+ def __call__(self, *args):
+ rospy.init_node(f"train_env_{self.i}", disable_signals=False, anonymous=True)
+ return self.fn(self.data)
+
+def MakeCPUAsyncConstructor_patched(max_num_cpus, metadata):
+ if True:
+ rospy.init_node("train_env", disable_signals=False, anonymous=True)
+ return ConcatVecEnv
+ else:
+
+ def constructor(env_fn_list, obs_space, act_space):
+ num_fns = len(env_fn_list)
+ envs_per_cpu = (num_fns + max_num_cpus - 1) // max_num_cpus
+ alloced_num_cpus = (num_fns + envs_per_cpu - 1) // envs_per_cpu
+
+ env_cpu_div = []
+ num_envs_alloced = 0
+ while num_envs_alloced < num_fns:
+ start_idx = num_envs_alloced
+ end_idx = min(num_fns, start_idx + envs_per_cpu)
+ env_cpu_div.append(env_fn_list[start_idx:end_idx])
+ num_envs_alloced = end_idx
+
+ assert alloced_num_cpus == len(env_cpu_div)
+
+ cat_env_fns = [call_wrap_patched(ConcatVecEnv, env_fns, i) for i, env_fns in enumerate(env_cpu_div)]
+ return ProcConcatVec_patched(cat_env_fns, obs_space, act_space, num_fns, metadata)
+
+ return constructor
\ No newline at end of file
diff --git a/arena_marl/marl_agent/utils/utils.py b/arena_marl/marl_agent/utils/utils.py
new file mode 100644
index 000000000..0741a5cea
--- /dev/null
+++ b/arena_marl/marl_agent/utils/utils.py
@@ -0,0 +1,101 @@
+import sys
+import os, sys, rospy, time
+
+from typing import Callable, List
+
+import rospy
+import rospkg
+
+from rl_agent.training_agent_wrapper import TrainingDRLAgent
+from rl_agent.base_agent_wrapper import BaseDRLAgent
+
+
+def get_hyperparameter_file(filename: str) -> str:
+ """Function which returns the path to the hyperparameter file.
+
+ Args:
+ filename (str): Name of the hyperparameter file.
+
+ Returns:
+ str: Path to the hyperparameter file.
+ """
+ return os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "hyperparameters",
+ filename,
+ )
+
+
+def instantiate_train_drl_agents(
+ num_robots: int = 1,
+ existing_robots: int = 0,
+ robot_model: str = "burger",
+ ns: str = None,
+ robot_name_prefix: str = "robot",
+ hyperparameter_path: str = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "hyperparameters",
+ "default.json",
+ ),
+) -> List[TrainingDRLAgent]:
+ """Function which generates a list agents which handle the ROS connection.
+
+ Args:
+ num_robots (int, optional): Number of robots in the environment. Defaults to 1.
+ robot_model (str, optional): Model of the robot. Defaults to "burger".
+ ns (str, optional): Name of the namespace (used for ROS topics). Defaults to None.
+ robot_name_prefix (str, optional): Name with which to prefix robots in the ROS environment. Defaults to "robot".
+ hyperparameter_path (str, optional): Path where to load hyperparameters from. Defaults to DEFAULT_HYPERPARAMETER.
+ action_space_path (str, optional): Path where to load action spaces from. Defaults to DEFAULT_ACTION_SPACE.
+
+ Returns:
+ List[TrainingDRLAgent]: List containing _num\_robots_ agent classes.
+ """
+ return [
+ TrainingDRLAgent(
+ ns=ns,
+ robot_model=robot_model,
+ robot_ns=robot_name_prefix + str(i + 1),
+ hyperparameter_path=hyperparameter_path,
+ )
+ for i in range(existing_robots, existing_robots + num_robots)
+ ]
+
+
+def instantiate_deploy_drl_agents(
+ num_robots: int = 1,
+ existing_robots: int = 0,
+ robot_model: str = "burger",
+ ns: str = None,
+ robot_name_prefix: str = "robot",
+ hyperparameter_path: str = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "hyperparameters",
+ "default.json",
+ ),
+) -> List[BaseDRLAgent]:
+ """Function which generates a list agents which handle the ROS connection.
+
+ Args:
+ num_robots (int, optional): Number of robots in the environment. Defaults to 1.
+ robot_model (str, optional): Model of the robot. Defaults to "burger".
+ ns (str, optional): Name of the namespace (used for ROS topics). Defaults to None.
+ robot_name_prefix (str, optional): Name with which to prefix robots in the ROS environment. Defaults to "robot".
+ hyperparameter_path (str, optional): Path where to load hyperparameters from. Defaults to DEFAULT_HYPERPARAMETER.
+ action_space_path (str, optional): Path where to load action spaces from. Defaults to DEFAULT_ACTION_SPACE.
+
+ Returns:
+ List[TrainingDRLAgent]: List containing _num\_robots_ agent classes.
+ """
+ return [
+ BaseDRLAgent(
+ ns=ns,
+ robot_model=robot_model,
+ robot_ns=robot_name_prefix + str(i + 1),
+ hyperparameter_path=hyperparameter_path,
+ )
+ for i in range(existing_robots, existing_robots + num_robots)
+ ]
diff --git a/arena_marl/marl_tools/__init__.py b/arena_marl/marl_tools/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/arena_marl/marl_tools/argsparser.py b/arena_marl/marl_tools/argsparser.py
new file mode 100644
index 000000000..6d7b3bbf2
--- /dev/null
+++ b/arena_marl/marl_tools/argsparser.py
@@ -0,0 +1,211 @@
+import argparse
+import os
+import numpy as np
+
+from rl_agent.model.agent_factory import AgentFactory
+from marl_tools.custom_mlp_utils import get_net_arch
+
+
+def training_args(parser):
+ """program arguments training script"""
+ parser.add_argument(
+ "--n_envs", type=int, default=1, help="number of parallel environments"
+ )
+ parser.add_argument(
+ "--no-gpu", action="store_true", help="disables gpu for training"
+ )
+ parser.add_argument(
+ "--debug",
+ action="store_true",
+ help="disables multiprocessing in order to debug",
+ )
+ group = parser.add_mutually_exclusive_group(required=True)
+ group.add_argument(
+ "--agent",
+ type=str,
+ choices=AgentFactory.registry.keys(),
+ help="predefined agent to train",
+ )
+ group.add_argument(
+ "--custom-mlp",
+ action="store_true",
+ help="enables training with custom multilayer perceptron",
+ )
+ group.add_argument(
+ "--load",
+ type=str,
+ metavar="[agent name]",
+ help="agent to be loaded for training",
+ )
+ parser.add_argument(
+ "--config",
+ type=str,
+ metavar="[config name]",
+ default="default",
+ help="name of the json file containing" "the hyperparameters",
+ )
+ parser.add_argument(
+ "--n", type=int, help="timesteps in total to be generated for training"
+ )
+ parser.add_argument(
+ "-log",
+ "--eval_log",
+ action="store_true",
+ help="enables storage of evaluation data",
+ )
+ parser.add_argument("--tb", action="store_true", help="enables tensorboard logging")
+
+
+def marl_training_args(parser):
+ parser.add_argument("--robots", type=int, default=1, help="number of robots")
+
+
+def run_agent_args(parser):
+ parser.add_argument(
+ "--no-gpu", action="store_true", help="disables gpu for training"
+ )
+ parser.add_argument(
+ "--load",
+ type=str,
+ metavar="[agent name]",
+ help="agent to be loaded for training",
+ )
+ parser.add_argument(
+ "--log",
+ action="store_true",
+ help="store log file with episode information",
+ )
+ parser.add_argument(
+ "-s",
+ "--scenario",
+ type=str,
+ metavar="[scenario name]",
+ default="scenario1",
+ help="name of scenario file for deployment",
+ )
+ parser.add_argument(
+ "--num_eps",
+ type=int,
+ metavar="[num episodes]",
+ default=100,
+ help="number of episodes the agent/s get/s challenged",
+ )
+ parser.add_argument(
+ "--max_steps",
+ type=int,
+ metavar="[max steps per episode]",
+ default=np.inf,
+ help="max amount of actions per episode before the simulation is resetted",
+ )
+ parser.add_argument("-v", "--verbose", choices=["0", "1"], default="1")
+
+
+def custom_mlp_args(parser):
+ """arguments for the custom mlp mode"""
+ custom_mlp_args = parser.add_argument_group(
+ "custom mlp args", "architecture arguments for the custom mlp"
+ )
+
+ custom_mlp_args.add_argument(
+ "--body",
+ type=str,
+ default="",
+ metavar="{num}-{num}-...",
+ help="architecture of the shared latent network, "
+ "each number representing the number of neurons per layer",
+ )
+ custom_mlp_args.add_argument(
+ "--pi",
+ type=str,
+ default="",
+ metavar="{num}-{num}-...",
+ help="architecture of the latent policy network, "
+ "each number representing the number of neurons per layer",
+ )
+ custom_mlp_args.add_argument(
+ "--vf",
+ type=str,
+ default="",
+ metavar="{num}-{num}-...",
+ help="architecture of the latent value network, "
+ "each number representing the number of neurons per layer",
+ )
+ custom_mlp_args.add_argument(
+ "--act_fn",
+ type=str,
+ default="relu",
+ choices=["relu", "sigmoid", "tanh"],
+ help="activation function to be applied after each hidden layer",
+ )
+
+
+def process_training_args(parsed_args):
+ """argument check function"""
+ if parsed_args.no_gpu:
+ os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
+ if parsed_args.custom_mlp:
+ setattr(parsed_args, "net_arch", get_net_arch(parsed_args))
+ else:
+ if parsed_args.body != "" or parsed_args.pi != "" or parsed_args.vf != "":
+ print("[custom mlp] arguments will be ignored..")
+ delattr(parsed_args, "body")
+ delattr(parsed_args, "pi")
+ delattr(parsed_args, "vf")
+ delattr(parsed_args, "act_fn")
+
+
+def process_run_agent_args(parsed_args):
+ if parsed_args.no_gpu:
+ os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
+
+
+def parse_training_args(args=None, ignore_unknown=False):
+ """parser for training script"""
+ arg_populate_funcs = [training_args, custom_mlp_args]
+ arg_check_funcs = [process_training_args]
+
+ return parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown)
+
+
+def parse_marl_training_args(args=None, ignore_unknown=False):
+ """parser for training script"""
+ arg_populate_funcs = [training_args, custom_mlp_args, marl_training_args]
+ arg_check_funcs = [process_training_args]
+
+ return parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown)
+
+
+def parse_run_agent_args(args=None, ignore_unknown=False):
+ """parser for training script"""
+ arg_populate_funcs = [run_agent_args]
+ arg_check_funcs = [process_run_agent_args]
+
+ return parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown)
+
+
+def parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown):
+ """generic arg parsing function"""
+ parser = argparse.ArgumentParser()
+
+ for func in arg_populate_funcs:
+ func(parser)
+
+ if ignore_unknown:
+ parsed_args, unknown_args = parser.parse_known_args(args=args)
+ else:
+ parsed_args = parser.parse_args(args=args)
+ unknown_args = []
+
+ for func in arg_check_funcs:
+ func(parsed_args)
+
+ print_args(parsed_args)
+ return parsed_args, unknown_args
+
+
+def print_args(args):
+ print("\n-------------------------------")
+ print(" ARGUMENTS ")
+ for k in args.__dict__:
+ print("- {} : {}".format(k, args.__dict__[k]))
+ print("--------------------------------\n")
diff --git a/arena_marl/marl_tools/custom_mlp_utils.py b/arena_marl/marl_tools/custom_mlp_utils.py
new file mode 100644
index 000000000..4ae5d200e
--- /dev/null
+++ b/arena_marl/marl_tools/custom_mlp_utils.py
@@ -0,0 +1,53 @@
+import torch as th
+import argparse
+
+
+def get_net_arch(args: argparse.Namespace):
+ """function to convert input args into valid syntax for the PPO"""
+ body, policy, value = None, None, None
+
+ if args.body != "":
+ body = parse_string(args.body)
+ if args.pi != "":
+ policy = parse_string(args.pi)
+ if args.vf != "":
+ value = parse_string(args.vf)
+
+ if body is None:
+ body = []
+ vf_pi = {}
+ if value is not None:
+ vf_pi["vf"] = value
+ if policy is not None:
+ vf_pi["pi"] = policy
+
+ return body + [vf_pi]
+
+
+def parse_string(string: str):
+ """function to convert a string into a int list
+
+ Example:
+
+ Input: parse_string("64-64")
+ Output: [64, 64]
+
+ """
+ string_arr = string.split("-")
+ int_list = []
+ for string in string_arr:
+ try:
+ int_list.append(int(string))
+ except:
+ raise Exception("Invalid argument format on: " + string)
+ return int_list
+
+
+def get_act_fn(act_fn_string: str):
+ """function to convert str into pytorch activation function class"""
+ if act_fn_string == "relu":
+ return th.nn.ReLU
+ elif act_fn_string == "sigmoid":
+ return th.nn.Sigmoid
+ elif act_fn_string == "tanh":
+ return th.nn.Tanh
diff --git a/arena_marl/marl_tools/staged_train_callback.py b/arena_marl/marl_tools/staged_train_callback.py
new file mode 100644
index 000000000..b9c5c5147
--- /dev/null
+++ b/arena_marl/marl_tools/staged_train_callback.py
@@ -0,0 +1,140 @@
+import warnings
+import rospy
+import numpy as np
+import time
+
+from typing import List
+from std_msgs.msg import Bool
+from stable_baselines3.common.callbacks import BaseCallback, EvalCallback, MarlEvalCallback
+from task_generator.task_generator.tasks import StagedRandomTask
+
+
+class InitiateNewTrainStage(BaseCallback):
+ """
+ Introduces new training stage when threshhold reached.
+ It must be used with "EvalCallback".
+
+ :param treshhold_type (str): checks threshhold for either percentage of successful episodes (succ) or mean reward (rew)
+ :param rew_threshold (int): mean reward threshold to trigger new stage
+ :param succ_rate_threshold (float): threshold percentage of succesful episodes to trigger new stage
+ :param task_mode (str): training task mode, if not 'staged' callback won't be called
+ :param verbose:
+ """
+
+ def __init__(
+ self,
+ n_envs: int = 1,
+ treshhold_type: str = "succ",
+ upper_threshold: float = 0,
+ lower_threshold: float = 0,
+ task_mode: str = "staged",
+ verbose=0,
+ ):
+
+ super(InitiateNewTrainStage, self).__init__(verbose=verbose)
+ self.n_envs = n_envs
+ self.threshhold_type = treshhold_type
+
+ assert self.threshhold_type in {
+ "rew",
+ "succ",
+ }, "given theshhold type neither 'rew' or 'succ'"
+
+ # default values
+ if self.threshhold_type == "rew" and upper_threshold == 0:
+ self.upper_threshold = 13
+ self.lower_threshold = 7
+ elif self.threshhold_type == "succ" and upper_threshold == 0:
+ self.upper_threshold = 0.85
+ self.lower_threshold = 0.6
+ else:
+ self.upper_threshold = upper_threshold
+ self.lower_threshold = lower_threshold
+
+ assert (
+ self.upper_threshold > self.lower_threshold
+ ), "upper threshold has to be bigger than lower threshold"
+ assert (
+ self.upper_threshold >= 0 and self.lower_threshold >= 0
+ ), "upper/lower threshold have to be positive numbers"
+ if self.threshhold_type == "succ":
+ assert (
+ self.upper_threshold <= 1 and self.lower_threshold >= 0
+ ), "succ thresholds have to be between [1.0, 0.0]"
+
+ self.verbose = verbose
+ self.activated = bool(task_mode == "staged")
+
+ if self.activated:
+ rospy.set_param("/last_stage_reached", False)
+ self._instantiate_publishers()
+
+ self._trigger = Bool()
+ self._trigger.data = True
+
+ def _instantiate_publishers(self):
+ self._publishers_next = []
+ self._publishers_previous = []
+
+ self._publishers_next.append(
+ rospy.Publisher(f"/eval_sim/next_stage", Bool, queue_size=1)
+ )
+ self._publishers_previous.append(
+ rospy.Publisher(f"/eval_sim/previous_stage", Bool, queue_size=1)
+ )
+
+ for env_num in range(self.n_envs):
+ self._publishers_next.append(
+ rospy.Publisher(f"/sim_{env_num+1}/next_stage", Bool, queue_size=1)
+ )
+ self._publishers_previous.append(
+ rospy.Publisher(f"/sim_{env_num+1}/previous_stage", Bool, queue_size=1)
+ )
+
+ def _on_step(self, EvalObject: EvalCallback) -> bool:
+ assert isinstance(
+ EvalObject, EvalCallback
+ ) or isinstance(
+ EvalObject, MarlEvalCallback
+ ), f"InitiateNewTrainStage must be called within EvalCallback"
+
+ if self.activated:
+ if EvalObject.n_eval_episodes < 20:
+ warnings.warn(
+ "Only %d evaluation episodes considered for threshold monitoring,"
+ "results might not represent agent performance well"
+ % EvalObject.n_eval_episodes
+ )
+
+ if (
+ self.threshhold_type == "rew"
+ and EvalObject.best_mean_reward <= self.lower_threshold
+ ) or (
+ self.threshhold_type == "succ"
+ and EvalObject.last_success_rate <= self.lower_threshold
+ ):
+ for i, pub in enumerate(self._publishers_previous):
+ pub.publish(self._trigger)
+ if i == 0:
+ self.log_curr_stage(EvalObject.logger)
+
+ if (
+ self.threshhold_type == "rew"
+ and EvalObject.best_mean_reward >= self.upper_threshold
+ ) or (
+ self.threshhold_type == "succ"
+ and EvalObject.last_success_rate >= self.upper_threshold
+ ):
+ if not rospy.get_param("/last_stage_reached"):
+ EvalObject.best_mean_reward = -np.inf
+ EvalObject.last_success_rate = -np.inf
+
+ for i, pub in enumerate(self._publishers_next):
+ pub.publish(self._trigger)
+ if i == 0:
+ self.log_curr_stage(EvalObject.logger)
+
+ def log_curr_stage(self, logger):
+ time.sleep(1)
+ curr_stage = rospy.get_param("/curr_stage", -1)
+ logger.record("train_stage/stage_idx", curr_stage)
diff --git a/arena_marl/marl_tools/train_agent_utils.py b/arena_marl/marl_tools/train_agent_utils.py
new file mode 100644
index 000000000..a0462f77c
--- /dev/null
+++ b/arena_marl/marl_tools/train_agent_utils.py
@@ -0,0 +1,565 @@
+from typing import Union, Type
+
+import argparse
+from datetime import datetime as dt
+import gym
+import json
+import os
+
+import yaml
+import rosnode
+import rospkg
+import time
+import warnings
+
+from stable_baselines3 import PPO
+from stable_baselines3.common.monitor import Monitor
+from stable_baselines3.common.policies import ActorCriticPolicy
+from stable_baselines3.common.utils import set_random_seed
+
+
+from rl_agent.envs.flatland_gym_env import (
+ FlatlandEnv,
+)
+from rl_agent.model.agent_factory import AgentFactory
+from rl_agent.model.base_agent import BaseAgent
+from marl_tools.custom_mlp_utils import get_act_fn
+import rospy
+
+"""
+Dict containing agent specific hyperparameter keys (for documentation and typing validation purposes)
+
+:key agent_name: Precise agent name (as generated by get_agent_name())
+:key robot: Robot name to load robot specific .yaml file containing settings
+:key batch_size: Batch size (n_envs * n_steps)
+:key gamma: Discount factor
+:key n_steps: The number of steps to run for each environment per update
+:key ent_coef: Entropy coefficient for the loss calculation
+:key learning_rate: The learning rate, it can be a function
+ of the current progress remaining (from 1 to 0)
+ (i.e. batch size is n_steps * n_env where n_env is number of environment copies running in parallel)
+:key vf_coef: Value function coefficient for the loss calculation
+:key max_grad_norm: The maximum value for the gradient clipping
+:key gae_lambda: Factor for trade-off of bias vs variance for Generalized Advantage Estimator
+:key m_batch_size: Minibatch size
+:key n_epochs: Number of epoch when optimizing the surrogate loss
+:key clip_range: Clipping parameter, it can be a function of the current progress
+ remaining (from 1 to 0).
+:key train_max_steps_per_episode: Max timesteps per training episode
+:key eval_max_steps_per_episode: Max timesteps per evaluation episode
+:key goal_radius: Radius of the goal
+:key reward_fnc: Number of the reward function (defined in ../rl_agent/utils/reward.py)
+:key discrete_action_space: If robot uses discrete action space
+:key normalize: If observations are normalized before fed to the network
+:key task_mode: Mode tasks will be generated in (custom, random, staged).
+:key curr_stage: In case of staged training which stage to start with.
+:param n_timesteps: The number of timesteps trained on in total.
+"""
+hyperparams = {
+ key: None
+ for key in [
+ "agent_name",
+ "robot",
+ "actions_in_observationspace",
+ "batch_size",
+ "gamma",
+ "n_steps",
+ "ent_coef",
+ "learning_rate",
+ "vf_coef",
+ "max_grad_norm",
+ "gae_lambda",
+ "m_batch_size",
+ "n_epochs",
+ "clip_range",
+ "reward_fnc",
+ "discrete_action_space",
+ "normalize",
+ "task_mode",
+ "curr_stage",
+ "train_max_steps_per_episode",
+ "eval_max_steps_per_episode",
+ "goal_radius",
+ ]
+}
+
+
+def load_config(config_name: str) -> dict:
+ """
+ Load config parameters from config file
+ """
+ config_location = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"), "configs", config_name
+ )
+ with open(config_location, "r", encoding="utf-8") as target:
+ config = yaml.load(target, Loader=yaml.FullLoader)
+
+ return config
+
+
+def initialize_hyperparameters(PATHS: dict, config: dict, n_envs: int) -> dict:
+ """
+ Write hyperparameters to json file in case agent is new otherwise load existing hyperparameters
+
+ :param PATHS: dictionary containing model specific paths
+ :param load_target: unique agent name (when calling --load)
+ :param config_name: name of the hyperparameter file in /configs/hyperparameters
+ :param n_envs: number of envs
+ """
+ # when building new agent
+ if config["resume"] is None:
+ hyperparams = load_hyperparameters_json(PATHS=PATHS, from_scratch=True)
+ hyperparams["agent_name"] = PATHS["model"].split("/")[-1]
+ else:
+ hyperparams = load_hyperparameters_json(PATHS=PATHS)
+
+ # dynamically adapt n_steps according to batch size and n envs
+ # then update .json
+ check_batch_size(n_envs, hyperparams["batch_size"], hyperparams["m_batch_size"])
+ hyperparams["n_steps"] = int(hyperparams["batch_size"] / n_envs)
+ if not rospy.get_param("debug_mode"):
+ write_hyperparameters_json(hyperparams, PATHS)
+ print_hyperparameters(hyperparams)
+ return hyperparams
+
+
+def write_hyperparameters_json(hyperparams: dict, PATHS: dict) -> None:
+ """
+ Write hyperparameters.json to agent directory
+
+ :param hyperparams: dict containing model specific hyperparameters
+ :param PATHS: dictionary containing model specific paths
+ """
+ doc_location = os.path.join(PATHS.get("model"), "hyperparameters.json")
+
+ with open(doc_location, "w", encoding="utf-8") as target:
+ json.dump(hyperparams, target, ensure_ascii=False, indent=4)
+
+
+def load_hyperparameters_json(PATHS: dict, from_scratch: bool = False) -> dict:
+ """
+ Load hyperparameters from model directory when loading - when training from scratch
+ load from ../configs/hyperparameters
+
+ :param PATHS: dictionary containing model specific paths
+ :param from_scatch: if training from scratch
+ :param config_name: file name of json file when training from scratch
+ """
+ if from_scratch:
+ doc_location = os.path.join(PATHS.get("hyperparams"))
+ else:
+ doc_location = os.path.join(PATHS.get("model"), "hyperparameters.json")
+
+ if os.path.isfile(doc_location):
+ with open(doc_location, "r") as file:
+ hyperparams = json.load(file)
+ check_hyperparam_format(loaded_hyperparams=hyperparams, PATHS=PATHS)
+ return hyperparams
+ else:
+ if from_scratch:
+ raise FileNotFoundError("Found no '%s'" % PATHS.get("hyperparams"))
+ else:
+ raise FileNotFoundError(
+ "Found no 'hyperparameters.json' in %s" % PATHS.get("model")
+ )
+
+
+def update_total_timesteps_json(timesteps: int, PATHS: dict) -> None:
+ """
+ Update total number of timesteps in json file
+
+ :param hyperparams_obj(object, agent_hyperparams): object containing containing model specific hyperparameters
+ :param PATHS: dictionary containing model specific paths
+ """
+ doc_location = os.path.join(PATHS.get("model"), "hyperparameters.json")
+ hyperparams = load_hyperparameters_json(PATHS=PATHS)
+
+ try:
+ curr_timesteps = int(hyperparams["n_timesteps"]) + timesteps
+ hyperparams["n_timesteps"] = curr_timesteps
+ except Exception:
+ raise Warning(
+ "Parameter 'total_timesteps' not found or not of type Integer in 'hyperparameter.json'!"
+ )
+ else:
+ with open(doc_location, "w", encoding="utf-8") as target:
+ json.dump(hyperparams, target, ensure_ascii=False, indent=4)
+
+
+def print_hyperparameters(hyperparams: dict) -> None:
+ print("\n--------------------------------")
+ print(" HYPERPARAMETERS \n")
+ for param, param_val in hyperparams.items():
+ print("{:30s}{:<10s}".format((param + ":"), str(param_val)))
+ print("--------------------------------\n\n")
+
+
+def check_hyperparam_format(loaded_hyperparams: dict, PATHS: dict) -> None:
+ if set(hyperparams.keys()) != set(loaded_hyperparams.keys()):
+ missing_keys = set(hyperparams.keys()).difference(
+ set(loaded_hyperparams.keys())
+ )
+ redundant_keys = set(loaded_hyperparams.keys()).difference(
+ set(hyperparams.keys())
+ )
+ raise AssertionError(
+ f"unmatching keys, following keys missing: {missing_keys} \n"
+ f"following keys unused: {redundant_keys}"
+ )
+ if not isinstance(loaded_hyperparams["discrete_action_space"], bool):
+ raise TypeError("Parameter 'discrete_action_space' not of type bool")
+ if loaded_hyperparams["task_mode"] not in ["custom", "random", "staged"]:
+ raise TypeError("Parameter 'task_mode' has unknown value")
+
+
+def update_hyperparam_model(
+ model: PPO, PATHS: dict, params: dict, n_envs: int = 1
+) -> None:
+ """
+ Updates parameter of loaded PPO agent when it was manually changed in the configs yaml.
+
+ :param model(object, PPO): loaded PPO agent
+ :param PATHS: program relevant paths
+ :param params: dictionary containing loaded hyperparams
+ :param n_envs: number of parallel environments
+ """
+ if model.batch_size != params["batch_size"]:
+ model.batch_size = params["batch_size"]
+ if model.gamma != params["gamma"]:
+ model.gamma = params["gamma"]
+ if model.n_steps != params["n_steps"]:
+ model.n_steps = params["n_steps"]
+ if model.ent_coef != params["ent_coef"]:
+ model.ent_coef = params["ent_coef"]
+ if model.learning_rate != params["learning_rate"]:
+ model.learning_rate = params["learning_rate"]
+ if model.vf_coef != params["vf_coef"]:
+ model.vf_coef = params["vf_coef"]
+ if model.max_grad_norm != params["max_grad_norm"]:
+ model.max_grad_norm = params["max_grad_norm"]
+ if model.gae_lambda != params["gae_lambda"]:
+ model.gae_lambda = params["gae_lambda"]
+ if model.n_epochs != params["n_epochs"]:
+ model.n_epochs = params["n_epochs"]
+ """
+ if model.clip_range != params['clip_range']:
+ model.clip_range = params['clip_range']
+ """
+ if model.n_envs != n_envs:
+ model.update_n_envs()
+ if model.rollout_buffer.buffer_size != params["n_steps"]:
+ model.rollout_buffer.buffer_size = params["n_steps"]
+ if model.tensorboard_log != PATHS["tb"]:
+ model.tensorboard_log = PATHS["tb"]
+
+
+def check_batch_size(n_envs: int, batch_size: int, mn_batch_size: int) -> None:
+ assert (
+ batch_size > mn_batch_size
+ ), f"Mini batch size {mn_batch_size} is bigger than batch size {batch_size}"
+
+ assert (
+ batch_size % mn_batch_size == 0
+ ), f"Batch size {batch_size} isn't divisible by mini batch size {mn_batch_size}"
+
+ assert (
+ batch_size % n_envs == 0
+ ), f"Batch size {batch_size} isn't divisible by n_envs {n_envs}"
+
+ assert (
+ batch_size % mn_batch_size == 0
+ ), f"Batch size {batch_size} isn't divisible by mini batch size {mn_batch_size}"
+
+
+def get_agent_name(args: argparse.Namespace) -> str:
+ """Function to get agent name to save to/load from file system
+
+ Example names:
+ "MLP_B_64-64_P_32-32_V_32-32_relu_2021_01_07__10_32"
+ "DRL_LOCAL_PLANNER_2021_01_08__7_14"
+
+ :param args (argparse.Namespace): Object containing the program arguments
+ """
+ START_TIME = dt.now().strftime("%Y_%m_%d__%H_%M")
+
+ if args.custom_mlp:
+ return (
+ "MLP_B_"
+ + args.body
+ + "_P_"
+ + args.pi
+ + "_V_"
+ + args.vf
+ + "_"
+ + args.act_fn
+ + "_"
+ + START_TIME
+ )
+ if args.load is None:
+ return args.agent + "_" + START_TIME
+ return args.load
+
+
+def get_MARL_agent_name_and_start_time() -> str:
+ """Function to get MARL agent parent dir, where seperate agents for different robots are saved"""
+ START_TIME = dt.now().strftime("%Y_%m_%d__%H_%M")
+
+ return "MARL_AGENTS_" + START_TIME, START_TIME
+
+
+def get_paths(
+ marl_dir: str,
+ robot: str,
+ agent_name: str,
+ config_params: dict,
+ curriculum: str,
+ eval_log: bool,
+ tb: bool,
+) -> dict:
+ """
+ Function to generate agent specific paths
+
+ :param agent_name: Precise agent name (as generated by get_agent_name())
+ :param args (argparse.Namespace): Object containing the program arguments
+ """
+ dir = rospkg.RosPack().get_path("arena_local_planner_drl")
+ PATHS = {
+ "model": os.path.join(dir, "agents", marl_dir, agent_name)
+ if config_params["resume"] is None
+ else config_params["resume"],
+ "tb": os.path.join(dir, "training_logs", "tensorboard", marl_dir, agent_name),
+ "eval": os.path.join(
+ dir, "training_logs", "train_eval_log", marl_dir, agent_name
+ ),
+ "robot_setting": os.path.join(
+ rospkg.RosPack().get_path("simulator_setup"),
+ "robot",
+ robot + ".model.yaml",
+ ),
+ "hyperparams": os.path.join(
+ dir, "configs", "hyperparameters", config_params["hyperparameter_file"]
+ ),
+ "robot_as": os.path.join(
+ dir, "configs", "action_spaces", "default_settings_" + robot + ".yaml"
+ ),
+ "curriculum": os.path.join(dir, "configs", "training_curriculums", curriculum),
+ }
+ rospy.get_param("/debug_mode")
+
+ # check for mode
+ if not rospy.get_param("/debug_mode"):
+ if config_params["resume"] is None:
+ os.makedirs(PATHS["model"])
+ elif not os.path.isfile(
+ os.path.join(PATHS["model"], agent_name + ".zip")
+ ) and not os.path.isfile(os.path.join(PATHS["model"], "best_model.zip")):
+ raise FileNotFoundError(
+ "Couldn't find model named %s.zip' or 'best_model.zip' in '%s'"
+ % (agent_name, PATHS["model"])
+ )
+
+ # evaluation log enabled
+ if eval_log:
+ if not os.path.exists(PATHS["eval"]):
+ os.makedirs(PATHS["eval"])
+ else:
+ PATHS["eval"] = None
+ # tensorboard log enabled
+ if tb:
+ if not os.path.exists(PATHS["tb"]):
+ os.makedirs(PATHS["tb"])
+ else:
+ PATHS["tb"] = None
+ else:
+ PATHS["eval"] = None
+ PATHS["tb"] = None
+
+ return PATHS
+
+
+def make_envs(
+ args: argparse.Namespace,
+ with_ns: bool,
+ rank: int,
+ params: dict,
+ seed: int = 0,
+ PATHS: dict = None,
+ train: bool = True,
+):
+ """
+ Utility function for multiprocessed env
+
+ :param with_ns: (bool) if the system was initialized with namespaces
+ :param rank: (int) index of the subprocess
+ :param params: (dict) hyperparameters of agent to be trained
+ :param seed: (int) the inital seed for RNG
+ :param PATHS: (dict) script relevant paths
+ :param train: (bool) to differentiate between train and eval env
+ :param args: (Namespace) program arguments
+ :return: (Callable)
+ """
+
+ def _init() -> Union[gym.Env, gym.Wrapper]:
+ train_ns = f"sim_{rank+1}" if with_ns else ""
+ eval_ns = f"eval_sim" if with_ns else ""
+
+ if train:
+ # train env
+ env = FlatlandEnv(
+ train_ns,
+ params["reward_fnc"],
+ params["discrete_action_space"],
+ goal_radius=params["goal_radius"],
+ max_steps_per_episode=params["train_max_steps_per_episode"],
+ debug=args.debug,
+ task_mode=params["task_mode"],
+ curr_stage=params["curr_stage"],
+ PATHS=PATHS,
+ )
+ else:
+ # eval env
+ env = Monitor(
+ FlatlandEnv(
+ eval_ns,
+ params["reward_fnc"],
+ params["discrete_action_space"],
+ goal_radius=params["goal_radius"],
+ max_steps_per_episode=params["eval_max_steps_per_episode"],
+ train_mode=False,
+ debug=args.debug,
+ task_mode=params["task_mode"],
+ curr_stage=params["curr_stage"],
+ PATHS=PATHS,
+ ),
+ PATHS.get("eval"),
+ info_keywords=("done_reason", "is_success"),
+ )
+ env.seed(seed + rank)
+ return env
+
+ set_random_seed(seed)
+ return _init
+
+
+def wait_for_nodes(
+ with_ns: bool, n_envs: int, timeout: int = 30, nodes_per_ns: int = 3
+) -> None:
+ """
+ Checks for timeout seconds if all nodes to corresponding namespace are online.
+
+ :param with_ns: (bool) if the system was initialized with namespaces
+ :param n_envs: (int) number of virtual environments
+ :param timeout: (int) seconds to wait for each ns
+ :param nodes_per_ns: (int) usual number of nodes per ns
+ """
+ if with_ns:
+ assert (
+ with_ns and n_envs >= 1
+ ), f"Illegal number of environments parsed: {n_envs}"
+ else:
+ assert (
+ not with_ns and n_envs == 1
+ ), f"Simulation setup isn't compatible with the given number of envs"
+
+ for i in range(n_envs):
+ for k in range(timeout):
+ ns = "sim_" + str(i + 1) if with_ns else ""
+ namespaces = rosnode.get_node_names(namespace=ns)
+
+ if len(namespaces) >= nodes_per_ns:
+ break
+
+ warnings.warn(
+ f"Check if all simulation parts of namespace '{ns}' are running properly"
+ )
+ warnings.warn(f"Trying to connect again..")
+ assert (
+ k < timeout - 1
+ ), f"Timeout while trying to connect to nodes of '{ns}'"
+
+ time.sleep(1)
+
+
+from stable_baselines3.common.vec_env import VecNormalize
+from stable_baselines3.common.vec_env.base_vec_env import VecEnv
+
+
+def load_vec_normalize(params: dict, PATHS: dict, env: VecEnv, eval_env: VecEnv):
+ if params["normalize"]:
+ load_path = os.path.join(PATHS["model"], "vec_normalize.pkl")
+ if os.path.isfile(load_path):
+ env = VecNormalize.load(load_path=load_path, venv=env)
+ eval_env = VecNormalize.load(load_path=load_path, venv=eval_env)
+ print("Succesfully loaded VecNormalize object from pickle file..")
+ else:
+ env = VecNormalize(
+ env,
+ training=True,
+ norm_obs=True,
+ norm_reward=False,
+ clip_reward=15,
+ )
+ eval_env = VecNormalize(
+ eval_env,
+ training=True,
+ norm_obs=True,
+ norm_reward=False,
+ clip_reward=15,
+ )
+ return env, eval_env
+
+
+def choose_agent_model(AGENT_NAME, PATHS, config, env, params, n_envs):
+ if config["resume"] is None:
+ agent: Union[
+ Type[BaseAgent], Type[ActorCriticPolicy]
+ ] = AgentFactory.instantiate(config["architecture_name"])
+ if isinstance(agent, BaseAgent):
+ model = PPO(
+ agent.type.value,
+ env,
+ policy_kwargs=agent.get_kwargs(),
+ gamma=params["gamma"],
+ n_steps=params["n_steps"],
+ ent_coef=params["ent_coef"],
+ learning_rate=params["learning_rate"],
+ vf_coef=params["vf_coef"],
+ max_grad_norm=params["max_grad_norm"],
+ gae_lambda=params["gae_lambda"],
+ batch_size=params["m_batch_size"],
+ n_epochs=params["n_epochs"],
+ clip_range=params["clip_range"],
+ tensorboard_log=PATHS.get("tb"),
+ verbose=1,
+ )
+ elif issubclass(agent, ActorCriticPolicy):
+ model = PPO(
+ agent,
+ env,
+ gamma=params["gamma"],
+ n_steps=params["n_steps"],
+ ent_coef=params["ent_coef"],
+ learning_rate=params["learning_rate"],
+ vf_coef=params["vf_coef"],
+ max_grad_norm=params["max_grad_norm"],
+ gae_lambda=params["gae_lambda"],
+ batch_size=params["m_batch_size"],
+ n_epochs=params["n_epochs"],
+ clip_range=params["clip_range"],
+ tensorboard_log=PATHS.get("tb"),
+ verbose=1,
+ )
+ else:
+ architecture_name = config["architecture_name"]
+ raise TypeError(
+ f"Registered agent class {architecture_name} is neither of type"
+ "'BaseAgent' or 'ActorCriticPolicy'!"
+ )
+ else:
+ # load flag
+ assert os.path.isfile(
+ os.path.join(config["resume"], "best_model.zip")
+ ), f"Couldn't find best model in {config['resume']}"
+ model = PPO.load(os.path.join(config["resume"], "best_model.zip"), env)
+ update_hyperparam_model(model, PATHS, params, n_envs)
+ return model
diff --git a/arena_marl/package.xml b/arena_marl/package.xml
new file mode 100755
index 000000000..8cf8131e7
--- /dev/null
+++ b/arena_marl/package.xml
@@ -0,0 +1,84 @@
+
+
+ arena_marl
+ 0.0.0
+ The arena_marl package
+
+
+
+
+ root
+
+
+
+
+
+ TODO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ catkin
+ geometry_msgs
+ nav_msgs
+ roscpp
+ rospy
+ sensor_msgs
+ tf2
+ tf2_geometry_msgs
+ tf2_ros
+ geometry_msgs
+ nav_msgs
+ roscpp
+ rospy
+ sensor_msgs
+ tf2
+ tf2_geometry_msgs
+ tf2_ros
+ geometry_msgs
+ nav_msgs
+ roscpp
+ rospy
+ sensor_msgs
+ tf2
+ tf2_geometry_msgs
+ tf2_ros
+ flatland_msgs
+
+
+
+
+
+
+
+
diff --git a/arena_marl/scripts/__init__.py b/arena_marl/scripts/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/arena_marl/scripts/deployment/action_publisher.py b/arena_marl/scripts/deployment/action_publisher.py
new file mode 100755
index 000000000..cb74fc3d8
--- /dev/null
+++ b/arena_marl/scripts/deployment/action_publisher.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+import rospy
+import time
+
+from geometry_msgs.msg import Twist
+from rosgraph_msgs.msg import Clock
+from std_msgs.msg import Bool
+
+
+class ActionPublisher:
+ def __init__(self):
+ if rospy.get_param("train_mode"):
+ raise Exception("This node should be used solely in eval mode!")
+
+ rospy.init_node("action_publisher", anonymous=True)
+
+ self._step_size = rospy.get_param("step_size")
+ self._update_rate = rospy.get_param("update_rate")
+ # real time second in sim time
+ self._real_second_in_sim = self._step_size * self._update_rate
+ self._action_publish_rate = rospy.get_param("/robot_action_rate")
+
+ # apply rate in sim time
+ rate = (1 / self._action_publish_rate) / self._real_second_in_sim
+
+ ns_prefix = "" if "/single_env" in rospy.get_param_names() else "/eval_sim/"
+ self._pub_cmd_vel = rospy.Publisher(f"{ns_prefix}cmd_vel", Twist, queue_size=1)
+ self._pub_cycle_trigger = rospy.Publisher(
+ f"{ns_prefix}next_cycle", Bool, queue_size=1
+ )
+ self._sub = rospy.Subscriber(
+ f"{ns_prefix}cmd_vel_pub",
+ Twist,
+ self.callback_receive_cmd_vel,
+ queue_size=1,
+ )
+
+ # to measure sim time
+ # self._clock_sub = rospy.Subscriber(
+ # f"{ns_prefix}clock", Clock, self.callback_clock)
+ # last = 0
+
+ self._action = Twist()
+ self._signal = Bool()
+ self._clock = Clock().clock.to_sec()
+
+ last_action = self._action
+
+ while not rospy.is_shutdown():
+ if self._sub.get_num_connections() < 1:
+ print(f"ActionPublisher: No publisher to {ns_prefix}cmd_vel_pub yet.. ")
+ time.sleep(1)
+ continue
+
+ self._pub_cmd_vel.publish(self._action)
+ self._pub_cycle_trigger.publish(self._signal)
+
+ print(f"Published same action: {last_action==self._action}")
+ last_action = self._action
+
+ time.sleep(rate)
+
+ # print(f"sim time between cmd_vel: {self._clock - last}")
+ # last = self._clock
+
+ def callback_receive_cmd_vel(self, msg_cmd_vel: Twist):
+ self._action = msg_cmd_vel
+
+ def callback_clock(self, msg_clock: Clock):
+ self._clock = msg_clock.clock.to_sec()
+
+
+if __name__ == "__main__":
+ try:
+ ActionPublisher()
+ except rospy.ROSInterruptException:
+ pass
diff --git a/arena_marl/scripts/deployment/drl_agent_node.py b/arena_marl/scripts/deployment/drl_agent_node.py
new file mode 100755
index 000000000..f31c07efe
--- /dev/null
+++ b/arena_marl/scripts/deployment/drl_agent_node.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+import os
+import pickle
+import rospy
+import rospkg
+import sys
+
+from stable_baselines3 import PPO
+
+from flatland_msgs.srv import StepWorld, StepWorldRequest
+from rospy.exceptions import ROSException
+from std_msgs.msg import Bool
+
+from rl_agent.base_agent_wrapper import BaseDRLAgent
+
+
+""" TEMPORARY GLOBAL CONSTANTS """
+NS_PREFIX = ""
+TRAINED_MODELS_DIR = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"), "agents"
+)
+DEFAULT_ACTION_SPACE = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "default_settings.yaml",
+)
+
+
+class DeploymentDRLAgent(BaseDRLAgent):
+ def __init__(
+ self,
+ agent_name: str,
+ ns: str = None,
+ robot_name: str = None,
+ action_space_path: str = DEFAULT_ACTION_SPACE,
+ *args,
+ **kwargs,
+ ) -> None:
+ """Initialization procedure for the DRL agent node.
+
+ Args:
+ agent_name (str):
+ Agent name (directory has to be of the same name)
+ robot_name (str, optional):
+ Robot specific ROS namespace extension. Defaults to None.
+ ns (str, optional):
+ Simulation specific ROS namespace. Defaults to None.
+ action_space_path (str, optional):
+ Path to yaml file containing action space settings.
+ Defaults to DEFAULT_ACTION_SPACE.
+ """
+ self.name = agent_name
+ self.setup_agent()
+
+ hyperparameter_path = os.path.join(
+ TRAINED_MODELS_DIR, self.name, "hyperparameters.json"
+ )
+ super().__init__(
+ ns,
+ robot_name,
+ hyperparameter_path,
+ action_space_path,
+ )
+
+ if not self._is_train_mode:
+ rospy.init_node(f"DRL_local_planner", anonymous=True)
+
+ if self._is_train_mode:
+ # step world to fast forward simulation time
+ self._service_name_step = f"{self._ns}step_world"
+ self._sim_step_client = rospy.ServiceProxy(
+ self._service_name_step, StepWorld
+ )
+
+ def setup_agent(self) -> None:
+ """Loads the trained policy and when required the VecNormalize object."""
+ model_file = os.path.join(
+ TRAINED_MODELS_DIR, self.name, "best_model.zip"
+ )
+ vecnorm_file = os.path.join(
+ TRAINED_MODELS_DIR, self.name, "vec_normalize.pkl"
+ )
+
+ assert os.path.isfile(
+ model_file
+ ), f"Compressed model cannot be found at {model_file}!"
+ assert os.path.isfile(
+ vecnorm_file
+ ), f"VecNormalize file cannot be found at {vecnorm_file}!"
+
+ with open(vecnorm_file, "rb") as file_handler:
+ vec_normalize = pickle.load(file_handler)
+
+ custom_objects = {
+ "learning_rate": 0.0,
+ "lr_schedule": lambda _: 0.0,
+ "clip_range": lambda _: 0.0,
+ }
+
+ self._agent = PPO.load(model_file, custom_objects=custom_objects).policy
+ self._obs_norm_func = vec_normalize.normalize_obs
+
+ def run(self) -> None:
+ """Loop for running the agent until ROS is shutdown.
+
+ Note:
+ Calls the 'step_world'-service for fast-forwarding the \
+ simulation time in training mode. The simulation is forwarded \
+ by action_frequency seconds. Otherwise, communicates with \
+ the ActionPublisher node in order to comply with the specified \
+ action publishing rate.
+ """
+ while not rospy.is_shutdown():
+ if self._is_train_mode:
+ self.call_service_takeSimStep(self._action_frequency)
+ else:
+ self._wait_for_next_action_cycle()
+ obs = self.get_observations()[0]
+ action = self.get_action(obs)
+ self.publish_action(action)
+
+ def _wait_for_next_action_cycle(self) -> None:
+ """Stops the loop until a trigger message is sent by the ActionPublisher
+
+ Note:
+ Only use this method in combination with the ActionPublisher node!
+ That node is only booted when training mode is off.
+ """
+ try:
+ rospy.wait_for_message(f"{self._ns_robot}next_cycle", Bool)
+ except ROSException:
+ pass
+
+ def call_service_takeSimStep(self, t: float = None) -> None:
+ """Fast-forwards the simulation time.
+
+ Args:
+ t (float, optional):
+ Time in seconds. When t is None, time is forwarded by 'step_size' s.
+ Defaults to None.
+ """
+ request = StepWorldRequest() if t is None else StepWorldRequest(t)
+
+ try:
+ response = self._sim_step_client(request)
+ rospy.logdebug("step service=", response)
+ except rospy.ServiceException as e:
+ rospy.logdebug("step Service call failed: %s" % e)
+
+
+def main(agent_name: str) -> None:
+ AGENT = DeploymentDRLAgent(agent_name=agent_name, ns=NS_PREFIX)
+
+ try:
+ AGENT.run()
+ except rospy.ROSInterruptException:
+ pass
+
+
+if __name__ == "__main__":
+ AGENT_NAME = sys.argv[1]
+ main(agent_name=AGENT_NAME)
diff --git a/arena_marl/scripts/deployment/evaluation.py b/arena_marl/scripts/deployment/evaluation.py
new file mode 100644
index 000000000..aabf27a90
--- /dev/null
+++ b/arena_marl/scripts/deployment/evaluation.py
@@ -0,0 +1,127 @@
+from typing import Any, Callable, Dict, List, Optional, Tuple, Union
+
+import numpy as np
+from stable_baselines3.common import base_class
+import supersuit.vector.sb3_vector_wrapper as sb3vw
+
+
+def evaluate_policy(
+ model: "base_class.BaseAlgorithm",
+ # env: Union[sb3vw.SB3VecEnvWrapper, VecEnv],
+ num_robots: int,
+ env: sb3vw.SB3VecEnvWrapper,
+ n_eval_episodes: int = 10,
+ deterministic: bool = True,
+ render: bool = False,
+ callback: Optional[Callable[[Dict[str, Any], Dict[str, Any]], None]] = None,
+ reward_threshold: Optional[float] = None,
+ return_episode_rewards: bool = False,
+ warn: bool = True,
+) -> Union[Tuple[float, float], Tuple[List[float], List[int]]]:
+ """
+ Runs policy for ``n_eval_episodes`` episodes and returns average reward.
+ This is made to work only with one env.
+
+ .. note::
+ If environment has not been wrapped with ``Monitor`` wrapper, reward and
+ episode lengths are counted as it appears with ``env.step`` calls. If
+ the environment contains wrappers that modify rewards or episode lengths
+ (e.g. reward scaling, early episode reset), these will affect the evaluation
+ results as well. You can avoid this by wrapping environment with ``Monitor``
+ wrapper before anything else.
+
+ :param model: The RL agent you want to evaluate.
+ :param env: The gym environment. In the case of a ``VecEnv``
+ this must contain only one environment.
+ :param n_eval_episodes: Number of episode to evaluate the agent
+ :param deterministic: Whether to use deterministic or stochastic actions
+ :param render: Whether to render the environment or not
+ :param callback: callback function to do additional checks,
+ called after each step. Gets locals() and globals() passed as parameters.
+ :param reward_threshold: Minimum expected reward per episode,
+ this will raise an error if the performance is not met
+ :param return_episode_rewards: If True, a list of rewards and episode lengths
+ per episode will be returned instead of the mean.
+ :param warn: If True (default), warns user about lack of a Monitor wrapper in the
+ evaluation environment.
+ :return: Mean reward per episode, std of reward per episode.
+ Returns ([float], [int]) when ``return_episode_rewards`` is True, first
+ list containing per-episode rewards and second containing per-episode lengths
+ (in number of steps).
+ """
+ is_monitor_wrapped = False
+ # Avoid circular import
+ from stable_baselines3.common.env_util import is_wrapped
+ from stable_baselines3.common.monitor import Monitor
+
+ not_reseted = True
+ # Avoid double reset, as VecEnv are reset automatically.
+ if not_reseted:
+ # Observations dictionary in {_agent name_: _respective observations_}
+ obs = env.reset()
+ not_reseted = False
+
+ agents = [a for a, _ in obs.items()]
+ episode_rewards = {a: [None] for a in agents}
+ episode_lengths = []
+
+ default_dones = {a: False for a in agents}
+ default_states = {a: None for a in agents}
+ default_actions = {a: None for a in agents}
+ default_episode_reward = {a: None for a in agents}
+ while len(episode_rewards) < n_eval_episodes:
+ # Number of loops here might differ from true episodes
+ # played, if underlying wrappers modify episode lengths.
+ # Avoid double reset, as VecEnv are reset automatically.
+ if not_reseted:
+ # Observations dictionary in {_agent name_: _respective observations_}
+ obs = env.reset()
+ not_reseted = False
+ dones = default_dones.copy()
+ states = default_states.copy()
+ actions = default_actions.copy()
+ episode_reward = default_episode_reward.copy()
+ episode_length = 0
+ while not dones:
+ # Get actions and states from each agent
+ for agent, state in states.items():
+ actions[agent], states[agent] = model.predict(
+ obs[agent], state, deterministic=deterministic
+ )
+
+ obs, rewards, dones, infos = env.step(actions)
+ for agent, reward in zip(agents, rewards.values()):
+ episode_reward[agent] += reward
+
+ if callback is not None:
+ callback(locals(), globals())
+
+ episode_length += 1
+ if render:
+ env.render()
+
+ # if is_monitor_wrapped:
+ # # Do not trust "done" with episode endings.
+ # # Remove vecenv stacking (if any)
+ # # if isinstance(env, VecEnv):
+ # # info = info[0]
+ # if "episode" in info.keys():
+ # # Monitor wrapper includes "episode" key in info if environment
+ # # has been wrapped with it. Use those rewards instead.
+ # episode_rewards.append(info["episode"]["r"])
+ # episode_lengths.append(info["episode"]["l"])
+ # else:
+ for agent, reward in episode_reward.items():
+ episode_rewards[agent].append(reward)
+ episode_lengths.append(episode_length)
+
+ mean_rewards = {agent: np.mean(episode_rewards[agent]) for agent in agents}
+ std_rewards = {agent: np.std(episode_rewards[agent]) for agent in agents}
+ if reward_threshold is not None:
+ assert min(mean_rewards.values()) > reward_threshold, (
+ "Atleast one mean reward below threshold: "
+ f"{min(mean_rewards.values()):.2f} < {reward_threshold:.2f}"
+ )
+ if return_episode_rewards:
+ return episode_rewards, episode_lengths
+ return mean_rewards, std_rewards
diff --git a/arena_marl/scripts/deployment/marl_deployment.py b/arena_marl/scripts/deployment/marl_deployment.py
new file mode 100644
index 000000000..ef1712d2b
--- /dev/null
+++ b/arena_marl/scripts/deployment/marl_deployment.py
@@ -0,0 +1,108 @@
+from multiprocessing import cpu_count
+import os
+
+
+from arena_marl.marl_agent.utils.utils import (
+ instantiate_deploy_drl_agents,
+ get_hyperparameter_file,
+ instantiate_train_drl_agents,
+)
+
+from arena_marl.scripts.deployment.evaluation import evaluate_policy
+
+from marl_tools.train_agent_utils import (
+ load_config,
+)
+from marl_tools import train_agent_utils
+
+import rospy
+
+from stable_baselines3.common.vec_env import DummyVecEnv
+from stable_baselines3 import PPO
+from arena_marl.marl_agent.utils.supersuit_utils import (
+ vec_env_create,
+)
+from arena_marl.marl_agent.envs.pettingzoo_env import env_fn
+
+
+from tools.argsparser import parse_training_args
+
+
+def main(args):
+ # load configuration
+ config = load_config(args.config)
+
+ # set debug_mode
+ rospy.set_param("debug_mode", config["debug_mode"])
+
+ # load agents
+ assert len(config["robots"]) == 1, "Only one robot type is supported"
+
+ robots, existing_robots = {}, 0
+
+ # loop over all robots in config
+ for robot_name, robot_train_params in config["robots"].items():
+ # Get the Base Agent instance(s)
+ robots[robot_name] = {
+ "agents": instantiate_train_drl_agents(
+ num_robots=robot_train_params["num_robots"],
+ robot_model=robot_name,
+ hyperparameter_path=get_hyperparameter_file(
+ robot_train_params["hyperparameter_file"]
+ ),
+ )
+ }
+ existing_robots += robot_train_params["num_robots"]
+
+ # Define paths. If the agent is a SARL agent, MARL_DIR is None.
+ MARL_DIR = ""
+ paths = train_agent_utils.get_paths(
+ MARL_DIR,
+ robot_name,
+ robot_train_params["resume"].split("/")[-1],
+ robot_train_params,
+ config["training_curriculum"]["training_curriculum_file"],
+ config["eval_log"],
+ config["tb"],
+ )
+
+ # Create env and store in dict
+ env = vec_env_create(
+ env_fn,
+ instantiate_train_drl_agents,
+ num_robots=robot_train_params["num_robots"],
+ num_cpus=cpu_count() - 1,
+ num_vec_envs=config["n_envs"],
+ task_mode=config["task_mode"],
+ PATHS=paths,
+ agent_list_kwargs={
+ "existing_robots": existing_robots,
+ "robot_model": robot_name,
+ },
+ max_num_moves_per_eps=config["max_num_moves_per_eps"],
+ )
+ robots[robot_name] = {"env": env}
+
+ # Load PPO model and store in dict
+ model = PPO.load(
+ os.path.join(robot_train_params["resume"], "best_model.zip"), env
+ )
+ robots[robot_name]["model"] = model
+
+ # Evaluate the policy for one robot type!
+ # Integration of multiple robot types is not yet implemented
+ mean_rewards, std_rewards = evaluate_policy(
+ model=robots[robot_name]["model"],
+ num_robots=robot_train_params["num_robots"],
+ env=robots[robot_name]["env"],
+ n_eval_episodes=config["periodic_eval"]["n_eval_episodes"],
+ return_episode_rewards=False,
+ )
+
+ print("Mean rewards: {}".format(mean_rewards))
+ print("Std rewards: {}".format(std_rewards))
+
+
+if __name__ == "__main__":
+ args, _ = parse_training_args()
+ main(args)
diff --git a/arena_marl/scripts/deployment/run_agent.py b/arena_marl/scripts/deployment/run_agent.py
new file mode 100644
index 000000000..6c758fc1a
--- /dev/null
+++ b/arena_marl/scripts/deployment/run_agent.py
@@ -0,0 +1,217 @@
+import os
+import sys
+import rospy
+import rospkg
+import json
+import numpy as np
+import time
+import warnings
+
+from stable_baselines3 import PPO
+from stable_baselines3.common.vec_env import SubprocVecEnv, DummyVecEnv, VecNormalize
+from stable_baselines3.common.monitor import Monitor
+from stable_baselines3.common.evaluation import evaluate_policy
+
+from rl_agent.envs.flatland_gym_env import FlatlandEnv
+from task_generator.task_generator.tasks import StopReset
+from marl_tools.argsparser import parse_run_agent_args
+from marl_tools.train_agent_utils import (
+ load_hyperparameters_json,
+ print_hyperparameters,
+)
+
+### AGENT LIST ###
+AGENTS = [
+ "AGENT_1_2021_04_02__22_03",
+ "AGENT_2_2021_03_30__23_10",
+ "AGENT_3_2021_04_01__08_06",
+ "AGENT_4_2021_04_02__01_07",
+ "AGENT_5_2021_03_31__18_52",
+ "AGENT_6_2021_04_04__02_12",
+ "AGENT_7_2021_04_06__07_00",
+ "AGENT_8_2021_04_05__22_11",
+ "AGENT_9_2021_04_04__11_09",
+ "AGENT_10_2021_04_03__16_57",
+ "AGENT_11_2021_04_14__20_13",
+ "AGENT_12_2021_04_05__12_08",
+ "AGENT_13_2021_04_04__18_04",
+ "AGENT_14_2021_04_07__01_17",
+ "AGENT_15_2021_04_08__01_27",
+ "AGENT_16_2021_04_09__22_24",
+ "AGENT_17_2021_04_10__15_36",
+ "AGENT_18_2021_04_11__13_54",
+ "AGENT_19_2021_04_12__13_17",
+]
+
+
+def get_paths(args: dict, AGENT: str):
+ dir = rospkg.RosPack().get_path("arena_local_planner_drl")
+ PATHS = {
+ "model": os.path.join(dir, "agents", AGENT),
+ "vecnorm": os.path.join(dir, "agents", AGENT, "vec_normalize.pkl"),
+ "robot_setting": os.path.join(
+ rospkg.RosPack().get_path("simulator_setup"), "robot", "myrobot.model.yaml"
+ ),
+ "robot_as": os.path.join(dir, "configs", "default_settings.yaml"),
+ "scenario": os.path.join(
+ rospkg.RosPack().get_path("simulator_setup"),
+ "scenarios",
+ args.scenario + ".json",
+ ),
+ "curriculum": os.path.join(
+ dir, "configs", "training_curriculum_map1small.yaml"
+ ),
+ "log": os.path.join(dir, "evaluation_logs", AGENT),
+ }
+ if args.log and not os.path.exists(PATHS["log"]):
+ os.makedirs(PATHS["log"])
+ return PATHS
+
+
+def make_env(
+ with_ns: bool, PATHS: dict, PARAMS: dict, log: bool = False, max_steps: int = 1000
+):
+ """
+ Utility function for the evaluation environment.
+
+ :param params: (dict) hyperparameters of agent to be trained
+ :param PATHS: (dict) script relevant paths
+ :param log: (bool) to differentiate between train and eval env
+ :param max_steps: (int) number of steps before the episode is stopped
+ :return: (Callable)
+ """
+
+ def _init():
+ ns = f"eval_sim" if with_ns else ""
+
+ env = FlatlandEnv(
+ ns,
+ PARAMS["reward_fnc"],
+ PARAMS["discrete_action_space"],
+ goal_radius=0.05,
+ max_steps_per_episode=max_steps,
+ train_mode=False,
+ task_mode="scenario",
+ PATHS=PATHS,
+ curr_stage=4,
+ extended_eval=True,
+ )
+ if log:
+ # eval env
+ env = Monitor(
+ env,
+ PATHS["log"],
+ False,
+ info_keywords=(
+ "collisions",
+ "distance_travelled",
+ "time_safe_dist",
+ "time",
+ "done_reason",
+ "is_success",
+ ),
+ )
+ return env
+
+ return _init
+
+
+if __name__ == "__main__":
+ args, _ = parse_run_agent_args()
+
+ if args.load:
+ AGENTS = [args.load]
+ assert len(AGENTS) > 0, "No agent name was given for evaluation"
+
+ ros_params = rospy.get_param_names()
+ ns_for_nodes = "/single_env" not in ros_params
+
+ start = time.time()
+ while len(AGENTS) != 0:
+ AGENT = AGENTS.pop(0)
+ print(f"START RUNNING AGENT: {AGENT}")
+ PATHS = get_paths(args, AGENT)
+
+ assert os.path.isfile(os.path.join(PATHS["model"], "best_model.zip")), (
+ "No model file found in %s" % PATHS["model"]
+ )
+ assert os.path.isfile(PATHS["scenario"]), (
+ "No scenario file named %s" % PATHS["scenario"]
+ )
+
+ PARAMS = load_hyperparameters_json(PATHS)
+ print_hyperparameters(PARAMS)
+
+ env = DummyVecEnv(
+ [make_env(ns_for_nodes, PATHS, PARAMS, args.log, args.max_steps)]
+ )
+ if PARAMS["normalize"]:
+ if not os.path.isfile(PATHS["vecnorm"]):
+ # without it agent performance will be strongly altered
+ warnings.warn(
+ f"Couldn't find VecNormalize pickle for {PATHS['model'].split('/')[-1]}, going to skip this model"
+ )
+ continue
+
+ env = VecNormalize.load(PATHS["vecnorm"], env)
+
+ # load agent
+ agent = PPO.load(os.path.join(PATHS["model"], "best_model.zip"), env)
+
+ try:
+ evaluate_policy(
+ model=agent, env=env, n_eval_episodes=args.num_eps, deterministic=True
+ )
+ except StopReset:
+ pass
+
+ time = round(time.time() - start)
+ print(f"Time passed: {time}s")
+ print("EVALUATION DONE!")
+ sys.exit()
+
+ # env.reset()
+ # first_obs = True
+
+ # # iterate through each scenario max_repeat times
+ # while True:
+ # if first_obs:
+ # # send action 'stand still' in order to get first obs
+ # if params['discrete_action_space']:
+ # obs, rewards, dones, info = env.step([6])
+ # else:
+ # obs, rewards, dones, info = env.step([[0.0, 0.0]])
+ # first_obs = False
+ # cum_reward = 0.0
+
+ # # timer = time.time()
+ # action, _ = agent.predict(obs, deterministic=True)
+ # # print(f"Action predict time: {(time.time()-timer)*2.5} (sim time)")
+
+ # # clip action
+ # if not params['discrete_action_space']:
+ # action = np.maximum(
+ # np.minimum(agent.action_space.high, action), agent.action_space.low)
+
+ # # apply action
+ # obs, rewards, done, info = env.step(action)
+
+ # cum_reward += rewards
+
+ # if done:
+ # if args.verbose == '1':
+ # if info[0]['done_reason'] == 0:
+ # done_reason = "exceeded max steps"
+ # elif info[0]['done_reason'] == 1:
+ # done_reason = "collision"
+ # else:
+ # done_reason = "goal reached"
+
+ # print("Episode finished with reward of %f (finish reason: %s)"% (cum_reward, done_reason))
+ # env.reset()
+ # first_obs = True
+
+ # time.sleep(0.001)
+ # if rospy.is_shutdown():
+ # print('shutdown')
+ # break
diff --git a/arena_marl/scripts/training/marl_test_script.py b/arena_marl/scripts/training/marl_test_script.py
new file mode 100644
index 000000000..b9b79e37a
--- /dev/null
+++ b/arena_marl/scripts/training/marl_test_script.py
@@ -0,0 +1,125 @@
+import numpy as np
+import rospy
+import rospkg
+import os
+
+from stable_baselines3 import PPO
+from stable_baselines3.common.callbacks import MarlEvalCallback
+from supersuit import pettingzoo_env_to_vec_env_v0, black_death_v2
+from supersuit.vector.sb3_vector_wrapper import SB3VecEnvWrapper
+
+rospy.set_param("/MARL", True)
+rospy.set_param("/num_robots", 8)
+
+from rl_agent.training_agent_wrapper import TrainingDRLAgent
+from scripts.deployment.drl_agent_node import DeploymentDRLAgent
+from marl_agent.envs.pettingzoo_env import FlatlandPettingZooEnv
+
+from nav_msgs.srv import GetMap
+
+DEFAULT_HYPERPARAMETER = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "hyperparameters",
+ "default.json",
+)
+DEFAULT_ACTION_SPACE = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "default_settings.yaml",
+)
+
+
+def instantiate_drl_agents(
+ num_robots: int = 1,
+ ns: str = None,
+ robot_name_prefix: str = "robot",
+ hyperparameter_path: str = DEFAULT_HYPERPARAMETER,
+ action_space_path: str = DEFAULT_ACTION_SPACE,
+) -> list:
+ return [
+ TrainingDRLAgent(
+ ns=ns,
+ robot_name=robot_name_prefix + str(i + 1),
+ hyperparameter_path=hyperparameter_path,
+ action_space_path=action_space_path,
+ )
+ for i in range(num_robots)
+ ]
+
+
+def main():
+ # rospy.init_node(f"USER_NODE", anonymous=True)
+
+ # agent_list = instantiate_drl_agents(
+ # num_robots=8,
+ # ns="sim_1",
+ # robot_name_prefix=rospy.get_param("base_robot_name", default="robot"),
+ # )
+ NUM_ROBOTS = 8
+
+ env = SB3VecEnvWrapper(
+ pettingzoo_env_to_vec_env_v0(
+ black_death_v2(
+ FlatlandPettingZooEnv(
+ num_agents=NUM_ROBOTS,
+ ns="sim_1",
+ agent_list_fn=instantiate_drl_agents,
+ max_num_moves_per_eps=2000,
+ )
+ )
+ )
+ )
+ obs = env.reset()
+
+ # AGENT = DeploymentDRLAgent(
+ # agent_name="rule_04", ns="sim_1", robot_name="test1"
+ # )
+
+ model = PPO("MlpPolicy", env)
+ model.learn(
+ total_timesteps=200000,
+ callbacks=get_evalcallback(
+ num_robots=NUM_ROBOTS,
+ ),
+ reset_num_timesteps=True,
+ )
+ exit()
+ agent_names = env.agents
+ for _ in range(100000000):
+ if not agent_names:
+ agent_names = env.possible_agents[:]
+ obs = env.reset()
+
+ actions = {agent: AGENT.get_action(obs[agent]) for agent in agent_names}
+ obs, rewards, dones, infos = env.step(actions)
+
+ done_agents = [k for k, v in dones.items() if v]
+ for agent in done_agents:
+ agent_names.remove(agent)
+
+ if done_agents:
+ for agent in done_agents:
+ if infos[agent]["is_success"]:
+ print(f"{agent}: finito")
+
+
+def get_evalcallback(num_robots: int) -> MarlEvalCallback:
+ eval_env = FlatlandPettingZooEnv(
+ num_agents=num_robots,
+ ns="eval_sim",
+ agent_list_fn=instantiate_drl_agents,
+ max_num_moves_per_eps=2000,
+ )
+
+ return MarlEvalCallback(
+ eval_env,
+ num_robots,
+ n_eval_episodes=40,
+ eval_freq=1,
+ deterministic=True,
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/arena_marl/scripts/training/train_marl_agent.py b/arena_marl/scripts/training/train_marl_agent.py
new file mode 100644
index 000000000..689bd45d1
--- /dev/null
+++ b/arena_marl/scripts/training/train_marl_agent.py
@@ -0,0 +1,226 @@
+import sys
+import os, sys, rospy, time
+
+from datetime import time
+from multiprocessing import cpu_count
+
+import rospy
+from multiprocessing import cpu_count, set_start_method
+
+# from stable_baselines3 import PPO
+# from supersuit.vector import MakeCPUAsyncConstructor, ConcatVecEnv
+# from supersuit.vector.sb3_vector_wrapper import SB3VecEnvWrapper
+
+# from marl_tools.custom_mlp_utils import *
+from arena_navigation.arena_local_planner.learning_based.arena_local_planner_drl.rl_agent.model.custom_policy import *
+from arena_navigation.arena_local_planner.learning_based.arena_local_planner_drl.rl_agent.model.custom_sb3_policy import *
+
+# from tools.argsparser import parse_training_args
+from marl_tools.staged_train_callback import InitiateNewTrainStage
+
+from tools.argsparser import parse_training_args
+
+from arena_marl.marl_agent.envs.pettingzoo_env import env_fn
+from arena_marl.marl_agent.utils.supersuit_utils import (
+ vec_env_create,
+)
+
+# from tools.argsparser import parse_marl_training_args
+from marl_tools.train_agent_utils import (
+ get_MARL_agent_name_and_start_time,
+ get_paths,
+ choose_agent_model,
+ load_config,
+ initialize_hyperparameters,
+)
+from marl_tools import train_agent_utils
+
+from stable_baselines3.common.callbacks import (
+ StopTrainingOnRewardThreshold,
+ MarlEvalCallback,
+)
+
+from arena_navigation.arena_local_planner.learning_based.arena_local_planner_drl.tools.train_agent_utils import *
+
+from marl_agent.utils.utils import instantiate_train_drl_agents
+
+
+def main(args):
+ # load configuration
+ config = load_config(args.config)
+
+ # set debug_mode
+ rospy.set_param("debug_mode", config["debug_mode"])
+
+ robots, existing_robots = {}, 0
+ MARL_NAME, START_TIME = get_MARL_agent_name_and_start_time()
+
+ # create seperate model instances for each robot
+ for robot_name, robot_train_params in config["robots"].items():
+ # generate agent name and model specific paths
+ agent_name = (
+ robot_train_params["resume"].split("/")[-1]
+ if robot_train_params["resume"]
+ else f"{robot_name}_{START_TIME}"
+ )
+
+ paths = train_agent_utils.get_paths(
+ MARL_NAME,
+ robot_name,
+ agent_name,
+ robot_train_params,
+ config["training_curriculum"]["training_curriculum_file"],
+ config["eval_log"],
+ config["tb"],
+ )
+
+ # initialize hyperparameters (save to/ load from json)
+ hyper_params = train_agent_utils.initialize_hyperparameters(
+ PATHS=paths,
+ config=robot_train_params,
+ n_envs=config["n_envs"],
+ )
+
+ env = vec_env_create(
+ env_fn,
+ instantiate_train_drl_agents,
+ num_robots=robot_train_params["num_robots"],
+ num_cpus=cpu_count() - 1,
+ num_vec_envs=config["n_envs"],
+ task_mode=config["task_mode"],
+ PATHS=paths,
+ agent_list_kwargs={
+ "existing_robots": existing_robots,
+ "robot_model": robot_name,
+ },
+ max_num_moves_per_eps=config["max_num_moves_per_eps"],
+ )
+
+ existing_robots += robot_train_params["num_robots"]
+
+ # env = VecNormalize(
+ # env,
+ # training=True,
+ # norm_obs=True,
+ # norm_reward=True,
+ # clip_reward=15,
+ # clip_obs=15,
+ # )
+
+ model = choose_agent_model(
+ agent_name, paths, robot_train_params, env, hyper_params, config["n_envs"]
+ )
+
+ # add configuration for one robot to robots dictionary
+ robots[robot_name] = {
+ "model": model,
+ "env": env,
+ "n_envs": config["n_envs"],
+ "robot_train_params": robot_train_params,
+ "hyper_params": hyper_params,
+ "paths": paths,
+ }
+
+ # set num of timesteps to be generated
+ n_timesteps = 40000000 if config["n_timesteps"] is None else config["n_timesteps"]
+
+ start = time.time()
+ try:
+ model = robots[robot_name]["model"]
+ model.learn(
+ total_timesteps=n_timesteps,
+ reset_num_timesteps=True,
+ # Übergib einfach das dict für den aktuellen roboter
+ callback=get_evalcallback(
+ eval_config=config["periodic_eval"],
+ curriculum_config=config["training_curriculum"],
+ stop_training_config=config["stop_training"],
+ train_env=robots[robot_name]["env"],
+ num_robots=robots[robot_name]["robot_train_params"]["num_robots"],
+ num_envs=config["n_envs"],
+ task_mode=config["task_mode"],
+ PATHS=robots[robot_name]["paths"],
+ ),
+ )
+ except KeyboardInterrupt:
+ print("KeyboardInterrupt..")
+ # finally:
+ # update the timesteps the model has trained in total
+ # update_total_timesteps_json(n_timesteps, PATHS)
+
+ robots[robot_name][model].env.close()
+ print(f"Time passed: {time.time() - start}s")
+ print("Training script will be terminated")
+ sys.exit()
+
+
+def get_evalcallback(
+ eval_config: dict,
+ curriculum_config: dict,
+ stop_training_config: dict,
+ train_env: VecEnv,
+ num_robots: int,
+ num_envs: int,
+ task_mode: str,
+ PATHS: dict,
+) -> MarlEvalCallback:
+ """Function which generates an evaluation callback with an evaluation environment.
+
+ Args:
+ train_env (VecEnv): Vectorized training environment
+ num_robots (int): Number of robots in the environment
+ num_envs (int): Number of parallel spawned environments
+ task_mode (str): Task mode for the current experiment
+ PATHS (dict): Dictionary which holds hyperparameters for the experiment
+
+ Returns:
+ MarlEvalCallback: [description]
+ """
+ eval_env = env_fn(
+ num_agents=num_robots,
+ ns="eval_sim",
+ agent_list_fn=instantiate_train_drl_agents,
+ max_num_moves_per_eps=eval_config["max_num_moves_per_eps"],
+ PATHS=PATHS,
+ )
+
+ # eval_env = VecNormalize(
+ # eval_env,
+ # training=False,
+ # norm_obs=True,
+ # norm_reward=False,
+ # clip_reward=15,
+ # clip_obs=3.5,
+ # )
+
+ return MarlEvalCallback(
+ train_env=train_env,
+ eval_env=eval_env,
+ num_robots=num_robots,
+ n_eval_episodes=eval_config["n_eval_episodes"],
+ eval_freq=eval_config["eval_freq"],
+ deterministic=True,
+ log_path=PATHS["eval"],
+ best_model_save_path=PATHS["model"],
+ callback_on_eval_end=InitiateNewTrainStage(
+ n_envs=num_envs,
+ treshhold_type=curriculum_config["threshold_type"],
+ upper_threshold=curriculum_config["upper_threshold"],
+ lower_threshold=curriculum_config["lower_threshold"],
+ task_mode=task_mode,
+ verbose=1,
+ ),
+ callback_on_new_best=StopTrainingOnRewardThreshold(
+ treshhold_type=stop_training_config["threshold_type"],
+ threshold=stop_training_config["threshold"],
+ verbose=1,
+ ),
+ )
+
+
+if __name__ == "__main__":
+ set_start_method("fork")
+ # args, _ = parse_marl_training_args()
+ args, _ = parse_training_args()
+ # rospy.init_node("train_env", disable_signals=False, anonymous=True)
+ main(args)
diff --git a/arena_marl/setup.py b/arena_marl/setup.py
new file mode 100755
index 000000000..2dd27cb99
--- /dev/null
+++ b/arena_marl/setup.py
@@ -0,0 +1,16 @@
+## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD
+
+from distutils.core import setup
+from catkin_pkg.python_setup import generate_distutils_setup
+
+# fetch values from package.xml
+setup_args = generate_distutils_setup(
+ packages=[
+ "rl_agent",
+ "rl_agent.envs",
+ "rl_agent.utils",
+ "scripts",
+ "tools",
+ ],
+)
+setup(**setup_args)
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings.yaml
similarity index 96%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings.yaml
index cc4c53e98..bcf07f4f1 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings.yaml
@@ -1,4 +1,5 @@
robot:
+ holonomic: False
discrete_actions:
- name: move_forward
linear: 0.30
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_agvota.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_agvota.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_agvota.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_agvota.yaml
index de7680846..d9c3a3482 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_agvota.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_agvota.yaml
@@ -1,4 +1,5 @@
robot:
+ radius: 0.629
holonomic: False
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_burger.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_burger.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_burger.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_burger.yaml
index 749d30395..dc0782237 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_burger.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_burger.yaml
@@ -1,4 +1,5 @@
robot:
+ radius: 0.113
holonomic: False
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_cob4.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_cob4.yaml
similarity index 98%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_cob4.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_cob4.yaml
index 2fb2c227b..41ed40c74 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_cob4.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_cob4.yaml
@@ -1,5 +1,6 @@
# TODO: The discrete actions have not been changed (from ridgeback here)
robot:
+ radius: 0.36
holonomic: True
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_jackal.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_jackal.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_jackal.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_jackal.yaml
index 7d43b0e1d..2e6ddbed4 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_jackal.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_jackal.yaml
@@ -1,4 +1,5 @@
robot:
+ radius: 0.267
holonomic: False
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_ridgeback.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_ridgeback.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_ridgeback.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_ridgeback.yaml
index 10cbb2319..eeae900c3 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_ridgeback.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_ridgeback.yaml
@@ -1,4 +1,5 @@
robot:
+ radius: 0.625
holonomic: True
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_rto.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_rto.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_rto.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_rto.yaml
index 1157ed13c..0ccfef053 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_rto.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_rto.yaml
@@ -1,5 +1,6 @@
# TODO: The discrete actions have not been changed (from ridgeback here)
robot:
+ radius: 0.225
holonomic: True
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_rto_real.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_rto_real.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_rto_real.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_rto_real.yaml
index 8e9005ef2..b9fee66ea 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_rto_real.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_rto_real.yaml
@@ -1,5 +1,6 @@
# TODO: The discrete actions have not been changed (from ridgeback here)
robot:
+ radius: 0.225
holonomic: True
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_tiago.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_tiago.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_tiago.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_tiago.yaml
index 78232be97..c7a2d634c 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_tiago.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_tiago.yaml
@@ -1,5 +1,6 @@
# TODO: @Tuan adjust the discrete actions
robot:
+ radius: 0.27
holonomic: False
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_waffle_pi.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_waffle_pi.yaml
similarity index 97%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_waffle_pi.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_waffle_pi.yaml
index 3fd4756f6..1cdd02a0c 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_waffle_pi.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_waffle_pi.yaml
@@ -1,5 +1,6 @@
# TODO: The discrete actions have not been changed (from burger here)
robot:
+ radius: 0.208
holonomic: False
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_youbot.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_youbot.yaml
similarity index 98%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_youbot.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_youbot.yaml
index 40ef0e741..bfe85d23f 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/default_settings_youbot.yaml
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/action_spaces/default_settings_youbot.yaml
@@ -2,6 +2,7 @@
# values from: https://github.com/youbot/youbot_navigation/blob/hydro-devel/youbot_navigation_common/config/base_local_planner_params.yaml
# and values from here: http://www.youbot-store.com/wiki/index.php/YouBot_Detailed_Specifications
robot:
+ radius: 0.347
holonomic: True
discrete_actions:
- name: move_forward
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/deployment_config.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/deployment_config.yaml
new file mode 100644
index 000000000..f5503fb10
--- /dev/null
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/deployment_config.yaml
@@ -0,0 +1,68 @@
+# in debug_mode no agent directories will be created and no models will be saved
+debug_mode: true
+
+# Hardware specifc configurations
+# number of parallel environments
+n_envs: 1
+# gpu yes or no
+no_gpu: false
+
+
+### Global configuration that applies to all robots
+# navigation task mode, chose from "random" or "staged"
+task_mode: "staged"
+# number of simulation timesteps
+n_timesteps: 40000000
+max_num_moves_per_eps: 1000
+
+periodic_eval:
+ # max number of steps per episode
+ max_num_moves_per_eps: 1000
+ # number of evaluation episodes
+ n_eval_episodes: 100
+ # evaluation frequency, evaluation after every n_envs * 20000 timesteps
+ eval_freq: 20000
+
+### training_curriculum
+# threshold metric to be considered during evaluation
+# can be either "succ" (success rate) or "rew" (reward)
+training_curriculum:
+ # file for the robot's learning curriculum
+ training_curriculum_file: "training_curriculum.yaml"
+ threshold_type: "succ"
+ upper_threshold: 0.8
+ lower_threshold: 0.6
+
+### stop training on threshold
+# stops training when last stage reached and threshold satisfied
+stop_training:
+ threshold_type: "succ"
+ threshold: 0.9
+
+
+# save evaluation stats during training in log file
+eval_log: false
+# use tensorboard
+tb: false
+
+robots:
+ jackal:
+ # number of robots of this type
+ num_robots: 2
+ # name of hyperparameter file located in the configs/hyperparameters directory
+ hyperparameter_file: "default.json"
+ # name of architecture defined in the Policy factory
+ architecture_name: "AGENT_24"
+ # path to latest checkpoint; if provided the training will be resumed from that checkpoint
+ resume: "/home/ignc/catkin_ws/src/arena-rosnav/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/agents/jackal"
+ # burger:
+ # # number of robots of this type
+ # num_robots: 4
+ # # name of hyperparameter file located in the configs/hyperparameters directory
+ # hyperparameter_file: "default.json"
+ # # name of architecture defined in the Policy factory
+ # architecture_name: "AGENT_24"
+ # # path to latest checkpoint; if provided the training will be resumed from that checkpoint
+ # resume: null
+ # add more robots as you wish
+
\ No newline at end of file
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/hyperparameters/default.json b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/hyperparameters/default.json
index aab39666c..c7a84645a 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/hyperparameters/default.json
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/hyperparameters/default.json
@@ -2,7 +2,7 @@
"agent_name": "",
"robot": "myrobot",
"actions_in_observationspace": true,
- "reward_fnc": "rule_05",
+ "reward_fnc": "rule_04",
"discrete_action_space": false,
"normalize": false,
"task_mode": "staged",
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_config.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_config.yaml
new file mode 100644
index 000000000..99ef999c7
--- /dev/null
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_config.yaml
@@ -0,0 +1,68 @@
+# in debug_mode no agent directories will be created and no models will be saved
+debug_mode: true
+
+# Hardware specifc configurations
+# number of parallel environments
+n_envs: 1
+# gpu yes or no
+no_gpu: false
+
+
+### Global configuration that applies to all robots
+# navigation task mode, chose from "random" or "staged"
+task_mode: "staged"
+# number of simulation timesteps
+n_timesteps: 40000000
+max_num_moves_per_eps: 1000
+
+periodic_eval:
+ # max number of steps per episode
+ max_num_moves_per_eps: 1000
+ # number of evaluation episodes
+ n_eval_episodes: 100
+ # evaluation frequency, evaluation after every n_envs * 20000 timesteps
+ eval_freq: 20000
+
+### training_curriculum
+# threshold metric to be considered during evaluation
+# can be either "succ" (success rate) or "rew" (reward)
+training_curriculum:
+ # file for the robot's learning curriculum
+ training_curriculum_file: "training_curriculum.yaml"
+ threshold_type: "succ"
+ upper_threshold: 0.8
+ lower_threshold: 0.6
+
+### stop training on threshold
+# stops training when last stage reached and threshold satisfied
+stop_training:
+ threshold_type: "succ"
+ threshold: 0.9
+
+
+# save evaluation stats during training in log file
+eval_log: false
+# use tensorboard
+tb: false
+
+robots:
+ jackal:
+ # number of robots of this type
+ num_robots: 2
+ # name of hyperparameter file located in the configs/hyperparameters directory
+ hyperparameter_file: "default.json"
+ # name of architecture defined in the Policy factory
+ architecture_name: "AGENT_24"
+ # path to latest checkpoint; if provided the training will be resumed from that checkpoint
+ resume: null
+ # burger:
+ # # number of robots of this type
+ # num_robots: 4
+ # # name of hyperparameter file located in the configs/hyperparameters directory
+ # hyperparameter_file: "default.json"
+ # # name of architecture defined in the Policy factory
+ # architecture_name: "AGENT_24"
+ # # path to latest checkpoint; if provided the training will be resumed from that checkpoint
+ # resume: null
+ # add more robots as you wish
+
\ No newline at end of file
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculum.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculums/training_curriculum.yaml
similarity index 100%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculum.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculums/training_curriculum.yaml
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculum_map1small.yaml b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculums/training_curriculum_map1small.yaml
similarity index 100%
rename from arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculum_map1small.yaml
rename to arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/configs/training_curriculums/training_curriculum_map1small.yaml
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/__pycache__/__init__.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/__pycache__/__init__.cpython-38.pyc
index cde1f812d..071e0c50a 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/__pycache__/__init__.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/__pycache__/__init__.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/base_agent_wrapper.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/base_agent_wrapper.py
index ff3e18a6f..f8e3e5f70 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/base_agent_wrapper.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/base_agent_wrapper.py
@@ -16,14 +16,7 @@
from rl_agent.utils.reward import RewardCalculator
robot_model = rospy.get_param("model")
-ROOT_ROBOT_PATH = os.path.join(
- rospkg.RosPack().get_path("simulator_setup"), "robot"
-)
-DEFAULT_ACTION_SPACE = os.path.join(
- rospkg.RosPack().get_path("arena_local_planner_drl"),
- "configs",
- f"default_settings_{robot_model}.yaml",
-)
+ROOT_ROBOT_PATH = os.path.join(rospkg.RosPack().get_path("simulator_setup"), "robot")
DEFAULT_HYPERPARAMETER = os.path.join(
rospkg.RosPack().get_path("arena_local_planner_drl"),
"configs",
@@ -38,39 +31,46 @@ class BaseDRLAgent(ABC):
def __init__(
self,
ns: str = None,
- robot_name: str = None,
+ robot_model: str = "burger",
+ robot_ns: str = None,
hyperparameter_path: str = DEFAULT_HYPERPARAMETER,
- action_space_path: str = DEFAULT_ACTION_SPACE,
- *args,
- **kwargs,
) -> None:
"""[summary]
Args:
ns (str, optional):
Agent name (directory has to be of the same name). Defaults to None.
- robot_name (str, optional):
+ robot_model (str, optional):
+ Robot model name. Defaults to "burger".
+ robot_ns (str, optional):
Robot specific ROS namespace extension. Defaults to None.
hyperparameter_path (str, optional):
Path to json file containing defined hyperparameters.
Defaults to DEFAULT_HYPERPARAMETER.
- action_space_path (str, optional):
- Path to yaml file containing action space settings.
- Defaults to DEFAULT_ACTION_SPACE.
"""
self._is_train_mode = rospy.get_param("/train_mode")
- self._ns = "" if ns is None or ns == "" else ns + "/"
- self._ns_robot = (
- self._ns if robot_name is None else self._ns + robot_name + "/"
- )
- self._robot_sim_ns = robot_name
+ self._ns = "" if ns is None or not ns else f"{ns}/"
+ self._ns_robot = self._ns if robot_ns is None else self._ns + robot_ns
+ self._robot_sim_ns = robot_ns
+
+ self.robot_model = robot_model
- self.load_hyperparameters(path=hyperparameter_path)
robot_setting_path = os.path.join(
- ROOT_ROBOT_PATH, self.robot_config_name + ".model.yaml"
+ ROOT_ROBOT_PATH, f"{self.robot_model}.model.yaml"
)
+
+ action_space_path = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "action_spaces",
+ f"default_settings_{self.robot_model}.yaml",
+ )
+
+ self.load_hyperparameters(path=hyperparameter_path)
self.read_setting_files(robot_setting_path, action_space_path)
+ # self._check_robot_type_from_params()
+
self.setup_action_space()
self.setup_reward_calculator()
@@ -84,13 +84,13 @@ def __init__(
if self._is_train_mode:
# w/o action publisher node
self._action_pub = rospy.Publisher(
- f"{self._ns_robot}cmd_vel", Twist, queue_size=1
+ f"{self._ns_robot}/cmd_vel", Twist, queue_size=1
)
else:
# w/ action publisher node
# (controls action rate being published on '../cmd_vel')
self._action_pub = rospy.Publisher(
- f"{self._ns_robot}cmd_vel_pub", Twist, queue_size=1
+ f"{self._ns_robot}/cmd_vel_pub", Twist, queue_size=1
)
@abstractmethod
@@ -108,15 +108,13 @@ def load_hyperparameters(self, path: str) -> None:
Args:
path (str): Path to the json file.
"""
- assert os.path.isfile(
- path
- ), f"Hyperparameters file cannot be found at {path}!"
+ assert os.path.isfile(path), f"Hyperparameters file cannot be found at {path}!"
with open(path, "r") as file:
hyperparams = json.load(file)
self._agent_params = hyperparams
- self._get_robot_name_from_params()
+ self.robot_config_name = robot_model
rospy.set_param(
"actions_in_obs",
self._agent_params.get("actions_in_observationspace", False),
@@ -139,7 +137,12 @@ def read_setting_files(
"""
self._num_laser_beams = None
self._laser_range = None
- self._robot_radius = rospy.get_param("radius") * 1.05
+
+ with open(action_space_yaml, "r", encoding="utf-8") as target:
+ config = yaml.load(target, Loader=yaml.FullLoader)
+
+ self._robot_radius = config["robot"]["radius"] * 1.05
+
with open(robot_setting_yaml, "r") as fd:
robot_data = yaml.safe_load(fd)
@@ -151,10 +154,8 @@ def read_setting_files(
laser_angle_increment = plugin["angle"]["increment"]
self._num_laser_beams = int(
round(
- (laser_angle_max - laser_angle_min)
- / laser_angle_increment
+ (laser_angle_max - laser_angle_min) / laser_angle_increment
)
- + 1
)
self._laser_range = plugin["range"]
@@ -187,19 +188,21 @@ def read_setting_files(
],
}
- def _get_robot_name_from_params(self):
+ def _check_robot_type_from_params(self):
"""Retrives the agent-specific robot name from the dictionary loaded\
- from respective 'hyperparameter.json'.
+ from respective 'hyperparameter.json' and compares it to the provided
+ robot model from initialization.
"""
assert self._agent_params and self._agent_params["robot"]
- self.robot_config_name = self._agent_params["robot"]
+ assert self.robot_model == self._agent_params["robot"], (
+ "Robot model in hyperparameter.json is not the same as the parsed model!"
+ f"({self.robot_model} != {self._agent_params['robot']})"
+ )
def setup_action_space(self) -> None:
"""Sets up the action space. (spaces.Box)"""
assert self._discrete_actions or self._cont_actions
- assert (
- self._agent_params and "discrete_action_space" in self._agent_params
- )
+ assert self._agent_params and "discrete_action_space" in self._agent_params
if self._agent_params["discrete_action_space"]:
# self._discrete_actions is a list, each element is a dict with the keys ["name", 'linear','angular']
@@ -298,9 +301,7 @@ def normalize_observations(self, merged_obs: np.ndarray) -> np.ndarray:
Returns:
np.ndarray: Normalized observations array.
"""
- assert self._agent_params["normalize"] and hasattr(
- self, "_obs_norm_func"
- )
+ assert self._agent_params["normalize"] and hasattr(self, "_obs_norm_func")
return self._obs_norm_func(merged_obs)
def get_action(self, obs: np.ndarray) -> np.ndarray:
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/__init__.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/__init__.cpython-38.pyc
index 51c998e79..4cf9b4cbe 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/__init__.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/__init__.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/flatland_gym_env.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/flatland_gym_env.cpython-38.pyc
index 17b7b6ca3..a4ba6ebb8 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/flatland_gym_env.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/__pycache__/flatland_gym_env.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/flatland_gym_env.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/flatland_gym_env.py
index f3fc28340..fb33047ad 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/flatland_gym_env.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/envs/flatland_gym_env.py
@@ -1,10 +1,12 @@
#! /usr/bin/env python3
from operator import is_
+import os
from random import randint
import gym
from gym import spaces
from gym.spaces import space
from typing import Union
+import rospkg
from stable_baselines3.common.env_checker import check_env
import yaml
from rl_agent.utils.observation_collector import ObservationCollector
@@ -68,7 +70,9 @@ def __init__(
ns_int = int(ns.split("_")[1])
time.sleep(ns_int * 2)
except Exception:
- rospy.logwarn(f"Can't not determinate the number of the environment, training script may crash!")
+ rospy.logwarn(
+ f"Can't not determinate the number of the environment, training script may crash!"
+ )
# process specific namespace in ros system
self.ns_prefix = "" if (ns == "" or ns is None) else "/" + ns + "/"
@@ -115,17 +119,25 @@ def __init__(
# action agent publisher
if self._is_train_mode:
- self.agent_action_pub = rospy.Publisher(f"{self.ns_prefix}cmd_vel", Twist, queue_size=1)
+ self.agent_action_pub = rospy.Publisher(
+ f"{self.ns_prefix}cmd_vel", Twist, queue_size=1
+ )
else:
- self.agent_action_pub = rospy.Publisher(f"{self.ns_prefix}cmd_vel_pub", Twist, queue_size=1)
+ self.agent_action_pub = rospy.Publisher(
+ f"{self.ns_prefix}cmd_vel_pub", Twist, queue_size=1
+ )
# service clients
if self._is_train_mode:
self._service_name_step = f"{self.ns_prefix}step_world"
- self._sim_step_client = rospy.ServiceProxy(self._service_name_step, StepWorld)
+ self._sim_step_client = rospy.ServiceProxy(
+ self._service_name_step, StepWorld
+ )
# instantiate task manager
- self.task = get_predefined_task(ns, mode=task_mode, start_stage=kwargs["curr_stage"], PATHS=PATHS)
+ self.task = get_predefined_task(
+ ns, mode=task_mode, start_stage=kwargs["curr_stage"], PATHS=PATHS
+ )
self._steps_curr_episode = 0
self._episode = 0
@@ -153,7 +165,8 @@ def setup_by_configuration(self, robot_yaml_path: str, settings_yaml_path: str):
Args:
robot_yaml_path (str): [description]
"""
- self._robot_radius = rospy.get_param("radius") + 0.25
+ self._robot_radius = yaml.load(settings_yaml_path)["robot"]["radius"] + 0.25
+
with open(robot_yaml_path, "r") as fd:
robot_data = yaml.safe_load(fd)
@@ -163,7 +176,11 @@ def setup_by_configuration(self, robot_yaml_path: str, settings_yaml_path: str):
laser_angle_min = plugin["angle"]["min"]
laser_angle_max = plugin["angle"]["max"]
laser_angle_increment = plugin["angle"]["increment"]
- self._laser_num_beams = int(round((laser_angle_max - laser_angle_min) / laser_angle_increment))
+ self._laser_num_beams = int(
+ round(
+ (laser_angle_max - laser_angle_min) / laser_angle_increment
+ )
+ )
self._laser_max_range = plugin["range"]
with open(settings_yaml_path, "r") as fd:
@@ -173,12 +190,18 @@ def setup_by_configuration(self, robot_yaml_path: str, settings_yaml_path: str):
if self._is_action_space_discrete:
# self._discrete_actions is a list, each element is a dict with the keys ["name", 'linear','angular']
- assert not self._holonomic, "Discrete action space currently not supported for holonomic robots"
+ assert (
+ not self._holonomic
+ ), "Discrete action space currently not supported for holonomic robots"
self._discrete_acitons = setting_data["robot"]["discrete_actions"]
self.action_space = spaces.Discrete(len(self._discrete_acitons))
else:
- linear_range = setting_data["robot"]["continuous_actions"]["linear_range"]
- angular_range = setting_data["robot"]["continuous_actions"]["angular_range"]
+ linear_range = setting_data["robot"]["continuous_actions"][
+ "linear_range"
+ ]
+ angular_range = setting_data["robot"]["continuous_actions"][
+ "angular_range"
+ ]
if not self._holonomic:
self.action_space = spaces.Box(
@@ -218,7 +241,9 @@ def _pub_action(self, action: np.ndarray) -> Twist:
self.agent_action_pub.publish(action_msg)
def _translate_disc_action(self, action):
- assert not self._holonomic, "Discrete action space currently not supported for holonomic robots"
+ assert (
+ not self._holonomic
+ ), "Discrete action space currently not supported for holonomic robots"
new_action = np.array([])
new_action = np.append(new_action, self._discrete_acitons[action]["linear"])
new_action = np.append(new_action, self._discrete_acitons[action]["angular"])
@@ -252,7 +277,9 @@ def step(self, action: np.ndarray):
self._steps_curr_episode += 1
# wait for new observations
- merged_obs, obs_dict = self.observation_collector.get_observations(last_action=self._last_action)
+ merged_obs, obs_dict = self.observation_collector.get_observations(
+ last_action=self._last_action
+ )
self._last_action = action
# calculate reward
@@ -337,7 +364,9 @@ def _update_eval_statistics(self, obs_dict: dict, reward_info: dict):
"""
# distance travelled
if self._last_robot_pose is not None:
- self._distance_travelled += FlatlandEnv.get_distance(self._last_robot_pose, obs_dict["robot_pose"])
+ self._distance_travelled += FlatlandEnv.get_distance(
+ self._last_robot_pose, obs_dict["robot_pose"]
+ )
# collision detector
if "crash" in reward_info:
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/__init__.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/__init__.cpython-38.pyc
index c3d1d140b..69c58f917 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/__init__.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/__init__.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/agent_factory.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/agent_factory.cpython-38.pyc
index 88b5715b0..c5651744e 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/agent_factory.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/agent_factory.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/base_agent.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/base_agent.cpython-38.pyc
index 6702e4224..32cd09bb5 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/base_agent.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/base_agent.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_policy.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_policy.cpython-38.pyc
index 3789ff424..cc886151e 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_policy.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_policy.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_sb3_policy.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_sb3_policy.cpython-38.pyc
index 5ae530396..80b162dc6 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_sb3_policy.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/custom_sb3_policy.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/feature_extractors.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/feature_extractors.cpython-38.pyc
index 3e0d55ca5..f4226141a 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/feature_extractors.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/__pycache__/feature_extractors.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/agent_factory.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/agent_factory.py
index 945f9627d..cd381e86d 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/agent_factory.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/model/agent_factory.py
@@ -1,3 +1,4 @@
+from warnings import warn
from typing import Callable, Type, Union
from stable_baselines3.common.policies import BasePolicy
@@ -24,7 +25,11 @@ def register(cls, name: str) -> Callable:
"""
def inner_wrapper(wrapped_class) -> Callable:
- assert name not in cls.registry, f"Agent '{name}' already exists!"
+ if name in cls.registry:
+ warn(
+ f"Try to register agent: '{name}' although an entry already exists. "
+ "Register entry will be overwritten if there exists another architecture with the same id."
+ )
assert issubclass(wrapped_class, BaseAgent) or issubclass(
wrapped_class, BasePolicy
), f"Wrapped class {wrapped_class.__name__} is neither of type 'BaseAgent' nor 'BasePolicy!'"
@@ -54,7 +59,7 @@ def instantiate(cls, name: str, **kwargs) -> Union[Type[BaseAgent], Type[BasePol
"""
assert name in cls.registry, f"Agent '{name}' is not registered!"
agent_class = cls.registry[name]
-
+
if issubclass(agent_class, BaseAgent):
return agent_class(**kwargs)
else:
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/training_agent_wrapper.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/training_agent_wrapper.py
index cbb49bea1..edbf5746d 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/training_agent_wrapper.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/training_agent_wrapper.py
@@ -8,23 +8,16 @@
from geometry_msgs.msg import Twist
-robot_model = rospy.get_param("model")
-DEFAULT_ACTION_SPACE = os.path.join(
- rospkg.RosPack().get_path("arena_local_planner_drl"),
- "configs",
- f"default_settings_{robot_model}.yaml",
-)
-
class TrainingDRLAgent(BaseDRLAgent):
def __init__(
self,
ns: str,
- robot_name: str,
+ robot_model: str,
+ robot_ns: str,
hyperparameter_path: str,
- action_space_path: str = DEFAULT_ACTION_SPACE,
*args,
- **kwargs
+ **kwargs,
) -> None:
"""DRL Agent Wrapper Class for training.
@@ -51,7 +44,7 @@ def __init__(
Args:
ns (str, optional):
Agent name (directory has to be of the same name). Defaults to None.
- robot_name (str, optional):
+ robot_ns (str, optional):
Robot specific ROS namespace extension. Defaults to None.
hyperparameter_path (str, optional):
Path to json file containing defined hyperparameters.
@@ -62,11 +55,11 @@ def __init__(
"""
super().__init__(
ns=ns,
- robot_name=robot_name,
+ robot_model=robot_model,
+ robot_ns=robot_ns,
hyperparameter_path=hyperparameter_path,
- action_space_path=action_space_path,
*args,
- **kwargs
+ **kwargs,
)
def setup_agent(self) -> None:
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/observation_collector.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/observation_collector.py
index 089b0d908..9a48a07f2 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/observation_collector.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/observation_collector.py
@@ -69,9 +69,7 @@ def __init__(
dtype=np.float32,
),
spaces.Box(low=0, high=15, shape=(1,), dtype=np.float32),
- spaces.Box(
- low=-np.pi, high=np.pi, shape=(1,), dtype=np.float32
- ),
+ spaces.Box(low=-np.pi, high=np.pi, shape=(1,), dtype=np.float32),
)
)
else:
@@ -83,9 +81,7 @@ def __init__(
shape=(num_lidar_beams,),
dtype=np.float32,
),
- spaces.Box(
- low=0, high=15, shape=(1,), dtype=np.float32
- ), # rho
+ spaces.Box(low=0, high=15, shape=(1,), dtype=np.float32), # rho
spaces.Box(
low=-np.pi,
high=np.pi,
@@ -123,9 +119,7 @@ def __init__(
# synchronization parameters
self._ext_time_sync = external_time_sync
- self._first_sync_obs = (
- True # whether to return first sync'd obs or most recent
- )
+ self._first_sync_obs = True # whether to return first sync'd obs or most recent
self.max_deque_size = 10
self._sync_slop = 0.05
@@ -167,18 +161,22 @@ def __init__(
# self._clock_sub = rospy.Subscriber(
# f'{self.ns_prefix}clock', Clock, self.callback_clock, tcp_nodelay=True)
-
+ goal_topic = (
+ f"{self.ns_prefix}subgoal"
+ if rospy.get_param("num_robots", default=1) == 1
+ else f"{self.ns_prefix}goal"
+ )
self._subgoal_sub = rospy.Subscriber(
- f"{self.ns_prefix}subgoal", PoseStamped, self.callback_subgoal
+ goal_topic, PoseStamped, self.callback_subgoal
)
-
self._globalplan_sub = rospy.Subscriber(
f"{self.ns_prefix}globalPlan", Path, self.callback_global_plan
)
# service clients
if self._is_train_mode:
- self._service_name_step = f"{self.ns_prefix}step_world"
+ _sim_namespace = self.ns_prefix.split("/")[1]
+ self._service_name_step = f"/{_sim_namespace}/step_world"
self._sim_step_client = rospy.ServiceProxy(
self._service_name_step, StepWorld
)
@@ -244,9 +242,9 @@ def _get_goal_pose_in_robot_frame(goal_pos: Pose2D, robot_pos: Pose2D):
y_relative = goal_pos.y - robot_pos.y
x_relative = goal_pos.x - robot_pos.x
rho = (x_relative ** 2 + y_relative ** 2) ** 0.5
- theta = (
- np.arctan2(y_relative, x_relative) - robot_pos.theta + 4 * np.pi
- ) % (2 * np.pi) - np.pi
+ theta = (np.arctan2(y_relative, x_relative) - robot_pos.theta + 4 * np.pi) % (
+ 2 * np.pi
+ ) - np.pi
return rho, theta
def get_sync_obs(self):
@@ -315,9 +313,7 @@ def callback_subgoal(self, msg_Subgoal):
return
def callback_global_plan(self, msg_global_plan):
- self._globalplan = ObservationCollector.process_global_plan_msg(
- msg_global_plan
- )
+ self._globalplan = ObservationCollector.process_global_plan_msg(msg_global_plan)
return
def callback_scan(self, msg_laserscan):
@@ -330,9 +326,7 @@ def callback_robot_state(self, msg_robotstate):
self._rs_deque.popleft()
self._rs_deque.append(msg_robotstate)
- def callback_observation_received(
- self, msg_LaserScan, msg_RobotStateStamped
- ):
+ def callback_observation_received(self, msg_LaserScan, msg_RobotStateStamped):
# process sensor msg
self._scan = self.process_scan_msg(msg_LaserScan)
self._robot_pose, self._robot_vel = self.process_robot_state_msg(
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/reward.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/reward.py
index f3c212f9c..89e667d0f 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/reward.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/rl_agent/utils/reward.py
@@ -9,7 +9,7 @@
class RewardCalculator:
def __init__(
self,
- holonomic: float,
+ holonomic: bool,
robot_radius: float,
safe_dist: float,
goal_radius: float,
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/scripts/training/train_agent.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/scripts/training/train_agent.py
index af281c3f3..2a8702901 100755
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/scripts/training/train_agent.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/scripts/training/train_agent.py
@@ -20,6 +20,7 @@
def main():
+ # parse name of training config file
args, _ = parse_training_args()
# in debug mode, we emulate multiprocessing on only one process
@@ -51,11 +52,19 @@ def main():
# when debug run on one process only
if not args.debug and ns_for_nodes:
env = SubprocVecEnv(
- [make_envs(args, ns_for_nodes, i, params=params, PATHS=PATHS) for i in range(args.n_envs)],
+ [
+ make_envs(args, ns_for_nodes, i, params=params, PATHS=PATHS)
+ for i in range(args.n_envs)
+ ],
start_method="fork",
)
else:
- env = DummyVecEnv([make_envs(args, ns_for_nodes, i, params=params, PATHS=PATHS) for i in range(args.n_envs)])
+ env = DummyVecEnv(
+ [
+ make_envs(args, ns_for_nodes, i, params=params, PATHS=PATHS)
+ for i in range(args.n_envs)
+ ]
+ )
# threshold settings for training curriculum
# type can be either 'succ' or 'rew'
@@ -69,7 +78,9 @@ def main():
)
# stop training on reward threshold callback
- stoptraining_cb = StopTrainingOnRewardThreshold(treshhold_type="succ", threshold=0.95, verbose=1)
+ stoptraining_cb = StopTrainingOnRewardThreshold(
+ treshhold_type="succ", threshold=0.95, verbose=1
+ )
# instantiate eval environment
# take task_manager from first sim (currently evaluation only provided for single process)
@@ -113,7 +124,9 @@ def main():
model = PPO(
"MlpPolicy",
env,
- policy_kwargs=dict(net_arch=args.net_arch, activation_fn=get_act_fn(args.act_fn)),
+ policy_kwargs=dict(
+ net_arch=args.net_arch, activation_fn=get_act_fn(args.act_fn)
+ ),
gamma=params["gamma"],
n_steps=params["n_steps"],
ent_coef=params["ent_coef"],
@@ -128,7 +141,9 @@ def main():
verbose=1,
)
elif args.agent is not None:
- agent: Union[Type[BaseAgent], Type[ActorCriticPolicy]] = AgentFactory.instantiate(args.agent)
+ agent: Union[
+ Type[BaseAgent], Type[ActorCriticPolicy]
+ ] = AgentFactory.instantiate(args.agent)
if isinstance(agent, BaseAgent):
model = PPO(
agent.type.value,
@@ -166,7 +181,8 @@ def main():
)
else:
raise TypeError(
- f"Registered agent class {args.agent} is neither of type" "'BaseAgent' or 'ActorCriticPolicy'!"
+ f"Registered agent class {args.agent} is neither of type"
+ "'BaseAgent' or 'ActorCriticPolicy'!"
)
else:
# load flag
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/__init__.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/__init__.cpython-38.pyc
index 3da04eff7..56b64218c 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/__init__.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/__init__.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/argsparser.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/argsparser.cpython-38.pyc
index 047799d7d..fcd858fe1 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/argsparser.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/argsparser.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/staged_train_callback.cpython-38.pyc b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/staged_train_callback.cpython-38.pyc
index 3151cf5ed..69e95e7db 100644
Binary files a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/staged_train_callback.cpython-38.pyc and b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/__pycache__/staged_train_callback.cpython-38.pyc differ
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/argsparser.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/argsparser.py
index 2d1860112..1abe17a76 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/argsparser.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/argsparser.py
@@ -7,59 +7,79 @@
def training_args(parser):
"""program arguments training script"""
- parser.add_argument(
- "--n_envs", type=int, default=1, help="number of parallel environments"
- )
- parser.add_argument(
- "--no-gpu", action="store_true", help="disables gpu for training"
- )
- parser.add_argument(
- "--debug",
- action="store_true",
- help="disables multiprocessing in order to debug",
- )
- group = parser.add_mutually_exclusive_group(required=True)
-
- import rl_agent.model.custom_policy
- import rl_agent.model.custom_sb3_policy
- from rl_agent.model.agent_factory import AgentFactory
-
- group.add_argument(
- "--agent",
- type=str,
- choices=AgentFactory.registry.keys(),
- help="predefined agent to train",
- )
- group.add_argument(
- "--custom-mlp",
- action="store_true",
- help="enables training with custom multilayer perceptron",
- )
- group.add_argument(
- "--load",
- type=str,
- metavar="[agent name]",
- help="agent to be loaded for training",
- )
parser.add_argument(
"--config",
type=str,
metavar="[config name]",
- default="default",
- help="name of the json file containing" "the hyperparameters",
- )
- parser.add_argument(
- "--n", type=int, help="timesteps in total to be generated for training"
- )
- parser.add_argument(
- "-log",
- "--eval_log",
- action="store_true",
- help="enables storage of evaluation data",
- )
- parser.add_argument(
- "--tb", action="store_true", help="enables tensorboard logging"
- )
+ default="training_config.yaml",
+ help="name of the config file",
+ )
+
+ ###########################################################################
+
+ # parser.add_argument(
+ # "--n_envs", type=int, default=1, help="number of parallel environments"
+ # )
+ # parser.add_argument(
+ # "--no-gpu", action="store_true", help="disables gpu for training"
+ # )
+ # parser.add_argument(
+ # "--debug",
+ # action="store_true",
+ # help="disables multiprocessing in order to debug",
+ # )
+ # group = parser.add_mutually_exclusive_group(required=True)
+
+ # import rl_agent.model.custom_policy
+ # import rl_agent.model.custom_sb3_policy
+ # from rl_agent.model.agent_factory import AgentFactory
+
+ # group.add_argument(
+ # "--agent",
+ # type=str,
+ # choices=AgentFactory.registry.keys(),
+ # help="predefined agent to train",
+ # )
+ # group.add_argument(
+ # "--custom-mlp",
+ # action="store_true",
+ # help="enables training with custom multilayer perceptron",
+ # )
+ # group.add_argument(
+ # "--load",
+ # type=str,
+ # metavar="[agent name]",
+ # help="agent to be loaded for training",
+ # )
+ # parser.add_argument(
+ # "--config",
+ # type=str,
+ # metavar="[config name]",
+ # default="default",
+ # help="name of the json file containing" "the hyperparameters",
+ # )
+ # parser.add_argument(
+ # "--n", type=int, help="timesteps in total to be generated for training"
+ # )
+ # parser.add_argument(
+ # "-log",
+ # "--eval_log",
+ # action="store_true",
+ # help="enables storage of evaluation data",
+ # )
+ # parser.add_argument("--tb", action="store_true", help="enables tensorboard logging")
+
+
+# def marl_training_args(parser):
+# parser.add_argument("--robots", type=int, default=1, help="number of robots")
+
+
+# def parse_marl_training_args(args=None, ignore_unknown=False):
+# """parser for training script"""
+# arg_populate_funcs = [training_args, custom_mlp_args, marl_training_args]
+# arg_check_funcs = [process_training_args]
+
+# return parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown)
def run_agent_args(parser):
@@ -143,21 +163,17 @@ def custom_mlp_args(parser):
def process_training_args(parsed_args):
"""argument check function"""
- if parsed_args.no_gpu:
- os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
- if parsed_args.custom_mlp:
- setattr(parsed_args, "net_arch", get_net_arch(parsed_args))
- else:
- if (
- parsed_args.body != ""
- or parsed_args.pi != ""
- or parsed_args.vf != ""
- ):
- print("[custom mlp] arguments will be ignored..")
- delattr(parsed_args, "body")
- delattr(parsed_args, "pi")
- delattr(parsed_args, "vf")
- delattr(parsed_args, "act_fn")
+ # if parsed_args.no_gpu:
+ # os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
+ # if parsed_args.custom_mlp:
+ # setattr(parsed_args, "net_arch", get_net_arch(parsed_args))
+ # else:
+ # if parsed_args.body != "" or parsed_args.pi != "" or parsed_args.vf != "":
+ # print("[custom mlp] arguments will be ignored..")
+ # delattr(parsed_args, "body")
+ # delattr(parsed_args, "pi")
+ # delattr(parsed_args, "vf")
+ # delattr(parsed_args, "act_fn")
def process_run_agent_args(parsed_args):
@@ -170,9 +186,7 @@ def parse_training_args(args=None, ignore_unknown=False):
arg_populate_funcs = [training_args, custom_mlp_args]
arg_check_funcs = [process_training_args]
- return parse_various_args(
- args, arg_populate_funcs, arg_check_funcs, ignore_unknown
- )
+ return parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown)
def parse_run_agent_args(args=None, ignore_unknown=False):
@@ -180,14 +194,10 @@ def parse_run_agent_args(args=None, ignore_unknown=False):
arg_populate_funcs = [run_agent_args]
arg_check_funcs = [process_run_agent_args]
- return parse_various_args(
- args, arg_populate_funcs, arg_check_funcs, ignore_unknown
- )
+ return parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown)
-def parse_various_args(
- args, arg_populate_funcs, arg_check_funcs, ignore_unknown
-):
+def parse_various_args(args, arg_populate_funcs, arg_check_funcs, ignore_unknown):
"""generic arg parsing function"""
parser = argparse.ArgumentParser()
diff --git a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/train_agent_utils.py b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/train_agent_utils.py
index 74c2af519..c7ae5a533 100644
--- a/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/train_agent_utils.py
+++ b/arena_navigation/arena_local_planner/learning_based/arena_local_planner_drl/tools/train_agent_utils.py
@@ -5,6 +5,8 @@
import gym
import json
import os, rospy
+
+import yaml
import rosnode
import rospkg
import time
@@ -83,6 +85,21 @@
}
+def load_config(config_name: str) -> dict:
+ """
+ Load config parameters from config file
+ """
+ config_location = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ config_name + ".yaml",
+ )
+ with open(config_location, "r", encoding="utf-8") as target:
+ config = yaml.load(target, Loader=yaml.FullLoader)
+
+ return config
+
+
def initialize_hyperparameters(
PATHS: dict, load_target: str, config_name: str = "default", n_envs: int = 1
) -> dict:
@@ -117,9 +134,7 @@ def initialize_hyperparameters(
# dynamically adapt n_steps according to batch size and n envs
# then update .json
- check_batch_size(
- n_envs, hyperparams["batch_size"], hyperparams["m_batch_size"]
- )
+ check_batch_size(n_envs, hyperparams["batch_size"], hyperparams["m_batch_size"])
hyperparams["n_steps"] = int(hyperparams["batch_size"] / n_envs)
write_hyperparameters_json(hyperparams, PATHS)
print_hyperparameters(hyperparams)
@@ -151,9 +166,7 @@ def load_hyperparameters_json(
:param config_name: file name of json file when training from scratch
"""
if from_scratch:
- doc_location = os.path.join(
- PATHS.get("hyperparams"), config_name + ".json"
- )
+ doc_location = os.path.join(PATHS.get("hyperparams"), config_name + ".json")
else:
doc_location = os.path.join(PATHS.get("model"), "hyperparameters.json")
@@ -165,8 +178,7 @@ def load_hyperparameters_json(
else:
if from_scratch:
raise FileNotFoundError(
- "Found no '%s.json' in %s"
- % (config_name, PATHS.get("hyperparams"))
+ "Found no '%s.json' in %s" % (config_name, PATHS.get("hyperparams"))
)
else:
raise FileNotFoundError(
@@ -230,9 +242,7 @@ def check_hyperparam_format(loaded_hyperparams: dict, PATHS: dict) -> None:
"actions_in_observationspace" in loaded_hyperparams
and type(loaded_hyperparams["actions_in_observationspace"]) is not bool
):
- raise TypeError(
- "Parameter 'actions_in_observationspace' has to be a boolean!"
- )
+ raise TypeError("Parameter 'actions_in_observationspace' has to be a boolean!")
def update_hyperparam_model(
@@ -336,9 +346,7 @@ def get_paths(agent_name: str, args: argparse.Namespace) -> dict:
PATHS = {
"model": os.path.join(dir, "agents", agent_name),
"tb": os.path.join(dir, "training_logs", "tensorboard", agent_name),
- "eval": os.path.join(
- dir, "training_logs", "train_eval_log", agent_name
- ),
+ "eval": os.path.join(dir, "training_logs", "train_eval_log", agent_name),
"robot_setting": os.path.join(
rospkg.RosPack().get_path("simulator_setup"),
"robot",
@@ -346,10 +354,10 @@ def get_paths(agent_name: str, args: argparse.Namespace) -> dict:
),
"hyperparams": os.path.join(dir, "configs", "hyperparameters"),
"robot_as": os.path.join(
- dir, "configs", f"default_settings_{robot_model}.yaml"
+ dir, "configs", "action_spaces", f"default_settings_{robot_model}.yaml"
),
"curriculum": os.path.join(
- dir, "configs", "training_curriculum_map1small.yaml"
+ dir, "configs", "training_curriculums", "training_curriculum_map1small.yaml"
),
}
# check for mode
@@ -485,9 +493,7 @@ def wait_for_nodes(
from stable_baselines3.common.vec_env.base_vec_env import VecEnv
-def load_vec_normalize(
- params: dict, PATHS: dict, env: VecEnv, eval_env: VecEnv
-):
+def load_vec_normalize(params: dict, PATHS: dict, env: VecEnv, eval_env: VecEnv):
if params["normalize"]:
load_path = os.path.join(PATHS["model"], "vec_normalize.pkl")
if os.path.isfile(load_path):
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 000000000..3ebb20867
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,3024 @@
+[[package]]
+name = "absl-py"
+version = "1.0.0"
+description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "actionlib"
+version = "1.12.0"
+description = "The actionlib stack provides a standardized interface for interfacing with preemptable tasks. Examples of this include moving the base to a target location, performing a laser scan and retu..."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "actionlib-msgs"
+version = "1.13.0.post3"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genpy = ">=0.6.14,<2000"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "appnope"
+version = "0.1.2"
+description = "Disable App Nap on macOS >= 10.9"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "argon2-cffi"
+version = "21.1.0"
+description = "The secure Argon2 password hashing algorithm."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+cffi = ">=1.0.0"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "furo", "wheel", "pre-commit"]
+docs = ["sphinx", "furo"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
+
+[[package]]
+name = "attrs"
+version = "21.2.0"
+description = "Classes Without Boilerplate"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+
+[[package]]
+name = "backcall"
+version = "0.2.0"
+description = "Specifications for callback functions passed in to an API"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "black"
+version = "22.3.0"
+description = "The uncompromising code formatter."
+category = "dev"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
+typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "bleach"
+version = "4.1.0"
+description = "An easy safelist-based HTML-sanitizing tool."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+packaging = "*"
+six = ">=1.9.0"
+webencodings = "*"
+
+[[package]]
+name = "cachetools"
+version = "4.2.4"
+description = "Extensible memoizing collections and decorators"
+category = "main"
+optional = false
+python-versions = "~=3.5"
+
+[[package]]
+name = "catkin"
+version = "0.7.18"
+description = "Low-level build system macros and infrastructure for ROS."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "catkin-pkg"
+version = "0.4.24"
+description = "catkin package library"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+docutils = "*"
+pyparsing = "*"
+python-dateutil = "*"
+
+[[package]]
+name = "certifi"
+version = "2021.10.8"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cffi"
+version = "1.15.0"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.0.7"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
+
+[[package]]
+name = "click"
+version = "8.1.3"
+description = "Composable command line interface toolkit"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "cloudpickle"
+version = "2.0.0"
+description = "Extended pickling support for Python objects"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "colorama"
+version = "0.4.4"
+description = "Cross-platform colored terminal text."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "cycler"
+version = "0.11.0"
+description = "Composable style cycles"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "debugpy"
+version = "1.5.1"
+description = "An implementation of the Debug Adapter Protocol for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
+
+[[package]]
+name = "decorator"
+version = "5.1.0"
+description = "Decorators for Humans"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "defusedxml"
+version = "0.7.1"
+description = "XML bomb protection for Python stdlib modules"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "distro"
+version = "1.6.0"
+description = "Distro - an OS platform information API"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "docutils"
+version = "0.18"
+description = "Docutils -- Python Documentation Utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "empy"
+version = "3.3.4"
+description = "A templating system for Python."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "entrypoints"
+version = "0.3"
+description = "Discover and load entry points from installed packages."
+category = "main"
+optional = false
+python-versions = ">=2.7"
+
+[[package]]
+name = "filelock"
+version = "3.3.2"
+description = "A platform independent file lock."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
+testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
+
+[[package]]
+name = "genmsg"
+version = "0.5.12"
+description = "Standalone Python library for generating ROS message and service data structures for various languages."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "genpy"
+version = "0.6.14"
+description = "Python ROS message and service generators."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genmsg = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "geometry-msgs"
+version = "1.13.0.post2"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genpy = ">=0.6.14,<2000"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "gnupg"
+version = "2.3.1"
+description = "A Python wrapper for GnuPG"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+psutil = ">=1.2.1"
+
+[package.extras]
+docs = ["Sphinx (>=1.1)", "sphinxcontrib-fulltoc (==1.0)"]
+
+[[package]]
+name = "google-auth"
+version = "2.3.3"
+description = "Google Authentication Library"
+category = "main"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
+
+[package.dependencies]
+cachetools = ">=2.0.0,<5.0"
+pyasn1-modules = ">=0.2.1"
+rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""}
+six = ">=1.9.0"
+
+[package.extras]
+aiohttp = ["requests (>=2.20.0,<3.0.0dev)", "aiohttp (>=3.6.2,<4.0.0dev)"]
+pyopenssl = ["pyopenssl (>=20.0.0)"]
+reauth = ["pyu2f (>=0.1.5)"]
+
+[[package]]
+name = "google-auth-oauthlib"
+version = "0.4.6"
+description = "Google Authentication Library"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+google-auth = ">=1.0.0"
+requests-oauthlib = ">=0.7.0"
+
+[package.extras]
+tool = ["click (>=6.0.0)"]
+
+[[package]]
+name = "grpcio"
+version = "1.41.1"
+description = "HTTP/2-based RPC framework"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = ">=1.5.2"
+
+[package.extras]
+protobuf = ["grpcio-tools (>=1.41.1)"]
+
+[[package]]
+name = "gym"
+version = "0.21.0"
+description = "Gym: A universal API for reinforcement learning environments."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+cloudpickle = ">=1.2.0"
+numpy = ">=1.18.0"
+
+[package.extras]
+accept-rom-license = ["autorom[accept-rom-license] (>=0.4.2,<0.5.0)"]
+all = ["mujoco_py (>=1.50,<2.0)", "lz4 (>=3.1.0)", "opencv-python (>=3)", "ale-py (>=0.7.1,<0.8.0)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)", "box2d-py (==2.3.5)", "pyglet (>=1.4.0)", "ale-py (>=0.7.1,<0.8.0)", "lz4 (>=3.1.0)", "opencv-python (>=3)", "pyglet (>=1.4.0)", "box2d-py (==2.3.5)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)", "mujoco_py (>=1.50,<2.0)"]
+atari = ["ale-py (>=0.7.1,<0.8.0)"]
+box2d = ["box2d-py (==2.3.5)", "pyglet (>=1.4.0)"]
+classic_control = ["pyglet (>=1.4.0)"]
+mujoco = ["mujoco_py (>=1.50,<2.0)"]
+nomujoco = ["lz4 (>=3.1.0)", "opencv-python (>=3)", "ale-py (>=0.7.1,<0.8.0)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)", "box2d-py (==2.3.5)", "pyglet (>=1.4.0)"]
+other = ["lz4 (>=3.1.0)", "opencv-python (>=3)"]
+robotics = ["mujoco_py (>=1.50,<2.0)"]
+toy_text = ["scipy (>=1.4.1)"]
+
+[[package]]
+name = "idna"
+version = "3.3"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "importlib-resources"
+version = "5.4.0"
+description = "Read resources from Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[[package]]
+name = "ipykernel"
+version = "6.5.0"
+description = "IPython Kernel for Jupyter"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+appnope = {version = "*", markers = "platform_system == \"Darwin\""}
+debugpy = ">=1.0.0,<2.0"
+ipython = ">=7.23.1,<8.0"
+jupyter-client = "<8.0"
+matplotlib-inline = ">=0.1.0,<0.2.0"
+tornado = ">=4.2,<7.0"
+traitlets = ">=5.1.0,<6.0"
+
+[package.extras]
+test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose", "ipyparallel"]
+
+[[package]]
+name = "ipython"
+version = "7.29.0"
+description = "IPython: Productive Interactive Computing"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+appnope = {version = "*", markers = "sys_platform == \"darwin\""}
+backcall = "*"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+decorator = "*"
+jedi = ">=0.16"
+matplotlib-inline = "*"
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
+pickleshare = "*"
+prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
+pygments = "*"
+traitlets = ">=4.2"
+
+[package.extras]
+all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
+doc = ["Sphinx (>=1.3)"]
+kernel = ["ipykernel"]
+nbconvert = ["nbconvert"]
+nbformat = ["nbformat"]
+notebook = ["notebook", "ipywidgets"]
+parallel = ["ipyparallel"]
+qtconsole = ["qtconsole"]
+test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
+
+[[package]]
+name = "ipython-genutils"
+version = "0.2.0"
+description = "Vestigial utilities from IPython"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "ipywidgets"
+version = "7.6.5"
+description = "IPython HTML widgets for Jupyter"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ipykernel = ">=4.5.1"
+ipython = {version = ">=4.0.0", markers = "python_version >= \"3.3\""}
+ipython-genutils = ">=0.2.0,<0.3.0"
+jupyterlab-widgets = {version = ">=1.0.0", markers = "python_version >= \"3.6\""}
+nbformat = ">=4.2.0"
+traitlets = ">=4.3.1"
+widgetsnbextension = ">=3.5.0,<3.6.0"
+
+[package.extras]
+test = ["pytest (>=3.6.0)", "pytest-cov", "mock"]
+
+[[package]]
+name = "jedi"
+version = "0.18.0"
+description = "An autocompletion tool for Python that can be used for text editors."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+parso = ">=0.8.0,<0.9.0"
+
+[package.extras]
+qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
+testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"]
+
+[[package]]
+name = "jinja2"
+version = "3.0.3"
+description = "A very fast and expressive template engine."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "jsonschema"
+version = "4.2.1"
+description = "An implementation of JSON Schema validation for Python"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+attrs = ">=17.4.0"
+importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""}
+pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
+
+[package.extras]
+format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
+format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
+
+[[package]]
+name = "jupyter"
+version = "1.0.0"
+description = "Jupyter metapackage. Install all the Jupyter components in one go."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ipykernel = "*"
+ipywidgets = "*"
+jupyter-console = "*"
+nbconvert = "*"
+notebook = "*"
+qtconsole = "*"
+
+[[package]]
+name = "jupyter-client"
+version = "7.0.6"
+description = "Jupyter protocol implementation and client libraries"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+entrypoints = "*"
+jupyter-core = ">=4.6.0"
+nest-asyncio = ">=1.5"
+python-dateutil = ">=2.1"
+pyzmq = ">=13"
+tornado = ">=4.1"
+traitlets = "*"
+
+[package.extras]
+doc = ["myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
+test = ["codecov", "coverage", "ipykernel", "ipython", "mock", "mypy", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov", "pytest-timeout", "jedi (<0.18)"]
+
+[[package]]
+name = "jupyter-console"
+version = "6.4.0"
+description = "Jupyter terminal console"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+ipykernel = "*"
+ipython = "*"
+jupyter-client = "*"
+prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
+pygments = "*"
+
+[package.extras]
+test = ["pexpect"]
+
+[[package]]
+name = "jupyter-core"
+version = "4.9.1"
+description = "Jupyter core package. A base package on which Jupyter projects rely."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""}
+traitlets = "*"
+
+[[package]]
+name = "jupyterlab-pygments"
+version = "0.1.2"
+description = "Pygments theme using JupyterLab CSS variables"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pygments = ">=2.4.1,<3"
+
+[[package]]
+name = "jupyterlab-widgets"
+version = "1.0.2"
+description = "A JupyterLab extension."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "kiwisolver"
+version = "1.3.2"
+description = "A fast implementation of the Cassowary constraint solver"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "mako"
+version = "1.1.5"
+description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+MarkupSafe = ">=0.9.2"
+
+[package.extras]
+babel = ["babel"]
+lingua = ["lingua"]
+
+[[package]]
+name = "markdown"
+version = "3.3.4"
+description = "Python implementation of Markdown."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+testing = ["coverage", "pyyaml"]
+
+[[package]]
+name = "markupsafe"
+version = "2.0.1"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "matplotlib"
+version = "3.4.3"
+description = "Python plotting package"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+cycler = ">=0.10"
+kiwisolver = ">=1.0.1"
+numpy = ">=1.16"
+pillow = ">=6.2.0"
+pyparsing = ">=2.2.1"
+python-dateutil = ">=2.7"
+
+[[package]]
+name = "matplotlib-inline"
+version = "0.1.3"
+description = "Inline Matplotlib backend for Jupyter"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+traitlets = "*"
+
+[[package]]
+name = "mistune"
+version = "0.8.4"
+description = "The fastest markdown parser in pure Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "mypy-extensions"
+version = "0.4.3"
+description = "Experimental type system extensions for programs checked with the mypy typechecker."
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "nbclient"
+version = "0.5.8"
+description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+jupyter-client = ">=6.1.5"
+nbformat = ">=5.0"
+nest-asyncio = "*"
+traitlets = ">=4.2"
+
+[package.extras]
+dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"]
+sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"]
+test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"]
+
+[[package]]
+name = "nbconvert"
+version = "6.3.0"
+description = "Converting Jupyter Notebooks"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+bleach = "*"
+defusedxml = "*"
+entrypoints = ">=0.2.2"
+jinja2 = ">=2.4"
+jupyter-core = "*"
+jupyterlab-pygments = "*"
+mistune = ">=0.8.1,<2"
+nbclient = ">=0.5.0,<0.6.0"
+nbformat = ">=4.4"
+pandocfilters = ">=1.4.1"
+pygments = ">=2.4.1"
+testpath = "*"
+traitlets = ">=5.0"
+
+[package.extras]
+all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.6)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
+docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
+serve = ["tornado (>=4.0)"]
+test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.6)"]
+webpdf = ["pyppeteer (==0.2.6)"]
+
+[[package]]
+name = "nbformat"
+version = "5.1.3"
+description = "The Jupyter Notebook format"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+ipython-genutils = "*"
+jsonschema = ">=2.4,<2.5.0 || >2.5.0"
+jupyter-core = "*"
+traitlets = ">=4.1"
+
+[package.extras]
+fast = ["fastjsonschema"]
+test = ["check-manifest", "fastjsonschema", "testpath", "pytest", "pytest-cov"]
+
+[[package]]
+name = "nest-asyncio"
+version = "1.5.1"
+description = "Patch asyncio to allow nested event loops"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "netifaces"
+version = "0.10.9"
+description = "Portable network interface information."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "notebook"
+version = "6.4.5"
+description = "A web-based notebook environment for interactive computing"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+argon2-cffi = "*"
+ipykernel = "*"
+ipython-genutils = "*"
+jinja2 = "*"
+jupyter-client = ">=5.3.4"
+jupyter-core = ">=4.6.1"
+nbconvert = "*"
+nbformat = "*"
+prometheus-client = "*"
+pyzmq = ">=17"
+Send2Trash = ">=1.5.0"
+terminado = ">=0.8.3"
+tornado = ">=6.1"
+traitlets = ">=4.2.1"
+
+[package.extras]
+docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme", "myst-parser"]
+json-logging = ["json-logging"]
+test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "requests-unixsocket"]
+
+[[package]]
+name = "numpy"
+version = "1.21.4"
+description = "NumPy is the fundamental package for array computing with Python."
+category = "main"
+optional = false
+python-versions = ">=3.7,<3.11"
+
+[[package]]
+name = "oauthlib"
+version = "3.1.1"
+description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+rsa = ["cryptography (>=3.0.0,<4)"]
+signals = ["blinker (>=1.4.0)"]
+signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"]
+
+[[package]]
+name = "opencv-python"
+version = "3.4.16.57"
+description = "Wrapper package for OpenCV python bindings."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+numpy = ">=1.21.2"
+
+[[package]]
+name = "packaging"
+version = "21.0"
+description = "Core utilities for Python packages"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+pyparsing = ">=2.0.2"
+
+[[package]]
+name = "pandas"
+version = "1.3.4"
+description = "Powerful data structures for data analysis, time series, and statistics"
+category = "main"
+optional = false
+python-versions = ">=3.7.1"
+
+[package.dependencies]
+numpy = [
+ {version = ">=1.17.3", markers = "platform_machine != \"aarch64\" and platform_machine != \"arm64\" and python_version < \"3.10\""},
+ {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""},
+ {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""},
+]
+python-dateutil = ">=2.7.3"
+pytz = ">=2017.3"
+
+[package.extras]
+test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"]
+
+[[package]]
+name = "pandocfilters"
+version = "1.5.0"
+description = "Utilities for writing pandoc filters in python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "parso"
+version = "0.8.2"
+description = "A Python Parser"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
+testing = ["docopt", "pytest (<6.0.0)"]
+
+[[package]]
+name = "pathlib"
+version = "1.0.1"
+description = "Object-oriented filesystem paths"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pathspec"
+version = "0.9.0"
+description = "Utility library for gitignore style pattern matching of file paths."
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[[package]]
+name = "pdoc3"
+version = "0.10.0"
+description = "Auto-generate API documentation for Python projects."
+category = "dev"
+optional = false
+python-versions = ">= 3.6"
+
+[package.dependencies]
+mako = "*"
+markdown = ">=3.0"
+
+[[package]]
+name = "pettingzoo"
+version = "1.13.1"
+description = "Gym for multi-agent reinforcement learning"
+category = "main"
+optional = false
+python-versions = ">=3.7, <3.10"
+
+[package.dependencies]
+gym = ">=0.21.0"
+numpy = ">=1.18.0"
+
+[package.extras]
+all = ["multi_agent_ale_py (==0.1.11)", "pygame (==2.0.0)", "chess (==1.7.0)", "rlcard (==1.0.4)", "pygame (==2.0.0)", "hanabi_learning_environment (==0.0.1)", "pygame (==2.0.0)", "pymunk (==6.2.0)", "magent (==0.1.14)", "pyglet (>=1.4.0)", "pygame (==2.0.0)", "box2d-py (==2.3.5)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)", "pillow (>=8.0.1)"]
+atari = ["multi_agent_ale_py (==0.1.11)", "pygame (==2.0.0)"]
+butterfly = ["pygame (==2.0.0)", "pymunk (==6.2.0)"]
+classic = ["chess (==1.7.0)", "rlcard (==1.0.4)", "pygame (==2.0.0)", "hanabi_learning_environment (==0.0.1)"]
+magent = ["magent (==0.1.14)"]
+mpe = ["pyglet (>=1.4.0)"]
+other = ["pillow (>=8.0.1)"]
+sisl = ["pygame (==2.0.0)", "box2d-py (==2.3.5)", "pyglet (>=1.4.0)", "scipy (>=1.4.1)"]
+tests = ["pynput"]
+
+[[package]]
+name = "pexpect"
+version = "4.8.0"
+description = "Pexpect allows easy control of interactive console applications."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ptyprocess = ">=0.5"
+
+[[package]]
+name = "pickleshare"
+version = "0.7.5"
+description = "Tiny 'shelve'-like database with concurrency support"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pillow"
+version = "8.4.0"
+description = "Python Imaging Library (Fork)"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "platformdirs"
+version = "2.5.2"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
+test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
+
+[[package]]
+name = "prometheus-client"
+version = "0.12.0"
+description = "Python client for the Prometheus monitoring system."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+twisted = ["twisted"]
+
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.22"
+description = "Library for building powerful interactive command lines in Python"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+wcwidth = "*"
+
+[[package]]
+name = "protobuf"
+version = "3.19.1"
+description = "Protocol Buffers"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "psutil"
+version = "5.8.0"
+description = "Cross-platform lib for process and system monitoring in Python."
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
+
+[[package]]
+name = "ptyprocess"
+version = "0.7.0"
+description = "Run a subprocess in a pseudo terminal"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "py"
+version = "1.11.0"
+description = "library with cross-python path, ini-parsing, io, code, log facilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pyasn1"
+version = "0.4.8"
+description = "ASN.1 types and codecs"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pyasn1-modules"
+version = "0.2.8"
+description = "A collection of ASN.1-based protocols modules."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pyasn1 = ">=0.4.6,<0.5.0"
+
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pycryptodome"
+version = "3.11.0"
+description = "Cryptographic library for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pycryptodomex"
+version = "3.11.0"
+description = "Cryptographic library for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "pygments"
+version = "2.10.0"
+description = "Pygments is a syntax highlighting package written in Python."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "pyparsing"
+version = "3.0.5"
+description = "Python parsing module"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+diagrams = ["jinja2", "railroad-diagrams"]
+
+[[package]]
+name = "pyqt5"
+version = "5.15.6"
+description = "Python bindings for the Qt cross platform application toolkit"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+PyQt5-Qt5 = ">=5.15.2"
+PyQt5-sip = ">=12.8,<13"
+
+[[package]]
+name = "pyqt5-qt5"
+version = "5.15.2"
+description = "The subset of a Qt installation needed by PyQt5."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pyqt5-sip"
+version = "12.9.0"
+description = "The sip module support for PyQt5"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "pyrsistent"
+version = "0.18.0"
+description = "Persistent/Functional/Immutable data structures"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.2"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "pytz"
+version = "2021.3"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pywin32"
+version = "302"
+description = "Python for Window Extensions"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pywinpty"
+version = "1.1.5"
+description = "Pseudo terminal support for Windows from Python."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "pyyaml"
+version = "5.4.1"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[[package]]
+name = "pyzmq"
+version = "22.3.0"
+description = "Python bindings for 0MQ"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+cffi = {version = "*", markers = "implementation_name == \"pypy\""}
+py = {version = "*", markers = "implementation_name == \"pypy\""}
+
+[[package]]
+name = "qtconsole"
+version = "5.2.0"
+description = "Jupyter Qt console"
+category = "main"
+optional = false
+python-versions = ">= 3.6"
+
+[package.dependencies]
+ipykernel = ">=4.1"
+ipython-genutils = "*"
+jupyter-client = ">=4.1"
+jupyter-core = "*"
+pygments = "*"
+pyzmq = ">=17.1"
+qtpy = "*"
+traitlets = "*"
+
+[package.extras]
+doc = ["Sphinx (>=1.3)"]
+test = ["flaky", "pytest", "pytest-qt"]
+
+[[package]]
+name = "qtpy"
+version = "1.11.2"
+description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets."
+category = "main"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
+
+[[package]]
+name = "requests"
+version = "2.26.0"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
+
+[[package]]
+name = "requests-oauthlib"
+version = "1.3.0"
+description = "OAuthlib authentication support for Requests."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+oauthlib = ">=3.0.0"
+requests = ">=2.0.0"
+
+[package.extras]
+rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
+
+[[package]]
+name = "rosbag"
+version = "1.15.11"
+description = "This is a set of tools for recording from and playing back to ROS topics. It is intended to be high performance and avoids deserialization and reserialization of the messages."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genmsg = "*"
+genpy = "<2000"
+gnupg = "*"
+pycryptodome = "*"
+pycryptodomex = "*"
+roslib = "*"
+rospkg = "*"
+rospy = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "roscpp"
+version = "1.15.11"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genpy = ">=0.6.14,<2000"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "rosgraph"
+version = "1.15.11"
+description = "rosgraph contains the rosgraph command-line tool, which prints information about the ROS Computation Graph. It also provides an internal library that can be used by graphical tools."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+rospkg = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "rosgraph-msgs"
+version = "1.11.3.post2"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genpy = ">=0.6.14,<2000"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "roslib"
+version = "1.14.7.post0"
+description = "Base dependencies and support libraries for ROS. roslib contains many of the common data structures and tools that are shared across ROS client library implementations."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+catkin = "*"
+rospkg = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "rospkg"
+version = "1.3.0"
+description = "ROS package library"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+catkin-pkg = "*"
+distro = "*"
+PyYAML = "*"
+
+[[package]]
+name = "rospy"
+version = "1.15.11"
+description = "rospy is a pure Python client library for ROS. The rospy client API enables Python programmers to quickly interface with ROS Topics, =3.5, <4"
+
+[package.dependencies]
+pyasn1 = ">=0.1.3"
+
+[[package]]
+name = "scipy"
+version = "1.7.2"
+description = "SciPy: Scientific Library for Python"
+category = "main"
+optional = false
+python-versions = ">=3.7,<3.11"
+
+[package.dependencies]
+numpy = ">=1.16.5,<1.23.0"
+
+[[package]]
+name = "send2trash"
+version = "1.8.0"
+description = "Send file to trash natively under Mac OS X, Windows and Linux."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+nativelib = ["pyobjc-framework-cocoa", "pywin32"]
+objc = ["pyobjc-framework-cocoa"]
+win32 = ["pywin32"]
+
+[[package]]
+name = "sensor-msgs"
+version = "1.13.0.post3"
+description = "This package defines messages for commonly used sensors, including cameras and scanning laser rangefinders."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "stable-baselines3"
+version = "1.1.0a1"
+description = "Pytorch version of Stable Baselines, implementations of reinforcement learning algorithms."
+category = "main"
+optional = false
+python-versions = "*"
+develop = false
+
+[package.dependencies]
+cloudpickle = "*"
+gym = ">=0.17"
+matplotlib = "*"
+numpy = "*"
+pandas = "*"
+torch = ">=1.4.0"
+
+[package.extras]
+docs = ["sphinx", "sphinx-autobuild", "sphinx-rtd-theme", "sphinxcontrib.spelling", "sphinx-autodoc-typehints"]
+extra = ["opencv-python", "atari-py (>=0.2.0,<0.3.0)", "pillow", "tensorboard (>=2.2.0)", "psutil"]
+tests = ["pytest", "pytest-cov", "pytest-env", "pytest-xdist", "pytype", "flake8 (>=3.8)", "flake8-bugbear", "isort (>=5.0)", "black"]
+
+[package.source]
+type = "git"
+url = "https://github.com/ignc-research/stable-baselines3"
+reference = "marl"
+resolved_reference = "323a3730f2cc6674332f47f8ae7bb5094a1909ed"
+
+[[package]]
+name = "std-msgs"
+version = "0.5.13.post0"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genpy = ">=0.6.14,<2000"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "supersuit"
+version = "3.3.1"
+description = "Wrappers for Gym and PettingZoo"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+opencv-python = ">=3.4.0,<3.5.0"
+pettingzoo = ">=1.13.1"
+
+[[package]]
+name = "tensorboard"
+version = "2.7.0"
+description = "TensorBoard lets you watch Tensors Flow"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+absl-py = ">=0.4"
+google-auth = ">=1.6.3,<3"
+google-auth-oauthlib = ">=0.4.1,<0.5"
+grpcio = ">=1.24.3"
+markdown = ">=2.6.8"
+numpy = ">=1.12.0"
+protobuf = ">=3.6.0"
+requests = ">=2.21.0,<3"
+tensorboard-data-server = ">=0.6.0,<0.7.0"
+tensorboard-plugin-wit = ">=1.6.0"
+werkzeug = ">=0.11.15"
+
+[[package]]
+name = "tensorboard-data-server"
+version = "0.6.1"
+description = "Fast data loading for TensorBoard"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "tensorboard-plugin-wit"
+version = "1.8.0"
+description = "What-If Tool TensorBoard plugin."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "terminado"
+version = "0.12.1"
+description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+ptyprocess = {version = "*", markers = "os_name != \"nt\""}
+pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""}
+tornado = ">=4"
+
+[package.extras]
+test = ["pytest"]
+
+[[package]]
+name = "testpath"
+version = "0.5.0"
+description = "Test utilities for code working with files and commands"
+category = "main"
+optional = false
+python-versions = ">= 3.5"
+
+[package.extras]
+test = ["pytest", "pathlib2"]
+
+[[package]]
+name = "tf"
+version = "1.12.1.post3"
+description = "tf is a package that lets the user keep track of multiple coordinate"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genmsg = "*"
+genpy = "<2000"
+geometry-msgs = "*"
+roslib = "*"
+rospkg = "*"
+sensor-msgs = "*"
+std-msgs = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "tf2-msgs"
+version = "0.7.2.post3"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+genpy = ">=0.6.14,<2000"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "tf2-py"
+version = "0.6.5.post1"
+description = ""
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+geometry_msgs = "*"
+rospy = "*"
+tf2_msgs = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "tf2-ros"
+version = "0.6.5"
+description = "This package contains the ROS bindings for the tf2 library, for both Python and C++."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+actionlib = "*"
+actionlib-msgs = "*"
+geometry-msgs = "*"
+rospy = "*"
+tf2-msgs = "*"
+tf2-py = "*"
+
+[package.source]
+type = "legacy"
+url = "https://rospypi.github.io/simple"
+reference = "ros"
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "torch"
+version = "1.10.0"
+description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
+category = "main"
+optional = false
+python-versions = ">=3.6.2"
+
+[package.dependencies]
+typing-extensions = "*"
+
+[[package]]
+name = "tornado"
+version = "6.1"
+description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
+category = "main"
+optional = false
+python-versions = ">= 3.5"
+
+[[package]]
+name = "traitlets"
+version = "5.1.1"
+description = "Traitlets Python configuration system"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+test = ["pytest"]
+
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.2"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "urllib3"
+version = "1.26.7"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[[package]]
+name = "wcwidth"
+version = "0.2.5"
+description = "Measures the displayed width of unicode strings in a terminal"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "webencodings"
+version = "0.5.1"
+description = "Character encoding aliases for legacy web content"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "werkzeug"
+version = "2.0.2"
+description = "The comprehensive WSGI web application library."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+watchdog = ["watchdog"]
+
+[[package]]
+name = "widgetsnbextension"
+version = "3.5.2"
+description = "IPython HTML widgets for Jupyter"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+notebook = ">=4.4.1"
+
+[[package]]
+name = "zipp"
+version = "3.6.0"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
+
+[metadata]
+lock-version = "1.1"
+python-versions = ">=3.8,<3.10"
+content-hash = "41e16c86ccb17760d49ec243797e45b9bca7c7f7bfe3fc4077726c07a6a8547c"
+
+[metadata.files]
+absl-py = [
+ {file = "absl-py-1.0.0.tar.gz", hash = "sha256:ac511215c01ee9ae47b19716599e8ccfa746f2e18de72bdf641b79b22afa27ea"},
+ {file = "absl_py-1.0.0-py3-none-any.whl", hash = "sha256:84e6dcdc69c947d0c13e5457d056bd43cade4c2393dce00d684aedea77ddc2a3"},
+]
+actionlib = []
+actionlib-msgs = []
+appnope = [
+ {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
+ {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
+]
+argon2-cffi = [
+ {file = "argon2-cffi-21.1.0.tar.gz", hash = "sha256:f710b61103d1a1f692ca3ecbd1373e28aa5e545ac625ba067ff2feca1b2bb870"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-macosx_10_14_x86_64.whl", hash = "sha256:217b4f0f853ccbbb5045242946ad2e162e396064575860141b71a85eb47e475a"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa7e7d1fc22514a32b1761fdfa1882b6baa5c36bb3ef557bdd69e6fc9ba14a41"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-win32.whl", hash = "sha256:e4d8f0ae1524b7b0372a3e574a2561cbdddb3fdb6c28b70a72868189bda19659"},
+ {file = "argon2_cffi-21.1.0-cp35-abi3-win_amd64.whl", hash = "sha256:65213a9174320a1aee03fe826596e0620783966b49eb636955958b3074e87ff9"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-macosx_10_7_x86_64.whl", hash = "sha256:245f64a203012b144b7b8c8ea6d468cb02b37caa5afee5ba4a10c80599334f6a"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4ad152c418f7eb640eac41ac815534e6aa61d1624530b8e7779114ecfbf327f8"},
+ {file = "argon2_cffi-21.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:bc513db2283c385ea4da31a2cd039c33380701f376f4edd12fe56db118a3b21a"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:c7a7c8cc98ac418002090e4add5bebfff1b915ea1cb459c578cd8206fef10378"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:165cadae5ac1e26644f5ade3bd9c18d89963be51d9ea8817bd671006d7909057"},
+ {file = "argon2_cffi-21.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:566ffb581bbd9db5562327aee71b2eda24a1c15b23a356740abe3c011bbe0dcb"},
+]
+attrs = [
+ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+]
+backcall = [
+ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
+ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
+]
+black = [
+ {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"},
+ {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"},
+ {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"},
+ {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"},
+ {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"},
+ {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"},
+ {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"},
+ {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"},
+ {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"},
+ {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"},
+ {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"},
+ {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"},
+ {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"},
+ {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"},
+ {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"},
+ {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"},
+ {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"},
+ {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"},
+ {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"},
+ {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"},
+ {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"},
+ {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
+ {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
+]
+bleach = [
+ {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"},
+ {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"},
+]
+cachetools = [
+ {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"},
+ {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"},
+]
+catkin = []
+catkin-pkg = [
+ {file = "catkin_pkg-0.4.24-py3-none-any.whl", hash = "sha256:6ae0dddcde95689266e91dac13e1d8e9cdec4739bc7e2037b14cbcc1d7af96b2"},
+ {file = "catkin_pkg-0.4.24.tar.gz", hash = "sha256:f26d22cc5d8cb54f681f13fec4d06637b4983d493aa054f8e69ba888d632c6b4"},
+]
+certifi = [
+ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
+ {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
+]
+cffi = [
+ {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
+ {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
+ {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
+ {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
+ {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
+ {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
+ {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
+ {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
+ {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
+ {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
+ {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
+ {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
+ {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
+ {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
+ {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
+ {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
+ {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
+ {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
+ {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
+ {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
+ {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
+ {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
+ {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
+ {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
+ {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
+ {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
+]
+charset-normalizer = [
+ {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
+ {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
+]
+click = [
+ {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
+ {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
+]
+cloudpickle = [
+ {file = "cloudpickle-2.0.0-py3-none-any.whl", hash = "sha256:6b2df9741d06f43839a3275c4e6632f7df6487a1f181f5f46a052d3c917c3d11"},
+ {file = "cloudpickle-2.0.0.tar.gz", hash = "sha256:5cd02f3b417a783ba84a4ec3e290ff7929009fe51f6405423cfccfadd43ba4a4"},
+]
+colorama = [
+ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+]
+cycler = [
+ {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"},
+ {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"},
+]
+debugpy = [
+ {file = "debugpy-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:70b422c63a833630c33e3f9cdbd9b6971f8c5afd452697e464339a21bbe862ba"},
+ {file = "debugpy-1.5.1-cp310-cp310-win32.whl", hash = "sha256:3a457ad9c0059a21a6c7d563c1f18e924f5cf90278c722bd50ede6f56b77c7fe"},
+ {file = "debugpy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:5d76a4fd028d8009c3faf1185b4b78ceb2273dd2499447664b03939e0368bb90"},
+ {file = "debugpy-1.5.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:16db27b4b91991442f91d73604d32080b30de655aca9ba821b1972ea8171021b"},
+ {file = "debugpy-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b073ad5e8d8c488fbb6a116986858bab0c9c4558f28deb8832c7a5a27405bd6"},
+ {file = "debugpy-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:318f81f37341e4e054b4267d39896b73cddb3612ca13b39d7eea45af65165e1d"},
+ {file = "debugpy-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b5b3157372e0e0a1297a8b6b5280bcf1d35a40f436c7973771c972726d1e32d5"},
+ {file = "debugpy-1.5.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1ec3a086e14bba6c472632025b8fe5bdfbaef2afa1ebd5c6615ce6ed8d89bc67"},
+ {file = "debugpy-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:26fbe53cca45a608679094791ce587b6e2798acd1d4777a8b303b07622e85182"},
+ {file = "debugpy-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:d876db8c312eeb02d85611e0f696abe66a2c1515e6405943609e725d5ff36f2a"},
+ {file = "debugpy-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4404a62fb5332ea5c8c9132290eef50b3a0ba38cecacad5529e969a783bcbdd7"},
+ {file = "debugpy-1.5.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f3a3dca9104aa14fd4210edcce6d9ce2b65bd9618c0b222135a40b9d6e2a9eeb"},
+ {file = "debugpy-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2df2c373e85871086bd55271c929670cd4e1dba63e94a08d442db830646203b"},
+ {file = "debugpy-1.5.1-cp38-cp38-win32.whl", hash = "sha256:82f5f9ce93af6861a0713f804e62ab390bb12a17f113153e47fea8bbb1dfbe36"},
+ {file = "debugpy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:17a25ce9d7714f92fc97ef00cc06269d7c2b163094990ada30156ed31d9a5030"},
+ {file = "debugpy-1.5.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:01e98c594b3e66d529e40edf314f849cd1a21f7a013298df58cd8e263bf8e184"},
+ {file = "debugpy-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f73988422b17f071ad3c4383551ace1ba5ed810cbab5f9c362783d22d40a08dc"},
+ {file = "debugpy-1.5.1-cp39-cp39-win32.whl", hash = "sha256:23df67fc56d59e386c342428a7953c2c06cc226d8525b11319153e96afb65b0c"},
+ {file = "debugpy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2aa64f6d2ca7ded8a7e8a4e7cae3bc71866b09876b7b05cecad231779cb9156"},
+ {file = "debugpy-1.5.1-py2.py3-none-any.whl", hash = "sha256:194f95dd3e84568b5489aab5689a3a2c044e8fdc06f1890b8b4f70b6b89f2778"},
+ {file = "debugpy-1.5.1.zip", hash = "sha256:d2b09e91fbd1efa4f4fda121d49af89501beda50c18ed7499712c71a4bf3452e"},
+]
+decorator = [
+ {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"},
+ {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"},
+]
+defusedxml = [
+ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
+ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
+]
+distro = [
+ {file = "distro-1.6.0-py2.py3-none-any.whl", hash = "sha256:c8713330ab31a034623a9515663ed87696700b55f04556b97c39cd261aa70dc7"},
+ {file = "distro-1.6.0.tar.gz", hash = "sha256:83f5e5a09f9c5f68f60173de572930effbcc0287bb84fdc4426cb4168c088424"},
+]
+docutils = [
+ {file = "docutils-0.18-py2.py3-none-any.whl", hash = "sha256:a31688b2ea858517fa54293e5d5df06fbb875fb1f7e4c64529271b77781ca8fc"},
+ {file = "docutils-0.18.tar.gz", hash = "sha256:c1d5dab2b11d16397406a282e53953fe495a46d69ae329f55aa98a5c4e3c5fbb"},
+]
+empy = [
+ {file = "empy-3.3.4.tar.gz", hash = "sha256:73ac49785b601479df4ea18a7c79bc1304a8a7c34c02b9472cf1206ae88f01b3"},
+]
+entrypoints = [
+ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
+ {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
+]
+filelock = [
+ {file = "filelock-3.3.2-py3-none-any.whl", hash = "sha256:bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b"},
+ {file = "filelock-3.3.2.tar.gz", hash = "sha256:7afc856f74fa7006a289fd10fa840e1eebd8bbff6bffb69c26c54a0512ea8cf8"},
+]
+genmsg = []
+genpy = []
+geometry-msgs = []
+gnupg = [
+ {file = "gnupg-2.3.1-py2.7.egg", hash = "sha256:d2e16c486aaeecbb65633f8493ccab025e2487c0e1dc59a83c520b6f81dff9b8"},
+ {file = "gnupg-2.3.1-py3.4.egg", hash = "sha256:be656a2f693dbac4362537c1b6cfd03131ac5ce7a4b2bb21bff7e3145f94f7ea"},
+ {file = "gnupg-2.3.1.tar.gz", hash = "sha256:8db5a05c369dbc231dab4c98515ce828f2dffdc14f1534441a6c59b71c6d2031"},
+]
+google-auth = [
+ {file = "google-auth-2.3.3.tar.gz", hash = "sha256:d83570a664c10b97a1dc6f8df87e5fdfff012f48f62be131e449c20dfc32630e"},
+ {file = "google_auth-2.3.3-py2.py3-none-any.whl", hash = "sha256:a348a50b027679cb7dae98043ac8dbcc1d7951f06d8387496071a1e05a2465c0"},
+]
+google-auth-oauthlib = [
+ {file = "google-auth-oauthlib-0.4.6.tar.gz", hash = "sha256:a90a072f6993f2c327067bf65270046384cda5a8ecb20b94ea9a687f1f233a7a"},
+ {file = "google_auth_oauthlib-0.4.6-py2.py3-none-any.whl", hash = "sha256:3f2a6e802eebbb6fb736a370fbf3b055edcb6b52878bf2f26330b5e041316c73"},
+]
+grpcio = [
+ {file = "grpcio-1.41.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:ead9885b53777bed4b0694ff0baea9d2c519ff774b17b177bde43d73e2b4aa38"},
+ {file = "grpcio-1.41.1-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:3b4b7c1ab18283eb64af5648d20eabef9237a2aec09e30a805f18adc9497258d"},
+ {file = "grpcio-1.41.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:f68aa98f5970eccb6c94456f3447a99916c42fbddae1971256bc4e7c40a6593b"},
+ {file = "grpcio-1.41.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:71d9ed5a732a54b9c87764609f2fd2bc4ae72fa85e271038eb132ea723222209"},
+ {file = "grpcio-1.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aa1af3e1480b6dd3092ee67c4b67b1ea88d638fcdc4d1a611ae11e311800b34"},
+ {file = "grpcio-1.41.1-cp310-cp310-win32.whl", hash = "sha256:766f1b943abc3e27842b72fba6e28fb9f57c9b84029fd7e91146e4c37034d937"},
+ {file = "grpcio-1.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:3713e3918da6ae10812a64e75620a172f01af2ff0a1c99d6481c910e1d4a9053"},
+ {file = "grpcio-1.41.1-cp36-cp36m-linux_armv7l.whl", hash = "sha256:3f0b70cf8632028714a8341b841b011a47900b1c163bf5fababb4ab3888c9b6c"},
+ {file = "grpcio-1.41.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:8824b36e6b0e45fefe0b4eac5ad460830e0cbc856a0c794f711289b4b8933d53"},
+ {file = "grpcio-1.41.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:788154b32bf712e9711d001df024af5f7b2522117876c129bb27b9ad6e5461fb"},
+ {file = "grpcio-1.41.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:c32c470e077b34a52e87e7de26644ad0f9e9ff89a785ff7e6466870869659e05"},
+ {file = "grpcio-1.41.1-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:a3bb4302389b23f2006ecaaea5eb4a39cc80ea98d1964159e59c1c20ef39a483"},
+ {file = "grpcio-1.41.1-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e156ea12adb7a7ca8d8280c9df850c15510b790c785fc26c9a3fb928cd221fd4"},
+ {file = "grpcio-1.41.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eb8180a6d9e47fc865a4e92a2678f3202145021ef2c1bccf165fa5744f6ec95"},
+ {file = "grpcio-1.41.1-cp36-cp36m-win32.whl", hash = "sha256:888d8519709652dd39415de5f79abd50257201b345dd4f40151feffc3dad3232"},
+ {file = "grpcio-1.41.1-cp36-cp36m-win_amd64.whl", hash = "sha256:734690b3f35468f8ed4003ec7622d2d47567f1881f5fcdca34f1e52551c2ef55"},
+ {file = "grpcio-1.41.1-cp37-cp37m-linux_armv7l.whl", hash = "sha256:133fb9a3cf4519543e4e41eb18b5dac0da26941aeabca8122dbcf3decbad2d21"},
+ {file = "grpcio-1.41.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:a5ac91db3c588296366554b2d91116fc3a9f05bae516cafae07220e1f05bfef7"},
+ {file = "grpcio-1.41.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:b8dd1b6456c6fb3681affe0f81dff4b3bc46f825fc05e086d64216545da9ad92"},
+ {file = "grpcio-1.41.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:9e403d07d77ed4495ad3c18994191525b11274693e72e464241c9139e2f9cd7c"},
+ {file = "grpcio-1.41.1-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:9d1be99f216b18f8a9dbdfbdbcc9a6caee504d0d27295fdbb5c8da35f5254a69"},
+ {file = "grpcio-1.41.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebbe9582ef06559a2358827a588ab4b92a2639517de8fe428288772820ab03b5"},
+ {file = "grpcio-1.41.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd570720871dc84d2adc8430ce287319c9238d1e2f70c140f9bc54c690fabd1b"},
+ {file = "grpcio-1.41.1-cp37-cp37m-win32.whl", hash = "sha256:0c075616d5e86fb65fd4784d5a87d6e5a1882d277dce5c33d9b67cfc71d79899"},
+ {file = "grpcio-1.41.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9170b5d2082fc00c057c6ccd6b893033c1ade05717fcec1d63557c3bc7afdb1b"},
+ {file = "grpcio-1.41.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:61aa02f4505c5bbbaeba80fef1bd6871d1aef05a8778a707ab91303ee0865ad0"},
+ {file = "grpcio-1.41.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d1461672b2eaef9affb60a71014ebd2f789deea7c9acb1d4bd163de92dd8e044"},
+ {file = "grpcio-1.41.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c35b847bc6bd3c3a118a13277d91a772e7dd163ce7dd2791239f9941b6eaafe3"},
+ {file = "grpcio-1.41.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8487fb0649ebebc9c5dca1a6dc4eb7fddf701183426b3eefeb3584639d223d43"},
+ {file = "grpcio-1.41.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6ca497ccecaa8727f14c4ccc9ffb70a19c6413fe1d4650500c90a7febd662860"},
+ {file = "grpcio-1.41.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1232c5efc8a9e4b7a13db235c51135412beb9e62e618a2a89dd0463edb3d929"},
+ {file = "grpcio-1.41.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a47af7356fb5ed3120636dd75c5efb571ecf15737484119e31286687f0e52a"},
+ {file = "grpcio-1.41.1-cp38-cp38-win32.whl", hash = "sha256:7a22a7378ea59ad1e6f2e79f9da6862eb9e1f6586253aee784d419a49e3f4bd9"},
+ {file = "grpcio-1.41.1-cp38-cp38-win_amd64.whl", hash = "sha256:32b7ca83f1a6929217098aaaac89fc49879ae714c95501d40df41a0e7506164c"},
+ {file = "grpcio-1.41.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:3213dfe3abfc3fda7f30e86aa5967dce0c2eb4cc90a0504f95434091bf6b219a"},
+ {file = "grpcio-1.41.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:fd11995e3402af0f838844194707da8b3235f1719bcac961493f0138f1325893"},
+ {file = "grpcio-1.41.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:23a3f03e1d9ac429ff78d23d2ab07756d3728ee1a68b5f244d8435006608b6aa"},
+ {file = "grpcio-1.41.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:fc2eadfb5ec956c556c138fab0dfc1d2395c57ae0bfea047edae1976a26b250c"},
+ {file = "grpcio-1.41.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:2a34c8979de10b04a44d2cad07d41d83643e65e49f84a05b1adf150aeb41c95f"},
+ {file = "grpcio-1.41.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72d0bdc3605dc8f4187b302e1180643963896e3f2917a52becb51afb54448e3e"},
+ {file = "grpcio-1.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:740f5b21a7108a8c08bf522434752dc1d306274d47ca8b4d51af5588a16b6113"},
+ {file = "grpcio-1.41.1-cp39-cp39-win32.whl", hash = "sha256:2f2ee78a6ae88d668ceda56fa4a18d8a38b34c2f2e1332083dd1da1a92870703"},
+ {file = "grpcio-1.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:c3a446b6a1f8077cc03d0d496fc1cecdd3d0b66860c0c5b65cc92d0549117840"},
+ {file = "grpcio-1.41.1.tar.gz", hash = "sha256:9b751271b029432a526a4970dc9b70d93eb6f0963b6a841b574f780b72651969"},
+]
+gym = [
+ {file = "gym-0.21.0.tar.gz", hash = "sha256:0fd1ce165c754b4017e37a617b097c032b8c3feb8a0394ccc8777c7c50dddff3"},
+]
+idna = [
+ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
+ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
+]
+importlib-resources = [
+ {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"},
+ {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"},
+]
+ipykernel = [
+ {file = "ipykernel-6.5.0-py3-none-any.whl", hash = "sha256:f43de132feea90f86d68c51013afe9694f9415f440053ec9909dd656c75b04b5"},
+ {file = "ipykernel-6.5.0.tar.gz", hash = "sha256:299795cca2c4aed7e233e3ad5360e1c73627fd0dcec11a9e75d5b2df43629353"},
+]
+ipython = [
+ {file = "ipython-7.29.0-py3-none-any.whl", hash = "sha256:a658beaf856ce46bc453366d5dc6b2ddc6c481efd3540cb28aa3943819caac9f"},
+ {file = "ipython-7.29.0.tar.gz", hash = "sha256:4f69d7423a5a1972f6347ff233e38bbf4df6a150ef20fbb00c635442ac3060aa"},
+]
+ipython-genutils = [
+ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
+ {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
+]
+ipywidgets = [
+ {file = "ipywidgets-7.6.5-py2.py3-none-any.whl", hash = "sha256:d258f582f915c62ea91023299603be095de19afb5ee271698f88327b9fe9bf43"},
+ {file = "ipywidgets-7.6.5.tar.gz", hash = "sha256:00974f7cb4d5f8d494c19810fedb9fa9b64bffd3cda7c2be23c133a1ad3c99c5"},
+]
+jedi = [
+ {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
+ {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"},
+]
+jinja2 = [
+ {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
+ {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
+]
+jsonschema = [
+ {file = "jsonschema-4.2.1-py3-none-any.whl", hash = "sha256:2a0f162822a64d95287990481b45d82f096e99721c86534f48201b64ebca6e8c"},
+ {file = "jsonschema-4.2.1.tar.gz", hash = "sha256:390713469ae64b8a58698bb3cbc3859abe6925b565a973f87323ef21b09a27a8"},
+]
+jupyter = [
+ {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"},
+ {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"},
+ {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"},
+]
+jupyter-client = [
+ {file = "jupyter_client-7.0.6-py3-none-any.whl", hash = "sha256:074bdeb1ffaef4a3095468ee16313938cfdc48fc65ca95cc18980b956c2e5d79"},
+ {file = "jupyter_client-7.0.6.tar.gz", hash = "sha256:8b6e06000eb9399775e0a55c52df6c1be4766666209c22f90c2691ded0e338dc"},
+]
+jupyter-console = [
+ {file = "jupyter_console-6.4.0-py3-none-any.whl", hash = "sha256:7799c4ea951e0e96ba8260575423cb323ea5a03fcf5503560fa3e15748869e27"},
+ {file = "jupyter_console-6.4.0.tar.gz", hash = "sha256:242248e1685039cd8bff2c2ecb7ce6c1546eb50ee3b08519729e6e881aec19c7"},
+]
+jupyter-core = [
+ {file = "jupyter_core-4.9.1-py3-none-any.whl", hash = "sha256:1c091f3bbefd6f2a8782f2c1db662ca8478ac240e962ae2c66f0b87c818154ea"},
+ {file = "jupyter_core-4.9.1.tar.gz", hash = "sha256:dce8a7499da5a53ae3afd5a9f4b02e5df1d57250cf48f3ad79da23b4778cd6fa"},
+]
+jupyterlab-pygments = [
+ {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"},
+ {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"},
+]
+jupyterlab-widgets = [
+ {file = "jupyterlab_widgets-1.0.2-py3-none-any.whl", hash = "sha256:f5d9efface8ec62941173ba1cffb2edd0ecddc801c11ae2931e30b50492eb8f7"},
+ {file = "jupyterlab_widgets-1.0.2.tar.gz", hash = "sha256:7885092b2b96bf189c3a705cc3c412a4472ec5e8382d0b47219a66cccae73cfa"},
+]
+kiwisolver = [
+ {file = "kiwisolver-1.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1d819553730d3c2724582124aee8a03c846ec4362ded1034c16fb3ef309264e6"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d93a1095f83e908fc253f2fb569c2711414c0bfd451cab580466465b235b470"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4550a359c5157aaf8507e6820d98682872b9100ce7607f8aa070b4b8af6c298"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2210f28778c7d2ee13f3c2a20a3a22db889e75f4ec13a21072eabb5693801e84"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:82f49c5a79d3839bc8f38cb5f4bfc87e15f04cbafa5fbd12fb32c941cb529cfb"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9661a04ca3c950a8ac8c47f53cbc0b530bce1b52f516a1e87b7736fec24bfff0"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ddb500a2808c100e72c075cbb00bf32e62763c82b6a882d403f01a119e3f402"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72be6ebb4e92520b9726d7146bc9c9b277513a57a38efcf66db0620aec0097e0"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-win32.whl", hash = "sha256:83d2c9db5dfc537d0171e32de160461230eb14663299b7e6d18ca6dca21e4977"},
+ {file = "kiwisolver-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:cba430db673c29376135e695c6e2501c44c256a81495da849e85d1793ee975ad"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4116ba9a58109ed5e4cb315bdcbff9838f3159d099ba5259c7c7fb77f8537492"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19554bd8d54cf41139f376753af1a644b63c9ca93f8f72009d50a2080f870f77"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a4cf5bbdc861987a7745aed7a536c6405256853c94abc9f3287c3fa401b174"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0007840186bacfaa0aba4466d5890334ea5938e0bb7e28078a0eb0e63b5b59d5"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec2eba188c1906b05b9b49ae55aae4efd8150c61ba450e6721f64620c50b59eb"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3dbb3cea20b4af4f49f84cffaf45dd5f88e8594d18568e0225e6ad9dec0e7967"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-win32.whl", hash = "sha256:5326ddfacbe51abf9469fe668944bc2e399181a2158cb5d45e1d40856b2a0589"},
+ {file = "kiwisolver-1.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c6572c2dab23c86a14e82c245473d45b4c515314f1f859e92608dcafbd2f19b8"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b5074fb09429f2b7bc82b6fb4be8645dcbac14e592128beeff5461dcde0af09f"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:22521219ca739654a296eea6d4367703558fba16f98688bd8ce65abff36eaa84"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c358721aebd40c243894298f685a19eb0491a5c3e0b923b9f887ef1193ddf829"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ba5a1041480c6e0a8b11a9544d53562abc2d19220bfa14133e0cdd9967e97af"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44e6adf67577dbdfa2d9f06db9fbc5639afefdb5bf2b4dfec25c3a7fbc619536"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d45d1c74f88b9f41062716c727f78f2a59a5476ecbe74956fafb423c5c87a76"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70adc3658138bc77a36ce769f5f183169bc0a2906a4f61f09673f7181255ac9b"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6a5431940f28b6de123de42f0eb47b84a073ee3c3345dc109ad550a3307dd28"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-win32.whl", hash = "sha256:ee040a7de8d295dbd261ef2d6d3192f13e2b08ec4a954de34a6fb8ff6422e24c"},
+ {file = "kiwisolver-1.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:8dc3d842fa41a33fe83d9f5c66c0cc1f28756530cd89944b63b072281e852031"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a498bcd005e8a3fedd0022bb30ee0ad92728154a8798b703f394484452550507"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80efd202108c3a4150e042b269f7c78643420cc232a0a771743bb96b742f838f"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f8eb7b6716f5b50e9c06207a14172cf2de201e41912ebe732846c02c830455b9"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f441422bb313ab25de7b3dbfd388e790eceb76ce01a18199ec4944b369017009"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:30fa008c172355c7768159983a7270cb23838c4d7db73d6c0f6b60dde0d432c6"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f8f6c8f4f1cff93ca5058d6ec5f0efda922ecb3f4c5fb76181f327decff98b8"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba677bcaff9429fd1bf01648ad0901cea56c0d068df383d5f5856d88221fe75b"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7843b1624d6ccca403a610d1277f7c28ad184c5aa88a1750c1a999754e65b439"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-win32.whl", hash = "sha256:e6f5eb2f53fac7d408a45fbcdeda7224b1cfff64919d0f95473420a931347ae9"},
+ {file = "kiwisolver-1.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:eedd3b59190885d1ebdf6c5e0ca56828beb1949b4dfe6e5d0256a461429ac386"},
+ {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dedc71c8eb9c5096037766390172c34fb86ef048b8e8958b4e484b9e505d66bc"},
+ {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bf7eb45d14fc036514c09554bf983f2a72323254912ed0c3c8e697b62c4c158f"},
+ {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b65bd35f3e06a47b5c30ea99e0c2b88f72c6476eedaf8cfbc8e66adb5479dcf"},
+ {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25405f88a37c5f5bcba01c6e350086d65e7465fd1caaf986333d2a045045a223"},
+ {file = "kiwisolver-1.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:bcadb05c3d4794eb9eee1dddf1c24215c92fb7b55a80beae7a60530a91060560"},
+ {file = "kiwisolver-1.3.2.tar.gz", hash = "sha256:fc4453705b81d03568d5b808ad8f09c77c47534f6ac2e72e733f9ca4714aa75c"},
+]
+mako = [
+ {file = "Mako-1.1.5-py2.py3-none-any.whl", hash = "sha256:6804ee66a7f6a6416910463b00d76a7b25194cd27f1918500c5bd7be2a088a23"},
+ {file = "Mako-1.1.5.tar.gz", hash = "sha256:169fa52af22a91900d852e937400e79f535496191c63712e3b9fda5a9bed6fc3"},
+]
+markdown = [
+ {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"},
+ {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"},
+]
+markupsafe = [
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
+ {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+]
+matplotlib = [
+ {file = "matplotlib-3.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c988bb43414c7c2b0a31bd5187b4d27fd625c080371b463a6d422047df78913"},
+ {file = "matplotlib-3.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1c5efc278d996af8a251b2ce0b07bbeccb821f25c8c9846bdcb00ffc7f158aa"},
+ {file = "matplotlib-3.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:eeb1859efe7754b1460e1d4991bbd4a60a56f366bc422ef3a9c5ae05f0bc70b5"},
+ {file = "matplotlib-3.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:844a7b0233e4ff7fba57e90b8799edaa40b9e31e300b8d5efc350937fa8b1bea"},
+ {file = "matplotlib-3.4.3-cp37-cp37m-win32.whl", hash = "sha256:85f0c9cf724715e75243a7b3087cf4a3de056b55e05d4d76cc58d610d62894f3"},
+ {file = "matplotlib-3.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c70b6311dda3e27672f1bf48851a0de816d1ca6aaf3d49365fbdd8e959b33d2b"},
+ {file = "matplotlib-3.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b884715a59fec9ad3b6048ecf3860f3b2ce965e676ef52593d6fa29abcf7d330"},
+ {file = "matplotlib-3.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a78a3b51f29448c7f4d4575e561f6b0dbb8d01c13c2046ab6c5220eb25c06506"},
+ {file = "matplotlib-3.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6a724e3a48a54b8b6e7c4ae38cd3d07084508fa47c410c8757e9db9791421838"},
+ {file = "matplotlib-3.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:48e1e0859b54d5f2e29bb78ca179fd59b971c6ceb29977fb52735bfd280eb0f5"},
+ {file = "matplotlib-3.4.3-cp38-cp38-win32.whl", hash = "sha256:01c9de93a2ca0d128c9064f23709362e7fefb34910c7c9e0b8ab0de8258d5eda"},
+ {file = "matplotlib-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:ebfb01a65c3f5d53a8c2a8133fec2b5221281c053d944ae81ff5822a68266617"},
+ {file = "matplotlib-3.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b8b53f336a4688cfce615887505d7e41fd79b3594bf21dd300531a4f5b4f746a"},
+ {file = "matplotlib-3.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:fcd6f1954943c0c192bfbebbac263f839d7055409f1173f80d8b11a224d236da"},
+ {file = "matplotlib-3.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6be8df61b1626e1a142c57e065405e869e9429b4a6dab4a324757d0dc4d42235"},
+ {file = "matplotlib-3.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:41b6e307458988891fcdea2d8ecf84a8c92d53f84190aa32da65f9505546e684"},
+ {file = "matplotlib-3.4.3-cp39-cp39-win32.whl", hash = "sha256:f72657f1596199dc1e4e7a10f52a4784ead8a711f4e5b59bea95bdb97cf0e4fd"},
+ {file = "matplotlib-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:f15edcb0629a0801738925fe27070480f446fcaa15de65946ff946ad99a59a40"},
+ {file = "matplotlib-3.4.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:556965514b259204637c360d213de28d43a1f4aed1eca15596ce83f768c5a56f"},
+ {file = "matplotlib-3.4.3-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:54a026055d5f8614f184e588f6e29064019a0aa8448450214c0b60926d62d919"},
+ {file = "matplotlib-3.4.3.tar.gz", hash = "sha256:fc4f526dfdb31c9bd6b8ca06bf9fab663ca12f3ec9cdf4496fb44bc680140318"},
+]
+matplotlib-inline = [
+ {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
+ {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
+]
+mistune = [
+ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"},
+ {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"},
+]
+mypy-extensions = [
+ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
+ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
+]
+nbclient = [
+ {file = "nbclient-0.5.8-py3-none-any.whl", hash = "sha256:e85d4d6280d0a0237c1a6ec7a5e0757cf40a1fcb8c47253516b3a1f87f4ceae8"},
+ {file = "nbclient-0.5.8.tar.gz", hash = "sha256:34f52cc9cb831a5d8ccd7031537e354c75dc61a24487f998712d1289de320a25"},
+]
+nbconvert = [
+ {file = "nbconvert-6.3.0-py3-none-any.whl", hash = "sha256:8f23fbeabda4a500685d788ee091bf22cf34119304314304fb39f16e2fc32f37"},
+ {file = "nbconvert-6.3.0.tar.gz", hash = "sha256:5e77d6203854944520105e38f2563a813a4a3708e8563aa598928a3b5ee1081a"},
+]
+nbformat = [
+ {file = "nbformat-5.1.3-py3-none-any.whl", hash = "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"},
+ {file = "nbformat-5.1.3.tar.gz", hash = "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8"},
+]
+nest-asyncio = [
+ {file = "nest_asyncio-1.5.1-py3-none-any.whl", hash = "sha256:76d6e972265063fe92a90b9cc4fb82616e07d586b346ed9d2c89a4187acea39c"},
+ {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"},
+]
+netifaces = [
+ {file = "netifaces-0.10.9-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:b2ff3a0a4f991d2da5376efd3365064a43909877e9fabfa801df970771161d29"},
+ {file = "netifaces-0.10.9-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:0c4304c6d5b33fbd9b20fdc369f3a2fef1a8bbacfb6fd05b9708db01333e9e7b"},
+ {file = "netifaces-0.10.9-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7a25a8e28281504f0e23e181d7a9ed699c72f061ca6bdfcd96c423c2a89e75fc"},
+ {file = "netifaces-0.10.9-cp27-cp27m-win32.whl", hash = "sha256:6d84e50ec28e5d766c9911dce945412dc5b1ce760757c224c71e1a9759fa80c2"},
+ {file = "netifaces-0.10.9-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f911b7f0083d445c8d24cfa5b42ad4996e33250400492080f5018a28c026db2b"},
+ {file = "netifaces-0.10.9-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:4921ed406386246b84465950d15a4f63480c1458b0979c272364054b29d73084"},
+ {file = "netifaces-0.10.9-cp33-cp33m-manylinux1_i686.whl", hash = "sha256:5b3167f923f67924b356c1338eb9ba275b2ba8d64c7c2c47cf5b5db49d574994"},
+ {file = "netifaces-0.10.9-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:db881478f1170c6dd524175ba1c83b99d3a6f992a35eca756de0ddc4690a1940"},
+ {file = "netifaces-0.10.9-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:f0427755c68571df37dc58835e53a4307884a48dec76f3c01e33eb0d4a3a81d7"},
+ {file = "netifaces-0.10.9-cp34-cp34m-win32.whl", hash = "sha256:7cc6fd1eca65be588f001005446a47981cbe0b2909f5be8feafef3bf351a4e24"},
+ {file = "netifaces-0.10.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:b47e8f9ff6846756be3dc3fb242ca8e86752cd35a08e06d54ffc2e2a2aca70ea"},
+ {file = "netifaces-0.10.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f8885cc48c8c7ad51f36c175e462840f163cb4687eeb6c6d7dfaf7197308e36b"},
+ {file = "netifaces-0.10.9-cp35-cp35m-win32.whl", hash = "sha256:755050799b5d5aedb1396046f270abfc4befca9ccba3074f3dbbb3cb34f13aae"},
+ {file = "netifaces-0.10.9-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:ad10acab2ef691eb29a1cc52c3be5ad1423700e993cc035066049fa72999d0dc"},
+ {file = "netifaces-0.10.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:563a1a366ee0fb3d96caab79b7ac7abd2c0a0577b157cc5a40301373a0501f89"},
+ {file = "netifaces-0.10.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:30ed89ab8aff715caf9a9d827aa69cd02ad9f6b1896fd3fb4beb998466ed9a3c"},
+ {file = "netifaces-0.10.9-cp36-cp36m-win32.whl", hash = "sha256:75d3a4ec5035db7478520ac547f7c176e9fd438269e795819b67223c486e5cbe"},
+ {file = "netifaces-0.10.9-cp36-cp36m-win_amd64.whl", hash = "sha256:078986caf4d6a602a4257d3686afe4544ea74362b8928e9f4389b5cd262bc215"},
+ {file = "netifaces-0.10.9-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:3095218b66d359092b82f07c5422293c2f6559cf8d36b96b379cc4cdc26eeffa"},
+ {file = "netifaces-0.10.9-cp37-cp37m-win32.whl", hash = "sha256:da298241d87bcf468aa0f0705ba14572ad296f24c4fda5055d6988701d6fd8e1"},
+ {file = "netifaces-0.10.9-cp37-cp37m-win_amd64.whl", hash = "sha256:86b8a140e891bb23c8b9cb1804f1475eb13eea3dbbebef01fcbbf10fbafbee42"},
+ {file = "netifaces-0.10.9.tar.gz", hash = "sha256:2dee9ffdd16292878336a58d04a20f0ffe95555465fee7c9bd23b3490ef2abf3"},
+]
+notebook = [
+ {file = "notebook-6.4.5-py3-none-any.whl", hash = "sha256:f7b4362698fed34f44038de0517b2e5136c1e7c379797198c1736121d3d597bd"},
+ {file = "notebook-6.4.5.tar.gz", hash = "sha256:872e20da9ae518bbcac3e4e0092d5bd35454e847dedb8cb9739e9f3b68406be0"},
+]
+numpy = [
+ {file = "numpy-1.21.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8890b3360f345e8360133bc078d2dacc2843b6ee6059b568781b15b97acbe39f"},
+ {file = "numpy-1.21.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:69077388c5a4b997442b843dbdc3a85b420fb693ec8e33020bb24d647c164fa5"},
+ {file = "numpy-1.21.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e89717274b41ebd568cd7943fc9418eeb49b1785b66031bc8a7f6300463c5898"},
+ {file = "numpy-1.21.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b78ecfa070460104934e2caf51694ccd00f37d5e5dbe76f021b1b0b0d221823"},
+ {file = "numpy-1.21.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:615d4e328af7204c13ae3d4df7615a13ff60a49cb0d9106fde07f541207883ca"},
+ {file = "numpy-1.21.4-cp310-cp310-win_amd64.whl", hash = "sha256:1403b4e2181fc72664737d848b60e65150f272fe5a1c1cbc16145ed43884065a"},
+ {file = "numpy-1.21.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74b85a17528ca60cf98381a5e779fc0264b4a88b46025e6bcbe9621f46bb3e63"},
+ {file = "numpy-1.21.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92aafa03da8658609f59f18722b88f0a73a249101169e28415b4fa148caf7e41"},
+ {file = "numpy-1.21.4-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5d95668e727c75b3f5088ec7700e260f90ec83f488e4c0aaccb941148b2cd377"},
+ {file = "numpy-1.21.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5162ec777ba7138906c9c274353ece5603646c6965570d82905546579573f73"},
+ {file = "numpy-1.21.4-cp37-cp37m-win32.whl", hash = "sha256:81225e58ef5fce7f1d80399575576fc5febec79a8a2742e8ef86d7b03beef49f"},
+ {file = "numpy-1.21.4-cp37-cp37m-win_amd64.whl", hash = "sha256:32fe5b12061f6446adcbb32cf4060a14741f9c21e15aaee59a207b6ce6423469"},
+ {file = "numpy-1.21.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c449eb870616a7b62e097982c622d2577b3dbc800aaf8689254ec6e0197cbf1e"},
+ {file = "numpy-1.21.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e4ed57f45f0aa38beca2a03b6532e70e548faf2debbeb3291cfc9b315d9be8f"},
+ {file = "numpy-1.21.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1247ef28387b7bb7f21caf2dbe4767f4f4175df44d30604d42ad9bd701ebb31f"},
+ {file = "numpy-1.21.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34f3456f530ae8b44231c63082c8899fe9c983fd9b108c997c4b1c8c2d435333"},
+ {file = "numpy-1.21.4-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c9c23158b87ed0e70d9a50c67e5c0b3f75bcf2581a8e34668d4e9d7474d76c6"},
+ {file = "numpy-1.21.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4799be6a2d7d3c33699a6f77201836ac975b2e1b98c2a07f66a38f499cb50ce"},
+ {file = "numpy-1.21.4-cp38-cp38-win32.whl", hash = "sha256:bc988afcea53e6156546e5b2885b7efab089570783d9d82caf1cfd323b0bb3dd"},
+ {file = "numpy-1.21.4-cp38-cp38-win_amd64.whl", hash = "sha256:170b2a0805c6891ca78c1d96ee72e4c3ed1ae0a992c75444b6ab20ff038ba2cd"},
+ {file = "numpy-1.21.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fde96af889262e85aa033f8ee1d3241e32bf36228318a61f1ace579df4e8170d"},
+ {file = "numpy-1.21.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c885bfc07f77e8fee3dc879152ba993732601f1f11de248d4f357f0ffea6a6d4"},
+ {file = "numpy-1.21.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e6f5f50d1eff2f2f752b3089a118aee1ea0da63d56c44f3865681009b0af162"},
+ {file = "numpy-1.21.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ad010846cdffe7ec27e3f933397f8a8d6c801a48634f419e3d075db27acf5880"},
+ {file = "numpy-1.21.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c74c699b122918a6c4611285cc2cad4a3aafdb135c22a16ec483340ef97d573c"},
+ {file = "numpy-1.21.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9864424631775b0c052f3bd98bc2712d131b3e2cd95d1c0c68b91709170890b0"},
+ {file = "numpy-1.21.4-cp39-cp39-win32.whl", hash = "sha256:b1e2312f5b8843a3e4e8224b2b48fe16119617b8fc0a54df8f50098721b5bed2"},
+ {file = "numpy-1.21.4-cp39-cp39-win_amd64.whl", hash = "sha256:e3c3e990274444031482a31280bf48674441e0a5b55ddb168f3a6db3e0c38ec8"},
+ {file = "numpy-1.21.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a3deb31bc84f2b42584b8c4001c85d1934dbfb4030827110bc36bfd11509b7bf"},
+ {file = "numpy-1.21.4.zip", hash = "sha256:e6c76a87633aa3fa16614b61ccedfae45b91df2767cf097aa9c933932a7ed1e0"},
+]
+oauthlib = [
+ {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"},
+ {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"},
+]
+opencv-python = [
+ {file = "opencv-python-3.4.16.57.tar.gz", hash = "sha256:fa1e5ad9b2cd3b3f83d0ba7aceb20706cd79d91e5028c283d14ef75299203352"},
+ {file = "opencv_python-3.4.16.57-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66a7973ff3e39e5b55d5f37dd305189fb6245d7710c7fe3d562aa8df4f89b3bf"},
+ {file = "opencv_python-3.4.16.57-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3eafb8c4e8efb80455648eb845644aa18d7d121d5ac8d01e9ed74236ae8d2cfc"},
+ {file = "opencv_python-3.4.16.57-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:6f6d76e06dad8e1432b9dfd9d0ae5b60c54d232a3dce73f17f9ef547e9770d1e"},
+ {file = "opencv_python-3.4.16.57-cp310-cp310-win32.whl", hash = "sha256:fd30a0f77dfead731144aae843edd5e2d89fc1b7104147503aaf908b87c8b268"},
+ {file = "opencv_python-3.4.16.57-cp310-cp310-win_amd64.whl", hash = "sha256:f47e6d4d61cb90ba9fac0cc0d80a35b71f3db32341f1fb67a39730b4e31c9985"},
+ {file = "opencv_python-3.4.16.57-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:11351d0990e0994b1e5cbf02da248746d6c1e5c05dacf8d149607c8d8eb4e763"},
+ {file = "opencv_python-3.4.16.57-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:fde2cb8ee6aa15417996140e18703e63db9b460bb467709e4e91966f7dac99fc"},
+ {file = "opencv_python-3.4.16.57-cp36-cp36m-win32.whl", hash = "sha256:ba31e3debe484ef902be06f681d795f86ec6493780b902e1dd12c25037f482f4"},
+ {file = "opencv_python-3.4.16.57-cp36-cp36m-win_amd64.whl", hash = "sha256:c221511f3eec66a5c2ef34706b5bad5181506e46b2a8c3c2840f381dfb4ce261"},
+ {file = "opencv_python-3.4.16.57-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:0859665853dee1e3ae4ed21b6d1218c85378f6cfc2a6aa9fbb5e5306beda0bac"},
+ {file = "opencv_python-3.4.16.57-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:8c52c5eabd6fc5488f76a65caa8db0b253316ce9ed78870b8472e96a426c7cec"},
+ {file = "opencv_python-3.4.16.57-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4cba52764275f0d23e65ed3ded7aa7cd4975339cd47d88ddb073aeb0853f51f3"},
+ {file = "opencv_python-3.4.16.57-cp37-cp37m-win32.whl", hash = "sha256:9936248c3f17fcfda434934a0b104c6652330cdd05c388de165a4811b9178c75"},
+ {file = "opencv_python-3.4.16.57-cp37-cp37m-win_amd64.whl", hash = "sha256:7db9ef3272d1becbd6752a7537539a6fba7872822451841edc8a68d18a193610"},
+ {file = "opencv_python-3.4.16.57-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:598f9682bf9a30a1e0c142123c8c30bb870c58ee2e1d7f049ca2dd5f46fd5fab"},
+ {file = "opencv_python-3.4.16.57-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fc695fa8c95145fd74c6cd7c61f2bbdae8992ffa1754eb887b62ffc0fdb9f1d6"},
+ {file = "opencv_python-3.4.16.57-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:fedba9f88f1b0a495cd46e6467808c028b38225c74a72416f2b6f765d47f0528"},
+ {file = "opencv_python-3.4.16.57-cp38-cp38-win32.whl", hash = "sha256:b8562ac95a2c5ab991e49b706174ec5bf311833fc649dcd1d7f9554cbc48a731"},
+ {file = "opencv_python-3.4.16.57-cp38-cp38-win_amd64.whl", hash = "sha256:2db066d915ffd7f8b596151ef4260cbe364160e5fb056322094fc7fbfc42cd07"},
+ {file = "opencv_python-3.4.16.57-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:1b21211a2b69109aa1b5092957f3ee7811b5a44548de1c245d4ade4e36fd7982"},
+ {file = "opencv_python-3.4.16.57-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccb127cc16d214287a36f689a1a5bb5087ba3740846cb228ae7a71873702b978"},
+ {file = "opencv_python-3.4.16.57-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b25549d5182fddcb292731cf132acf91fc6957f2e53b39f98f1729a1d64646"},
+ {file = "opencv_python-3.4.16.57-cp39-cp39-win32.whl", hash = "sha256:f2d293a7b5bb1abe953634ee0a22fc589cbbe3934c7495bdb0882227ca267ea9"},
+ {file = "opencv_python-3.4.16.57-cp39-cp39-win_amd64.whl", hash = "sha256:e41c3a74e7a57ef3b9edc40737e9af47d0c8aa55a78a7beb63f120291cbfbc80"},
+]
+packaging = [
+ {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"},
+ {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"},
+]
+pandas = [
+ {file = "pandas-1.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9707bdc1ea9639c886b4d3be6e2a45812c1ac0c2080f94c31b71c9fa35556f9b"},
+ {file = "pandas-1.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2f44425594ae85e119459bb5abb0748d76ef01d9c08583a667e3339e134218e"},
+ {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:372d72a3d8a5f2dbaf566a5fa5fa7f230842ac80f29a931fb4b071502cf86b9a"},
+ {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99d2350adb7b6c3f7f8f0e5dfb7d34ff8dd4bc0a53e62c445b7e43e163fce63"},
+ {file = "pandas-1.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:4acc28364863127bca1029fb72228e6f473bb50c32e77155e80b410e2068eeac"},
+ {file = "pandas-1.3.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c2646458e1dce44df9f71a01dc65f7e8fa4307f29e5c0f2f92c97f47a5bf22f5"},
+ {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5298a733e5bfbb761181fd4672c36d0c627320eb999c59c65156c6a90c7e1b4f"},
+ {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22808afb8f96e2269dcc5b846decacb2f526dd0b47baebc63d913bf847317c8f"},
+ {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b528e126c13816a4374e56b7b18bfe91f7a7f6576d1aadba5dee6a87a7f479ae"},
+ {file = "pandas-1.3.4-cp37-cp37m-win32.whl", hash = "sha256:fe48e4925455c964db914b958f6e7032d285848b7538a5e1b19aeb26ffaea3ec"},
+ {file = "pandas-1.3.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaca36a80acaacb8183930e2e5ad7f71539a66805d6204ea88736570b2876a7b"},
+ {file = "pandas-1.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42493f8ae67918bf129869abea8204df899902287a7f5eaf596c8e54e0ac7ff4"},
+ {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a388960f979665b447f0847626e40f99af8cf191bce9dc571d716433130cb3a7"},
+ {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba0aac1397e1d7b654fccf263a4798a9e84ef749866060d19e577e927d66e1b"},
+ {file = "pandas-1.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f567e972dce3bbc3a8076e0b675273b4a9e8576ac629149cf8286ee13c259ae5"},
+ {file = "pandas-1.3.4-cp38-cp38-win32.whl", hash = "sha256:c1aa4de4919358c5ef119f6377bc5964b3a7023c23e845d9db7d9016fa0c5b1c"},
+ {file = "pandas-1.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:dd324f8ee05925ee85de0ea3f0d66e1362e8c80799eb4eb04927d32335a3e44a"},
+ {file = "pandas-1.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47750cf07dee6b55d8423471be70d627314277976ff2edd1381f02d52dbadf9"},
+ {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d1dc09c0013d8faa7474574d61b575f9af6257ab95c93dcf33a14fd8d2c1bab"},
+ {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10e10a2527db79af6e830c3d5842a4d60383b162885270f8cffc15abca4ba4a9"},
+ {file = "pandas-1.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c77609acd2e4d517da41bae0c11c70d31c87aae8dd1aabd2670906c6d2c143"},
+ {file = "pandas-1.3.4-cp39-cp39-win32.whl", hash = "sha256:003ba92db58b71a5f8add604a17a059f3068ef4e8c0c365b088468d0d64935fd"},
+ {file = "pandas-1.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:a51528192755f7429c5bcc9e80832c517340317c861318fea9cea081b57c9afd"},
+ {file = "pandas-1.3.4.tar.gz", hash = "sha256:a2aa18d3f0b7d538e21932f637fbfe8518d085238b429e4790a35e1e44a96ffc"},
+]
+pandocfilters = [
+ {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"},
+ {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"},
+]
+parso = [
+ {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"},
+ {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"},
+]
+pathlib = [
+ {file = "pathlib-1.0.1-py3-none-any.whl", hash = "sha256:f35f95ab8b0f59e6d354090350b44a80a80635d22efdedfa84c7ad1cf0a74147"},
+ {file = "pathlib-1.0.1.tar.gz", hash = "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f"},
+]
+pathspec = [
+ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
+ {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
+]
+pdoc3 = [
+ {file = "pdoc3-0.10.0-py3-none-any.whl", hash = "sha256:ba45d1ada1bd987427d2bf5cdec30b2631a3ff5fb01f6d0e77648a572ce6028b"},
+ {file = "pdoc3-0.10.0.tar.gz", hash = "sha256:5f22e7bcb969006738e1aa4219c75a32f34c2d62d46dc9d2fb2d3e0b0287e4b7"},
+]
+pettingzoo = [
+ {file = "PettingZoo-1.13.1.tar.gz", hash = "sha256:b95d0a9e6e2ca61188ad0b72a2bc4b68a3b21ca0c85d0971e42c9c71c66abd73"},
+]
+pexpect = [
+ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
+ {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
+]
+pickleshare = [
+ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
+ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
+]
+pillow = [
+ {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"},
+ {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"},
+ {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"},
+ {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"},
+ {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"},
+ {file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"},
+ {file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"},
+ {file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"},
+ {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"},
+ {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"},
+ {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"},
+ {file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"},
+ {file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"},
+ {file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"},
+ {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"},
+ {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"},
+ {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"},
+ {file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"},
+ {file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"},
+ {file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"},
+ {file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"},
+ {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"},
+ {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"},
+ {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"},
+ {file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"},
+ {file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"},
+ {file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"},
+ {file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"},
+ {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"},
+ {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"},
+ {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"},
+ {file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"},
+ {file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"},
+ {file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"},
+ {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"},
+ {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"},
+ {file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"},
+ {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"},
+ {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"},
+ {file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"},
+ {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"},
+]
+platformdirs = [
+ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
+ {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
+]
+prometheus-client = [
+ {file = "prometheus_client-0.12.0-py2.py3-none-any.whl", hash = "sha256:317453ebabff0a1b02df7f708efbab21e3489e7072b61cb6957230dd004a0af0"},
+ {file = "prometheus_client-0.12.0.tar.gz", hash = "sha256:1b12ba48cee33b9b0b9de64a1047cbd3c5f2d0ab6ebcead7ddda613a750ec3c5"},
+]
+prompt-toolkit = [
+ {file = "prompt_toolkit-3.0.22-py3-none-any.whl", hash = "sha256:48d85cdca8b6c4f16480c7ce03fd193666b62b0a21667ca56b4bb5ad679d1170"},
+ {file = "prompt_toolkit-3.0.22.tar.gz", hash = "sha256:449f333dd120bd01f5d296a8ce1452114ba3a71fae7288d2f0ae2c918764fa72"},
+]
+protobuf = [
+ {file = "protobuf-3.19.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8"},
+ {file = "protobuf-3.19.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d"},
+ {file = "protobuf-3.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f"},
+ {file = "protobuf-3.19.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6"},
+ {file = "protobuf-3.19.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6"},
+ {file = "protobuf-3.19.1-cp36-cp36m-win32.whl", hash = "sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c"},
+ {file = "protobuf-3.19.1-cp36-cp36m-win_amd64.whl", hash = "sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942"},
+ {file = "protobuf-3.19.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853"},
+ {file = "protobuf-3.19.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d"},
+ {file = "protobuf-3.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6"},
+ {file = "protobuf-3.19.1-cp37-cp37m-win32.whl", hash = "sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04"},
+ {file = "protobuf-3.19.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea"},
+ {file = "protobuf-3.19.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7"},
+ {file = "protobuf-3.19.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089"},
+ {file = "protobuf-3.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e"},
+ {file = "protobuf-3.19.1-cp38-cp38-win32.whl", hash = "sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3"},
+ {file = "protobuf-3.19.1-cp38-cp38-win_amd64.whl", hash = "sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b"},
+ {file = "protobuf-3.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d"},
+ {file = "protobuf-3.19.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995"},
+ {file = "protobuf-3.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560"},
+ {file = "protobuf-3.19.1-cp39-cp39-win32.whl", hash = "sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2"},
+ {file = "protobuf-3.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002"},
+ {file = "protobuf-3.19.1-py2.py3-none-any.whl", hash = "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17"},
+ {file = "protobuf-3.19.1.tar.gz", hash = "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7"},
+]
+psutil = [
+ {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"},
+ {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"},
+ {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"},
+ {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"},
+ {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"},
+ {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"},
+ {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"},
+ {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"},
+ {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"},
+ {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"},
+ {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"},
+ {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"},
+ {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"},
+ {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"},
+ {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"},
+ {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"},
+ {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"},
+ {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"},
+ {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"},
+ {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"},
+ {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"},
+ {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"},
+ {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"},
+ {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"},
+ {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"},
+ {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"},
+ {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
+ {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
+]
+ptyprocess = [
+ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
+ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
+]
+py = [
+ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
+ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
+]
+pyasn1 = [
+ {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"},
+ {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"},
+ {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"},
+ {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"},
+ {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
+ {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"},
+ {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"},
+ {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"},
+ {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"},
+ {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"},
+ {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"},
+ {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"},
+ {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
+]
+pyasn1-modules = [
+ {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"},
+ {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"},
+ {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"},
+ {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"},
+ {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"},
+ {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"},
+ {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"},
+ {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"},
+ {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"},
+ {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"},
+ {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"},
+ {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"},
+ {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"},
+]
+pycparser = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+pycryptodome = [
+ {file = "pycryptodome-3.11.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ffd0cac13ff41f2d15ed39dc6ba1d2ad88dd2905d656c33d8235852f5d6151fd"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:ead516e03dfe062aefeafe4a29445a6449b0fc43bc8cb30194b2754917a63798"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4ce6b09547bf2c7cede3a017f79502eaed3e819c13cdb3cb357aea1b004e4cc6"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:014c758af7fa38cab85b357a496b76f4fc9dda1f731eb28358d66fef7ad4a3e1"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a843350d08c3d22f6c09c2f17f020d8dcfa59496165d7425a3fba0045543dda7"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:53989477044be41fa4a63da09d5038c2a34b2f4554cfea2e3933b17186ee9e19"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-win32.whl", hash = "sha256:f9bad2220b80b4ed74f089db012ab5ab5419143a33fad6c8aedcc2a9341eac70"},
+ {file = "pycryptodome-3.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:3c7ed5b07274535979c730daf5817db5e983ea80b04c22579eee8da4ca3ae4f8"},
+ {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:8f3a60926be78422e662b0d0b18351b426ce27657101c8a50bad80300de6a701"},
+ {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:fce7e22d96030b35345637c563246c24d4513bd3b413e1c40293114837ab8912"},
+ {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:bc3c61ff92efdcc14af4a7b81da71d849c9acee51d8fd8ac9841a7620140d6c6"},
+ {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:b33c9b3d1327d821e28e9cc3a6512c14f8b17570ddb4cfb9a52247ed0fcc5d8b"},
+ {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:75e78360d1dd6d02eb288fd8275bb4d147d6e3f5337935c096d11dba1fa84748"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:621a90147a5e255fdc2a0fec2d56626b76b5d72ea9e60164c9a5a8976d45b0c9"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:0ca7a6b4fc1f9fafe990b95c8cda89099797e2cfbf40e55607f2f2f5a3355dcb"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b59bf823cfafde8ef1105d8984f26d1694dff165adb7198b12e3e068d7999b15"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:ce81b9c6aaa0f920e2ab05eb2b9f4ccd102e3016b2f37125593b16a83a4b0cc2"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:ae29fcd56152f417bfba50a36a56a7a5f9fb74ff80bab98704cac704de6568ab"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:ae31cb874f6f0cedbed457c6374e7e54d7ed45c1a4e11a65a9c80968da90a650"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-win32.whl", hash = "sha256:6db1f9fa1f52226621905f004278ce7bd90c8f5363ffd5d7ab3755363d98549a"},
+ {file = "pycryptodome-3.11.0-cp35-abi3-win_amd64.whl", hash = "sha256:d7e5f6f692421e5219aa3b545eb0cffd832cd589a4b9dcd4a5eb4260e2c0d68a"},
+ {file = "pycryptodome-3.11.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:da796e9221dda61a0019d01742337eb8a322de8598b678a4344ca0a436380315"},
+ {file = "pycryptodome-3.11.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:ed45ef92d21db33685b789de2c015e9d9a18a74760a8df1fc152faee88cdf741"},
+ {file = "pycryptodome-3.11.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4169ed515742425ff21e4bd3fabbb6994ffb64434472fb72230019bdfa36b939"},
+ {file = "pycryptodome-3.11.0-pp27-pypy_73-win32.whl", hash = "sha256:f19edd42368e9057c39492947bb99570dc927123e210008f2af7cf9b505c6892"},
+ {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:06162fcfed2f9deee8383fd59eaeabc7b7ffc3af50d3fad4000032deb8f700b0"},
+ {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:6eda8a3157c91ba60b26a07bedd6c44ab8bda6cd79b6b5ea9744ba62c39b7b1e"},
+ {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ff701fc283412e651eaab4319b3cd4eaa0827e94569cd37ee9075d5c05fe655"},
+ {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:2a4bcc8a9977fee0979079cd33a9e9f0d3ddba5660d35ffe874cf84f1dd399d2"},
+ {file = "pycryptodome-3.11.0.tar.gz", hash = "sha256:428096bbf7a77e207f418dfd4d7c284df8ade81d2dc80f010e92753a3e406ad0"},
+]
+pycryptodomex = [
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:7abfd84a362e4411f7c5f5758c18cbf377a2a2be64b9232e78544d75640c677e"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6a76d7821ae43df8a0e814cca32114875916b9fc2158603b364853de37eb9002"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:1580db5878b1d16a233550829f7c189c43005f7aa818f2f95c7dddbd6a7163cc"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c825611a951baad63faeb9ef1517ef96a20202d6029ae2485b729152cc703fab"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7cc5ee80b2d5ee8f59a761741cfb916a068c97cac5e700c8ce01e1927616aa2f"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:fbe09e3ae95f47c7551a24781d2e348974cde4a0b33bc3b1566f6216479db2b1"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-win32.whl", hash = "sha256:9eace1e5420abc4f9e76de01e49caca349b7c80bda9c1643193e23a06c2a332c"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:adc25aa8cfc537373dd46ae97863f16fd955edee14bf54d3eb52bde4e4ac8c7b"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cf30b5e03d974874185b989839c396d799f6e2d4b4d5b2d8bd3ba464eb3cc33f"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:c91772cf6808cc2d80279e80b491c48cb688797b6d914ff624ca95d855c24ee5"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:c391ec5c423a374a36b90f7c8805fdf51a0410a2b5be9cebd8990e0021cb6da4"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:64a83ab6f54496ab968a6f21a41a620afe0a742573d609fd03dcab7210645153"},
+ {file = "pycryptodomex-3.11.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:252ac9c1e1ae1c256a75539e234be3096f2d100b9f4bae42ef88067787b9b249"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:bf2ea67eaa1fff0aecef6da881144f0f91e314b4123491f9a4fa8df0598e48fe"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:fe2b8c464ba335e71aed74f830bf2b2881913f8905d166f9c0fe06ca44a1cb5e"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:ff0826f3886e85708a0e8ef7ec47020723b998cfed6ae47962d915fcb89ec780"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:1d4d13c59d2cfbc0863c725f5812d66ff0d6836ba738ef26a52e1291056a1c7c"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:2b586d13ef07fa6197b6348a48dbbe9525f4f496205de14edfa4e91d99e69672"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:f35ccfa44a1dd267e392cd76d8525cfcfabee61dd070e15ad2119c54c0c31ddf"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-win32.whl", hash = "sha256:5baf690d27f39f2ba22f06e8e32c5f1972573ca65db6bdbb8b2c7177a0112dab"},
+ {file = "pycryptodomex-3.11.0-cp35-abi3-win_amd64.whl", hash = "sha256:919cadcedad552e78349d1626115cfd246fc03ad469a4a62c91a12204f0f0d85"},
+ {file = "pycryptodomex-3.11.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:c10b2f6bcbaa9aa51fe08207654100074786d423b03482c0cbe44406ca92d146"},
+ {file = "pycryptodomex-3.11.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:91662b27f5aa8a6d2ad63be9a7d1a403e07bf3c2c5b265a7cc5cbadf6f988e06"},
+ {file = "pycryptodomex-3.11.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:207e53bdbf3a26de6e9dcf3ebaf67ba70a61f733f84c464eca55d278211c1b71"},
+ {file = "pycryptodomex-3.11.0-pp27-pypy_73-win32.whl", hash = "sha256:1dd4271d8d022216533c3547f071662b44d703fd5dbb632c4b5e77b3ee47567f"},
+ {file = "pycryptodomex-3.11.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c43ddcff251e8b427b3e414b026636617276e008a9d78a44a9195d4bdfcaa0fe"},
+ {file = "pycryptodomex-3.11.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:ef25d682d0d9ab25c5022a298b5cba9084c7b148a3e71846df2c67ea664eacc7"},
+ {file = "pycryptodomex-3.11.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:4c7c6418a3c08b2ebfc2cf50ce52de267618063b533083a2c73b40ec54a1b6f5"},
+ {file = "pycryptodomex-3.11.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:15d25c532de744648f0976c56bd10d07b2a44b7eb2a6261ffe2497980b1102d8"},
+ {file = "pycryptodomex-3.11.0.tar.gz", hash = "sha256:0398366656bb55ebdb1d1d493a7175fc48ade449283086db254ac44c7d318d6d"},
+]
+pygments = [
+ {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"},
+ {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"},
+]
+pyparsing = [
+ {file = "pyparsing-3.0.5-py3-none-any.whl", hash = "sha256:4881e3d2979f27b41a3a2421b10be9cbfa7ce2baa6c7117952222f8bbea6650c"},
+ {file = "pyparsing-3.0.5.tar.gz", hash = "sha256:9329d1c1b51f0f76371c4ded42c5ec4cc0be18456b22193e0570c2da98ed288b"},
+]
+pyqt5 = [
+ {file = "PyQt5-5.15.6-cp36-abi3-macosx_10_13_x86_64.whl", hash = "sha256:33ced1c876f6a26e7899615a5a4efef2167c263488837c7beed023a64cebd051"},
+ {file = "PyQt5-5.15.6-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:9d6efad0377aa78bf081a20ac752ce86096ded18f04c592d98f5b92dc879ad0a"},
+ {file = "PyQt5-5.15.6-cp36-abi3-win32.whl", hash = "sha256:9d2dcdaf82263ae56023410a7af15d1fd746c8e361733a7d0d1bd1f004ec2793"},
+ {file = "PyQt5-5.15.6-cp36-abi3-win_amd64.whl", hash = "sha256:f411ecda52e488e1d3c5cce7563e1b2ca9cf0b7531e3c25b03d9a7e56e07e7fc"},
+ {file = "PyQt5-5.15.6.tar.gz", hash = "sha256:80343bcab95ffba619f2ed2467fd828ffeb0a251ad7225be5fc06dcc333af452"},
+]
+pyqt5-qt5 = [
+ {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
+]
+pyqt5-sip = [
+ {file = "PyQt5_sip-12.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6d5bca2fc222d58e8093ee8a81a6e3437067bb22bc3f86d06ec8be721e15e90a"},
+ {file = "PyQt5_sip-12.9.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:d59af63120d1475b2bf94fe8062610720a9be1e8940ea146c7f42bb449d49067"},
+ {file = "PyQt5_sip-12.9.0-cp310-cp310-win32.whl", hash = "sha256:0fc9aefacf502696710b36cdc9fa2a61487f55ee883dbcf2c2a6477e261546f7"},
+ {file = "PyQt5_sip-12.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:485972daff2fb0311013f471998f8ec8262ea381bded244f9d14edaad5f54271"},
+ {file = "PyQt5_sip-12.9.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:d85002238b5180bce4b245c13d6face848faa1a7a9e5c6e292025004f2fd619a"},
+ {file = "PyQt5_sip-12.9.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:83c3220b1ca36eb8623ba2eb3766637b19eb0ce9f42336ad8253656d32750c0a"},
+ {file = "PyQt5_sip-12.9.0-cp36-cp36m-win32.whl", hash = "sha256:d8b2bdff7bbf45bc975c113a03b14fd669dc0c73e1327f02706666a7dd51a197"},
+ {file = "PyQt5_sip-12.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:69a3ad4259172e2b1aa9060de211efac39ddd734a517b1924d9c6c0cc4f55f96"},
+ {file = "PyQt5_sip-12.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:42274a501ab4806d2c31659170db14c282b8313d2255458064666d9e70d96206"},
+ {file = "PyQt5_sip-12.9.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a8701892a01a5a2a4720872361197cc80fdd5f49c8482d488ddf38c9c84f055"},
+ {file = "PyQt5_sip-12.9.0-cp37-cp37m-win32.whl", hash = "sha256:ac57d796c78117eb39edd1d1d1aea90354651efac9d3590aac67fa4983f99f1f"},
+ {file = "PyQt5_sip-12.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4347bd81d30c8e3181e553b3734f91658cfbdd8f1a19f254777f906870974e6d"},
+ {file = "PyQt5_sip-12.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c446971c360a0a1030282a69375a08c78e8a61d568bfd6dab3dcc5cf8817f644"},
+ {file = "PyQt5_sip-12.9.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fc43f2d7c438517ee33e929e8ae77132749c15909afab6aeece5fcf4147ffdb5"},
+ {file = "PyQt5_sip-12.9.0-cp38-cp38-win32.whl", hash = "sha256:055581c6fed44ba4302b70eeb82e979ff70400037358908f251cd85cbb3dbd93"},
+ {file = "PyQt5_sip-12.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:c5216403d4d8d857ec4a61f631d3945e44fa248aa2415e9ee9369ab7c8a4d0c7"},
+ {file = "PyQt5_sip-12.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a25b9843c7da6a1608f310879c38e6434331aab1dc2fe6cb65c14f1ecf33780e"},
+ {file = "PyQt5_sip-12.9.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:dd05c768c2b55ffe56a9d49ce6cc77cdf3d53dbfad935258a9e347cbfd9a5850"},
+ {file = "PyQt5_sip-12.9.0-cp39-cp39-win32.whl", hash = "sha256:4f8e05fe01d54275877c59018d8e82dcdd0bc5696053a8b830eecea3ce806121"},
+ {file = "PyQt5_sip-12.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:b09f4cd36a4831229fb77c424d89635fa937d97765ec90685e2f257e56a2685a"},
+ {file = "PyQt5_sip-12.9.0.tar.gz", hash = "sha256:d3e4489d7c2b0ece9d203ae66e573939f7f60d4d29e089c9f11daa17cfeaae32"},
+]
+pyrsistent = [
+ {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"},
+ {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da6e5e818d18459fa46fac0a4a4e543507fe1110e808101277c5a2b5bab0cd2d"},
+ {file = "pyrsistent-0.18.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5e4395bbf841693eaebaa5bb5c8f5cdbb1d139e07c975c682ec4e4f8126e03d2"},
+ {file = "pyrsistent-0.18.0-cp36-cp36m-win32.whl", hash = "sha256:527be2bfa8dc80f6f8ddd65242ba476a6c4fb4e3aedbf281dfbac1b1ed4165b1"},
+ {file = "pyrsistent-0.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2aaf19dc8ce517a8653746d98e962ef480ff34b6bc563fc067be6401ffb457c7"},
+ {file = "pyrsistent-0.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58a70d93fb79dc585b21f9d72487b929a6fe58da0754fa4cb9f279bb92369396"},
+ {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4916c10896721e472ee12c95cdc2891ce5890898d2f9907b1b4ae0f53588b710"},
+ {file = "pyrsistent-0.18.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:73ff61b1411e3fb0ba144b8f08d6749749775fe89688093e1efef9839d2dcc35"},
+ {file = "pyrsistent-0.18.0-cp37-cp37m-win32.whl", hash = "sha256:b29b869cf58412ca5738d23691e96d8aff535e17390128a1a52717c9a109da4f"},
+ {file = "pyrsistent-0.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:097b96f129dd36a8c9e33594e7ebb151b1515eb52cceb08474c10a5479e799f2"},
+ {file = "pyrsistent-0.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:772e94c2c6864f2cd2ffbe58bb3bdefbe2a32afa0acb1a77e472aac831f83427"},
+ {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1a9ff320fa699337e05edcaae79ef8c2880b52720bc031b219e5b5008ebbdef"},
+ {file = "pyrsistent-0.18.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cd3caef37a415fd0dae6148a1b6957a8c5f275a62cca02e18474608cb263640c"},
+ {file = "pyrsistent-0.18.0-cp38-cp38-win32.whl", hash = "sha256:e79d94ca58fcafef6395f6352383fa1a76922268fa02caa2272fff501c2fdc78"},
+ {file = "pyrsistent-0.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:a0c772d791c38bbc77be659af29bb14c38ced151433592e326361610250c605b"},
+ {file = "pyrsistent-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5ec194c9c573aafaceebf05fc400656722793dac57f254cd4741f3c27ae57b4"},
+ {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6b5eed00e597b5b5773b4ca30bd48a5774ef1e96f2a45d105db5b4ebb4bca680"},
+ {file = "pyrsistent-0.18.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:48578680353f41dca1ca3dc48629fb77dfc745128b56fc01096b2530c13fd426"},
+ {file = "pyrsistent-0.18.0-cp39-cp39-win32.whl", hash = "sha256:f3ef98d7b76da5eb19c37fda834d50262ff9167c65658d1d8f974d2e4d90676b"},
+ {file = "pyrsistent-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:404e1f1d254d314d55adb8d87f4f465c8693d6f902f67eb6ef5b4526dc58e6ea"},
+ {file = "pyrsistent-0.18.0.tar.gz", hash = "sha256:773c781216f8c2900b42a7b638d5b517bb134ae1acbebe4d1e8f1f41ea60eb4b"},
+]
+python-dateutil = [
+ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+ {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+]
+pytz = [
+ {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
+ {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
+]
+pywin32 = [
+ {file = "pywin32-302-cp310-cp310-win32.whl", hash = "sha256:251b7a9367355ccd1a4cd69cd8dd24bd57b29ad83edb2957cfa30f7ed9941efa"},
+ {file = "pywin32-302-cp310-cp310-win_amd64.whl", hash = "sha256:79cf7e6ddaaf1cd47a9e50cc74b5d770801a9db6594464137b1b86aa91edafcc"},
+ {file = "pywin32-302-cp36-cp36m-win32.whl", hash = "sha256:fe21c2fb332d03dac29de070f191bdbf14095167f8f2165fdc57db59b1ecc006"},
+ {file = "pywin32-302-cp36-cp36m-win_amd64.whl", hash = "sha256:d3761ab4e8c5c2dbc156e2c9ccf38dd51f936dc77e58deb940ffbc4b82a30528"},
+ {file = "pywin32-302-cp37-cp37m-win32.whl", hash = "sha256:48dd4e348f1ee9538dd4440bf201ea8c110ea6d9f3a5010d79452e9fa80480d9"},
+ {file = "pywin32-302-cp37-cp37m-win_amd64.whl", hash = "sha256:496df89f10c054c9285cc99f9d509e243f4e14ec8dfc6d78c9f0bf147a893ab1"},
+ {file = "pywin32-302-cp38-cp38-win32.whl", hash = "sha256:e372e477d938a49266136bff78279ed14445e00718b6c75543334351bf535259"},
+ {file = "pywin32-302-cp38-cp38-win_amd64.whl", hash = "sha256:543552e66936378bd2d673c5a0a3d9903dba0b0a87235ef0c584f058ceef5872"},
+ {file = "pywin32-302-cp39-cp39-win32.whl", hash = "sha256:2393c1a40dc4497fd6161b76801b8acd727c5610167762b7c3e9fd058ef4a6ab"},
+ {file = "pywin32-302-cp39-cp39-win_amd64.whl", hash = "sha256:af5aea18167a31efcacc9f98a2ca932c6b6a6d91ebe31f007509e293dea12580"},
+]
+pywinpty = [
+ {file = "pywinpty-1.1.5-cp310-none-win_amd64.whl", hash = "sha256:59e38276f732121b7b708b488055132c695ab7f8790b6ebee9b5b277e30c40e1"},
+ {file = "pywinpty-1.1.5-cp36-none-win_amd64.whl", hash = "sha256:0f73bea7f4ecc4711d3706bb0adea0b426c384ff38b619e169d58e20bc307eb0"},
+ {file = "pywinpty-1.1.5-cp37-none-win_amd64.whl", hash = "sha256:4cefeef61ab82e9e2bfe228d83a49117e33899931766dd18d576ea5c9187c1e0"},
+ {file = "pywinpty-1.1.5-cp38-none-win_amd64.whl", hash = "sha256:44c78a9a74f1b6bff957f8b0acad0525f48f716ac61fd9d39e1eb6f87f1a46a0"},
+ {file = "pywinpty-1.1.5-cp39-none-win_amd64.whl", hash = "sha256:ad12ddf276446e0440a760b7c0ba128d39602bc8e6641e0ef8447f1a466a8346"},
+ {file = "pywinpty-1.1.5.tar.gz", hash = "sha256:92125f0f8e4e64bb5f3bf270a182c9206dc1765542c59bc07441908a9db17504"},
+]
+pyyaml = [
+ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+ {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+ {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
+]
+pyzmq = [
+ {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd"},
+ {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149"},
+ {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93"},
+ {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea"},
+ {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f"},
+ {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3"},
+ {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e"},
+ {file = "pyzmq-22.3.0-cp310-cp310-win32.whl", hash = "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d"},
+ {file = "pyzmq-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-win32.whl", hash = "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9"},
+ {file = "pyzmq-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-win32.whl", hash = "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d"},
+ {file = "pyzmq-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d"},
+ {file = "pyzmq-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a"},
+ {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0"},
+ {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8"},
+ {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74"},
+ {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067"},
+ {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59"},
+ {file = "pyzmq-22.3.0-cp38-cp38-win32.whl", hash = "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7"},
+ {file = "pyzmq-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45"},
+ {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356"},
+ {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973"},
+ {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36"},
+ {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57"},
+ {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2"},
+ {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966"},
+ {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b"},
+ {file = "pyzmq-22.3.0-cp39-cp39-win32.whl", hash = "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b"},
+ {file = "pyzmq-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2"},
+ {file = "pyzmq-22.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f"},
+ {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf"},
+ {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb"},
+ {file = "pyzmq-22.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115"},
+ {file = "pyzmq-22.3.0.tar.gz", hash = "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c"},
+]
+qtconsole = [
+ {file = "qtconsole-5.2.0-py3-none-any.whl", hash = "sha256:a287f9f0f7365ccb2f2a88e0cd4da883822e94d15b75dc19098cd8eec44d70d1"},
+ {file = "qtconsole-5.2.0.tar.gz", hash = "sha256:6bb4df839609f240194213407872076f871e3a3884cf8e785068e8c7f39344c6"},
+]
+qtpy = [
+ {file = "QtPy-1.11.2-py2.py3-none-any.whl", hash = "sha256:83c502973e9fdd7b648d8267a421229ea3d9a0651c22e4c65a4d9228479c39b6"},
+ {file = "QtPy-1.11.2.tar.gz", hash = "sha256:d6e4ae3a41f1fcb19762b58f35ad6dd443b4bdc867a4cb81ef10ccd85403c92b"},
+]
+requests = [
+ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
+ {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
+]
+requests-oauthlib = [
+ {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
+ {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
+ {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"},
+]
+rosbag = []
+roscpp = []
+rosgraph = []
+rosgraph-msgs = []
+roslib = []
+rospkg = [
+ {file = "rospkg-1.3.0-py3-none-any.whl", hash = "sha256:dc8fab3ed3f163bc301f94ec65ed39a2c40ded6757615a0fb9f2cb2c82470fbe"},
+ {file = "rospkg-1.3.0.tar.gz", hash = "sha256:e024f9d617c55c76dd47530dceb0882c85cb83234450b8df3d1cb809e009f6cb"},
+]
+rospy = []
+rsa = [
+ {file = "rsa-4.7.2-py3-none-any.whl", hash = "sha256:78f9a9bf4e7be0c5ded4583326e7461e3a3c5aae24073648b4bdfa797d78c9d2"},
+ {file = "rsa-4.7.2.tar.gz", hash = "sha256:9d689e6ca1b3038bc82bf8d23e944b6b6037bc02301a574935b2dd946e0353b9"},
+]
+scipy = [
+ {file = "scipy-1.7.2-1-cp310-cp310-win_amd64.whl", hash = "sha256:dc2d1bf41294e63c7302bf499973ac0c7f73c93c01763db43055f6525234bf11"},
+ {file = "scipy-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97eb573e361a73a553b915dc195c6f72a08249964b1a33f157f9659f3b6210d1"},
+ {file = "scipy-1.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:359b60a0cccd17723b9d5e329a5212a710e771a3ddde800e472fb93732756c46"},
+ {file = "scipy-1.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82c5befebf54d799d77e5f0205c03030f57f69ba2541baa44d2e6ad138c28cd3"},
+ {file = "scipy-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:54951f51d731c832b1b8885e0a92e89f33d087de7e40d02078bf0d49c7cbdbb5"},
+ {file = "scipy-1.7.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:30bdda199667e74b50208a793eb1ba47a04e5e3fa16f5ff06c6f7969ae78e4da"},
+ {file = "scipy-1.7.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e3efe7ef75dfe627b354ab0af0dbc918eadee97cc80ff1aabea6d3e01114ebdd"},
+ {file = "scipy-1.7.2-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:17fd991a275e4283453f89d404209aa92059ac68d76d804b4bc1716a3742e1b5"},
+ {file = "scipy-1.7.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74f518ce542533054695f743e4271cb8986b63f95bb51d70fcee4f3929cbff7d"},
+ {file = "scipy-1.7.2-cp37-cp37m-win32.whl", hash = "sha256:1437073f1d4664990879aa8f9547524764372e0fef84a077be4b19e82bba7a8d"},
+ {file = "scipy-1.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:39f838ea5ce8da868785193d88d05cf5a6d5c390804ec99de29a28e1dcdd53e6"},
+ {file = "scipy-1.7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e08b81fcd9bf98740b58dc6fdd7879e33a64dcb682201c1135f7d4a75216bb05"},
+ {file = "scipy-1.7.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7b1d0f5f524518f1a86f288443528e4ff4a739c0966db663af4129b7ac7849f8"},
+ {file = "scipy-1.7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cac71d5476a6f56b50459da21f6221707e0051ebd428b2137db32ef4a43bb15e"},
+ {file = "scipy-1.7.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d175ba93e00d8eef8f7cd70d4d88a9106a86800c82ea03cf2268c36d6545483"},
+ {file = "scipy-1.7.2-cp38-cp38-win32.whl", hash = "sha256:8b5726a0fedeaa6beb1095e4466998bdd1d1e960b28db9b5a16c89cbd7b2ebf1"},
+ {file = "scipy-1.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:8482c8e45857ab0a5446eb7460d2307a27cbbe659d6d2257820c6d6eb950fd0f"},
+ {file = "scipy-1.7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1ea6233f5a365cb7945b4304bd06323ece3ece85d6a3fa8598d2f53e513467c9"},
+ {file = "scipy-1.7.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d86abd1ddf421dea5e9cebfeb4de0d205b3dc04e78249afedba9c6c3b2227ff2"},
+ {file = "scipy-1.7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d25272c03ee3c0fe5e0dff1bb7889280bb6c9e1766fa9c7bde81ad8a5f78694"},
+ {file = "scipy-1.7.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5273d832fb9cd5724ee0d335c16a903b923441107dd973d27fc4293075a9f4e3"},
+ {file = "scipy-1.7.2-cp39-cp39-win32.whl", hash = "sha256:87cf3964db0f1cce17aeed5bfc1b89a6b4b07dbfc48e50d21fa3549e00456803"},
+ {file = "scipy-1.7.2-cp39-cp39-win_amd64.whl", hash = "sha256:a80eb01c43fd98257ec7a49ff5cec0edba32031b5f86503f55399a48cb2c5379"},
+ {file = "scipy-1.7.2.tar.gz", hash = "sha256:fa2dbabaaecdb502641b0b3c00dec05fb475ae48655c66da16c9ed24eda1e711"},
+]
+send2trash = [
+ {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"},
+ {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"},
+]
+sensor-msgs = []
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+stable-baselines3 = []
+std-msgs = []
+supersuit = [
+ {file = "SuperSuit-3.3.1.tar.gz", hash = "sha256:bc9a7810425a4168c3db18138d1f60ae6c1e27f02f4e7a83f4a555a0010b1cef"},
+]
+tensorboard = [
+ {file = "tensorboard-2.7.0-py3-none-any.whl", hash = "sha256:239f78a4a8dff200ce585a030c787773a8c1184d5c159252f5f85bac4e3c3b38"},
+]
+tensorboard-data-server = [
+ {file = "tensorboard_data_server-0.6.1-py3-none-any.whl", hash = "sha256:809fe9887682d35c1f7d1f54f0f40f98bb1f771b14265b453ca051e2ce58fca7"},
+ {file = "tensorboard_data_server-0.6.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:fa8cef9be4fcae2f2363c88176638baf2da19c5ec90addb49b1cde05c95c88ee"},
+ {file = "tensorboard_data_server-0.6.1-py3-none-manylinux2010_x86_64.whl", hash = "sha256:d8237580755e58eff68d1f3abefb5b1e39ae5c8b127cc40920f9c4fb33f4b98a"},
+]
+tensorboard-plugin-wit = [
+ {file = "tensorboard_plugin_wit-1.8.0-py3-none-any.whl", hash = "sha256:2a80d1c551d741e99b2f197bb915d8a133e24adb8da1732b840041860f91183a"},
+]
+terminado = [
+ {file = "terminado-0.12.1-py3-none-any.whl", hash = "sha256:09fdde344324a1c9c6e610ee4ca165c4bb7f5bbf982fceeeb38998a988ef8452"},
+ {file = "terminado-0.12.1.tar.gz", hash = "sha256:b20fd93cc57c1678c799799d117874367cc07a3d2d55be95205b1a88fa08393f"},
+]
+testpath = [
+ {file = "testpath-0.5.0-py3-none-any.whl", hash = "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589"},
+ {file = "testpath-0.5.0.tar.gz", hash = "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417"},
+]
+tf = []
+tf2-msgs = []
+tf2-py = []
+tf2-ros = []
+tomli = [
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+torch = [
+ {file = "torch-1.10.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:56022b0ce94c54e95a2f63fc5a1494feb1fc3d5c7a9b35a62944651d03edef05"},
+ {file = "torch-1.10.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:13e1ffab502aa32d6841a018771b47028d02dbbc685c5b79cfd61db5464dae4e"},
+ {file = "torch-1.10.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3c0a942e0df104c80b0eedc30d2a19cdc3d28601bc6e280bf24b2e6255016d3b"},
+ {file = "torch-1.10.0-cp36-none-macosx_10_9_x86_64.whl", hash = "sha256:eea16c01af1980ba709c00e8d5e6c09bedb5b30f9fa2085f6a52a78d7dc4e125"},
+ {file = "torch-1.10.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b812e8d40d7037748da40bb695bd849e7b2e7faad4cd06df53d2cc4531926fda"},
+ {file = "torch-1.10.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:034df0b20603bfc81325094586647302891b9b20be7e36f152c7dd6af00deac1"},
+ {file = "torch-1.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67fc509e207b8e7330f2e76e77800950317d31d035a4d19593db991962afead4"},
+ {file = "torch-1.10.0-cp37-none-macosx_10_9_x86_64.whl", hash = "sha256:4499055547087d7ef7e8a754f09c2c4f1470297ae3e5490363dba66c75501b21"},
+ {file = "torch-1.10.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ab0cf330714c8f79a837c04784a7a5658b014cf5a4ca527e7b710155ae519cdf"},
+ {file = "torch-1.10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e01ba5946267014abfdb30248bcdbd457aaa20cff749febe7fc191e5ae096af4"},
+ {file = "torch-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:9013002adcb42bac05dcdbf0a03dd9f6bb5d7ab8b9817041c1176a014870786b"},
+ {file = "torch-1.10.0-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:aef7afb62e9b174b4e0e5e1e4a42e3bab3b8490a668d666f62f7d4517559fbf2"},
+ {file = "torch-1.10.0-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:d6185827b285780653cdd81d77a09fdca76a5b190d5986d552be2a5c442cfaa4"},
+ {file = "torch-1.10.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d82e68302c9b5c76ed585e04d61be0ca2184f70cb8ffeba8610570609ad5d7c9"},
+ {file = "torch-1.10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:e5822200bf80a1495ad98a2bb41803eeba4a85ce373e35fc65765f7f888f5374"},
+ {file = "torch-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:ca2c88fa4376e2648785029ab108e6e7abd784eb6535fc6036004b9254f9f7c1"},
+ {file = "torch-1.10.0-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:d6ef87470b44df9970e84542547d5ba7720bb89616602441df555a39b124e2bc"},
+ {file = "torch-1.10.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:eea675ec01ec4b4a0655fd2984f166a5ca3b933dae6ad4eb4e52eba7026dc176"},
+]
+tornado = [
+ {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"},
+ {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"},
+ {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"},
+ {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"},
+ {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"},
+ {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"},
+ {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"},
+ {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"},
+ {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"},
+ {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"},
+ {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"},
+ {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"},
+ {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"},
+ {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"},
+ {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"},
+ {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"},
+ {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"},
+ {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"},
+ {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"},
+ {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"},
+ {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"},
+ {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"},
+ {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"},
+ {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"},
+ {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"},
+ {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"},
+]
+traitlets = [
+ {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"},
+ {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
+]
+typing-extensions = [
+ {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
+ {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
+ {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
+ {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
+]
+wcwidth = [
+ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+]
+webencodings = [
+ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
+ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
+]
+werkzeug = [
+ {file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
+ {file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"},
+]
+widgetsnbextension = [
+ {file = "widgetsnbextension-3.5.2-py2.py3-none-any.whl", hash = "sha256:763a9fdc836d141fa080005a886d63f66f73d56dba1fb5961afc239c77708569"},
+ {file = "widgetsnbextension-3.5.2.tar.gz", hash = "sha256:e0731a60ba540cd19bbbefe771a9076dcd2dde90713a8f87f27f53f2d1db7727"},
+]
+zipp = [
+ {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
+ {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 000000000..17871ff86
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,40 @@
+[tool.poetry]
+name = "arena-rosnav"
+version = "0.1.0"
+description = ""
+authors = ["Your Name "]
+
+[[tool.poetry.source]]
+name = "ros"
+url = "https://rospypi.github.io/simple/"
+secondary = true
+
+[tool.poetry.dependencies]
+python = ">=3.8,<3.10"
+rospy = "^1.15.7"
+rosbag = "^1.15.7"
+tf = "^1.12.1"
+tf2-ros = "^0.6.5"
+PyYAML = "^5.4.1"
+catkin-pkg = "^0.4.23"
+netifaces = "^0.10.9"
+pathlib = "^1.0.1"
+empy = "^3.3.4"
+setuptools = "^56.2.0"
+filelock = "^3.0.12"
+PyQt5 = "^5.15.4"
+scipy = "^1.7.1"
+PettingZoo = "1.13.1"
+SuperSuit = "^3.3.0"
+stable-baselines3 = {git = "https://github.com/ignc-research/stable-baselines3", rev = "marl"}
+tensorboard = "^2.7.0"
+notebook = "^6.4.5"
+jupyter = "^1.0.0"
+
+[tool.poetry.dev-dependencies]
+pdoc3 = "^0.10.0"
+black = {version = "^22.3.0", allow-prereleases = true}
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/task_generator/scripts/__pycache__/clear_costmap.cpython-38.pyc b/task_generator/scripts/__pycache__/clear_costmap.cpython-38.pyc
index 7746ab08d..83286bbc7 100644
Binary files a/task_generator/scripts/__pycache__/clear_costmap.cpython-38.pyc and b/task_generator/scripts/__pycache__/clear_costmap.cpython-38.pyc differ
diff --git a/task_generator/task_generator/__pycache__/__init__.cpython-38.pyc b/task_generator/task_generator/__pycache__/__init__.cpython-38.pyc
index 6b95fc612..c9fe4e596 100644
Binary files a/task_generator/task_generator/__pycache__/__init__.cpython-38.pyc and b/task_generator/task_generator/__pycache__/__init__.cpython-38.pyc differ
diff --git a/task_generator/task_generator/__pycache__/obstacles_manager.cpython-38.pyc b/task_generator/task_generator/__pycache__/obstacles_manager.cpython-38.pyc
index 101cdfd3b..657abe12b 100644
Binary files a/task_generator/task_generator/__pycache__/obstacles_manager.cpython-38.pyc and b/task_generator/task_generator/__pycache__/obstacles_manager.cpython-38.pyc differ
diff --git a/task_generator/task_generator/__pycache__/robot_manager.cpython-38.pyc b/task_generator/task_generator/__pycache__/robot_manager.cpython-38.pyc
index 8149f67f5..f8ac9e363 100644
Binary files a/task_generator/task_generator/__pycache__/robot_manager.cpython-38.pyc and b/task_generator/task_generator/__pycache__/robot_manager.cpython-38.pyc differ
diff --git a/task_generator/task_generator/__pycache__/tasks.cpython-38.pyc b/task_generator/task_generator/__pycache__/tasks.cpython-38.pyc
index c1b60355d..812775137 100644
Binary files a/task_generator/task_generator/__pycache__/tasks.cpython-38.pyc and b/task_generator/task_generator/__pycache__/tasks.cpython-38.pyc differ
diff --git a/task_generator/task_generator/__pycache__/utils.cpython-38.pyc b/task_generator/task_generator/__pycache__/utils.cpython-38.pyc
index d032aeb82..53fc29e7f 100644
Binary files a/task_generator/task_generator/__pycache__/utils.cpython-38.pyc and b/task_generator/task_generator/__pycache__/utils.cpython-38.pyc differ
diff --git a/task_generator/task_generator/marl_robot_manager.py b/task_generator/task_generator/marl_robot_manager.py
new file mode 100755
index 000000000..414eec98e
--- /dev/null
+++ b/task_generator/task_generator/marl_robot_manager.py
@@ -0,0 +1,296 @@
+# from math import ceil, sqrt
+from typing import Union
+
+import math
+import os
+
+from torch.nn.modules.module import T
+import rospy
+import rospkg
+import tf
+import yaml
+
+from flatland_msgs.srv import (
+ MoveModel,
+ MoveModelRequest,
+ SpawnModelRequest,
+ SpawnModel,
+ StepWorld,
+)
+from geometry_msgs.msg import Pose2D, PoseStamped
+from nav_msgs.msg import OccupancyGrid
+
+from .utils import generate_freespace_indices, get_random_pos_on_map
+
+
+class RobotManager:
+ """
+ A manager class using flatland provided services to spawn, move and delete Robot. Currently only one robot
+ is managed
+ """
+
+ def __init__(
+ self,
+ ns: str,
+ map_: OccupancyGrid,
+ robot_yaml_path: str,
+ robot_type: str,
+ robot_id: str = "myrobot",
+ timeout=20,
+ ) -> None:
+ """[summary]
+
+ Args:
+ ns(namespace): if ns == '', we will use global namespace
+ map_ (OccupancyGrid): the map info
+ robot_yaml_path (str): the file name of the base robot yaml file.
+
+ """
+ """SET A METHOD TO EXTRACT IF MARL IS BEIN REQUESTED"""
+ MARL = rospy.get_param("num_robots") > 1
+
+ self.ns = ns
+ self.ns_prefix = "" if ns == "" else "/" + ns + "/"
+
+ self.robot_id = robot_id
+ self.robot_type = robot_type
+
+ self.is_training_mode = rospy.get_param("/train_mode")
+ self.step_size = rospy.get_param("step_size")
+
+ self._get_robot_config(robot_yaml_path)
+ robot_yaml_path = (
+ self._generate_robot_config_with_adjusted_topics()
+ if MARL
+ else robot_yaml_path
+ )
+
+ # setup proxy to handle services provided by flatland
+ rospy.wait_for_service(f"{self.ns_prefix}move_model", timeout=timeout)
+ rospy.wait_for_service(f"{self.ns_prefix}spawn_model", timeout=timeout)
+
+ self._srv_move_model = rospy.ServiceProxy(
+ f"{self.ns_prefix}move_model", MoveModel
+ )
+ self._srv_spawn_model = rospy.ServiceProxy(
+ f"{self.ns_prefix}spawn_model", SpawnModel
+ )
+ # it's only needed in training mode to send the clock signal.
+ self._step_world = rospy.ServiceProxy(f"{self.ns_prefix}step_world", StepWorld)
+
+ # publisher
+ goal_topic = (
+ f"{self.ns_prefix}{self.robot_id}/goal" if MARL else f"{self.ns_prefix}goal"
+ )
+ self._goal_pub = rospy.Publisher(
+ f"{goal_topic}", PoseStamped, queue_size=1, latch=True
+ )
+
+ self.update_map(map_)
+ self._spawn_robot(robot_yaml_path)
+
+ # remove temporary config file
+ if MARL:
+ os.remove(robot_yaml_path)
+
+ def _spawn_robot(self, robot_yaml_path: str) -> None:
+ request = SpawnModelRequest()
+ request.yaml_path = robot_yaml_path
+ request.name = self.robot_id
+ request.ns = self.ns
+ self._srv_spawn_model(request)
+
+ def _get_robot_config(self, robot_yaml_path: str) -> None:
+ """Get robot info e.g robot name, radius, Laser related infomation
+
+ Args:
+ robot_yaml_path ([type]): [description]
+ """
+ robot_config = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "action_spaces",
+ "default_settings_" + self.robot_type + ".yaml",
+ )
+ with open(robot_config, "r", encoding="utf-8") as target:
+ config = yaml.load(target, Loader=yaml.FullLoader)
+ self.ROBOT_RADIUS = config["robot"]["radius"]
+
+ with open(robot_yaml_path, "r") as f:
+ self._robot_data = yaml.safe_load(f)
+
+ # get laser_update_rate
+ for plugin in self._robot_data["plugins"]:
+ if plugin["type"] == "Laser":
+ self.LASER_UPDATE_RATE = plugin.setdefault("update_rate", 1)
+
+ def _generate_robot_config_with_adjusted_topics(self) -> str:
+ """Generates a robot-specific config file (yaml) where the publication \
+ and subscription topics are adjusted with the robot's namespace.
+
+ Returns:
+ str: Path of the robot-specific adjusted config file.
+
+ Note:
+ - The namespaces consist of: [simulation ns] / [robot name] / *topic*\
+ e.g.: sim_1/myrobot/scan
+ - The yaml files are temporarily dumped into *../simulator_setup/tmp_robot_configs*
+ """
+ self._robot_data["bodies"][0]["name"] = (
+ self.robot_id + "_" + self._robot_data["bodies"][0]["name"]
+ )
+
+ for plugin in self._robot_data["plugins"]:
+ if plugin["type"] == "DiffDrive":
+ plugin["body"] = self._robot_data["bodies"][0]["name"]
+ plugin["odom_frame_id"] = self.robot_id + "_" + plugin["odom_frame_id"]
+ plugin["odom_pub"] = self.robot_id + "/" + plugin["odom_pub"]
+ plugin["twist_sub"] = (
+ self.robot_id + "/" + plugin.get("twist_sub", "cmd_vel")
+ )
+
+ elif plugin["type"] == "Laser":
+ plugin["topic"] = self.robot_id + "/" + plugin["topic"]
+ plugin["body"] = self._robot_data["bodies"][0]["name"]
+ plugin["frame"] = self.robot_id + "_" + plugin["frame"]
+
+ tmp_folder_path = os.path.join(
+ rospkg.RosPack().get_path("simulator_setup"), "tmp_robot_configs"
+ )
+ os.makedirs(tmp_folder_path, exist_ok=True)
+ tmp_config_name = self.ns + self.robot_id + ".robot_config.yaml"
+ tmp_config_path = os.path.join(tmp_folder_path, tmp_config_name)
+
+ with open(tmp_config_path, "w") as fd:
+ yaml.dump(self._robot_data, fd)
+
+ return tmp_config_path
+
+ def update_map(self, new_map: OccupancyGrid):
+ self.map = new_map
+ # a tuple stores the indices of the non-occupied spaces. format ((y,....),(x,...)
+ self._free_space_indices = generate_freespace_indices(self.map)
+
+ def move_robot(self, pose: Pose2D):
+ """Move the robot to a given position.
+
+ Args:
+ pose (Pose2D): Target postion.
+ """
+ # call service move_model
+
+ srv_request = MoveModelRequest()
+ srv_request.name = self.robot_id
+ srv_request.pose = pose
+
+ # call service
+ self._srv_move_model(srv_request)
+ if self.is_training_mode:
+ # a necessaray procedure to let the flatland publish the
+ # laser,odom's Transformation, which are needed for creating
+ # global path
+ # assert self.step_size * \
+ # self.LASER_UPDATE_RATE == 1, f"TO run the traning successfully, make sure the laser_update_rate*step_size == 1 \
+ # \n\tcurrent step_size:\t {self.step_size}\n\tcurrent laser's update rate:\t {self.LASER_UPDATE_RATE} "
+ for _ in range(math.ceil(1 / (self.step_size * self.LASER_UPDATE_RATE))):
+ self._step_world()
+
+ def set_start_pos_random(self):
+ start_pos = Pose2D()
+ start_pos.x, start_pos, start_pos.theta = get_random_pos_on_map(
+ self._free_space_indices, self.map, self.ROBOT_RADIUS * 2
+ )
+ self.move_robot(start_pos)
+
+ def set_start_pos_goal_pos(
+ self,
+ start_pos: Union[Pose2D, None] = None,
+ goal_pos: Union[Pose2D, None] = None,
+ min_dist=1,
+ forbidden_zones: Union[list, None] = None,
+ ):
+ """Set up start position and the goal postion. Path validation checking will be conducted. If it failed, an
+ exception will be raised.
+
+ Args:
+ start_pos (Union[Pose2D,None], optional): start position. if None, it will be set randomly. Defaults to None.
+ goal_pos (Union[Pose2D,None], optional): [description]. if None, it will be set randomly .Defaults to None.
+ min_dist (float): minimum distance between start_pos and goal_pos
+ forbidden_zones (list): a list of tuples with the format (x,y,r), where the the robot should not be reset.
+ Exception:
+ Exception("can not generate a path with the given start position and the goal position of the robot")
+ """
+
+ def dist(x1, y1, x2, y2):
+ return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
+
+ if start_pos is None or goal_pos is None:
+ # if any of them need to be random generated, we set a higher threshold,otherwise only try once
+ max_try_times = 20
+ else:
+ max_try_times = 1
+
+ i_try = 0
+ start_pos_ = None
+ goal_pos_ = None
+ while i_try < max_try_times:
+
+ if start_pos is None:
+ start_pos_ = Pose2D()
+ (start_pos_.x, start_pos_.y, start_pos_.theta,) = get_random_pos_on_map(
+ self._free_space_indices,
+ self.map,
+ self.ROBOT_RADIUS * 2,
+ forbidden_zones=forbidden_zones,
+ )
+ else:
+ start_pos_ = start_pos
+
+ if goal_pos is None:
+ goal_pos_ = Pose2D()
+ (goal_pos_.x, goal_pos_.y, goal_pos_.theta,) = get_random_pos_on_map(
+ self._free_space_indices, self.map, self.ROBOT_RADIUS * 2
+ )
+ else:
+ goal_pos_ = goal_pos
+
+ if dist(start_pos_.x, start_pos_.y, goal_pos_.x, goal_pos_.y) < min_dist:
+ i_try += 1
+ continue
+ # move the robot to the start pos
+ self.move_robot(start_pos_)
+ try:
+ # publish the goal, if the gobal plath planner can't generate a path, a, exception will be raised.
+ self.publish_goal(goal_pos_.x, goal_pos_.y, goal_pos_.theta)
+ break
+ except rospy.ServiceException:
+ i_try += 1
+ if i_try == max_try_times:
+ # TODO Define specific type of Exception
+ raise rospy.ServiceException(
+ "can not generate a path with the given start position and the goal position of the robot"
+ )
+ else:
+ return start_pos_, goal_pos_
+
+ def publish_goal(self, x, y, theta):
+ """
+ Publishing goal (x, y, theta)
+ :param x x-position of the goal
+ :param y y-position of the goal
+ :param theta theta-position of the goal
+ """
+ goal = PoseStamped()
+ goal.header.stamp = rospy.get_rostime()
+ goal.header.frame_id = "map"
+ goal.pose.position.x = x
+ goal.pose.position.y = y
+ quaternion = tf.transformations.quaternion_from_euler(0, 0, 0)
+ goal.pose.orientation.w = quaternion[0]
+ goal.pose.orientation.x = quaternion[1]
+ goal.pose.orientation.y = quaternion[2]
+ goal.pose.orientation.z = quaternion[3]
+ self._goal_pub.publish(goal)
+
+ def __mean_square_dist_(self, x, y):
+ return math.sqrt(math.pow(x, 2) + math.pow(y, 2))
diff --git a/task_generator/task_generator/marl_tasks.py b/task_generator/task_generator/marl_tasks.py
new file mode 100644
index 000000000..14989314d
--- /dev/null
+++ b/task_generator/task_generator/marl_tasks.py
@@ -0,0 +1,309 @@
+from typing import Type, Union, List
+
+import json
+import os
+import rospkg
+import rospy
+import yaml
+
+from abc import ABC, abstractmethod
+from enum import Enum, unique
+from threading import Lock
+from filelock import FileLock
+
+from nav_msgs.msg import OccupancyGrid
+from nav_msgs.srv import GetMap
+from std_msgs.msg import Bool
+
+from .obstacles_manager import ObstaclesManager
+from .marl_robot_manager import RobotManager
+
+
+class ABSMARLTask(ABC):
+ """An abstract class for the DRL agent navigation tasks."""
+
+ def __init__(
+ self,
+ obstacles_manager: ObstaclesManager,
+ robot_manager: List[RobotManager],
+ ):
+ self.obstacles_manager = obstacles_manager
+ self.robot_manager = robot_manager
+ self._service_client_get_map = rospy.ServiceProxy("/static_map", GetMap)
+ self._map_lock = Lock()
+ rospy.Subscriber("/map", OccupancyGrid, self._update_map)
+ # a mutex keep the map is not unchanged during reset task.
+
+ @abstractmethod
+ def reset(self):
+ """
+ Funciton to reset the task/scenery. Make sure that _map_lock is used.
+ """
+
+ def _update_map(self, map_: OccupancyGrid):
+ with self._map_lock:
+ self.obstacles_manager.update_map(map_)
+ for manager in self.robot_manager:
+ manager.update_map(map_)
+
+
+class RandomMARLTask(ABSMARLTask):
+ """Sets a randomly drawn start and goal position for each robot episodically."""
+
+ def __init__(
+ self,
+ obstacles_manager: ObstaclesManager,
+ robot_manager: List[RobotManager],
+ ):
+ super().__init__(obstacles_manager, robot_manager)
+ self._num_robots = len(self.robot_manager)
+
+ def reset(self):
+ """[summary]"""
+ with self._map_lock:
+ max_fail_times = 3
+ fail_times = 0
+ while fail_times < max_fail_times:
+ try:
+ starts, goals = [None] * self._num_robots, [None] * self._num_robots
+ for i, manager in enumerate(self.robot_manager):
+ start_pos, goal_pos = manager.set_start_pos_goal_pos(
+ forbidden_zones=starts
+ )
+ starts[i] = (
+ start_pos.x,
+ start_pos.y,
+ manager.ROBOT_RADIUS * 1.5,
+ )
+ goals[i] = (
+ goal_pos.x,
+ goal_pos.y,
+ manager.ROBOT_RADIUS * 1.5,
+ )
+ self.obstacles_manager.reset_pos_obstacles_random(
+ forbidden_zones=starts + goals
+ )
+ break
+ except rospy.ServiceException as e:
+ rospy.logwarn(repr(e))
+ fail_times += 1
+ if fail_times == max_fail_times:
+ raise Exception("reset error!")
+
+
+class StagedMARLRandomTask(RandomMARLTask):
+ """
+ Enforces the paradigm of curriculum learning.
+ The training stages are defined in 'training_curriculum.yaml'
+ """
+
+ def __init__(
+ self,
+ ns: str,
+ obstacles_manager: ObstaclesManager,
+ robot_manager: List[RobotManager],
+ start_stage: int = 1,
+ PATHS=None,
+ ) -> None:
+ super().__init__(obstacles_manager, robot_manager)
+ self.ns = ns
+ self.ns_prefix = f"/{ns}/" if ns else ""
+
+ self._curr_stage = start_stage
+ self._stages = {}
+ self._PATHS = PATHS
+ self._read_stages_from_yaml()
+
+ # check start stage format
+ if not isinstance(start_stage, int):
+ raise ValueError("Given start_stage not an Integer!")
+ if self._curr_stage < 1 or self._curr_stage > len(self._stages):
+ raise IndexError(
+ "Start stage given for training curriculum out of bounds! Has to be between {1 to %d}!"
+ % len(self._stages)
+ )
+ rospy.set_param("/curr_stage", self._curr_stage)
+
+ # hyperparamters.json location
+ self.json_file = os.path.join(self._PATHS.get("model"), "hyperparameters.json")
+ if not rospy.get_param("debug_mode"):
+ assert os.path.isfile(self.json_file), (
+ "Found no 'hyperparameters.json' at %s" % self.json_file
+ )
+
+ self._lock_json = FileLock(f"{self.json_file}.lock")
+
+ # subs for triggers
+ self._sub_next = rospy.Subscriber(
+ f"{self.ns_prefix}next_stage", Bool, self.next_stage
+ )
+ self._sub_previous = rospy.Subscriber(
+ f"{self.ns_prefix}previous_stage", Bool, self.previous_stage
+ )
+
+ self._initiate_stage()
+
+ def next_stage(self, *args, **kwargs):
+ if self._curr_stage < len(self._stages):
+ self._curr_stage = self._curr_stage + 1
+ self._initiate_stage()
+
+ if self.ns == "eval_sim":
+ rospy.set_param("/curr_stage", self._curr_stage)
+ if not rospy.get_param("debug_mode"):
+ with self._lock_json:
+ self._update_curr_stage_json()
+
+ if self._curr_stage == len(self._stages):
+ rospy.set_param("/last_stage_reached", True)
+ else:
+ print(
+ f"({self.ns}) INFO: Tried to trigger next stage but already reached last one"
+ )
+
+ def previous_stage(self, *args, **kwargs):
+ if self._curr_stage > 1:
+ rospy.set_param("/last_stage_reached", False)
+
+ self._curr_stage = self._curr_stage - 1
+ self._initiate_stage()
+
+ if self.ns == "eval_sim":
+ rospy.set_param("/curr_stage", self._curr_stage)
+ with self._lock_json:
+ self._update_curr_stage_json()
+ else:
+ print(
+ f"({self.ns}) INFO: Tried to trigger previous stage but already reached first one"
+ )
+
+ def _initiate_stage(self):
+ self._remove_obstacles()
+
+ static_obstacles = self._stages[self._curr_stage]["static"]
+ dynamic_obstacles = self._stages[self._curr_stage]["dynamic"]
+
+ self.obstacles_manager.register_random_static_obstacles(
+ self._stages[self._curr_stage]["static"]
+ )
+ self.obstacles_manager.register_random_dynamic_obstacles(
+ self._stages[self._curr_stage]["dynamic"]
+ )
+
+ print(
+ f"({self.ns}) Stage {self._curr_stage}:"
+ f"Spawning {static_obstacles} static and {dynamic_obstacles} dynamic obstacles!"
+ )
+
+ def _read_stages_from_yaml(self):
+ file_location = self._PATHS.get("curriculum")
+ if os.path.isfile(file_location):
+ with open(file_location, "r") as file:
+ self._stages = yaml.load(file, Loader=yaml.FullLoader)
+ assert isinstance(
+ self._stages, dict
+ ), "'training_curriculum.yaml' has wrong fromat! Has to encode dictionary!"
+ else:
+ raise FileNotFoundError(
+ "Couldn't find 'training_curriculum.yaml' in %s "
+ % self._PATHS.get("curriculum")
+ )
+
+ def _update_curr_stage_json(self):
+ with open(self.json_file, "r") as file:
+ hyperparams = json.load(file)
+ try:
+ hyperparams["curr_stage"] = self._curr_stage
+ except Exception as e:
+ raise Warning(
+ f" {e} \n Parameter 'curr_stage' not found in 'hyperparameters.json'!"
+ )
+ else:
+ with open(self.json_file, "w", encoding="utf-8") as target:
+ json.dump(hyperparams, target, ensure_ascii=False, indent=4)
+
+ def _remove_obstacles(self):
+ self.obstacles_manager.remove_obstacles()
+
+
+@unique
+class ARENA_TASKS(Enum):
+ MANUAL = "manual"
+ RANDOM = "random"
+ STAGED = "staged"
+ SCENARIO = "scenario"
+
+
+def get_mode(mode: str) -> ARENA_TASKS:
+ return ARENA_TASKS(mode)
+
+
+def get_MARL_task(
+ ns: str,
+ mode: str,
+ robot_ids: List[str],
+ PATHS: dict,
+ start_stage: int = 1,
+) -> ABSMARLTask:
+ """Function to return desired navigation task manager.
+
+ Args:
+ ns (str): Environments' ROS namespace. There should only be one env per ns.
+ mode (str): avigation task mode for the agents. Modes to chose from: ['random', 'staged']. \
+ Defaults to "random".
+ robot_ids (List[str]): List containing all robots' names in order to address the right namespaces.
+ start_stage (int, optional): Starting difficulty level for the learning curriculum. Defaults to 1.
+ PATHS (dict, optional): Dictionary containing program related paths. Defaults to None.
+
+ Raises:
+ NotImplementedError: The manual task mode is currently not implemented.
+ NotImplementedError: The scenario task mode is currently not implemented.
+
+ Returns:
+ ABSMARLTask: A task manager instance.
+ """
+ assert type(robot_ids) is list
+
+ task_mode = get_mode(mode)
+
+ # get the map
+ service_client_get_map = rospy.ServiceProxy("/static_map", GetMap)
+ map_response = service_client_get_map()
+
+ # use rospkg to get the path where the model config yaml file stored
+ models_folder_path = rospkg.RosPack().get_path("simulator_setup")
+
+ # robot's yaml file is needed to get its configurations etc.
+ robot_model = rospy.get_param("model")
+ base_robot_yaml = os.path.join(
+ models_folder_path, "robot", f"{robot_model}.model.yaml"
+ )
+
+ robot_manager = [
+ RobotManager(
+ ns=ns,
+ map_=map_response.map,
+ robot_yaml_path=base_robot_yaml,
+ robot_type=robot_model,
+ robot_id=name,
+ )
+ for name in robot_ids
+ ]
+
+ obstacles_manager = ObstaclesManager(ns, map_response.map)
+
+ task = None
+ if task_mode == ARENA_TASKS.MANUAL:
+ raise NotImplementedError
+ if task_mode == ARENA_TASKS.RANDOM:
+ rospy.set_param("/task_mode", "random")
+ obstacles_manager.register_random_obstacles(10, 1.0)
+ task = RandomMARLTask(obstacles_manager, robot_manager)
+ if task_mode == ARENA_TASKS.STAGED:
+ rospy.set_param("/task_mode", "staged")
+ task = StagedMARLRandomTask(
+ ns, obstacles_manager, robot_manager, start_stage, PATHS
+ )
+ if task_mode == ARENA_TASKS.SCENARIO:
+ raise NotImplementedError
+ return task
diff --git a/task_generator/task_generator/robot_manager.py b/task_generator/task_generator/robot_manager.py
index 89f9ef0db..97baca81e 100755
--- a/task_generator/task_generator/robot_manager.py
+++ b/task_generator/task_generator/robot_manager.py
@@ -1,5 +1,6 @@
# from math import ceil, sqrt
import math
+import rospkg
import yaml
import os
import threading
@@ -26,9 +27,7 @@ class RobotManager:
is managed
"""
- def __init__(
- self, ns: str, map_: OccupancyGrid, robot_yaml_path: str, timeout=20
- ):
+ def __init__(self, ns: str, map_: OccupancyGrid, robot_yaml_path: str, timeout=20):
"""[summary]
Args:
@@ -54,9 +53,7 @@ def __init__(
f"{self.ns_prefix}spawn_model", SpawnModel
)
# it's only needed in training mode to send the clock signal.
- self._step_world = rospy.ServiceProxy(
- f"{self.ns_prefix}step_world", StepWorld
- )
+ self._step_world = rospy.ServiceProxy(f"{self.ns_prefix}step_world", StepWorld)
# publisher
# publish the start position of the robot
@@ -99,7 +96,18 @@ def _get_robot_configration(self, robot_yaml_path):
robot_yaml_path ([type]): [description]
"""
self.ROBOT_NAME = os.path.basename(robot_yaml_path).split(".")[0]
- self.ROBOT_RADIUS = rospy.get_param("radius")
+
+ robot_config = os.path.join(
+ rospkg.RosPack().get_path("arena_local_planner_drl"),
+ "configs",
+ "action_spaces",
+ "default_settings_" + self.ROBOT_NAME + ".yaml",
+ )
+
+ with open(robot_config, "r", encoding="utf-8") as target:
+ config = yaml.load(target, Loader=yaml.FullLoader)
+ self.ROBOT_RADIUS = config["robot"]["radius"]
+
with open(robot_yaml_path, "r") as f:
robot_data = yaml.safe_load(f)
@@ -134,9 +142,7 @@ def move_robot(self, pose: Pose2D):
# assert self.step_size * \
# self.LASER_UPDATE_RATE == 1, f"TO run the traning successfully, make sure the laser_update_rate*step_size == 1 \
# \n\tcurrent step_size:\t {self.step_size}\n\tcurrent laser's update rate:\t {self.LASER_UPDATE_RATE} "
- for _ in range(
- math.ceil(1 / (self.step_size * self.LASER_UPDATE_RATE))
- ):
+ for _ in range(math.ceil(1 / (self.step_size * self.LASER_UPDATE_RATE))):
self._step_world()
def set_start_pos_random(self):
@@ -179,31 +185,20 @@ def dist(x1, y1, x2, y2):
if start_pos is None:
start_pos_ = Pose2D()
- (
- start_pos_.x,
- start_pos_.y,
- start_pos_.theta,
- ) = get_random_pos_on_map(
+ (start_pos_.x, start_pos_.y, start_pos_.theta,) = get_random_pos_on_map(
self._free_space_indices, self.map, self.ROBOT_RADIUS * 2
)
else:
start_pos_ = start_pos
if goal_pos is None:
goal_pos_ = Pose2D()
- (
- goal_pos_.x,
- goal_pos_.y,
- goal_pos_.theta,
- ) = get_random_pos_on_map(
+ (goal_pos_.x, goal_pos_.y, goal_pos_.theta,) = get_random_pos_on_map(
self._free_space_indices, self.map, self.ROBOT_RADIUS * 2
)
else:
goal_pos_ = goal_pos
- if (
- dist(start_pos_.x, start_pos_.y, goal_pos_.x, goal_pos_.y)
- < min_dist
- ):
+ if dist(start_pos_.x, start_pos_.y, goal_pos_.x, goal_pos_.y) < min_dist:
i_try += 1
continue
# move the robot to the start pos
diff --git a/task_generator/task_generator/utils.py b/task_generator/task_generator/utils.py
index 9653e8af0..eac16f5d9 100755
--- a/task_generator/task_generator/utils.py
+++ b/task_generator/task_generator/utils.py
@@ -30,7 +30,11 @@ def get_random_pos_on_map(free_space_indices, map_: OccupancyGrid, safe_dist: fl
def is_pos_valid(x_in_meters, y_in_meters):
for forbidden_zone in forbidden_zones:
- if (x_in_meters-forbidden_zone[0])**2+(y_in_meters-forbidden_zone[1])**2 < (forbidden_zone[2]+safe_dist)**2:
+ if (
+ forbidden_zone
+ and (x_in_meters - forbidden_zone[0]) ** 2 + (y_in_meters - forbidden_zone[1]) ** 2
+ < (forbidden_zone[2] + safe_dist) ** 2
+ ):
return False
# in pixel
@@ -48,8 +52,7 @@ def is_pos_valid(x_in_meters, y_in_meters):
try:
value = map_.data[index]
except IndexError:
- print("IndexError: index: %d, map_length: %d" %
- (index, len(map_.data)))
+ print("IndexError: index: %d, map_length: %d" % (index, len(map_.data)))
return False
if value != 0:
@@ -57,7 +60,8 @@ def is_pos_valid(x_in_meters, y_in_meters):
return True
assert len(free_space_indices) == 2 and len(free_space_indices[0]) == len(
- free_space_indices[1]), "free_space_indices is not correctly setup"
+ free_space_indices[1]
+ ), "free_space_indices is not correctly setup"
if forbidden_zones is None:
forbidden_zones = []
@@ -66,7 +70,7 @@ def is_pos_valid(x_in_meters, y_in_meters):
n_check_failed = 0
x_in_meters, y_in_meters = None, None
while not pos_valid:
- idx = random.randint(0, n_freespace_cells-1)
+ idx = random.randint(0, n_freespace_cells - 1)
# in cells
y_in_cells, x_in_cells = free_space_indices[0][idx], free_space_indices[1][idx]
# convert x, y in meters
@@ -76,8 +80,7 @@ def is_pos_valid(x_in_meters, y_in_meters):
if not pos_valid:
n_check_failed += 1
if n_check_failed > 100:
- raise Exception(
- "cann't find any no-occupied space please check the map information")
+ raise Exception("cann't find any no-occupied space please check the map information")
# in radius
theta = random.uniform(-math.pi, math.pi)