上一篇研究了ubuntu系統下ROS環境的安裝與配置,接下來進入真正的實戰,開始擼碼,按照慣例通常都是從hello wrold開始。node
在寫代碼以前咱們先熟悉一下ROS中的幾個經常使用概念:c++
一、Node(節點):執行一些運算任務的進程,數據傳輸和處理的程序,能夠理解爲C++或者Python程序主函數所在的程序。web
二、Message(消息):節點之間是經過傳送消息進行通信的,每個消息都是一個嚴格的數據結構。即支持標準的數據類型(整型,浮點型,布爾型等等)和原始數組類型。也能夠包含任意嵌套的結構和數組(相似於C語言的結構structs)。好比GPS採集到位置消息,溫度計採集到的溫度等,任何數據都能做爲message。ubuntu
三、Topic(主題):消息是以一種發佈/訂閱的方式進行傳遞的。一個節點能夠在一個給定的主題中發佈消息。另外的節點針對某個主題關注與訂閱特定類型的數據。能夠同時有多個節點發布或者訂閱同一個主題的消息。數組
四、Service(服務):雖然基於話題的發佈/訂閱模型是很靈活的通信模式,可是它廣播式的路徑規劃對於能夠簡化節點設計的同步傳輸模式並不適合。在ROS中,咱們稱之爲一個服務,用一個字符串和一對嚴格規範的消息定義:一個用於請求,一個用於迴應。這相似於web服務器,web服務器是由URIs定義的,同時帶有完整定義類型的請求和回覆文檔。須要注意的是,不像話題,只有一個節點能夠以任意獨有的名字廣播一個服務:只有一個服務能夠稱之爲「分類象徵」,好比說,任意一個給出的URI地址只能有一個web服務器。bash
五、Discovery(發現):ROS2裏是沒有master的,那麼各節點之間是怎麼知道彼此存在的呢?這就靠一種自動發現的機制——Discovery。什麼意思呢?簡單來講,當一個節點啓動後,首先在網絡中發條廣播,大聲告訴這個世界我來了,其餘節點聽到以後,也紛紛反饋各自的信息,這樣一來二去也就聯繫上了。固然,萬一哪一個節點掉線了怎們辦?不要緊,每一個節點都會週期性的發佈廣播,告訴其餘節點他還在線,就算要下線,他也會廣播告訴其餘節點他要走了,下次再聊。總之,發現(Discovery)能夠理解爲一種身份聲明和響應的機制。服務器
六、Package(包):ROS的軟件以包的方式組織起來。包包含節點、ROS依賴庫、數據套、配置文件、第三方軟件、或者任何其餘邏輯構成。包的目標是提供一種易於使用的結構以便於軟件的重複使用。網絡
七、Stack(堆):堆是包的集合,它提供一個完整的功能,像「navigation stack」。Stack與版本號關聯,同時也是如何發行ROS軟件方式的關鍵。數據結構
ROS是一種分佈式處理框架。這使可執行文件能被單獨設計,而且在運行時鬆散耦合。這些過程能夠封裝到包(Packages)和堆(Stacks)中,以便於共享和分發。app
第一個ROS程序
如下代碼採用官網的例子,官網地址
1:建立workspace,打開一個terminal, 依次輸入下面命令:
1 source /opt/ros/melodic/setup.bash #進入ros環境,我這裏是melodic版本,若是使用其餘版本就把這個名稱替換掉 2 mkdir -p ~/catkin_ws/src #建立ros工做空間,我這裏按照官網叫catkin_ws,這個空間的名字能夠隨便取,可是src目錄必須得有,由於是用來存放源程序的
3 cd ~/catkin_ws/ #進入ros工做空間
4 catkin_make #進行編譯,編譯成功的話會發現多了build和devel兩個文件夾
創建好工做空間以後開始建立第一個ros項目,繼續在terminal中輸入下面的命令:
1 cd ~/catkin_ws/src #進入src目錄 2 catkin_create_pkg pub_sub_test std_msgs rospy roscpp #執行ros命令,建立消息包 3 cd .. #進入工做空間 4 catkin_make #進行編譯
2:發佈消息程序的代碼(發佈器),繼續在terminal中輸入下面的命令:
1 cd ~/catkin_ws/src/pub_sub_test/src #進入src文件夾 2 touch pub_string.cpp #建立一個pub_string的c++文件
3:打開pub_string.cpp文件,裏面的內容以下:
1 #include "ros/ros.h" 2 #include "std_msgs/String.h" 3 4 #include <sstream> 5 6 /** 7 * This tutorial demonstrates simple sending of messages over the ROS system. 8 */ 9 int main(int argc, char **argv) 10 { 11 /** 12 * The ros::init() function needs to see argc and argv so that it can perform 13 * any ROS arguments and name remapping that were provided at the command line. For programmatic 14 * remappings you can use a different version of init() which takes remappings 15 * directly, but for most command-line programs, passing argc and argv is the easiest 16 * way to do it. The third argument to init() is the name of the node. 17 * 18 * You must call one of the versions of ros::init() before using any other 19 * part of the ROS system. 20 */ 21 ros::init(argc, argv, "talker"); 22 23 /** 24 * NodeHandle is the main access point to communications with the ROS system. 25 * The first NodeHandle constructed will fully initialize this node, and the last 26 * NodeHandle destructed will close down the node. 27 */ 28 ros::NodeHandle n; 29 30 /** 31 * The advertise() function is how you tell ROS that you want to 32 * publish on a given topic name. This invokes a call to the ROS 33 * master node, which keeps a registry of who is publishing and who 34 * is subscribing. After this advertise() call is made, the master 35 * node will notify anyone who is trying to subscribe to this topic name, 36 * and they will in turn negotiate a peer-to-peer connection with this 37 * node. advertise() returns a Publisher object which allows you to 38 * publish messages on that topic through a call to publish(). Once 39 * all copies of the returned Publisher object are destroyed, the topic 40 * will be automatically unadvertised. 41 * 42 * The second parameter to advertise() is the size of the message queue 43 * used for publishing messages. If messages are published more quickly 44 * than we can send them, the number here specifies how many messages to 45 * buffer up before throwing some away. 46 */ 47 ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); 48 49 ros::Rate loop_rate(10); 50 51 /** 52 * A count of how many messages we have sent. This is used to create 53 * a unique string for each message. 54 */ 55 int count = 0; 56 while (ros::ok()) 57 { 58 /** 59 * This is a message object. You stuff it with data, and then publish it. 60 */ 61 std_msgs::String msg; 62 63 std::stringstream ss; 64 ss << "hello world " << count; 65 msg.data = ss.str(); 66 67 ROS_INFO("%s", msg.data.c_str()); 68 69 /** 70 * The publish() function is how you send messages. The parameter 71 * is the message object. The type of this object must agree with the type 72 * given as a template parameter to the advertise<>() call, as was done 73 * in the constructor above. 74 */ 75 chatter_pub.publish(msg); 76 77 ros::spinOnce(); 78 79 loop_rate.sleep(); 80 ++count; 81 } 82 83 84 return 0; 85 }
以上代碼簡單來講就是循環發佈hello world消息,直到按下Ctrl+C退出程序,具體能夠看裏面的官方註釋。
4:接收消息的程序(接收器),上面完成了發佈器,接下來開始寫接收器。打開terminal,繼續輸入下面的命令:
1 cd ~/catkin_ws/src/pub_sub_test/src #進入src文件夾 2 touch sub_string.cpp #建立一個sub_string的c++文件
打開sub_string.cpp咱們把下面的代碼複製進去
1 #include "ros/ros.h" 2 #include "std_msgs/String.h" 3 4 /** 5 * This tutorial demonstrates simple receipt of messages over the ROS system. 6 */ 7 void chatterCallback(const std_msgs::String::ConstPtr& msg) 8 { 9 ROS_INFO("I heard: [%s]", msg->data.c_str()); 10 } 11 12 int main(int argc, char **argv) 13 { 14 /** 15 * The ros::init() function needs to see argc and argv so that it can perform 16 * any ROS arguments and name remapping that were provided at the command line. For programmatic 17 * remappings you can use a different version of init() which takes remappings 18 * directly, but for most command-line programs, passing argc and argv is the easiest 19 * way to do it. The third argument to init() is the name of the node. 20 * 21 * You must call one of the versions of ros::init() before using any other 22 * part of the ROS system. 23 */ 24 ros::init(argc, argv, "listener"); 25 26 /** 27 * NodeHandle is the main access point to communications with the ROS system. 28 * The first NodeHandle constructed will fully initialize this node, and the last 29 * NodeHandle destructed will close down the node. 30 */ 31 ros::NodeHandle n; 32 33 /** 34 * The subscribe() call is how you tell ROS that you want to receive messages 35 * on a given topic. This invokes a call to the ROS 36 * master node, which keeps a registry of who is publishing and who 37 * is subscribing. Messages are passed to a callback function, here 38 * called chatterCallback. subscribe() returns a Subscriber object that you 39 * must hold on to until you want to unsubscribe. When all copies of the Subscriber 40 * object go out of scope, this callback will automatically be unsubscribed from 41 * this topic. 42 * 43 * The second parameter to the subscribe() function is the size of the message 44 * queue. If messages are arriving faster than they are being processed, this 45 * is the number of messages that will be buffered up before beginning to throw 46 * away the oldest ones. 47 */ 48 ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback); 49 50 /** 51 * ros::spin() will enter a loop, pumping callbacks. With this version, all 52 * callbacks will be called from within this thread (the main one). ros::spin() 53 * will exit when Ctrl-C is pressed, or the node is shutdown by the master. 54 */ 55 ros::spin(); 56 57 return 0; 58 }
以上代碼簡單來講就是接收發布器發佈的hello world消息,並打印在終端上,具體能夠看裏面的官方註釋。
5:編譯ros程序,打開terminal,繼續輸入下面的命令:
1 cd ~/catkin_ws/src/pub_sub_test/ 2 gedit CMakeLists.txt
用gedit打開CMakeLists.txt文件,而後找到##Declare a C++ executable,在這一行的前面或者後面添加以下內容:
1 add_executable(pub_string src/pub_string.cpp) 2 target_link_libraries(pub_string ${catkin_LIBRARIES}) 3 add_executable(sub_string src/sub_string.cpp) 4 target_link_libraries(sub_string ${catkin_LIBRARIES})
添加完以上內容後咱們保存並退出CMakeLists.txt文件。而後在terminal中繼續輸入以下命令:
1 cd ~/catkin_ws/ 2 catkin_make
不出意外的話編譯成功,接下來就能夠執行你的第一個ROS demo了。
6:執行ROS程序
一切順利的話接下來就是見證奇蹟的時候了,咱們先打開三個terminal,在第一個terminal中輸入以下命令:
1 roscore
第二個terminal中輸入以下命令:
1 cd ~/catkin_ws/ 2 source devel/setup.bash 3 rosrun pub_sub_test sub_string
第三個terminal中輸入以下命令:
cd ~/catkin_ws/ source devel/setup.bash rosrun pub_sub_test pub_string
這個時候應該會在第二個和第三個terminal中看到以下內容,其中左邊的是消息發佈器,右邊的是消息接收器,兩個都是循環的,若是要退出的話按下Ctr+C便可。
在退出以前咱們能夠再打開一個terminal,輸入下面的內容:
1 source ~/catkin_ws/devel/setup.bash 2 rqt_graph
這時應該能夠看到以下圖片:
talker
就是發佈信息的程序中取的節點的名字,
listener
就是接收信息的程序中取的節點的名字,
chatter
就是你在程序中取的topic的名字.這副圖傳遞的信息是
talker
經過topicchatter
向節點listener
發佈消息
rqt_graph
命令簡潔地表現除了node和node之間的關係,當之後有不少節點、不少topic時,能夠經過查看這個圖像理清它們之間的關係。
至此ROS的消息發佈器和訂閱器的demo已經完成。