目标:学习 ROS 2 启动文件中的事件处理程序
教程级别:中级
时间:15 分钟
目录
背景
先决条件
使用事件处理程序
1 事件处理程序示例启动文件
构建包
启动示例
文档
摘要
背景
ROS 2 中的 Launch 是一个执行和管理用户定义的进程的系统。它负责监控其启动的进程的状态,以及报告和响应这些进程状态的变化。这些变化被称为事件,可以通过在启动系统中注册一个事件处理器来处理。事件处理器可以为特定事件注册,并可用于监控进程状态。此外,它们还可以用来定义一组复杂的规则,这些规则可以用来动态修改启动文件。
本教程展示了 ROS 2 启动文件中事件处理程序的使用示例。
先决条件
本教程使用 turtlesim 包。本教程还假设您已经创建了一个名为 launch_tutorial
的新 ament_python
类型构建包。
这个教程扩展了在使用启动文件中的替换教程https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Launch/Using-Substitutions.html 中显示的代码。
使用事件处理程序
1 .事件处理程序示例启动文件
在 launch_tutorial
包的 launch
文件夹中创建一个名为 example_event_handlers_launch.py
的新文件。
# 导入Node类,用于创建节点
from launch_ros.actions import Node
# 导入相关的类和函数
from launch import LaunchDescription
from launch.actions import (DeclareLaunchArgument, EmitEvent, ExecuteProcess,
LogInfo, RegisterEventHandler, TimerAction)
from launch.conditions import IfCondition
from launch.event_handlers import (OnExecutionComplete, OnProcessExit,
OnProcessIO, OnProcessStart, OnShutdown)
from launch.events import Shutdown
from launch.substitutions import (EnvironmentVariable, FindExecutable,
LaunchConfiguration, LocalSubstitution,
PythonExpression)
# 定义启动描述生成函数
def generate_launch_description():
# 创建启动配置对象
turtlesim_ns = LaunchConfiguration('turtlesim_ns') # 创建命名空间配置
use_provided_red = LaunchConfiguration('use_provided_red') # 创建使用提供的红色配置
new_background_r = LaunchConfiguration('new_background_r') # 创建新的背景红色配置
# 声明启动参数
turtlesim_ns_launch_arg = DeclareLaunchArgument(
'turtlesim_ns', # 参数名
default_value='turtlesim1' # 默认值
)
use_provided_red_launch_arg = DeclareLaunchArgument(
'use_provided_red',
default_value='False'
)
new_background_r_launch_arg = DeclareLaunchArgument(
'new_background_r',
default_value='200'
)
# 创建节点
turtlesim_node = Node(
package='turtlesim', # 节点所在的包
namespace=turtlesim_ns, # 节点的命名空间
executable='turtlesim_node', # 节点的可执行文件名
name='sim' # 节点的名字
)
# 执行进程,调用服务生成乌龟
spawn_turtle = ExecuteProcess(
cmd=[[
FindExecutable(name='ros2'), # 查找可执行文件的命令
' service call ', # 调用服务的命令
turtlesim_ns, # 命名空间
'/spawn ', # 服务名
'turtlesim/srv/Spawn ', # 服务类型
'"{x: 2, y: 2, theta: 0.2}"' # 服务参数
]],
shell=True
)
# 执行进程,改变背景颜色的红色分量
change_background_r = ExecuteProcess(
cmd=[[
FindExecutable(name='ros2'), # 查找可执行文件的命令
' param set ', # 设置参数的命令
turtlesim_ns, # 命名空间
'/sim background_r ', # 参数名
'120' # 参数值
]],
shell=True
)
# 执行进程,根据条件改变背景颜色的红色分量
change_background_r_conditioned = ExecuteProcess(
condition=IfCondition( # 条件
PythonExpression([ # Python表达式
new_background_r,
' == 200',
' and ',
use_provided_red
])
),
cmd=[[
FindExecutable(name='ros2'), # 查找可执行文件的命令
' param set ', # 设置参数的命令
turtlesim_ns, # 命名空间
'/sim background_r ', # 参数名
new_background_r # 参数值
]],
shell=True
)
# 返回启动描述对象
return LaunchDescription([
turtlesim_ns_launch_arg,
use_provided_red_launch_arg,
new_background_r_launch_arg,
turtlesim_node,
RegisterEventHandler(
OnProcessStart( # 当进程开始时
target_action=turtlesim_node, # 目标动作是乌龟模拟节点
on_start=[ # 开始时
LogInfo(msg='Turtlesim started, spawning turtle'), # 记录信息
spawn_turtle # 生成乌龟
]
)
),
RegisterEventHandler(
OnProcessIO( # 当进程输入输出时
target_action=spawn_turtle, # 目标动作是生成乌龟的进程
on_stdout=lambda event: LogInfo( # 当标准输出时
msg='Spawn request says "{}"'.format( # 记录信息
event.text.decode().strip()) # 解码并去除两端空白的文本
)
)
),
RegisterEventHandler(
OnExecutionComplete( # 当执行完成时
target_action=spawn_turtle, # 目标动作是生成乌龟的进程
on_completion=[ # 完成时
LogInfo(msg='Spawn finished'), # 记录信息
change_background_r, # 改变背景颜色的红色分量
TimerAction( # 定时器动作
period=2.0, # 定时器周期,每2.0秒执行一次
actions=[change_background_r_conditioned], # 定时器动作,满足条件时改变背景颜色的红色分量
)
]
)
),
RegisterEventHandler(
OnProcessExit( # 当进程退出时
target_action=turtlesim_node, # 目标动作是乌龟模拟节点
on_exit=[ # 退出时
LogInfo(msg=(EnvironmentVariable(name='USER'), # 记录信息,获取环境变量USER的值
' closed the turtlesim window')), # 用户关闭了乌龟模拟窗口
EmitEvent(event=Shutdown( # 发出事件,关闭
reason='Window closed')) # 原因是窗口关闭
]
)
),
RegisterEventHandler(
OnShutdown( # 当关闭时
on_shutdown=[LogInfo( # 记录信息
msg=['Launch was asked to shutdown: ', # 启动被要求关闭
LocalSubstitution('event.reason')] # 本地替换事件原因
)]
)
),
])
在启动描述中定义了 RegisterEventHandler
的动作,用于 OnProcessStart
、 OnProcessIO
、 OnExecutionComplete
、 OnProcessExit
和 OnShutdown
事件。
当 turtlesim 节点启动时, OnProcessStart
事件处理程序用于注册一个回调函数。它会在控制台记录一条消息,并在 turtlesim 节点启动时执行 spawn_turtle
操作。
RegisterEventHandler(
OnProcessStart(
target_action=turtlesim_node,
on_start=[
LogInfo(msg='Turtlesim started, spawning turtle'),
spawn_turtle
]
)
),
# [INFO] [launch.user]: Turtlesim started, spawning turtle
# [INFO] [Spawn "{x: 2, y: 2, theta: 0.2}"-2]: process started with pid [21940]
` OnProcessIO
`事件处理程序用于注册一个回调函数,该函数在` spawn_turtle
`动作写入其标准输出时执行。它记录了生成请求的结果。
RegisterEventHandler(
OnProcessIO(
target_action=spawn_turtle,
on_stdout=lambda event: LogInfo(
msg='Spawn request says "{}"'.format(
event.text.decode().strip())
)
)
),
# [INFO] [launch.user]: Spawn request says "requester: making request: turtlesim.srv.Spawn_Request(x=2.0, y=2.0, theta=0.2, name='')"
OnExecutionComplete
事件处理程序用于注册一个回调函数,该函数在 spawn_turtle
操作完成时执行。它在生成操作完成时会在控制台记录一条消息,并执行 change_background_r
和 change_background_r_conditioned
操作。
RegisterEventHandler(
OnExecutionComplete(
target_action=spawn_turtle,
on_completion=[
LogInfo(msg='Spawn finished'),
change_background_r,
TimerAction(
period=2.0,
actions=[change_background_r_conditioned],
)
]
)
),
# [INFO] [launch.user]: Spawn finished
# [INFO] [sim background_r 120-3]: process started with pid [21975]
# [INFO] [sim background_r 120-3]: process has finished cleanly [pid 21975]
# [INFO] [sim background_r 200-4]: process started with pid [22016]
# [INFO] [sim background_r 200-4]: process has finished cleanly [pid 22016]
当 turtlesim 节点退出时, OnProcessExit
事件处理程序用于注册一个回调函数。它会在控制台记录一条消息,并在 turtlesim 节点退出时执行 EmitEvent
操作来发出 Shutdown
事件。这意味着当 turtlesim 窗口关闭时,启动过程将会关闭。
RegisterEventHandler(
OnProcessExit(
target_action=turtlesim_node,
on_exit=[
LogInfo(msg=(EnvironmentVariable(name='USER'),
' closed the turtlesim window')),
EmitEvent(event=Shutdown(
reason='Window closed'))
]
)
),
# [INFO] [launch.user]: cxy closed the turtlesim window
最后, OnShutdown
事件处理程序用于注册一个回调函数,当启动文件被要求关闭时执行。它会记录一条消息到控制台,说明为什么要求关闭启动文件。它会记录带有关闭原因的消息,比如 turtlesim 窗口的关闭或用户发出的 ctrl-c 信号。
RegisterEventHandler(
OnShutdown(
on_shutdown=[LogInfo(
msg=['Launch was asked to shutdown: ',
LocalSubstitution('event.reason')]
)]
)
),
# [INFO] [launch.user]: Launch was asked to shutdown: Window closed
打包
转到工作区的根目录,然后构建包:
colcon build
cxy@ubuntu2404-cxy:~$ cd ~/ros2_ws
cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select launch_tutorial
Starting >>> launch_tutorial
Finished <<< launch_tutorial [2.36s]
Summary: 1 package finished [2.60s]
cxy@ubuntu2404-cxy:~/ros2_ws$ . install/setup.bash
也请记得在构建后配置工作空间的环境变量。
启动示例
现在您可以使用 ros2 launch
命令启动 example_event_handlers_launch.py
文件。
ros2 launch launch_tutorial example_event_handlers_launch.py turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200
cxy@ubuntu2404-cxy:~/ros2_ws$ ros2 launch launch_tutorial example_event_handlers_launch.py turtlesim_ns:='turtlesim3' use_provided_red:='True' new_background_r:=200
[INFO] [launch]: All log files can be found below /home/cxy/.ros/log/2024-07-10-11-16-00-294697-ubuntu2404-cxy-21936
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [turtlesim_node-1]: process started with pid [21939]
[INFO] [launch.user]: Turtlesim started, spawning turtle
[INFO] [Spawn "{x: 2, y: 2, theta: 0.2}"-2]: process started with pid [21940]
[turtlesim_node-1] [INFO] [1720581360.532861765] [turtlesim3.sim]: Starting turtlesim with node name /turtlesim3/sim
[turtlesim_node-1] [INFO] [1720581360.539665137] [turtlesim3.sim]: Spawning turtle [turtle1] at x=[5.544445], y=[5.544445], theta=[0.000000]
[INFO] [launch.user]: Spawn request says "requester: making request: turtlesim.srv.Spawn_Request(x=2.0, y=2.0, theta=0.2, name='')"
[turtlesim_node-1] [INFO] [1720581361.009548299] [turtlesim3.sim]: Spawning turtle [turtle2] at x=[2.000000], y=[2.000000], theta=[0.200000]
[INFO] [launch.user]: Spawn request says "response:
turtlesim.srv.Spawn_Response(name='turtle2')"
[INFO] [Spawn "{x: 2, y: 2, theta: 0.2}"-2]: process has finished cleanly [pid 21940]
[INFO] [launch.user]: Spawn finished
[INFO] [sim background_r 120-3]: process started with pid [21975]
[INFO] [sim background_r 120-3]: process has finished cleanly [pid 21975]
[INFO] [sim background_r 200-4]: process started with pid [22016]
[INFO] [sim background_r 200-4]: process has finished cleanly [pid 22016]
这将做以下事情:
启动一个带有蓝色背景的 turtlesim 节点
生成第二只乌龟
将颜色更改为紫色
如果提供的
background_r
参数是200
并且use_provided_red
参数是True
,则在两秒后将颜色更改为粉红色当 turtlesim 窗口关闭时,关闭启动文件
此外,当以下情况发生时,它会将消息记录到控制台:
乌龟仿真节点启动
生成操作已执行
执行了
change_background_r
操作执行了
change_background_r_conditioned
操作乌龟仿真节点退出
发射过程被要求关闭。
文档
启动文档 https://docs.ros.org/en/jazzy/p/launch/architecture.html 提供了关于可用事件处理程序的详细信息。
摘要
在本教程中,您学习了如何在启动文件中使用事件处理程序。您了解了它们的语法和使用示例,以定义一组复杂的规则来动态修改启动文件。