< >
Home » ROS2入门教程 » ROS2入门教程-lifecycle简介

ROS2入门教程-lifecycle简介

ROS2入门教程-lifecycle简介

说明:

  • 介绍ros2程序的生命周期管理

LifecycleNodes/生命周期节点

  • ROS2 引入了托管节点的概念,也称为 LifecycleNodes。

  • 在接下来的教程中,我们将解释这些节点的用途、它们与常规节点的不同之处以及它们如何遵守生命周期管理。

  • 受管节点的范围是有限数量的状态的状态机。

  • 可以通过调用指示后续连续状态的转换 id 来更改这些状态。

  • 我们的实现区分了主要状态和过渡状态。

  • 主要状态应该是稳定状态,任何节点都可以在其中完成受人尊敬的任务。

  • 另一方面,过渡状态是指附加到过渡的临时中间状态。

  • 这些中间状态的结果用于指示两个主要状态之间的转换是否被认为是成功的。

  • 因此,任何受管节点都可以处于以下状态之一:

主要状态(稳态):

  • 状态
unconfigured/未配置
inactive/未激活
active/激活
shutdown/关闭

过渡状态(中间状态):

  • 状态
configuring/正在配置
activating/正在激活
deactivating/正在停用
cleaningup/正在清理
shuttingdown/正在关闭

要调用的可能转换是:

  • 状态
configure/配置
activate/启用
deactivate/停用
cleanup/清理
shutdown/关闭

演示:

  • 三个独立的程序
lifecycle_talker
lifecycle_listener
lifecycle_service_client
  • Lifecycle_talker 代表一个受管节点,根据节点所处的状态发布。

  • 我们将talker节点的任务拆分为单独的部分,并按如下方式执行。

    • 配置:我们初始化我们的发布者和计时器
    • 激活:我们激活发布者和计时器以启用发布
    • 停用:我们停止发布者和计时器
    • 清理:我们销毁发布者和计时器
  • 该原理在此演示中实现为典型的发布者/订阅者演示。
  • 但是,使用可能具有相当长的启动阶段的附加硬件(即激光或相机)对真实场景进行成像。
  • 可以想象在配置状态下启动设备驱动程序,仅启动和停止设备数据的发布,并且只有在清理/关闭阶段才真正关闭设备。
  • lifecycle_listener是一个简单的订阅者,它显示了生命周期谈话者的特征。
  • talker 仅在活动状态下启用消息发布,从而使订阅者仅在 talker 处于活动状态时接收消息。
  • Lifecycle_service_client 是一个脚本,它在生命周期_talker 上调用不同的转换。
  • 这意味着外部用户控制节点的生命周期。
  • 运行lifecycle_talker
$ ros2 run lifecycle lifecycle_talker
  • 运行lifecycle_listener
$ ros2 run lifecycle lifecycle_listener
  • 运行lifecycle_service_client
$ ros2 run lifecycle lifecycle_service_client
  • 使用launch启动程序
ros2 launch lifecycle lifecycle_demo.launch.py
  • 如果我们查看lifecycle_talker 的输出,我们会注意到似乎没有发生任何事情。
  • 这确实是有道理的,因为每个节点都以未配置的方式启动。 Lifecycle_talker 尚未配置,在我们的示例中,还没有创建发布者和计时器。
  • 对于lifecycle_listener,可以看到相同的行为,考虑到此时没有可用的发布者,这并不令人惊讶。
  • 有趣的部分从第三个终端开始。 在那里我们启动了我们的生命周期服务客户端,它负责改变生命周期对话者的状态。

触发转换 1(配置)

  • 查看日志
[lc_client] Transition 1 successfully triggered.
[lc_client] Node lc_talker has current state inactive.
  • 使生命周期对话者将其状态更改为非活动状态。 非活动意味着所有发布者和计时器都已创建和配置。
  • 但是,该节点仍然不活动。 因此没有消息被发布。
  • 查看日志
[lc_talker] on_configure() is called.
Lifecycle publisher is currently inactive. Messages are not published.
...
  • 生命周期侦听器在侦听生命周期对话者的每个状态更改通知时同时收到通知。 实际上,侦听器会收到两个连续的通知。
  • 一种用于从主要状态“未配置”更改为“正在配置”。
  • 因为在生命周期谈话者中配置步骤是成功的,所以从“配置”到“非活动”的第二个通知。
  • 查看日志
[lc_listener] notify callback: Transition from state unconfigured to configuring
[lc_listener] notify callback: Transition from state configuring to inactive

触发转换 2(激活)

  • 查看日志
[lc_client] Transition 2 successfully triggered.
[lc_client] Node lc_talker has current state active.
  • 使生命周期谈话者将其状态更改为活动。
  • 这意味着所有发布者和计时器现在都已激活,因此消息现在正在发布。
  • 查看日志
[lc_talker] on_activate() is called.
[lc_talker] Lifecycle publisher is active. Publishing: [Lifecycle HelloWorld #11]
[lc_talker] Lifecycle publisher is active. Publishing: [Lifecycle HelloWorld #12]
...
  • 生命周期侦听器接收与以前相同的一组通知。
  • Lifecycle talker 将其状态从非活动更改为活动。
  • 查看日志
[lc_listener]: notify callback: Transition from state inactive to activating
[lc_listener]: notify callback: Transition from state activating to active
  • 与之前的转换事件不同的是,我们的监听器现在也接收实际发布的数据。
[lc_listener] data_callback: Lifecycle HelloWorld #11
[lc_listener] data_callback: Lifecycle HelloWorld #12
...
  • 请注意,已发布消息的索引已在 11。
  • 此演示的目的是展示即使我们在生命周期对话者的每个状态都调用发布,但只有当状态处于活动状态时,才会真正发布消息。
  • 至于 beta1,所有其他消息都被忽略了。 为了提供更好的错误处理,此行为可能会在未来版本中更改。
  • 对于演示的其余部分,您将看到类似的输出,因为我们停用并激活生命周期谈话者并最终将其关闭。

代码解读:

  • 如果我们看一下代码,与普通的谈话者相比,生命周期谈话者有一个重大变化。
  • 我们的节点不是继承自常规的 rclcpp::node::Node 而是继承自 rclcpp_lifecycle::LifecycleNode。
class LifecycleTalker : public rclcpp_lifecycle::LifecycleNode
  • LifecycleNodes 的每个子节点都提供了一组回调。 这些回调与附加到它的应用状态机一起出现。
  • 这些回调是:
rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_configure(const rclcpp_lifecycle::State & previous_state)

rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_activate(const rclcpp_lifecycle::State & previous_state)

rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_deactivate(const rclcpp_lifecycle::State & previous_state)

rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_cleanup(const rclcpp_lifecycle::State & previous_state)

rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
on_shutdown(const rclcpp_lifecycle::State & previous_state)
  • 下面我们假设我们在命名空间 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface 内,以缩短返回类型的名称。 所有这些回调都有一个正的默认返回值(return CallbackReturn::SUCCESS)。
  • 这允许生命周期节点更改其状态,即使没有明确的回调函数被覆盖。 还有另一个用于错误处理的回调函数。 每当状态转换抛出未捕获的异常时,我们都会调用 on_error。
CallbackReturn on_error(const rclcpp_lifecycle::State & previous_state)
  • 这为执行自定义错误处理提供了空间。 只有 (!) 在此函数返回 CallbackReturn::SUCCESS 的情况下,状态机才会转换到未配置状态。 默认情况下,on_error 返回 CallbackReturn::FAILURE 并且状态机转换为 finalized。
  • 同时,每个生命周期节点默认有 5 个不同的通信接口。
  • Publisher <node_name>__transition_event:在发生转换时发布。
  • 这允许用户获得网络内转换事件的通知。
  • Service <node_name>__get_state:查询节点的当前状态。
  • 返回主要或过渡状态。
  • Service <node_name>__change_state:触发当前节点的转换。
  • 此服务调用需要一个转换 ID。 只有在此转换 ID 是当前状态的有效转换的情况下,才完成转换。 所有其他情况都被忽略了。
  • Service <node_name>__get_available_states:这是一个自省工具。
  • 它返回该节点可能处于的所有可能状态的列表。
  • Service <node_name>__get_available_transitions:同上,用于自省工具。
  • 它返回该节点可以执行的所有可能转换的列表。

ros2生命周期命令行界面

  • Lifecycle_service_client 应用程序是一个固定顺序脚本,仅用于此演示目的。
  • 它解释了此生命周期实现的使用和 API 调用,但可能不方便使用。
  • 出于这个原因,我们实现了一个命令行工具,可以让您动态更改状态或各种节点。
  • 如果您想获取 lc_talker 节点的当前状态,您可以调用:
$ ros2 lifecycle get /lc_talker
unconfigured [1]
  • 下一步是执行状态更改:
$ ros2 lifecycle set /lc_talker configure
Transitioning successful
  • 为了查看当前可用的状态:
$ ros2 lifecycle list lc_talker
- configure [1]
  Start: unconfigured
  Goal: configuring
- shutdown [5]
  Start: unconfigured
  Goal: shuttingdown
  • 在这种情况下,我们看到当前可用的转换是配置和关闭。
  • 可以使用以下命令查看完整的状态机,这有助于调试或可视化目的
$ ros2 lifecycle list lc_talker -a
- configure [1]
  Start: unconfigured
  Goal: configuring
- transition_success [10]
  Start: configuring
  Goal: inactive
- transition_failure [11]
  Start: configuring
  Goal: unconfigured
- transition_error [12]
  Start: configuring
  Goal: errorprocessing

[...]

- transition_error [62]
  Start: errorprocessing
  Goal: finalized
  • 以上所有命令只不过是调用生命周期节点的服务。
  • 话虽如此,我们也可以直接使用 ros2 命令行界面调用这些服务:
$ ros2 service call /lc_talker/get_state lifecycle_msgs/GetState
requester: making request: lifecycle_msgs.srv.GetState_Request()

response:
lifecycle_msgs.srv.GetState_Response(current_state=lifecycle_msgs.msg.State(id=1, label='unconfigured'))
  • 为了触发转换,我们调用 change_state 服务
$ ros2 service call /lc_talker/change_state lifecycle_msgs/ChangeState "{transition: {id: 2}}"
requester: making request: lifecycle_msgs.srv.ChangeState_Request(transition=lifecycle_msgs.msg.Transition(id=2, label=''))

response:
lifecycle_msgs.srv.ChangeState_Response(success=True)
  • 它稍微不太方便,因为您必须知道对应于每个转换的 ID。 您可以在生命周期_msgs 包中找到它们。
$ ros2 interface show lifecycle_msgs/msg/Transition
  • 功能展望
  • 上面的描述指向了 beta1 的当前开发状态。
  • 该主题的未来待办事项列表包括:
Python 生命周期节点
生命周期管理器:一个全局节点,处理和调度多个节点的触发请求。
LifeyclceSubscriber/LifecycleWalltimer/... 添加更多生命周期控制的实体。

参考:

纠错,疑问,交流: 请进入讨论区点击加入Q群

获取最新文章: 扫一扫右上角的二维码加入“创客智造”公众号


标签: ros2入门教程