赞
踩
ros2 launch文件中最主要的概念是action,ros2 launch把每一个要执行的节点,文件,脚本,功能等全部抽象成action,用统一的接口来控制其启动,最主要的结构是:
def generate_launch_description():
return LaunchDescription([
action_1,
action_2,
...
action_n
])
要启动的节点或其他launch文件全部都传入LaunchDescription()
函数中,该函数中接受一个或多launch.actions
或launch_ros.actions
类型的对象,以下列举一下常用的action:
launch_ros.actions.Node · 此函数功能是启动一个ros2节点; launch_ros.actions.PushRosNamespace · 此函数功能是给一个节点或组设置命名空间; launch.actions.IncludeLaunchDescription · 此函数功能是直接引用另一个launch文件; launch.actions.SetLaunchConfiguration · 此函数功能是在launch文件内声明一个参数,并给定参数值; launch.actions.SetEnvironmentVariable · 此函数功能是声明一个环境变量并给定环境变量的值; launch.actions.AppendEnvironmentVariable · 此函数将对一个环境变量追加一个值,如果不存在则创建; launch.actions.DeclareLaunchArgument · 此函数功能是声明一个启动描述参数,该参数具有名称、默认值和文档; launch.actions.TimerAction · 此函数功能是在一段时间后执行一个或多个action; launch.actions.GroupAction · 此函数功能是将action分组,同组内的action可以统一设定参数方便集中管理; launch.actions.ExecuteProcess · 此函数功能是根据输入执行一个进程或脚本; launch.actions.EmitEvent · 此函数功能是发出一个事件,触发以注册的事件函数被调用; launch.actions.RegisterEventHandler · 此函数功能是注册一个事件; launch.actions.UnregisterEventHandler · 此函数功能是删除一个注册过的事件;
启动一个节点的launch示例
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output='screen'
)
])
或者:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
action_1 = Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output='screen'
)
return LaunchDescription([
action_1
])
launch文件名为test.launch.py,调用launch文件启动节点:
ros2 launch ros2_test test.launch.py
launch文件中添加节点的namespace
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output='screen',
namespace='my_ros2_test'
)
])
设置了namespace后,此节点的话题前会加上namespace的前缀,例如原来的话题名称’/test’,设置后变为’/my_ros2_test/test’
注意,有以下情况namespace无效:
/
;launch文件中的话题名称映射
话题映射的字段是remappings,语法示例如下:
remappings=[
('src1', 'dest1'),
('src2', 'dest2'),
...
]
如果需要话题名称映射的话要将该字段加入到节点的函数内,假如节点内有个test1话题要映射到test2,示例如下:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output="screen",
remappings=[("test1", "test2")]
)
])
launch文件中向节点内传入命令行参数
参数字段是arguments,语法示例如下:
arguments=['arg1', 'arg2', ...]
如果需要向ros2_test_publisher_node节点中传入命令行参数,示例如下:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output="screen",
arguments=['arg1', 'arg2']
)
])
传入的参数可以从节点启动时main函数中的argc参数获取到参数个数,argv参数获取到参数内容,详情参考c++的命令行参数获取;
launch文件中向节点内传入rosparam
字段是parameter,语法示例如下:
parameters=[
{'port': '/dev/ttyUsb0'},
...
]
如果需要在launch文件中发布某个节点空间下的parameter,示例如下:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output="screen",
parameter=[{'port': '/dev/ttyUSB0'}]
)
])
如果需要参数的值可以从外部调用launch文件时灵活更改,则可以使用变量的形式作为参数的值,在文件中给定默认值,若外部给定则以外部给定的值为准,若外部不给则以默认值为准:
port_var = LaunchConfiguration('port', default='/dev/ttyS1')
同时还可以使用DeclareLaunchArgument函数增加对参数的描述,增加描述后可以通过ros2 launch的命令行工具展示出每个launch文件中有哪些rosparam以及他的默认值和具体含义:
DeclareLaunchArgument(
'port',
default_value=port_var,
description='This is the port address value'
)
如果文件中加入了参数介绍则可以通过以下指令看到launch文件中有哪些可以自定义的参数以及参数含义:
ros2 launch ros2_test test.launch.py --show-args
所以如果launch文件中存在可配置参数,应该在LaunchDescription中添加DeclareLaunchArgument,让其他使用者明白变量名,以及他的含义是什么;
完整的launch文件如下:
from launch import LaunchDescription from launch_ros.actions import Node from launch.substitutions import LaunchConfiguration def generate_launch_description(): port_var = LaunchConfiguration('port', default='/dev/ttyS1') return LaunchDescription([ DeclareLaunchArgument( 'port', default_value=port_var, description='This is the port address value' ), Node( package='ros2_test', executable='ros2_test_publisher_node', name='ros2_test_publisher_node', output="screen", parameter=[{'port': port_var}] ) ])
此时我们可以把参数通过调用launch的时候传入到节点中:
ros2 launch ros2_test test.launch.py port_var:=/dev/ttyUSB2
在ros2_test_publisher_node节点中如果要想获取该参数,需要先声明parameter,然后再进行get:
this->declare_parameter<std::string>("port");
string port_str;
this->get_parameter_or<std::string>("port", port_str, "null");
if (port_str != "null")
RCLCPP_INFO(this->get_logger(), "get ros2param port value : %s", port_str.c_str());
在launch文件中使用条件变量
字段是condition,语法示例如下:
condition=IfCondition(variable)
IfCondition(variable)
函数内只接受true, false, 0, 1参数,如果节点启动的描述中加入该字段并且参数值为false或0则节点不启动
完整的launch文件如下:
from launch import LaunchDescription from launch_ros.actions import Node from launch.substitutions import LaunchConfiguration from launch.conditions import IfCondition def generate_launch_description(): variable = LaunchConfiguration('start_flag', default='true') return LaunchDescription([ DeclareLaunchArgument( 'start_flag', default_value=variable, description='This is the ros2_test_publisher_node start flag' ), Node( package='ros2_test', executable='ros2_test_publisher_node', name='ros2_test_publisher_node', output="screen", condition=IfCondition(variable) ) ])
此时
运行:ros2 launch ros2_test test.launch.py
节点会正常启动
运行:ros2 launch ros2_test test.launch.py start_flag:=false
节点不会启动
进阶的IfCondition函数使用方式:
由于LaunchConfiguration()函数的返回是一个对象,所以我们不可以拿来直接做运算,但是可以使用PythonExpression()
做参数的表达式运算,语法如下:
PythonExpression([variable, '== 1']) 或
PythonExpression([variable, '+ 1 == 2']) 或
PythonExpression([variable, "== 'test'"]) 等等
完整的launch文件如下:
from launch import LaunchDescription from launch_ros.actions import Node from launch.substitutions import LaunchConfiguration from launch.conditions import IfCondition def generate_launch_description(): port_var = LaunchConfiguration('port', default='/dev/ttyS1') return LaunchDescription([ DeclareLaunchArgument( 'port', default_value=port_var, description='This is the port address value' ), Node( package='ros2_test', executable='ros2_test_publisher_node', name='ros2_test_publisher_node', output="screen", condition=IfCondition(PythonExpression([port_var, "== '/dev/ttyUSB0'"])) ) ])
此时
运行:ros2 launch ros2_test test.launch.py
节点不会启动
运行:ros2 launch ros2_test test.launch.py port_var:=/dev/ttyUSB0
节点正常启动
action的分组
GroupAction()
函数可以将一个或多个action加入到一个组中,组内可以共用参数,控制整组节点全部启动或全部不启动等,方便action的管理,需要接受的部分参数有:
actions:接受一个list, [action_1, action_2,…],列表中装要执行的action
condition:条件变量参数
launch_configuration:参数
完整的launch文件如下:
from launch import LaunchDescription from launch_ros.actions import Node from launch.actions import GroupAction from launch_ros.actions import PushRosNamespace def generate_launch_description(): action_1 = Node( package='ros2_test', executable='ros2_test_publisher_node', name='ros2_test_publisher_node', output='screen', namespace='my_ros2_test' ) action_2 = Node( package='ros2_test', executable='ros2_test_subscriber_node', name='ros2_test_subscriber_node', output='screen' ) test_group = GroupAction( actions=[ PushRosNamespace("my_group_test"), action_1, action_2 ] ) return LaunchDescription([ test_group ])
示例代码中PushRosNamespace()
函数的作用是向test_group中设置组内的namespace,但是action_1中已经设置过了namespace,所以此时不会生效,该namespace只会在action_2中生效;
action的启动延时控制
TimerAction()
函数可以在指定的时间后执行一个action,需要接受参数有
完整的launch文件如下:
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import TimerAction
def generate_launch_description():
action_1 = Node(
package='ros2_test',
executable='ros2_test_publisher_node',
name='ros2_test_publisher_node',
output='screen'
)
return LaunchDescription([
TimerAction(period=5.0, actions=[action_1])
])
在launch文件中调用另一个launch文件
在launch文件中调用其他launch需要调用PythonLaunchDescriptionSource()
函数,参数是被调用launch的路径,示例如下:
import os from ament_index_python import get_package_share_directory from launch import LaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='ros2_test', executable='ros2_test_publisher_node', name='ros2_test_publisher_node', output='screen' ), PythonLaunchDescriptionSource( os.path.join( get_package_share_directory('ros2_test'), 'launch/test_subscriber.launch.py' ) ) ])
还可以更复杂一点的写法,可以像启动节点一样,加入条件以及参数等,示例如下:
from launch.launch_description_sources import PythonLaunchDescriptionSource from launch.substitutions import ThisLaunchFileDir from launch.conditions import IfCondition from launch.substitutions import LaunchConfiguration from launch.actions import IncludeLaunchDescription def generate_launch_description(): inlucde_other_file = LaunchConfiguration('inlucde_other_file', default='true') test_var = LaunchConfiguration('test_var', default='test_var_2') return launch.LaunchDescription([ IncludeLaunchDescription( PythonLaunchDescriptionSource( [ThisLaunchFileDir(), '/test_subscriber.launch.py'] ), condition=IfCondition(inlucde_other_file), launch_arguments={'test_var': test_var}.items() ) ])
在launch文件中运行脚本
from launch import LaunchDescription
from launch.actions import ExecuteProcess
def generate_launch_description():
return LaunchDescription([
ExecuteProcess(
cmd=['ros2', 'run', 'ros2_test', 'ros2_test_publisher_node'],
output="screen"
)
])
以上脚本同样可实现启动ros2_test_publisher_node节点,同时ExecuteProcess()
函数还支持condition、additional_env等参数的设置;
在launch文件中设置环境变量或读取环境变量
SetEnvironmentVariable(var_name, var_value)
函数用于设置环境变量,接受两个参数分别是要设置的环境变量名称和环境变量的值;
EnvironmentVariable(var_name)
函数用于获取环境变量的值,返回值类型是一个对象,输入参数是要获取的环境变量名称;
完整的launch文件如下:
from launch import LaunchDescription
from launch.actions import SetEnvironmentVariable, ExecuteProcess
from launch.substitutions import EnvironmentVariable
def generate_launch_description():
return LaunchDescription([
SetEnvironmentVariable('PRODUCT_MODEL', 'spray_robot'),
ExecuteProcess(
cmd=['echo', EnvironmentVariable('PRODUCT_MODEL')],
output='screen')
])
运行以上脚本会看到如下输出:
[INFO] [echo-1]: process started with pid [195168]
[echo-1] spray_robot
[INFO] [echo-1]: process has finished cleanly [pid 195168]
至此,ros2 launch 的基础功能介绍结束;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。