這裡寫自定義目錄标題
- 1.話題通信
-
- 概述
- 自定義msg
- 話題通信實作(python)
- 2.服務通信
-
- 概述
- 自定義srv
- 服務通信自定義srv調用B(python)
- 注意事項
- 3.參數伺服器
-
- 概述
- 參數操作(python)
- 4.通信機制比較
在ros中每一個功能點是一個單獨的程序,每一個程序都是獨立運作的,ros是程序(也稱為nodes)的分布式架構
ros中的基本通信機制主要有如下三種實作政策:
-
話題通信-釋出訂閱模式
控制turtle路徑;擷取位姿
-
服務通信-請求響應模式
在指定位置生成turtle
-
參數伺服器-參數共享模式
修改turtle背景顔色
1.話題通信
概述
一個節點釋出消息,另外一個節點訂閱該消息,即一個釋出方Talker,訂閱方Listener,傳輸是資料就是話題topic
目标:使用自定義資料類型實作資料互動
自定義msg
關于自定義資料msg
# <package>/msg/<class_name>
string name
uint32 age
float64 height
# 然後要配置檔案<package>/CMakeLists.txt, package.xml,再編譯
2.配置CMakeLists.txt檔案
# <package>/package.xml
# 添加到對應位置
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
3.配置package.xml檔案
# <package>/CMakeList.txt
find_package(catkin REQUIRED COMPONENTS
-snap-
message_generation
)
# 編譯需要
add_message_files(
FILES
Person.msg
)
generate_message(
DEPENDENCIES
std_msgs
)
# find_package所依賴
catkin_package(
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
)
話題通信實作(python)
1.配置vscode settings.json,将編譯生成的中間檔案dist-packages目錄添加
2.關于Talker部分代碼
#! /usr/bin/env python
# scripts/demo03_pub_person_p.py
import rospy
from plumbing_pub_sub.msg import Person
if __name__=="__main__":
rospy.init_node("China")
pub=rospy.Publisher("person",Person,queue_size=10)
p=Person()
p.name="Lihua"
p.age=18
p.height=171.3
rate=rospy.Rate(0.5)
while not rospy.is_shutdown():
pub.publish(p)
rospy.loginfo("the published messages is: {0}, {1}, {2}".format(p.name, p.age, p.height))
rate.sleep()
# 添權重限、配置檔案、編譯
3.關于Listener實作
#! scripts/usr/bin/env python
# scripts/demo04_sub_person_p.py
import rospy
from plumbing_pub_sub.msg import Person
def doPerson(p):
rospy.loginfo("the subscribed message is: {}, {}, {}".format(p.name, p.age, p.height))
if __name__=="__main__":
rospy.init_node("Listener1")
sub=rospy.Subscriber("person",Person, doPerson)
rospy.spin()
# 添權重限、配置檔案、編譯
2.服務通信
概述
概念:服務通信基于請求響應模式,是一種應答機制,寄:A節點向B節點發送請求,B節點接收請求并響應結果傳回給A
作用:用于偶然的、對時效性要求、對一定邏輯處理需求的資料傳輸場景
案例:實作兩個數字的求和,用戶端節點發送兩個數字,伺服器端點接收數字後求和并傳回給用戶端
自定義srv
srv檔案内可用資料類型與msg檔案一緻,且定義srv實作流程與自動逸msg實作流程類似:按照固定格式建立srv檔案;編輯配置檔案;生成
1.建立plumbing_server_client --><package_>
# <package>/srv/<xx.srv>-->AddInts
int32 num1
int32 num2
---
int32 num3
# srv中請求和響應的資料用---分割
2.配置CMakeLists.txt、package.xml檔案
# <package>/package.xml
# 添加到對應位置
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
# <package>/CMakeList.txt
find_package(catkin REQUIRED COMPONENTS
-snap-
message_generation
)
add_service_files(
FILES
<xx>.srv
)
generate_message(
DEPENDENCIES
std_msgs
)
# find_package所依賴
catkin_package(
CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
)
# 配置完兩個檔案按後進行編譯生成中間檔案
服務通信自定義srv調用B(python)
1.配置vscode settings.json,将編譯生成的中間檔案dist-packages目錄添加
2.Server實作
# <package>/<scripts>/<xx.py>-->demo01_server_p.py
#! usr/bin/env python
import rospy
from plumbing_server_client.srv import AddInts,AddIntsRequest,AddIntsResponse
def doNum(request):
# 封裝并處理請求的資料
num1 = request.num1
num2 = request.num2
s = num1+num2
response = AddIntsResponse()
response.num3 = s
# 此處響應response的屬性名必須是srv定義的變量名
rospy.loginfo("the collected data are: {}, {}".format(num1, num2))
return response
if __name__=="__main__":
rospy.init_node("server")
rospy.loginfo("the server has been started")
# 建立server對象
server=rospy.Service('Sum', AddInts, doNum)
rospy.spin()
# 添權重限,配置檔案,編譯
# rosservice call sum "num1:10 num2:20"
3.Client實作
# <package>/<scripts>/<xx.py>-->demo02_server_p.py
#! usr/bin/env python
import rospy
from plumbing_server_client.srv import AddInts, AddIntsRequest, AddIntsResponse
if __name__=="__main__":
rospy.init_node("client")
client=rospy.ServiceProxy('Sum', AddInts)
response = client.call(num1=10, num2=20)
rospy.loginfo("the response data is {}".format(response.num3))
# 添權重限,配置檔案,編譯
3+.Client優化實作——可在執行節點時,動态傳入參數
在cmd中調用節點指令時,可附加上輸入的參數
#! usr/bin/env python
import rospy
from plumbing_server_client.srv import AddInts, AddIntsRequest, AddIntsResponse
if __name__=="__main__":
if len(sys.argv) != 3:
# argv為數組,需先判斷數組的長度;[0]是檔案名,[1],[2]對應着num1,num2
rospy.logerr("Incoming data is incorrect")
sys.exit(1)
rospy.init_node("client")
client=rospy.ServiceProxy('Sum', AddInts)
num1=int(sys.argv[1])
num2=int(sys.argv[2])
#client.wait_for_service()
#rospy.wait_for_service('Sum')
# 等待伺服器啟動
response=client.call(num1, num2)
rospy.loginfo("the response data is {}".format(response.num3))
# 添權重限,配置檔案,編譯
# rosrun plumbing_server_client demo02_client_py x y
注意事項
存在問題:若client先于server啟動,會抛出異常;若想令client先于server啟動時不要抛出異常而是挂起,等待伺服器
實作:ros中内置了相關函數,這些函數可以判斷伺服器的狀态,如果伺服器沒有啟動,那麼就讓用戶端挂起
1.使用client用戶端對象方法
client.wait_for_service()
2.使用rospy方法
rospy.wait_for_service('topic')
3.參數伺服器
概述
參數伺服器主要用于不同節點之間的資料共享,參數伺服器相當于時獨立于所有節點的一個公共容器,可以将資料存儲容器中,可以被不同節點所調用,不同節點也可以往裡存儲伺服器
應用場景:導航實作時,會進行路徑規劃;全局路徑規劃,設計一個從出發點到目标點的大緻路徑;本地路徑規劃,會根據目前路況實時生成行進的路徑
參數伺服器,一般用于存在資料共享的一些應用場景
概念:已共享的方式實作不同節點之間資料互動的通信模式
作用:存儲一些多節點共享的資料,類似于全局變量
案例:實作參數 增删改查操作
參數可以使用資料類型
32-bit integers #4位元組整型資料
booleans #布爾值
strings #文本
doubles #浮點數
iso8601 dates #iso8601時間表示方法
lists #清單
dic #字典
base64-encoded binary data #以base64編碼的二進制資料
注意:參數伺服器不是為高性能而設計的,是以最好用于存儲靜态的非二進制的簡單資料
參數操作(python)
1.增&改
rospy.set_param(<key>, <value>)
# plumbing_param_server/scripts/demo01_param_set_p.py
#! usr/bin/env python
import rospy
if __name__=="__main__":
rospy.init_node('param_set_p')
rospy.set_param('type_p', 'common')
rospy.set_param('radius_p', 0.15)
# 新增兩組參數
# 添權重限,配置檔案,編譯
# rosrun
# rosparam list #列出目前參數-鍵
# rosparam get <key> #得到對應key的value
2.查詢參數
rospy.get_param(<key>, defaults)
rospy.get_param_cached(<key>, defaults)
rospy.get_param_names()
rospy.has_param(<key>)
# plumbing_param_server/scripts/demo02_param_get_p.py
#! /usr/bin/env python
import rospy
if __name__=="__main__":
rospy.init_node("get_param_p")
r=rospy.get_param('radius_p', 0.5)
# 擷取key=radius_p的value,并且設定預設值為0.5
p=rospy.get_param('radius', 0.5)
r1=rospy.get_param_cached('radius_p', 0.5)
# 從緩存裡查詢資料
keys=rospy.get_param_names()
# 擷取鍵,傳回清單?元組?
bool1=rospy.has_param('radius_p')
# 判斷key=radius_p是否存在傳回bool
rospy.loginfo('radius_p = {}'.format(r))
rospy.loginfo('radius = {}'.format(p))
rospy.loginfo('radius_p1 = {}'.format(r1))
for n in keys:
rospy.loginfo('key = {}'.format(n))
if bool1:
rospy.loginfo('radius_p exist')
else:
rosypy.loginfo('radius_p doesn\'t exist ')
3.删除
rospy.delete_param(<key>)
# plumbing_param_server/scriptsdemo03_param_del_p.py
#! /usr/bin/env python
import rospy
if __name__=='__main__':
rospy.init_node('del_param_p')
try:
rospy.delete_param('radius_p')
except Exception as e:
rospy.loginfo('the data doesns\'t exist')
4.通信機制比較
三種通信機制中,參數伺服器是一種資料共享機制,可以在不同節點之間共享資料;話題通信和服務通信時在不同節點之間傳遞資料,三者時ROS中最基礎的通信機制
二者的實作流程設計四個要素
- 消息的釋出方,客戶方publisher,client
- 消息的訂閱方,伺服器subscriber,server
- 話題名稱topic、service
- 資料載體msg,src
topic | service | |
---|---|---|
通信模式 | 釋出/訂閱 | 請求/響應 |
同步性 | 異步 | 同步 |
底層協定 | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
緩沖區 | 有 | 無 |
時時性 | 弱 | 強 |
節點關系 | 多對多 | 一對多 |
通信資料 | msg | srv |
使用場景 | 連續高頻的資料釋出和接收 | 偶爾調用或執行的某一項功能 |