【ROS】话题通信--从理论介绍到模型实现(C++)
1.简单介绍
话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。像雷达、摄像头、GPS… 等等一些传感器数据的采集,也都是使用了话题通信,换言之,话题通信适用于不断更新的、少逻辑处理的数据传输场景。
2.理论模型
该模型中涉及到三个角色:
ROS Master (管理者)
Talker (发布者)
Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。

整个流程由以下步骤实现(引用自Autolabor-ROS):
0.Talker注册
Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。
1.Listener注册
Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。
2.ROS Master实现信息匹配
ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。
3.Listener向Talker发送请求
Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。
4.Talker确认请求
Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。
5.Listener与Talker件里连接
Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。
6.Talker向Listener发送消息
连接建立后,Talker 开始向 Listener 发布消息。
注意1:上述实现流程中,前五步使用的 RPC协议,最后两步使用的是 TCP 协议
注意2: Talker 与 Listener 的启动无先后顺序要求
注意3: Talker 与 Listener 都可以有多个
注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。即便关闭ROS Master,Talker 与 Listern 照常通信。
3.模型实现(C++)
首先创建工作空间,并shift+ctrl+B配置好编译文件,然后创建功能包plumbing_pub_sub并添加依赖,然后创建cpp源文件
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,所以大体流程如下:
1.编写发布方实现;
2.编写订阅方实现;
3.编辑配置文件;
4.编译并执行。
1.发布方实现:demo01_pub.cpp
/*需求: 实现基本的话题通信,一方发布数据,一方接收数据,实现的关键点:1.发送方2.接收方3.数据(此处为普通文本)PS: 二者需要设置相同的话题消息发布方:循环发布信息:HelloWorld 后缀数字编号实现流程:1.包含头文件 2.初始化 ROS 节点:命名(唯一)3.实例化 ROS 句柄4.实例化 发布者 对象5.组织被发布的数据,并编写逻辑发布数据*/
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>int main(int argc, char *argv[])
{ //设置编码,避免中文乱码setlocale(LC_ALL,"");//2.初始化 ROS 节点:命名(唯一)// 参数1和参数2 后期为节点传值会使用// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一ros::init(argc,argv,"talker");//3.实例化 ROS 句柄ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能//4.实例化 发布者 对象//泛型: 发布的消息类型//参数1: 要发布到的话题//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);//5.组织被发布的数据,并编写逻辑发布数据//数据(动态组织)std_msgs::String msg;std::string msg_front = "Hello 你好!"; //消息前缀int count = 0; //消息计数器//逻辑(一秒1次/1HZ)ros::Rate r(1);//节点不死while (ros::ok()){//使用 stringstream 拼接字符串与编号std::stringstream ss;ss << msg_front << count;msg.data = ss.str();//发布消息pub.publish(msg);//加入调试,打印发送的消息ROS_INFO("发送的消息是:%s",msg.data.c_str());//根据前面制定的发送频率自动休眠 休眠时间 = 1/频率;r.sleep();count++;//循环结束前,让 count 自增}return 0;
}
2.订阅方实现:demo02_sub.cpp
/*需求: 实现基本的话题通信,一方发布数据,一方接收数据,实现的关键点:1.发送方2.接收方3.数据(此处为普通文本)消息订阅方:订阅话题并打印接收到的消息实现流程:1.包含头文件 2.初始化 ROS 节点:命名(唯一)3.实例化 ROS 句柄4.实例化 订阅者 对象5.处理订阅的消息(回调函数)6.设置循环调用回调函数*/
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h"//通过msg_p获取并操作订阅到的数据
void doMsg(const std_msgs::String::ConstPtr& msg_p){ROS_INFO("我听见:%s",msg_p->data.c_str());// ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}int main(int argc, char *argv[])
{setlocale(LC_ALL,"");//2.初始化 ROS 节点:命名(唯一)ros::init(argc,argv,"listener");//3.实例化 ROS 句柄ros::NodeHandle nh;//4.实例化 订阅者 对象//5.处理订阅的消息(回调函数)ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);//6.设置循环调用回调函数ros::spin();//循环读取接收的数据,并调用回调函数处理return 0;
}
3.编辑配置文件CMakeList
add_executable(demo01_pubsrc/demo01_pub.cpp
)
add_executable(demo02_subsrc/demo02_sub.cpp
)target_link_libraries(demo01_pub${catkin_LIBRARIES}
)
target_link_libraries(demo02_sub${catkin_LIBRARIES}
)
然后ctrl + shift + B 编译后再执行:编译完成后启动roscore ,再启动发布节点,再启动订阅节点,效果如下。。。

一些注意事项:
补充0:
vscode 中的 main 函数 声明 int main(int argc, char const *argv[]){},默认生成 argv 被 const 修饰,需要去除该修饰符
补充1:
ros/ros.h No such file or directory …
检查 CMakeList.txt find_package 依赖出现重复,删除多出来的包即可

补充2:
订阅时,第一条数据丢失
原因: 发送第一条数据时, publisher 还未在 roscore 注册完毕
解决: 注册后,加入休眠 ros::Duration(3.0).sleep(); 延迟第一条数据的发送
补充3:
可以新开一个终端,输入rqt_graph查看计算图

4.话题通信自定义msg
ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: 激光雷达的信息… std_msgs 由于描述性较差而显得力不从心,这种场景下可以使用自定义的消息类型
msgs只是简单的文本文件,每行具有字段类型和字段名称,可以使用的字段类型有:
int8, int16, int32, int64 (或者无符号类型: uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C]
ROS中还有一种特殊类型:Header,标头包含时间戳和ROS中常用的坐标帧信息。会经常看到msg文件的第一行具有Header标头。
现在要自定义消息,该消息包含人的信息:姓名、身高、年龄等。
1.定义msg文件
功能包下新建 msg 目录,添加文件 Person.msg
string name
uint16 age
float64 height
2.编辑配置文件
package.xml中添加编译依赖与执行依赖
<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend>
一条是编译,一条是运行

CMakeLists.txt编辑 msg 相关配置
find_package(catkin REQUIRED COMPONENTSroscpprospystd_msgsmessage_generation
)
# 需要加入 message_generation,必须有 std_msgs
放开后多添加一个编译依赖

## 配置 msg 源文件
add_message_files(FILESPerson.msg
)
放开后添加自定义msg文件

# 生成消息时依赖于 std_msgs
generate_messages(DEPENDENCIESstd_msgs
)
直接找到并放开

#执行时依赖
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES demo02_talker_listenerCATKIN_DEPENDS roscpp rospy std_msgs message_runtime
# DEPENDS system_lib
)
放开后,多添加一个执行依赖

然后编译生成中间文件:之后只要调用头文件就可以正常使用了。

5.自定义msg的调用(C++)
vscode 配置
为了方便代码提示以及避免误抛异常,需要先配置 vscode,将前面生成的 head 文件路径配置进 c_cpp_properties.json 的 includepath属性:
右击头文件所在的包,在集成终端中打开,并输入pwd
会得到这个头文件的路径


把这个路径复制到这个文件的includePath中,把最后的包名改成 * * 可以包括进所有包,添加的时候,前一条路径最后面的逗号别忘了加

发布方
和之前一样的道理,只不过发送的消息不同
/*需求: 循环发布人的信息*/#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"int main(int argc, char *argv[])
{setlocale(LC_ALL,""); //避免输出乱码//1.初始化 ROS 节点ros::init(argc,argv,"banZhuRen");//2.创建 ROS 句柄ros::NodeHandle nh;//3.创建发布者对象ros::Publisher pub = nh.advertise<plumbing_pub_sub::Person>("chatter_person",1000);//4.组织被发布的消息,编写发布逻辑并发布消息//创建被发布的数据plumbing_pub_sub::Person p;p.name = "孙悟空";p.age = 2000;p.height = 1.45;//发布频率ros::Rate r(1);//循环发布while (ros::ok()){pub.publish(p);p.age += 1;ROS_INFO("我叫:%s,今年%d岁,高%.2f米", p.name.c_str(), p.age, p.height);r.sleep();ros::spinOnce();}return 0;
}
订阅方
/*需求: 订阅人的信息*/#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"void doPerson(const plumbing_pub_sub::Person::ConstPtr& person_p){ROS_INFO("订阅的人信息:%s, %d, %.2f", person_p->name.c_str(), person_p->age, person_p->height);
}int main(int argc, char *argv[])
{ setlocale(LC_ALL,"");//1.初始化 ROS 节点ros::init(argc,argv,"jiaZhang");//2.创建 ROS 句柄ros::NodeHandle nh;//3.创建订阅者对象//4.回调函数中处理 personros::Subscriber sub = nh.subscribe<plumbing_pub_sub::Person>("chatter_person",10,doPerson);//5.转头执行回调函数ros::spin();return 0;
}
配置CMakeList
需要添加 add_dependencies 用以设置所依赖的消息相关的中间文件。
以保证先编译自定义msg文件,再去编译cpp源文件
add_executable(新发布方源文件名 src/源文件名.cpp)
add_executable(新订阅方源文件名 src/源文件名.cpp)add_dependencies(新发布方源文件名 ${PROJECT_NAME}_generate_messages_cpp)
add_dependencies(新订阅方源文件名 ${PROJECT_NAME}_generate_messages_cpp)target_link_libraries(新发布方源文件名${catkin_LIBRARIES}
)
target_link_libraries(新订阅方源文件名${catkin_LIBRARIES}
)


然后编译运行即可。。。。效果和之前差不多,不过这次的消息类型是自定义的。
节点关系图

相关文章:
【ROS】话题通信--从理论介绍到模型实现(C++)
1.简单介绍 话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。像雷达、摄像头、GPS… 等等一些传感器数据的采集,也都是使用了话题通信,换言之…...
服务器数据恢复-EqualLogic存储RAID5数据恢复案例
服务器数据恢复环境: 一台DELL EqualLogic存储中有一组由16块SAS硬盘组建的RAID5阵列。存储存放虚拟机文件,采用VMFS文件系统,划分了4个lun。 服务器故障&检测&分析: 存储设备上有两个硬盘指示灯显示黄色,存储…...
qsort函数详解
大家好,我是苏貝,本篇博客带大家了解qsort函数,如果你觉得我写的不错的话,可以给我一个赞👍吗,感谢❤️ 文章目录 一. qsort函数参数详解1.数组首元素地址base2.数组的元素个数num和元素所占内存空间大小w…...
C#学习,委托,事件,泛型,匿名方法
目录 委托 声明委托 实例化委托 委托的多播 委托的用途 事件 通过事件使用委托 声明事件 泛型 泛型的特性 泛型方法 泛型的委托 匿名方法 编写匿名方法的语法 委托 类似于指针,委托是存有对某个方法的引用的一种引用类型变量,引用可以在运…...
2023最新版本~KEIL5使用C++开发STM32
先看效果 开始教学 因为是第一次写这个配置教程 我会尽量详细些 打开一个Keil工程 移除本地core 添加在线core 第一次编译代码 不会有报错 修改main.c文件类型为C 点击魔术棒 把ARM编译器修改为V6 第二次编译会报错语法不兼容 我把汇编部分的这些代码做了…...
汽车领域专业术语
1. DMS/OMS/RMS/IMS DMS:即Driver Monitoring System,监测对象为Driver(驾驶员)。DMS三大核心: OMS:即Occupancy Monitoring System,监测对象为乘客。 RMS:后排盲区检测系统 IMS&…...
H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包
环境: H3C S6520-26Q-SI version 7.1.070, Release 6326 Win 10 专业版 Wireshake Version 4.0.3 问题描述: H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包 解决方案: 配置交换机本地端口镜像 1.进入系统视图,并创建本地镜像组1 <H3C>system-vie…...
零基础自学:2023 年的今天,请谨慎进入网络安全行业
前言 2023 年的今天,慎重进入网安行业吧,目前来说信息安全方向的就业对于学历的容忍度比软件开发要大得多,还有很多高中被挖过来的大佬。 理由很简单,目前来说,信息安全的圈子人少,985、211 院校很多都才…...
向gitee推送代码
目录 一、Gitee创建仓库 二、将刚刚创建的仓库放到虚拟机上 2.1 https 方式克隆仓库 2.2 ssh的方式克隆仓库 三、本地开发,推送 3.1 查看是否有远程库 3.2 推送代码 3.3 查看是否推送成功 一、Gitee创建仓库 二、将刚刚创建的仓库放到虚拟机上 2.1 https 方式…...
双指针算法实例1(移动零)
常⻅的双指针有两种形式: 1 对撞指针(左右指针): a 对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼 近 b 终止条件一般是两指针相遇or错过(也可能在循…...
C#程序随系统启动例子 - 开源研究系列文章
今天讲讲C#中应用程序随系统启动的例子。 我们知道,应用程序随系统启动,都是直接在操作系统注册表中写入程序的启动参数,这样操作系统在启动的时候就根据启动参数来启动应用程序,而我们要做的就是将程序启动参数写入注册表即可。此…...
最全攻略之人工智能顶会论文发表
最全攻略之人工智能顶会论文发表 1. 人工智能顶会1.1 CCF 顶会列表2023年人工智能顶会时间线 2.人工智能顶会论文发表流程2.1 顶会论文发表流程2.2 顶会论文审稿流程 3.1顶会论文发表指南3.1 顶会论文七要素3.2 顶会论文写作要点 4.人工智能发展趋势4.1 人工智能未来趋势4.2 人…...
Redis基于内存的key-value结构化NOSQL(非关系型)数据库
Redis Redis介绍Redis的优点Redis的缺点Redis的安装Redis的连接Redis的使用Redis中的数据类型String的使用get setsetex(expire)ttlsetnx(not exit)HashList列表(队列)Set集合ZSet集合Redis 通用命令Redis图形客户端Redis在Java中的使用RedisTemplate...
Spring学习笔记+SpringMvc+SpringBoot学习笔记
壹、核心概念: 1.1. IOC和DI IOC(Inversion of Control)控制反转:对象的创建控制权由程序转移到外部,这种思想称为控制反转。/使用对象时,由主动new产生对象转换为由外部提供对象,此过程种对象…...
如何在 3Ds Max 中准确地将参考图像调整为正确的尺寸?
您是否想知道如何在 3Ds Max 中轻松直观地调整参考图像的大小,而无需借助第三方解决方案、插件或脚本? 我问自己这个问题,并高兴地发现了FFD Box 2x2x2,我无法停止钦佩这个修改器的多功能性。 在本文中,我想与您分享一…...
集简云推出的全国第一款 AI+连接器解决方案产品语聚AI
语聚AI是集简云推出的全国第一款 AI连接器解决方案产品,官网:https://yuju.jijyun.cn 语聚AI包括了多个不同的AI功能,协助企业和个人更好的使用AI语言模型所带来的能力,包括: 应用助手 希望通过AI智能助手帮助您查询C…...
git错误记录
露id没有影响,搞得微软不知道我ip一样 git fatal: 拒绝合并无关的历史的错误解决(亲测有效)...
linux使用jmeter进行压测
1.准备好服务器,这里默认服务器用的系统镜像为contos7.9.2009 2.准备好jmeter的测试计划文件 .jmx 这里默认测试计划的jmx文件在 /nas目录下 3.安装JDK与jmeter进行测试 #创建JDK与jmeter目录,并复制安装文件 mkdir /jmeter mkdir /jmeter/jav…...
leetcode 139. 单词拆分
2023.8.18 本题可以看作完全背包问题,字符串s为背包,字符串列表worddict中的字符串为物品。由于本题的物品集合是排列问题(即物品的排列顺序对结果有影响),所以遍历顺序为:先遍历背包再遍历物品。 接下来看代码: clas…...
若依的使用(token补充、HTTPS(网络安全)、分页前后端配置)
本文章转载于公众号:王清江唷,仅用于学习和讨论,如有侵权请联系 QQ交流群:298405437 本人QQ:4206359 具体视频地址:8 跑后端_哔哩哔哩_bilibili 1、HTTP? 曾经我们在讲JWT的时候,当时JWT需要配合https…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
