這一篇繼續介紹如何用 Python 來寫一個服務代理,我們就能做到 Day 10 介紹的 rosservice call
指令相同的效果。
這一個範例 turtle_callsrv_reset.py
可以做到呼叫服務 /reset
的效果,並重置烏龜的位置和狀態。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import rospy
# 訊息: std_srvs/Empty 表示不需要參數的標準服務
from std_srvs.srv import Empty as EmptySrv
def turtle_callsrv_reset():
# 設定暫停等待服務可用
rospy.wait_for_service('/reset')
try:
call1 = rospy.ServiceProxy('/reset', EmptySrv)
resp1 = call1()
return resp1
except rospy.ServiceException as e:
print ("Service call failed: {}".format(e))
rospy.spin()
if __name__ == '__main__':
# 節點初始化
rospy.init_node('turtle_srv_reset', anonymous=True)
turtle_callsrv_reset()
下面來解釋這段腳本的每個部份在做什麼。
import rospy
from std_srvs.srv import Empty as EmptySrv
在這之前可以先查詢服務的訊息類型,輸入指令 rosservice type /reset
查到訊息類型是 std_srvs/Empty
。和上一篇同理,這個訊息是定義在 std_srvs.srv
模組的 Empty
類別,因此 python 程式開頭先匯入。
turtle_callsrv_reset()
函式def turtle_callsrv_reset():
rospy.wait_for_service('/reset')
try:
# 建立服務代理
callsrv = rospy.ServiceProxy('/reset', EmptySrv)
# 呼叫服務取得回應
response = callsrv()
return response
# 處理異常狀況
except rospy.ServiceException as e:
print ("Service call failed: {}".format(e))
rospy.spin()
在這段函式中包含了幾個功能:
設定暫停:
rospy.wait_for_service('/reset')
這行的用意是讓腳本暫停,等待直到 /reset
這個服務可以使用時才用,避免出現錯誤。
建立服務代理:
callsrv = rospy.ServiceProxy('/reset', EmptySrv)
這行利用 rospy.ServiceProxy
建立了一個服務代理,它會負責向服務 /reset
提出請求並將結果儲存起來。
格式:
callsrv = rospy.ServiceProxy(proxy_name, srv_class)
callsrv
:用一個變數儲存服務代理的資訊,以便後面可以直接使用。
proxy_name
:服務代理的名稱。
srv_class
:提供的服務的類型。
服務代理是客戶端呼叫服務的仲介,實際上是一個可呼叫(Callable)的物件,這個服務代理會負責所有底層的通訊細節,包括與 ROS 伺服器建立連接、傳送請求以及接收回應。也就是說我們可以不理它的原理,只要知道我們能加上 ()
後像函式一樣使用它。
呼叫服務
response = callsrv()
這行是呼叫 callsrv()
,並且將呼叫後得到的回應儲存到變數 response
中。callsrv
會按照前面所說去向服務 /reset
提出請求,再回傳結果。
處理異常
最後處理 rospy.ServiceException
這個異常狀況,包含呼叫服務過程中出現的問題,並且用變數 e
來保存。當異常狀況發生時,這段程式碼可以顯示錯誤的原因。
扣掉第一天的開賽宣言,不知不覺就寫完十一篇了,到這裡差不多把 ROS 的基本概念介紹過一遍,現在我們可以建立節點,也可以寫出各種對機器人下指令的程式,可以說熱身完畢了。
不過接下來,先不急著馬上開始控制無人機。後面幾篇我將介紹與 ROS 平台整合最好的機器人模擬器: Gazebo ,以及如何匯入機器人到模擬環境中,這樣就不用真的跑去買一台無人機來控制囉。