今天我們要來講 ROS 中最為核心的部分,ROS 提供了 四種通訊方式,分別為 Topic、Service、Parameter Service 、Actionlib
在人跟人的溝通上,通常我們會訂出一個話題才會開始聊天,你總不可能跟一個人沒話題的亂聊吧,不然這是沒有意義的,而在 ROS 也是這樣的,假設我們現在有 A、B 兩個節點,此時 A 需要跟 B 說 嗨,這時 A node 便被稱為發布者 publisher ,B被稱為訂閱者 subscriber 現在我們來看下面這張圖, A 發布 HI 給 Topic 給 B ,而B去接收到 Hi 這個訊息。
但是你以為你發出去的訊息對方一定當會收到嗎? Topic 他是一個異步的通訊方式,這是什麼意思? 這就像是你平常去密學妹訊息一樣,你發出去的訊息,他不一定會看的到一樣,但是你不管對方有沒有收到,你仍會持續的發送。
在 ROS 中也是如此,從下圖來看,我們的相機 node 將 我們的 RGB 資料給傳遞給 影像處理 node 來進行處理,當我們的 影像處理 node 沒有收到任何訊息時,他就不會去進行動作,此時他可以去幹其他事情,而當我們的 相機 node 發送訊號過來時,我們的 影像處理 node 就必須去做事情了
但是如果一個 topic 他同時還受其他的 node 訂閱時,他會有影響嗎? 當然是不會的,就跟你可以同時跟多個妹子一起聊天依樣,會有影響嗎? 不會的,機器也是依樣,我們這時多了一個 display node 來查看當前的影像,如果你覺得你熟悉的話,他就是在 Gazboo 這個模擬器中的使用情形。
當然多對多也是可以的,只要你電腦跑得動都是可以的。
接下來我們來看一些 Topic 的相關指令
指令 | 作用 |
---|---|
rostopic list | 列出當前所有的 topic |
rostopic info topic_name | 顯示某個 topic 的屬性訊息 |
rostopic echo topic_name | 顯示某個 topic 的內容 |
rostopic pub topic_name ... | 向某個 topic 發佈內容 |
rostopic bw topic_name | 查看某個 topic 的帶寬 |
rostopic hz topic_name | 查看某個 topic 的頻率 |
rostopic find topic_type | 查找某個類型的 topic |
rostopic type topic_name | 查看某個 topic 的類型(msg) |
在 Topic 中他有很嚴格的訊息格式,在 ROS 中 message 其實是一種數據的格式,他定義於 .msg 檔中,我們發的每一條訊息都要去遵守這個格式。
他有 bool、int8、int16、int32、int64、uint、float32、float64、string、time、duration、header、array、vector(可變長度的array)
下面這邊是一個 msg 的範例
std_msg/Header header
uint32 seq
time stamp
string frame_id
uint32 height
uint32 width
string encoding
uint8 is_bigendian
uint32 step
uint8[] data
接下來來看 MSG 的相關指令
rosmsg 指令 | 作用 |
---|---|
rosmsg list | 列出系統上所有的msg |
rosmsg show msg_name | 顯示某個msg的內容 |
Topic 他是一個單向傳遞的一種方式,但是有時候這種方式並不能滿足我們的需求,假設我現在有一個人臉識別的機器人,他有一個功能是透過你的照片來判斷人臉,而這個功能在沒有抓到人臉的時候是不需要去運行的,但是當我們用 topic 的方式時,我們就會一直等啊等地持續的運行,這樣子非常的消耗資源。接下來要講的 Service 就可以完美的解決這個問題。
Service 他是一種請求查詢式的服務架構,他跟 Topic 最大的區別變在於他是雙向的,他不僅可以發送訊息還可以接受到反饋,所以 Service 可以分成兩部分,客戶端跟伺服器端,客戶端透過發送請求給伺服器端來取得一個回復。
Service 他是採取同步的通訊方式,也就是說 Node A 在發出請求後,會等到 Node B 的回復後,才會繼續做事情,Node A 在等待的過程中是處於封閉的狀態,這樣的通訊規則不會造成傳遞衝突或是高系統資源的使用,只有在接受請求才執行,是十分好的一種方法。
最後我們來講一些常用的指令
rosservice 指令 | 作用 |
---|---|
rosservice list | 顯示服務列表 |
rosservice info | 印出服務訊息 |
rosservice type | 印出服務類型 |
rosservice uri | 印出服務 ROSRPC uri |
rosservice find | 按造服務的類型找服務 |
rosservice call | 使用所提供的 args 調用服務 |
rosservice args | 印出服務參數 |
在這邊我們把 Topic 跟 Service 來做一個比對
名稱 | Topic | Service |
---|---|---|
通訊方式 | 異步通訊 | 同步通訊 |
原理 | TCP/IP | TCP/IP |
通訊方式 | Publish-Subscribe | Request-Reply |
映射關系 | Publish-Subscribe(多對多) | Request-Reply(多對一) |
特點 | 接受者收到資訊會回調(Callback) | 遠程過程呼叫(RPC)服務器端的服務 |
應用場景 | 連續、高頻的資訊發布 | 偶爾使用的功能/具體的任務 |
舉例 | 雷達 | 開關傳感器、拍照 |
SRV 他是 Service 的一種訊息格式,跟 MSG 是很像的東西, 他被定義在 .src 文件中,SRV 文件它包含請求跟回應的格式,這邊以追蹤人體來當作範例
bool start_detect
---
my_pkg/HumanPose[] pose_data
上面是請求的格式,下面是回應的格式,中間用三個短橫來區隔。
在定義完 SRV 跟 MSG 後,這邊有兩個文件需要注意一下,我們需要添加一些依賴上去,第一個是package.xml ,其中 ** ** 是需要添加的地方
<build_depend>** message_generation **</build_depend>
<run_depend>** message_runtime **</run_depend>
第二個是 CMakeList.txt
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) **
最後我們來講一些常用的指令
rossrv 指令 | 作用 |
---|---|
rossrv show | 顯示服务描述 |
rossrv list | 列出所有服務 |
rossrv md5 | 顯示服務md5sum |
rossrv package | 列出 package 中的服務 |
rossrv packages | 列出含服務的 package |
我們在之前啟動 master 的時候,有提到說參數服務器 Parameter server ,他是會一起被啟動的,那他到底在幹嘛呢?他負責維護一個字典,他跟 topic 跟 service 都是不太一樣的,他是節點用來存參數,他可以提供任意節點進行調用。
如果你有學過 python,他就是裡面的 dict 的那種概念,每一組 key 都會對應到一組 value,我們通常會將一些不太會需要修改的值給丟上去,而 Parameter server 的設定可以分成指令、launch文件、 node 三種方式
rosparam 指令 | 作用 |
---|---|
rosparam set param_key param_value | 設置參數 |
rosparam get param_key | 顯示參數 |
rosparam load file_name | 從文件讀取參數 |
rosparam dump file_name | 存參數到文件裡 |
rosparam delete | 刪除參數 |
rosparam list | 列出參數名稱 |
在 launch 文件中有很多標籤,跟 參數服務器有關的就只有 param 跟 rosparam,但是param 只會設定一個參數,可以看下面的例子
<param name="publish_frequency" value="20.0"/>
在這邊可以看到這邊是去指定說 "publish_frequency" 他的值是 20.0,或是他有另一種方式
<param name="robot_description" command="$(find xacro)/xacro.py $(find robot_sim_demo)/urdf/robot.xacro" />
這邊他並沒有說 value 是多少,而是一個命令,他的意思是 xacro.py 這個程式去執行 robot.xacro 這個文件得到的結果就是我們的值。
另一種標籤 rosparam 他的用法就比較固定,一般是先指定一個 yaml 文件,然後對她使用 command,在下面這個例子中,可以看成是 `rosparam load xbot-u_control.yaml```
<rosparam file="$(find robot_sim_demo)/param/xbot-u_control.yaml" command="load"/>
除了前面兩種用法,還有一種方法是修改 ROS 的程式碼,也就是利用 API 來使用參數服務器,這個會在後面跟 ROS CPP 、 PY 的地方一起說明。
接下來來說最後一種 ROS 的通訊方式,他也是最為麻煩(複雜)的方式,他跟 Service 很像,但是因為 Service 太過於簡單,有時候是不合適的,像是一台汽車再開的時候,他一直往前開,但是突然出現一個人,我們需要緊急的停止汽車,這時如果是 Service 他是無法中途停止的,但是 Action 卻可以,這是因為他多了一個機制,就是在伺服器端在跑的時候,他是會時時的吐資訊給你,你也可以中途去丟指令給他,讓他知道需要中斷,這樣你可以知道伺服器端是不是死了,或是跑很慢,所以這個是比較適合現今許多較為複雜的架構去做使用的,其通常用在長時間、需要中斷的任務之中。
.action 檔包含了三個部分,分別是目標、回復、結果。
這邊以洗碗機器人來當作範例
我們先去定義說我們要讓哪一台洗碗機器人去做事,然後在過程中回傳(feedback)說現在做幾%了,最後會回傳一個結果給我們,跟我們說他洗了多少個碗
# 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
以上就是 Action 這種通訊方式的講解。
到現在為止,我們了解了 ROS 中的四種通訊方式,我們可以來比較這四種通訊方式,來思
考說每一種通訊方式的優缺點和他適合用在哪邊,在正確的地方用正確的通訊方式,這樣整個 ROS 的通訊
會更加的有效率,我們的機器人也會更加的靈活 www
#Arm Arm Platforms