2.1ROS通信机制


在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
topicservice
通信模式发布/订阅请求/响应
同步性异步同步
底层协议ROSTCP/ROSUDPROSTCP/ROSUDP
缓冲区
时时性
节点关系多对多一对多
通信数据msgsrv
使用场景连续高频的数据发布和接收偶尔调用或执行的某一项功能
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ROS通信机制包括话题(Topic)、服务(Service)和动作(Action)三种方式。 1. 话题:话题是一种发布/订阅(Publish/Subscribe)机制,发布者(Publisher)将消息发布到话题中,订阅者(Subscriber)从话题中接收消息。话题的实现流程如下: - 发布者指定话题名、消息类型和队列长度。 - 发布者将消息发布到话题中。 - 订阅者从话题中接收消息。 2. 服务:服务是一种请求/响应(Request/Response)机制,客户端(Client)向服务器(Server)发送请求,服务器对请求进行处理并返回响应。服务的实现流程如下: - 定义服务类型和服务名。 - 服务器等待客户端请求。 - 客户端向服务器发送请求。 - 服务器对请求进行处理并返回响应。 3. 动作:动作是一种异步请求/响应机制,客户端向服务器发送请求,服务器对请求进行处理并返回响应,但在响应返回之前,客户端可以取消请求或者向服务器发送取消请求。动作的实现流程如下: - 定义动作类型和动作名。 - 服务器等待客户端请求。 - 客户端向服务器发送请求。 - 服务器对请求进行处理并返回响应。 - 在响应返回之前,客户端可以取消请求或者向服务器发送取消请求。 相同点:话题、服务和动作都是ROS通信机制的一部分,它们都实现了不同的通信机制,使得ROS系统中的组件能够进行数据交换和协作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值