【ROS2】演示:创建内容过滤订阅

目标:创建内容过滤订阅。

 教程级别:高级

 时间:15 分钟

 目录

  •  概述

  •  RMW 支持

  •  安装演示

  • 温度过滤演示

  •  相关内容

 概述 

ROS 2 应用程序通常由主题组成,以便将数据从发布者传输到订阅者。基本上,订阅者会接收主题上发布者发布的所有数据。但有时,订阅者可能只对发布者发送的数据的一个子集感兴趣内容过滤订阅允许仅接收应用程序感兴趣的数据。

在此演示中,我们将重点介绍如何创建内容过滤订阅及其工作原理。

 RMW 支持 

内容过滤订阅需要 RMW 实施支持。

内容过滤订阅支持状态 

rmw_fastrtps

 支持

rmw_connextdds

 支持

rmw_cyclonedds

 不支持

目前所有支持内容过滤订阅的 RMW 实现都是基于 DDS 的。这意味着支持的过滤表达式和参数也依赖于 DDS https://www.omg.org/omg-dds-portal/ ,详情请参阅 DDS https://www.omg.org/spec/DDS/1.4/PDF 规范 Annex B - Syntax for Queries and Filters 。

安装演示 

请参阅安装说明以了解安装 ROS 2 的详细信息。

如果您已经从软件包安装了 ROS 2,请确保已安装 ros-jazzy-demo-nodes-cpp 。如果您下载了存档或从源代码构建了 ROS 2,它将已经是安装的一部分。

温度过滤演示 

此演示展示了如何使用内容过滤订阅仅接收超出可接受温度范围的温度值,从而检测紧急情况。内容过滤订阅会过滤掉不感兴趣的温度数据,因此不会发出订阅回调。

内容过滤发布者:

https://github.com/ros2/demos/blob/jazzy/demo_nodes_cpp/src/topics/content_filtering_publisher.cpp

#include <array> // 引入数组头文件
#include <chrono> // 引入时间库头文件
#include <memory> // 引入智能指针头文件
#include <utility> // 引入实用工具头文件


#include "rclcpp/rclcpp.hpp" // 引入ROS2的核心库
#include "rclcpp_components/register_node_macro.hpp" // 引入ROS2节点注册宏


#include "std_msgs/msg/float32.hpp" // 引入标准消息类型Float32


#include "demo_nodes_cpp/visibility_control.h" // 引入可见性控制头文件


namespace demo_nodes_cpp
{
// 模拟的温度数据从-100.0开始,到150.0结束,步长为10.0
constexpr std::array<float, 3> TEMPERATURE_SETTING {-100.0f, 150.0f, 10.0f};


// 创建一个ContentFilteringPublisher类,继承自rclcpp::Node基类。
// 下面的主函数将实例化这个类作为一个ROS节点。
class ContentFilteringPublisher final : public rclcpp::Node
{
public:
  DEMO_NODES_CPP_PUBLIC
  explicit ContentFilteringPublisher(const rclcpp::NodeOptions & options)
  : Node("content_filtering_publisher", options)
  {
    // 创建一个用于发送消息的函数。
    auto publish_message =
      [this]() -> void
      {
        msg_ = std::make_unique<std_msgs::msg::Float32>(); // 创建一个Float32类型的消息
        msg_->data = temperature_; // 设置消息数据为当前温度
        temperature_ += TEMPERATURE_SETTING[2]; // 温度增加步长
        if (temperature_ > TEMPERATURE_SETTING[1]) { // 如果温度超过最大值
          temperature_ = TEMPERATURE_SETTING[0]; // 重置为最小值
        }
        RCLCPP_INFO(this->get_logger(), "Publishing: '%f'", msg_->data); // 记录发布的消息
        // 将消息放入队列以供中间件处理。
        // 这个调用是非阻塞的。
        pub_->publish(std::move(msg_)); // 发布消息
      };
    // 创建一个具有自定义质量服务配置文件的发布者。
    // 建议使用统一初始化,以便用户可以轻松更改为rclcpp::KeepAll{}。
    // (rclcpp::KeepLast(7) -> rclcpp::KeepAll() 编译失败)
    rclcpp::QoS qos(rclcpp::KeepLast{7});
    pub_ = this->create_publisher<std_msgs::msg::Float32>("temperature", qos); // 创建发布者


    int64_t publish_ms = this->declare_parameter("publish_ms", 1000); // 声明发布间隔参数,默认值为1000毫秒


    // 使用定时器定期调度消息发布。
    timer_ = this->create_wall_timer(std::chrono::milliseconds(publish_ms), publish_message); // 创建定时器
  }


private:
  float temperature_ = TEMPERATURE_SETTING[0]; // 初始化温度为最小值
  std::unique_ptr<std_msgs::msg::Float32> msg_; // 定义一个Float32类型的智能指针消息
  rclcpp::Publisher<std_msgs::msg::Float32>::SharedPtr pub_; // 定义一个Float32类型的发布者
  rclcpp::TimerBase::SharedPtr timer_; // 定义一个定时器
};


}  // namespace demo_nodes_cpp


RCLCPP_COMPONENTS_REGISTER_NODE(demo_nodes_cpp::ContentFilteringPublisher) // 注册节点

内容过滤器是在订阅方定义的,发布者不需要以任何特殊方式进行配置以允许内容过滤。 ContentFilteringPublisher 节点发布的模拟温度数据从-100.0 开始,到 150.0 结束,每秒步长为 10.0。

我们可以通过运行 ros2 run demo_nodes_cpp content_filtering_publisher 可执行文件来运行演示(不要忘记先获取设置文件的源):

30051ac6c740a55e40727dd12c340fa7.png

内容过滤订阅者:

https://github.com/ros2/demos/blob/jazzy/demo_nodes_cpp/src/topics/content_filtering_subscriber.cpp

#include <array> // 引入数组库
#include <string> // 引入字符串库


#include "rclcpp/rclcpp.hpp" // 引入ROS 2的C++客户端库
#include "rclcpp_components/register_node_macro.hpp" // 引入ROS 2节点注册宏
#include "rcpputils/join.hpp" // 引入rcpputils库中的join函数


#include "std_msgs/msg/float32.hpp" // 引入标准消息类型Float32


#include "demo_nodes_cpp/visibility_control.h" // 引入可见性控制头文件


namespace demo_nodes_cpp // 定义命名空间demo_nodes_cpp
{
// 紧急温度数据小于-30或大于100
constexpr std::array<float, 2> EMERGENCY_TEMPERATURE {-30.0f, 100.0f};


// 创建一个ContentFilteringSubscriber类,继承自通用的rclcpp::Node基类。
// 下面的主函数将实例化该类作为一个ROS节点。
class ContentFilteringSubscriber : public rclcpp::Node
{
public:
  DEMO_NODES_CPP_PUBLIC
  explicit ContentFilteringSubscriber(const rclcpp::NodeOptions & options)
  : Node("content_filtering_subscriber", options) // 初始化节点名称为content_filtering_subscriber
  {
    // 创建一个回调函数用于接收消息。
    auto callback =
      this -> void
      {
        if (msg.data < EMERGENCY_TEMPERATURE[0] || msg.data > EMERGENCY_TEMPERATURE[1]) {
          RCLCPP_INFO(
            this->get_logger(),
            "I receive an emergency temperature data: [%f]", msg.data); // 接收到紧急温度数据
        } else {
          RCLCPP_INFO(this->get_logger(), "I receive a temperature data: [%f]", msg.data); // 接收到普通温度数据
        }
      };


    // 初始化一个带有内容过滤器的订阅,以接收小于-30或大于100的紧急温度数据。
    rclcpp::SubscriptionOptions sub_options;
    sub_options.content_filter_options.filter_expression = "data < %0 OR data > %1"; // 设置过滤表达式
    sub_options.content_filter_options.expression_parameters = {
      std::to_string(EMERGENCY_TEMPERATURE[0]), // 设置表达式参数
      std::to_string(EMERGENCY_TEMPERATURE[1])
    };


    sub_ = create_subscription<std_msgs::msg::Float32>("temperature", 10, callback, sub_options); // 创建订阅


    if (!sub_->is_cft_enabled()) {
      RCLCPP_WARN(
        this->get_logger(), "Content filter is not enabled since it's not supported"); // 内容过滤器不支持
    } else {
      RCLCPP_INFO(
        this->get_logger(),
        "subscribed to topic \"%s\" with content filter options \"%s, {%s}\"",
        sub_->get_topic_name(),
        sub_options.content_filter_options.filter_expression.c_str(),
        rcpputils::join(sub_options.content_filter_options.expression_parameters, ", ").c_str()); // 订阅成功
    }
  }


private:
  rclcpp::Subscription<std_msgs::msg::Float32>::SharedPtr sub_; // 定义一个Float32消息的订阅者
};


}  // namespace demo_nodes_cpp


RCLCPP_COMPONENTS_REGISTER_NODE(demo_nodes_cpp::ContentFilteringSubscriber) // 注册节点

要启用内容过滤,应用程序可以在 SubscriptionOptions 中设置过滤表达式和表达式参数。应用程序还可以检查订阅是否启用了内容过滤

在此演示中, ContentFilteringSubscriber 节点创建了一个内容过滤订阅,只有当温度值小于 -30.0 或大于 100.0 时才会接收消息

如前所述,内容过滤订阅支持取决于 RMW 实现。应用程序可以使用 is_cft_enabled 方法检查订阅上是否实际启用了内容过滤。

要测试内容过滤订阅,让我们运行它:

ac05c88027eddc96b01121e73917fba1.png

您应该看到一条消息,显示所使用的内容过滤选项和每条收到的消息的日志,只有当温度值小于-30.0 或大于 100.0 时才会显示。

如果 RMW 实现不支持内容过滤,订阅仍将创建,但不启用内容过滤。我们可以通过执行 RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ros2 run demo_nodes_cpp content_filtering_publisher 来尝试。

$ RMW_IMPLEMENTATION=rmw_cyclonedds_cpp ros2 run demo_nodes_cpp content_filtering_subscriber
[WARN] [1651096637.893842072] [content_filtering_subscriber]: Content filter is not enabled since it is not supported
[INFO] [1651096641.246043703] [content_filtering_subscriber]: I receive an emergency temperature data: [-100.000000]
[INFO] [1651096642.245833527] [content_filtering_subscriber]: I receive an emergency temperature data: [-90.000000]
[INFO] [1651096643.245743471] [content_filtering_subscriber]: I receive an emergency temperature data: [-80.000000]
[INFO] [1651096644.245833932] [content_filtering_subscriber]: I receive an emergency temperature data: [-70.000000]
[INFO] [1651096645.245916679] [content_filtering_subscriber]: I receive an emergency temperature data: [-60.000000]
[INFO] [1651096646.245861895] [content_filtering_subscriber]: I receive an emergency temperature data: [-50.000000]
[INFO] [1651096647.245946352] [content_filtering_subscriber]: I receive an emergency temperature data: [-40.000000]
[INFO] [1651096648.245934569] [content_filtering_subscriber]: I receive a temperature data: [-30.000000]
[INFO] [1651096649.245877906] [content_filtering_subscriber]: I receive a temperature data: [-20.000000]
[INFO] [1651096650.245939068] [content_filtering_subscriber]: I receive a temperature data: [-10.000000]
[INFO] [1651096651.245911450] [content_filtering_subscriber]: I receive a temperature data: [0.000000]
[INFO] [1651096652.245879830] [content_filtering_subscriber]: I receive a temperature data: [10.000000]
[INFO] [1651096653.245858329] [content_filtering_subscriber]: I receive a temperature data: [20.000000]
[INFO] [1651096654.245916370] [content_filtering_subscriber]: I receive a temperature data: [30.000000]
[INFO] [1651096655.245933741] [content_filtering_subscriber]: I receive a temperature data: [40.000000]
[INFO] [1651096656.245833975] [content_filtering_subscriber]: I receive a temperature data: [50.000000]
[INFO] [1651096657.245971483] [content_filtering_subscriber]: I receive a temperature data: [60.000000]

您可以看到消息 Content filter is not enabled ,因为底层 RMW 实现不支持该功能,但演示仍然成功创建了正常订阅以接收所有温度数据。

 相关内容 

  • 内容过滤示例 https://github.com/ros2/examples/blob/jazzy/rclcpp/topics/minimal_subscriber/content_filtering.cpp ,涵盖内容过滤订阅的所有接口。

  • 内容过滤设计PR  https://github.com/ros2/design/pull/282

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS2中,std::make_unique函数用于创建一个唯一指针。它是C++14中引入的一个函数模板,用于在堆上创建一个对象,并返回一个指向该对象的唯一指针。在ROS2中,可以使用std::make_unique函数来创建ROS2节点。例如,可以使用以下代码创建一个继承自rclcpp::Node的类的实例: ```cpp auto node = std::make_unique<MinimalPublisher>(); ``` 这将创建一个MinimalPublisher类的实例,并返回一个指向该实例的唯一指针。然后,可以使用该指针来访问该节点的成员函数和变量。 引用: \[1\] 截至目前,galactic虽然对以上过程进行了一定程度的封装,但封装也极为简化,同时read的流程仍没有封装,使用还是很麻烦。节点的建立ROS2使用了完全的面向对象设计,以C++为例,大量的ROS API都封装在了rclcpp::Node这个类中,也就意味着需要使用这些API,你必须定义一个继承自Node的类,这也就强制用户必须使用类的方式构建整个系统,官方的一个简单的例子如下:class MinimalPublisher : public rclcpp::Node { public: MinimalPublisher() : Node("minimal_publisher"), count_(0) { publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10); timer_ = this->create_wall_timer( 500ms, std::bind(&MinimalPublisher::timer_callback, this)); } private: rclcpp::TimerBase::SharedPtr timer_; rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; size_t count_; } \[2\] 只要在同一ID的机器就可以接受到彼此的消息。yaml配置文件比较坑的一个细节,ROS2在yaml使用上有两个变化,如下:test_node: ros__parameters: common: topic: "your_topic" \[3\] 主要是下面三个部分:名称重定向日志配置参数使用命令行传参在eloquent之后需要加 --ros-args标志,dashing版本不需要eloquent:ros2 run my_package node_executable --ros-args ... #### 引用[.reference_title] - *1* *2* [ROS2实践总结](https://blog.csdn.net/liu3612162/article/details/121906612)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [[ros2学习]-学习ROS 2工具集](https://blog.csdn.net/weixin_36628778/article/details/106375420)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值