ROS学习笔记(一) 发布者节点和订阅者节点的创建和运行


最近刚开始学习ROS入门,由于编程基础薄弱,因此尽量找到每一行代码的解释作为学习,可能有错误,请指正。

一、创建和运行过程

1. 准备

VMware Workstation 、Ubuntu16.04.6、ROS kinetic及基础配置

根据书上给的示例和代码,创建一个功能包,功能是通过发布者、订阅者节点发送和接收实时时间消息。

2. 创建功能包

快捷键[Ctrl+Alt+t]打开一个新的终端,输入以下代码:

cd ~/catkin_ws/src
catkin_create_pkg ros_tutorials_topic message_generation std_msgs roscpp

解释:
1.“cd ~/catkin_ws/src”
进入用户主目录下/catkin_ws/src目录
(1)“cd”是进入某目录的命令
“ ~/"指“/home/用户名/”

(2)“catkin_ws”是通常用的工作目录,工作目录是对用户创建的功能包和其他开发人员公开的功能包进行存储和构建的空间。

(3)“~/catkin_ws/src“是用户源代码的空间。在这个目录中建立和保存自己的ROS功能包或其他开发者开发的功能包。

2. “catkin_create_pkg ros_tutorials_topic message_generation std_msgs roscpp ”
在刚才进入的目录下生成了一个名为“ros_tutotials_topic”的功能包
(1)“catkin_create_pkg”
是创建ROS功能包的命令,在创建时会生成catkin构建系统所需要的CMakeLists.txt和package.xml文件的包目录。

(2)“CMake(Cross Platform Make)”
是ROS的构建系统默认使用的构建平台,”CMakeLists.txt“文件用于描述其构建环境。在ROS中,CMake被修改为适合于ROS的”catkin“构建系统。

(3)创建ROS功能包的命令格式为:
catkin_create_pkg [功能包名称][依赖功能包1][依赖功能包2]…
本功能包添加了message_genertation、std_msgs、roscpp三个依赖包。意味着创建使用新的功能包的时候将要用到这三个功能包,必须在创建功能包之前安装它们。这些功能包可以在创建功能包时指定,也可以在创建之后直接在package.xml中输入。
message_generation:为了使用创建新消息的功能包
std_msgs:为了使用ROS的标准消息包
roscpp:为了在ROS中使用C/C++

3. 修改功能包配置文件

输入以下命令:

cd ros_tutorials_topic
gedit package.xml

将打开后的package.xml文件内容修改为:

<?xml version="1.0"?>
<package>
<name>ros_tutorials_action</name>
<version>0.1.0</version>
<description>ROS tutorial package to learn the action</description>
<license>BSD</license>
<author>Melonee Wise</author>
<maintainer email="pyo@robotis.com">pyo</maintainer>
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>actionlib</build_depend>
<build_depend>message_generation</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>actionlib_msgs</build_depend>
<run_depend>roscpp</run_depend>
<run_depend>actionlib</run_depend>
<run_depend>std_msgs</run_depend>
<run_depend>actionlib_msgs</run_depend>
<run_depend>message_runtime</run_depend>
<export></export>
</package>

解释:
1.“cd ros_tutorials_topic”
进入ros_tutorials_topic功能包目录

2.“gedit package.xml”
使用gedit编辑器打开“package.xml”文件

package.xml文件是ROS的配置文件之一,包括功能包名称、作者、许可证和依赖功能包。

4. 修改构建配置文件

输入以下命令:

gedit CMakeLists.txt

将打开后的CMakeLists.txt文件内容修改为

cmake_minimum_required(VERSION 3.0.2) 
project(ros_tutorials_topic) 
## catkin构建时需要的组件包。
## 是依赖包,是message_generation、 std_msgs和roscpp。
## 如果这些功能包不存在,在构建过程中会发生错误。
find_package(catkin REQUIRED COMPONENTS
 message_generation 
 std_msgs 
 roscpp
 )
## 消息声明:MsgTutorial.msg
add_message_files(FILES MsgTutorial.msg)
## 这是设置依赖性消息的选项。
## 如果未安装std_msgs,则在构建过程中会发生错误。
generate_messages(DEPENDENCIES std_msgs)
## catkin功能包选项,描述了库、catkin构建依赖项和系统依赖的功能包。
catkin_package( 
LIBRARIES ros_tutorials_topic
CATKIN_DEPENDS std_msgs roscpp
)
## 设置包含目录。
include_directories(${catkin_INCLUDE_DIRS})
## topic_publisher节点的构建选项。
## 配置可执行文件、目标链接库和其他依赖项。
add_executable(topic_publisher src/topic_publisher.cpp)
add_dependencies(topic_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(topic_publisher ${catkin_LIBRARIES})
## topic_subscriber节点的构建选项。
add_executable(topic_subscriber src/topic_subscriber.cpp)
add_dependencies(topic_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(topic_subscriber ${catkin_LIBRARIES})

解释:
CMakeLists.txt文件中设置了可执行文件的创建、依赖包优先构建、连接器的创建等等。
1.“cmake_minimum_required(VERSION 3.0.2)”
指定操作系统中安装的cmake的最低版本。

如果使用低于此版本的cmake,则必须更新版本。

2.“project(ros_tutorials_topic)”
输入功能包的名称
这里输入与package.xml中的标记中描述相同的名称,否则在构建时会出错

3.“find_package(catkin REQUIRED COMPONENTS message_generation std_msgs roscpp)”
构建所需的组件包。
由于在创建功能包时将message_generation、std_msgs、roscpp添加为了依赖包,因此需要在此输入依赖包的名称,让用户优先创建依赖功能包。
find_package()是CMake的函数,功能是查找并加载外来工程。

4.“add_message_files(FILES MsgTutorial.msg)”
引用当前目录中的MsgTutorial.msg文件,并自动生成一个MsgTutorial.h头文件。
add_message_files是添加消息文件的选项,FILES将引用当前功能包目录的msg目录中的*.msg文件,自动生成一个头文件。
msg文件时用于话题的消息文件,这种msg文件一般只包含一个字段类型和一个字段名称。
由于在这里构建了消息文件,因此接下来需要创建MsgTutorial.msg文件。

5.“generate_messages(DEPENDENCIES std_msgs)”
设置依赖的消息选项为std_msgs消息包。

6.“catkin_package(
LIBRARIES ros_tutorials_topic
CATKIN_DEPENDS std_msgs roscpp
)”
设置catkin构建选项。

LIBRARIES表示将使用ros_tutorials_topic功能包的库。
CATKIN_DEPENDS后面指定系统依赖包。

7.“include_directories ($ {catkin_INCLUDE_DIRS})”
指定包含目录。

include_directories是可以指定包含目录的选项。目前设定为${catkin_INCLUDE_DIRS},这意味着将引用每个功能包中的include目录中的头文件。

8.topic_publisher节点的构建选项

“add_executable(topic_publisher src/topic_publisher.cpp)”
引用src/topic_publisher.cpp文件生成topic_publisher可执行文件
由于此处在src目录中构建了topic_publisher.cpp文件以创建topic_publisher可执行文件,因此之后需要创建topic_publisher.cpp文件并编写代码。

“add_dependencies(topic_publisher
$ {${PROJECT_NAME}_EXPORTED_TARGETS}
${catkin_EXPORTED_TARGETS})”

优先生成topic_publisher库依赖的消息及dynamic reconfigure
在构建该库和可执行文件之前,如果有需要预先生成的有依赖性的消息或dynamic reconfigure,则要先执行。

“target_link_libraries(topic_publisher ${catkin_LIBRARIES})”
在创建特定的可执行文件之前将库和可执行文件进行链接

9.topic_subscriber节点的构建选项
add_executable(topic_subscriber src/topic_subscriber.cpp)

add_dependencies(topic_subscriber KaTeX parse error: Expected '}', got 'EOF' at end of input: {{PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(topic_subscriber ${catkin_LIBRARIES})

5. 创建消息文件

在CMakeLists.txt中“add_message_files(FILES MsgTutorial.msg)”引用了当前功能包目录的msg目录中的 MsgTutorial.msg文件,因此需要在msg目录中创建这个文件

输入以下命令:

roscd ros_tutorials_topic
mkdir msg
cd msg
gedit MsgTutorial.msg

解释:
1.“roscd ros_tutorials_topic”
移动到功能包目录

roscd:移动到指定的ROS功能包目录

2.“mkdir msg”
创建名为msg的消息目录

mkdir:创建目录命令

3.“cd msg”
进入刚才创建的msg文件

4.“gedit MsgTutorial.msg”
创建“MsgTurotial.msg”文件并用gedit编辑器打开文件

在打开的MsgTutorial.msg文件中输入以下内容:

time stamp
int32 data

解释:
创建了一个time类型的stamp消息和一个int32类型的data消息。

6. 创建发布者节点

在CMakeLists.txt中“add_executable(topic_publisher src/topic_publisher.cpp)”构建了src目录中的topic_publisher.cpp文件,因此需要在src目录中创建这个文件。

输入以下命令:

roscd ros_tutorials_topic/src
gedit topic_publisher.cpp

解释:
移至功能包文件夹中的src目录
新建topic_publisher.cpp文件并修改内容

在打开的topic_publisher.cpp文件中输入以下内容:

#include "ros/ros.h"					// ROS默认头文件
#include "ros_tutorials_topic/MsgTutorial.h"		// MsgTutorial消息头文件( 构建后自动生成)
int main(int argc, char **argv)			 // 节点主函数
{
ros::init(argc, argv, "topic_publisher");		// 初始化节点名称
ros::NodeHandle nh;					// 声明一个节点句柄来与ROS系统进行通信
// 声明发布者,创建一个使用ros_tutorials_topic功能包的MsgTutorial 消息文件的
// 发布者ros_tutorial_pub。话题名称是"ros_tutorial_msg",
// 消息文件发布者队列(queue)的大小设置为100 
ros::Publisher ros_tutorial_pub = 
nh.advertise<ros_tutorials_topic::MsgTutorial>("ros_tutorial_msg", 100);
// 设定循环周期。"10"是指10Hz,是以0.1秒间隔重复
ros::Rate loop_rate(10);
ros_tutorials_topic::MsgTutorial msg; // 以MsgTutorial消息文件格式声明一个 叫做msg的消息
int count = 0; 			// 声明要在消息中使用的变量
while (ros::ok())
{
msg.stamp = ros::Time::now();			// 把当前时间传给msg的下级消息stamp
msg.data = count; 				// 将变量count的值传给下级消息data
ROS_INFO("send msg = %d", msg.stamp.sec); // 显示stamp.sec消息
ROS_INFO("send msg = %d", msg.stamp.nsec); // 显示stamp.nsec消息
ROS_INFO("send msg = %d", msg.data); // 显示data消息
ros_tutorial_pub.publish(msg);		// 发布消息。
loop_rate.sleep();				// 按照上面定义的循环周期进行暂歇
++count;						// 变量count增加1
}
return 0; }

解释:
1.#include “ros/ros.h”
ROS默认头文件。

引用了ROS中大部分常用头文件,如果要使用ROS,必须包含这个头文件

2.#include "ros_tutorials_topic/MsgTutorial.h"
引用ros_tutorials_topic/MsgTutorial消息
为了发布MsgTutorial类型的消息,需要引用对应的头文件。
在CMakeLists.txt文件中“add_message_files(FILES MsgTutorial.msg)”引用当前目录中的MsgTutorial.msg文件,并自动生成了MsgTutorial.h头文件

3.“int main(int argc, char **argv)”
节点主函数
argc是整型参数,用于统计程序运行时发送给main函数的命令行参数的个数。
argv是指向字符型指针的指针数组,每一个元素指向一个参数,显示想main函数发送的内容。

4.ros::init(argc, argv, “topic_publisher”);
初始化ROS,指定节点名称为“topic_publisher”

5.“ ros::NodeHandle nh”
声明一个节点句柄来与ROS系统进行通信

为进程的节点创建一个句柄,第一个创建的句柄会为节点初始化,最后一个销毁的句柄会释放该节点所占用的资源。NodeHandle是和ROS系统通信的重要工具,

6.“ ros::Publisher ros_tutorial_pub =
nh.advertise<ros_tutorials_topic::MsgTutorial>(“ros_tutorial_msg”, 100);”

声明一个名为“ros_tutorial_pub”的发布者
话题名为“ros_tutorial_msg”
消息类型为“ros_tutorials_topic”功能包中的“MsgTutorial”消息。
这样之后发布者就能够告诉所有订阅了该话题的节点,将要有数据发布。
设置发布者队列的大小为100,在两次发布信息的间隔,即将被发布的信息被写进缓冲区,如果消息在这期间大于100条,前面的信息将被消除。
nh.advertise的作用是:
返回一个ros::Publisher对象,它有一个publish()成员函数可以让你在topic上发布消息,如果消息类型不对它会拒绝发布。

7.“ros::Rate loop_rate(10);”
设置发布消息的频率为10Hz,即每0.1s发布一次消息。

ros::Rate指定自循环的频率,它会追踪记录自上一次调用Rate::sleep()后时间的流逝,并休眠直到一个频率周期的时间。与loop_rate.sleep()配合使用。

8.“ ros_tutorials_topic::MsgTutorial msg”
声明一个名为msg的MsgTutorial消息文件格式的消息

9.“while (ros::ok())”
一旦ros::ok()返回false,所有的ROS调用都会失效。
出现以下情况,ros::ok()返回false:
SIGINT被触发,按Ctri+Ct结束程序
被另一同名节点踢出ROS网络
ros::shutdown()被程序的另一部分调用
节点中的所有ros::NodeHandles都已被销毁

10.“ msg.stamp = ros::Time::now()”
把当前时间传给msg消息中的名为“stamp”的下级消息

ros::Time::now()是Time类型中的静态函数,将检索当前时间并根据ROS时钟返回当前时间。
stamp是msg消息中声明的time类型消息

11.“msg.data = count;”
将变量count的值传给下级消息data,用于计数

12.“ROS_INFO(“send msg = %d”, msg.stamp.sec)”
显示sramp.sec消息

ROS_INFO是ROS的专用函数,这个函数与printf()函数类似,将信息显示在屏幕上。
msg.stamp.sec是time类型中的公用数据,stamp.sec参数是以秒为单位的当前的时间。

13.“ROS_INFO(“send msg = %d”, msg.stamp.nsec)”
显示stamp.nsec消息

msg.stamp.sec是time类型中的公用数据,stamp.sec参数是以纳秒为单位的当前的时间。其值始终小于10^9,因此相当于表示秒小数点后面精确到纳秒的时间。

根据以上解释可以看出,此程序将在屏幕上以10Hz的频率显示ROS时间(以秒和纳秒为单位)、发布消息次数,并发布msg消息。

7. 创建订阅者节点

在CMakeLists.txt中“add_executable(topic_subscriber src/topic_publisher.cpp)”构建了src目录中的topic_sunscriber.cpp文件,因此需要在src目录中创建这个文件。

输入以下命令:

roscd ros_tutorials_topic/src
gedit topic_subscriber.cpp

解释:
移至功能包文件夹中的src目录
新建topic_subscriber.cpp文件并修改内容

在打开的topic_subscriber.cpp文件中输入以下内容:

#include "ros/ros.h"					// ROS的默认头文件
#include "ros_tutorials_topic/MsgTutorial.h"		// MsgTutorial消息头文件(构建后自动生成)
// 这是一个消息后台函数,
// 此函数在收到一个下面设置的名为ros_tutorial_msg的话题时候被调用。
// 输入的消息是从ros_tutorials_topic功能包接收MsgTutorial消息。
void msgCallback(const ros_tutorials_topic::MsgTutorial::ConstPtr& msg) {
ROS_INFO("recieve msg = %d", msg->stamp.sec);		// 显示stamp.sec消息
ROS_INFO("recieve msg = %d", msg->stamp.nsec);		// 显示stamp.nsec消息
ROS_INFO("recieve msg = %d", msg->data);		// 显示data消息
}
int main(int argc, char **argv)			// 节点主函数
{
ros::init(argc, argv, "topic_subscriber");		// 初始化节点名称 
ros::NodeHandle nh; 			// 声明用于ROS系统和通信的节点句柄
// 声明订阅者,创建一个订阅者ros_tutorial_sub,
// 它利用ros_tutorials_topic功能包的的MsgTutorial消息文件。
// 话题名称是"ros_tutorial_msg",订阅者队列(queue)的大小设为100。
ros::Subscriber ros_tutorial_sub = nh.subscribe("ros_tutorial_msg", 100, msgCallback);
// 用于调用后台函数,等待接收消息。在接收到消息时执行后台函数。
ros::spin();
return 0; }

解释:
1.“void msgCallback(const ros_tutorials_topic::MsgTutorial::ConstPtr& msg) ”
其实这里我目前还没太看懂,应该是:
定义了一个名为“msgCallback”的函数,网上的解释是“ConstPtr&”是一个指向常量的智能指针,因此在这里声明了一个名为“msg”的指向ros_tutorials_topic,名为msg指针指向ros_tutorials_topic::MsgTutorial的地址。这里的msg与上一个函数中的msg没有关系,换成另外的任意名字并修改函数中的指针名,运行订阅者程序同样可以得到相同的效果。
在该函数中加上“ROS_INFO(“receive msg = %p”,msg)”,执行程序后将会打印指针地址,如图所示:
在这里插入图片描述
2. msgCallback函数的内容是:
调用指针,显示msg消息文件中的内容

3.ros::Subscriber ros_tutorial_sub = nh.subscribe(“ros_tutorial_msg”, 100, msgCallback)
创建一个名为“ros_tutorial_sub”的订阅者,订阅名为“ros_tutorial_msg”的话题,此时订阅者订阅的话题必须是发布者发布的话题,否则订阅者不会接收到消息。
订阅者队列的大小为100。

4.“ros::spin()”
这是ROS的消息回调处理函数,订阅了消息之后,只有使用ros::spin(),订阅者才能收到其订阅的消息。

8. 构建功能包

输入以下命令:

cd ~/catkin_ws
catkin_make

解释:
catkin_make是基于catkin构建系统的构建,构建用户创建的功能包或构建下载的功能包的命令。用法如下:
catkin_make 构建~/catkin_ws/src目录中所有功能包
catkin_make –pkg [包名] 构建某一功能包

9.运行发布者

输入以下命令:

roscore
rosrun ros_tutorials_topic topic_publisher

将会显示如下信息:
在这里插入图片描述
但这时候显示的消息只是用ROS_INFO()打印的消息。

10. 运行订阅者

快捷键[Ctrl+Alt+t]打开一个新的终端,输入以下代码:

rosrun ros_tutorials_topic topic_subscriber

将会显示如下信息:
在这里插入图片描述

11. 检查运行中的节点的通信状态

输入以下命令:

rqt_graph

将会显示以下内容:
在这里插入图片描述
如果未安装rqt,请先输入以下命令安装:
输入以下命令:

sudo apt-get install ros-kinetic-rqt*
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值