ubuntu16.04下ROS作業系統學習(四 / 二)ROS基礎-ROS通訊程式設計
1.話題程式設計
首先我們要有一個釋出話題的Talker,還要有一個訂閱話題的Listener,然後就是負責管理整個系統的ROS Master。
話題程式設計的流程主要是以下四個步驟:
- 建立釋出者
- 建立訂閱者
- 新增編譯選項
- 執行可執行程式
上面的前兩步是程式設計實現的,第三步是通過編譯的方式生成可執行檔案。最後一步就是去執行一下這個可執行的程式。
我們之前建立的功能包learning_communication裡面是沒有程式碼的:
然後在這個檔案下面建立一個talker.cpp檔案
裡面的程式碼解釋也非常清晰。通過控制代碼.advertise釋出訊息,裡面需要傳入釋出訊息的具體型別,以及佇列的長度。
程式碼的流程如下:
- 初始化ROS節點
- 向ROS Master註冊節點資訊,包括髮布的話題名和話題中的訊息型別。
- 按照一定頻率迴圈釋出訊息。
有了釋出者之後,我們接下來需要去定義一個訂閱者,步驟如下:
- 初始化ROS節點
- 訂閱需要的話題
- 迴圈等待話題訊息,接收到訊息後進入回撥函式。
- 在回撥函式裡面完成訊息處理。
原始碼如下:
現在的話我們就已經寫好了c++檔案,我們需要對其進行編譯,如果我們使用的是python檔案的話,我們就不用對其進行編譯了。編譯程式碼主要有以下三個步驟:
- 設定需要編譯的程式碼和生成的可執行檔案;
- 設定連結庫;
- 設定依賴。
在上面這個資料夾下面的CMakeLists.txt就是我們具體需要編譯的選項的。接下來我們需要在這個檔案下面設定我們需要編譯的選項。在這個檔案裡面很多都是被註釋掉了,我們很多時候去掉其中的註釋然後再改一點就可以了,並不需要我們自己寫。將程式碼生成可執行檔案就是需要使用add_executable這個配置。
這裡我們在下面再貼上一下:
這裡我們只需要看未被註釋的,第一行的意思就是將talker.cpp生成可執行檔案。如果需要更多的檔案來生成可執行檔案的話,需要在後面再多加幾個c++檔案。
因為我們需要依賴第三方的庫,所以我們需要新增target_link_libraries來與第三方的庫做一個連結。之後我們回到工作空間對其進行編譯。
編譯成功之後就會有以下提示:顯示達到1005並且沒有報錯的話就說明編譯成功了。
我們在下面這個檔案裡面就可以找到我們編譯生成的可執行檔案了。
之後我們啟動roscore
再執行傳送“hello world”可執行檔案:
再執行接收檔案的話,就可以看到下面的結果:
如果我們需要自己定義話題訊息的話,我們可以採取以下方式自定義話題訊息:
string name
uint8 sex
unit8 age
unit8 unknown = 0
unit8 male = 1
unit8 female = 2
具體操作步驟如下:
- 定義msg檔案
- 在package.xml中新增功能包依賴
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
- 在CMakeList.txt新增編譯選項
接下來具體操作一下:
首先建立一個資料夾,命名為msg檔案
在這個資料夾下面建立一個具體的Person.msg檔案
之後我們在package.xml檔案裡面去新增依賴。部分的ROS版本中的exec_depend需要改成run_depend。
之後的話我們需要在CMakeLists.txt檔案裡面新增編譯選項,在下面新增message_generation功能包。
然後新增編譯時候的依賴:
接下來新增是哪一個具體的msg檔案:
之後對其進行編譯:
我們可以在程式碼終端裡面驗證一下:
2.服務程式設計
listener通過請求的形式來完成跟talker的一個通訊,talker把處理完成之後的資料處理完成之後再發布給listener,整個服務程式設計流程可以大致分為以下四個步驟:
- 建立伺服器
- 建立客戶端
- 新增編譯選項
- 執行可執行程式
假設如下場景,listener釋出某兩個加數給talker,talker接收到這兩個加數之後將這兩個加數進行相加,並且把求和的結果告訴listener。具體步驟如下:
- 定義srv檔案
- 在package.xml中新增功能包依賴
- 在CMakeLists.txt新增編譯選項
首先建立一個srv檔案:
然後在下面定義以下檔案:
中間的三條橫線將資料分成兩部分,上面的是服務的請求部分,下面的是服務的應答部分。
之後我們需要在pacaage.xml檔案中新增功能包依賴
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
這個和之前的talker-listener是一樣的,因為我們剛才新增好了,這裡就不用去做任何修改:
之後修改CMakeLists.txt檔案,和之前的也差不多:
首先修改message_generation,和message_runtime。
之後再修改add_service_files:
之後進入工作空間下面,對其進行編譯:
我們接下來看一下怎麼實現一個服務端,實現伺服器的程式設計:
我們在這個功能包下面的src資料夾下面建立一個server.cpp檔案:
一個伺服器的實現也需要分成四個步驟:
- 初始化ROS節點;
- 建立Server例項;
- 迴圈等待服務請求,進入回撥函式;
- 在回撥函式中完成服務功能的處理,並反饋應答資料。
其程式碼與之前的比較類似。再回調函式裡面,由於之前是將資料分成了兩個部分,所以這個也是做兩個部分,一個是request,一個是response。
之後我們還要建立一個客戶端:
具體程式碼如下:
之後需要對其進行編譯:
- 設定需要編譯的程式碼和生成的可執行檔案;
- 設定連結庫;
- 設定依賴;
add_executable(server src/server.cpp)
target_link_libraries(server ${catkin_LIBRARIES})
add_dependencies(server ${PROJECT_NAME}_gencpp)
add_executable(client src/client.cpp)
target_link_libraries(client ${catkin_LIBRARIES})
add_dependencies(client ${PROJECT_NAME}_gencpp)
設定完成之後,需要回到工作空間的根目錄下面對其進行編譯:
接下來我們執行編譯成功之後的可執行檔案:
首先我們roscore啟動master,之後啟動服務端,再啟動客戶端,傳入引數:
roscore
rosrun learning_communication server
rosrun learning_communication client 1 1
3.動作程式設計
動作也是話題服務的一種機制,但是跟話題服務又有一些區別。比如說我們想讓機器人前往一個目標點,並且讓機器人不斷地返回自己的實時狀態,甚至說在機器人運動過程當中,我們不想讓機器人前往這個目標點了,給他釋出他一個資訊,讓它停止下來。像這種需要維持一段時間,並且有反饋的這樣一種通訊是沒有辦法用話題或者服務來完成的。動作的實現也是通過ROS的訊息機制來實現的。
實現原理圖如下所示:
同樣也是分為服務端與客戶端,通過二者之間的通訊來實現。
Action的介面有:
- goal:釋出任務目標。客戶端可以給服務端傳送一個動作的目標。
- cancel:請求取消任務。在執行的過程當中,也可以將其取消。
- status:通知客戶端當前的狀態。通知客戶端當前伺服器的狀態。
- feedback:週期反饋任務執行的監控資料。週期性地反饋,告訴機器人當前伺服器的狀態。
- result:向客戶端傳送任務的執行結果,只發布一次。當完成客戶端的命令之後會執行一次。
那麼我們如何來實現這樣一個具體的動作程式設計呢:
- 定義action檔案
- 在package.xml中新增功能包依賴
<build_depend>actionlib</build_depend>
<build_depend>actionlib_msgs</build_depend>
<exec_depend>actionlib</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
- 在CMakeLists.txt中新增編譯選項
find_package(catkin REQUIRED actionlib_msgs actionlib)
add_action_files(DIRECTORY action FILES DoDishes.action)
generate_messages(DEPENDENCIES actionlib_msgs)
同樣,如果ROS中有自定義的工作訊息給我們的話我麼可以直接呼叫,如果沒有的話那我們就需要自己來定義這樣一個自定義的動作訊息。
我們現在來假設一個洗盤子這樣的任務,客戶端傳送一個洗盤子的命令給服務端,服務端接收到這個命令之後開始洗盤子,然後反饋給客戶端洗了多少了,客戶端也可以讓服務端終止洗盤子這個命令。盤子洗碗之後返回一個洗碗的訊號給客戶端。
如果我們想要建立一個動作訊息,我們需要在功能包裡面建立一個資料夾action。然後在action裡面建立具體的動作訊息。
這裡有兩個三橫槓,把這整個資料內容分成了三個部分。
第一部分是定義目標資料的,也就是客戶端傳送什麼樣的一個動作的目的給服務端。
第二部分定義的是結果的,也就是說我們整個動作執行完成之後到底是一個什麼樣的結果。
第三部分是一定週期內反饋給客戶端的資料內容。這裡的話就是反饋洗盤子的百分比。
定義完成之後需要在package.xml裡面去新增功能包的依賴。
package.xml改完之後要去改CMakeLists.txt檔案。
之後我們編譯一下工作空間,看看定義的這些action是否是正確的
編譯是沒有錯誤的,所以我們剛才的操作是沒有錯誤的。
我們現在來看一下如何實現一個動作的伺服器,主要是以下步驟:
- 初始化ROS節點
- 建立動作伺服器例項
- 啟動伺服器,等待動作請求
- 在回撥函式中完成動作服務功能的處理,並反饋進度資訊
- 動作完成,傳送結束資訊。
我們從視訊資源中把原始碼拷貝過來:
我們首先看一下服務端程式碼:
在main函式裡面,程式流程大致都差不多。啟動伺服器之後伺服器就會一直迴圈等待命令。一旦接收到命令之後,伺服器就會進入到回撥函式裡面。回撥函式裡面就會開始具體的服務處理。
完成服務端之後我們需要去完成客戶端的功能:
- 初始化ROS節點
- 建立動作客戶端例項
- 連線動作服務端
- 傳送動作目標
- 根據不同型別的服務端反饋處理回撥函式
程式碼完成之後就是來編譯這些程式碼:
add_executable(DoDishes_client src/DoDishes_client.cpp)
target_link_libraries( DoDishes_client ${catkin_LIBRARIES})
add_dependencies(DoDishes_client ${${PROJECT_NAME}_EXPORTED_TARGETS})
add_executable(DoDishes_server src/DoDishes_server.cpp)
target_link_libraries( DoDishes_server ${catkin_LIBRARIES})
add_dependencies(DoDishes_server ${${PROJECT_NAME}_EXPORTED_TARGETS})
之後進入工作空間目錄下對其進行編譯
我們接下來對其進行驗證測試:
roscore
rosrun learning_communication DoDishes_client
rosrun learning_communication DoDishes_client