ROS学习笔记(二):话题通信、服务通信的了解和对应节点的搭建(C++)

ROS学习笔记(二):话题通信、服务通信的了解和对应节点的搭建(C++和Python)

  • 前言
  • 一、Topics话题通信(C++)
    • 0、自定义msg消息类型文件
    • 1、发布者(Publisher)
    • 2、订阅者(Subscriber)
    • 3、更改配置
  • 二、Services服务通信(C++)
    • 0、定义srv服务类型文件
    • 1、客户端(Client)
    • 2、服务端(Service)
    • 3、更改配置

前言

在ros这种分布式框架中,各个节点可以独立的运行,但是在实际运用的多数情况下各个节点之间都需要有一定的联系或者说是信息的共享,因此在程序的设计上,各节点之间的通信尤为重要。
官方wiki教程链接:ROS Tutorials
如下所示可让我们对ros通信机制有初步的了解,其中也包括了对应通信节点的搭建。
在这里插入图片描述

关于工作空间的搭建参考第一篇文章 ROS学习笔记(一):安装ROS并搭建一个自己的ROS工作空间 ,这里主要记录在程序上如何实现节点通信。

一、Topics话题通信(C++)

话题通信是指节点间以发布订阅的方式进行通信,大体流程为发布者以一定的频率不断地在某个话题上发送消息出去,然后订阅者在ros masters这个平台上通过话题名称发现发布者发送了消息,然后接收其消息进行处理。可同时存在多个订阅者关注同一个话题,也可同时存在多个发布者在同一个话题上发布消息,当然一个订阅者也可以关注多个话题,一个发布者也可以发布多个话题。
关于话题通信,官方的C++案例链接:Writing a Simple Publisher and Subscriber (C++)

0、自定义msg消息类型文件

首先在该功能包下创建msg文件夹,然后在创建一个.msg文件。即:

mkdir msg
touch msg/xx.msg

该文件的编写格式为:

	类型    名称
如:	int32   num_hi

然后需要进入package.xml更改配置,分别添加下面两行:

<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend>

即下图所示:
在这里插入图片描述
然后进入CmakeLists.txt,添加如下配置,此处代表该功能包的依赖包:
在这里插入图片描述
这里的add_message_files打开注释后,还要将文件改成自己的msg文件,如下所示:
在这里插入图片描述
如下所示打开generate_messages的注释为srv文件添加std_msgs依赖。
加粗样式
再打开如下的注释并加入message_runtime,此处代表了find_package中的包的依赖包。
在这里插入图片描述
完成配置后回到工作空间主目录再次编译,然后就会生成如下所示的C++和python的msg头文件。
在这里插入图片描述
在这里插入图片描述
需要使用自定义的msg时,以上述的num.msg为例,如下所示操作即可。

#include "topics/num.h"  // 调用自定义消息类型的头文件topics::num num;  // 用于引用自定义的类型
num.num_hi = xx;  // 赋值

之后把num.num_hi当成正常的变量用就行了。

1、发布者(Publisher)

pub.cpp程序:

#include "ros/ros.h"
#include "std_msgs/String.h"int main(int argc, char **argv)
{setlocale (LC_ALL, "");  // 防止中文乱码std_msgs::String msg;  // 定义发送数据类型int count = 1;ros::init(argc, argv, "talker");  // 初始化ros节点ros::NodeHandle n;  // 定义节点句柄// 定义发布者ros::Publisher pub = n.advertise<std_msgs::String>("what", 1000);//ros::Rate rate(10);  // 设置发布频率为10HZwhile (ros::ok())  // 节点正常运行时返回true{std::stringstream ss;ss << "data" << count;msg.data = ss.str();pub.publish(msg);  // 将话题内容msg发送出去给所有的订阅者ROS_INFO("发布的消息为:%s", msg.data.c_str());  // 打印datacount += 1;//rate.sleep();  // 对应频率改变睡眠时长}return 0;
}

程序大部分都写了注释,这些再补充记录一下:
1、ros::Publisher:定义了一个发布者对象,上文程序中的"what"为话题名称,1000为数据的缓冲区长度,<std_msgs::String>指该话题的消息类型为String类型。
2、关于那个 ros::ok() 返回flase的条件如下:

(1)、判定终端输入Ctrl - C停止程序的运行
(2)、出现同名的节点
(3)、程序调用了ros::shutdown()
(4)、节点句柄被销毁

3、ROS_INFO是printf的替代品,可用于打印某些数据至终端。
4、c_str():指向该字符数组的指针。
5、ros::Rate:如上方设置为10HZ,其会根据上次调用sleep()的时间来配置这次的睡眠时间以保持10HZ的发布速率。

2、订阅者(Subscriber)

sub.cpp程序:

#include "ros/ros.h"
#include "std_msgs/String.h"// 消息回调函数
void what_callback(const std_msgs::String::ConstPtr& msg)
{ROS_INFO("订阅到的消息为:%s", msg->data.c_str());
}int main(int argc, char **argv)
{setlocale (LC_ALL, "");  // 防止中文乱码ros::init(argc, argv, "listeners");  // 初始化ros节点ros::NodeHandle n;  // 定义节点句柄// 定义订阅者ros::Subscriber sub = n.subscribe("what", 1000, what_callback);ros::spin();  // 进入循环,调用消息回调函数。return 0;
}

程序大部分都写了注释,这些再补充记录一下:
1、ros::Subscriber:定义订阅者对象,what为话题名称,1000为数据的缓冲区长度,what_callback为消息回调函数。
2、消息回调函数:上述程序中定义了what_callback()为消息回调函数,在定义订阅者时配置为:当话题"what"有新消息发布时,调用一次消息回调函数what_callback()。
3、ros::spin():进入循环,调用消息回调函数,当ros::ok()返回 false 后退出,因此该循环类似于while (ros::ok()),可参考发布者的第2点补充。

3、更改配置

关于配置的更改参考第一篇文章的 (3)、创建节点文件并配置编译依据文件中的更改配置文件,例如该话题通信功能包的配置更改,首先打开功能包中的CMakeLists.txt文件,然后如下所示进行添加替换即可,如下所示。

add_executable(pub src/pub.cpp)
add_executable(sub src/sub.cpp)
target_link_libraries(pub${catkin_LIBRARIES}
)
target_link_libraries(sub${catkin_LIBRARIES}
)

完成后回到工作空间主目录进行catkin_make编译一下,然后source一下,再运行pub和sub即可,当然工作前提是ros master运行中,即有一个终端已经运行了roscore。3个终端命令分别如下:

roscore
source ./devel/setup.bash
rosrun topics pub
source ./devel/setup.bash
rosrun topics sub

效果如下:
在这里插入图片描述

二、Services服务通信(C++)

服务通信是指节点间以请求响应的方式进行通信,大体流程为客户端节点发送请求给服务端节点,服务端节点接收到请求后处理消息,再发出响应给客户端节点,以此实现节点间的通信,在通信和服务端处理消息期间,客户端的单线程是处在阻塞状态的。
关于服务通信,官方的C++的案例链接:Writing a Simple Service and Client (C++)

0、定义srv服务类型文件

首先在该功能包下创建msg文件夹,然后在创建一个.msg文件。即:

mkdir srv
touch srv/xx.srv

该文件的编写格式为:

客户端请求
---
服务端响应
如:
int32 request_num
---
int32 response_num

然后和自定义msg一样,需要进入package.xml更改配置,分别添加下面两行:

<build_depend>message_generation</build_depend><exec_depend>message_runtime</exec_depend>

然后进入CmakeLists.txt,添加如下配置,此处代表该功能包的依赖包:
在这里插入图片描述
然后和自定义msg不同的是,这里需要添加的配置是add_service_files,而不是add_message_files,即打开add_service_files的注释,并改成自己的srv文件,如下所示:
在这里插入图片描述
如下所示打开generate_messages的注释为srv文件添加std_msgs依赖。
加粗样式
再打开如下的注释并加入message_runtime,此处代表了find_package中的包的依赖包。
在这里插入图片描述
完成配置后回到工作空间主目录再次编译,然后就会生成如下所示的C++和python的srv头文件。

在这里插入图片描述
在这里插入图片描述

下文将使用该自定义的服务类型进行节点间的通信。

1、客户端(Client)

client.cpp程序:

#include "ros/ros.h"
#include "services/numm.h"  // 此处添加的头文件为:功能包名/srv文件名.hint main(int argc, char **argv)
{bool flag;setlocale (LC_ALL, "");  // 防止中文乱码ros::init(argc, argv, "client");  // 初始化ros节点ros::NodeHandle n;  // 定义节点句柄// 定义客户端ros::ServiceClient client = n.serviceClient<services::numm>("why");ros::Rate rate(1);  // 设置发布频率为1HZservices::numm srv;srv.request.request_num = 1;  // 赋请求值while(1){flag = client.call(srv);  // 获取返回值并赋值给flagif(flag){ROS_INFO("请求成功,已将请求值×10,收到的响应为:%d", srv.response.response_num);  // 打印出响应内容srv.request.request_num += 1;}else{ROS_INFO("请求失败。");}rate.sleep();  // 对应频率改变睡眠时长  }
}

程序大部分都写了注释,这些再补充记录一下:
1、ros::ServiceClient:定义了一个客户端对象,上文程序中的"why"为服务名称,< services::numm >指服务类型,即自定义的srv文件中的服务类型。
2、client.call(srv):调用服务时将会处于堵塞状态,调用成功则返回true,response中的值为有效值;调用成功则返回flase,response中的值将为无效值。

注:一些和话题通信重复的程序在话题通信那块有注释。

2、服务端(Service)

service.cpp程序:

#include "ros/ros.h"
#include "services/numm.h"  // 此处添加的头文件为:功能包名/srv文件名.h// 参数1:当前功能包下的srv文件的请求,参数2:当前功能包下的srv文件的响应
bool service_callback(services::numm::Request &request, services::numm::Response &response)
{int pending_num = request.request_num;  // 获取客户端请求,赋值为待处理数据ROS_INFO("收到的请求为:%d", pending_num);  // 打印dataresponse.response_num = pending_num * 10;  // 处理数据:×10return true;
}int main(int argc, char **argv)
{setlocale (LC_ALL, "");  // 防止中文乱码ros::init(argc, argv, "service");  // 初始化ros节点ros::NodeHandle n;  // 定义节点句柄// 定义服务端ros::ServiceServer service = n.advertiseService("why", service_callback);ros::spin();  // 进入循环,调用消息回调函数。
}

程序大部分都写了注释,这些再补充记录一下:
1、ros::ServiceServer:定义服务端对象,why为话题名称,service_callback为服务回调函数。
2、服务回调函数:上述程序中定义了service_callback()为服务回调函数,在定义服务端时配置为:当话题"why"有新消息发布时,调用一次服务回调函数service_callback()。

注:一些和话题通信重复的程序在话题通信那块有注释。

3、更改配置

关于配置的更改参考第一篇文章的 (3)、创建节点文件并配置编译依据文件中的更改配置文件,例如该话题通信功能包的配置更改,首先打开功能包中的CMakeLists.txt文件,然后如下所示进行添加替换即可,如下所示。

add_executable(cli src/client.cpp)
add_executable(ser src/service.cpp)
add_dependencies(cli ${PROJECT_NAME}_gencpp)
add_dependencies(ser ${PROJECT_NAME}_gencpp)
target_link_libraries(cli${catkin_LIBRARIES}
)
target_link_libraries(ser${catkin_LIBRARIES}
)

完成后回到工作空间主目录进行catkin_make编译一下,然后source一下,再运行pub和sub即可,当然工作前提是ros master运行中,即有一个终端已经运行了roscore。3个终端命令分别如下:

roscore
source ./devel/setup.bash
rosrun services ser
source ./devel/setup.bash
rosrun services cli

效果如下:
在这里插入图片描述

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

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

相关文章

thinkphp递归实现无限级子分类合并上级children

//设别分类列表public function getCategoryList(){$list = Db::name(categorys)->select(

MODBUS转PROFINET网关与全数字交流伺服配置案例

MODBUS转PROFINET网关连接与全数字交流伺服驱动系统的配置案例&#xff0c;这一通信方式极大地简化了工业自动化系统中的数据传输和控制过程。变频器和伺服电机可以实现数据交流和控制指令的实时传输&#xff0c;从而实现更精确更高效的生产过程。 案例简介&#xff1a;本案例是…

跟我学java|Stream流式编程——Stream 基础

一、流式编程的概念和作用 Java 流(Stream)是一连串的元素序列&#xff0c;可以进行各种操作以实现数据的转换和处理。流式编程的概念基于函数式编程的思想&#xff0c;旨在简化代码&#xff0c;提高可读性和可维护性。 Java Stream 的主要作用有以下几个方面&#xff1a; 简化…

苹果Find My查找芯片-伦茨科技ST17H6x支持苹果Find My认证

Apple「查找」Find My可通过庞大的“Apple Find My Network” 实现全球查找功能。无数iOS、iPadOS、macOS、watchOS激活设备与Find My 设备结合在一起&#xff0c;无需连接到Wi-Fi或者蜂窝网络&#xff0c;用户也可以给遗失的设备定位。对于任何iOS、iPadOS、macOS、watchOS设备…

el-dialog的modal-class

今天发现个事 <el-dialogv-model"bindDialogVisible":title"bindDialogTitle"append-to-bodyclose-on-press-escapedraggablemodal-class"bindNdevice-dialog"width"500px"></el-dialog> 这个样式这样写生效 <style …

【Docker】私有仓库

目录 1.搭建 2. 上传镜像 3.拉取镜像 1.搭建 1.拉取私有仓库的镜像 docker pull registry 2.创建私有仓库容器 docker run -id --nameregistry -p 5000:5000 registry 3.打开浏览器,输入地址&#xff08;http:私有仓库服务器ip:5000/v2/_catalog&#xff09; 出现如图表示私…

【数据结构】栈的基本知识详解

栈的基本概念与基本操作 导言一、栈的基本概念1.1 栈的定义1.2 栈的重要术语1.3 栈的数学性质 二、栈的基本操作结语 导言 大家好&#xff0c;很高兴又和大家见面了&#xff01;&#xff01;&#xff01; 今天开始&#xff0c;咱们将正式进入【数据结构】第三章的内容介绍。在…

vue3用户权限管理(路由控制等)1

在前端开发的过程中&#xff0c;我们需要做前端的权限管理&#xff0c;我们需要根据后端提供的信息来控制权限&#xff0c;这时候就需要根据用户的操作来进行权限控制了。逻辑稍微有一点绕&#xff0c;多理解就好了。 用户路由权限管理 大致的实现原理&#xff1a; 一般将路由…

解析IT运维领域ITSS和ITIL证书

&#x1f33b;IT运维领域ITSS和ITIL证书是两种广泛认可的专业认证。 &#x1f4d7;ITSS认证证书 ITSS是中国电子技术标准化研究院推出的&#xff0c;&#x1f449;包含“IT 服务工程师”和“IT 服务经理”的系列培训。有效满足GB/T 28827.1 的符合性评估要求和ITSS服务资质升级…

中国建设银行 关于解决微软升级导致插入网银盾无法自动打开企业网银的通知

关于解决微软升级导致插入网银盾无法自动打开企业网银的通知 发布时间&#xff1a;2023-10-18 尊敬的客户&#xff1a; 近期Windows操作系统升级会禁止使用IE浏览器&#xff0c;可能会导致您在插入网银盾后无法自动弹出企业网银登录页面&#xff0c;您可以通过以下方式解决&…

QUV紫外光老化加速试验机

1.1 IEC61215标准背景 IEC61215Crystallinesiliconterrestrialphotovoltaic(PV)modules—Designqualificationandtypeapproval》是国际电工委员会的一个产品测试方法。目前太阳能行业正在广泛引用这个标准&#xff0c;对材料或产品进行测试。 2 材料耐候性老化测试原理 在介…

杨中科 ASP.NET Core 中的依赖注入的使用

ASP.NET CORE中服务注入的地方 1、在ASP.NET Core项目中一般不需要自己创建ServiceCollection、IServiceProvider。在Program.cs的builder.Build()之前向builderServices中注入 2、在Controller中可以通过构造方法注入服 务。 3、演示 新建一个calculator类 注入 新建TestC…

网页内容任君采撷-右键无法复制

CSDN一年一度的博客之星评选活动已经结束&#xff0c;刚好点击来看看学习一下大佬们的博客。 发现绝大部分的博主对于知识的公开度都是非常高的&#xff0c;当然除了收费的专栏外。 其中少部分博主对自己的博文设定了一定的操作&#xff0c;无法直接使用博文中的内容。 现在大…

Photoshop Express一款出色的照片编辑器

​【应用名称】&#xff1a;Photoshop Express ​【适用平台】&#xff1a;#Android ​【软件标签】&#xff1a;#Photoshop ​【应用版本】&#xff1a;12.1.2 ​【应用大小】&#xff1a;223MB ​【软件说明】&#xff1a;软件升级更新。一款出色的照片编辑器&#xff0c…

生成模型 | 2024年新年新论文:audio2photoreal[正在更新中]

本博客主要包含了20240103新出的论文From Audio to Photoreal Embodiment: Synthesizing Humans in Conversations论文解释及项目实现~ 论文题目&#xff1a;20240103_From Audio to Photoreal Embodiment: Synthesizing Humans in Conversations 论文地址&#xff1a;2401.018…

8年测试总结,正确的自动化测试实施-单元/接口/Web自动化...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 今天给大家分享自…

作业--day42

界面设计 MyProWin::MyProWin(QWidget *parent): QMainWindow(parent) {/**********窗口主体**********///窗口大小this->setFixedSize(644, 493);this->setWindowTitle("QQ");this->setWindowIcon(QIcon("C:/Users/10988/Downloads/pictrue/pictrue/…

java中实现对文件高效的复制

不多说我们直接上代码&#xff1a; 这个是使用NIO包下的FileChannel和ByteBuffer进行文件的操作的&#xff0c;会比较高效。

JavaScript(WebAPI)

文章目录 1. 什么是 WebAPI2. DOM 基本概念2.1 DOM 树 3. 获取元素3.1 querySelector3.2 querySelectorAll 4. 事件初识4.1 键盘事件 5. 操作元素5.1 获取/修改元素属性5.2 获取/修改表单元素属性5.3 获取/修改样式属性 6. 操作节点6.1 新增节点6.2 删除节点 1. 什么是 WebAPI …

浅谈智慧路灯安全智能供电方案设计——安科瑞赵嘉敏

摘要: 智慧路灯&#xff0c;作为智慧城市、新基建、城市更新的主要组成部分&#xff0c;近些年在各大城市已得到很好的落地和 应用&#xff0c;但其与传统路灯相比集成大量异元异构电子设备&#xff0c;这些设备的供电电压、接口形式、权属单位各不相同&#xff0c; 如何设计一…