ROS学习第七弹--写简单的发布订阅(C++)

用C++写简单的发布和订阅

1.   写发布节点

roscdbeginner_tutorials

1.1代码

在beginner_tutorials中创建一个src目录,并创建文件talker.cpp

代码地址:

https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/talker/talker.cpp

创建目录:

mkdir -p src

代码内容:

  27#include "ros/ros.h"
  28#include "std_msgs/String.h"
  29
  30#include <sstream>
  31
  32/**
  33 * This tutorial demonstrates simple sending of messages over the ROS system.
  34 */
  35intmain(intargc, char **argv)
  36{
  37  /**
  38   * The ros::init() function needs to see argc and argv so that it can perform
  39   * any ROS arguments and name remapping that were provided at the command line.
  40   * For programmatic remappings you can use a different version of init() which takes
  41   * remappings directly, but for most command-line programs, passing argc and argv is
  42   * the easiest way to do it.  The third argument to init() is the name of the node.
  43   *
  44   * You must call one of the versions of ros::init() before using any other
  45   * part of the ROS system.
  46   */
  47  ros::init(argc, argv, "talker");
  48
  49  /**
  50   * NodeHandle is the main access point to communications with the ROS system.
  51   * The first NodeHandle constructed will fully initialize this node, and the last
  52   * NodeHandle destructed will close down the node.
  53   */
  54  ros::NodeHandlen;
  55
  56  /**
  57   * The advertise() function is how you tell ROS that you want to
  58   * publish on a given topic name. This invokes a call to the ROS
  59   * master node, which keeps a registry of who is publishing and who
  60   * is subscribing. After this advertise() call is made, the master
  61   * node will notify anyone who is trying to subscribe to this topic name,
  62   * and they will in turn negotiate a peer-to-peer connection with this
  63   * node.  advertise() returns a Publisher object which allows you to
  64   * publish messages on that topic through a call to publish().  Once
  65   * all copies of the returned Publisher object are destroyed, the topic
  66   * will be automatically unadvertised.
  67   *
  68   * The second parameter to advertise() is the size of the message queue
  69   * used for publishing messages.  If messages are published more quickly
  70   * than we can send them, the number here specifies how many messages to
  71   * buffer up before throwing some away.
  72   */
  73  ros::Publisherchatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  74
  75  ros::Rateloop_rate(10);
  76
  77  /**
  78   * A count of how many messages we have sent. This is used to create
  79   * a unique string for each message.
  80   */
  81  intcount = 0;
  82  while (ros::ok())
  83  {
  84    /**
  85     * This is a message object. You stuff it with data, and then publish it.
  86     */
  87    std_msgs::Stringmsg;
  88
  89    std::stringstreamss;
  90    ss << "hello world " << count;
  91    msg.data = ss.str();
  92
  93    ROS_INFO("%s", msg.data.c_str());
  94
  95    /**
  96     * The publish() function is how you send messages. The parameter
  97     * is the message object. The type of this object must agree with the type
  98     * given as a template parameter to the advertise<>() call, as was done
  99     * in the constructor above.
 100     */
 101    chatter_pub.publish(msg);
 102
 103    ros::spinOnce();
 104
 105    loop_rate.sleep();
 106    ++count;
 107  }
 108
 109
 110  return0;
 111}

1.2代码解释

 27#include "ros/ros.h"

这两行是包含ros的最常用的ROS头文件

28#include "std_msgs/String.h"

这包括在std_msgs包中的std_msgs / String消息。 这是从该包中的String.msg文件自动生成的头文件。 有关消息定义的更多信息,请参阅消息页面。

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

初始化ROS。 这使得ROS可以通过命令行重新命名 - 现在不重要。 这也是我们指定节点名称的地方。 节点名称在运行的系统中必须是唯一的。

这里使用的名称必须是基本名称,即它不能包含/。

54  ros::NodeHandlen;

创建一个句柄到这个进程的节点。 创建的第一个NodeHandle实际上将执行节点的初始化,而最后一个NodeHandle将清除节点正在使用的任何资源。

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

告诉主节点,我们将要发布关于主题聊天的类型为std_msgs / String的消息。 这可以让主节点告诉任何正在监听的节点,我们将发布关于该主题的数据。 第二个参数是我们的发布队列的大小。 在这种情况下,如果我们发布速度过快,最多可能会缓存1000条消息,然后才开始丢弃旧消息。

NodeHandle ::advertise()返回一个ros:: Publisher对象,它有两个用途:1)它包含一个publish()方法,它允许你将消息发布到它创建的主题上; 2)当它超出范围 ,它会自动发布。

75  ros::Rateloop_rate(10);

ros :: Rate对象允许你指定一个你想循环的频率。 它会记录自上次调用Rate :: sleep()以来已经过了多长时间,并且在正确的时间内睡眠。

在这种情况下,我们告诉它我们要以10Hz运行。

 81  intcount = 0;
 82  while (ros::ok())
 83  {

默认情况下,roscpp会安装一个SIGINT处理程序,它提供了Ctrl-C处理,如果发生这种情况,会导致ros :: ok()返回false。

ros :: ok()会返回false,如果:

接收到一个SIGINT(Ctrl-C)

我们已经被同名的另一个节点踢出了网络

ros :: shutdown()已被应用程序的另一部分调用。

所有ros :: NodeHandles都被销毁了

一旦ros:: ok()返回false,所有的ROS调用都将失败。

  87    std_msgs::Stringmsg;
  88
  89    std::stringstreamss;
  90    ss << "hello world " << count;
  91    msg.data = ss.str();

我们使用消息适配类在ROS上广播消息,通常从msg文件生成。 更复杂的数据类型是可能的,但现在我们要使用标准的String消息,它有一个成员:“data”。

 101    chatter_pub.publish(msg);

现在我们实际上是把消息广播给任何连接的节点。

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

ROS_INFO和friends是我们的printf / cout的替代品。 有关更多信息,请参阅rosconsole文档。

 103    ros::spinOnce();

在这个简单的程序中调用ros :: spinOnce()并不是必须的,因为我们没有收到任何回调。 但是,如果您要在此应用程序中添加订阅,并且在此处没有ros :: spinOnce(),则您的回调将永远不会被调用。 所以,为了好的习惯添加它。

 105    loop_rate.sleep();

现在我们使用ros:: Rate对象来休眠剩下的时间,让我们达到10Hz的发布速率。

以下是正在发生的事情的简明版本:

初始化ROS系统

广告宣传我们将发布关于聊天主题的std_msgs / String消息

循环不断发布消息每秒钟10次

现在我们需要编写一个节点来接收这些消息。

简洁代码:

  27#include "ros/ros.h"
  28#include "std_msgs/String.h"
  30#include <sstream>
  35intmain(intargc, char **argv)
  36{
  47  ros::init(argc, argv, "talker");
  54  ros::NodeHandlen;
  73  ros::Publisherchatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
  75  ros::Rateloop_rate(10);
  81  intcount = 0;
  82  while (ros::ok())
  83  {
  87    std_msgs::Stringmsg;
  89    std::stringstreamss;
  90    ss << "hello world " << count;
  91    msg.data = ss.str();
  93    ROS_INFO("%s", msg.data.c_str());
 101    chatter_pub.publish(msg);
 103    ros::spinOnce();
 105    loop_rate.sleep();
 106    ++count;
 107  }
 110  return0;
 111}

2.   写一个订阅节点

在src下创建文件listener.cpp,代码地址:

https://raw.github.com/ros/ros_tutorials/kinetic-devel/roscpp_tutorials/listener/listener.cpp

代码内容:

  28#include "ros/ros.h"
  29#include "std_msgs/String.h"
  30
  31/**
  32 * This tutorial demonstrates simple receipt of messages over the ROS system.
  33 */
  34voidchatterCallback(conststd_msgs::String::ConstPtr& msg)
  35{
  36  ROS_INFO("I heard: [%s]", msg->data.c_str());
  37}
  38
  39intmain(intargc, char **argv)
  40{
  41  /**
  42   * The ros::init() function needs to see argc and argv so that it can perform
  43   * any ROS arguments and name remapping that were provided at the command line.
  44   * For programmatic remappings you can use a different version of init() which takes
  45   * remappings directly, but for most command-line programs, passing argc and argv is
  46   * the easiest way to do it.  The third argument to init() is the name of the node.
  47   *
  48   * You must call one of the versions of ros::init() before using any other
  49   * part of the ROS system.
  50   */
  51  ros::init(argc, argv, "listener");
  52
  53  /**
  54   * NodeHandle is the main access point to communications with the ROS system.
  55   * The first NodeHandle constructed will fully initialize this node, and the last
  56   * NodeHandle destructed will close down the node.
  57   */
  58  ros::NodeHandlen;
  59
  60  /**
  61   * The subscribe() call is how you tell ROS that you want to receive messages
  62   * on a given topic.  This invokes a call to the ROS
  63   * master node, which keeps a registry of who is publishing and who
  64   * is subscribing.  Messages are passed to a callback function, here
  65   * called chatterCallback.  subscribe() returns a Subscriber object that you
  66   * must hold on to until you want to unsubscribe.  When all copies of the Subscriber
  67   * object go out of scope, this callback will automatically be unsubscribed from
  68   * this topic.
  69   *
  70   * The second parameter to the subscribe() function is the size of the message
  71   * queue.  If messages are arriving faster than they are being processed, this
  72   * is the number of messages that will be buffered up before beginning to throw
  73   * away the oldest ones.
  74   */
  75  ros::Subscribersub = n.subscribe("chatter", 1000, chatterCallback);
  76
  77  /**
  78   * ros::spin() will enter a loop, pumping callbacks.  With this version, all
  79   * callbacks will be called from within this thread (the main one).  ros::spin()
  80   * will exit when Ctrl-C is pressed, or the node is shutdown by the master.
  81   */
  82  ros::spin();
  83
  84  return0;
  85}

2.2代码解释

现在,让我们一块一块地分解它,忽略上面已经解释过的一些内容:

 34voidchatterCallback(conststd_msgs::String::ConstPtr& msg)
  35{
  36  ROS_INFO("I heard: [%s]", msg->data.c_str());
  37}

这是在新消息到达聊天话题时将被调用的回调函数。 这个消息是通过一个boost shared_ptr传递的,这意味着如果你愿意的话,你可以把它存储起来,而不用担心它被删除,而且不需要复制底层的数据。

  75  ros::Subscribersub = n.subscribe("chatter", 1000, chatterCallback);

连续不断的订阅主节点topic。 每当新消息到达,ROS都会调用chatterCallback()函数。 第二个参数是队列大小,以防止我们不能够足够快地处理消息。 在这种情况下,如果队列达到1000条消息,我们将在新消息到达时开始丢弃旧消息。

NodeHandle :: subscribe()返回一个ros :: Subscriber对象,你必须坚持直到你想取消订阅。 当订阅者对象被破坏时,它将自动取消订阅聊天主题。

NodeHandle :: subscribe()函数的版本允许您指定一个类成员函数,甚至可以由Boost.Function对象调用的任何东西。 roscpp概述包含更多信息。

  82  ros::spin();

ros :: spin()进入一个循环,尽可能快地调用消息回调。 不要担心,如果没有什么可做的话,它不会占用太多的CPU。 ros:: spin()将会退出,一旦ros :: ok()返回false,这意味着ros :: shutdown()已经被默认的Ctrl-C处理程序调用,主控告诉我们关闭,或者被调用手动。

还有其他的方式来回调,但我们不会担心这里的人。 roscpp_tutorials包有一些演示应用程序来演示这个。 roscpp概述还包含更多信息。

现在再来看一下这个程序的执行流程:

初始化ROS系统

订阅连续发布的topic

旋转,等待消息到达

当消息到达时,调用chatterCallback()函数

3.   重新编译节点

你在之前的教程中使用了catkin_create_pkg,它为你创建了一个package.xml和一个CMakeLists.txt文件。

生成的CMakeLists.txt看起来应该是这样的(修改了创建Msgs和Srvs教程以及删除了未使用的注释和示例):(代码地址)

https://raw.github.com/ros/catkin_tutorials/master/create_package_modified/catkin_ws/src/beginner_tutorials/CMakeLists.txt

代码内容:

   1cmake_minimum_required(VERSION 2.8.3)
   2project(beginner_tutorials)
   3
   4## Find catkin and any catkin packages
   5find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
   6
   7## Declare ROS messages and services
   8add_message_files(DIRECTORY msg FILES Num.msg)
   9add_service_files(DIRECTORY srv FILES AddTwoInts.srv)
  10
  11## Generate added messages and services
  12generate_messages(DEPENDENCIES std_msgs)
  13
  14## Declare a catkin package
  15catkin_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文件应该如下所示:(代码地址:

https://raw.github.com/ros/catkin_tutorials/master/create_package_pubsub/catkin_ws/src/beginner_tutorials/CMakeLists.txt

   1cmake_minimum_required(VERSION 2.8.3)
   2project(beginner_tutorials)
   3
   4## Find catkin and any catkin packages
   5find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
   6
   7## Declare ROS messages and services
   8add_message_files(FILES Num.msg)
   9add_service_files(FILES AddTwoInts.srv)
  10
  11## Generate added messages and services
  12generate_messages(DEPENDENCIES std_msgs)
  13
  14## Declare a catkin package
  15catkin_package()
  16
  17## Build talker and listener
  18include_directories(include ${catkin_INCLUDE_DIRS})
  19
  20add_executable(talker src/talker.cpp)
  21target_link_libraries(talker ${catkin_LIBRARIES})
  22add_dependencies(talker beginner_tutorials_generate_messages_cpp)
  23
  24add_executable(listener src/listener.cpp)
  25target_link_libraries(listener ${catkin_LIBRARIES})
  26add_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来调用它们。 它们不会放在'<前缀> / bin'中,因为在将软件包安装到系统时会污染PATH。如果希望安装时将可执行文件放在PATH上,可以设置安装目标,请参阅:catkin / CMakeLists.txt

有关CMakeLists.txt文件的更多详细信息,请参阅:catkin / CMakeLists.txt

现在运行catkin_make:

# In your catkin workspace
$ cd ~/catkin_ws
$ catkin_make  

注意:或者,如果您添加为新的pkg,则可能需要通过--force-cmake选项告诉catkin强制生成。Seecatkin/教程/using_a_workspaceWith_catkin_make

(这个地方直接在CMakeLists.txt后面添加那些代码之后编译出错,后来将CMakeLists.txt内容全部替换为官网内容编译成功。)

现在你已经写了一个简单的发布者和订阅者,让我们来看看简单的发布者和订阅者。

4.   运行发布和订阅

运行发布:

# In your catkin workspace
$ roscore
$ cd ~/catkin_ws

$ source./devel/setup.bash

$ rosrunbeginner_tutorials talker

运行订阅:

$ rosrunbeginner_tutorials listener

猜你喜欢

转载自blog.csdn.net/zzmj_f/article/details/78871872