消息釋出器和訂閱器 (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++).
作者:柒月