节点是通过ROS Graph进行通信的可执行进程。在本文中,节点将通过话题以字符串消息的形式相互传递信息。这里使用的例子是一个简单的"talker"和“listener”系统,一个节点发布数据,另一个节点订阅话题,以便接收该数据。

这些示例中使用的代码可以在这里找到。

1.创建一个功能

上一节的基础上,即拥有dev_ws功能包的前提下,执行以下命令:

cd dev_ws/src
ros2 pkg create --build-type ament_cmake cpp_pubsub

终端将返回一条消息,验证cpp_pubsub包及其所有必要的文件和文件夹的创建。

导航到dev_ws/src/cpp_pubsub/src中,这就是包含可执行文件的源文件所在的目录。

2.编写一个发布节点

首先,通过以下命令下载示例talker代码(在src目录下):

wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/foxy/rclcpp/topics/minimal_publisher/member_function.cpp

打开刚刚下载的文件publisher_member_function.cpp:

#include <chrono>
#include <memory> #include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp" using namespace std::chrono_literals; /* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */ 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:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}
rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;
}; int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}

2.1审阅代码

代码的顶部为程序所需要的C++头文件,在头文件之后是rclcpp/rclcpp.hpp,它包含了ROS2系统中最常见的部分,最后一是std_msgs/msg/string.hpp,它包含了用于发布数据的内置消息类型。

这些代码行表示了节点的依赖关系:

#include <chrono>
#include <memory> #include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp" using namespace std::chrono_literals;

下一行通过继承rclcpp:: node来创建节点类MinimalPublisher。代码中的每个this都指向该节点。

class MinimalPublisher : public rclcpp::Node

公共构造函数将节点命名为minimal_publisher,并将coun_初始化为0。在构造函数内部,使用String消息类型、话题名称topic,并且限制消息队列的大小来初始化发布者。接下来,初始化timer_,这将导致timer_callback函数每秒执行两次。

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));
}

timer_callback函数是设置消息数据和实际发布消息的地方。RCLCPP_INFO宏确保将每个发布的消息打印到控制台:

private:
void timer_callback()
{
auto message = std_msgs::msg::String();
message.data = "Hello, world! " + std::to_string(count_++);
RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
publisher_->publish(message);
}

最后是定时器(timer)、发布器(publisher)和计数器(counter)字段的声明:

rclcpp::TimerBase::SharedPtr timer_;
rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
size_t count_;

MinimalPublisher类之后是main,其中节点实际执行的位置。rclcpp::init初始化ROS 2, rclcpp::spin开始处理节点的数据,包括定时器的回调。

int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalPublisher>());
rclcpp::shutdown();
return 0;
}

2.2添加依赖

返回到dev_ws/src/cpp_pubsub目录,其中已经包含CMakeLists.txtpackage.xml文件。

打开package.xml文件,确保<description>, <maintainer> and <license> 这些标签填写完毕:

<description>Examples of minimal publisher/subscriber using rclcpp</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>

ament_cmake构建工具依赖项后添加一行,并粘贴以下依赖项对应于节点的include语句:

<depend>rclcpp</depend>
<depend>std_msgs</depend>

这表明了程序执行是需要依赖rclcppstd_msgs

不要忘记保存文件。

2.3CMakeLists.txt

打开CMakeLists.txt文件,在现有的依赖项find_package(ament_cmake REQUIRED)下面,添加以下行:

find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

然后,添加可执行文件并将其命名为talker,这样就可以使用ros2 run节点了:

add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs)

最后,添加install(TARGETS…)部分,让ros2 run可以找到可执行文件:

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

删除一些不必要的注释来清理CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
project(cpp_pubsub) # Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif() find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED) add_executable(talker src/publisher_member_function.cpp)
ament_target_dependencies(talker rclcpp std_msgs) install(TARGETS
talker
DESTINATION lib/${PROJECT_NAME}) ament_package()

现在可以编译包,生成本地设置文件并运行它(不要忘记设置环境变量,否则提示包不存在):

ros2 run cpp_pubsub talker

3.编写订阅者节点

回到dev_ws/src/cpp_pubsub/src来创建订阅者节点,在终端输入:

wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/foxy/rclcpp/topics/minimal_subscriber/member_function.cpp

在终端输入ls,此时返回:

publisher_member_function.cpp subscriber_member_function.cpp

打开subscriber_member_function.cpp文件:

#include <memory>

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using std::placeholders::_1; class MinimalSubscriber : public rclcpp::Node
{
public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
} private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
}; int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalSubscriber>());
rclcpp::shutdown();
return 0;
}

3.1审阅代码

订阅者节点的代码与发布者的代码几乎相同。现在该节点被命名为minimal_subscriber,构造函数使用该节点的create_subscription类来执行回调。

订阅者没有计时器,因为订阅者只是在数据被发布到topic时进行响应。

public:
MinimalSubscriber()
: Node("minimal_subscriber")
{
subscription_ = this->create_subscription<std_msgs::msg::String>(
"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));
}

发布者和订阅者使用的话题名称和消息类型必须匹配,才能进行通信。

topic_callback函数接收通过话题发布的字符串消息数据,并使用RCLCPP_INFO宏将其写入控制台。

该类中唯一的字段声明是subscription_。

private:
void topic_callback(const std_msgs::msg::String::SharedPtr msg) const
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());
}
rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;

main函数完全相同,只是现在它启动MinimalSubscriber节点。对于发布者节点,spin意味着启动计时器,但对于订阅者,它仅仅意味着准备在消息到来时接收它们。

由于此节点与发布者节点具有相同的依赖关系,因此无需向package.xml添加任何新内容。

3.2CMakeLists.txt

重新打开CMakeLists.txt,并在发布者条目下面添加订阅者节点的可执行文件:

add_executable(listener src/subscriber_member_function.cpp)
ament_target_dependencies(listener rclcpp std_msgs) install(TARGETS
talker
listener
DESTINATION lib/${PROJECT_NAME})

保存文件。

4.编译和运行

可以使用如下命令解决依赖问题:

rosdep install -i --from-path src --rosdistro foxy -y

接下来在工作空间中编译新包:

colcon build --packages-select cpp_pubsub

打开一个新终端,source设置文件:

. install/setup.bash

现在运行tlaker节点:

ros2 run cpp_pubsub talker

终端应该每0.5秒发布一次信息消息,像这样:

打开另一个终端,再次从dev_ws设置setup文件,然后启动listener节点:

ros2 run cpp_pubsub listener

listener将开始向控制台打印消息,从当时打开的消息计数开始,如下所示:

在每个终端中输入Ctrl+C,停止节点运行。

5.总结

本文创建了两个节点来发布和订阅话题上的数据。在编译和运行它们之前,需要将它们的依赖项和可执行文件添加到包配置文件中。

如果给您带来帮助,希望能给点个关注,以后还会陆续更新有关机器人的内容,点个关注不迷路~欢迎大家一起交流学习。

都看到这了,点个推荐再走吧~

未经允许,禁止转载。

ROS2学习之旅(14)——编写简单的发布者和订阅者(C++)的更多相关文章

  1. ROS2学习之旅(4)——理解ROS2 Graph中的节点

    ROS(2)图(ROS(2) graph)是一个同时处理数据的基于ROS2元素的网络,它包含了所有的可执行文件以及它们之间的连接.图中的基本元素包括:节点(nodes).话题(topics).服务(s ...

  2. ROS2学习之旅(21)——创建一个动作服务和客户节点(C++)

    动作是ROS中的一种异步通信形式,动作客户端向动作服务器发送目标请求,目标服务器向操作客户端发送目标反馈和结果.本文基于前一篇自定义动作博文. 1.创建一个action_turtorials_cpp包 ...

  3. ROS2学习之旅(15)——编写简单的服务和客户节点(C++)

    当节点使用服务进行通信时,发送数据请求的节点称为客户节点,响应请求的节点称为服务节点.请求和响应的结构由.srv文件决定. 本文的例子是一个简单的整数加法系统:一个节点请求两个整数的和,另一个节点响应 ...

  4. ROS2学习之旅(13)——创建ROS2 功能包

    一个功能包可以被认为是ROS2代码的容器.如果希望能够管理代码或与他人共享代码,那么需要将其组织在一个包中.通过包,可以发布ROS2工作,并允许其他人轻松地构建和使用它. 在ROS2中,创建功能包使用 ...

  5. ROS2学习之旅(2)——配置ROS2环境

    目录 1.source一下setup文件 2.自动source 3.自动进入工作区(不常用) 4.检查环境变量是否设置成功 5.总结 ROS2依赖于使用shell(终端)环境组合工作空间的概念.工作空 ...

  6. ROS2学习之旅(12)——创建工作空间

    workspace(工作空间)是包含ROS2 packages(包)的文件夹.在使用ROS 2之前,有必要在终端中source一下ROS 2的安装工作区,这样就可以在该终端中使用ROS 2的软件包. ...

  7. ROS学习(十三)—— 编写简单的Service和Client (C++)

    一.编写Service节点 1.节点功能: 我们将创建一个简单的service节点("add_two_ints_server"),该节点将接收到两个整形数字,并返回它们的和. 2. ...

  8. ROS2学习之旅(1)——初识ROS2

    本系列用来记录ROS2的学习过程,有错误或者不合理的地方请大家指正.由于博主具有ROS1的学习经历,会添加一些与ROS1的一些对比,当然这对于ROS2本身的学习内容没有丝毫影响,欢迎大家积极与我在评论 ...

  9. ROS2学习之旅(20)——创建一个动作消息

    本文用来自定义一个动作消息类型. 以下命令用来创建一个工作空间并建立一个功能包: mkdir -p action_ws/src cd action_ws/src ros2 pkg create act ...

随机推荐

  1. Redis SWAPDB 命令背后做了什么

    Redis SWAPDB 命令背后做了什么 目录 Redis SWAPDB 命令背后做了什么 0x00 摘要 0x01 SWAPDB 基础 1.1 命令说明 1.2 演示 0x02 预先校验 0x03 ...

  2. Linux C 文件IO

    文件IO 2021-05-31 12:46:14 星期一 目录 文件IO 基础IO open 错误 creat read 一个例子 write close lseek 文件空洞 unlink删除 io ...

  3. C# Asp.Net 实现PPT/PDF转成图片(不依赖office)

    最近公司有个需求,将PPT课件转成图片列表,然后在前端展示成轮播图,于是一开始通过Microsoft.Office.Interop.PowerPoint包实现了这个需求具体代码如下: /// < ...

  4. 达梦数据库产品支持技术学习分享_Week2

    本周主要从以下几个方面进行本人对达梦数据库学习的分享,学习进度和学习情况因人而异,仅供参考. 一.文本命令行工具使用的方法(Disql和dmfldr) 二.数据库备份 三.定时作业功能 四.系统表和动 ...

  5. 特斯拉ADAS

    特斯拉ADAS Tesla 目前在Model S和Model X上面采用的自动辅助驾驶系统集成了12个超声波传感器,用来识别周围环境: 一个前置摄像头,用来辨识前方物体: 一个前置雷达,用来辨识前方物 ...

  6. NVIDIA GPUs上深度学习推荐模型的优化

    NVIDIA GPUs上深度学习推荐模型的优化 Optimizing the Deep Learning Recommendation Model on NVIDIA GPUs 推荐系统帮助人在成倍增 ...

  7. Spring Cloud07: Feign 声明式接口调用

    一.什么是Feign Feign也是去实现负载均衡,但是它的使用要比Ribbon更加简化,它实际上是基于Ribbon进行了封装,让我们可以通过调用接口的方式实现负载均衡.Feign和Ribbon都是由 ...

  8. Centos7 安装 Cacti 1.2

    1. 安装web server(略) 注意系统时间/时区 ntp server是否定时同步 2. 安装 mysql 5.7 (略) 安装成功后 2.1 编辑 my.ini 文件在 [mysqld] 下 ...

  9. 再看 Java 中的单例

    此前面试遇到了单例问题,本以为已经背的滚瓜烂熟,没想到被问单例如何避免被反射和序列化破坏,虽然后来还是等到了通知,但还是复习一下单例的实现方式,并学习防止反射和序列化破坏的手段. 基本实现方式 其他相 ...

  10. 2、java基础语法(上):变量与运算符

    关键字与保留字 关键字 定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词) 特点:关键字中所有字母都为小写 官方地址:https://docs.oracle.com/javase/tut ...