ROS-参数服务器、常用命令、通信机制实操及通信机制进阶_ros通信
一、参数服务器
1.参数服务器涉及到的三个角色:
ROS Master (管理者)、Talker (参数设置者)、Listener (参数调用者)
ROS Master 作为一个公共容器保存参数,Talker 可以向容器中设置参数,Listener 可以获取参数。
流程:
(1)Talker 设置参数
Talker 通过 RPC 向参数服务器发送参数(包括参数名与参数值),ROS Master 将参数保存到参数列表中。
(2)Listener 获取参数
Listener 通过 RPC 向参数服务器发送参数查找请求,请求中包含要查找的参数名。
(3)ROS Master 向 Listener 发送参数值
ROS Master 根据步骤2请求提供的参数名查找参数值,并将查询结果通过 RPC 发送给 Listener。
参数可使用数据类型:32-bit integers,booleans,strings,doubles,iso8601 dates,lists,base64-encoded binary data,字典
注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据
2.参数操作(python)
(1)参数服务器新增参数
在终端中运行:chmod +x ~/catkin_ws/src/demo3/scripts/add_data.py加入权限
先在终端运行roscore
使用rosparam list
可以查看参数是否已经设置进入参数服务器
(2)获取数据
跟上面相同配置权限等操作完成后编译运行
先运行roscore,再运行如下内容:
(3)删除数学
配置完权限编译之后运行:
先运行roscore
二、常用命令
在 ROS 同提供了一些实用的命令行工具,可以用于获取不同节点的各类信息,常用的命令如下:
rosnode:操作结点
rostopic:操作话题
rosservice:操作服务
rosmsg:操作msg消息
rossrv:操作srv消息
rosparam:操作参数
作用:和之前介绍的文件系统操作命令比较,文件操作命令是静态的,操作的是磁盘上的文件,而上述命令是动态的,在ROS程序启动后,可以动态的获取运行中的节点或参数的相关信息。
参考文档:http://wiki.ros.org/ROS/CommandLineTools
(1)rosnode 是用于获取节点信息的命令
rosnode ping 测试到节点的连接状态
rosnode list 列出活动节点
rosnode info 打印节点信息
rosnode machine 列出指定设备上节点
rosnode kill 杀死某个节点
rosnode cleanup 清除不可连接的节点,启动乌龟节点,然后 ctrl + c 关闭,该节点并没被彻底清除,可以使用 cleanup 清除节点
(2)rostopic包含rostopic命令行工具,用于显示有关ROS 主题的调试信息,包括发布者,订阅者,发布频率和ROS消息。它还包含一个实验性Python库,用于动态获取有关主题的信息并与之交互。
rostopic bw 显示主题使用的带宽
rostopic delay 显示带有 header 的主题延迟,列出消息头信息
rostopic echo 打印消息到屏幕,获取指定话题当前发布的消息
rostopic find 根据消息类型查找主题
rostopic hz 显示主题的发布频率
rostopic info 显示主题相关信息,获取当前话题的小关信息,消息类型,发布者信息,订阅者信息
rostopic list(-v) 显示所有活动状态下的主题:
直接调用即可,控制台将打印当前运行状态下的主题名称
rostopic list -v : 获取话题详情(比如列出:发布者和订阅者个数...)
rostopic pub 将数据发布到主题:
可以直接调用命令向订阅者发布消息
为roboware 自动生成的 发布/订阅 模型案例中的 订阅者 发布一条字符串
rostopic pub /主题名称 消息类型 消息内容
rostopic pub /chatter std_msgs gagaxixi
rostopic type 打印主题类型,列出话题的消息类型
(3)rosmsg是用于显示有关 ROS消息类型的 信息的命令行工具。
rosmsg show 显示消息描述
rosmsg show 消息名称 eg.rosmsg show turtlesim/Pose
rosmsg info 显示消息信息
rosmsg list 会列出当前 ROS 中的所有 msg
rosmsg md5 显示 md5 加密后的消息(一种校验算法)http://wiki.ros.org/ROS/Technical%20Overview#Message_serialization_and_msg_MD5_sums
rosmsg package 显示某个功能包下的所有消息msg
//rosmsg package 包名
rosmsg package turtlesim
rosmsg packages 列出包含消息的功能包
(4)rosservice包含用于列出和查询ROSServices的rosservice命令行工具。
rosservice args 打印服务参数 rosservice args /spawn
x y theta name
rosservice call 使用提供的参数调用服务
rosservice find 按照服务类型查找服务
rosservice info 打印有关服务的信息
rosservice list 列出所有活动的服务(service)
rosservice type 打印服务类型
rosservice uri 打印服务的 ROSRPC(服务器) uri
(5)rossrv是用于显示有关ROS服务类型的信息的命令行工具,与 rosmsg 使用语法高度雷同。
rossrv show 显示服务消息详情
rossrv info 显示服务消息相关信息
rossrv list 列出所有服务srv信息
rossrv md5 显示 md5 加密后的服务消息
rossrv package 显示某个包下所有服务消息
//rossrv package 包名 rossrv package turtlesim
rossrv packages 显示包含服务消息的所有包
(6)rosparam包含rosparam命令行工具,用于使用YAML编码文件在参数服务器上获取和设置ROS参数。
rosparam set 设置参数
rosparam get 获取参数
rosparam load 从外部文件加载参数
rosparam dump 将参数写出到外部文件
rosparam delete 删除参数
rosparam list 列出所有参数
三、通信机制实操
1.话题发布:
配置权限:chmod +x *.py
运行所需要的命令:roscore
rosrun turtlesim turtlesim_node 启动turtlesim仿真器
rosrun demo4 cricle.py
运行结果:
2.话题订阅
配置权限如上
运行命令:roscore
rosrun turtlesim turtlesim_node 启动turtlesim仿真器
rosrun turtlesim turtle_teleop_key 启动键盘控制节点
roscore demo4 sub_pose.py
运行结果:
3.服务调用
配置权限如上
运行命令:roscore
rosrun turtlesim turtlesim_node
roscore demo4 set_turtle.py
4.参数设置
运行命令:roscore
roscore demo4 hehe.py
rosrun turtlesim turtlesim_node
运行结果:
四、通信机制的比较
三种通信机制中,参数服务器是一种数据共享机制,可以在不同的节点之间共享数据,话题通信与服务通信是在不同的节点之间传递数据的,三者是ROS中最基础也是应用最为广泛的通信机制。
这其中,话题通信和服务通信有一定的相似性也有本质上的差异,在此将二者做一下简单比较:
二者的实现流程是比较相似的,都是涉及到四个要素:
要素1: 消息的发布方/客户端(Publisher/Client)
要素2: 消息的订阅方/服务端(Subscriber/Server)
要素3: 话题名称(Topic/Service)
要素4: 数据载体(msg/srv)
可以概括为: 两个节点通过话题关联到一起,并使用某种类型的数据载体实现数据传输。
二者的实现也是有本质差异的,具体比较如下:
五、通信机制进阶
1.常用API
(1)ROS节点的初始化相关API;
(2)NodeHandle 的基本使用相关API;
(3)话题的发布方,订阅方对象相关API;
(4)服务的服务端,客户端对象相关API;
(5)时间相关API;
(6)日志输出相关API。
常用API的参考文档:
http://wiki.ros.org/APIs
https://docs.ros.org/en/api/roscpp/html/
(1)python初始化结点:
def init_node(name, argv=None, anonymous=False, log_level=None, disable_rostime=False, disable_rosout=False, disable_signals=False, xmlrpc_port=0, tcpros_port=0):
注释:
在ROS msater中注册节点
@param name: 节点名称,必须保证节点名称唯一,节点名称中不能使用命名空间(不能包含 \'/\')
@type name: str
@param anonymous: 取值为 true 时,为节点名称后缀随机编号
@type anonymous: bool
(2)话题与服务相关对象
对象获取:
class Publisher(Topic):
\"\"\"
在ROS master注册为相关话题的发布方
\"\"\"
def __init__(self, name, data_class, subscriber_listener=None, tcp_nodelay=False, latch=False, headers=None, queue_size=None):
\"\"\"
Constructor
@param name: 话题名称
@type name: str
@param data_class: 消息类型
@param latch: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时会将该消息发送给订阅者
@type latch: bool
@param queue_size: 等待发送给订阅者的最大消息数量
@type queue_size: int
\"\"\"
消息发布函数:
def publish(self, *args, **kwds):
\"\"\"
发布消息
\"\"\"
订阅对象:
class Subscriber(Topic):
\"\"\"
类注册为指定主题的订阅者,其中消息是给定类型的。
\"\"\"
def __init__(self, name, data_class, callback=None, callback_args=None,
queue_size=None, buff_size=DEFAULT_BUFF_SIZE, tcp_nodelay=False):
\"\"\"
Constructor.
@param name: 话题名称
@type name: str
@param data_class: 消息类型
@type data_class: L{Message} class
@param callback: 处理订阅到的消息的回调函数
@type callback: fn(msg, cb_args)
@param queue_size: 消息队列长度,超出长度时,头部的消息将被弃用
\"\"\"
服务对象:
class Service(ServiceImpl):
\"\"\"
声明一个ROS服务
使用示例::
s = Service(\'getmapservice\', GetMap, get_map_handler)
\"\"\"
def __init__(self, name, service_class, handler,
buff_size=DEFAULT_BUFF_SIZE, error_handler=None):
\"\"\"
@param name: 服务主题名称 ``str``
@param service_class:服务消息类型
@param handler: 回调函数,处理请求数据,并返回响应数据
@type handler: fn(req)->resp
\"\"\"
客户端对象:
class ServiceProxy(_Service):
\"\"\"
创建一个ROS服务的句柄
示例用法::
add_two_ints = ServiceProxy(\'add_two_ints\', AddTwoInts)
resp = add_two_ints(1, 2)
\"\"\"
def __init__(self, name, service_class, persistent=False, headers=None):
\"\"\"
ctor.
@param name: 服务主题名称
@type name: str
@param service_class: 服务消息类型
@type service_class: Service class
\"\"\"
消息发送函数:
def call(self, *args, **kwds):
\"\"\"
发送请求,返回值为响应数据
\"\"\"
服务等待函数:
def wait_for_service(service, timeout=None):
\"\"\"
调用该函数时,程序会处于阻塞状态直到服务可用
@param service: 被等待的服务话题名称
@type service: str
@param timeout: 超时时间
@type timeout: double|rospy.Duration
\"\"\"
(3)回旋函数
def spin():
\"\"\"
进入循环处理回调
\"\"\"
(4)时间
时刻:(获取时刻,或是设置指定时刻)
# 获取当前时刻
right_now = rospy.Time.now()
rospy.loginfo(\"当前时刻:%.2f\",right_now.to_sec())
rospy.loginfo(\"当前时刻:%.2f\",right_now.to_nsec())
# 自定义时刻
some_time1 = rospy.Time(1234.567891011)
some_time2 = rospy.Time(1234,567891011)
rospy.loginfo(\"设置时刻1:%.2f\",some_time1.to_sec())
rospy.loginfo(\"设置时刻2:%.2f\",some_time2.to_sec())
# 从时间创建对象
# some_time3 = rospy.Time.from_seconds(543.21)
some_time3 = rospy.Time.from_sec(543.21) # from_sec 替换了 from_seconds
rospy.loginfo(\"设置时刻3:%.2f\",some_time3.to_sec())
持续时间:(设置一个时间区间(间隔))
# 持续时间相关API
rospy.loginfo(\"持续时间测试开始.....\")
du = rospy.Duration(3.3)
rospy.loginfo(\"du1 持续时间:%.2f\",du.to_sec())
rospy.sleep(du) #休眠函数
rospy.loginfo(\"持续时间测试结束.....\")
持续时间与时刻运算:(为了方便使用,ROS中提供了时间与时刻的运算)
rospy.loginfo(\"时间运算\")
now = rospy.Time.now()
du1 = rospy.Duration(10)
du2 = rospy.Duration(20)
rospy.loginfo(\"当前时刻:%.2f\",now.to_sec())
before_now = now - du1
after_now = now + du1
dd = du1 + du2
# now = now + now #非法
rospy.loginfo(\"之前时刻:%.2f\",before_now.to_sec())
rospy.loginfo(\"之后时刻:%.2f\",after_now.to_sec())
rospy.loginfo(\"持续时间相加:%.2f\",dd.to_sec())
设置运行频率:
# 设置执行频率
rate = rospy.Rate(0.5)
while not rospy.is_shutdown():
rate.sleep() #休眠
rospy.loginfo(\"+++++++++++++++\")
定时器:
#定时器设置
\"\"\"
def __init__(self, period, callback, oneshot=False, reset=False):
Constructor.
@param period: 回调函数的时间间隔
@type period: rospy.Duration
@param callback: 回调函数
@type callback: function taking rospy.TimerEvent
@param oneshot: 设置为True,就只执行一次,否则循环执行
@type oneshot: bool
@param reset: if True, timer is reset when rostime moved backward. [default: False]
@type reset: bool
\"\"\"
rospy.Timer(rospy.Duration(1),doMsg)
# rospy.Timer(rospy.Duration(1),doMsg,True) # 只执行一次
rospy.spin()
回调函数:
def doMsg(event):
rospy.loginfo(\"+++++++++++\")
rospy.loginfo(\"当前时刻:%s\",str(event.current_real))
(5)其他函数
在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,
python 中则通过 rospy.is_shutdown() 来实现判断,导致节点退出的原因主要有如下几种:
节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
同名节点启动,导致现有节点退出;
程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())
另外,日志相关的函数也是极其常用的,在ROS中日志被划分成如下级别:
DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
WARN(警告):提醒一些异常情况,但程序仍然可以执行;
ERROR(错误):提示错误信息,此类错误会影响程序运行;
FATAL(严重错误):此类错误将阻止节点继续运行。
节点状态判断:
def is_shutdown():
\"\"\"
@return: True 如果节点已经被关闭
@rtype: bool
\"\"\"
结点关闭函数:
def signal_shutdown(reason):
\"\"\"
关闭节点
@param reason: 节点关闭的原因,是一个字符串
@type reason: str
\"\"\"
def on_shutdown(h):
\"\"\"
节点被关闭时调用的函数
@param h: 关闭时调用的回调函数,此函数无参
@type h: fn()
\"\"\"
日志函数:(使用示例)
rospy.logdebug(\"hello,debug\") #不会输出
rospy.loginfo(\"hello,info\") #默认白色字体
rospy.logwarn(\"hello,warn\") #默认黄色字体
rospy.logerr(\"hello,error\") #默认红色字体
rospy.logfatal(\"hello,fatal\") #默认红色字体
2.python模块导入
需求:首先新建一个Python文件A,再创建Python文件UseA,在UseA中导入A并调用A的实现。
实现:
-
新建两个Python文件,使用 import 实现导入关系;
文件A实现(包含一个变量):
#! /usr/bin/env pythonnum = 1000
文件B核心实现:
import osimport syspath = os.path.abspath(\".\")# 核心sys.path.insert(0,path + \"/src/plumbing_pub_sub/scripts\")import tools........ rospy.loginfo(\"num = %d\",tools.num)
2.添加可执行权限、编辑配置文件并执行(与上面配置过程相同)