def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('nav2_bringup') bringup_launch_dir = os.path.join(bringup_dir, 'launch') spawner_dir = get_package_share_directory('robot_spawner_pkg') tf_exchange_dir = get_package_share_directory('tf_exchange') # Names and poses of the robots robots = [{ 'name': 'robot1', 'x_pose': 0.0, 'y_pose': 0.5, 'z_pose': 0.01 }, { 'name': 'robot2', 'x_pose': 0.0, 'y_pose': -0.5, 'z_pose': 0.01 }] # On this example all robots are launched with the same settings map_yaml_file = LaunchConfiguration('map') default_bt_xml_filename = LaunchConfiguration('default_bt_xml_filename') autostart = LaunchConfiguration('autostart') rviz_config_file = LaunchConfiguration('rviz_config') use_robot_state_pub = LaunchConfiguration('use_robot_state_pub') use_rviz = LaunchConfiguration('use_rviz') log_settings = LaunchConfiguration('log_settings', default='true') declare_base_frame = DeclareLaunchArgument('base_frame', default_value='base_link') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', default_value=os.path.join(bringup_dir, 'maps', 'turtlebot3_world.yaml'), description='Full path to map file to load') declare_robot1_params_file_cmd = DeclareLaunchArgument( 'robot1_params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_multirobot_params_1.yaml'), description= 'Full path to the ROS2 parameters file to use for robot1 launched nodes' ) declare_robot2_params_file_cmd = DeclareLaunchArgument( 'robot2_params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_multirobot_params_2.yaml'), description= 'Full path to the ROS2 parameters file to use for robot2 launched nodes' ) declare_bt_xml_cmd = DeclareLaunchArgument( 'default_bt_xml_filename', default_value=os.path.join( get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', 'navigate_w_replanning_and_recovery.xml'), description='Full path to the behavior tree xml file to use') declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='false', description='Automatically startup the stacks') declare_rviz_config_file_cmd = DeclareLaunchArgument( 'rviz_config', default_value=os.path.join( get_package_share_directory('robot_spawner_pkg'), 'nav2_namespaced_view.rviz'), description='Full path to the RVIZ config file to use.') declare_use_robot_state_pub_cmd = DeclareLaunchArgument( 'use_robot_state_pub', default_value='True', description='Whether to start the robot state publisher') declare_use_rviz_cmd = DeclareLaunchArgument( 'use_rviz', default_value='True', description='Whether to start RVIZ') # Define commands for spawing the robots into Gazebo spawn_robots_cmds = [] for robot in robots: spawn_robots_cmds.append( IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(spawner_dir, 'spawn_tb3.launch.py')), launch_arguments={ 'x_pose': TextSubstitution(text=str(robot['x_pose'])), 'y_pose': TextSubstitution(text=str(robot['y_pose'])), 'z_pose': TextSubstitution(text=str(robot['z_pose'])), 'robot_name': robot['name'], 'turtlebot_type': TextSubstitution(text='burger') }.items())) # Define commands for launching the navigation instances nav_instances_cmds = [] for robot in robots: params_file = LaunchConfiguration(robot['name'] + '_params_file') group = GroupAction([ IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_launch_dir, 'rviz_launch.py')), condition=IfCondition(use_rviz), launch_arguments={ 'namespace': TextSubstitution(text=robot['name']), 'use_namespace': 'True', 'rviz_config': rviz_config_file }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'tb3_simulation_launch.py')), launch_arguments={ 'namespace': robot['name'], 'use_namespace': 'True', 'map': map_yaml_file, 'use_sim_time': 'True', 'params_file': params_file, 'default_bt_xml_filename': default_bt_xml_filename, 'autostart': autostart, 'use_rviz': 'False', 'use_simulator': 'False', 'headless': 'False', 'use_robot_state_pub': use_robot_state_pub }.items()), IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(tf_exchange_dir, 'tf_exchange.launch.py')), launch_arguments={ 'namespace': TextSubstitution(text=robot['name']), 'robot_name': robot['name'], 'base_frame': LaunchConfiguration('base_frame') # 'params_file': params_file }.items()), LogInfo(condition=IfCondition(log_settings), msg=['Launching ', robot['name']]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' map yaml: ', map_yaml_file]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' params yaml: ', params_file]), LogInfo(condition=IfCondition(log_settings), msg=[ robot['name'], ' behavior tree xml: ', default_bt_xml_filename ]), LogInfo( condition=IfCondition(log_settings), msg=[robot['name'], ' rviz config file: ', rviz_config_file]), LogInfo(condition=IfCondition(log_settings), msg=[ robot['name'], ' using robot state pub: ', use_robot_state_pub ]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' autostart: ', autostart]) ]) nav_instances_cmds.append(group) simulator = IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(spawner_dir, 'simulator.launch.py')), launch_arguments={}.items()) # Create the launch description and populate ld = LaunchDescription() # Declare the launch options ld.add_action(declare_base_frame) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_robot1_params_file_cmd) ld.add_action(declare_robot2_params_file_cmd) ld.add_action(declare_bt_xml_cmd) ld.add_action(declare_use_rviz_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_rviz_config_file_cmd) ld.add_action(declare_use_robot_state_pub_cmd) # Add the actions to start gazebo, robots and simulations ld.add_action(simulator) for spawn_robot_cmd in spawn_robots_cmds: ld.add_action(spawn_robot_cmd) for simulation_instance_cmd in nav_instances_cmds: ld.add_action(simulation_instance_cmd) return ld
def generate_launch_description(): tf_exchange_dir = get_package_share_directory('tf_exchange') #rviz_config_file = LaunchConfiguration('rviz_config') #map_yaml_file = LaunchConfiguration('map') bringup_dir = get_package_share_directory('nav2_bringup') slam = LaunchConfiguration('slam') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', default_value=os.path.join(bringup_dir, 'maps', 'turtlebot3_world.yaml'), description='Full path to map file to load') declare_robot_name = DeclareLaunchArgument('robot_name', default_value='robot1') declare_base_frame = DeclareLaunchArgument('base_frame', default_value='base_link') declare_x_pose = DeclareLaunchArgument( 'x_pose', default_value=launch.substitutions.TextSubstitution(text='0.0')) declare_y_pose = DeclareLaunchArgument( 'y_pose', default_value=launch.substitutions.TextSubstitution(text='0.0')) declare_z_pose = DeclareLaunchArgument( 'z_pose', default_value=launch.substitutions.TextSubstitution(text='0.0')) declare_yaw_pose = DeclareLaunchArgument( 'yaw_pose', default_value=launch.substitutions.TextSubstitution(text='0.0')) declare_use_rviz_cmd = DeclareLaunchArgument( 'use_rviz', default_value='True', description='Whether to start RVIZ') declare_rviz_config_file_cmd = DeclareLaunchArgument( 'rviz_config', default_value=os.path.join( get_package_share_directory('robot_spawner_pkg'), 'custom.rviz'), description='Full path to the RVIZ config file to use.') declare_slam_cmd = DeclareLaunchArgument('slam', default_value='False', description='Whether run a SLAM') declare_behaviour_cmd = DeclareLaunchArgument( 'behaviour', default_value='True', description='Whether run the default behaviour.') tf_exchange = IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(tf_exchange_dir, 'tf_exchange.launch.py')), launch_arguments={ 'namespace': LaunchConfiguration('robot_name'), 'robot_name': LaunchConfiguration('robot_name'), 'base_frame': LaunchConfiguration('base_frame') #'params_file': [LaunchConfiguration('robot_name'), launch.substitutions.TextSubstitution(text='_params_file')] }.items()) spawner = launch_ros.actions.Node( package='robot_spawner_pkg', executable='nav2_gazebo_spawner', output='screen', arguments=[ '--robot_name', LaunchConfiguration('robot_name'), '--robot_namespace', LaunchConfiguration('robot_name'), '--turtlebot_type', launch.substitutions.EnvironmentVariable('TURTLEBOT3_MODEL'), '-x', LaunchConfiguration('x_pose'), '-y', LaunchConfiguration('y_pose'), '-z', LaunchConfiguration('z_pose'), '-yaw', LaunchConfiguration('yaw_pose'), ]) rviz = IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'rviz_launch.py')), condition=IfCondition( LaunchConfiguration('use_rviz')), launch_arguments={ 'namespace': LaunchConfiguration('robot_name'), 'use_namespace': 'true', 'use_sim_time': 'true' }.items()) #, #'rviz_config': rviz_config_file}.items()) namespace = LaunchConfiguration('robot_name') use_sim_time = TextSubstitution(text='True') autostart = 'True' params_file = os.path.join( get_package_share_directory('robot_spawner_pkg'), 'nav2_multirobot_params_1.yaml') urdf = os.path.join(get_package_share_directory('turtlebot3_description'), 'urdf', 'turtlebot3_burger.urdf') bringup_cmd_group = GroupAction([ launch_ros.actions.PushRosNamespace(namespace=namespace), launch_ros.actions.Node( package='robot_state_publisher', executable='robot_state_publisher', name='robot_state_publisher', #namespace=namespace, output='screen', parameters=[{ 'use_sim_time': use_sim_time }], arguments=[urdf], remappings=[('/tf', 'tf'), ('/tf_static', 'tf_static')]), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'slam_launch.py')), condition=IfCondition(slam), launch_arguments={ 'namespace': namespace, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'localization_launch.py')), condition=IfCondition( PythonExpression(['not ', slam])), launch_arguments={ 'namespace': namespace, 'map': LaunchConfiguration('map'), 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'use_lifecycle_mgr': 'false' }.items()) ]) drive = launch_ros.actions.Node( executable='turtlebot3_drive', condition=IfCondition(LaunchConfiguration('behaviour')), package='turtlebot3_gazebo', namespace=LaunchConfiguration('robot_name'), ) ld = LaunchDescription() ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_robot_name) ld.add_action(declare_base_frame) ld.add_action(declare_x_pose) ld.add_action(declare_y_pose) ld.add_action(declare_z_pose) ld.add_action(declare_yaw_pose) ld.add_action(declare_use_rviz_cmd) ld.add_action(declare_slam_cmd) ld.add_action(declare_behaviour_cmd) ld.add_action(declare_rviz_config_file_cmd) ld.add_action(spawner) ld.add_action(rviz) ld.add_action(bringup_cmd_group) ld.add_action(tf_exchange) ld.add_action(drive) return ld
def launch_setup(context, *args, **kwargs): # Perform substitutions debug = Lc('debug').perform(context) namespace = Lc('namespace').perform(context) x = Lc('x').perform(context) y = Lc('y').perform(context) z = Lc('z').perform(context) roll = Lc('roll').perform(context) pitch = Lc('pitch').perform(context) yaw = Lc('yaw').perform(context) # use_sim_time = Lc('use_sim_time').perform(context) use_world_ned = Lc('use_ned_frame').perform(context) is_write_on_disk = Lc('write_file_on_disk').perform(context) # Request sim time value to the global node res = is_sim_time(return_param=False, use_subprocess=True) # Xacro #xacro_file = PathJoinSubstitution(get_package_share_directory('uuv_descriptions'),'robots','rexrov_') xacro_file = os.path.join( get_package_share_directory('uuv_descriptions'), 'robots', 'rexrov_' + (Lc('mode')).perform(context) + '.xacro') # Build the directories, check for existence path = os.path.join( get_package_share_directory('uuv_descriptions'), 'robots', 'generated', namespace, ) if not pathlib.Path(path).exists(): try: # Create directory if required and sub-directory os.makedirs(path) except OSError: print("Creation of the directory %s failed" % path) output = os.path.join(path, 'robot_description.urdf') if not pathlib.Path(xacro_file).exists(): exc = 'Launch file ' + xacro_file + ' does not exist' raise Exception(exc) mapping = {} if to_bool(use_world_ned): mappings = { 'debug': debug, 'namespace': namespace, 'inertial_reference_frame': 'world_ned' } else: mappings = { 'debug': debug, 'namespace': namespace, 'inertial_reference_frame': 'world' } doc = xacro.process(xacro_file, mappings=mappings) if is_write_on_disk: with open(output, 'w') as file_out: file_out.write(doc) # URDF spawner args = ( '-gazebo_namespace /gazebo ' '-x %s -y %s -z %s -R %s -P %s -Y %s -entity %s -topic robot_description' % (x, y, z, roll, pitch, yaw, namespace)).split() # Urdf spawner. NB: node namespace does not prefix the spawning service, # as using a leading / # NB 2: node namespace prefixes the robot_description topic urdf_spawner = Node(name='urdf_spawner', package='gazebo_ros', executable='spawn_entity.py', output='screen', parameters=[{ 'use_sim_time': res }], arguments=args) # A joint state publisher plugin already is started with the model, no need to use the default joint state publisher # N.B.: alternative way to generate a robot_description string: # string = str('ros2 run xacro xacro ' + xacro_file + ' debug:=false namespace:=' + namespace + ' inertial_reference_frame:=world') # cmd = Command(string) # but it currently yields yaml parsing error due to ":" in the comments of the xacro description files robot_state_publisher = Node( name='robot_state_publisher', package='robot_state_publisher', executable='robot_state_publisher', output='screen', parameters=[{ 'use_sim_time': res }, { 'robot_description': doc }], # Use subst here ) # Message to tf message_to_tf_launch = os.path.join( get_package_share_directory('uuv_assistants'), 'launch', 'message_to_tf.launch') if not pathlib.Path(message_to_tf_launch).exists(): exc = 'Launch file ' + message_to_tf_launch + ' does not exist' raise Exception(exc) launch_args = [ ('namespace', namespace), ('world_frame', 'world'), ('child_frame_id', '/' + namespace + '/base_link'), ('use_sim_time', str(res).lower()), ] message_to_tf_launch = IncludeLaunchDescription( AnyLaunchDescriptionSource(message_to_tf_launch), launch_arguments=launch_args) group = GroupAction([ PushRosNamespace(namespace), urdf_spawner, robot_state_publisher, ]) return [group, message_to_tf_launch]
def generate_launch_description(): pkg_bringup = get_package_share_directory('nanosaur_bringup') pkg_description = get_package_share_directory('nanosaur_description') pkg_control = get_package_share_directory('nanosaur_control') # Load nanosaur configuration and check if are included extra parameters # This feature is used to avoid pass configuration from docker conf = load_config(os.path.join(pkg_bringup, 'param', 'robot.yml')) # Load namespace from robot.yml namespace_conf = os.getenv("HOSTNAME") if conf.get("multirobot", False) else "nanosaur" # Load cover_type if "cover_type" in conf: cover_type_conf = conf.get("cover_type", 'fisheye') print(f"Load cover_type from robot.xml: {cover_type_conf}") else: cover_type_conf = os.getenv("NANOSAUR_COVER_TYPE", 'fisheye') print(f"Load cover_type from ENV: {cover_type_conf}") config_common_path = LaunchConfiguration('config_common_path') namespace = LaunchConfiguration('namespace') cover_type = LaunchConfiguration('cover_type') declare_config_common_path_cmd = DeclareLaunchArgument( 'config_common_path', default_value=os.path.join(pkg_bringup, 'param', 'nanosaur.yml'), description='Path to the `nanosaur.yml` file.') declare_namespace_cmd = DeclareLaunchArgument( 'namespace', default_value=namespace_conf, description= 'Enable a namespace for multiple robot. This namespace group all nodes and topics.' ) declare_cover_type_cmd = DeclareLaunchArgument( name='cover_type', default_value=cover_type_conf, description= 'Cover type to use. Options: pi, fisheye, realsense, zedmini.') jtop_node = Node(package='jetson_stats_wrapper', namespace=namespace, executable='jtop', name='jtop', remappings=[('diagnostics', '/diagnostics')]) # System manager system_manager = Node(package='ros2_system_manager', namespace=namespace, executable='system_manager', name='system_manager') nanosaur_base_node = Node(package='nanosaur_base', namespace=namespace, executable='nanosaur_base', name='nanosaur_base', respawn=True, respawn_delay=5, parameters=[config_common_path], output='screen') # include another launch file in nanosaur namespace # https://docs.ros.org/en/foxy/How-To-Guides/Launch-file-different-formats.html description_launch = GroupAction(actions=[ # push-ros-namespace to set namespace of included nodes PushRosNamespace(namespace), # Nanosaur description IncludeLaunchDescription(PythonLaunchDescriptionSource( [pkg_description, '/launch/description.launch.py']), launch_arguments={'cover_type': cover_type }.items()) ]) # include another launch file in nanosaur namespace twist_control_launch = GroupAction(actions=[ # push-ros-namespace to set namespace of included nodes PushRosNamespace(namespace), # nanosaur twist launch IncludeLaunchDescription( PythonLaunchDescriptionSource( [pkg_control, '/launch/twist_control.launch.py'])) ]) joy2eyes_node = Node(package='nanosaur_base', namespace="teleop", executable='joy2eyes', name='joy2eyes', remappings=[('eyes', f'/{namespace_conf}/eyes')], output='screen') system_manager_node = Node(package='ros2_system_manager', namespace="teleop", executable='joy2sm', name='joy2sm', parameters=[config_common_path], remappings=[('shutdown', f'/{namespace_conf}/shutdown')], output='screen') teleop_extra_node_actions = [ # push-ros-namespace to set namespace of included nodes PushRosNamespace(namespace), # nanosaur twist launch system_manager_node ] # Extra Debug packages # - Eyes bridge if conf.get("debug", False): print("DEBUG variable exist - Load extra nodes") teleop_extra_node_actions += [joy2eyes_node] # include another launch file in nanosaur namespace teleop_extra_nodes_launch = GroupAction(actions=teleop_extra_node_actions) # https://answers.ros.org/question/306935/ros2-include-a-launch-file-from-a-launch-file/ # include another launch file in the chatter_ns namespace teleop_launch = GroupAction(actions=[ # push-ros-namespace to set namespace of included nodes PushRosNamespace(namespace), # teleoperation launcher IncludeLaunchDescription(PythonLaunchDescriptionSource( [pkg_control, '/launch/teleop.launch.py']), launch_arguments={ 'joy_vel': f"/{namespace_conf}/joy_vel", 'config_filepath': os.path.join(pkg_control, 'config', 'ps3.nanosaur.yml') }.items()) ]) print(f"----- cover_type: {cover_type} -----") # Define LaunchDescription variable and return it ld = LaunchDescription() # Namespace nanosaur ld.add_action(declare_namespace_cmd) # cover_type ld.add_action(declare_cover_type_cmd) # Nanosaur parameter yml file path ld.add_action(declare_config_common_path_cmd) # Nanosaur description launch ld.add_action(description_launch) # jtop node ld.add_action(jtop_node) # System manager ld.add_action(system_manager) # Nanusaur driver motors and display ld.add_action(nanosaur_base_node) # Twist control launcher if conf.get("no_twist_mux", False): print("Disable twist-mux") else: ld.add_action(twist_control_launch) # teleoperation joystick nanosaur # only if joystick is connected if os.path.exists("/dev/input/js0"): print("Enable Joystick") # Teleoperation control ld.add_action(teleop_launch) # Run Joystick to system_manager node ld.add_action(teleop_extra_nodes_launch) return ld
def generate_launch_description(): composable_nodes = [ ComposableNode( package='stereo_image_proc', plugin='stereo_image_proc::DisparityNode', parameters=[{ 'approximate_sync': LaunchConfiguration('approximate_sync'), 'use_system_default_qos': LaunchConfiguration('use_system_default_qos'), 'stereo_algorithm': LaunchConfiguration('stereo_algorithm'), 'prefilter_size': LaunchConfiguration('prefilter_size'), 'prefilter_cap': LaunchConfiguration('prefilter_cap'), 'correlation_window_size': LaunchConfiguration('correlation_window_size'), 'min_disparity': LaunchConfiguration('min_disparity'), 'disparity_range': LaunchConfiguration('disparity_range'), 'texture_threshold': LaunchConfiguration('texture_threshold'), 'speckle_size': LaunchConfiguration('speckle_size'), 'speckle_range': LaunchConfiguration('speckle_range'), 'disp12_max_diff': LaunchConfiguration('disp12_max_diff'), 'uniqueness_ratio': LaunchConfiguration('uniqueness_ratio'), 'P1': LaunchConfiguration('P1'), 'P2': LaunchConfiguration('P2'), 'full_dp': LaunchConfiguration('full_dp'), }], remappings=[ ('left/image_rect', [LaunchConfiguration('left_namespace'), '/image_rect']), ('left/camera_info', [LaunchConfiguration('left_namespace'), '/camera_info']), ('right/image_rect', [LaunchConfiguration('right_namespace'), '/image_rect']), ('right/camera_info', [LaunchConfiguration('right_namespace'), '/camera_info']), ]), ComposableNode( package='stereo_image_proc', plugin='stereo_image_proc::PointCloudNode', parameters=[{ 'approximate_sync': LaunchConfiguration('approximate_sync'), 'avoid_point_cloud_padding': LaunchConfiguration('avoid_point_cloud_padding'), 'use_color': LaunchConfiguration('use_color'), 'use_system_default_qos': LaunchConfiguration('use_system_default_qos'), }], remappings=[ ('left/camera_info', [LaunchConfiguration('left_namespace'), '/camera_info']), ('right/camera_info', [LaunchConfiguration('right_namespace'), '/camera_info']), ('left/image_rect_color', [LaunchConfiguration('left_namespace'), '/image_rect_color']), ]), ] return LaunchDescription([ DeclareLaunchArgument( name='approximate_sync', default_value='False', description= 'Whether to use approximate synchronization of topics. Set to true if ' 'the left and right cameras do not produce exactly synced timestamps.' ), DeclareLaunchArgument( name='avoid_point_cloud_padding', default_value='False', description='Avoid alignment padding in the generated point cloud.' 'This reduces bandwidth requirements, as the point cloud size is halved.' 'Using point clouds without alignment padding might degrade performance ' 'for some algorithms.'), DeclareLaunchArgument( name='use_color', default_value='True', description='Generate point cloud with rgb data.'), DeclareLaunchArgument( name='use_system_default_qos', default_value='False', description= 'Use the RMW QoS settings for the image and camera info subscriptions.' ), DeclareLaunchArgument( name='launch_image_proc', default_value='True', description= 'Whether to launch debayer and rectify nodes from image_proc.'), DeclareLaunchArgument(name='left_namespace', default_value='left', description='Namespace for the left camera'), DeclareLaunchArgument(name='right_namespace', default_value='right', description='Namespace for the right camera'), DeclareLaunchArgument( name='container', default_value='', description= ('Name of an existing node container to load launched nodes into. ' 'If unset, a new container will be created.')), # Stereo algorithm parameters DeclareLaunchArgument( name='stereo_algorithm', default_value='0', description= 'Stereo algorithm: Block Matching (0) or Semi-Global Block Matching (1)' ), DeclareLaunchArgument( name='prefilter_size', default_value='9', description='Normalization window size in pixels (must be odd)'), DeclareLaunchArgument(name='prefilter_cap', default_value='31', description='Bound on normalized pixel values'), DeclareLaunchArgument( name='correlation_window_size', default_value='15', description='SAD correlation window width in pixels (must be odd)' ), DeclareLaunchArgument( name='min_disparity', default_value='0', description='Disparity to begin search at in pixels'), DeclareLaunchArgument( name='disparity_range', default_value='64', description= 'Number of disparities to search in pixels (must be a multiple of 16)' ), DeclareLaunchArgument( name='texture_threshold', default_value='10', description= 'Filter out if SAD window response does not exceed texture threshold' ), DeclareLaunchArgument( name='speckle_size', default_value='100', description='Reject regions smaller than this size in pixels'), DeclareLaunchArgument( name='speckle_range', default_value='4', description= 'Maximum allowed difference between detected disparities'), DeclareLaunchArgument( name='disp12_max_diff', default_value='0', description= 'Maximum allowed difference in the left-right disparity check in pixels ' '(Semi-Global Block Matching only)'), DeclareLaunchArgument( name='uniqueness_ratio', default_value='15.0', description= 'Filter out if best match does not sufficiently exceed the next-best match' ), DeclareLaunchArgument( name='P1', default_value='200.0', description= 'The first parameter ccontrolling the disparity smoothness ' '(Semi-Global Block Matching only)'), DeclareLaunchArgument( name='P2', default_value='400.0', description= 'The second parameter ccontrolling the disparity smoothness ' '(Semi-Global Block Matching only)'), DeclareLaunchArgument( name='full_dp', default_value='False', description= 'Run the full variant of the algorithm (Semi-Global Block Matching only)' ), ComposableNodeContainer( condition=LaunchConfigurationEquals('container', ''), package='rclcpp_components', executable='component_container', name='stereo_image_proc_container', namespace='', composable_node_descriptions=composable_nodes, ), LoadComposableNodes( condition=LaunchConfigurationNotEquals('container', ''), composable_node_descriptions=composable_nodes, target_container=LaunchConfiguration('container'), ), # If a container name is not provided, # set the name of the container launched above for image_proc nodes SetLaunchConfiguration(condition=LaunchConfigurationEquals( 'container', ''), name='container', value='stereo_image_proc_container'), GroupAction( [ PushRosNamespace(LaunchConfiguration('left_namespace')), IncludeLaunchDescription(PythonLaunchDescriptionSource([ FindPackageShare('image_proc'), '/launch/image_proc.launch.py' ]), launch_arguments={ 'container': LaunchConfiguration('container') }.items()), ], condition=IfCondition(LaunchConfiguration('launch_image_proc')), ), GroupAction( [ PushRosNamespace(LaunchConfiguration('right_namespace')), IncludeLaunchDescription(PythonLaunchDescriptionSource([ FindPackageShare('image_proc'), '/launch/image_proc.launch.py' ]), launch_arguments={ 'container': LaunchConfiguration('container') }.items()), ], condition=IfCondition(LaunchConfiguration('launch_image_proc')), ) ])
def test_group_action_constructors(): """Test the constructors for the GroupAction class.""" GroupAction([]) GroupAction([Action()]) GroupAction([Action()], scoped=False) GroupAction([Action()], scoped=False, launch_configurations={'foo': 'FOO'})
def test_group_action_execute(): """Test the execute() of the the GroupAction class.""" lc1 = LaunchContext() assert len(lc1.launch_configurations) == 0 assert len(GroupAction([], scoped=False).visit(lc1)) == 0 assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([]).visit(lc1) assert len(result) == 2 # push and pop actions, due to scope=True assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], launch_configurations={'foo': 'FOO'}).visit(lc1) assert len( result) == 3 # push, set 1 launch_configurations, and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], launch_configurations={ 'foo': 'FOO', 'bar': 'BAR' }).visit(lc1) assert len( result) == 4 # push, set 2 launch_configurations, and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], SetLaunchConfiguration) assert isinstance(result[3], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 PushLaunchConfigurations().visit(lc1) result = GroupAction([], scoped=False, launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len(result) == 1 # set 1 launch_configurations assert isinstance(result[0], SetLaunchConfiguration) for a in result: a.visit(lc1) assert len( lc1.launch_configurations) == 1 # still set after group was included PopLaunchConfigurations().visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 PushLaunchConfigurations().visit(lc1) result = GroupAction([Action()], scoped=False, launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len( result ) == 2 # set 1 launch_configurations, then the 1 included actions assert isinstance(result[0], SetLaunchConfiguration) assert isinstance(result[1], Action) for a in result: a.visit(lc1) assert len( lc1.launch_configurations) == 1 # still set after group was included PopLaunchConfigurations().visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([Action()], launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len( result ) == 4 # push, set 1 launch_configurations, the 1 action, and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], Action) assert isinstance(result[3], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0
def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('amazon_robot_bringup') launch_dir = os.path.join(bringup_dir, 'launch') # Create the launch configuration variables namespace = LaunchConfiguration('namespace') use_namespace = LaunchConfiguration('use_namespace') slam = LaunchConfiguration('slam') map_yaml_file = LaunchConfiguration('map') use_sim_time = LaunchConfiguration('use_sim_time') params_file = LaunchConfiguration('params_file') default_bt_xml_filename = LaunchConfiguration('default_bt_xml_filename') autostart = LaunchConfiguration('autostart') stdout_linebuf_envvar = SetEnvironmentVariable( 'RCUTILS_LOGGING_BUFFERED_STREAM', '1') declare_namespace_cmd = DeclareLaunchArgument( 'namespace', default_value='', description='Top-level namespace') declare_use_namespace_cmd = DeclareLaunchArgument( 'use_namespace', default_value='false', description='Whether to apply a namespace to the navigation stack') declare_slam_cmd = DeclareLaunchArgument('slam', default_value='False', description='Whether run a SLAM') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', description='Full path to map yaml file to load') declare_use_sim_time_cmd = DeclareLaunchArgument( 'use_sim_time', default_value='false', description='Use simulation (Gazebo) clock if true') declare_params_file_cmd = DeclareLaunchArgument( 'params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'), description= 'Full path to the ROS2 parameters file to use for all launched nodes') declare_bt_xml_cmd = DeclareLaunchArgument( 'default_bt_xml_filename', default_value=os.path.join( get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', 'navigate_w_replanning_and_recovery.xml'), description='Full path to the behavior tree xml file to use') declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='true', description='Automatically startup the nav2 stack') # Specify the actions bringup_cmd_group = GroupAction([ PushRosNamespace(condition=IfCondition(use_namespace), namespace=namespace), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'slam_launch.py')), condition=IfCondition(slam), launch_arguments={ 'namespace': namespace, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'localization_launch.py')), condition=IfCondition( PythonExpression(['not ', slam])), launch_arguments={ 'namespace': namespace, 'map': map_yaml_file, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'use_lifecycle_mgr': 'false' }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'navigation_launch.py')), launch_arguments={ 'namespace': namespace, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'default_bt_xml_filename': default_bt_xml_filename, 'use_lifecycle_mgr': 'false', 'map_subscribe_transient_local': 'true' }.items()), ]) # Create the launch description and populate ld = LaunchDescription() # Set environment variables ld.add_action(stdout_linebuf_envvar) # Declare the launch options ld.add_action(declare_namespace_cmd) ld.add_action(declare_use_namespace_cmd) ld.add_action(declare_slam_cmd) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_use_sim_time_cmd) ld.add_action(declare_params_file_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_bt_xml_cmd) # Add the actions to launch all of the navigation nodes ld.add_action(bringup_cmd_group) return ld
def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('nav2_bringup') launch_dir = os.path.join(bringup_dir, 'launch') # Create the launch configuration variables namespace = LaunchConfiguration('namespace') use_namespace = LaunchConfiguration('use_namespace') map_yaml_file = LaunchConfiguration('map') use_sim_time = LaunchConfiguration('use_sim_time') params_file = LaunchConfiguration('params_file') bt_xml_file = LaunchConfiguration('bt_xml_file') autostart = LaunchConfiguration('autostart') use_remappings = LaunchConfiguration('use_remappings') stdout_linebuf_envvar = SetEnvironmentVariable( 'RCUTILS_CONSOLE_STDOUT_LINE_BUFFERED', '1') declare_namespace_cmd = DeclareLaunchArgument( 'namespace', default_value='navigation', description='Top-level namespace') declare_use_namespace_cmd = DeclareLaunchArgument( 'use_namespace', default_value='false', description='Whether to apply a namespace to the navigation stack') # Declare the launch arguments declare_map_yaml_cmd = DeclareLaunchArgument( 'map', description='Full path to map yaml file to load') declare_use_sim_time_cmd = DeclareLaunchArgument( 'use_sim_time', default_value='false', description='Use simulation (Gazebo) clock if true') declare_params_file_cmd = DeclareLaunchArgument( 'params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'), description='Full path to the ROS2 parameters file to use for all launched nodes') declare_bt_xml_cmd = DeclareLaunchArgument( 'bt_xml_file', default_value=os.path.join( get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', 'navigate_w_replanning_and_recovery.xml'), description='Full path to the behavior tree xml file to use') declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='true', description='Automatically startup the nav2 stack') declare_use_remappings_cmd = DeclareLaunchArgument( 'use_remappings', default_value='false', description='Arguments to pass to all nodes launched by the file') # Specify the actions bringup_cmd_group = GroupAction([ PushRosNamespace( condition=IfCondition(use_namespace), namespace=namespace), IncludeLaunchDescription( PythonLaunchDescriptionSource(os.path.join(launch_dir, 'nav2_localization_launch.py')), launch_arguments={'namespace': namespace, 'map': map_yaml_file, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'use_lifecycle_mgr': 'false', 'use_remappings': use_remappings}.items()), IncludeLaunchDescription( PythonLaunchDescriptionSource(os.path.join(launch_dir, 'nav2_navigation_launch.py')), launch_arguments={'namespace': namespace, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'bt_xml_file': bt_xml_file, 'use_lifecycle_mgr': 'false', 'use_remappings': use_remappings, 'map_subscribe_transient_local': 'true'}.items()), Node( package='nav2_lifecycle_manager', node_executable='lifecycle_manager', node_name='lifecycle_manager', output='screen', parameters=[{'use_sim_time': use_sim_time}, {'autostart': autostart}, {'node_names': ['map_server', 'amcl', 'controller_server', 'planner_server', 'recoveries_server', 'bt_navigator', 'waypoint_follower']}]), ]) # Create the launch description and populate ld = LaunchDescription() # Set environment variables ld.add_action(stdout_linebuf_envvar) # Declare the launch options ld.add_action(declare_namespace_cmd) ld.add_action(declare_use_namespace_cmd) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_use_sim_time_cmd) ld.add_action(declare_params_file_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_bt_xml_cmd) ld.add_action(declare_use_remappings_cmd) # Add the actions to launch all of the navigation nodes ld.add_action(bringup_cmd_group) return ld
def generate_launch_description(): map_yaml_file = os.getenv('TEST_MAP') world = os.getenv('TEST_WORLD') urdf = os.getenv('TEST_URDF') sdf = os.getenv('TEST_SDF') bt_xml_file = os.path.join( get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', os.getenv('BT_NAVIGATOR_XML')) bringup_dir = get_package_share_directory('nav2_bringup') robot1_params_file = os.path.join( bringup_dir, # noqa: F841 'params/nav2_multirobot_params_1.yaml') robot2_params_file = os.path.join( bringup_dir, # noqa: F841 'params/nav2_multirobot_params_2.yaml') # Names and poses of the robots robots = [{ 'name': 'robot1', 'x_pose': 0.0, 'y_pose': 0.5, 'z_pose': 0.01 }, { 'name': 'robot2', 'x_pose': 0.0, 'y_pose': -0.5, 'z_pose': 0.01 }] # Launch Gazebo server for simulation start_gazebo_cmd = ExecuteProcess(cmd=[ 'gzserver', '-s', 'libgazebo_ros_factory.so', '--minimal_comms', world ], output='screen') # Define commands for spawing the robots into Gazebo spawn_robots_cmds = [] for robot in robots: spawn_robots_cmds.append( Node(package='nav2_gazebo_spawner', node_executable='nav2_gazebo_spawner', output='screen', arguments=[ '--robot_name', TextSubstitution(text=robot['name']), '--robot_namespace', TextSubstitution(text=robot['name']), '--sdf', TextSubstitution(text=sdf), '-x', TextSubstitution(text=str(robot['x_pose'])), '-y', TextSubstitution(text=str(robot['y_pose'])), '-z', TextSubstitution(text=str(robot['z_pose'])) ])) # Define commands for launching the robot state publishers robot_state_pubs_cmds = [] for robot in robots: robot_state_pubs_cmds.append( Node(package='robot_state_publisher', node_executable='robot_state_publisher', node_namespace=TextSubstitution(text=robot['name']), output='screen', parameters=[{ 'use_sim_time': 'True' }], remappings=[('/tf', 'tf'), ('/tf_static', 'tf_static')], arguments=[urdf])) # Define commands for launching the navigation instances nav_instances_cmds = [] for robot in robots: params_file = eval(robot['name'] + '_params_file') group = GroupAction([ # Instances use the robot's name for namespace PushRosNamespace(robot['name']), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'nav2_bringup_launch.py')), launch_arguments={ 'namespace': robot['name'], 'map': map_yaml_file, 'use_sim_time': 'True', 'params_file': params_file, 'bt_xml_file': bt_xml_file, 'autostart': 'True', 'use_remappings': 'True' }.items()) ]) nav_instances_cmds.append(group) ld = LaunchDescription() ld.add_action( SetEnvironmentVariable('RCUTILS_CONSOLE_STDOUT_LINE_BUFFERED', '1'), ) ld.add_action(start_gazebo_cmd) for spawn_robot in spawn_robots_cmds: ld.add_action(spawn_robot) for state_pub in robot_state_pubs_cmds: ld.add_action(state_pub) for nav_instance in nav_instances_cmds: ld.add_action(nav_instance) return ld
def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('nav2_bringup') # Names and poses of the robots robots = [{ 'name': 'robot1', 'x_pose': 0.0, 'y_pose': 0.5, 'z_pose': 0.01 }, { 'name': 'robot2', 'x_pose': 0.0, 'y_pose': -0.5, 'z_pose': 0.01 }] # Simulation settings world = LaunchConfiguration('world') simulator = LaunchConfiguration('simulator') # On this example all robots are launched with the same settings map_yaml_file = LaunchConfiguration('map') params_file = LaunchConfiguration('params_file') bt_xml_file = LaunchConfiguration('bt_xml_file') autostart = LaunchConfiguration('autostart') rviz_config_file = LaunchConfiguration('rviz_config') use_robot_state_pub = LaunchConfiguration('use_robot_state_pub') use_rviz = LaunchConfiguration('use_rviz') log_settings = LaunchConfiguration('log_settings', default='true') # Declare the launch arguments declare_world_cmd = DeclareLaunchArgument( 'world', default_value=os.path.join(bringup_dir, 'worlds', 'world_only.model'), description='Full path to world file to load') declare_simulator_cmd = DeclareLaunchArgument( 'simulator', default_value='gazebo', description='The simulator to use (gazebo or gzserver)') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', default_value=os.path.join(bringup_dir, 'maps', 'turtlebot3_world.yaml'), description='Full path to map file to load') declare_params_file_cmd = DeclareLaunchArgument( 'params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'), description= 'Full path to the ROS2 parameters file to use for all launched nodes') declare_bt_xml_cmd = DeclareLaunchArgument( 'bt_xml_file', default_value=os.path.join( get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', 'navigate_w_replanning_and_recovery.xml'), description='Full path to the behavior tree xml file to use') declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='false', description='Automatically startup the stacks') declare_rviz_config_file_cmd = DeclareLaunchArgument( 'rviz_config', default_value=os.path.join(bringup_dir, 'rviz', 'nav2_namespaced_view.rviz'), description='Full path to the RVIZ config file to use') declare_use_robot_state_pub_cmd = DeclareLaunchArgument( 'use_robot_state_pub', default_value='True', description='Whether to start the robot state publisher') declare_use_rviz_cmd = DeclareLaunchArgument( 'use_rviz', default_value='True', description='Whether to start RVIZ') # Start Gazebo with plugin providing the robot spawing service start_gazebo_cmd = ExecuteProcess( cmd=[simulator, '--verbose', '-s', 'libgazebo_ros_factory.so', world], output='screen') # Define commands for spawing the robots into Gazebo spawn_robots_cmds = [] for robot in robots: spawn_robots_cmds.append( IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'spawn_tb3_launch.py')), launch_arguments={ 'x_pose': TextSubstitution(text=str(robot['x_pose'])), 'y_pose': TextSubstitution(text=str(robot['y_pose'])), 'z_pose': TextSubstitution(text=str(robot['z_pose'])), 'robot_name': robot['name'], 'turtlebot_type': TextSubstitution(text='waffle') }.items())) # Define commands for launching the navigation instances nav_instances_cmds = [] for robot in robots: namespaced_rviz_config_file = ReplaceString( source_file=rviz_config_file, replacements={'<robot_namespace>': ('/' + robot['name'])}) group = GroupAction([ # TODO(orduno) # Each `action.Node` within the `localization` and `navigation` launch # files has two versions, one with the required remaps and another without. # The `use_remappings` flag specifies which runs. # A better mechanism would be to have a PushNodeRemapping() action: # https://github.com/ros2/launch_ros/issues/56 # For more on why we're remapping topics, see the note below # PushNodeRemapping(remappings) # Instances use the robot's name for namespace PushRosNamespace(robot['name']), IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'nav2_tb3_simulation_launch.py')), launch_arguments={ # TODO(orduno) might not be necessary to pass the robot name 'namespace': robot['name'], 'map_yaml_file': map_yaml_file, 'use_sim_time': 'True', 'params_file': params_file, 'bt_xml_file': bt_xml_file, 'autostart': autostart, 'use_remappings': 'True', 'rviz_config_file': namespaced_rviz_config_file, 'use_rviz': use_rviz, 'use_simulator': 'False', 'use_robot_state_pub': use_robot_state_pub }.items()), LogInfo(condition=IfCondition(log_settings), msg=['Launching ', robot['name']]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' map yaml: ', map_yaml_file]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' params yaml: ', params_file]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' behavior tree xml: ', bt_xml_file]), LogInfo(condition=IfCondition(log_settings), msg=[ robot['name'], ' rviz config file: ', namespaced_rviz_config_file ]), LogInfo(condition=IfCondition(log_settings), msg=[ robot['name'], ' using robot state pub: ', use_robot_state_pub ]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' autostart: ', autostart]) ]) nav_instances_cmds.append(group) # A note on the `remappings` variable defined above and the fact it's passed as a node arg. # A few topics have fully qualified names (have a leading '/'), these need to be remapped # to relative ones so the node's namespace can be prepended. # In case of the transforms (tf), currently, there doesn't seem to be a better alternative # for multi-robot transforms: # https://github.com/ros/geometry2/issues/32 # https://github.com/ros/robot_state_publisher/pull/30 # Create the launch description and populate ld = LaunchDescription() # Declare the launch options ld.add_action(declare_simulator_cmd) ld.add_action(declare_world_cmd) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_params_file_cmd) ld.add_action(declare_bt_xml_cmd) ld.add_action(declare_use_rviz_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_rviz_config_file_cmd) ld.add_action(declare_use_robot_state_pub_cmd) # Add the actions to start gazebo, robots and simulations ld.add_action(start_gazebo_cmd) for spawn_robot_cmd in spawn_robots_cmds: ld.add_action(spawn_robot_cmd) for simulation_instance_cmd in nav_instances_cmds: ld.add_action(simulation_instance_cmd) return ld
def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('nav2_bringup') launch_dir = os.path.join(bringup_dir, 'launch') # Names and poses of the robots robots = [{ 'name': 'robot1', 'x_pose': 0.0, 'y_pose': 0.5, 'z_pose': 0.01 }, { 'name': 'robot2', 'x_pose': 0.0, 'y_pose': -0.5, 'z_pose': 0.01 }] # Simulation settings world = LaunchConfiguration('world') simulator = LaunchConfiguration('simulator') # On this example all robots are launched with the same settings map_yaml_file = LaunchConfiguration('map') autostart = LaunchConfiguration('autostart') rviz_config_file = LaunchConfiguration('rviz_config') use_robot_state_pub = LaunchConfiguration('use_robot_state_pub') use_rviz = LaunchConfiguration('use_rviz') log_settings = LaunchConfiguration('log_settings', default='true') # Declare the launch arguments declare_world_cmd = DeclareLaunchArgument( 'world', default_value=os.path.join(bringup_dir, 'worlds', 'world_only.model'), description='Full path to world file to load') declare_simulator_cmd = DeclareLaunchArgument( 'simulator', default_value='gazebo', description='The simulator to use (gazebo or gzserver)') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', default_value=os.path.join(bringup_dir, 'maps', 'turtlebot3_world.yaml'), description='Full path to map file to load') declare_robot1_params_file_cmd = DeclareLaunchArgument( 'robot1_params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_multirobot_params_1.yaml'), description= 'Full path to the ROS2 parameters file to use for robot1 launched nodes' ) declare_robot2_params_file_cmd = DeclareLaunchArgument( 'robot2_params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_multirobot_params_2.yaml'), description= 'Full path to the ROS2 parameters file to use for robot2 launched nodes' ) declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='false', description='Automatically startup the stacks') declare_rviz_config_file_cmd = DeclareLaunchArgument( 'rviz_config', default_value=os.path.join(bringup_dir, 'rviz', 'nav2_namespaced_view.rviz'), description='Full path to the RVIZ config file to use.') declare_use_robot_state_pub_cmd = DeclareLaunchArgument( 'use_robot_state_pub', default_value='True', description='Whether to start the robot state publisher') declare_use_rviz_cmd = DeclareLaunchArgument( 'use_rviz', default_value='True', description='Whether to start RVIZ') # Start Gazebo with plugin providing the robot spawing service start_gazebo_cmd = ExecuteProcess(cmd=[ simulator, '--verbose', '-s', 'libgazebo_ros_init.so', '-s', 'libgazebo_ros_factory.so', world ], output='screen') # Define commands for spawing the robots into Gazebo spawn_robots_cmds = [] for robot in robots: spawn_robots_cmds.append( IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'spawn_tb3_launch.py')), launch_arguments={ 'x_pose': TextSubstitution(text=str(robot['x_pose'])), 'y_pose': TextSubstitution(text=str(robot['y_pose'])), 'z_pose': TextSubstitution(text=str(robot['z_pose'])), 'robot_name': robot['name'], 'turtlebot_type': TextSubstitution(text='waffle') }.items())) # Define commands for launching the navigation instances nav_instances_cmds = [] for robot in robots: params_file = LaunchConfiguration(f"{robot['name']}_params_file") group = GroupAction([ IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'rviz_launch.py')), condition=IfCondition(use_rviz), launch_arguments={ 'namespace': TextSubstitution(text=robot['name']), 'use_namespace': 'True', 'rviz_config': rviz_config_file }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(bringup_dir, 'launch', 'tb3_simulation_launch.py')), launch_arguments={ 'namespace': robot['name'], 'use_namespace': 'True', 'map': map_yaml_file, 'use_sim_time': 'True', 'params_file': params_file, 'autostart': autostart, 'use_rviz': 'False', 'use_simulator': 'False', 'headless': 'False', 'use_robot_state_pub': use_robot_state_pub }.items()), LogInfo(condition=IfCondition(log_settings), msg=['Launching ', robot['name']]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' map yaml: ', map_yaml_file]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' params yaml: ', params_file]), LogInfo( condition=IfCondition(log_settings), msg=[robot['name'], ' rviz config file: ', rviz_config_file]), LogInfo(condition=IfCondition(log_settings), msg=[ robot['name'], ' using robot state pub: ', use_robot_state_pub ]), LogInfo(condition=IfCondition(log_settings), msg=[robot['name'], ' autostart: ', autostart]) ]) nav_instances_cmds.append(group) # Create the launch description and populate ld = LaunchDescription() # Declare the launch options ld.add_action(declare_simulator_cmd) ld.add_action(declare_world_cmd) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_robot1_params_file_cmd) ld.add_action(declare_robot2_params_file_cmd) ld.add_action(declare_use_rviz_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_rviz_config_file_cmd) ld.add_action(declare_use_robot_state_pub_cmd) # Add the actions to start gazebo, robots and simulations ld.add_action(start_gazebo_cmd) for spawn_robot_cmd in spawn_robots_cmds: ld.add_action(spawn_robot_cmd) for simulation_instance_cmd in nav_instances_cmds: ld.add_action(simulation_instance_cmd) return ld
def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('nav2_bringup') launch_dir = os.path.join(bringup_dir, 'launch') # Create the launch configuration variables namespace = LaunchConfiguration('namespace') use_namespace = LaunchConfiguration('use_namespace') slam = LaunchConfiguration('slam') map_yaml_file = LaunchConfiguration('map') use_sim_time = LaunchConfiguration('use_sim_time') params_file = LaunchConfiguration('params_file') autostart = LaunchConfiguration('autostart') use_composition = LaunchConfiguration('use_composition') # Map fully qualified names to relative ones so the node's namespace can be prepended. # In case of the transforms (tf), currently, there doesn't seem to be a better alternative # https://github.com/ros/geometry2/issues/32 # https://github.com/ros/robot_state_publisher/pull/30 # TODO(orduno) Substitute with `PushNodeRemapping` # https://github.com/ros2/launch_ros/issues/56 remappings = [('/tf', 'tf'), ('/tf_static', 'tf_static')] # Create our own temporary YAML files that include substitutions param_substitutions = { 'use_sim_time': use_sim_time, 'yaml_filename': map_yaml_file } configured_params = RewrittenYaml(source_file=params_file, root_key=namespace, param_rewrites=param_substitutions, convert_types=True) stdout_linebuf_envvar = SetEnvironmentVariable( 'RCUTILS_LOGGING_BUFFERED_STREAM', '1') declare_namespace_cmd = DeclareLaunchArgument( 'namespace', default_value='', description='Top-level namespace') declare_use_namespace_cmd = DeclareLaunchArgument( 'use_namespace', default_value='false', description='Whether to apply a namespace to the navigation stack') declare_slam_cmd = DeclareLaunchArgument('slam', default_value='False', description='Whether run a SLAM') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', description='Full path to map yaml file to load') declare_use_sim_time_cmd = DeclareLaunchArgument( 'use_sim_time', default_value='false', description='Use simulation (Gazebo) clock if true') declare_params_file_cmd = DeclareLaunchArgument( 'params_file', default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'), description= 'Full path to the ROS2 parameters file to use for all launched nodes') declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='true', description='Automatically startup the nav2 stack') declare_use_composition_cmd = DeclareLaunchArgument( 'use_composition', default_value='True', description='Whether to use composed bringup') # Specify the actions bringup_cmd_group = GroupAction([ PushRosNamespace(condition=IfCondition(use_namespace), namespace=namespace), Node(condition=IfCondition(use_composition), name='nav2_container', package='rclcpp_components', executable='component_container_isolated', parameters=[configured_params, { 'autostart': autostart }], remappings=remappings, output='screen'), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'slam_launch.py')), condition=IfCondition(slam), launch_arguments={ 'namespace': namespace, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'localization_launch.py')), condition=IfCondition( PythonExpression(['not ', slam])), launch_arguments={ 'namespace': namespace, 'map': map_yaml_file, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'use_composition': use_composition, 'container_name': 'nav2_container' }.items()), IncludeLaunchDescription(PythonLaunchDescriptionSource( os.path.join(launch_dir, 'navigation_launch.py')), launch_arguments={ 'namespace': namespace, 'use_sim_time': use_sim_time, 'autostart': autostart, 'params_file': params_file, 'use_composition': use_composition, 'container_name': 'nav2_container' }.items()), ]) # Create the launch description and populate ld = LaunchDescription() # Set environment variables ld.add_action(stdout_linebuf_envvar) # Declare the launch options ld.add_action(declare_namespace_cmd) ld.add_action(declare_use_namespace_cmd) ld.add_action(declare_slam_cmd) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_use_sim_time_cmd) ld.add_action(declare_params_file_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_use_composition_cmd) # Add the actions to launch all of the navigation nodes ld.add_action(bringup_cmd_group) return ld
def generate_launch_description(): # Get the launch directory bringup_dir = get_package_share_directory('nav2_bringup') mod_bringup_dir = get_package_share_directory('rsb_nav2') launch_dir = os.path.join(bringup_dir, 'launch') # Names and poses of the robots robots = [ {'name': 'robot1', 'x_pose': 0.0, 'y_pose': 0.5, 'z_pose': 0.01}, {'name': 'robot2', 'x_pose': 0.0, 'y_pose': -0.5, 'z_pose': 0.01} ] # Create the launch configuration variables slam = LaunchConfiguration('slam') namespace = LaunchConfiguration('namespace') use_namespace = LaunchConfiguration('use_namespace') map_yaml_file = LaunchConfiguration('map') use_sim_time = LaunchConfiguration('use_sim_time') params_file = LaunchConfiguration('params_file') default_bt_xml_filename = LaunchConfiguration('default_bt_xml_filename') autostart = LaunchConfiguration('autostart') # Launch configuration variables specific to simulation rviz_config_file = LaunchConfiguration('rviz_config_file') use_simulator = LaunchConfiguration('use_simulator') use_robot_state_pub = LaunchConfiguration('use_robot_state_pub') use_rviz = LaunchConfiguration('use_rviz') headless = LaunchConfiguration('headless') world = LaunchConfiguration('world') # Map fully qualified names to relative ones so the node's namespace can be prepended. # In case of the transforms (tf), currently, there doesn't seem to be a better alternative # https://github.com/ros/geometry2/issues/32 # https://github.com/ros/robot_state_publisher/pull/30 # TODO(orduno) Substitute with `PushNodeRemapping` # https://github.com/ros2/launch_ros/issues/56 remappings = [('/tf', 'tf'), ('/tf_static', 'tf_static')] # Declare the launch arguments declare_namespace_cmd = DeclareLaunchArgument( 'namespace', default_value='', description='Top-level namespace') declare_use_namespace_cmd = DeclareLaunchArgument( 'use_namespace', default_value='true', description='Whether to apply a namespace to the navigation stack') declare_slam_cmd = DeclareLaunchArgument( 'slam', default_value='False', description='Whether run a SLAM') declare_map_yaml_cmd = DeclareLaunchArgument( 'map', default_value=os.path.join(bringup_dir, 'maps', 'turtlebot3_world.yaml'), description='Full path to map file to load') declare_use_sim_time_cmd = DeclareLaunchArgument( 'use_sim_time', default_value='true', description='Use simulation (Gazebo) clock if true') declare_robot1_params_file_cmd = DeclareLaunchArgument( 'robot1_params_file', default_value=os.path.join(mod_bringup_dir, 'params', 'nav2_multirobot_params_1.yaml'), #MOD description='Full path to the ROS2 parameters file to use for robot1 launched nodes') declare_robot2_params_file_cmd = DeclareLaunchArgument( 'robot2_params_file', default_value=os.path.join(mod_bringup_dir, 'params', 'nav2_multirobot_params_2.yaml'), #MOD description='Full path to the ROS2 parameters file to use for robot2 launched nodes') declare_bt_xml_cmd = DeclareLaunchArgument( 'default_bt_xml_filename', default_value=os.path.join( get_package_share_directory('nav2_bt_navigator'), 'behavior_trees', 'navigate_w_replanning_and_recovery.xml'), description='Full path to the behavior tree xml file to use') declare_autostart_cmd = DeclareLaunchArgument( 'autostart', default_value='true', description='Automatically startup the nav2 stack') declare_rviz_config_file_cmd = DeclareLaunchArgument( 'rviz_config_file', default_value=os.path.join(bringup_dir, 'rviz', 'nav2_default_view.rviz'), description='Full path to the RVIZ config file to use') declare_use_simulator_cmd = DeclareLaunchArgument( 'use_simulator', default_value='True', description='Whether to start the simulator') declare_use_robot_state_pub_cmd = DeclareLaunchArgument( 'use_robot_state_pub', default_value='True', description='Whether to start the robot state publisher') declare_use_rviz_cmd = DeclareLaunchArgument( 'use_rviz', default_value='True', description='Whether to start RVIZ') declare_simulator_cmd = DeclareLaunchArgument( 'headless', default_value='False', description='Whether to execute gzclient)') declare_world_cmd = DeclareLaunchArgument( 'world', # TODO(orduno) Switch back once ROS argument passing has been fixed upstream # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/91 # default_value=os.path.join(get_package_share_directory('turtlebot3_gazebo'), # 'worlds/turtlebot3_worlds/waffle.model'), default_value=os.path.join(mod_bringup_dir, 'worlds', 'double_tb3_world.model'), description='Full path to world model file to load') # Specify the actions start_gazebo_server_cmd = ExecuteProcess( condition=IfCondition(use_simulator), cmd=['gzserver', '-s', 'libgazebo_ros_init.so', world], cwd=[launch_dir], output='screen') start_gazebo_client_cmd = ExecuteProcess( condition=IfCondition(PythonExpression([use_simulator, ' and not ', headless])), cmd=['gzclient'], cwd=[launch_dir], output='screen') urdf = os.path.join(bringup_dir, 'urdf', 'turtlebot3_waffle.urdf') start_robot_state_publisher_cmd = Node( condition=IfCondition(use_robot_state_pub), package='robot_state_publisher', executable='robot_state_publisher', name='robot_state_publisher', namespace=namespace, output='screen', parameters=[{'use_sim_time': use_sim_time}], remappings=remappings, arguments=[urdf]) rviz_cmd = IncludeLaunchDescription( PythonLaunchDescriptionSource(os.path.join(launch_dir, 'rviz_launch.py')), condition=IfCondition(use_rviz), launch_arguments={'namespace': '', 'use_namespace': 'True', 'rviz_config': rviz_config_file}.items()) bringup_cmd = IncludeLaunchDescription( PythonLaunchDescriptionSource(os.path.join(launch_dir, 'bringup_launch.py')), launch_arguments={'namespace': namespace, 'use_namespace': use_namespace, 'slam': slam, 'map': map_yaml_file, 'use_sim_time': use_sim_time, 'params_file': params_file, 'default_bt_xml_filename': default_bt_xml_filename, 'autostart': autostart}.items()) # Define commands for launching the navigation instances nav_instances_cmds = [] for robot in robots: group = GroupAction([ IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(launch_dir, 'rviz_launch.py')), condition=IfCondition(use_rviz), launch_arguments={ 'namespace': TextSubstitution(text=robot['name']), 'use_namespace': 'True', 'rviz_config': rviz_config_file}.items()), ]) nav_instances_cmds.append(group) # Create the launch description and populate ld = LaunchDescription() # Declare the launch options ld.add_action(declare_namespace_cmd) ld.add_action(declare_use_namespace_cmd) ld.add_action(declare_slam_cmd) ld.add_action(declare_map_yaml_cmd) ld.add_action(declare_use_sim_time_cmd) ld.add_action(declare_robot1_params_file_cmd) ld.add_action(declare_robot2_params_file_cmd) ld.add_action(declare_bt_xml_cmd) ld.add_action(declare_autostart_cmd) ld.add_action(declare_rviz_config_file_cmd) ld.add_action(declare_use_simulator_cmd) ld.add_action(declare_use_robot_state_pub_cmd) ld.add_action(declare_use_rviz_cmd) ld.add_action(declare_simulator_cmd) ld.add_action(declare_world_cmd) # Add any conditioned actions ld.add_action(start_gazebo_server_cmd) ld.add_action(start_gazebo_client_cmd) # Add the actions to launch all of the navigation nodes ld.add_action(start_robot_state_publisher_cmd) ld.add_action(rviz_cmd) ld.add_action(bringup_cmd) for simulation_instance_cmd in nav_instances_cmds: ld.add_action(simulation_instance_cmd) return ld
def generate_launch_description(): base_driver = LaunchConfiguration('base_driver') use_sim_time = LaunchConfiguration('use_sim_time') standard_params = {'use_sim_time': LaunchConfiguration('use_sim_time')} return LaunchDescription([ DeclareLaunchArgument('base_driver', default_value='true'), DeclareLaunchArgument('slam', default_value='false'), DeclareLaunchArgument('nav', default_value='false'), DeclareLaunchArgument('use_sim_time', default_value='false'), DeclareLaunchArgument('map_path'), # Base # -- Kobuki Base include_launch( 'hls_lfcd_lds_driver', 'hlds_laser.launch.py', cond='base_driver', launch_arguments={ 'port': '/dev/lds01', 'frame_id': 'laser_link', 'use_sim_time': use_sim_time, }.items()), include_launch( 'robot_runtime', 'description.launch.py', cond=None, launch_arguments={ **standard_params, 'joint_states': base_driver, }.items()), include_launch( 'robot_runtime', 'teleop.launch.py', launch_arguments={ 'base_driver': base_driver, 'use_sim_time': use_sim_time, }.items()), Node( package='prometheus_exporter', node_executable='prometheus_exporter', node_name='prometheus_exporter', parameters=[standard_params], output='screen', ), GroupAction([ PushRosNamespace('parking'), include_launch( 'parking', 'parking.launch.py', launch_arguments={ 'map': LaunchConfiguration('map_path'), 'use_sim_time': use_sim_time, }.items()), ]), include_launch( 'robot_runtime', 'cartographer.launch.py', cond='slam', launch_arguments={ 'use_sim_sime': use_sim_time, 'configuration_basename': 'kobuki_lds_2d.lua', }.items()), include_launch( 'robot_runtime', 'nav.launch.py', cond='nav', launch_arguments={ 'use_sim_time': use_sim_time, 'map': LaunchConfiguration('map_path'), }.items()), ])
def test_group_action_execute(): """Test the execute() of the the GroupAction class.""" lc1 = LaunchContext() assert len(lc1.launch_configurations) == 0 assert len(GroupAction([], scoped=False).visit(lc1)) == 0 assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], forwarding=True).visit(lc1) assert len( result) == 2 # push and pop actions, due to scope=True, forwarded=True assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], forwarding=False).visit(lc1) assert len( result ) == 3 # push, reset, pop actions, due to scope=True, forwarded=False assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], ResetLaunchConfigurations) assert isinstance(result[2], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], forwarding=True, launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len( result) == 3 # push, set 1 launch_configurations, and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], forwarding=True, launch_configurations={ 'foo': 'FOO', 'bar': 'BAR' }).visit(lc1) assert len( result) == 4 # push, set 2 launch_configurations, and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], SetLaunchConfiguration) assert isinstance(result[3], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([], forwarding=False, launch_configurations={ 'foo': 'FOO', 'bar': 'BAR' }).visit(lc1) assert len( result ) == 3 # push, reset (which will set launch_configurations), and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], ResetLaunchConfigurations) assert isinstance(result[2], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 PushLaunchConfigurations().visit(lc1) result = GroupAction([], scoped=False, launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len(result) == 1 # set 1 launch_configurations assert isinstance(result[0], SetLaunchConfiguration) for a in result: a.visit(lc1) assert len( lc1.launch_configurations) == 1 # still set after group was included PopLaunchConfigurations().visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 PushLaunchConfigurations().visit(lc1) result = GroupAction([Action()], scoped=False, launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len( result ) == 2 # set 1 launch_configurations, then the 1 included actions assert isinstance(result[0], SetLaunchConfiguration) assert isinstance(result[1], Action) for a in result: a.visit(lc1) assert len( lc1.launch_configurations) == 1 # still set after group was included PopLaunchConfigurations().visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 result = GroupAction([Action()], forwarding=True, launch_configurations={ 'foo': 'FOO' }).visit(lc1) assert len( result ) == 4 # push, set 1 launch_configurations, the 1 action, and pop actions assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], Action) assert isinstance(result[3], PopLaunchConfigurations) for a in result: a.visit(lc1) assert len(lc1.launch_configurations) == 0 assert len(lc1.launch_configurations) == 0 lc1.launch_configurations['foo'] = 'FOO' lc1.launch_configurations['bar'] = 'BAR' result = GroupAction([Action()], forwarding=False, launch_configurations={ 'bar': LaunchConfiguration('bar'), 'baz': 'BAZ' }).visit(lc1) # push, reset (which will set launch_configurations), 1 action, and pop actions assert len(result) == 4 assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], ResetLaunchConfigurations) assert isinstance(result[2], Action) assert isinstance(result[3], PopLaunchConfigurations) result[0].visit(lc1) # Push assert 'foo' in lc1.launch_configurations.keys( ) # Copied to new scope, before Reset assert lc1.launch_configurations['foo'] == 'FOO' assert 'bar' in lc1.launch_configurations.keys( ) # Copied to new scope, before Reset assert lc1.launch_configurations['bar'] == 'BAR' result[1].visit(lc1) # Reset assert 'foo' not in lc1.launch_configurations.keys( ) # Cleared from scope in Reset assert 'bar' in lc1.launch_configurations.keys( ) # Evaluated and forwarded in Reset assert lc1.launch_configurations['bar'] == 'BAR' assert 'baz' in lc1.launch_configurations.keys( ) # Evaluated and added in Reset assert lc1.launch_configurations['baz'] == 'BAZ' result[2].visit(lc1) # Action result[3].visit(lc1) # Pop assert 'foo' in lc1.launch_configurations.keys() # Still in original scope assert lc1.launch_configurations['foo'] == 'FOO' assert 'bar' in lc1.launch_configurations.keys() # Still in original scope assert lc1.launch_configurations['bar'] == 'BAR' assert 'baz' not in lc1.launch_configurations.keys( ) # Out of scope from pop, no longer exists assert len(lc1.launch_configurations) == 2 lc1.launch_configurations.clear() assert len(lc1.launch_configurations) == 0 lc1.launch_configurations['foo'] = 'FOO' lc1.launch_configurations['bar'] = 'BAR' result = GroupAction([Action()], forwarding=True, launch_configurations={ 'foo': 'OOF' }).visit(lc1) # push, 1 set (overwrite), 1 action, and pop actions assert len(result) == 4 assert isinstance(result[0], PushLaunchConfigurations) assert isinstance(result[1], SetLaunchConfiguration) assert isinstance(result[2], Action) assert isinstance(result[3], PopLaunchConfigurations) result[0].visit(lc1) # Push assert 'foo' in lc1.launch_configurations.keys( ) # Copied to new scope, before Set assert lc1.launch_configurations['foo'] == 'FOO' assert 'bar' in lc1.launch_configurations.keys() # Copied to new scope assert lc1.launch_configurations['bar'] == 'BAR' result[1].visit(lc1) # Set assert 'foo' in lc1.launch_configurations.keys() # Overwritten in Set assert lc1.launch_configurations['foo'] == 'OOF' assert 'bar' in lc1.launch_configurations.keys() # Untouched assert lc1.launch_configurations['bar'] == 'BAR' result[2].visit(lc1) # Action result[3].visit(lc1) # Pop assert 'foo' in lc1.launch_configurations.keys( ) # Still in original scope with original value assert lc1.launch_configurations['foo'] == 'FOO' assert 'bar' in lc1.launch_configurations.keys( ) # Still in original scope with original value assert lc1.launch_configurations['bar'] == 'BAR' lc1.launch_configurations.clear()