编写第一个ROS程序-发布器(Publisher)

在上一篇文章中我们安装好了ROS环境。本篇文章我们将熟悉ROS中的一些概念(Concept),并尝试使用C++来实现一个发布器(Publisher)和一个订阅器(Subscriber)。

该文章是个人学习ROS的过程记录,参考的书是中文版《ROS机器人编程:原理与应用》,英文版为A Systematic Approach to Learning Robot Programming with ROS,该书代码托管在作者wsnewman的github上,感谢作者的辛苦付出。

该篇文章分为以下几个部分:

  1. ROS概念
  2. 在ROS中实现一个发布器
  3. 运行你的发布器

1. ROS

我仅仅列举了几个本篇文章将会涉及到的概念,这些概念的定义来源于ROS Wiki,有兴趣可以进一步深入了解。

1.1 ROS 文件系统

主要是介绍在ROS中的文件组织方式,类似于Python中包(Package)的文件组织形式。

  • Packages: ROS包是ROS中程序的主要组织单元,在一个包中可能包含一系列相关的节点(nodes),ROS依赖库,配置文件等等。ROS包是你可以编译及发行的最小单元了,大部分时候你执行编译操作时便是在编译包。
  • Package.xml: 主要是用来描述包,提供关于包的一些信息,包括包名称,版本,简述,版权信息,依赖等等。
  • 消息类型:对话题(topic)中的消息进行定义,以便发布器/订阅器都能正确编码/解码字节流。

1.2 ROS概念

  • 节点(Nodes): 节点就是可执行文件,该可执行文件可能是你使用roscpp或者rospy创建的。

  • Master: 在上一篇文章中我们运行过命令roscore该命令的作用之一就是启动ROS Master,Master主要是提供名称注册与解析,以便每个节点可以通过名称来找到另外的节点。

  • 消息(message):节点通过消息来完成彼此之间的交流,消息是一个类似于struct的数据结构,其中可能包含几个字段。举个例子:在路径/opt/share/melodic/share/std_msgs/msg(如果你是像我上篇文章中那样安装的)中,你可以看到许多的消息定义,打开一个比如说ColorRGBA.msg,内容如下:

    float32 r;
    float32 g;
    float32 b;
    float32 a;
    
  • 话题(topic): 话题由话题名(topic name)表示,你可以把它想象为一个邮箱,发布器认为我只需要把消息投放到邮箱里就行了,而订阅器认为我只需要去这个邮箱拿消息就行了,因为节点交流的本质是传递消息,话题只是指明一个双方约定好的交流路径。整个过程可以如下的图来表示

2. 在ROS中实现一个发布器

2.1 创建工作区和包

在编写ROS程序之前,首先要建议一个ROS工作区,之后的编写工作都将在该工作区下进行,工作区有一定的格式,你可以选择在你的home目录下创建,依次输入以下几行命令

mkdir -p ~/catkin_ws/src # 创建工作区
cd ~/catkin_ws/src # 进入工作区
# catkin_create_pkg package_name(包名称) dependencies(依赖)
catkin_create_pkg my_minimal_node roscpp std_msgs #新建一个ROS包

创建完成以后,我们可以看到在目录my_minimal_node下多了两个文件(package.xml ,CMakeLists.txt)和两个文件夹(src, include)。

其中的package.xml便是之前提到的ROS包配置文件,描述关于包的信息。CMakeLists.txt是用来配置编译过程,这是本篇文章所主要使用的两个文件。

2.2 修改package.xml

首先我们来修改package.xml,在编辑器中打开该文件,可以看到文件中包含了大量的注释,这些注释都是来指导你该如何书写该文件。

一般说来我们应该包括<name><version><description><maintanner><license><author><build_depend><build_export_depend><exec_depend>便可以了,这部分大家可以按照自己的信息进行修改。

修改后的package.xml类似于下面的内容,当然你也可以不修改,对于这一篇内容来说,这无关紧要。

<?xml version="1.0"?>
<package format="2">
  <name>my_minimal_node</name>
  <version>0.1.0</version>
  <description>The my_minimal_node package</description>
  <maintainer email="gnc@todo.todo">gnc</maintainer>
  <license>MIT</license>
  <author email="sharku">Jane Doe</author>
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>std_msgs</exec_depend>
</package>

2.3 编写第一个简单的ROS程序-发布器

进入到目录~/catkin_ws/src/my_minimal_node/下,在该目录下的src文件夹创建minimal_publisher.cpp,内容如下,代码内容也可以在文章最开始给出的github中找到。

#include <ros/ros.h>
#include <std_msgs/Float64.h>

int main(int argc, char **argv) {
    ros::init(argc, argv, "minimal_publisher"); // 初始化节点名
    ros::NodeHandle n; // 
    ros::Publisher my_publisher_object = n.advertise<std_msgs::Float64>("topic1", 1); // 创建一个发布器,调用advertise通知ROS Master话题名称以及话题类型
    //"topic1" 是话题名
    // 参数 "1" 是queue_size,表示缓冲区大小
    
    std_msgs::Float64 input_float; // 创建一个发布器将要使用的消息变量
    // 该消息定义在: /opt/ros/indigo/share/std_msgs
    // 在ROS中发布的消息都应该提前定义,以便订阅者接收到消息后该如何解读
    // Float64消息的定义如下,其中包含一个数据字段data:
    // float64 data
    
    input_float.data = 0.0; // 设置数据字段
    
    
    // 程序所要做的工作将在下面的循环里完成
    while (ros::ok()) 
    {
        // 该循环没有sleep,因此将一直处于运行状态,不断消耗CPU资源
        input_float.data = input_float.data + 0.001; //每循环一次+0.01
        my_publisher_object.publish(input_float); // 发布消息到对应的话题
    }
}

代码中,#include <ros/ros.h>表示包含ROS头文件,#include <std_msgs/Float64.h>表示包含标准消息类型中的Float64,它的具体类型是float64

在上述代码中,在main函数中,首先对ROS进行初始化,调用ros::init定义了节点名称。然后定义了ros::NodeHandle该变量用来创建一个发布者,通过调用函数.advertise,表示向Master声明我要注册一个话题名为topic1,然后我将会向该话题发布消息,然后返回一个发布器对象。随后我们便可以调用该发布器的.publish来进行消息的发布。

其余部分的解释可以看代码中的注释。

2.4 修改CMakeLists.txt文件

在编写了发布器的程序以后,我们还必须在CMakeLists文件中进行声明,以便让编译器知道应该编译新增的文件。

在CMakeLists文件的最后一行加入下面两行代码:

add_executable(my_minimal_publisher src/minimal_publisher.cpp) # 第一个参数是生成后的可执行文件名 第二个参数
# 是源文件路径名
target_link_libraries(my_minimal_publisher ${catkin_LIBRARIES}) # 链接库

然后关闭文件,打开终端,导航到目录~/catkin_ws/下,运行命令catkin_make即可编译我们刚刚创建的文件。

有可能你的输出结果和我这里显示的不同,因为我已经编译过了,所以编译器没有执行任何动作。

3. 运行你的发布器

在上面的编译过程没有出现问题的情况下,那么你的编译就成功了,接下来我们来运行我们刚刚编写的发布器。

3.1 将工作区路径加入你的终端

类似于$PATH变量,ROS也有自己的路径变量,用来搜索所有的ROS包,因为到这里我们还没有将我们的ROS包加入到ROS路径中去,因此在运行的时候会找不到我们的ROS包。

不过幸运的是,ROS帮我们生成了一个方便的编译脚本,位于路径~/catkin_ws/devel/setup.bash下。如果我们直接运行source ~/catkin_ws/devel/setup.bash那么只会在当前的终端下生效,当你重新打开一个终端,又会无效。因此,我们需要将上面的命令加入到启动脚本中,运行下面的命令完成:

echo "source  ~/catkin_ws/devel/setup.bash" >>  ~/.bashrc

之后你的终端在启动时都将首先运行该命令,然后ROS就可以找到你的程序了。

3.2 启动发布器

打开终端,运行命令:

roscore # 启动ROS Master

然后运行:

 rosrun my_minimal_node my_minimal_publisher # 启动发布器

启动以后,尽管看起来什么都没有发生,但是事实上发布器已经在不断发布消息了。下面我们用ros工具来查看该发布器。

运行命令:

rosnode list # rosnode list 用来列出当前ROS中运行的节点名 

我们可以得到当前ROS系统运行的节点名称,可以看到我们初始化的节点名称出现在了该列表中,证明我们的发布器的确在运行中。

然后运行命令:

rostopic list # rostopic list 用来列出当前ROS中所有的话题名

同样可以看到我们注册的话题名称topic1

然后运行命令:

rostopic  hz /topic1 # rostopic hz 用来检查话题的发布频率

可以看到发布平均频率是3.3kHz,发布频率比较高,因此造成的问题是长时间占用CPU。如下图所示:

这一问题在之后我们将使用sleep方法来设置一个合适的发布频率。

然后运行命令:

rostopic echo -n1 /topic1 # rostopic echo 用来显示话题的消息 -n1代表只接收一次

可以看出输出值为192239.12749,因此通过简单的计算便可以得出程序已经循环了19223912次左右。

视频

ROS编写第一个发布器程序

以上所有过程我录制了一个视频,在浏览文章过程中如果遇到问题,您可以查看视频来看看我是怎么做的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容