天天看點

ROS總結——ROS消息釋出和訂閱

消息釋出器和訂閱器 (C++)

本部落格總結紹如何用 C++ 編寫消息釋出器節點和訂閱器節點。

1.編寫釋出器節點

節點(Node) 是指 ROS 網絡中可執行檔案。接下來,将會建立一個釋出器節點(“talker”),它将不斷的在 ROS 網絡中廣播消息。切換到之前建立的 beginner_tutorials package 路徑:

$ cd ~/catkin_ws/src/beginner_tutorials

在 beginner_tutorials package 路徑下建立一個src檔案夾,這個檔案夾将會用來放置 beginner_tutorials package 的所有源代碼:

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

1.1 src/talker.cpp代碼

1.2 代碼解釋說明

#include "ros/ros.h"

ros/ros.h 是一個實用的頭檔案,它引用了 ROS 系統中大部分常用的頭檔案。

#include "std_msgs/String.h"

引用了 std_msgs/String 消息, 它存放在 std_msgs package 裡,是由 String.msg 檔案自動生成的頭檔案。

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

初始化 ROS 。它允許 ROS 通過指令行進行名稱重映射——然而這并不是現在讨論的重點。在這裡,我們也可以指定節點的名稱——運作過程中,節點的名稱必須唯一。這裡的名稱必須是一個 base name ,也就是說,名稱内不能包含 / 等符号。

ros::NodeHandle n;

為這個程序的節點建立一個句柄。第一個建立的 NodeHandle 會為節點進行初始化,最後一個銷毀的 NodeHandle 則會釋放該節點所占用的所有資源。

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

告訴 master 将要在 chatter(話題名) 上釋出 std_msgs/String 消息類型的消息。這樣 master 就會告訴所有訂閱了 chatter 話題的節點,将要有資料釋出。第二個參數是釋出序列的大小。如果釋出的消息的頻率太高,緩沖區中的消息在大于 1000 個的時候就會開始丢棄先前釋出的消息。NodeHandle::advertise() 傳回一個 ros::Publisher 對象,它有兩個作用: 1) 它有一個 publish() 成員函數可以在topic上釋出消息; 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:

SIGINT 被觸發 (Ctrl-C)

被另一同名節點踢出 ROS 網絡

ros::shutdown() 被程式的另一部分調用

節點中的所有 ros::NodeHandles 都已經被銷毀

一旦 ros::ok() 傳回 false, 所有的 ROS 調用都會失效。

std_msgs::String msg;

std::stringstream ss;

ss << "hello world " << count;

msg.data = ss.str();

使用一個由 msg file 檔案産生的『消息自适應』類在 ROS 網絡中廣播消息。現在使用标準的String消息,它隻有一個資料成員 “data”。當然,也可以釋出更複雜的消息類型。

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

ROS_INFO 和其他類似的函數可以用來代替 printf/cout 等函數。

chatter_pub.publish(msg);

向所有訂閱 chatter 話題的節點發送消息。

ros::spinOnce();

在這個例子中并不是一定要調用 ros::spinOnce(),因為不接受回調。然而,如果程式裡包含其他回調函數,最好在這裡加上 ros::spinOnce()這一語句,否則回調函數就永遠也不會被調用了。

loop_rate.sleep();

這條語句是調用 ros::Rate 對象來休眠一段時間以使得釋出頻率為 10Hz。

對上邊消息釋出進行一下總結:

初始化 ROS 系統

在 ROS 網絡内廣播我們将要在 chatter 話題上釋出 std_msgs/String 類型的消息

以每秒 10 次的頻率在 chatter 上釋出消息

2. 編寫訂閱器節點

在 beginner_tutorials package 目錄下建立 src/listener.cpp 檔案,并粘貼如下代碼。

2.1 src/listener.cpp代碼

void chatterCallback(const std_msgs::String::ConstPtr& msg)

ROS_INFO("I heard: [%s]", msg->data.c_str());

}

int main(int argc, char **argv){

ros::init(argc, argv, "listener");

ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);

ros::spin();

return 0;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

2.2 代碼解釋說明

之前解釋過的代碼就不再多說明了。

這是一個回調函數,當接收到 chatter 話題的時候就會被調用。消息是以 boost shared_ptr 指針的形式傳輸,這就意味着你可以存儲它而又不需要複制資料。

告訴 master 要訂閱 chatter 話題上的消息。當有消息釋出到這個話題時,ROS 就會調用 chatterCallback() 函數。第二個參數是隊列大小,以防處理消息的速度不夠快,當緩存達到 1000 條消息後,再有新的消息到來就将開始丢棄先前接收的消息。

NodeHandle::subscribe() 傳回 ros::Subscriber 對象,讓它處于活動狀态直到不再想訂閱該消息。當這個對象銷毀時,它将自動退訂 chatter 話題的消息。

有各種不同的 NodeHandle::subscribe() 函數,可以指定類的成員函數,甚至是 Boost.Function 對象可以調用的任何資料類型。

ros::spin() 進入自循環,可以盡可能快的調用消息回調函數。如果沒有消息到達,它不會占用很多 CPU,是以不用擔心。一旦 ros::ok() 傳回 false,ros::spin() 就會立刻跳出自循環。這有可能是 ros::shutdown() 被調用,或者是使用者按下了 Ctrl-C,使得 master 告訴節點要終止運作。也有可能是節點被人為關閉的。ros::spin() 進入自循環,可以盡可能快的調用消息回調函數。如果沒有消息到達,它不會占用很多 CPU,是以不用擔心。一旦 ros::ok() 傳回 false,ros::spin() 就會立刻跳出自循環。這有可能是 ros::shutdown() 被調用,或者是使用者按下了 Ctrl-C,使得 master 告訴節點要終止運作。也有可能是節點被人為關閉的。

下邊,總結訂閱消息:

初始化ROS系統

訂閱 chatter 話題

進入自循環,等待消息的到達

當消息到達,調用 chatterCallback() 函數

3. 編譯節點

之前教程中使用 catkin_create_pkg 建立了 package.xml 和 CMakeLists.txt 檔案,CMakeLists.txt 内容如下:

在工作空間之下以下指令:

4. 運作測試

確定roscore可用,在一個視窗中并運作:

新開一個視窗運作:

新開另一個視窗運作:

已經完了消息的釋出器和訂閱器,下面将來編寫一個服務和用戶端 (c++).

作者:柒月