本教程介绍如何使用C ++编写发布者和订阅者节点
1、编写 Publisher Node
“节点”是连接到ROS网络的可执行文件的ROS术语。现在将创建一个发布者(publisher “talker”)节点,该节点将不断广播消息。将目录更改为在之前的教程中创建catkin工作区中的beginner_tutorials包:
roscd beginner_tutorials
1.1 代码
在beginner_tutorials包目录中创建一个src目录:
mkdir -p src
该目录将包含beginner_tutorials包的所有源文件。
在beginner_tutorials包中创建src / talker.cpp文件,内容如下:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 0;
while (ros::ok())
{
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
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 / String消息,该消息驻留在std_msgs包中。这是从该包中的String.msg文件自动生成的标头
ros::init(argc, argv, "talker");
初始化ROS。允许ROS通过命令行进行名称重映射 - 现在不重要。这也是指定节点名称的地方。节点名称在运行的系统中必须是唯一的。这里使用的名称必须是基本名称,即不能包含路径。
ros::NodeHandle n;
为此进程的节点创建句柄。首先创建的NodeHandle实际上将执行节点的初始化,其次退出时将清除节点使用的任何资源。
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
告诉主机将在主题 chatter 中发布类型为std_msgs / String的消息。主机告诉任何监听 chatter节点的节点,chatter节点将发布该主题的数据。第二个参数是发布队列的大小,在这种情况下,如果发布得太快,缓冲最多1000条消息,之后将开始丢弃旧消息。
NodeHandle :: advertise()返回一个ros :: Publisher对象,它有两个目:
1)它包含一个publish()方法,允许你将消息发布到它创建的主题上;
2)当它超出范围时,它会自动退出。
ros::Rate loop_rate(10);
使用ros :: Rate对象指定要循环的频率。跟踪自上次调用Rate :: sleep()以来的持续时间。在这种情况下,告诉系统以10Hz运行。
int count = 0;
while (ros::ok())
{
默认情况下roscpp将安装一个SIGINT处理程序,该处理程序提供Ctrl-C处理,将导致ros :: ok()返回false。
出现以下情况,ros :: ok()将返回false:
a SIGINT is received (Ctrl-C)
we have been kicked off the network by another node with the same name
ros::shutdown() has been called by another part of the application.
all ros::NodeHandles have been destroyed
一旦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的替代品。相关文档查看 rosconsole documentation 。
ros::spinOnce();
这个简单程序可以不需要调用ros :: spinOnce(),因为没有收到任何回调。但是,如果要在此应用程序中添加订阅,且此处没有ros :: spinOnce(),则永远不会调用您的回调。所以,它是作为一个衡量存在。
loop_rate.sleep();
使用ros :: Rate对象达到10Hz的发布速度。
以上归结如下:
1、 初始化ROS系统;
2、将把chatter主题上的std_msgs / String消息发布给master
3、循环发布消息,每秒10次
2、 编写 the Subscriber Node
2.1 代码
在beginner_tutorials包中创建src / listener.cpp文件,内容如下:
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "listener");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The subscribe() call is how you tell ROS that you want to receive messages
* on a given topic. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. Messages are passed to a callback function, here
* called chatterCallback. subscribe() returns a Subscriber object that you
* must hold on to until you want to unsubscribe. When all copies of the Subscriber
* object go out of scope, this callback will automatically be unsubscribed from
* this topic.
*
* The second parameter to the subscribe() function is the size of the message
* queue. If messages are arriving faster than they are being processed, this
* is the number of messages that will be buffered up before beginning to throw
* away the oldest ones.
*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
/**
* ros::spin() will enter a loop, pumping callbacks. With this version, all
* callbacks will be called from within this thread (the main one). ros::spin()
* will exit when Ctrl-C is pressed, or the node is shutdown by the 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主题时将被调用。消息在boost shared_ptr中传递,这意味着您可以根据需要将其存储起来,而不必担心它会被删除,并且不会复制基础数据。
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
订阅聊天主题。每当有新消息到达时,ROS都会调用chatterCallback()函数。第二个参数是队列大小,以防我们无法足够快地处理消息。在这种情况下,如果队列达到1000条消息,将在新消息到达时开始丢弃旧消息。
NodeHandle :: subscribe()返回一个ros :: Subscriber对象,必须保留该对象,直到取消订阅。订阅对象被销毁后,将自动取消订阅chatter主题。
有一些版本的NodeHandle :: subscribe()函数允许您指定类成员函数,甚至可以指定Boost.Function对象可调用的任何函数。相关信息请查阅 roscpp overview。
ros::spin();
ros :: spin()进入一个循环,尽可能快地调用消息回调函数。如果没有什么可以做的话就不会占用太多的CPU。ros :: spin()将退出一次ros :: ok()返回false,这意味着已经调用了ros :: shutdown(),默认的Ctrl-C处理程序。
还有其他方法可以回调,但我们不会担心这些问题。roscpp_tutorials中有一些演示应用程序来演示这一点。更多roscpp信息请查阅 roscpp overview。
编写Subscribe总结如下:
1、初始化ROS系统;
2、订阅chatter主题;
3、spin,等待消息到达;
4、当消息到达时,将调用chatterCallback()函数
3、编译nodes
使用catkin_create_pkg创建了一个package.xml和一个CMakeLists.txt文件。
生成的CMakeLists.txt应如下所示:
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
## Declare ROS messages and services
add_message_files(DIRECTORY msg FILES Num.msg)
add_service_files(DIRECTORY srv FILES AddTwoInts.srv)
## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)
## Declare a catkin package
catkin_package()
不要担心修改注释(#)示例,只需将这几行添加到CMakeLists.txt的底部:
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)
生成的CMakeLists.txt文件应如下所示:
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
## Declare ROS messages and services
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)
## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)
## Declare a catkin package
catkin_package()
## Build talker and listener
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)
这将创建两个可执行文件,talker和listener,默认情况下将进入devel空间的package目录,默认位于〜/ catkin_ws / devel / lib / <package name>。
注意,必须将可执行目标的依赖项添加到生成目标:
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
这样可以确保在使用之前生成此包的头文件。如果使用catkin工作区内其他包的消息,则需要将依赖项添加到其各自的生成目标,因为catkin并行构建所有项目。从* Groovy *开始,可以使用以下变量来依赖所有必需的目标:
target_link_libraries(talker ${catkin_LIBRARIES})
可以直接调用可执行文件,也可以使用rosrun来调用它们。它们不会放在'<prefix> / bin'中,因为在将软件包安装到系统时会(污染)PATH。如果希望在安装时将可执行文件放在PATH上,则可以设置安装目标,查阅: catkin/CMakeLists.txt。
现在运行catkin_make:
# In your catkin workspace
$ cd ~/catkin_ws
$ catkin_make
注意:或者如果您要添加新的pkg,您可能需要通过--force-cmake选项告诉catkin强制制作。请参阅catkin/Tutorials/using_a_workspace#With_catkin_make.
已经写了一个简单的发布者和订阅者,让我们来查看ROS教程五-运行简单的发布者和订阅者。