当前位置:网站首页>2.ROS通信机制
2.ROS通信机制
2022-08-03 05:10:00 【janedipan】
这里写自定义目录标题
在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内置turtlesim案例,结合ROS命令获取节点话题、话题消息、服务、服务消息与参数的信息,最终再以编码的方式实现乌龟的控制、乌龟位姿的订阅、乌龟生成和乌龟窗体背景颜色的修改
1.话题发布
实现分析:
- 关键节点有两个,一个是乌龟运动显示节点turtlesim_node,另一个是控制节点,两者是通过订阅模式实现通信的,而之前- 运动控制节点是通过turtle_teltop_key键盘控制,现在需要自定义控制节点;
- 控制节点自定义,需要了解控制节点与显示节点通信使用的话题与消息;
- 通过cpp或py编写运动控制节点,通过指定的话题,按照一定的逻辑发布消息即可
实现流程:
- 获取topic、data、type
- 自定义pub编写控制节点
- 启动roscore、turtlesim_node以及自定义的控制节点,查看运行结果
启动完TurtleSim窗口后,另开cmd,键入rostopic list
获取当前话题topic,然后键入rqt_graph
打开计算图,获知两个话题之间的topic,再者rostopic info <topic>
确定消息类型Type—>msg,然后rosmsg info <Type>
获取消息格式
可获得Topic为/turtle1/cmd_vel
;Type为 geometry_msgs/Twist
;以及消息格式为:
其中linear为线速度(m/s),angular为角速度(rad/s);我们需要关注的是linear-float64 x
,angular-float64 z
以此为基础angular:x-翻滚角,y-俯仰角,z-偏航角
可调用rostopic echo /turtle1/cmd_vel
查看topic打印信息,验证消息格式
使用rostopic pub命令,可发布简单控制命令rostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist "linear: x, y, z angular: x, y, z"
python实现turtle圆周运动
<package>—>plumbing_test,在demo03下创建新的功能包
# plumbing_test/scripts/test01_pub_twist_p.py
#! /usr/bin/env python
import rospy
from geometry_msgs.msg import Twist
if __name__=='__main__':
rospy.init_node('my_control_p')
pub=rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
rate=rospy.Rate(10)
twist=Twist()
twist.linear.x=0.5
twist.linear.y=0.0
twist.linear.z=0.0
twist.angular.x=0.0
twist.angular.y=0.0
twist.angular.z=0.5
while not rospy.is_shutdown():
pub.publish(twist)
rate.sleep()
#添加权限,配置文件,编译
# roscore
# rosrun turtlesim turtlesim_node
# rosrun plumbing_test test01_pub_twist_p.py
2.话题订阅
获取turtle位姿信息、线速度、角速度
实现分析:启动turtle显示以及运动控制节点并控制乌龟运动;通过ROS命令,获取乌龟位姿发布的话题以及消息;编写订阅节点,订阅并打印乌龟的位姿信息
准备工作
实现流程:1.通过ros命令获取话题与消息信息 2.编码实现位姿获取节点 3.启动roscore、turtlesim_node、控制节点以及位姿订阅节点,控制乌龟运动并输出乌龟位姿
使用launch文件启动turtle的GUI以及键盘控制
# plumbing_test/launch/start_turtle.launch
<!-- 启动turtle GUI和key控制节点>
<launch>
<node pkg="turtlesim" type="turtlesim_node" name="turtle1" output="screen" />
<node pkg="turtlesim" type="turtle_teleop_key" name="key" ouput="screen" />
</launch>
roslaunch pluming_test start_turtle.launch
rostopic list
—> /turtle1/pose 查看话题信息rostopic info /turtle1/pose
—>Type: turtlesim/Pose 查看话题数据类型信息rosmsg info turtlesim/Pose
查看数据格式
运用命令的方式获取turtle位姿rostopic echo /turtle1/pose
python编码实现
# plumbing_pub_test/scripts/test02_sub_pose_p.py
#! /usr/bin/env python
import rospy
from turtlesim.msg import Pose
def doPose(pose):
rospy.loginfo('turtle位姿坐标({:.2f}, {:.2f}),朝向为{:.2f},线速度为{:2f}, 角速度为{:.2f}'.format(pose.x,pose.y,pose.theta,pose.linear_velocity,pose.angular_velocity)
if __name__=='__main__':
rospy.init_node('sub_pose')
sub=rospy.Subscribuer('/tuttle1/pose',Pose,doPose,queue_size=10)
rospy.spin()
# 添加权限,配置文件,编译
3.服务调用
编码向turtlesim发送请求,在turtle显示节点的窗体指定位姿生成一乌龟,这是一个服务请求操作
准备工作
实现分析:
- 启动显示乌龟的显示节点
- 通过ROS命令,获取乌龟生成服务的服务名称以及服务消息类型
- 编写服务请求节点,生成新的乌龟
实现步骤:
- ROS命令获取服务与服务消息等
- 编码实现服务请求节点
- 启动roscore, turtlesim_node, turtle生成节点,生成新的乌龟
rosservice list
查看当前服务列表,找到/spawn
rosservice info /spawn
查看服务信息:Node节点,URI本地地址,Type数据类型,Args请求服务的话得输入的参数rossrv info turtlesim/Spawn
查看具体参数格式
rosservice call /spawn
tab补齐参数信息,改变对应参数信息即可生成新的turtle
Python编码实现
# plumbing_pub_test/scripts/test03_service_client_p.py
#! /usr/bin/env pyhton
import rospy
from turtlesim.srv import Spawn,SpawnRequest,SpawnResponse
if __name__=='__main__':
rospy.init_node('service_call_p')
client=rospy.ServiceProxy('/spawn',Spawn)
# 创建服务端对象,输入'/spawn'是服务名称,Spawn是参数类型
request=SpawnRequest()
# 创建请求对象
request.x=4.5
request.y=2.0
reuqest.theta=3
request.name='no1'
client.wait_for_service()
# 等待服务端启动
try:
response=client.call(request)
# call()方法会返回创建turtle的name
rospy.loginfo('the new turtle is {}'.format(response))
except Exception as e:
rospy.loginfo('there is a problem with the input parameter format')
# 添加权限,配置文件,编译
4.参数服务器
修改turtlesim乌龟显示节点窗体背景色,已知背景色是通过参数服务器的方式一rgb方式设置的
准备工作
实现分析:
- 启动turtlesim节点
- 通过ROS命令,获取参数服务器中设置背景色的参数
- 编写参数设置节点,修改参数服务器中参数值
实现流程
- 通过ros命令获取参数信息
- 编码实现设置节点参数
- 启动roscore, turtlesim_node与参数竖直节点,查看运行结果
先启动turtlesim窗口
roscore
rosrun turtlesim turtlesim_node
rosparam list # 列出系统中的参数,找到/turtlesim/background_b,g,r
rosparam get /turtlesim/background_b\g\r #依次调用以获得三个参数对应的值
rosparam set /turtlesim/background_b\g\r xx #分别修改三个参数的值
# 需重新启动turtlesim节点,背景颜色才能改变
rosrun turtlesim turtlesim_node
python编码实现
# plumbing_pub_test/scripts/test04_param_p.py
#! /usr/bin/env python
import rospy
if __name__=='__main__':
rospy.init_node('change_bgColor_p')
rospy.set_param('/turtlesim/background_r', 100)
rospy.set_param('/turtlesim/background_g', 50)
rospy.set_param('/turtlesim/background_b', 200)
# 添加权限,配置文件,编译
其他实现方式
- 命令行实现
rosparam set /turtlesim/background ...
- 启动节点时,直接设置参数
rosrun turtlesim turtlesim_node _background_r:100 _background_g=50 _background_b=200
- 通过launch文件传参,后续学习补充
5.通信机制比较
三种通信机制中,参数服务器是一种数据共享机制,可以在不同节点之间共享数据;话题通信和服务通信时在不同节点之间传递数据,三者时ROS中最基础的通信机制
二者的实现流程设计四个要素
- 消息的发布方,客户方publisher,client
- 消息的订阅方,服务器subscriber,server
- 话题名称topic、service
- 数据载体msg,src
topic | service | |
---|---|---|
通信模式 | 发布/订阅 | 请求/响应 |
同步性 | 异步 | 同步 |
底层协议 | ROSTCP/ROSUDP | ROSTCP/ROSUDP |
缓冲区 | 有 | 无 |
时时性 | 弱 | 强 |
节点关系 | 多对多 | 一对多 |
通信数据 | msg | srv |
使用场景 | 连续高频的数据发布和接收 | 偶尔调用或执行的某一项功能 |
边栏推荐
- Flask的简单介绍及使用方法简介
- Lambda表达式案例
- ss-4.1-1个eurekaServer+1个providerPayment+1个consumerOrder
- 2. 两数相加
- Interface testing framework combat (3) | JSON request and response assertion
- 1089 狼人杀-简单版 (20 分)
- 【Biotin Azide|cas:908007-17-0】Price_Manufacturer
- web安全-sql注入漏洞
- C# async and multithreading
- BIOTIN ALKYNE CAS: 773888-45-2 Price, Supplier
猜你喜欢
随机推荐
typescript43-类型兼容性说明
typescript44-对象之间的类兼容器
ss-2.子项目互相访问(order80 -> payment8001)
typescript42-readonly修饰符
【特征选取】计算数据点曲率
在树莓派上搭建属于自己的网页(2)
The problem that the rosbag tool plotjuggler cannot open rosbag
Harmony OS ets ArkUI 】 【 】 the development basic page layout and data connection
Redis6学习笔记
typescript41-class类的私有修饰符
Djiango第三次培训
Newifi路由器第三方固件玩机教程,这个路由比你想的更强大以及智能_Newifi y1刷机_smzdm
1079 延迟的回文数 (20 分)
typescript45-接口之间的兼容性
Response 重写设置返回值
安装IIS服务(Internet信息服务(Internet Information Services,简写IIS,互联网信息服务)
建造者模式(Builder Pattern)
Kaggle(四)Scikit-learn
web安全-命令执行漏洞
Flink state