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代碼

#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>

int main(int argc, char **argv){
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
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 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代碼

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

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::NodeHandle n;
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 代碼解釋說明
之前解釋過的代碼就不再多說明了。

void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}

1
2
3
4

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

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

 

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

ros::spin();

 

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 內容如下:

cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)

add_message_files(DIRECTORY msg FILES Num.msg)
add_service_files(DIRECTORY srv FILES AddTwoInts.srv)

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)

include_directories(
${catkin_INCLUDE_DIRS}
)

 

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

# In your catkin workspace
$ catkin_make

 

4. 運行測試
確保roscore可用,在一個窗口中並運行:

$ roscore

 

新開一個窗口運行:

$ rosrun beginner_tutorials talker

[ INFO] [1495039634.704443313]: hello world 0
[ INFO] [1495039634.805231417]: hello world 1
[ INFO] [1495039634.905569506]: hello world 2
[ INFO] [1495039635.004821578]: hello world 3
[ INFO] [1495039635.105197141]: hello world 4
[ INFO] [1495039635.205296358]: hello world 5
[ INFO] [1495039635.304792273]: hello world 6
[ INFO] [1495039635.404978344]: hello world 7
[ INFO] [1495039635.504606982]: hello world 8
[ INFO] [1495039635.605256801]: hello world 9
[ INFO] [1495039635.704843209]: hello world 10
[ INFO] [1495039635.805375149]: hello world 11
[ INFO] [1495039635.904738599]: hello world 12
[ INFO] [1495039636.005312713]: hello world 13
[ INFO] [1495039636.104750013]: hello world 14

 

新開另一個窗口運行:

$ rosrun beginner_tutorials listener
[ INFO] [1495039700.814505519]: I heard: [hello world 28]
[ INFO] [1495039700.914675743]: I heard: [hello world 29]
[ INFO] [1495039701.014886681]: I heard: [hello world 30]
[ INFO] [1495039701.115726218]: I heard: [hello world 31]
[ INFO] [1495039701.214178230]: I heard: [hello world 32]
[ INFO] [1495039701.315041511]: I heard: [hello world 33]
[ INFO] [1495039701.414082857]: I heard: [hello world 34]
[ INFO] [1495039701.514349885]: I heard: [hello world 35]
[ INFO] [1495039701.615054193]: I heard: [hello world 36]
[ INFO] [1495039701.714480492]: I heard: [hello world 37]
[ INFO] [1495039701.814043800]: I heard: [hello world 38]
[ INFO] [1495039701.914712204]: I heard: [hello world 39]
[ INFO] [1495039702.015251383]: I heard: [hello world 40]
[ INFO] [1495039702.114955129]: I heard: [hello world 41]

已經完了消息的發布器和訂閱器,下面將來編寫一個服務和客戶端 (c++).


原文鏈接:https://blog.csdn.net/u010510350/article/details/72455970/


免責聲明!

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



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