理解 Spot 編程(Understanding Spot Programming)

前言大綱

本指南將幫助您了解驅動 Spot 和 Spot Python SDK 的編程原則。

基礎機器人服務(Fundamental Robot Services)

理解“id”命令(Understanding the “id” command)

“id”命令是最簡單的 Spot 命令之一,因此了解它的工作原理很有用,因為 Spot 的每個命令都執行許多相同的基本功能。

--verbose 指定詳細標誌,你會得到很多信息!我們將解釋這些片段……

$ python -m bosdyn.client --verbose 192.168.80.3 id
2020-03-26 17:30:27,571 - DEBUG - Creating standard Sdk, cert glob: "None"
2020-03-26 17:30:27,610 - DEBUG - Created client for robot-id
2020-03-26 17:30:27,615 - DEBUG - Created channel to 192.168.80.3 at port 443 with authority id.spot.robot
...

第一個輸出行創建一個 Spot SDK 物件。所有 Spot API 程序都以這種方式啟動。

請注意,輸出文本本身演示了 Spot 對 Python 日誌記錄工具 logging facility 的使用,我們建議您使用相同的方法執行日誌記錄。

第三行創建 Spot 的機器人 ID  robot-id 服務的 客戶端 client。 Spot API 通過一組網絡可訪問服務公開機器人功能 - 類似於 微服務microservice 架構。

上面輸出的最後一行顯示了啟動 gRPC 通道到 Spot 的命令。與機器人的所有通信均通過安全的 HTTPS 連接進行。Spot API 使用 gRPC 作為其底層 RPC(遠程過程調用)傳輸。gRPC 是一種用於服務的高性能網絡連接,支持多種編程環境。 gRPC 使用 Protocol Buffers 作為消息傳遞格式,它具有緊湊的在線表示並支持向後和向前兼容性。
2020-03-26 17:30:27,616 - DEBUG - blocking request: b'/bosdyn.api.RobotIdService/GetRobotId'
header {
  request_timestamp {
    seconds: 1585258227
    nanos: 616570624
  }
  client_name: "BosdynClientbblank02:__main__.py-28906"
}

在上面的輸出中,可以看到對 bosdyn.api.RobotIdService 進行了區塊性的 GetRobotId  RPC。

最後,RobotIdService 使用包含機器人信息的響應來響應 GetRobotId  RPC。
2020-03-26 17:30:27,650 - DEBUG - response: b'/bosdyn.api.RobotIdService/GetRobotId'
header {
  request_header {
    request_timestamp {
      seconds: 1585258227
      nanos: 616570624
    }
    client_name: "BosdynClientbblank02:__main__.py-28906"
  }
  request_received_timestamp {
    seconds: 1585258226
    nanos: 224952738
  }
  response_timestamp {
    seconds: 1585258226
    nanos: 224990830
  }
  error {
    code: CODE_OK
  }
  request {
    type_url: "type.googleapis.com/bosdyn.api.RobotIdRequest"
    value: "\n6\n\014\010\363\275\364\363\005\020\200\276\200\246\002\022&BosdynClientbblank02:__main__.py-28906"
  }
}
robot_id {
  serial_number: "beta-BD-90490007"
  species: "spot"
  version: "V3"
  software_release {
    version {
      major_version: 2
    }
    changeset_date {
      seconds: 1583941992
    }
    changeset: "b11205d698e"
    install_date {
      seconds: 1583953617
    }
  }
  nickname: "beta29"
  computer_serial_number: "02-19904-9903"
}

列表服務(Listing Services)

以下命令列出了機器人上可用的所有服務。注意robot-id 服務已列出,我們在上一節中使用過。服務是您在 Spot 上與之通信的內容,使用它們來發出命令、檢索信息...等。
$ python -m bosdyn.client --user user --password password 192.168.80.3 dir list 
$ python -m bosdyn.client --user user --password password 192.168.80.3 dir list
$ python -m bosdyn.client --user user --password password 192.168.80.3 dir list
name              	type                                     authority               tokens
---------------------------------------------------------------------------------------------------
auth                    bosdyn.api.AuthService                     auth.spot.robot
directory               bosdyn.api.DirectoryService                api.spot.robot            user
directory-registration  bosdyn.api.DirectoryRegistrationService    api.spot.robot            user
estop           	bosdyn.api.EstopService         	   estop.spot.robot 	     user
graph-nav-service      	bosdyn.api.graph_nav.GraphNavService       graph-nav.spot.robot      user
image              	bosdyn.api.ImageService            	   api.spot.robot            user
lease              	bosdyn.api.LeaseService           	   api.spot.robot            user
...

有關 Spot 的 API 架構Spot’s API Architecture 的更多詳細信息,請參閱概念文檔。

理解如何設置與命令機器人移動(Understanding How to Setup and Command Spot to Move)

從命令行運行 Spot 以了解命令 Spot 的基礎知識很有用。

啟動一個 python 解釋器並導入 bosdyn.client 套件 - 假設您已經成功完成了我們的 Spot Python SDK 快速入門QuickStart,這應該可以動作了。
$ python
Python 3.6.8 (default, Jan 14 2019, 11:02:34)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import bosdyn.client

創建SDK物件(Create the SDK object)

所有波士頓動力 API 程序都首先創建一個帶有客戶端名稱參數的 SDK 物件。客戶端名稱用於幫助調試,並且沒有任何語義信息 - 因此請使用對您有幫助的任何字符串。

>>> sdk = bosdyn.client.create_standard_sdk('understanding-spot')

創建機器人物件(Create a Robot object)

要像我們在 Spot Python SDK 快速入門QuickStart 中所做的那樣檢索機器人 ID,我們首先需要創建一個機器人物件,並使用其網絡地址作為參數。

>>> robot = sdk.create_robot('192.168.80.3')

檢索機器人ID(Retrieve the Robot ID)

如前所述,Spot 通過許多服務公開其功能。波士頓動力 Python API 為每個服務都有一組相應的客戶端,這些客戶端是從機器人物件創建的。

讓我們創建一個 robots-id 服務的 RobotIdClient,然後檢索 id 信息:
>>>id_client = robot.ensure_client('robot-id')
>>>id_client.get_id()
serial_number: "beta-BD-90490007"
species: "spot"
.....

閉塞vs非同步,機器人Python SDK 函式功能(Blocking vs. Asynchronous Spot Python SDK functions)

上面的 get_id() 調用是閉塞的——它在 RPC 完成後才會完成。可以調整調用的參數,例如等待多長時間的超時。下面的例子設置了一個太短的超時並且失敗了……
>>>id_client.get_id(timeout=0.0001)
Traceback (most recent call last):
  File "/mnt/c/spot_v2_0/bblank_spot_v2_0_env/lib/python3.6/site-packages/bosdyn/client/common.py", line 290, in call
    response = rpc_method(request, **kwargs)
  File "/mnt/c/spot_v2_0/bblank_spot_v2_0_env/lib/python3.6/site-packages/grpc/_channel.py", line 826, in __call__
    return _end_unary_response_blocking(state, call, False, None)
  File "/mnt/c/spot_v2_0/bblank_spot_v2_0_env/lib/python3.6/site-packages/grpc/_channel.py", line 729, in _end_unary_response_blocking
    raise _InactiveRpcError(state)
grpc._channel._InactiveRpcError: <_inactiverpcerror call.cc="" core="" created="" debug_error_string="{" description="" details="Deadline Exceeded" eadline="" exceeded="" file="" file_line="" from="" grpc_message="" grpc_status="" ipv4:192.168.80.3:443="" lib="" of="" peer="" received="" rpc="" rror="" src="" status="StatusCode.DEADLINE_EXCEEDED" surface="" terminated="" that="" with:="">
除了閉塞調用,客戶端還支持非閉塞非同步調用。這在高性能應用程序中非常有用,在這些應用程序中,執行緒不能停止等待 RPC 完成。Python 的 futures 架構被用作非同步通信的基礎。有關如何使用這些函數的信息,請參閱 get_robot_state_async programming example  編程示例。

讓我們對機器人 id 進行非同步調用並等待返回的 future object 的結果:
>>>fut = id_client.get_id_async()
>>>fut.result()
serial_number: "beta-BD-90490007"
species: "spot"
.....

檢視機器人狀態(Inspecting robot state)

機器人狀態服務 robot-state service 包含有關機器人的動態信息,例如位置、電池狀態等。

服務與授權(Services and Authentication)

在檢索機器人狀態之前,您需要對機器人進行身份驗證。 大多數服務都需要對用戶進行身份驗證 - 這可以防止隨機網絡攻擊者控制機器人或攔截可能敏感的信息。

假設用戶名是 user,密碼是 password,發出以下命令。

>>>robot.authenticate('user', 'password')

如果您提供了錯誤的憑據,則會引發異常。

處理機器人狀態(Retrieving Robot State)

現在我們可以為機器人狀態服務 robot-state service 創建一個 RobotStateClient,並獲取有關 Spot 的信息:
>>>state_client = robot.ensure_client('robot-state')
>>>state_client.get_robot_state()
power_state {
    timestamp {
        seconds: 1585324337
        nanos: 644209920
    }
    ... a whole lot more

機器人狀態是一種訊息,定義在 Protobufs (Robot State was a Message, Messages are defined by Protobufs)

上面檢索到的機器人狀態消息的結構由 protobuf 定義。 這是機器人所說的語言。 Spot SDK 完全公開了 protobuf,因此要真正了解 Spot 編程,您需要查看並了解 protobuf。 看一看,它們就在您的發行版中! ../../protos/bosdyn/api/robot_state.proto

機器人幀框(Spot’s Frames)

通常,了解 Spot 的位置以及它與周圍世界的關係很有用。 為了表達這些信息,Spot 使用幀來表示物件和位置(例如“身體”幀框),並使用 3D 變換來描述使用平移向量和旋轉四元數的兩個幀框之間的關係。 有關幀和轉換的更多詳細信息、3D 轉換的可能數學以及 Spot 知道的不同幀類別,請參閱 幾何和幀Geometry and Frames  文檔。

擷取並看相機圖像(Capture and View Camera images)

除了 5 個深度攝像頭之外,Spot 還擁有 5 個“魚眼”攝像頭。可以從這些圖像源中捕獲圖像。 ‘list_image_sources’ RPC 返回有效的相機來源名稱。
>>>from bosdyn.client.image import ImageClient
>>>image_client = robot.ensure_client(ImageClient.default_service_name)
>>>sources = image_client.list_image_sources()
>>>[source.name for source in sources]
['back_depth', 'back_depth_in_visual_frame', 'back_fisheye_image', 'frontleft_depth', 'frontleft_depth_in_visual_frame', 'frontleft_fisheye_image', 'frontright_depth', 'frontright_depth_in_visual_frame', 'frontright_fisheye_image', 'left_depth', 'left_depth_in_visual_frame', 'left_fisheye_image', 'right_depth', 'right_depth_in_visual_frame', 'right_fisheye_image']

使用上面列出的來源名稱,我們可以從一個或多個圖像源捕獲圖像。 這些圖像可以以 RAW 格式或 JPG 格式(具有指定畫質)捕獲。 在單個 RPC 中請求的多個圖像將在硬件時間上彼此同步。 讓我們檢索 left_fisheye_image 並顯示它(除非您使用的是 MacOS 或 WSL)……

>>>image_response = image_client.get_image_from_sources(["left_fisheye_image"])[0]
>>>from PIL import Image
>>>import io
>>>image = Image.open(io.BytesIO(image_response.shot.image.data))
>>>image.show()

配置電機起動授權-軟體的急停(Configuring “Motor Power Authority” - software E-Stop)

在 Spot 啟動之前,必須正確配置獨立的 Motor Power Authority。 我們在下面和我們的函數中使用術語“E-Stop”作為 Motor Power Authority 的簡寫。 急停是 Spot 的一項關鍵安全功能,可讓操作員在情況需要時立即切斷電機電源。 請注意,在某些圈子中,術語“E-Stop”意味著硬件電源短路,因此我們的語義舞蹈,因為 Spot 的 Motor Power Authority 是網絡軟件解決方案,而不是硬件解決方案。

我們通過為急停服務“E-Stop”創建一個客戶端並請求狀態來看看機器人的初始急停狀態:

>>>estop_client = robot.ensure_client('estop')
>>>estop_client.get_status()
stop_level: ESTOP_LEVEL_CUT
stop_level_details: "Not all endpoints are registered"

stop_level: ESTOP_LEVEL_CUT 行表示將不會啟用電源,因為 E-Stop 級別為 CUT。

stop_level_details: "Not all endpoints are registered" 行表示沒有註冊急停端點。 急停端點是急停系統的客戶端組件,可讓用戶立即斷電。 Spot 可能一次註冊多個 E-Stop 端點 - 例如,在操作員培訓期間,受訓者可能有一個平板電腦可以讓他們控制機器人和 E-Stop,而培訓師可能有一個平板電腦也可以讓他們 緊急停止機器人。

創建並註冊急停端點(Create and register an E-Stop Endpoint)

安全注意事項:註冊端點的行為將觸發機器人的緊急停止,因此僅當 Spot 的電機已關閉時才執行此步驟。

>>>estop_endpoint = bosdyn.client.estop.EstopEndpoint(client=estop_client, name='my_estop', estop_timeout=9.0)
>>>estop_endpoint.force_simple_setup()

急停端點應定期檢查機器人,以確保機器人得到安全控制。 如果超過 estop_timeout 秒,電機電源將被切斷。 調整這個數字很重要:數字太小,可能會因為暫時的網絡問題而斷電; 數量太大,您將面臨在沒有安全監督的情況下 Spot 運營的風險。

force_simple_setup 調用會發出一些 API 調用,使您的急停端點成為新急停配置中的唯一端點。

讓我們在註冊我們的端點後請求緊急停止狀態:
>>>estop_client.get_status()
endpoints {
  endpoint {
    role: "PDB_rooted"
    name: "my_estop"
    unique_id: "0"
    timeout {
      seconds: 9
    }
    cut_power_timeout {
      seconds: 13
    }
  }
  stop_level: ESTOP_LEVEL_CUT
  time_since_valid_response {
  }
}
stop_level: ESTOP_LEVEL_CUT
stop_level_details: "Endpoint requested stop"

現在出現一個名為 my_estop 的急停端點。 端點本身說 ESTOP_LEVEL_CUT,來自很久以前 time_since_valid_response 的響應。 尚未發生來自 E-Stop Endpoint 的簽到。 端點和 E-Stop 系統的停止級別都是 ESTOP_LEVEL_CUT - 如果單個端點想要斷電,則整個系統將斷電。

清除急停(Clear the E-Stop)

要更改 E-Stop 狀態並允許通電,端點需要定期檢查。我們將使用 EstopKeepAlive 類別從後台執行緒定期執行這些簽入。

>>>estop_keep_alive = bosdyn.client.estop.EstopKeepAlive(estop_endpoint)
>>>estop_client.get_status()
endpoints {
  endpoint {
    role: "PDB_rooted"
    name: "my_estop"
    unique_id: "0"
    timeout {
      seconds: 9
    }
    cut_power_timeout {
      seconds: 13
    }
  }
  stop_level: ESTOP_LEVEL_NONE
  time_since_valid_response {
    nanos: 996009984
  }
}
stop_level: ESTOP_LEVEL_NONE
stop_level 現在是 ESTOP_LEVEL_NONE,表示可以啟動電源。

請注意,在許多實現中,您應該為 EstopKeepAlive 指定 keep_running_cb 參數,該函數由後台執行緒調用以查看是否應繼續簽入。 例如,交互式 UI 應該為 E-Stop 系統提供一個 keep_running_cb 函數,該函數會阻塞直到 UI 執行緒運行一個循環。 這可以防止凍結的客戶端繼續為機器人供電。

取得機器人的擁有權(Taking ownership of Spot -Leases)

在為 Spot 的電機供電之前還有一步,那就是獲得機器人的所有權。 機器人可以有多個客戶端,但只有一個可以控制機器人,即使其他客戶端可能正在請求數據或充當急停端點。

為了控制機器人,客戶需要獲得 Lease 租約。 對機器人的每個移動命令都必須提供有效的租約。 當客戶不再想控制機器人時,可以退還租約。

與 E-Stop 一樣,租賃持有者需要定期與 Spot 簽到,以表明他們仍在主動控制機器人。 如果距離簽到時間過長,機器人將開始通訊丟失程序 - 坐下在可以的情況下,然後關閉電源。

讓我們為 lease 租用服務創建一個 LeaseClient 並列出當前 租用leases:

>>>lease_client = robot.ensure_client('lease')
>>>lease_client.list_leases()
[resource: "body"
lease {
  resource: "body"
  epoch: "IOSDMpfEqvdTHZGV"
  sequence: 0
}
lease_owner {
}
]

啟動機器人(Powering on the robot)

現在您已通過 Spot 身份驗證、創建 E-Stop 端點並獲得租約,是時候啟動機器人了。

確保機器人處於安全位置、坐姿、電池已充電且未連接到岸電。

power_on 輔助函數首先向機器人發出較低級別的啟動命令,然後等待啟動命令反饋。 該命令在機器人通電後返回,或者如果啟動命令因任何原因失敗則拋出錯誤。 通常需要幾秒鐘才能完成。

robot.power_on(timeout_sec=20)

機器人對象提供了一種檢查機器人電源狀態的方法。這只是使用 RobotStateService 來檢查 PowerState:

>>>robot.is_powered_on()
True

建立時間同步(Establishing timesync)

需要時間同步來協調您的設備和 Spot 之間的時鐘偏差。 從安全角度來看,這允許用戶定義命令有效的時間段。 機器人類別,維護一個時間同步執行緒。 下面的 wait_for_sync 調用將啟動一個時間同步執行緒,並阻塞直到建立同步。 建立時間同步後,該執行緒將定期調用以保持時間同步。 每個客戶端都有一個時鐘標識符,用於驗證客戶端是否為需要此功能的服務執行了時間同步。 客戶端函式庫的編寫使得時間同步的大多數實現細節都在後台處理。

>>>robot.time_sync.wait_for_sync()

下命令給機器人(Commanding the robot)

RobotCommandService 是指揮機動性的主要介面。 移動性和移動性相關命令包括 : stand,  sit,  selfright,  safe_power_off,  velocity,  and  trajectory(軌跡)。 在本教程中,我們將只發出stand 和safe power off 命令。

API 提供了一個輔助函數來支持 Spot。 此命令包裝了多個  RobotCommand  RPC 調用。 首先發出站立命令。 機器人檢查一些基本的先決條件(通電、未故障、未緊急停止)並返回命令 ID。 然後可以將此命令 id 傳遞給機器人命令反饋 RPC。 此 call 調用返回高級反饋(“機器人是否仍在處理命令?”)以及命令特定反饋feedback (在站立的情況下,“機器人是否站立?”)。

>>>from bosdyn.client.robot_command import RobotCommandClient, blocking_stand
>>>command_client = robot.ensure_client(RobotCommandClient.default_service_name)
>>>blocking_stand(command_client, timeout_sec=10)

機器人現在應該是站立的。 此外,可以修改站立命令以控制身體的高度以及身體相對於footprint frame足跡幀框 的方向。 足跡幀框是重力對齊幀框,其原點位於腳的幾何中心。 Z 軸向上,X 軸向前。 

命令 proto 可以非常具有表現力,因此,如果超出默認參數,則非常重要。 為了增加簡單性,Spot API 提供了幾個輔助函數,將 Spot API RPC 命令組合成單行函數。

我們鼓勵您嘗試使用這些不同的參數,參考用於一般波士頓動力機器人的 robots_command proto 父類別和機器人命令 robot command proto 子類別。

# Command Spot to rotate about the Z axis.
>>> from bosdyn.geometry import EulerZXY
>>> footprint_R_body = EulerZXY(yaw=0.4, roll=0.0, pitch=0.0)
>>> from bosdyn.client.robot_command import RobotCommandBuilder
>>> cmd = RobotCommandBuilder.stand_command(footprint_R_body=footprint_R_body)
>>> command_client.robot_command(cmd)
# Command Spot to raise up.
>>> cmd = RobotCommandBuilder.stand_command(body_height=0.1)
>>> command_client.robot_command(cmd)

機器人關機(Powering off the robot)

使用 power_off 命令關閉機器人電源。 請注意,首選方法是使用 cut_immediately=False ,其中 Spot 將停下來並在關閉電源之前輕輕坐下。 另一個關閉選項會立即切斷電機電源,這會導致機器人倒塌。
robot.power_off(cut_immediately=False)


參考資料

  • Understanding Spot Programming
  • 微服務microservice : 微服務是一種軟體架構風格,它是以專注於單一責任與功能的小型功能區塊 為基礎,利用模組化的方式組合出複雜的大型應用程式,各功能區塊使用與語言無關 的API集相互通訊。
  • tweak : 調整, 擰
  • intercept :  截聽
  • quaternion : 四元數

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

iptables的觀念與使用

網路設定必要參數IP、netmask(遮罩)、Gateway(閘道)、DNS

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

Pandas 模組

如何撰寫Shell Script

查詢指令或設定 -Linux 線上手冊 - man

下載網頁使用 requests 模組