ROS2中的LifecycleNode生命周期节点

ROS2引入了LifecycleNode,类似状态机,管理节点状态。主要状态包括Unconfigured、Inactive、Active、Shutdown,切换状态包括Configuring、Activating等。节点在不同状态间转换,如配置、激活、关闭等。当节点遇到错误,会返回Unconfigured状态。在Active状态,节点执行处理、响应服务请求。CleaningUp用于清理资源,Activating准备执行,Deactivating释放资源,ShuttingDown进行销毁前的清理。错误处理状态用于处理错误并尝试恢复或关闭节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文参考:ros2 node_lifecycle
ros2中加入了一种区别于普通的node节点的管理节点LifecycleNode,这种节点有点像是状态机,会在几种不同的状态中切换。

LifecycleNode 节点的状态主要分为基本状态(Primary States)和切换状态(Transition States)。主要状态是任何节点都可以做相应的任务的稳态,切换状态切换过程中短暂的临时状态。这些中间状态的结果用于指示两个主要状态之间的转换是否成功。

主要状态分为4种:
unconfigured
inactive
active
shutdown

切换状态分为6种:
configuring
activating
deactivating
cleaningup
shuttingdown
ErrorProcessing

关于状态的切换可以看这张图:
在这里插入图片描述

Unconfigured

节点实例化之后就会立马进入这个状态,节点出错之后也会返回到这个状态。

有效的状态转换:

  • 可以通过configure切换到Inactive状态
  • 可以通过shutdown切换到Finalized状态

Inactive

这个状态代表节点目前没有进行任何处理。这个状态的主要目的是让节点进行(重新)配置(比如改变配置参数、增加或删除topic的发布和订阅等)而不在运行时改变它的行为。

在这个状态时,节点不会接受任何运行时间来读取topic、进行数据处理或是回应service的请求等。Inactive状态下不会对收到的任何来自管理话题的数据进行读取或者是处理。数据保留将会取决于为主题配置的QoS策略。同时service请求也不会被回应(对请求者来说,会立马fail)。

有效的状态转换:

  • 通过shutdown切换到Finalized
  • 通过cleanup切换到Unconfigured
  • 通过acticate切换到Active

Active

这个lifecycle节点的主要状态。在这个状态时,节点会进行一些处理、回应service请求、读取和处理数据、产生输出等。

如果这个状态下节点或是系统发生了不能处理的错误,那么节点会切换到ErrorProcessing状态。

有效的状态切换:

  • 通过deactivate切换到Inactive
  • 通过shutdown切换到Finalized

Finalized

该状态是节点被销毁前会立马就结束的状态。这个状态只会通往销毁。

有效的状态切换:

  • 通过destroy被消除分配(deallocated)

Configuring

节点的conConfigure回调函数会被调用,来允许节点加载配置以及进行必要的设置。

节点的配置通常都会涉及到一些在节点的生命周期中必须被执行一次的任务,比如说申请内存、配置不会改变的topic的发布/订阅等等。

节点也会用此来设置一些它的整个生命周期中必须保留的资源(不论是active状态还是inactive状态),比如说topic的发布/订阅器,持续需要的内存空间以及初始的配置参数。

有效的状态切换:

  • 如果conConfigure回调函数成功被调用,那么节点会切换到Inactive状态。
  • 如果回调函数出现失败代码(需要具体的代码),那么节点就会返回到Unconfigured状态。
  • 如果回调函数引发或return了别的值,则节点将转换为ErrorProcessing状态。

CleaningUp

节点的onCleanup回调函数会被调用。函数中应当清除所有状态并返回到与初次创建时一样的状态。

有效的状态切换;

  • 如果onCleanup回调函数成功执行,就会被切换到Unconfigured状态。
  • 如果执行出错就会切换到ErrorProcessing

Activating

节点的onActivate回调函数会被调用。函数中应当做好开始执行前的最后准备,包括获得只在节点active期间会用到的资源,比如对硬件的访问。理想情况下,不应该在这里执行需要大量时间的准备工作(比如冗长的硬件初始化,我猜测可能是单目SLAM的初始化这种)。

有效的状态切换;

  • 如果onActivate回调函数成功执行,就会被切换到Active状态。
  • 如果执行出错就会切换到ErrorProcessing

Deactivating

节点的onDeactivate回调函数会被调用。函数中应当执行清除操作以便开始执行,并做onActivate中相反的操作。

有效的状态切换;

  • 如果onDeactivate回调函数成功执行,就会被切换到Active状态。
  • 如果执行出错就会切换到ErrorProcessing

ShuttingDown

节点的onShutdown回调函数会被调用。函数中应当执行销毁前的必要的清除。该状态应当可以从除了Finalized的所有状态进入,函数中应当将节点返回到初始状态。

有效的状态切换;

  • 如果onShutdown回调函数成功执行,就会被切换到Finalized状态。
  • 如果执行出错就会切换到ErrorProcessing

ErrorProcessing

该状态是清除所有错误的地方。可以从所有状态进入该状态。如果错误被成功处理,那么会返回到Unconfigured状态,如果没有执行所有的清理,那它必须失败然后切换到Finalized状态并等待销毁。

ErrorProcessing切换可以是回调函数(或是回调函数中的函数)中生成的错误返回值以及未捕获的异常。

有效的状态切换;

  • 如果onError回调函数成功执行,就会被切换到Unconfigured状态。期望的是onError会清除先前状态的所有状态。比如说如果是从Active进入的,那么必须提供onDeactivateonCleanup的来返回成功。
  • 如果执行出错就会切换到Finalized

实例

可以看下面的实例

#include <iostream>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_lifecycle/lifecycle_node.hpp>

#include "lifecycle_msgs/msg/transition.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

class my_lifecyclenode : public rclcpp_lifecycle::LifecycleNode {
   
   
public:
    // 构造函数
    // lifecyclenode 的构造函数都有相同的参数
    explicit my_lifecyclenode(const std::string& node_name, bool intra_process_comms = false)
        : rclcpp_lifecycle::LifecycleNode(node_name,
                                          rclcpp::NodeOptions().use_intra_process_comms(intra_process_comms)) {
   
   }
    void func() {
   
    std::cout << "in func" << std::endl; }

    // on_configure回调函数会在lifecyclenode进入configuring状态时被调用
    // 根据返回值的不同,节点会进入inactive或者停留在unconfigured
    // RANSITION_CALLBACK_SUCCESS transitions to "inactive"
    // RANSITION_CALLBACK_FAILURE transitions to "unconfigured"
    // TRANSITION_CALLBACK_ERROR or any uncaught exceptions to "errorprocessing"
    rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_configure(
        const rclcpp_lifecycle::State&) {
   
   
        RCLCPP_INFO(get_logger(), "on_configure() is called.");
        return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
    }

    // 没啥好说的,参考on_configure回调函数和前文
    rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_activate(
        const rclcpp_lifecycle::State&) {
   
   
        RCUTILS_LOG_INFO_NAMED(get_name(), "on_activate() is called.");
        std::this_thread::sleep_for(2s);
        return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
    }

    //
    rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_deactivate(
        const rclcpp_lifecycle::State&) {
   
   
        RCUTILS_LOG_INFO_NAMED(get_name(), "on_deactivate() is called.");
        return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
    }

    //
    rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::
<think>好的,我现在需要帮助用户解决如何在ROS2中实现和自定义move_base节点的问题。首先,我要回忆一下用户提供的引用内容,这些引用都是关于ROS1中的move_base的,但用户问的是ROS2,所以我得注意两者的区别。 首先,用户提到的引用[1]到[4]都是关于ROS1的move_base,比如move_base包的结构、全局和局部规划器的集成、成本地图以及参数配置工具如rqt_reconfigure。但ROS2中的导航框架已经发生了变化,从move_base变成了Nav2,所以需要明确告诉用户这一点。 接下来,我需要组织回答的结构。用户的问题分为两部分:实现和自定义。实现部分需要指导用户如何安装Nav2包,配置必要的文件如launch文件和参数文件。自定义部分则需要涉及规划器、控制器、恢复行为等的定制,以及成本地图的调整。 需要注意ROS2ROS1的不同之处,比如Nav2使用生命周期节点,参数文件的结构可能不同,以及新的工具如Nav2 Bringup。同时,用户可能对如何替换全局规划器或局部控制器感兴趣,所以需要给出具体的代码示例或步骤,比如创建新的插件并注册到ROS2系统中。 另外,用户提供的引用中提到move_base连接全局和局部规划器,维护成本地图,这些在ROS2的Nav2中也是核心概念,但实现方式有所不同。比如,Nav2使用插件机制,允许更灵活地替换组件。需要强调Nav2的模块化设计,并说明如何利用这种设计进行自定义。 还要考虑用户可能遇到的问题,比如参数配置错误,插件加载失败,或者启动文件配置不当。在回答中可以给出一些常见问题及解决方法,帮助用户避免常见陷阱。 最后,按照用户的要求,生成相关问题,这些问题应该围绕Nav2的进阶使用,比如性能优化、仿真测试、与SLAM的集成等,确保问题具有实际应用价值,并引导用户进一步探索。</think>### ROS2中move_base节点的实现与自定义指南 ROS2中导航功能通过Nav2框架实现(对应ROS1的move_base),采用模块化架构和生命周期节点管理[^3]。以下是具体实现步骤和自定义方法: #### 一、Nav2基础实现 1. **安装Nav2包** ```bash sudo apt install ros-<distro>-navigation2 ros-<distro>-nav2-bringup ``` 2. **创建启动文件** ```xml <!-- nav2_bringup/launch/navigation_launch.py --> from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='nav2_controller', executable='controller_server', output='screen'), Node( package='nav2_planner', executable='planner_server', output='screen'), # 其他必要节点 ]) ``` 3. **配置参数文件** ```yaml # nav2_params.yaml controller_server: ros__parameters: progress_checker_plugin: "progress_checker" goal_checker_plugin: "goal_checker" ``` #### 二、核心自定义方法 1. **自定义规划器(继承nav2_core接口)** ```cpp #include "nav2_core/global_planner.hpp" class CustomPlanner : public nav2_core::GlobalPlanner { public: void configure(rclcpp_lifecycle::LifecycleNode::SharedPtr parent) override; void activate() override; void deactivate() override; void createPlan( const geometry_msgs::msg::PoseStamped & start, const geometry_msgs::msg::PoseStamped & goal, nav_msgs::msg::Path & plan) override; }; ``` 2. **注册插件** ```xml <!-- custom_plugins.xml --> <library path="custom_nav_plugins"> <class type="custom_nav_plugins::CustomPlanner" base_class_type="nav2_core::GlobalPlanner"> </class> </library> ``` #### 三、关键配置项 1. **组件映射配置** ```yaml planner_server: ros__parameters: planner_plugins: ["GridBased"] GridBased: plugin: "nav2_navfn_planner/NavfnPlanner" ``` 2. **成本地图配置(多层传感器融合)** ```yaml global_costmap: ros__parameters: plugins: ["static_layer", "obstacle_layer"] obstacle_layer: observation_sources: "scan" scan: {data_type: "LaserScan", topic: "/scan"} ``` #### 四、调试与优化 1. **使用RQT动态调参** ```bash ros2 run rqt_reconfigure rqt_reconfigure ``` 2. **可视化调试工具** ```bash ros2 launch nav2_bringup rviz_launch.py ``` [^3]: ROS2的Nav2框架采用生命周期节点管理机制,相比ROS1的move_base具有更好的状态控制能力,支持热插拔组件。 [^4]: 参数配置继承ROS1的优化方案,但采用YAML格式进行结构化配置,支持多级参数嵌套。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值