Ros學習——C++釋出器publisher和訂閱器subscriber
1.編寫釋出器
- 初始化 ROS 系統
-
在 ROS 網路內廣播我們將要在 chatter 話題上釋出 std_msgs/String 型別的訊息
- 以每秒 10 次的頻率在 chatter 上釋出訊息
在 beginner_tutorials package 裡建立 src/talker.cpp 檔案:
#include "ros/ros.h" //一個實用的標頭檔案,它引用了 ROS 系統中大部分常用的標頭檔案 #include "std_msgs/String.h" //引用了 std_msgs/String 訊息, 它存放在std_msgs package 裡,是由 String.msg 檔案自動生成的標頭檔案。 #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"); //初始化 ROS.可指定節點的名稱。節點的名稱必須唯一(名稱內不能包含 / 等符號) /** * 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; //為這個程序的節點建立一個控制代碼。 //第一個建立的 NodeHandle 會為節點進行初始化,最後一個銷燬的 NodeHandle 則會釋放該節點所佔用的所有資源 /** * 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);//告訴master 我們將要在 chatter(話題名) 上釋出 std_msgs/String 訊息型別的訊息。 //如果我們釋出的訊息的頻率太高,緩衝區中的訊息在大於 1000 個的時候就會開始丟棄先前釋出的訊息。
//advertise返回一個 ros::Publisher 物件,它有兩個作用: 1) 它有一個 publish() 成員函式可以讓你在topic上釋出訊息; 2) 如果訊息型別不對,它會拒絕釋出。
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()) //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());//ROS_INFO 和其他類似的函式可以用來代替 printf/cout 等函式。 /** * 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);//向所有訂閱 chatter 話題的節點發送訊息。 ros::spinOnce(); //ros::spinOnce()這一語句,否則你的回撥函式就永遠也不會被呼叫 loop_rate.sleep(); //呼叫 ros::Rate 物件來休眠一段時間以使得釋出頻率為 10Hz。 ++count; } return 0; }
2.編寫訂閱器
- 初始化ROS系統
-
訂閱 chatter 話題
- 進入自迴圈,等待訊息的到達
-
當訊息到達,呼叫 chatterCallback() 函式
在 beginner_tutorials package 目錄下建立 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)//是一個回撥函式,當接收到 chatter 話題的時候就會被呼叫。 { 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);
//告訴 master 我們要訂閱 chatter 話題上的訊息。當有訊息釋出到這個話題時,ROS 就會呼叫 chatterCallback() 函式。第二個引數是佇列大小,以防我們處理訊息的速度不夠快,當快取達到 1000 條訊息後,再有新的訊息到來就將開始丟棄先前接收的訊息。
/** * 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();//ros::spin() 進入自迴圈,可以儘可能快的呼叫訊息回撥函式。
return 0;
}
3.編譯
- 1.確認CMakeLists.txt檔案
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(DIRECTORY msg FILES Num.msg) add_service_files(DIRECTORY srv FILES AddTwoInts.srv) ## Generate added messages and services generate_messages(DEPENDENCIES std_msgs) ## Declare a catkin package catkin_package(
- 2.在 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)
- 1-2會生成兩個可執行檔案, talker 和 listener, 預設儲存到 devel space 目錄下,具體在~/catkin_ws/devel/lib/<package name> 中.
- 3.如果在 *Groovy* 版本下,你可以使用下邊的這個變數來新增對所有必須的檔案依賴:
add_dependencies(talker ${catkin_EXPORTED_TARGETS})
- 4.執行 catkin_make
問題:
1.當你在package.xml中,新增完run_depend後,編譯出錯,顯示The manifest must not cotain the following tags:run_depend。
原因:軟體包格式分兩種,第一種的四種依賴關係分別是:
<buildtool_depend> <build_depend> <run_depend> <test_depend>
第二種的依賴關係變成:
<buildtool_depend> <build_depend> <build_export_depend> <exec_depend> <test_depend> <doc_depend>
解決:當你run_depend出錯時,有可能是你的軟體包格式是第二種,所以要把run_depend改成exec_depend
2.invoking "make cmake_check_build_system" failed
仔細檢查:
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(DIRECTORY msg FILES Num.msg) add_service_files(DIRECTORY srv FILES AddTwoInts.srv) ## Generate added messages and services generate_messages(DEPENDENCIES std_msgs) ## Declare a catkin package catkin_package() #尤其要注意,裡面為空否則報上面的錯