ROS學(xué)習(xí)--7.寫一個(gè)發(fā)布與訂閱節(jié)點(diǎn)

1.寫一個(gè)publisher節(jié)點(diǎn)

“節(jié)點(diǎn)”是連接到ROS網(wǎng)絡(luò)的可執(zhí)行文件的ROS術(shù)語。在這里,我們將創(chuàng)建一個(gè)發(fā)布者(“ talker”)節(jié)點(diǎn),該節(jié)點(diǎn)將不斷廣播消息。

1.1 將目錄更改為在catkin工作區(qū)以前的教程中創(chuàng)建的beginner_tutorials包:

roscd beginner_tutorials

1.2 在beginner_tutorials包目錄中創(chuàng)建一個(gè)src目錄:

mkdir -p src

該目錄將包含我們beginner_tutorials包的所有源文件。

1.3 在beginner_tutorials包中創(chuàng)建src / talker.cpp文件,并將以下內(nèi)容粘貼到其中:

// ros/ros.h是一個(gè)方便的工具,其中包括使用ROS系統(tǒng)最常見的公共部分所需的所有標(biāo)頭。
#include "ros/ros.h"

// 這包括std_msgs / String消息,該消息駐留在std_msgs包中。
// 這是從該程序包中的String.msg文件自動(dòng)生成的標(biāo)頭。有關(guān)消息定義的更多信息,請(qǐng)參見msg頁面。
#include "std_msgs/String.h"

#include <sstream>

/**
* 本教程演示了如何通過ROS系統(tǒng)簡單地發(fā)送消息。
*/

//        這是下面事情的精簡版:
//          初始化ROS系統(tǒng)
//          宣傳我們將在chatter主題上向主服務(wù)器發(fā)布std_msgs / String消息
//          在將消息發(fā)布到聊天時(shí)每秒循環(huán)10次

int main(int argc, char** argv)
{
//    初始化ROS。這使ROS可以通過命令行進(jìn)行名稱重映射-目前尚不重要。
//    這也是我們指定節(jié)點(diǎn)名稱的地方。節(jié)點(diǎn)名稱在運(yùn)行的系統(tǒng)中必須唯一。
    ros::init(argc, argv, "talker");
    
//    創(chuàng)建此進(jìn)程的節(jié)點(diǎn)的句柄。創(chuàng)建的第一個(gè)NodeHandle實(shí)際上將對(duì)節(jié)點(diǎn)進(jìn)行初始化,
//    而最后一個(gè)被破壞的節(jié)點(diǎn)將清理該節(jié)點(diǎn)正在使用的所有資源。
    ros::NodeHandle node_handle;
    
//    告訴管理員,我們將在主題討論中發(fā)布std_msgs / String類型的消息。
//    這樣,主服務(wù)器就可以告訴所有正在聊天的節(jié)點(diǎn),我們將發(fā)布有關(guān)該主題的數(shù)據(jù)。
//    第二個(gè)參數(shù)是發(fā)布隊(duì)列的大小。在這種情況下,如果我們發(fā)布得太快,
//    它將最多緩沖1000條消息,然后再開始丟棄舊消息。

//    NodeHandle :: advertise()返回一個(gè)ros :: Publisher對(duì)象,
//    該對(duì)象有兩個(gè)作用:
//    1)它包含一個(gè)publish()方法,可用于將消息發(fā)布到其創(chuàng)建的主題上;
//    2)當(dāng)它超出范圍時(shí),它將自動(dòng)取消廣告。
    ros::Publisher chatter_pub = node_handle.advertise<std_msgs::String>("chatter", 100);

//    ros :: Rate對(duì)象允許您指定要循環(huán)播放的頻率。
//    它將跟蹤自上次調(diào)用Rate::sleep()以來已等待了多長時(shí)間,并睡眠了正確的時(shí)間。
//    在這種情況下,我們告訴我們要以10Hz運(yùn)行。
    ros::Rate loop_rate(10);

    int count = 0;
    
//    默認(rèn)情況下,roscpp將安裝SIGINT處理程序,
//    該處理程序提供Ctrl-C處理,如果發(fā)生這種情況,
//    將導(dǎo)致ros :: ok()返回false。

//    ros :: ok()將在以下情況下返回false:
//      收到SIGINT(Ctrl-C)
//      我們已經(jīng)被另一個(gè)同名節(jié)點(diǎn)踢出網(wǎng)絡(luò)
//      ros :: shutdown()已由應(yīng)用程序的另一部分調(diào)用。
//      所有ros :: NodeHandles已被銷毀
//    一旦ros :: ok()返回false,所有ROS調(diào)用都會(huì)失敗。
    while (ros::ok())
    {
//        我們使用消息自適應(yīng)類在ROS上廣播消息,該類通常是從msg文件生成的。
//        可能有更復(fù)雜的數(shù)據(jù)類型,但是現(xiàn)在我們將使用標(biāo)準(zhǔn)的String消息,
//        該消息具有一個(gè)成員:“ data”。
        std_msgs::String msg;
        std::stringstream ss;

        ss << "hello world" << count;
        msg.data = ss.str();

//        ROS_INFO可以代替printf / cout。有關(guān)更多信息,請(qǐng)參見rosconsole文檔。
        ROS_INFO("%s", msg.data.c_str());

//        現(xiàn)在,我們實(shí)際上將消息廣播給了已連接的任何人。
        chatter_pub.publish(msg);

//        對(duì)于這個(gè)簡單的程序,不需要在此處調(diào)用ros :: spinOnce(),
//        因?yàn)槲覀儧]有收到任何回調(diào)。但是,如果您要向此應(yīng)用程序添加訂閱,
//        并且在此處沒有ros :: spinOnce(),則將永遠(yuǎn)不會(huì)調(diào)用您的回調(diào)。
//        因此,請(qǐng)將其添加為良好的度量。
        ros::spinOnce();

//        現(xiàn)在,我們使用ros :: Rate對(duì)象休眠剩余時(shí)間,使我們達(dá)到10Hz的發(fā)布速率。
        loop_rate.sleep();
        ++count;
    }
    return 0;
}

2.寫一個(gè)subscriber節(jié)點(diǎn)

  • 在beginner_tutorials包中創(chuàng)建src / listener.cpp文件,并將以下內(nèi)容粘貼到其中:
// ros/ros.h是一個(gè)方便的工具,其中包括使用ROS系統(tǒng)最常見的公共部分所需的所有標(biāo)頭。
#include "ros/ros.h"

// 這包括std_msgs / String消息,該消息駐留在std_msgs包中。
// 這是從該程序包中的String.msg文件自動(dòng)生成的標(biāo)頭。有關(guān)消息定義的更多信息,請(qǐng)參見msg頁面。
#include "std_msgs/String.h"

/**
本教程演示了通過ROS系統(tǒng)簡單接收消息的過程。
*/

//當(dāng)在chatter主題上收到新消息時(shí),將調(diào)用此回調(diào)函數(shù)。
// 該消息在boost shared_ptr中傳遞,
// 這意味著您可以根據(jù)需要將其存儲(chǔ)起來,
// 而不必?fù)?dān)心它下面的內(nèi)容被刪除,也無需復(fù)制基礎(chǔ)數(shù)據(jù)。
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
    ROS_INFO("I heard: [%s]", msg->data.c_str());
}

//  同樣,這是正在發(fā)生的事情的精簡版:
//      初始化ROS系統(tǒng)
//      訂閱聊天主題
//      旋轉(zhuǎn),等待消息到達(dá)
//      消息到達(dá)時(shí),將調(diào)用chatterCallback()函數(shù)

int main(int argc, char** argv)
{
//    初始化ROS。這使ROS可以通過命令行進(jìn)行名稱重映射-目前尚不重要。
//    這也是我們指定節(jié)點(diǎn)名稱的地方。節(jié)點(diǎn)名稱在運(yùn)行的系統(tǒng)中必須唯一。
    ros::init(argc, argv, "listener");
    
//    創(chuàng)建此進(jìn)程的節(jié)點(diǎn)的句柄。創(chuàng)建的第一個(gè)NodeHandle實(shí)際上將對(duì)節(jié)點(diǎn)進(jìn)行初始化,
//    而最后一個(gè)被破壞的節(jié)點(diǎn)將清理該節(jié)點(diǎn)正在使用的所有資源。
    ros::NodeHandle node_handle;
    
//    訂閱與主機(jī)的聊天主題。每當(dāng)有新消息到達(dá)時(shí),ROS就會(huì)調(diào)用chatterCallback()函數(shù)。
//    第二個(gè)參數(shù)是隊(duì)列大小,以防萬一我們不能足夠快地處理消息。
//    在這種情況下,如果隊(duì)列達(dá)到1000條消息,我們將在新消息到達(dá)時(shí)開始丟棄舊消息。

//    NodeHandle :: subscribe()返回ros :: Subscriber對(duì)象,
//    您必須保留該對(duì)象直到要取消訂閱為止。破壞Subscriber對(duì)象后,
//    它將自動(dòng)取消訂閱chatter主題。

//    NodeHandle :: subscribe()函數(shù)有多種版本,
//    可讓您指定類成員函數(shù),甚至可以指定Boost.Function對(duì)象可以調(diào)用的任何函數(shù)。
    ros::Subscriber sub = node_handle.subscribe("chatter", 100, chatterCallback);

//    ros :: spin()進(jìn)入循環(huán),并盡可能快地調(diào)用消息回調(diào)。
//    不過,請(qǐng)不要擔(dān)心,如果沒有什么可做的,則不會(huì)占用太多CPU。
//    一旦ros :: ok()返回false,ros :: spin()將退出,
//    這意味著ros :: shutdown()已被默認(rèn)Ctrl-C處理程序,
//    主程序告訴我們關(guān)閉或被調(diào)用了手動(dòng)。
    ros::spin();
    
    return 0;
}

3.編譯節(jié)點(diǎn)

您在上一教程中使用過catkin_create_pkg,該教程為您創(chuàng)建了package.xml和CMakeLists.txt文件。 生成的CMakeLists.txt應(yīng)該如下所示(對(duì)“創(chuàng)建Msgs和Srvs”教程進(jìn)行了修改,并刪除了未使用的注釋和示例):

   1 cmake_minimum_required(VERSION 2.8.3)
   2 project(beginner_tutorials)
   3 
   4 ## Find catkin and any catkin packages
   5 find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
   6 
   7 ## Declare ROS messages and services
   8 add_message_files(DIRECTORY msg FILES Num.msg)
   9 add_service_files(DIRECTORY srv FILES AddTwoInts.srv)
  10 
  11 ## Generate added messages and services
  12 generate_messages(DEPENDENCIES std_msgs)
  13 
  14 ## Declare a catkin package
  15 catkin_package()

不必?fù)?dān)心修改注釋的(#)示例,只需將以下幾行添加到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文件應(yīng)如下所示:
   1 cmake_minimum_required(VERSION 2.8.3)
   2 project(beginner_tutorials)
   3 
   4 ## Find catkin and any catkin packages
   5 find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
   6 
   7 ## Declare ROS messages and services
   8 add_message_files(FILES Num.msg)
   9 add_service_files(FILES AddTwoInts.srv)
  10 
  11 ## Generate added messages and services
  12 generate_messages(DEPENDENCIES std_msgs)
  13 
  14 ## Declare a catkin package
  15 catkin_package()
  16 
  17 ## Build talker and listener
  18 include_directories(include ${catkin_INCLUDE_DIRS})
  19 
  20 add_executable(talker src/talker.cpp)
  21 target_link_libraries(talker ${catkin_LIBRARIES})
  22 add_dependencies(talker beginner_tutorials_generate_messages_cpp)
  23 
  24 add_executable(listener src/listener.cpp)
  25 target_link_libraries(listener ${catkin_LIBRARIES})
  26 add_dependencies(listener beginner_tutorials_generate_messages_cpp)

這將創(chuàng)建兩個(gè)可執(zhí)行文件,talker和listener,默認(rèn)情況下它們將進(jìn)入開發(fā)空間的package目錄,默認(rèn)情況下位于?/ catkin_ws / devel / lib / <package name>。

  • 請(qǐng)注意,您必須將可執(zhí)行目標(biāo)的依賴項(xiàng)添加到消息生成目標(biāo):
target_link_libraries(talker ${catkin_LIBRARIES})

您可以直接調(diào)用可執(zhí)行文件,也可以使用rosrun調(diào)用它們。它們沒有放在'<prefix> / bin'中,因?yàn)樵趯④浖惭b到系統(tǒng)時(shí),這會(huì)污染PATH。

  • 現(xiàn)在運(yùn)行catkin_make編譯:
# In your catkin workspace
cd ~/catkin_ws
catkin_make  

4.測試你的節(jié)點(diǎn)(兩種方式)

  1. 打開三個(gè)終端分別運(yùn)行:
roscore
source ~/catkin_ws/devel/setup.bash
rosrun beginer_tutorials talker
source ~/catkin_ws/devel/setup.bash
rosrun beginer_tutorials listener
  1. 打開三個(gè)終端分別運(yùn)行:
roscore
~/catkin_ws/devel/lib/beginer_tutorials/talker
~/catkin_ws/devel/lib/beginer_tutorials/listener

最后結(jié)果:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容