1. 程式人生 > >ROS——通訊(二)

ROS——通訊(二)

4.1 Service

4.1.1 Service

上一章我們介紹了ROS的通訊方式中的topic(主題)通訊,我們知道topic是ROS中的一種單向的非同步通訊方式。然而有些時候單向的通訊滿足不了通訊要求,比如當一些節點只是臨時而非週期性的需要某些資料,如果用topic通訊方式時就會消耗大量不必要的系統資源,造成系統的低效率高功耗。
這種情況下,就需要有另外一種請求-查詢式的通訊模型。這節我們來介紹ROS通訊中的另一種通訊方式——service(服務)。

4.1.2 工作原理

簡介

為了解決以上問題,service方式在通訊模型上與topic做了區別。Service通訊是雙向的,它不僅可以傳送訊息,同時還會有反饋。所以service包括兩部分,一部分是請求方(Clinet),另一部分是應答方/服務提供方(Server)。這時請求方(Client)就會發送一個request,要等待server處理,反饋回一個reply,這樣通過類似“請求-應答”的機制完成整個服務通訊。

這種通訊方式的示意圖如下:
Node B是server(應答方),提供了一個服務的介面,叫做/Service,我們一般都會用string型別來指定service的名稱,類似於topic。Node A向Node B發起了請求,經過處理後得到了反饋。

過程

Service是同步通訊方式,所謂同步就是說,此時Node A釋出請求後會在原地等待reply,直到Node B處理完了請求並且完成了reply,Node A才會繼續執行。Node A等待過程中,是處於阻塞狀態的成通訊。這樣的通訊模型沒有頻繁的訊息傳遞,沒有衝突與高系統資源的佔用,只有接受請求才執行服務,簡單而且高效。

4.1.3 topic VS service

我們對比一下這兩種最常用的通訊方式,加深我們對兩者的理解和認識,具體見下表:

名稱 Topic Service
通訊方式 非同步通訊 同步通訊
實現原理 TCP/IP TCP/IP
通訊模型 Publish-Subscribe Request-Reply
對映關係 Publish-Subscribe(多對多) Request-Reply(多對一)
特點 接受者收到資料會回撥(Callback) 遠端過程呼叫(RPC)伺服器端的服務
應用場景 連續、高頻的資料釋出 偶爾使用的功能/具體的任務
舉例 鐳射雷達、里程計釋出資料 開關感測器、拍照、逆解計算

注意:遠端過程呼叫(Remote Procedure Call,RPC),可以簡單通俗的理解為在一個程序裡呼叫另一個程序的函式。

4.1.4 操作命令

在實際應用中,service通訊方式的命令時rosservice,具體的命令引數如下表:

rosservice 命令 作用
rosservice list 顯示服務列表
rosservice info 列印服務資訊
rosservice type 列印服務型別
rosservice uri 列印服務ROSRPC uri
rosservice find 按服務型別查詢服務
rosservice call 使用所提供的args呼叫服務
rosservice args 列印服務引數

4.1.5 測試例項

  1. 首先依然是開啟我們教材的模擬場景roslaunch robot_sim_demo robot_spawn.launch
  2. 輸入rosservice list,檢視當前執行的服務。
  3. 隨機選擇/gazebo/delete_light服務,觀察名稱,是刪除光源的操作。
  4. 輸入rosservice info /gazebo/delete_light檢視屬性資訊。可以看到資訊,Node:/gazebo,Type:gazebo_msgs/DeleteLight, Args:Light_name。這裡的型別type也就是下文介紹的srv,傳遞引數Light_name
  5. 輸入rosservice call /gazebo/delete_light sun,這裡的sun 是引數名,使我們模擬場景中的唯一光源太陽。操作完成後可以看到場景中的光線消失。
  6. 可以看到終端的回傳資訊:success: True和sun successfully deleted。這就是雙向通訊的資訊反饋,通知操作已經成功完成。

小結

本節我們詳細介紹了service通訊方式,建議與topic通訊方式進行對比記憶,這樣我們能更深的理解這兩種通訊方式,也能在以後的學習工作中更加合理使用每個通訊方式,獲得更高的效率。

4.2 Srv

4.2.1 簡介

類似msg檔案,srv檔案是用來描述服務(service資料型別的,service通訊的資料格式定義在*.srv中。它聲明瞭一個服務,包括請求(request)和響應(reply)兩部分。其格式宣告如下:

舉例:

msgs_demo/srv/DetectHuman.srv

bool start_detect
---
my_pkg/HumanPose[] pose_data

msgs_demo/msg/HumanPose.msg

std_msgs/Header header
string uuid
int32 number_of_joints
my_pkg/JointPose[]joint_data

msgs_demo/msg/JointPose.msg

string joint_name
geometry_msgs/Pose pose
floar32 confidence

DetectHUman.srv檔案為例,該服務例子取自OpenNI的人體檢測ROS軟體包。它是用來查詢當前深度攝像頭中的人體姿態和關節數的。srv檔案格式很固定,第一行是請求的格式,中間用---隔開,第三行是應答的格式。在本例中,請求為是否開始檢測,應答為一個數組,陣列的每個元素為某個人的姿態(HumanPose)。而對於人的姿態,其實是一個msg,所以srv可以巢狀msg在其中,但它不能巢狀srv。

4.2.2 操作命令

具體的操作指令如下表:

rossrv 命令 作用
rossrv show 顯示服務描述
rossrv list 列出所有服務
rossrv md5 顯示服務md5sum
rossrv package 列出包中的服務
rossrv packages 列出包含服務的包

4.2.3 修改部分檔案

定義完了msg、srv檔案,還有重要的一個步驟就是修改package.xml和修改CMakeList.txt。這些檔案需要新增一些必要的依賴等,例如:

<build_depend>** message_generation **</build_depend>
<run_depend>** message_runtime **</run_depend>

上述文字中“**”所引就是新新增的依賴。又例如:

find_package(...roscpp rospy std_msgs ** message_generation **)
catkin_package(
...
CATJIN_DEPENDS ** message_runtime ** ...
...)

add_message_file(
FILES
** DetectHuman.srv **
** HumanPose.msg **
** JointPos.msg **)

** generate_messages(DEPENDENCIES std_msgs) **

新增的這些內容指定了srv或者msg在編譯或者執行中需要的依賴。具體的作用我們初學者可不深究,我們需要了解的是,無論我們自定義了srv,還是msg,修改上述部分新增依賴都是必不可少的一步。

4.3 Parameter server

4.3.1 簡介

前文介紹了ROS中常見的兩種通訊方式——主題和服務,這節介紹另外一種通訊方式——引數伺服器(parameter server)。與前兩種通訊方式不同,引數伺服器也可以說是特殊的“通訊方式”。特殊點在於引數伺服器是節點儲存引數的地方、用於配置引數,全域性共享引數。引數伺服器使用網際網路傳輸,在節點管理器中執行,實現整個通訊過程。

引數伺服器,作為ROS中另外一種資料傳輸方式,有別於topic和service,它更加的靜態。引數伺服器維護著一個數據字典,字典裡儲存著各種引數和配置。

字典簡介

何為字典,其實就是一個個的鍵值對,我們小時候學習語文的時候,常常都會有一本字典,當遇到不認識的字了我們可以查部首查到這個字,獲取這個字的讀音、意義等等,而這裡的字典可以對比理解記憶。鍵值kay可以理解為語文裡的“部首”這個概念,每一個key都是唯一的,參照下圖:

每一個key不重複,且每一個key對應著一個value。也可以說字典就是一種對映關係,在實際的專案應用中,因為字典的這種靜態的對映特點,我們往往將一些不常用到的引數和配置放入引數伺服器裡的字典裡,這樣對這些資料進行讀寫都將方便高效。

維護方式

引數伺服器的維護方式非常的簡單靈活,總的來講有三種方式:

  • 命令列維護
  • launch檔案內讀寫
  • node原始碼

下面我們來一一介紹這三種維護方式。

4.3.2 命令列維護

使用命令列來維護引數伺服器,主要使用rosparam語句來進行操作的各種命令,如下表:

rosparam 命令 作用
rosparam set param_key param_value 設定引數
rosparam get param_key 顯示引數
rosparam load file_name 從檔案載入引數
rosparam dump file_name 儲存引數到檔案
rosparam delete 刪除引數
rosparam list 列出引數名稱

load&&dump檔案

load和dump檔案需要遵守YAML格式,YAML格式具體示例如下:

name:'Zhangsan'
age:20
gender:'M'
score{Chinese:80,Math:90}
score_history:[85,82,88,90]

簡明解釋。就是“名稱+:+值”這樣一種常用的解釋方式。一般格式如下:

key : value

遵循格式進行定義引數。其實就可以把YAML檔案的內容理解為字典,因為它也是鍵值對的形式。

4.3.3 launch檔案內讀寫

launch檔案中有很多標籤,而與引數伺服器相關的標籤只有兩個,一個是<param>,另一個是<rosparam>。這兩個標籤功能比較相近,但<param>一般只設置一個引數,請看下例:

(1) (2) (3)

觀察上例比如序號3的param就定義了一個key和一個value,交給了引數伺服器維護。而序號1的param只給出了key,沒有直接給出value,這裡的value是由後沒的指令碼執行結果作為value進行定義的。序號(2)就是rosparam的典型用法,先指定一個YAML檔案,然後施加command,其效果等於rosparam load file_name 。

4.3.4 node原始碼

除了上述最常用的兩種讀寫引數伺服器的方法,還有一種就是修改ROS的原始碼,也就是利用API來對引數伺服器進行操作。具體內容我們學習完後面章節再進行介紹。

4.3.5 操作例項

  1. 首先依然是開啟我們教材的模擬場景roslaunch robot_sim_demo robot_spawn.launch
  2. 輸入rosparam list檢視引數伺服器上的param。
  3. 查詢引數資訊,例如查詢豎直方向重力引數。輸入rosparam get /gazebo/gravity_z回車得到引數值value=-9.8。
  4. 嘗試儲存一個引數到檔案輸入rosparam dump param.yaml之後就可以在當前路徑看到該檔案,也就能開啟去檢視到相關的引數資訊。
  5. 引數伺服器的其他命令操作方式大致相同,我們可以多多練習,鞏固對引數伺服器的理解和應用。

引數型別

ROS引數伺服器為引數值使用XMLRPC資料型別,其中包括:strings, integers, floats, booleans, lists, dictionaries, iso8601 dates, and base64-encoded data。

4.4 Action

4.4.1 簡介

Actionlib是ROS中一個很重要的庫,類似service通訊機制,actionlib也是一種請求響應機制的通訊方式,actionlib主要彌補了service通訊的一個不足,就是當機器人執行一個長時間的任務時,假如利用service通訊方式,那麼publisher會很長時間接受不到反饋的reply,致使通訊受阻。當service通訊不能很好的完成任務時候,actionlib則可以比較適合實現長時間的通訊過程,actionlib通訊過程可以隨時被檢視過程進度,也可以終止請求,這樣的一個特性,使得它在一些特別的機制中擁有很高的效率。

4.4.2 通訊原理

Action的工作原理是client-server模式,也是一個雙向的通訊模式。通訊雙方在ROS Action Protocol下通過訊息進行資料的交流通訊。client和server為使用者提供一個簡單的API來請求目標(在客戶端)或通過函式呼叫和回撥來執行目標(在伺服器端)。

工作模式的結構示意圖如下:

通訊雙方在ROS Action Protocal下進行交流通訊是通過介面來實現,如下圖:

我們可以看到,客戶端會向伺服器傳送目標指令和取消動作指令,而伺服器則可以給客戶端傳送實時的狀態資訊,結果資訊,反饋資訊等等,從而完成了service沒法做到的部分.

4.4.3 Action 規範

利用動作庫進行請求響應,動作的內容格式應包含三個部分,目標、反饋、結果。

  • 目標

機器人執行一個動作,應該有明確的移動目標資訊,包括一些引數的設定,方向、角度、速度等等。從而使機器人完成動作任務。

  • 反饋

在動作進行的過程中,應該有實時的狀態資訊反饋給伺服器的實施者,告訴實施者動作完成的狀態,可以使實施者作出準確的判斷去修正命令。

  • 結果

當運動完成時,動作伺服器把本次運動的結果資料傳送給客戶端,使客戶端得到本次動作的全部資訊,例如可能包含機器人的運動時長,最終姿勢等等。

4.4.4 Action規範檔案格式

Action規範檔案的字尾名是.action,它的內容格式如下:

# Define the goal
uint32 dishwasher_id  # Specify which dishwasher we want to use
---
# Define the result
uint32 total_dishes_cleaned
---
# Define a feedback message
float32 percent_complete

4.4.5 Action例項詳解

Actionlib是一個用來實現action的一個功能包集。我們在demo中設定一個場景,執行一個搬運的action,搬運過程中客戶端會不斷的發回反饋資訊,最終完成整個搬運過程.

本小節的演示原始碼在課程的演示程式碼包裡,此處為連結.

首先寫handling.action檔案,類比如上的格式.包括三個部分,目標,結果,反饋.如下:

# Define the goal
uint32 handling_id 
---
# Define the result
uint32 Handling_completed
---
# Define a feedback message
float32 percent_complete

寫完之後修改資料夾裡CmakeLists.txt如下內容:

  1. find_package(catkin REQUIRED genmsg actionlib_msgs actionlib)

  2. add_action_files(DIRECTORY action FILES DoDishes.action) generate_messages(DEPENDENCIES actionlib_msgs)

  3. add_action_files(DIRECTORY action FILES Handling.action)

  4. generate_messages( DEPENDENCIES actionlib_msgs)

修改package.xml,新增所需要的依賴如下:

  1. <build_depend>actionlib </build_depend>
  2. <build_depend>actionlib_msgs</build_depend>
  3. <run_depend>actionlib</run_depend>
  4. <run_depend>actionlib_msgs</run_depend>

然後回到工作空間 catkin_ws進行編譯.

本例中設定的的action,定義了一個搬運的例子,首先寫客戶端,實現功能傳送action請求,包括進行目標活動,或者目標活動.之後寫伺服器,實驗返回客戶端活動當前狀態資訊,結果資訊,和反饋資訊.從而實現action.本例測試結果截圖如下:

 

小結

至此,ROS通訊架構的四種通訊方式就介紹結束,我們可以對比學習這四種通訊方式,去思考每一種通訊的優缺點和適用條件,在正確的地方用正確的通訊方式,這樣整個ROS的通訊會更加高效,機器人也將更加的靈活和智慧。機器人學會了通訊,也就相當於有了“靈魂”。

常見srv型別

4.5 常見srv型別

本小節介紹常見的srv型別及其定義 srv型別相當於兩個message通道,一個傳送,一個接收

AddTwoInts.srv

#對兩個整數求和,虛線前是輸入量,後是返回量
#檔案位置:自定義srv檔案
int32 a
int32 b
---
int32 sum

Empty.srv

#檔案位置:std_srvs/Empty.srv
#代表一個空的srv型別

---

GetMap.srv

#檔案位置:nav_msgs/GetMap.srv
#獲取地圖,注意請求部分為空

---
nav_msgs/OccupancyGrid map

GetPlan.srv

#檔案位置:nav_msgs/GetPlan.srv
#得到一條從當前位置到目標點的路徑
geometry_msgs/PoseStamped start        #起始點
geometry_msgs/PoseStamped goal        #目標點
float32 tolerance    #到達目標點的x,y方向的容錯距離
---
nav_msgs/Path plan

SetBool.srv

#檔案位置:std_srvs/SetBools.srv
bool data # 啟動或者關閉硬體
---
bool success   # 標示硬體是否成功執行
string message # 執行資訊

SetCameraInfo.srv

#檔案位置:sensor_msgs/SetCameraInfo.srv
#通過給定的CameraInfo相機資訊,來對相機進行標定
sensor_msgs/CameraInfo camera_info        #相機資訊
---
bool success            #如果呼叫成功,則返回true
string status_message    #給出呼叫成功的細節

SetMap.srv

#檔案位置:nav_msgs/SetMap.srv
#以初始位置為基準,設定新的地圖
nav_msgs/OccupancyGrid map
geometry_msgs/PoseWithCovarianceStamped initial_pose
---
bool success

TalkerListener.srv

#檔案位置: 自定義srv檔案
---
bool success   # 標示srv是否成功執行
string message # 資訊,如錯誤資訊等

Trigger.srv

#檔案位置:std_srvs/Trigger.srv
---
bool success   # 標示srv是否成功執行
string message # 資訊,如錯誤資訊等

4.6 常見action型別

本小節介紹常見的action型別以及其定義

AddTwoInts.action

#檔案位置:自定義action檔案
#表示將兩個整數求和
int64 a
int64 b
---
int64 sum
---

AutoDocking.action

#檔案位置:自定義action檔案
#goal
---
#result
string text
---
#feedback
string state
string text

GetMap.action

#檔案位置:nav_msgs/GetMap.action
#獲取地圖資訊,響應部分為空

---
nav_msgs/OccupancyGrid map
---
#無返回部分

MoveBase.action

#檔案位置:geometry_msgs/MoveBase.action
geometry_msgs/PoseStamped target_pose
---
---
geometry_msgs/PoseStamped base_position