2.1ROS通信机制

这里写自定义目录标题

  • 1.话题通信
    • 概述
    • 自定义msg
    • 话题通信实现(python)
  • 2.服务通信
    • 概述
    • 自定义srv
    • 服务通信自定义srv调用B(python)
    • 注意事项
  • 3.参数服务器
    • 概述
    • 参数操作(python)
  • 4.通信机制比较

在ros中每一个功能点是一个单独的进程,每一个进程都是独立运行的,ros是进程(也称为nodes)的分布式框架
ros中的基本通信机制主要有如下三种实现策略:

  • 话题通信-发布订阅模式
    控制turtle路径;获取位姿
  • 服务通信-请求响应模式
    在指定位置生成turtle
  • 参数服务器-参数共享模式
    修改turtle背景颜色

1.话题通信

概述

一个节点发布消息,另外一个节点订阅该消息,即一个发布方Talker,订阅方Listener,传输是数据就是话题topic
2.1ROS通信机制_第1张图片

目标:使用自定义数据类型实现数据交互

自定义msg

关于自定义数据msg

#  /msg/
string name
uint32 age
float64 height

# 然后要配置文件/CMakeLists.txt, package.xml,再编译

2.配置CMakeLists.txt文件

# /package.xml
# 添加到对应位置
message_generation
message_runtime

3.配置package.xml文件

# /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
作用:用于偶然的、对时效性要求、对一定逻辑处理需求的数据传输场景
案例:实现两个数字的求和,客户端节点发送两个数字,服务器端点接收数字后求和并返回给客户端
2.1ROS通信机制_第2张图片

自定义srv

srv文件内可用数据类型与msg文件一致,且定义srv实现流程与自动逸msg实现流程类似:按照固定格式创建srv文件;编辑配置文件;生成
1.创建plumbing_server_client -->

# /srv/-->AddInts

int32 num1
int32 num2
---
int32 num3

# srv中请求和响应的数据用---分割

2.配置CMakeLists.txt、package.xml文件

# /package.xml
# 添加到对应位置
message_generation
message_runtime
# /CMakeList.txt
find_package(catkin REQUIRED COMPONENTS
	-snap-
	message_generation
)

add_service_files(
	FILES
	.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实现

# //-->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实现

# //-->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.参数服务器

概述

参数服务器主要用于不同节点之间的数据共享,参数服务器相当于时独立于所有节点的一个公共容器,可以将数据存储容器中,可以被不同节点所调用,不同节点也可以往里存储服务器
应用场景:导航实现时,会进行路径规划;全局路径规划,设计一个从出发点到目标点的大致路径;本地路径规划,会根据当前路况实时生成行进的路径

参数服务器,一般用于存在数据共享的一些应用场景
概念:已共享的方式实现不同节点之间数据交互的通信模式
作用:存储一些多节点共享的数据,类似于全局变量
案例:实现参数 增删改查操作
2.1ROS通信机制_第3张图片
参数可以使用数据类型

32-bit integers #4字节整型数据
booleans #布尔值
strings #文本
doubles #浮点数
iso8601 dates #iso8601时间表示方法
lists #列表
dic #字典
base64-encoded binary data #以base64编码的二进制数据

注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据


参数操作(python)

1.增&改
rospy.set_param(, )

# 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的value

2.查询参数
rospy.get_param(, defaults)
rospy.get_param_cached(, defaults)
rospy.get_param_names()
rospy.has_param()

# 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()

# 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
使用场景 连续高频的数据发布和接收 偶尔调用或执行的某一项功能

你可能感兴趣的:(ROS学习日志,ubuntu,vscode)