在大多数入门教程中,运行的每个新节点打开新的终端。随着创建越来越多的节点同时运行的更复杂的系统,需要重复的打开终端和重新输入。
launch文件允许同时启动和配置许多包含ROS 2节点的可执行文件。
使用 ros2 launch 命令运行单个launch文件将立即启动整个系统所有节点及其配置。
启动一个名为test_node的节点,并指定了该节点的可执行文件所属包、名称、日志输出、参数等选项。
test_node = launch_ros.actions.Node(
package='demo',
executable='test_node',
output='screen',
parameters=[{
'hz': launch.substitutions.LaunchConfiguration('hz'),
}])
LaunchDescription可以包含 参数、其他launch文件和ros节点。
LaunchDescription是ROS 2中用于描述launch文件的主要对象之一。它是一个Python类,用于组织和描述launch文件中的各个启动操作。
在ROS 2中,launch文件用于同时启动和配置多个ROS节点。LaunchDescription对象允许你将多个启动操作组合在一起,并定义它们之间的依赖关系和顺序。通过使用LaunchDescription,可以以可编程的方式构建复杂的启动配置。
以下是LaunchDescription的一些重要特性和用法:
下面是一个简单的示例,展示了如何使用LaunchDescription来创建一个包含两个节点的launch文件描述:
from launch import LaunchDescription
from launch.actions import ExecuteProcess
def generate_launch_description():
ld = LaunchDescription()
node1_action = ExecuteProcess(
cmd=['/path/to/node1'],
output='screen'
)
node2_action = ExecuteProcess(
cmd=['/path/to/node2'],
output='screen'
)
ld.add_action(node1_action)
ld.add_action(node2_action)
return ld
在上述示例中,generate_launch_description函数返回一个LaunchDescription对象,该对象描述了一个包含两个节点的launch文件。通过添加ExecuteProcess(ROS2中用于执行外部进程)操作,指定节点的可执行文件路径,并将它们添加到LaunchDescription对象中。
需要注意的是,LaunchDescription仅用于描述launch文件的结构和内容,要真正运行launch文件,还需要使用ROS 2的启动工具,如ros2 launch命令行工具或者使用Python API进行启动。
总之,LaunchDescription提供了一种灵活且强大的方式来描述和组织ROS 2的launch文件,使得启动多个ROS节点变得更加方便和可控。
在ROS 2的launch文件中,GroupAction是一种用于组合多个动作(action)的特殊类型动作。它允许将多个动作作为一个整体来执行,以便更好地控制和管理节点启动顺序、参数设置和节点之间的依赖关系。
GroupAction可以包含各种类型的动作,如启动节点、设置参数、等待条件等。通过使用GroupAction,可以实现复杂的启动逻辑和依赖管理,以确保节点按照所需的顺序和条件启动。
# example.launch.py
import os
from ament_index_python import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.actions import GroupAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch.substitutions import TextSubstitution
from launch_ros.actions import Node
from launch_ros.actions import PushRosNamespace
def generate_launch_description():
# args that can be set from the command line or a default will be used
background_r_launch_arg = DeclareLaunchArgument(
"background_r", default_value=TextSubstitution(text="0")
)
background_g_launch_arg = DeclareLaunchArgument(
"background_g", default_value=TextSubstitution(text="255")
)
background_b_launch_arg = DeclareLaunchArgument(
"background_b", default_value=TextSubstitution(text="0")
)
chatter_ns_launch_arg = DeclareLaunchArgument(
"chatter_ns", default_value=TextSubstitution(text="my/chatter/ns")
)
# include another launch file
launch_include = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(
get_package_share_directory('demo_nodes_cpp'),
'launch/topics/talker_listener.launch.py'))
)
# include another launch file in the chatter_ns namespace
launch_include_with_namespace = GroupAction(
actions=[
# push_ros_namespace to set namespace of included nodes
PushRosNamespace('chatter_ns'),
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(
get_package_share_directory('demo_nodes_cpp'),
'launch/topics/talker_listener.launch.py'))
),
]
)
# start a turtlesim_node in the turtlesim1 namespace
turtlesim_node = Node(
package='turtlesim',
namespace='turtlesim1',
executable='turtlesim_node',
name='sim'
)
# start another turtlesim_node in the turtlesim2 namespace
# and use args to set parameters
turtlesim_node_with_parameters = Node(
package='turtlesim',
namespace='turtlesim2',
executable='turtlesim_node',
name='sim',
parameters=[{
"background_r": LaunchConfiguration('background_r'),
"background_g": LaunchConfiguration('background_g'),
"background_b": LaunchConfiguration('background_b'),
}]
)
# perform remap so both turtles listen to the same command topic
forward_turtlesim_commands_to_second_turtlesim_node = Node(
package='turtlesim',
executable='mimic',
name='mimic',
remappings=[
('/input/pose', '/turtlesim1/turtle1/pose'),
('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
]
)
return LaunchDescription([
background_r_launch_arg,
background_g_launch_arg,
background_b_launch_arg,
chatter_ns_launch_arg,
launch_include,
launch_include_with_namespace,
turtlesim_node,
turtlesim_node_with_parameters,
forward_turtlesim_commands_to_second_turtlesim_node,
])
是一种特殊的节点容器,用于组合多个节点(ComposableNode)为一个整体。个人理解和GroupAction的区别为:ComposableNodeContainer组合为一个新的节点(一个新的进程);而GroupAction只是强调了依赖关系,其中一个节点运行失败则整个组失败,但仍然是多个节点(多个进程)。
import launch
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode
def generate_launch_description():
container = ComposableNodeContainer(
name='image_container',
namespace='',
package='rclcpp_components',
executable='component_container',
composable_node_descriptions=[
ComposableNode(
package='image_tools',
plugin='image_tools::Cam2Image',
name='cam2image',
remappings=[('/image', '/burgerimage')],
parameters=[{'width': 320, 'height': 240, 'burger_mode': True, 'history': 'keep_last'}],
extra_arguments=[{'use_intra_process_comms': True}]),
ComposableNode(
package='image_tools',
plugin='image_tools::ShowImage',
name='showimage',
remappings=[('/image', '/burgerimage')],
parameters=[{'history': 'keep_last'}],
extra_arguments=[{'use_intra_process_comms': True}])
],
output='both',
)
return launch.LaunchDescription([container])
注意参数传递和Node存在区别,传递的不是字典,而是元组。
PathJoinSubstitution用于拼接目录
from launch_ros.substitutions import FindPackageShare
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import PathJoinSubstitution, TextSubstitution
def generate_launch_description():
colors = {
'background_r': '200'
}
return LaunchDescription([
IncludeLaunchDescription(
PythonLaunchDescriptionSource([
PathJoinSubstitution([
FindPackageShare('launch_tutorial'),
'launch',
'example_substitutions.launch.py'
])
]),
launch_arguments={
'turtlesim_ns': 'turtlesim2',
'use_provided_red': 'True',
'new_background_r': TextSubstitution(text=str(colors['background_r']))
}.items()
)
])
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
config = os.path.join(
get_package_share_directory('launch_tutorial'),
'config',
'turtlesim.yaml'
)
return LaunchDescription([
Node(
package='turtlesim',
executable='turtlesim_node',
namespace='turtlesim2',
name='sim',
parameters=[config]
)
])