(3)寫簡單釋出節點和訂閱節點
阿新 • • 發佈:2018-12-14
ROS與C++入門教程-寫簡單釋出節點和訂閱節點
說明:
- 介紹用C++編寫簡單釋出節點和訂閱節點
準備
- 第三節已經建立的基本的工作空間catkin_ws和教程包beginner_tutorials
編寫釋出節點
- 進入教程目錄,並新建源目錄和釋出節點talker.cpp(程式碼看參考github),實現釋出hello world
$ roscd beginner_tutorials
$ mkdir src
$ touch src/talker.cpp
$ vim src/talker.cpp
- 內容如下:
#include "ros/ros.h" #include "std_msgs/String.h" #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"); /** * 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 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. */ ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); 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; while (ros::ok()) { /** * This is a message object. You stuff it with data, and then publish it. */ 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); ros::spinOnce(); loop_rate.sleep(); ++count; } return 0; }
程式碼分析:
- 程式碼:
#include "ros/ros.h"
-
解釋:
- 匯入ROS系統包含核心公共標頭檔案
-
程式碼:
#include "std_msgs/String.h"
-
解釋:
- 匯入std_msgs/String訊息標頭檔案,這個是由std_msgs包的string.msg檔案自動生成。
- std_msgs這是標準的訊息包,可參考std_msgs ,更多的標準訊息定義,可參考msg
-
程式碼:
ros::init(argc, argv, "talker");
-
解釋:
- 初始化ROS,指定節點名稱為“talker”,節點名稱要保持唯一性。名稱定義參考
-
程式碼:
ros::NodeHandle n;
-
解釋:
- 例項化節點
-
程式碼:
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
-
解釋:
- 釋出一個訊息型別為std_msgs/String,命名為chatter的話題。
- 定義訊息佇列大小為1000,即超過1000條訊息之後,舊的訊息就會丟棄。
-
程式碼:
ros::Rate loop_rate(10);
-
解釋:
- 指定釋出訊息的頻率,這裡指10Hz,也即每秒10次
- 通過 Rate::sleep()來處理睡眠的時間來控制對應的釋出頻率。
-
程式碼:
int count = 0;
while (ros::ok())
{
-
解釋:
- 預設roscpp會植入一個SIGINT處理機制,當按下Ctrl-C,就會讓ros::ok()返回false,那迴圈就會結束。
- ros::ok() 返回false的幾種情況:
- SIGINT收到(Ctrl-C)訊號
- 另一個同名節點啟動,會先中止之前的同名節點
- ros::shutdown()被呼叫
- 所有的ros::NodeHandles被銷燬
-
程式碼:
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
-
解釋:
- 例項化訊息msg, 定義字串流“hello world”並賦值給ss, 最後轉成為字串賦值給msg.data
-
程式碼:
chatter_pub.publish(msg);
-
解釋:
- 實際釋出的函式
-
程式碼:
ROS_INFO("%s", msg.data.c_str());
-
解釋:
- 輸出除錯資訊,更多除錯相關參考Verbosity Levels, rosconsole
-
程式碼:
ros::spinOnce();
- 解釋:
- 不是必需的,但是保持增加這個呼叫,是好習慣。
- 如果程式裡也有訂閱話題,就必需,否則回撥函式不能起作用。
- 程式碼:
loop_rate.sleep();
- 解釋:
- 根據之前ros::Rate loop_rate(10)的定義來控制釋出話題的頻率。定義10即為每秒10次
編寫訂閱節點
- 進入beginner_tutorials,新建src/listener.cpp檔案,原始碼參考
cd beginner_tutorials
touch src/listener.cpp
vim src/listener.cpp
- 新增程式碼如下:
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
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.
*/
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();
return 0;
}
程式碼分析:
- 程式碼:
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
- 解釋:
- 定義回撥函式chatterCallback,當收到chatter話題的訊息就會呼叫這個函式。
- 訊息通過boost shared_ptr(共享指標)來傳遞。
- 但收到訊息,通過ROS_INFO函式顯示到終端
- 程式碼:
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
-
解釋:
- 定義訂閱節點,名稱為chatter,指定回撥函式chatterCallback
- 但收到訊息,則呼叫函式chatterCallback來處理。
- 引數1000,定義佇列大小,如果處理不夠快,超過1000,則丟棄舊的訊息。
-
程式碼:
ros::spin();
- 解釋:
- 呼叫此函式才真正開始進入迴圈處理,直到 ros::ok()返回false才停止。
構建節點
- 在之前使用catkin_create_pkg建立包beginner_tutorials,會得到 package.xml 和 CMakeLists.txt兩個檔案
- 增加如下程式碼到CMakeLists.txt:
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)
- 完整程式碼類似:
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
## Declare ROS messages and services
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)
## Generate added messages and services
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)
- 建立兩個執行檔案, talker and listener,預設在
~/catkin_ws/devel/lib/<package name>
- 注意,您必須為訊息生成目標新增可執行目標的依賴項:
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
- 這確保此包的訊息頭在使用前生成。如果你使用的資訊在你的catkin工作空間的其他包裡面,你需要新增依賴各自生成目標,因為catkin會平行構建的所有專案。
target_link_libraries(talker ${catkin_LIBRARIES})
- 進入工作空間編譯
$ cd ~/catkin_ws
$ catkin_make
執行節點
- 新開終端執行:
$ roscore
- 新開終端執行talker:
source ~/catkin_ws/devel/setup.bash
rosrun beginner_tutorials talker
- 新開終端執行listener:
source ~/catkin_ws/devel/setup.bash
rosrun beginner_tutorials listener
- talker終端效果:
[ INFO] I published [Hello there! This is message [0]]
[ INFO] I published [Hello there! This is message [1]]
[ INFO] I published [Hello there! This is message [2]]
- listener終端效果:
[ INFO] Received [Hello there! This is message [20]]
[ INFO] Received [Hello there! This is message [21]]
[ INFO] Received [Hello there! This is message [22]]