当前位置:   article > 正文

ROS通信机制~话题通信(Publisher&Subscriber)·笔记2_nh.subscribe

nh.subscribe

系列文章目录:

ROS开发(ubuntu)笔记·1_嘻·嘻的博客-CSDN博客

ROS通信机制~服务通信(server&client)·笔记3_嘻·嘻的博客-CSDN博客

话题通信·理论模型:(图源:2.1.1 话题通信理论模型 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程                                                 

基本操作:

首先,写发布者Talker要发布的信息的代码:

1. 在右击src创建新的功能包

2.然后导入依赖包

3.写代码(第6步有详细代码) 

写完后Ctrl+Shift+B,正常的话,不会出现ERROR,最后有100%

 对于advertise函数,源码如下图:

topic就是要发布的信息的话题,红色横线处是队列的长度(发布信息的条数),这里直接用queue(队列)的概念去理解,即我设置是10,当我们输入了12条信息时,他会输出后面10条信息。

4.配置一下CMakeLists 

修改完编译一下,没有问题就不会有Error输出,并且输出100%

5.检查此时发布者Talker要发布的消息能否正常运行                                具体步骤:

                           1.终端中输入roscore(执行ros的命令都要进行此操作)     

2.打开新的终端,输入 cd 当前工作空间

3.source一下环境,然后输入rosrun 功能包 映射名称(正常会没有东西输出)

4.输入rostopic echo 话题名称 (我写的是"fang")结果就是一直打印hello hello

最后输入 Ctrl+C 就停止该程序运行

6. 在原来的代码上添加发布逻辑

  1. #include "ros/ros.h"
  2. #include "std_msgs/String.h"
  3. #include<sstream>
  4. int main(int argc,char *argv[])
  5. { //解决输出中文出现乱码的问题
  6. setlocale(LC_ALL,"");
  7. //2.初始化 ROS 节点;
  8. // 参数1和参数2 后期为节点传值会使用
  9. // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
  10. ros::init(argc,argv,"talker");
  11. //3.创建节点句柄;
  12. ros::NodeHandle nh;
  13. //4.创建发布者对象;
  14. //泛型: 发布的消息类型
  15. //参数1: 要发布到的话题
  16. ros::Publisher pub =nh.advertise<std_msgs::String>("fang",10);//就是队列的长度,例如有12条信息的话就输出后面10条
  17. //5.编写发布逻辑并发布数据
  18. //要求以10HZ的频率发布数据,并且文本后添加编号
  19. //先创建被发布的消息
  20. std_msgs::String msg;
  21. //发布频率
  22. ros::Rate rate(10);
  23. //设置编号
  24. int count =0;
  25. //编写循环,循环中发布数据
  26. ros::Duration(3.0).sleep(); //休眠函数,延迟第一条数据的发送
  27. //原因:发送第一条数据时,publisher 还未在 roscore 注册完毕
  28. //若没有,就算先启动订阅者代码,也可能导致前几条信息丢失
  29. while(ros::ok())//ok()指只要该节点还活着,就循环
  30. { count++;
  31. //实现字符串拼接数字
  32. std::stringstream ss;
  33. ss<<"hello --->"<<count;
  34. //msg.data ="hello hello";
  35. msg.data =ss.str();
  36. pub.publish(msg);
  37. //添加日志
  38. ROS_INFO("发布的数据是:%s",ss.str().c_str());
  39. rate.sleep();//睡0.1秒,相当于10HZ
  40. ros::spinOnce();//在有回调函数时才有用,在此函数无用(官方建议添加)
  41. }
  42. return 0;
  43. }

   ros::Duration(3.0).sleep(); //休眠函数,延迟第一条数据的发送
   原因:发送第一条数据时,publisher 还未在 roscore 注册完毕
  若没有,就算先启动订阅者代码,也可能导致前几条信息丢失

 运行结果如图:

接下来是订阅者listener的代码:

  1. #include"ros/ros.h"
  2. #include"std_msgs/String.h"
  3. /* 订阅方实现:
  4. 1.包含头文件
  5. ROS中文本类型--->std_msgs/String.h
  6. 2.初始化ROS节点
  7. 3.创建节点句柄;
  8. 4。创建订阅者对象;
  9. 5.处理订阅到的数量;
  10. 6.spin()函数
  11. */
  12. void doMsg(const std_msgs::String::ConstPtr &msg)
  13. {
  14. //通过msg获取并操作订阅到的消息
  15. ROS_INFO("listener订阅的数据:%s",msg->data.c_str());
  16. }
  17. int main(int argc,char *argv[])
  18. {
  19. setlocale(LC_ALL,"");
  20. //2.初始化ROS节点
  21. // 参数1和参数2 后期为节点传值会使用
  22. // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
  23. ros::init(argc,argv,"listener");
  24. //3.创建节点句柄
  25. ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能
  26. //4.创建订阅者对象;
  27. ros::Subscriber sub =nh.subscribe("fang",10,doMsg);
  28. //5.处理订阅到的数量
  29. ros::spin();//循环读取接收的数据,并调用回调函数(doMsg)处理
  30. return 0;
  31. }

在第4点创建订阅者对象中,分析一下代码

ros::Subscriber sub=nh.subscribe("fang",10,doMsg);

首先是调用ros库中的订阅器Subscriber (类型)创建了一个sub,后面调用句柄nh的subscribe函数(创建订阅者对象)

括号中,"fang"是订阅者的话题,10是消息队列长度(最多缓存10条信息),当发布者发布了该话题时,就会调用doMsg函数(回调函数)

关于回调函数:一般函数是当程序进行到该函数处时,它就立刻实现,可以说是受我们自己控制的 而回调函数,当程序进行到该处时,它不会立刻执行,需要外界给予它一个信号才能运行,就像一个地雷,只有别人踩了才会爆,这就不受我们自己控制,而是受外界控制了。

我们传入的消息是 std_msgs::String类型的,而参数的类型是常量指针的引用,即ConstPtr &msg

ros::spin() 重复执行回调函数,即循环接收发布者发布的信息(与回调函数相对应)

还有一步:(记得修改CMakeLists.txt)与之前步骤相似 

 之后的操作也类似,先ctrl+shift+B看看有没有出问题,再打开终端,rosrun发布者和订阅者            运行如图:

在发布者的终端上输入Ctrl+C就可以停止发布,此时订阅者也停止接受信息了。

此时,我们可以通过计算图来看看两个节点之间的关系(新开一个终端输入rqt_graph

进阶(自定义message):发送的信息可以包含各种数据类型

比如:int8, int16, int32, int64 (或者无符号类型: uint*),float32, float64,string,time,duration,

variable-length array[] and fixed-length array[C] ......

【1】定义msg文件(在功能包下创建msg目录,再创建一个后缀为msg的文件)

【2】配置编辑文件

1.在package.xml中添加 编译 依赖 与 执行 依赖

2.在CMakeLists.txt编辑 msg 相关配置

下面这步配置msg源文件

修改完后,一定要编译一下ctrl+shift+B,没出现ERROR后,就会在devel目录下的include文件夹下出现一个Person.h (Person 就是之前创建的Person.msg中的Person)

下一步:为了方便代码提示以及避免误抛异常,需要先配置 vscode

1.将前面生成的 head 文件路径(在下图红色框中右击生成终端,输入pwd,复制路径)

2.配置进 c_cpp_properties.json 的 includepath属性(粘贴路径,可将后面具体功能包名改为**):

**意味着直接include目录下的所有内容

下面的操作就是实现了(代码与之前类似,注意头文件"plumbing_pub_sub/Person.h") 这里插一步,如果之前创建Person.msg时没有编译,而此时头文件已经用到了Person.h,则会发生逻辑上的错误,为了避免这种错误,需要在CMakeLists进行一步修改操作

 

(orag03_pub 和orag02_sub 是下面修改CMakeLists时,我设的映射名称 )

【发布者】

  1. #include "ros/ros.h"
  2. #include"plumbing_pub_sub/Person.h"
  3. int main(int argc,char *argv[])
  4. {
  5. setlocale(LC_ALL,"");
  6. ROS_INFO("这是消息发布方");
  7. ros::init(argc,argv,"talker_person");
  8. ros::NodeHandle nh;
  9. ros::Publisher pub =nh.advertise<plumbing_pub_sub::Person>("chatter_person",10);
  10. //创建被发布的数据
  11. plumbing_pub_sub::Person person;
  12. person.name ="张三";
  13. person.age=1;
  14. person.height=1.73;
  15. ros::Rate rate(1);
  16. ros::Duration(3).sleep();
  17. while(ros::ok())
  18. {
  19. // 修改数据
  20. person.age += 1;
  21. //数据发布
  22. pub.publish(person);
  23. ROS_INFO("发布的消息:%s,%d,%.2f", person.name.c_str(), person.age, person.height);
  24. //休眠
  25. rate.sleep();
  26. ros::spinOnce();
  27. }
  28. }

【订阅者】

  1. #include"ros/ros.h"
  2. #include"plumbing_pub_sub/Person.h"
  3. void doPerson(const plumbing_pub_sub::Person::ConstPtr &person)
  4. {
  5. ROS_INFO("订阅的人的消息:%s,%d,%.2f",person->name.c_str(),person->age,person->height);
  6. }
  7. int main(int argc,char*argv[])
  8. {
  9. setlocale(LC_ALL,"");
  10. ros::init(argc,argv,"listener_person");
  11. ros::NodeHandle nh;
  12. ros::Subscriber sub=nh.subscribe("chatter_person",10,doPerson);
  13. ros::spin();
  14. return 0;
  15. }

【修改CMakeLists】

 运行如图:

 可得到计算图:

这次笔记好长QAQ,能全部看完的真的很牛逼了^-^

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/49878
推荐阅读
  

闽ICP备14008679号