ROS教程四——编写Publisher和Subscriber节点(C++篇)

本教程介绍如何使用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教程五-运行简单的发布者和订阅者。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值