【ROS2】中级-监控参数变化(C++)

目标:学习使用 ParameterEventHandler 类来监控和响应参数变化。

教程级别:中级

 时间:20 分钟

 目录

  •  背景

  •  先决条件

  •  任务

    • 创建一个包

    • 编写 C++ 节点

    • 建立并运行

  •  摘要

  •  相关内容

 背景

经常需要节点响应其自身参数或其他节点参数的变化。ParameterEventHandler 类使得监听参数变化变得简单,以便您的代码可以对它们做出响应。本教程将向您展示如何使用 ParameterEventHandler 类的 C++ 版本来监控节点自身参数的变化以及其他节点参数的变化。

 先决条件

在开始本教程之前,您应该先完成以下教程:

  •  理解参数 https://docs.ros.org/en/jazzy/Tutorials/Beginner-CLI-Tools/Understanding-ROS2-Parameters/Understanding-ROS2-Parameters.html 

  • 使用类中的参数 (C++)  https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Using-Parameters-In-A-Class-CPP.html

 任务

在本教程中,您将创建一个新包来包含一些示例代码,编写一些 C++代码以使用 ParameterEventHandler 类,并测试结果代码。

创建一个包

首先,打开一个新的终端并且配置你的 ROS 2 安装,以便 ros2 命令能够工作。

按照这些指示创建一个名为 ros2_ws 的新工作区。

请记住,包应该在 src 目录中创建,而不是在工作区的根目录中。因此,请导航到 ros2_ws/src ,然后在那里创建一个新包:

cxy@ubuntu2404-cxy:~/ros2_ws/src$ ros2 pkg create --build-type ament_cmake --license Apache-2.0 cpp_parameter_event_handler --dependencies rclcpp
going to create a new package
package name: cpp_parameter_event_handler
destination directory: /home/cxy/ros2_ws/src
package format: 3
version: 0.0.0
description: TODO: Package description
maintainer: ['cxy <cxy@todo.todo>']
licenses: ['Apache-2.0']
build type: ament_cmake
dependencies: ['rclcpp']
creating folder ./cpp_parameter_event_handler
creating ./cpp_parameter_event_handler/package.xml
creating source and include folder
creating folder ./cpp_parameter_event_handler/src
creating folder ./cpp_parameter_event_handler/include/cpp_parameter_event_handler
creating ./cpp_parameter_event_handler/CMakeLists.txt

您的终端将返回一条消息,确认您的包 cpp_parameter_event_handler 及其所有必要的文件和文件夹已创建。

--dependencies `参数将自动向` package.xml `和` CMakeLists.txt `添加必要的依赖行。

 1.1 更新 package.xml 

因为您在创建包时使用了 --dependencies 选项,您无需手动将依赖项添加到 package.xml 或 CMakeLists.txt 。但是,一如既往,请确保将描述、维护者电子邮件和姓名以及许可信息添加到 package.xml 。

<description>C++ parameter events client tutorial</description>
  <maintainer email="cxy@126.com">cxy</maintainer>
  <license>Apache-2.0</license>

2. 编写 C++ 节点

在 ros2_ws/src/cpp_parameter_event_handler/src 目录中,创建一个名为 parameter_event_handler.cpp 的新文件,并将以下代码粘贴其中:

#include <memory>  // 引入内存管理库


#include "rclcpp/rclcpp.hpp"  // 引入ROS2的rclcpp库


class SampleNodeWithParameters : public rclcpp::Node  // 定义一个名为SampleNodeWithParameters的类,继承自rclcpp::Node
{
public:
  SampleNodeWithParameters()  // 类的构造函数
  : Node("node_with_parameters")  // 初始化节点名称为"node_with_parameters"
  {
    this->declare_parameter("an_int_param", 0);  // 声明一个名为"an_int_param"的参数,初始值为0


    // 创建一个参数订阅者,用于监控参数变化(包括此节点的参数以及其他节点的参数)
    param_subscriber_ = std::make_shared<rclcpp::ParameterEventHandler>(this);


    // 为此节点的整数参数"an_int_param"设置一个回调
    auto cb = [this](const rclcpp::Parameter & p) {
        RCLCPP_INFO(
          this->get_logger(), "cb: Received an update to parameter \"%s\" of type %s: \"%ld\"",
          p.get_name().c_str(),
          p.get_type_name().c_str(),
          p.as_int());
      };
    cb_handle_ = param_subscriber_->add_parameter_callback("an_int_param", cb);  // 添加参数回调
  }


private:
  std::shared_ptr<rclcpp::ParameterEventHandler> param_subscriber_;  // 参数订阅者
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle_;  // 回调句柄
};


int main(int argc, char ** argv)  // 主函数
{
  rclcpp::init(argc, argv);  // 初始化ROS2节点
  rclcpp::spin(std::make_shared<SampleNodeWithParameters>());  // 创建SampleNodeWithParameters节点并开始执行
  rclcpp::shutdown();  // 关闭ROS2节点


  return 0;  // 返回0表示程序正常结束
}
检查代码 2.1

第一个声明, #include <memory> 被包含在内,以便代码可以使用 std::make_shared 模板。接下来, #include "rclcpp/rclcpp.hpp" 被包含在内,以允许代码引用 rclcpp 接口提供的各种功能,包括 ParameterEventHandler 类

在类声明之后,代码定义了一个类, SampleNodeWithParameters 。该类的构造函数声明了一个整数参数 an_int_param ,默认值为 0。接下来,代码创建了一个 ParameterEventHandler ,用于监控参数的变化。最后,代码创建了一个 lambda 函数,并将其设置为每当 an_int_param 更新时调用的回调函数。

 便条

保存由 add_parameter_callback 返回的句柄非常重要;否则,回调将无法正确注册。

SampleNodeWithParameters()
: Node("node_with_parameters")
{
  this->declare_parameter("an_int_param", 0);


  // Create a parameter subscriber that can be used to monitor parameter changes
  // (for this node's parameters as well as other nodes' parameters)
  param_subscriber_ = std::make_shared<rclcpp::ParameterEventHandler>(this);


  // Set a callback for this node's integer parameter, "an_int_param"
  auto cb = [this](const rclcpp::Parameter & p) {
      RCLCPP_INFO(
        this->get_logger(), "cb: Received an update to parameter \"%s\" of type %s: \"%ld\"",
        p.get_name().c_str(),
        p.get_type_name().c_str(),
        p.as_int());
    };
  cb_handle_ = param_subscriber_->add_parameter_callback("an_int_param", cb);
}

跟随 SampleNodeWithParameters 是一个典型的 main 函数,它初始化 ROS,旋转样本节点以便它可以发送和接收消息,然后在用户在控制台输入^C 之后关闭。

int main(int argc, char ** argv)
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<SampleNodeWithParameters>());
  rclcpp::shutdown();


  return 0;
}
 2.2 添加可执行文件

要构建此代码,请首先打开 CMakeLists.txt 文件,并在依赖项 find_package(rclcpp REQUIRED) 下面添加以下代码行

add_executable(parameter_event_handler src/parameter_event_handler.cpp)
ament_target_dependencies(parameter_event_handler rclcpp)


install(TARGETS
  parameter_event_handler
  DESTINATION lib/${PROJECT_NAME}
)

3. 构建并运行

在工作区的根目录运行 rosdep ( ros2_ws )以检查构建前缺失的依赖项是一个好习惯:

rosdep install -i --from-path src --rosdistro $ROS_DISTRO -y

返回到您的工作区根目录, ros2_ws ,然后构建您的新包:

cxy@ubuntu2404-cxy:~/ros2_ws$ colcon build --packages-select cpp_parameter_event_handler
Starting >>> cpp_parameter_event_handler
[Processing: cpp_parameter_event_handler]                              
Finished <<< cpp_parameter_event_handler [30.1s]                    


Summary: 1 package finished [42.1s]

打开一个新的终端,导航到 ros2_ws ,并且导入设置文件:

. install/setup.bash

现在运行节点:

ros2 run cpp_parameter_event_handler parameter_event_handler

节点现在已激活,具有单一参数,并且每当此参数更新时都会打印一条消息。为了测试这一点,请打开另一个终端,像以前一样源 ROS 设置文件(. install/setup.bash),然后执行以下命令:

ros2 param set node_with_parameters an_int_param 43

运行节点的终端将显示类似于以下的消息:

[INFO] [1606950498.422461764] [node_with_parameters]: cb: Received an update to parameter "an_int_param" of type integer: "43"

我们之前在节点中设置的回调函数已被调用,并显示了新的更新值。您现在可以使用终端中的 ^C 终止正在运行的 parameter_event_handler 示例。

c3624391cd1a7785aadd11fe4090a3ab.png

3.1 监视另一节点参数的变化

您还可以使用 ParameterEventHandler 来监控其他节点参数的变化。让我们更新 SampleNodeWithParameters 类,以便也监控另一个节点中参数的变化。我们将使用 parameter_blackboard 演示应用程序来托管一个双参数,我们将监控其更新。

首先更新构造函数,将以下代码添加到现有代码之后:

// Now, add a callback to monitor any changes to the remote node's parameter. In this
// case, we supply the remote node name.
auto cb2 = [this](const rclcpp::Parameter & p) {
    RCLCPP_INFO(
      this->get_logger(), "cb2: Received an update to parameter \"%s\" of type: %s: \"%.02lf\"",
      p.get_name().c_str(),
      p.get_type_name().c_str(),
      p.as_double());
  };
auto remote_node_name = std::string("parameter_blackboard");
auto remote_param_name = std::string("a_double_param");
cb_handle2_ = param_subscriber_->add_parameter_callback(remote_param_name, cb2, remote_node_name);

然后添加另一个成员变量, cb_handle2 用于额外的回调句柄:

private:
  std::shared_ptr<rclcpp::ParameterEventHandler> param_subscriber_;
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle_;
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle2_;  // Add this
};

完整代码:使用C++智能指针来管理资源,并通过回调函数来响应参数的变化

#include <memory> // 包含内存管理的头文件


#include "rclcpp/rclcpp.hpp" // 包含ROS 2的核心库


// 定义一个名为SampleNodeWithParameters的类,继承自rclcpp::Node
class SampleNodeWithParameters : public rclcpp::Node
{
public:
  // 构造函数,初始化节点名称为"node_with_parameters"
  SampleNodeWithParameters()
  : Node("node_with_parameters")
  {
    // 声明一个名为"an_int_param"的整数参数,默认值为0
    this->declare_parameter("an_int_param", 0);


    // 创建一个参数订阅器,用于监控参数的变化
    // (不仅仅是这个节点的参数,也包括其他节点的参数)
    param_subscriber_ = std::make_shared<rclcpp::ParameterEventHandler>(this);


    // 为这个节点的整数参数"an_int_param"设置一个回调函数
    auto cb = this {
        RCLCPP_INFO(
          this->get_logger(), "cb: Received an update to parameter \"%s\" of type %s: \"%ld\"",
          p.get_name().c_str(),
          p.get_type_name().c_str(),
          p.as_int());
      };
    // 将回调函数绑定到参数订阅器上
    cb_handle_ = param_subscriber_->add_parameter_callback("an_int_param", cb);
    
    // 现在,添加一个回调函数来监控远程节点参数的变化。在这个例子中,我们提供了远程节点的名称。
    auto cb2 = this {
        RCLCPP_INFO(
          this->get_logger(), "cb2: Received an update to parameter \"%s\" of type: %s: \"%.02lf\"",
          p.get_name().c_str(),
          p.get_type_name().c_str(),
          p.as_double());
      };
    // 定义远程节点的名称
    auto remote_node_name = std::string("parameter_blackboard");
    // 定义远程参数的名称
    auto remote_param_name = std::string("a_double_param");
    // 将回调函数绑定到远程节点的参数订阅器上
    cb_handle2_ = param_subscriber_->add_parameter_callback(remote_param_name, cb2, remote_node_name);
  }


private:
  // 定义一个参数事件处理器的共享指针
  std::shared_ptr<rclcpp::ParameterEventHandler> param_subscriber_;
  // 定义一个参数回调句柄的共享指针
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle_;
  // 定义另一个参数回调句柄的共享指针
  std::shared_ptr<rclcpp::ParameterCallbackHandle> cb_handle2_;
};


// 主函数
int main(int argc, char ** argv)
{
  // 初始化ROS 2
  rclcpp::init(argc, argv);
  // 创建一个SampleNodeWithParameters节点并开始运行
  rclcpp::spin(std::make_shared<SampleNodeWithParameters>());
  // 关闭ROS 2
  rclcpp::shutdown();


  return 0; // 返回0表示程序正常结束
}

cfefa41910743c7de59c8f577cd72284.png

在终端中,导航回到您的工作区根目录, ros2_ws ,然后像以前一样构建您的更新包:

colcon build --packages-select cpp_parameter_event_handler

然后获取设置文件:

. install/setup.bash

现在,为了测试远程参数的监控,请首先运行新构建的 parameter_event_handler 代码:

ros2 run cpp_parameter_event_handler parameter_event_handler

接下来,在另一个已初始化 ROS 的终端中,运行 parameter_blackboard 演示应用程序,如下:

ros2 run demo_nodes_cpp parameter_blackboard

最后,在第三个终端(已初始化 ROS),让我们在 parameter_blackboard 节点上设置一个参数:

ros2 param set parameter_blackboard a_double_param 3.45

最后,在第三个终端(已初始化 ROS),让我们在 parameter_blackboard 节点上设置一个参数:

ros2 param set parameter_blackboard a_double_param 3.45

执行此命令后,您应该会在 parameter_event_handler 窗口中看到输出,表明在参数更新时调用了回调函数:

[INFO] [1606952588.237531933] [node_with_parameters]: cb2: Received an update to parameter "a_double_param" of type: double: "3.45"

6faaf048c66bd0cfe0acdeb5ab3a310b.png

 摘要

您创建了一个带有参数的节点,并使用 ParameterEventHandler 类设置了一个回调来监视该参数的变化。您还使用同一个类来监视远程节点的变化。ParameterEventHandler 是一种监控参数变化的便捷方式,以便您可以对更新后的值做出响应。

 相关内容 

要了解如何将 ROS 1 参数文件适配到 ROS 2,请参阅从 ROS 1 到 ROS 2 迁移 YAML 参数文件的教程。https://docs.ros.org/en/jazzy/How-To-Guides/Migrating-from-ROS1/Migrating-Parameters.html 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值