【ROS2笔记四】ROS2话题通信

news2024/11/24 1:24:19

4.ROS2话题通信

文章目录

  • 4.ROS2话题通信
    • 4.1订阅发布模型
    • 4.2ROS2话题工具
    • 4.3rclcpp实现话题
      • 4.3.1编写发布者
      • 4.4编写订阅者
    • Reference

话题是ROS2中常用的通信方式之一,话题通信采取的是订阅发布模型,一个节点的数据会发布到某个话题之上,然后另一个节点可以订阅这个话题,这个节点就可以通过这个话题来拿到发布节点发布的数据了。

4.1订阅发布模型

订阅发布模型可以是:

  1. 一对一的
  2. 一对多的
  3. 多对一的
  4. 多对多的
  5. 节点可以订阅本身发布的话题内容

4.2ROS2话题工具

运行ROS2自带的demo我们可以简单地使用rqt_graph来查看topic的传递路径

ros2 run demo_nodes_cpp listener
ros2 run demo_nodes_cpp talker
rqt_graph

就可以看到下面的结果

Image

除了rqt_graph之外,ROS2还有一些CLI(Command Line Interface)命令行工具可以使用:

ros2 topic -h

可以查看所有有关topic的CLI的使用方法,这里不再赘述。

4.3rclcpp实现话题

4.3.1编写发布者

(1)创建新的节点

首先,创建一个新的工作空间colcon_test03_ws和新的功能包example_topic_rclcpp(这里旨在回顾工作空间和功能包的创建方法,你也可以在现有的功能包中直接创建脚本文件),然后新建脚本文件topic_publisher_01.cpp

mkdir -p ./colcon_test03_ws/src
cd ./colcon_test03_ws/src
ros2 pkg create example_topic_rclcpp --build-type ament_cmake --license Apache-2.0 --dependencies rclcpp
touch ./example_topic_rclcpp/src/topic_publisher_01.cpp

使用tree命令来查看目录结构(如果没有该命令则需提前安装sudo apt install tree),如下:

.
└── example_topic_rclcpp
    ├── CMakeLists.txt
    ├── include
    │   └── example_topic_rclcpp
    ├── LICENSE
    ├── package.xml
    └── src
        └── topic_publisher_01.cpp

4 directories, 4 files

现在我们需要在这个脚本文件中写入内容了,推荐使用vscode作为编辑器。我们使用面向对象的方式来编写一个最简单的节点,

#include "rclcpp/rclcpp.hpp"
#include <string>

class TopicPublisher01: public rclcpp::Node{
public:
    // 构造函数
    TopicPublisher01(std::string name) : Node(name){
        RCLCPP_INFO(this->get_logger(), "%s node has been launched", name.c_str());
    }
private:

};

int main(int argc, char **argv){
    rclcpp::init(argc, argv);
    auto node = std::make_shared<TopicPublisher01>("topic_publisher_01");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

然后修改CMakeListst.txt,添加依赖

add_executable(topic_publisher_01 src/topic_publisher_01.cpp)
ament_target_dependencies(topic_publisher_01 rclcpp)
install(TARGETS
  topic_publisher_01
  DESTINATION lib/${PROJECT_NAME}
)

(2)导入消息接口

消息接口是ROS2通信时必须的一部分,通过消息接口ROS2才能够完成消息的序列化和反序列化。ROS2为我们定义好了常用的消息接口,并生成了C++和Python的依赖文件,我们可以直接在程序中进行导入。

ament_cmake类型功能包导入消息接口分为三部分:

  1. CMakeLists.txt中导入,具体是先find_packagesament_target_dependencies
  2. packages.xml中导入,具体是添加depend标签并将消息接口写入
  3. 在代码中导入,C++中是#inlcude "消息功能包/xxx/xxx.hpp"

这三步修改的文件内容如下:

CMakeLists.txt

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

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

packages.xml

<buildtool_depend>ament_cmake</buildtool_depend>

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

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

如果不想手动在CMakeLists.txtpackages.xml中添加包的依赖,我们可以在创建功能包的时候就添加上--dependencies std_msgs的依赖,这样就会为我们自动添加了。如下:

ros2 pkg create example_topic_rclcpp01 --build-type ament_cmake --license Apache-2.0 --dependencies rclcpp std_msgs

代码文件topic_publisher_01.cpp,包含消息头文件

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class TopicPublisher01 : public rclcpp::Node

(3)创建发布者Publisher

相关API文档可以参考:https://docs.ros2.org/latest/api/rclcpp/

Image

我们需要指定的参数有:

  • 话题名称(topic_name),我们设置为control_command即可
  • QosQos指定一个数字,这个数字对应的是KeepLast消息队列的长度。
  • 还需要指定函数原型templateMessageT的消息类型(即示例中的MsgT),我们直接设置成std_msgs::msg::String即可。

编写如下的发布者代码

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class TopicPublisher01: public rclcpp::Node{
public:
    // 构造函数
    TopicPublisher01(std::string name) : Node(name){
        RCLCPP_INFO(this->get_logger(), "%s node has been launched", name.c_str());
        // 创建发布者
        command_publisher_ = this->create_publisher<std_msgs::msg::String>('control_command', 10);
    }
private:
    // 声明话题发布者,在类的私有成员中再次声明发布者对象是为了在整个类中都能够访问和使用该对象。
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr command_publisher_;
};

int main(int argc, char **argv){
    rclcpp::init(argc, argv);
    auto node = std::make_shared<TopicPublisher01>("topic_publisher_01");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

(4)创建定时器Timer

相关API文档可以参考:https://docs.ros2.org/latest/api/rclcpp/

Image
  • period,回调函数调用的周期
  • callback,回调函数
  • group,调用回调函数所在的回调组,默认为nullptr

编写如下的Timer代码

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class TopicPublisher01: public rclcpp::Node{
public:
    // 构造函数
    TopicPublisher01(std::string name) : Node(name){
        RCLCPP_INFO(this->get_logger(), "%s node has been launched", name.c_str());
        // 创建发布者
        command_publisher_ = this->create_publisher<std_msgs::msg::String>('control_command', 10);
        // 创建定时器,500ms周期
        timer_ = this->create_wall_timer(std::chrono::milliseconds(500), std::bind(&TopicPublisher01::timer_callback, this));
    }
private:
    // 声明话题发布者,在类的私有成员中再次声明发布者对象是为了在整个类中都能够访问和使用该对象。
    rclcpp::Publisher<std_msgs::msg::String>::SharedPtr command_publisher_;
    // 声明定时器
    rclcpp::TimerBase::SharedPtr timer_;

    void timer_callback(){
        // 创建消息
        std_msgs::msg::String message;
        message.data = "forward";
        // 打印日志
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
        // 发布消息
        this->command_publisher_->publish(message);
    }
};

int main(int argc, char **argv){
    rclcpp::init(argc, argv);
    auto node = std::make_shared<TopicPublisher01>("topic_publisher_01");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

测试一下是否正确

colcon build --packages-select example_topic_rclcpp
source ./intsall/setup.bash
ros2 run example_topic_rclcpp topic_publisher_01 

使用CLI查看消息内容

ros2 topic list
ros2 topic echo /control_command

4.4编写订阅者

(1)创建订阅节点

cd ./colcon_test03_ws/src
touch ./example_topic_rclcpp/src/topic_subscriber_01.cpp

编写以下内容:

#include "rclcpp/rclcpp.hpp"

class TopicSubscriber01: public rclcpp::Node{
public:
    TopicSubscriber01(std::string name): Node(name){
        RCLCPP_INFO(this->get_logger(), "%s has been launched", name.c_str());
    }

private:
};

int main(int argc, char **argv){
    rclcpp::init(argc, argv);
    auto node = std::make_shared<TopicSubscriber01>("topic_subscriber_01");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

修改CMakeLists.txt文件

add_executable(topic_subscriber_01 src/topic_subscriber_01.cpp)
ament_target_dependencies(topic_subscriber_01 rclcpp std_msgs)

install(TARGETS
  topic_publisher_01
  topic_subscriber_01
  DESTINATION lib/${PROJECT_NAME}
)

然后可以测试运行一下

colcon build --packages-select example_topic_rclcpp
source install/setup.bash
ros2 run example_topic_rclcpp topic_subscriber_01

(2)创建订阅者Subscriber

相关API文档可以参考:https://docs.ros2.org/latest/api/rclcpp/

Image

我们只关心前面三个参数,后面两个参数可以选择默认的参数

  • topic_name,指定订阅的话题名称
  • qos,指定消息队列长度
  • callback,指定订阅后消息处理的回调函数

编写以下代码:

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

class TopicSubscriber01: public rclcpp::Node{
public:
    TopicSubscriber01(std::string name): Node(name){
        RCLCPP_INFO(this->get_logger(), "%s has been launched", name.c_str());
        // 创建一个订阅者
        command_subscriber_ = this->create_subscription<std_msgs::msg::String>("control_command", 10, std::bind(&TopicSubscriber01::command_callback, this, std::placeholders::_1));
    }

private:
    // 声明一个订阅者
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr command_subscriber_;
    // 消息处理的回调函数
    void command_callback(const std_msgs::msg::String::SharedPtr msg){
        double speed = 0.0f;
        if (msg->data == "forward"){
            speed = 0.2f;
        }
        RCLCPP_INFO(this->get_logger(), "Receive instruction [%s], send speed [%f]", msg->data.c_str(), speed);
    }

};

int main(int argc, char **argv){
    rclcpp::init(argc, argv);
    auto node = std::make_shared<TopicSubscriber01>("topic_subscriber_01");
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

构建然后运行

colcon build --packages-select example_topic_rclcpp
source install/setup.bash
ros2 run example_topic_rclcpp topic_publisher_01
ros2 run example_topic_rclcpp topic_subscriber_01

运行后的输出效果类似如下:

[INFO] [1712900021.099703358] [topic_publisher_01]: Publishing: 'forward'
[INFO] [1712900021.599713885] [topic_publisher_01]: Publishing: 'forward'
[INFO] [1712900022.099719858] [topic_publisher_01]: Publishing: 'forward'
[INFO] [1712900022.599664745] [topic_publisher_01]: Publishing: 'forward'


[INFO] [1712900027.600149013] [topic_subscriber_01]: Receive instruction [forward], send speed [0.200000]
[INFO] [1712900028.100186298] [topic_subscriber_01]: Receive instruction [forward], send speed [0.200000]
[INFO] [1712900028.600202891] [topic_subscriber_01]: Receive instruction [forward], send speed [0.200000]

Reference

d2lros2
ROS2 Tutorial

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1588554.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#基础--之数据类型

C#基础–之数据类型 在第一章我们了解了C#的输入、输出语句后&#xff0c;我这一节主要是介绍C#的基础知识&#xff0c;本节的内容也是后续章节的基础&#xff0c;好的开端等于成功的一半。在你阅读完本章后&#xff0c;你就有足够的C#知识编写简单的程序了。但还不能使用封装、…

一些优雅的算法(c++)

求最大公约数&#xff1a;辗转相除法 int gcd(int a,int b){return b0?a:gcd(b,a%b); }求最小公倍数&#xff1a;两整数之积除以最大公约数 int lcm(int a, int b){return a*b / gcd(a, b); }十进制转n进制&#xff1a; char get(int x){if(x<9){return x0;}else{return…

java 邮件发送表格

邮件发送表格 问题导入效果图 实现方案1. 拼接HTML文件&#xff08;不推荐&#xff09;2. excel 转HTML使用工具类来转化依赖工具类代码示例 使用已工具包 如 aspose-cells依赖代码示例 3.使用模板生成流程准备模板工具类代码示例 问题导入 在一些定时任务中&#xff0c;经常会…

SpringBoot 集成Swagger3

说明&#xff1a; 1&#xff09;、本文使用Spring2 集成Swagger3&#xff0c; 本想使用Springboot3 jdk 17 集成Swagger3, 但是搜了一些资料&#xff0c;Spring 想引用swagger3 需要依赖降级使用Spring2 版本&#xff0c; 或者使用Spring3 springdoc 实现swagger的功能&…

数据结构—顺序表(如果想知道顺序表的全部基础知识点,那么只看这一篇就足够了!)

前言&#xff1a;学习完了C语言的基础知识点之后&#xff0c;我们就需要使用我们所学的知识来进一步对存储在内存中的数据进行操作&#xff0c;这时候我们就需要学习数据结构。而这篇文章为数据结构中顺序表的讲解。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以…

JavaEE初阶——多线程(一)

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程的第一部分:引入线程以及创建多线程的几种方式 此文章是建立在前一篇文章进程的基础上的 如果有不足的或者错误的请您指出! 1.认识线程 我们知道现代的cpu大多都是多核心…

Flutter学习13 - Widget

1、Flutter中常用 Widget 2、StatelessWidget 和 StateFulWidget Flutter 中的 widget 有很多&#xff0c;但主要分两种&#xff1a; StatelessWidget无状态的 widget如果一个 widget 是最终的或不可变的&#xff0c;那么它就是无状态的StatefulWidget有状态的 widget如果一个…

SpringCloud Alibaba Sentinel 简介和安装

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十三篇&#xff0c;即介绍 SpringCloud Alibaba Sentinel 简介和安装。 二、Sentinel 简介 2.1 Sent…

2024Mathorcup(妈妈杯)数学建模C题python代码+数据教学

2024Mathorcup数学建模挑战赛&#xff08;妈妈杯&#xff09;C题保姆级分析完整思路代码数据教学 C题题目&#xff1a;物流网络分拣中心货量预测及人员排班 因为一些不可抗力&#xff0c;下面仅展示部分代码&#xff08;很少部分部分&#xff09;和部分分析过程&#xff0c;其…

(Java)数据结构——图(第七节)Folyd实现多源最短路径

前言 本博客是博主用于复习数据结构以及算法的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 Folyd实现原理 中心点的概念 感觉像是充当一个桥梁的作用 还是这个图 我们常在一些讲解视频中看到&#xff0c;就比如dist&#xff08;-1&#xff09;&#xff0…

bayes_opt引用失败,解决方案

bayes_opt引用失败&#xff0c;如图&#xff1a; 1.pip install bayesian-optimization报错&#xff0c;如图&#xff1a; 2.【解决方案】pip install -i https://pypi.tuna.tsinghua.edu.cn/simple bayesian-optimization

【opencv】示例-detect_blob.cpp

// 导入所需的OpenCV头文件 #include <opencv2/core.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <opencv2/features2d.hpp> // 导入向量和映射容器 #include <vector> #include <map> // 导入输入输出…

一文读懂传统服务器与云服务器的区别

传统服务器 传统服务器一般指的是物理服务器。物理服务器是独立存在的&#xff0c;无需与其他用户共享资源&#xff0c;拥有完全管理员权限和独立IP地址&#xff0c;安全稳定性高&#xff0c;性能优越。物理服务器与通用的计算机架构类似&#xff0c;由CPU、主板、内存条、硬…

区块链安全-----区块链基础

区块链是一种全新的信息网络架构 &#xff0c;是新一代信息基础设施 &#xff0c;是新型的价值交换方式、 分布式协 同生产机制以及新型的算法经济模式的基础。 区块链技术可以集成到多个领域。 区块链的主要用途 是作为加密货币的分布式总帐。 它在银行 &#xff0c;金融 &…

oracle数据库怎么查看当前登录的用户?

方法如下&#xff1a; 输入select * from dba_users; 即可。 常用语句&#xff1a; 一&#xff0c;查看数据库里面所有用户&#xff1a; select * from dba_users; 前提是你是有dba权限的帐号&#xff0c;如sys,system。 二&#xff0c;查看你能管理的所有用户&#xff1…

react17中配置webpack:使用@代表src目录

在vue的项目中可以使用表示src目录&#xff0c;使用该符号表示绝对路径&#xff0c;那么在react中想要使用怎么办呢&#xff1f; 在react中使用表示src目录是需要在webpack中配置的&#xff0c;在核心模块node_modules-》react-scripts-》config-》webpack.config.js中搜索找到…

python——列表(list)

概念 列表一般使用在一次性存储多个数据 语法 lst[数据1&#xff0c;数据2&#xff0c;.....]方法 #mermaid-svg-flVxgVdpSqFaZyrF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-flVxgVdpSqFaZyrF .error-icon{…

【2024最新博客美化教程重置版】一分钟教会你在博客页面中加入javascript点击出弹出文字效果!

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 我们可以在博客…

利用正射影像对斜射图像进行反向投影

在图像投影和映射领域,有两种类型的投影:正向投影和反向投影。正向投影涉及使用内部方向(即校准相机参数)将 3D 点(例如地面上的物体)投影到 2D 图像平面上。另一方面,向后投影是指根据 2D 图像确定地面物体的 3D 坐标的过程。 为了匹配倾斜图像和正射影像并确定相机位置…

[C++][算法基础]有向图拓扑排序(拓扑)

给定一个 n 个点 m 条边的有向图&#xff0c;点的编号是 1 到 n&#xff0c;图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列&#xff0c;如果拓扑序列不存在&#xff0c;则输出 −1。 若一个由图中所有点构成的序列 A 满足&#xff1a;对于图中的每条边 (x,y)&a…