[ROS]编写简单的发布器和订阅器

本贴最后更新于 3181 天前,其中的信息可能已经沧海桑田

####1.编写发布器节点

节点是 ROS 中连接 ROS 网络的可执行程序的专业术语。这里我们创建一个发布器节点 talker,它将连续的广播消息。

首先进入 beginner_tutorials 包:

cd ~/catkin_ws/src/beginner_tutorials

#####1.1 代码

在 beginner_tutorials 包目录下创建 src 目录:

mkdir -p ~/catkin_ws/src/beginner_tutorials/src

src 目录包含了 beginner_toturials 包的所有的源文件。
在 src 目录下创建 talker.cpp 文件,添加一下内容:

#include "ros/ros.h" #include "std_msgs/String.h" #include <sstream> /** * *本教程演示简单的发送消息到ROS系统 */ int main(int argc, char **argv) { /** * ros::init()方法需要argc和argv参数,这样就可以接收任意在命令行提供的ROS参数和名字。 * 对于程序化的映射,你可以使用不同版本的init()来直接进行映射, * * 在使用ROS系统的其他部分之前,你必须调用一个版本的ros::init()。 */ ros::init(argc, argv, "talker"); /** * NodeHandle是与ROS系统交流的main入口。 * 第一个NodeHandle构造器将完全的初始化节点,最后一个NodeHandle析构器将关闭节点。 */ ros::NodeHandle n; /** * advertise()方法提供方式告诉ROS在给定名字的主题上发布(消息)。 * 这将产生一个对ROSmaster节点的调用,ROS的master节点保存了发布器和订阅器的注册。 * 在advertise()执行完后,master节点将通知每一个向这个主题订阅的订阅器,订阅成功之后,它们将与主题所在的节点进行点对点的通信。 * advertise()返回一个发布器对象,这个发布器对象允许通过主题调用publish()方法发布消息。一旦发布器的所有副本都销毁了,这个主题将不会再自动的发布消息。 * advertise()方法的第二个参数是用来发布消息的队列的大小。如果发布消息比发送消息的速度快,这个参数的作用就是指定了缓存的消息数目。 */ ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); ros::Rate loop_rate(10); /** * 已经发送的消息的数目. 这个用来为每一个消息创建一个唯一的字符串 */ int count = 0; while (ros::ok()) { /** * 消息对象. 赋值之后发布. */ std_msgs::String msg; std::stringstream ss; ss << "hello world " << count; msg.data = ss.str(); ROS_INFO("%s", msg.data.c_str()); /** * publish()方法用来发送消息。参数是消息对象。对象的类型必须符合上面advertise<>()构造器中申明的<>中的参数类型。 */ chatter_pub.publish(msg); ros::spinOnce(); loop_rate.sleep(); ++count; } return 0; }

#####1.2.代码解析

接下来分析上面的代码。

#include "ros/ros.h"

ros/ros.h 头文件包含了 ROS 系统的大部分公共类库。

#include "std_msgs/String.h"

上面的头文件包含了 std_msgs 包中的 std_msgs/String 消息。这个头文件自动地根据 std_msgs 包中的 String.msg 文件生成。更多的信息参考 http://wiki.ros.org/msg。

ros::init(argc, argv, "talker");

初始化 ROS。这句话允许 ROS 从命令行完成名字的映射,同时这句代码也指定了节点的名字。节点的名字在系统运行的时候必须唯一,并且取名字的时候名字必须符合基本的取名规则,比如说名字就不能有/。

ros::NodeHandle n;

创建节点的 handle。首次创建的 NodeHandle 将完成节点的初始化,最后的一次析构将节点使用的任何资源。

ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

向 master 节点声明我们将要通过 chatter 主题发布 std_msgs/String 类型的数据。master 节点将通知所有监听 chatter 的节点 chatter 主题将要发布数据。第二个参数是发布队列的大小。如果发布的消息太快,超过了 1000 的最大缓存消息之后将会开始丢失旧的消息。
NodeHandle::advertise()方法返回 ros::Publisher 对象,这个方法提供了两种用途:
1)使用 publis()方法通过创建的主题发布消息;
2)一旦超过了范围将自动的停止发布。

ros::Rate loop_rate(10);

ros::Rate 对象允许你指定具体的发布频率。它将追踪距离上一次调用 Rate::sleep()已经过去多久,保证正确的休眠时间。这里我们指定它的速率是 10HZ。

int count = 0; while (ros::ok()) {

默认的,roscpp 将安装一个监听 ctrl+c 组合键的 SIGINT handler,按下组合键之后将导致 ros::ok()返回 false。
下面情况将导致 ros::ok()返回 false。
’‘’
1.SIGINT handler 接收到组合键 ctrl+c;
2.被另外一个重名节点踢出网络;
3.程序其他部分调用了 ros::shutdown();
4.所有的 ros::NodeHandles 都被销毁。
‘’‘
一旦 ros::ok()返回 false,所有的 ROS 调用将失败。

std_msgs::String msg; std::stringstream ss; ss \<\\\< \"hello world \" \<\< count; msg.data = ss.str();

在 ROS 上发布的消息是使用消息适用类,这个类是根据 msg 文件生成的。复杂的数据类型也是可能的,但是现在,我们只使用标准的 String 消息,这个消息只有一个成员:”data“。

chatter_pub.publish(msg);

现在我们可以向任意连接了的节点广播消息了。

ROS_INFO("%s", msg.data.c_str());

ROS_INFO 以及类似的代替了 printf/cout。更多的参考:http://wiki.ros.org/rosconsole

ros::spinOnce();

调用 ros::spinOnce()在简单编程的时候是没必要的,因为我们没有接收任何的回调。然而,如果我们需要在程序中添加订阅器,但是有没有在这里调用 ros::spinOnce(),回调将不会执行。所以这里最好将这句加上。

loop_rate.sleep();

上面这句话让 ros::Rate 对象休眠一会儿,以保证我们是以 10HZ 的速率发布消息。

让我们总结下之前的工作:

1.初始化ROS系统; 2.向master节点注册:之后将通过chatter主题发布std_msgs/String消息; 3.以一秒为周期,循环发布十次消息。

现在我们继续编写一个节点来接收消息。

####2.编写订阅器节点

#####2.1 源码

在 beginner_tutorials 包下的 src 下创建 listener.cpp 文件,并粘贴下面的内容:

#include "ros/ros.h" #include "std_msgs/String.h" /** * 本教程编写了简单的从ROS系统接收消息的消息接收器。 */ void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); } int main(int argc, char **argv) { /** * 初始化节点,名称为listener */ ros::init(argc, argv, "listener"); ros::NodeHandle n; ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); /** *循环处理回调,所有的回调都是主线程中调用。按ctrl+c退出或者节点呗master关闭 */ ros::spin(); return 0; }

2.2 代码解释

接下来一点点的解析代码,这里只解析之前没有的:

void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); }

这个回调方法将会被调用,当有新的消息到达 chatter 主题。消息是通过一种 http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm 的方法。这意味着你可以保存消息,而不用担心需要你去删除,不用复制底层数据。

有很多中方式处理回调的实现。roscpp_tutorlals 包有一些例子。参考:http://wiki.ros.org/roscpp/Overview。

总结:

1.初始化ROS系统 2.订阅chatter主题; 3.等待消息; 4.当消息达到,chatterCallback()方法将被调用。

3.构建节点

通过之前的教程,包初始化会自动生成 package.xml 和 CMakeList.txt 文件。

在 CMakeList.txt 低端加上:

include_directories(include ${catkin_INCLUDE_DIRS}) add_executable(talker src/talker.cpp) target_link_libraries(talker ${catkin_LIBRARIES}) add_dependencies(talker beginner_tutorials_generate_messages_cpp) add_executable(listener src/listener.cpp) target_link_libraries(listener ${catkin_LIBRARIES}) add_dependencies(listener beginner_tutorials_generate_messages_cpp)

上面的代码会创建 2 个可执行程序,talker 和 listener,默认的位置是 ~/catkin_ws/devel/lib/<package_name>.

之后执行:catkin_make。

关于依赖和构建安装程序,参考:http://wiki.ros.org/catkin/CMakeLists.txt

编译完成之后运行:

rosrun beginner_tutorials talker

可以看到:

[INFO] [WallTime: 1314931831.774057] hello world 1314931831.77 [INFO] [WallTime: 1314931832.775497] hello world 1314931832.77 [INFO] [WallTime: 1314931833.778937] hello world 1314931833.78 [INFO] [WallTime: 1314931834.782059] hello world 1314931834.78 [INFO] [WallTime: 1314931835.784853] hello world 1314931835.78 [INFO] [WallTime: 1314931836.788106] hello world 1314931836.79

运行:

rosrun beginner_tutorials listener

可以看到:

[INFO] [WallTime: 1314931969.258941] /listener_17657_1314931968795I heard hello world 1314931969.26 [INFO] [WallTime: 1314931970.262246] /listener_17657_1314931968795I heard hello world 1314931970.26 [INFO] [WallTime: 1314931971.266348] /listener_17657_1314931968795I heard hello world 1314931971.26 [INFO] [WallTime: 1314931972.270429] /listener_17657_1314931968795I heard hello world 1314931972.27 [INFO] [WallTime: 1314931973.274382] /listener_17657_1314931968795I heard hello world 1314931973.27 [INFO] [WallTime: 1314931974.277694] /listener_17657_1314931968795I heard hello world 1314931974.28 [INFO] [WallTime: 1314931975.283708] /listener_17657_1314931968795I heard hello world 1314931975.28

这样就完成了简单的发布器和订阅器的编写了。

  • ROS
    23 引用 • 2 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    125 引用 • 585 回帖
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    22 引用 • 148 回帖 • 17 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 316 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    181 引用 • 400 回帖
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    133 引用 • 1124 回帖 • 109 关注
  • 电影

    这是一个不能说的秘密。

    122 引用 • 608 回帖
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖 • 6 关注
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖 • 4 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 233 回帖
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    24 引用 • 241 回帖
  • Laravel

    Laravel 是一套简洁、优雅的 PHP Web 开发框架。它采用 MVC 设计,是一款崇尚开发效率的全栈框架。

    20 引用 • 23 回帖 • 740 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖 • 1 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    25523 引用 • 105573 回帖 • 1 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    21 引用 • 245 回帖 • 227 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    589 引用 • 3538 回帖
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖 • 2 关注
  • 倾城之链
    23 引用 • 66 回帖 • 167 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    9766 引用 • 44433 回帖 • 88 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    195 引用 • 291 回帖 • 373 关注
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 17 关注
  • 深度学习

    深度学习(Deep Learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。

    54 引用 • 44 回帖 • 1 关注
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 487 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 91 关注
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    91 引用 • 59 回帖 • 2 关注
  • 叶归
    8 引用 • 38 回帖 • 18 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 636 关注
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 386 关注