【ROS2】中级:Launch- 使用事件处理程序

目标:学习 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 的新文件。

5f13c00db29bff2287f2e1eee98e4d51.png

# 导入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]

66b7658bdcf2511a79c04c731f52d781.png

bd7ae66732d8c0886751d0328638092d.png

这将做以下事情:

  1. 启动一个带有蓝色背景的 turtlesim 节点

  2.  生成第二只乌龟

  3. 将颜色更改为紫色

  4. 如果提供的 background_r 参数是 200 并且 use_provided_red 参数是 True ,则在两秒后将颜色更改为粉红色

  5. 当 turtlesim 窗口关闭时,关闭启动文件

此外,当以下情况发生时,它会将消息记录到控制台:

  1. 乌龟仿真节点启动

  2. 生成操作已执行

  3. 执行了 change_background_r 操作

  4. 执行了 change_background_r_conditioned 操作

  5.  乌龟仿真节点退出

  6. 发射过程被要求关闭。

 文档

启动文档 https://docs.ros.org/en/jazzy/p/launch/architecture.html 提供了关于可用事件处理程序的详细信息。

 摘要

在本教程中,您学习了如何在启动文件中使用事件处理程序。您了解了它们的语法和使用示例,以定义一组复杂的规则来动态修改启动文件。

  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个关于ROS Noetic软件包依赖关系的问题。其中,下列软件包的依赖关系尚不足够满足要求,无法安装: ros-noetic-desktop-full: 依赖于 ros-noetic-desktop,但它不会被安装。 依赖于 ros-noetic-perception,但它不会被安装。 依赖于 ros-noetic-simulators,但它不会被安装。 依赖于 ros-noetic-urdf-sim-tu,但它不会被安装。 ### 回答2: 这个错误提示是说明在安装 ros-noetic-desktop-full 软件包时,发现它需要依赖一些其他的软件包,但是这些软件包未被安装。其中,ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu 是四个未满足依赖关系的软件包。 这个错误提示一般是由于软件源的问题所导致的。在安装软件包时,系统会从软件源中查找该软件包以及它所需的依赖关系。如果软件源中不存在某个软件包的依赖关系,则会提示这个错误信息。 要解决这个问题,可以尝试以下几个方法: 1. 更新软件源:可通过修改软件源配置文件使用软件源管理工具来更新软件源。更新后再次尝试安装软件包,看是否能够解决依赖关系问题。 2. 手动安装依赖关系:如果更新软件源后仍然无法解决依赖关系问题,可以尝试手动安装依赖关系。按照依赖关系的提示,逐个安装这四个软件包。安装完成后再次尝试安装 ros-noetic-desktop-full 软件包,看是否能够正常安装。 3. 使用 aptitude 命令安装:aptitude 命令可以自动处理依赖关系,可能会更好地解决这个问题。可以通过运行以下命令安装 ros-noetic-desktop-full 软件包: sudo aptitude install ros-noetic-desktop-full 以上是我的回答,希望能对你有所帮助。如果你还有其他问题,请随时回复。 ### 回答3: 这个问题意味着在安装 ros-noetic-desktop-full 软件包时,计算机无法满足所有需要的依赖关系。这些依赖关系包括 ros-noetic-desktop、ros-noetic-perception、ros-noetic-simulators 和 ros-noetic-urdf-sim-tu。 在解决这个问题之前,我们需要了解什么是依赖关系。在软件工程中,依赖关系指的是一个软件包需要另一个软件包才能正常运行的情况。例如,在 ROS 中,ros-noetic-desktop-full 需要依赖其他的软件包才能提供完整的功能。 为了解决这个问题,我们可以使用以下方法: 1. 更新软件包源列表。我们可以更新软件包源列表,这有助于计算机查找所需的软件包。在 Ubuntu 系统中,我们可以使用以下命令更新软件包源列表:sudo apt-get update。 2. 安装依赖关系。我们可以尝试单独安装缺失的依赖关系。在 ROS 中,我们可以使用以下命令安装缺失的软件包:sudo apt-get install ros-noetic-desktop ros-noetic-perception ros-noetic-simulators ros-noetic-urdf-sim-tu。 3. 检查软件包仓库。某些情况下,软件包源可能已经过时或不再受支持。我们可以检查软件包仓库,查看软件包是否可用。在 Ubuntu 系统中,我们可以使用以下命令查看软件包仓库:apt-cache search ros-noetic-desktop-full。 总之,无法满足依赖关系的问题是常见的,在解决这个问题之前,我们需要了解依赖关系的概念,并掌握一些解决方法。在 ROS 中,我们可以使用更新软件包源列表、安装依赖关系和检查软件包仓库等方法解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值