一、創建發布器節點
1 節點功能:
不斷的在ROS網絡中廣播消息
2 創建節點
(1)打開工作空間目錄
cd ~/catkin_ws/src/beginner_tutorials
(2)創建src文件夾
mkdir -p ~/catkin_ws/src/beginner_tutorials/src
(3)創建talkler.cpp文件,幷附上代碼
#include "ros/ros.h" // ros/ros.h是一個實用的頭文件,它引用了ROS系統中大部分常用的頭文件,使用它會使得編程很簡便。 #include "std_msgs/String.h" // 這引用了std_msgs/String 消息, 它存放在std_msgs package里,是由String.msg文件自動生成的頭文件。 // 需要更詳細的消息定義,參考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。它允許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. 為這個進程的節點創建一個句柄。第一個創建的NodeHandle會為節點進行初始化,最后一個銷毀的會清理節點使用的所有資源。 */ 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. */ /* 1、告訴master我們將要在chatter topic上發布一個std_msgs/String的消息。這樣master就會告訴所有訂閱了chatter topic的節點,將要有數據發布。 2、第二個參數是發布序列的大小。在這樣的情況下,如果我們發布的消息太快,緩沖區中的消息在大於1000個的時候就會開始丟棄先前發布的消息。 3、NodeHandle::advertise() 返回一個 ros::Publisher對象,它有兩個作用: 1) 它有一個publish()成員函數可以讓你在topic上發布消息; 2) 如果消息類型不對,它會拒絕發布。 */ ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); //ros::Rate對象可以允許你指定自循環的頻率。它會追蹤記錄自上一次調用Rate::sleep()后時間的流逝,並休眠直到一個頻率周期的時間。 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; /* roscpp會默認安裝一個SIGINT句柄,它負責處理Ctrl-C鍵盤操作——使得ros::ok()返回FALSE。 ros::ok()返回false,如果下列條件之一發生: 1)SIGINT接收到(Ctrl-C) 2)被另一同名節點踢出ROS網絡 3)ros::shutdown()被程序的另一部分調用 4) 所有的ros::NodeHandles都已經被銷毀 */ while (ros::ok()) { /** * This is a message object. You stuff it with data, and then publish it. */ //我們使用一個由msg file文件產生的‘消息自適應’類在ROS網絡中廣播消息。現在我們使用標准的String消息,它只有一個數據成員"data"。當然你也可以發布更復雜的消息類型。 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); //現在我們已經向所有連接到chatter topic的節點發送了消息 ros::spinOnce(); //在這個例子中並不是一定要調用ros::spinOnce(),因為我們不接受回調。 //然而,如果你想拓展這個程序,卻又沒有在這調用ros::spinOnce(),你的回調函數就永遠也不會被調用。所以,在這里最好還是加上這一語句。 loop_rate.sleep(); //這條語句是調用ros::Rate對象來休眠一段時間以使得發布頻率為10hz。 ++count; } return 0; }
二、編寫訂閱器節點
在src文件中,繼續寫入listener.cpp文件,幷附上代碼
#include "ros/ros.h" #include "std_msgs/String.h" /** * This tutorial demonstrates simple receipt of messages over the ROS system. * 這是一個回調函數,當消息到達chatter topic的時候就會被調用。消息是以 boost shared_ptr指針的形式傳輸,這就意味着你可以存儲它而又不需要復制數據 */ 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. */ //告訴master我們要訂閱chatter topic上的消息。當有消息到達topic時,ROS就會調用chatterCallback()函數。 //第二個參數是隊列大小,以防我們處理消息的速度不夠快,在緩存了1000個消息后,再有新的消息到來就將開始丟棄先前接收的消息。 //NodeHandle::subscribe()返回ros::Subscriber對象,你必須讓它處於活動狀態直到你不再想訂閱該消息。當這個對象銷毀時,它將自動退訂消息。 //有各種不同的NodeHandle::subscribe()函數,允許你指定類的成員函數,甚至是Boost.Function對象可以調用的任何數據類型。roscpp overview 提供了更為詳盡的信息。 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()進入自循環,可以盡可能快的調用消息回調函數。如果沒有消息到達,它不會占用很多CPU,所以不用擔心。 //一旦ros::ok()返回FALSE,ros::spin()就會立刻跳出自循環。 // 1)這有可能是ros::shutdown()被調用, // 2)或者是用戶按下了Ctrl-C,使得master告訴節點要shutdown。也有可能是節點被人為的關閉。 ros::spin(); return 0; }
三、編譯節點
1、在beginner_tutorials 包中的CMakeLists.txt 的末尾加入
## 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 和 listener, 默認存儲到devel space目錄,具體是在~/catkin_ws/devel/lib/<package name>中.
2、編譯
# In your catkin workspace
catkin_make
四、測試消息發布器和訂閱器
1、運行roscore
2、加入到ROS工作空間中
# In your catkin workspace cd ~/catkin_ws source ./devel/setup.bash
3、運行talker節點
rosrun beginner_tutorials talker
rosrun beginner_tutorials listener