ROS學習(十二)—— 編寫簡單的消息發布器和訂閱器(C++)


一、創建發布器節點

1 節點功能:

  不斷的在ROS網絡中廣播消息

 

2 創建節點

(1)打開工作空間目錄

cd ~/catkin_ws/src/beginner_tutorials

 

 

(2)創建src文件夾

mkdir -p ~/catkin_ws/src/beginner_tutorials/src

 

 

(3)創建talkler.cpp文件,幷附上代碼

#include "ros/ros.h"              //      ros/ros.h是一個實用的頭文件,它引用了ROS系統中大部分常用的頭文件,使用它會使得編程很簡便。 
#include "std_msgs/String.h"      //      這引用了std_msgs/String 消息, 它存放在std_msgs package里,是由String.msg文件自動生成的頭文件。
                                  //  需要更詳細的消息定義,參考msg頁面. 

#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");   // 初始化ROS。它允許ROS通過命令行進行名稱重映射——目前,這不是重點。
                                     // 同樣,我們也在這里指定我們節點的名稱——必須唯一。

  /**
   * 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.

    為這個進程的節點創建一個句柄。第一個創建的NodeHandle會為節點進行初始化,最后一個銷毀的會清理節點使用的所有資源。 
   */

  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.
   */
 
  /* 1、告訴master我們將要在chatter topic上發布一個std_msgs/String的消息。這樣master就會告訴所有訂閱了chatter topic的節點,將要有數據發布。
     2、第二個參數是發布序列的大小。在這樣的情況下,如果我們發布的消息太快,緩沖區中的消息在大於1000個的時候就會開始丟棄先前發布的消息。 
     3、NodeHandle::advertise() 返回一個 ros::Publisher對象,它有兩個作用: 1) 它有一個publish()成員函數可以讓你在topic上發布消息; 
                                                                     2) 如果消息類型不對,它會拒絕發布。 
  */
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

  //ros::Rate對象可以允許你指定自循環的頻率。它會追蹤記錄自上一次調用Rate::sleep()后時間的流逝,並休眠直到一個頻率周期的時間。
  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;

   
  /* roscpp會默認安裝一個SIGINT句柄,它負責處理Ctrl-C鍵盤操作——使得ros::ok()返回FALSE。 
         ros::ok()返回false,如果下列條件之一發生: 
              1)SIGINT接收到(Ctrl-C) 
              2)被另一同名節點踢出ROS網絡 
              3)ros::shutdown()被程序的另一部分調用 
              4) 所有的ros::NodeHandles都已經被銷毀 
  */

  while (ros::ok())     
  {
    /**
     * This is a message object. You stuff it with data, and then publish it.
     */
    //我們使用一個由msg file文件產生的‘消息自適應’類在ROS網絡中廣播消息。現在我們使用標准的String消息,它只有一個數據成員"data"。當然你也可以發布更復雜的消息類型。 
    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);    //現在我們已經向所有連接到chatter topic的節點發送了消息

    ros::spinOnce();             //在這個例子中並不是一定要調用ros::spinOnce(),因為我們不接受回調。
                                 //然而,如果你想拓展這個程序,卻又沒有在這調用ros::spinOnce(),你的回調函數就永遠也不會被調用。所以,在這里最好還是加上這一語句。 

    loop_rate.sleep();           //這條語句是調用ros::Rate對象來休眠一段時間以使得發布頻率為10hz。 

    ++count;
  }


  return 0;
}

 

 

二、編寫訂閱器節點

在src文件中,繼續寫入listener.cpp文件,幷附上代碼

#include "ros/ros.h"
#include "std_msgs/String.h"

/**
 * This tutorial demonstrates simple receipt of messages over the ROS system.
 * 這是一個回調函數,當消息到達chatter topic的時候就會被調用。消息是以 boost shared_ptr指針的形式傳輸,這就意味着你可以存儲它而又不需要復制數據 
 */
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.
   */

   //告訴master我們要訂閱chatter topic上的消息。當有消息到達topic時,ROS就會調用chatterCallback()函數。
   //第二個參數是隊列大小,以防我們處理消息的速度不夠快,在緩存了1000個消息后,再有新的消息到來就將開始丟棄先前接收的消息。 
   //NodeHandle::subscribe()返回ros::Subscriber對象,你必須讓它處於活動狀態直到你不再想訂閱該消息。當這個對象銷毀時,它將自動退訂消息。 
   //有各種不同的NodeHandle::subscribe()函數,允許你指定類的成員函數,甚至是Boost.Function對象可以調用的任何數據類型。roscpp overview 提供了更為詳盡的信息。 
   

   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()進入自循環,可以盡可能快的調用消息回調函數。如果沒有消息到達,它不會占用很多CPU,所以不用擔心。
  //一旦ros::ok()返回FALSE,ros::spin()就會立刻跳出自循環。
  //   1)這有可能是ros::shutdown()被調用,
  //   2)或者是用戶按下了Ctrl-C,使得master告訴節點要shutdown。也有可能是節點被人為的關閉。


  ros::spin();

  return 0;
}

 

三、編譯節點

1、在beginner_tutorials 包中的CMakeLists.txt 的末尾加入

## 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 space目錄,具體是在~/catkin_ws/devel/lib/<package name>.

 

2、編譯

# In your catkin workspace
catkin_make  

 

 

四、測試消息發布器和訂閱器

1、運行roscore

2、加入到ROS工作空間中

# In your catkin workspace
cd ~/catkin_ws
source ./devel/setup.bash

 

 

3、運行talker節點

rosrun beginner_tutorials talker

rosrun beginner_tutorials listener

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM