ROS通信机制进阶

目录

一、常用API

1.初始化

 2.话题与服务相关对象

2.1发布对象

2.2.订阅对象

2.3.服务对象

2.4.客户端对象

3.回旋函数

4.时间

4.1.时刻

4.2.持续时间

4.3.持续时间与时刻运算

4.4.设置运行频率

  4.5定时器

4.6.案例实现

5.其它函数 

5.1.节点状态判断

5.2.节点关闭函数

5.3.日志函数

 二.python文件导入

参考


一、常用API

1.初始化

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

  @argv=None  封装节点调用时传递的参数 

    @param anonymous: 取值为 true 时,为节点名称后缀随机编号,解决节点名称重名问题
    @type anonymous: bool


使用:
            1、argv使用:可以按照ros中指定的语法格式传参,ros可以解析并加以使用
            2、anonymous使用:节点名称为随机数

    """

  终端中使用 

 

 2.话题与服务相关对象

2.1发布对象

对象获取:

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):
        """
        发布消息
        """

代码:

#! /usr/bin/env python
# coding=UTF-8
""" 
需求: 实现基本的话题通信,一方发布数据,一方接收数据,
         实现的关键点:
         1.发送方
         2.接收方
         3.数据(此处为普通文本)

         PS: 二者需要设置相同的话题


    消息发布方:
        循环发布信息:HelloWorld 后缀数字编号

    实现流程:
        1.导包 
        2.初始化 ROS 节点:命名(唯一)
        3.创建发布者对象
        4.编写发布逻辑并发布数据
"""
# 导包
import rospy
from std_msgs.msg import String    #消息类型 std_msgs.msg导入是为了使我们可以重用std_msgs/String消息类型进行发布
if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一
    """
        作用:ROS初始化

        参数:
            name --- 设置节点名称
            argv = None --- 封装节点调用时传递的函数
            anonymous = False --- 可以为节点名称生成随机后缀,可以解决重名问题
        使用:
            1、argv使用:可以按照ros中指定的语法格式传参,ros可以解析并加以使用
            2、anonymous使用:节点名称为随机数
    """
    rospy.init_node("chenmo",anonymous=True)   #传入节点名称
    # 3.创建发布者对象
    """
        内容: latch
               bool值,默认值为False
        作用: 如果为 true,该话题发布的最后一条消息将被保存,并且后期当有订阅者连接时,会将该消息发送给新订阅者
        使用: latch=Ture

    """
    pub=rospy.Publisher("che",String,queue_size=10,latch=True)   #话题名称,消息类型
    # 4..编写发布逻辑并发布数据
    # 创建数据
    msg=String()   #创建空的消息类型为String的消息,
    msg_front="hellocehnmo"
    count=0   #计数器
    # 设置循环频率  
    rate=rospy.Rate(10)  
    # 循环体
    while not rospy.is_shutdown():
        count += 1  
        if count<10:
            msg.data=msg_front+str(count)
            # 发布数据   #发布对象: 前面创建的pub,  发布函数:.publish(),发布数据:前面创建的消息msg,  消息类型String
            pub.publish(msg) #将字符串发布到chatter话题上
           
            # 此循环调用rospy.loginf(), 它执行三项任务:将消息打印到屏幕上,将消息写入node的日志文件,并将消息写入rosout。
            #  rosout是一个方便的调试工具:你可以使用rqt_console提取消息,而不必查找带有node输出的控制台窗口。
            rospy.loginfo("写出的数据:%s",msg.data)
        rate.sleep()  


2.2.订阅对象

对象获取:

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: 消息队列长度,超出长度时,头部的消息将被弃用

        """

2.3.服务对象

对象获取:

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

        """

2.4.客户端对象

对象获取:

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():
    """
    进入循环处理回调 
    """

理解:

def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %s", data.data)


rospy.Subscriber("chatter", String, callback)
rospy.spin()


这声明你的节点订阅了消息类型为std_msgs.msg.String的chatter话题。收到新消息时,将以消息(String)作为第一个参数来调用回调函数。rospy.spin()只是使节点无法退出,直到该节点已关闭。

4.时间

4.1.时刻

获取时刻,或是设置指定时刻:

  #需求1:演示时间相关操作(获取当前时刻+设定指定时刻)
    rospy.init_node("hello_time")
    #获取时刻
    right_now=rospy.Time.now()  #将当前时刻(now函数被调用执行的那一刻)获取并封装成对象 参考的是1970.01.01 00.00
    rospy.loginfo("当前时刻:%.2d", right_now.to_sec())  #sec函数,获取当前时刻距离参考时刻1970.01.01 00.00 过去多久
    rospy.loginfo("当前时刻:%d", right_now.secs)

    # 设置指定时间
    time1=rospy.Time(100.5)   #将时间(参考时间1970.01.01 00.00 逝去100.5秒)封装成Time对象、
    time2=rospy.Time(100,35426)   #两个参数,秒和纳秒
    rospy.loginfo("指定时刻1:%.2f", time1.to_sec())
    rospy.loginfo("指定时刻2:%.2f", time2.to_sec())

    # 从某个时间值获取时间对象
    time3=rospy.Time.from_sec(200.23)
    rospy.loginfo("指定时刻3:%.2f", time3.to_sec())

4.2.持续时间

设置一个时间区间(间隔):


    #  需求2:设置一个时间区间(间隔):   程序执行中停顿5秒
    rospy.loginfo("休眠前-----------")
    #1.封装一个持续时间对象(5s)
    du=rospy.Duration(5,0)
    # 2.再将持续时间休眠
    rospy.sleep(du)
    rospy.loginfo("休眠后--------------")

4.3.持续时间与时刻运算

为了方便使用,ROS中提供了时间与时刻的运算:

  # 需求3:获取程序开始执行的时刻,且已知持续运行的时间,计算程序结束的时间
    #  获取一个时刻
    t1 = rospy.Time.now()
    # 设置一个持续时刻
    du1 = rospy.Duration(30)
    # 结束时刻 t2=t1+du1
    t2 = t1 + du1                       # 注意时刻与时刻之间不能相加减  ,持续时间可以于持续时间想加减
    rospy.loginfo("开始的时间:%.2d", t1.to_sec())
    rospy.loginfo("结束的时间:%.2d", t2.to_sec())

4.4.设置运行频率

# 需求4:设置运行频率,可以保持一定速率进行循环 
         # rate对象可以允许你指定自循环的频率。它会追踪记录自上一次调用
         # rate.sleep()后时间的流逝,并休眠直到一个频率周期的时间。参数为10时,我们期望每秒执行10次循环 
    rate = rospy.Rate(2) 
    while not rospy.is_shutdown:
        rate.sleep()
        rospy.loginfo("+++++++")

  

  4.5定时器

 ROS 中内置了专门的定时器,可以实现与 ros::Rate 类似的效果:

#需求5:   创建定时器,实现类似于rospr.Rate()的功能(每隔某个时间间隔执行某种操作)(Timer) 
    """ Constructor.
        @param period: desired period between callbacks  时间间隔
        @type  period: rospy.Duration
        @param callback: callback to be called     回调函数
        @type  callback: function taking rospy.TimerEvent
        @param oneshot: if True, fire only once, otherwise fire continuously until shutdown is called [default: False]
        @type  oneshot: bool
        @param reset: if True, timer is reset when rostime moved backward. [default: False]
        @type  reset: bool
    """
    timer=rospy.Timer(rospy.Duration(2),callback)  #创建一个定时器  功能:每隔了时间间隔(2s)执行回调函数(callback)
    # timer1=rospy.Timer(rospy.Duration(2),callback,oneshot=True)  #只执行一次定时器。隔两秒执行callback,后关闭
    rospy.spin()   #有回调函数时不要忘记 spin()

回调函数:

def callback(event):
    rospy.loginfo("--------")
    rospy.loginfo("调用回调函数的时刻:%.2f", event.current_real.to_sec())  #envent  获取时刻

 

4.6.案例实现

#! /usr/bin/env python
# coding=UTF-8

import rospy
"""
    需求1:

"""
def callback(event):
    rospy.loginfo("------------")
    rospy.loginfo("调用回调函数的时刻:%.2f", event.current_real.to_sec())   #envent  获取时刻



if __name__=="__main__":
    
 #需求1:演示时间相关操作(获取当前时刻+设定指定时刻)
    rospy.init_node("hello_time")
    #获取时刻
    right_now=rospy.Time.now()  #将当前时刻(now函数被调用执行的那一刻)获取并封装成对象 参考的是1970.01.01 00.00
    rospy.loginfo("当前时刻:%.2d", right_now.to_sec())  #sec函数,获取当前时刻距离参考时刻1970.01.01 00.00 过去多久
    rospy.loginfo("当前时刻:%d", right_now.secs)

    # 设置指定时间
    time1=rospy.Time(100.5)   #将时间(参考时间1970.01.01 00.00 逝去100.5秒)封装成Time对象、
    time2=rospy.Time(100,35426)   #两个参数,秒和纳秒
    rospy.loginfo("指定时刻1:%.2f", time1.to_sec())
    rospy.loginfo("指定时刻2:%.2f", time2.to_sec())

    # 从某个时间值获取时间对象
    time3=rospy.Time.from_sec(200.23)
    rospy.loginfo("指定时刻3:%.2f", time3.to_sec())


 #  需求2:设置一个时间区间(间隔):   程序执行中停顿5秒
    rospy.loginfo("休眠前-----------")
    #1.封装一个持续时间对象(5s)
    du=rospy.Duration(5,0)
    # 2.再将持续时间休眠
    rospy.sleep(du)
    rospy.loginfo("休眠后--------------")

 # 需求3:获取程序开始执行的时刻,且已知持续运行的时间,计算程序结束的时间
    #  获取一个时刻
    t1 = rospy.Time.now()
    # 设置一个持续时刻
    du1 = rospy.Duration(30)
    # 结束时刻 t2=t1+du1
    t2 = t1 + du1                       # 注意时刻与时刻之间不能相加减  ,持续时间可以于持续时间想加减
    rospy.loginfo("开始的时间:%.2d", t1.to_sec())
    rospy.loginfo("结束的时间:%.2d", t2.to_sec())

# # 需求4:设置运行频率,可以保持一定速率进行循环 
#          # rate对象可以允许你指定自循环的频率。它会追踪记录自上一次调用
#          # rate.sleep()后时间的流逝,并休眠直到一个频率周期的时间。参数为10时,我们期望每秒执行10次循环 
#     rate = rospy.Rate(2) 
#     while not rospy.is_shutdown():
#         rate.sleep()
#         rospy.loginfo("+++++++")

#需求5:   创建定时器,实现类似于rospr.Rate()的功能(每隔某个时间间隔执行某种操作)(Timer) 
    """ Constructor.
        @param period: desired period between callbacks  时间间隔
        @type  period: rospy.Duration
        @param callback: callback to be called     回调函数
        @type  callback: function taking rospy.TimerEvent
        @param oneshot: if True, fire only once, otherwise fire continuously until shutdown is called [default: False]
        @type  oneshot: bool
        @param reset: if True, timer is reset when rostime moved backward. [default: False]
        @type  reset: bool
    """
    timer=rospy.Timer(rospy.Duration(2),callback)  #创建一个定时器  功能:每隔了时间间隔(2s)执行回调函数(callback)
    # timer1=rospy.Timer(rospy.Duration(2),callback,oneshot=True)  #只执行一次定时器。隔两秒执行callback,后关闭
    rospy.spin()   #有回调函数时不要忘记 spin()
   

5.其它函数 

5.1.节点状态判断

def is_shutdown():
    """
    @return: True 如果节点已经被关闭
    @rtype: bool
    """

5.2.节点关闭函数

def signal_shutdown(reason):
    """
    关闭节点
    @param reason: 节点关闭的原因,是一个字符串
    @type  reason: str
    """
def on_shutdown(h):
    """
    节点被关闭时调用的函数
    @param h: 关闭时调用的回调函数,此函数无参
    @type  h: fn()
    """
#! /usr/bin/env python
# coding=UTF-8
""" 
        1.导包 
        2.初始化 ROS 节点:命名(唯一)
        3.创建发布者对象
        4.编写发布逻辑并发布数据
"""
# 导包
import rospy
from std_msgs.msg import String    #消息类型 std_msgs.msg导入是为了使我们可以重用std_msgs/String消息类型进行发布

def callback():
    rospy.loginfo("节点正在被关闭")

if __name__ == "__main__":
   
    rospy.init_node("chenmo",anonymous=True)   #传入节点名称
    # 3.创建发布者对象
    
    pub=rospy.Publisher("che",String,queue_size=10,latch=True)   #话题名称,消息类型
    # 4..编写发布逻辑并发布数据
    # 创建数据
    msg=String()   #创建空的消息类型为String的消息,
    msg_front="hellocehnmo"
    count=0   #计数器
    # 设置循环频率  
    rate=rospy.Rate(10)  
    # 循环体
    while not rospy.is_shutdown():
        count += 1  
        if count<10:
            msg.data=msg_front+str(count)
            # 发布数据   #发布对象: 前面创建的pub,  发布函数:.publish(),发布数据:前面创建的消息msg,  消息类型String
            pub.publish(msg) #将字符串发布到chatter话题上
           
            # 此循环调用rospy.loginf(), 它执行三项任务:将消息打印到屏幕上,将消息写入node的日志文件,并将消息写入rosout。
            #  rosout是一个方便的调试工具:你可以使用rqt_console提取消息,而不必查找带有node输出的控制台窗口。
            rospy.loginfo("写出的数据:%s",msg.data)
        else:
            #关闭节点
            rospy.on_shutdown(callback)
            rospy.signal_shutdown("关闭节点")
        rate.sleep()  


5.3.日志函数

使用示例

#! /usr/bin/env python
# coding=UTF-8

import rospy

if __name__=="__main__":
# 演示日志函数
    rospy.init_node("hello_log")

    rospy.logdebug("DEBUG消息...")
    rospy.loginfo("INFO消息...")
    rospy.logwarn("WARN消息...")
    rospy.logerr("ERROR消息...")
    rospy.logfatal("FATAL消息...")

 二.python文件导入

与C++类似的,在Python中导入其他模块时,也需要相关处理。

需求:首先新建一个Python文件A,再创建Python文件UseA,在UseA中导入A并调用A的实现。

实现:

  1. 新建两个Python文件,使用 import 实现导入关系;
  2. 添加可执行权限、编辑配置文件并执行UseA。

  1.文件A 实现

#! /usr/bin/env python
# coding=UTF-8

num = 1000

  2.UseA实现

#! /usr/bin/env python
# coding=UTF-8

import os
import sys
import rospy
"""
  异常:直接调用tools_A时,ModuleNotFoundError:No module named 'toole_A'
    原因:rosrun执行时默认的参考路径是工作空间下(demo02_ws),不是在scripts 下,在工作空间下无法查找依赖的模块
    解决:可以声明python的环境变量,当依赖某个模块时,先去指定的环境变量中查找依赖
"""

# 设置临时环境变量,
# 但是路径写死了,影响可移植性
# sys.path.insert(0, path + "/home/chenmo/demo02_ws/src/plumbing_apis/scripys")

# 优化
path = os.path.abspath(".")  # 动态获取tools.py的目录
rospy.loginfo(path)
sys.path.insert(0, path + "/src/plumbing_apis/scripts")

import tools_A

if __name__=="__main__":
    rospy.init_node("node")
    rospy.loginfo("num = %d", tools_A.num)


参考

https://zhuanlan.zhihu.com/p/440734846

http://www.autolabor.com.cn/book/ROSTutorials/di-3-zhang-ros-tong-xin-ji-zhi-jin-jie/31/314-shi-jian.htmlhttps://blog.csdn.net/qq_45152498/article/details/109880358 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值