ROS中的API:C++、Python(十)实现及理解

目录

简介

C++

初始化 

 话题与服务相关对象

 回旋函数

 ros::spinOnce()​

 ros::spin()​

时间相关API 

时刻

 持续时间

 持续时间与时刻运算

 定时器

节点生命周期API

 日志输出API

 Python

初始化API 

anonymous

话题与服务对象相关API 

 回旋函数

时间相关API

 时刻API 

持续时间(设置一个间隔)

持续时间与时刻运算 

运行频率 

定时器 (间隔某个时间间隔执行操作)

 节点相关API

节点状态判断 

节点关闭函数

 日志输出


简介

API个人理解:比如说ros::init这个函数其实你可以在代码中输入这个函数,然后编译后,ros系统内部会根据这个函数来对应重置节点,所以中文把API理解为接口,其实API就是一个命令去改动封装好的东西里的参数,对齐进行使用和更改 

首先,建议参考官方API文档或参考源码:

  • ROS节点的初始化相关API;
  • NodeHandle 的基本使用相关API;
  • 话题的发布方,订阅方对象相关API;
  • 服务的服务端,客户端对象相关API;
  • 时间相关API;
  • 日志输出相关API。

参数服务器相关API在第二章已经有详细介绍和应用,在此不再赘述。2.3.2 参数操作A(C++) · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程

另请参考:

  • APIs - ROS Wiki

  • roscpp: roscpp

ROS中的API:C++、Python(十)实现及理解_第1张图片

这个包就包含了很多重要的消息封装,速度消息,传感器消息等

是很重要的API

 ROS中的API:C++、Python(十)实现及理解_第2张图片

这个库就是之前一直用的很多命令,roscpp、rosparam、rospy等

 ROS中的API:C++、Python(十)实现及理解_第3张图片

 roscpp是ros一个关于c++实现的库,实现和ros的快速交互

通过话题、服务、参数来进行交互

是一个高性能库实现

还有关于roscpp的API文档roscpp: roscpp

 ROS中的API:C++、Python(十)实现及理解_第4张图片

 上面介绍了常用的API

以ros::NodeHandle为例roscpp: ros::NodeHandle Class Reference

里面介绍了该接口的详细应用

 rospy

ROS中的API:C++、Python(十)实现及理解_第5张图片

 介绍中说rospy并不是旨在提升性能的,而是为了更便利

C++

初始化 

 新建功能包

ROS中的API:C++、Python(十)实现及理解_第6张图片

ros::init(argc,argv,"erGouzi")
/*
  参数:
  1.argc-----封装实参的个数(N+1)传入的第一个是文件自身
  2.argv-----封装参数的数组
  3.name-----为节点命名(唯一性)
  4.options--节点启动选项
  返回值:void

  使用:
  1.argc与argv的使用
   如果按照ROS中的特定格式传入实参,那么ROS可以加以使用,比如用来设置全局参数,给节点重命名
   
  2.options的使用
   节点名称需要保证唯一,会导致一个问题,同一个节点不能重复启动,如果重名,之前的节点会被关闭。
   需求:特定场景下,需要一个节点多次启动且能正常运行,怎么办,此时就用options
   解决:设置启动项 ros::init_options::AnonymousName
     当创建ROS节点时,会在用户自定义的节点名称后缀岁数,从而避免重名问题



*/

ROS中的API:C++、Python(十)实现及理解_第7张图片

此时_length就被传递给argv了

ROS就会解析它,把它放进参数服务器

 ROS中的API:C++、Python(十)实现及理解_第8张图片

 ROS中的API:C++、Python(十)实现及理解_第9张图片

 需要一个节点启动多个

 这样每次启动会给节点名后面加上一个随机数避免了名称重复问题

ROS中的API:C++、Python(十)实现及理解_第10张图片

成功一个节点被启动两次

ROS中的API:C++、Python(十)实现及理解_第11张图片

此时可以看到有后缀

其实就是产生的随机数值(不重复) 

 话题与服务相关对象

在 roscpp 中,话题和服务的相关对象一般由 NodeHandle 创建。

NodeHandle有一个重要作用是可以用于设置命名空间,这是后期的重点,但是本章暂不介绍。

创建发布对象调用的API是来自

ros::NodeHandle nh;

再通过NodeHandle调用nh.advertise函数

这个函数会返回一个发布者对象

nh.advertise

作用:创建发布者对象

模板:被发布的消息类型

参数:
     1.话题名称


     2.队列长度


     3.latch(可选)

     是一个bool值 如果设置了true它会保存发布方最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发送给订阅者

使用:

  latch设置为true的作用?

  只发一次地图信息,但将latch设置为true,只要有新的订阅方连接就发送一次

  以静态地图发布为例,

  方案1:可以使用固定频率发送地图数据,但效率太低了

  方案2:可以将latch设置为true,并且发布方只发送一次数据,每当订阅者连接时,将地图数据      发送给订阅者 (只发送一次),这样提高了数据的发送效率

 ROS中的API:C++、Python(十)实现及理解_第12张图片

 ctrl+【 组合键是代码对齐

 启动终端

先让发布方发完十条消息,此时发布方不发数据了

ROS中的API:C++、Python(十)实现及理解_第13张图片

编写接收方(跟之前的话题通信的代码一致) 

 ROS中的API:C++、Python(十)实现及理解_第14张图片

 然后对应修改配置并且进行编译

运行终端

ROS中的API:C++、Python(十)实现及理解_第15张图片

没有接收到任何数据

然后关掉终端使用latch--》改为true

 ROS中的API:C++、Python(十)实现及理解_第16张图片

 然后编译终端运行

先启动发布方等数据发完

ROS中的API:C++、Python(十)实现及理解_第17张图片

 数据发完了,然后启动订阅方

ROS中的API:C++、Python(十)实现及理解_第18张图片

此时可以看到不同于上面,改为true后,订阅方拿到了最后一条数据

 回旋函数

 在ROS程序中,频繁的使用了 ros::spin() 和 ros::spinOnce() 两个回旋函数,可以用于处理回调函数。

 ros::spinOnce()ROS中的API:C++、Python(十)实现及理解_第19张图片

相同点:二者都用于处理回调函数;

不同点:ros::spin() 是进入了循环执行回调函数,而 ros::spinOnce() 只会执行一次回调函数(没有循环),在 ros::spin() 后的语句不会执行到,而 ros::spinOnce() 后的语句可以执行。

 ROS中的API:C++、Python(十)实现及理解_第20张图片

 ROS中的API:C++、Python(十)实现及理解_第21张图片

 加这一句代码,编译执行

ROS中的API:C++、Python(十)实现及理解_第22张图片

可以看到ros::spinOnce()只回调一次函数(回调到循环前的位置) 

 ros::spin()ROS中的API:C++、Python(十)实现及理解_第23张图片

 ROS中的API:C++、Python(十)实现及理解_第24张图片

 加上这一句

编译执行

ROS中的API:C++、Python(十)实现及理解_第25张图片

看不到ROS_INFO的打印信息

证明spin函数回调到回调函数并且让它自身内循环 

时间相关API 

时刻

 ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。

新建文件并配置

 ROS中的API:C++、Python(十)实现及理解_第26张图片

这是初始化操作

注意ros::NodeHandle这一步是初始化时间,如果没有会导致API调用失败

ros::Time right_now::Time::now();
//now函数会将当前时刻封装并返回
//当前时刻:now被调用执行的那一刻
//时间有参考系:(ex:当前时间是参考公元元年计算)计算机的时间参考是参考1970年1月1日00:00:00
(我们是东八区加8个小时)
right_now.toSec()可以将逝去时刻转换为秒为单位,输出数据类型时double类型
right_now.sec返回的时整形的

完整代码如下 

ROS中的API:C++、Python(十)实现及理解_第27张图片

 编译执行

ROS中的API:C++、Python(十)实现及理解_第28张图片

此时就获取到时间了

ros::Time t1(20,312424);设置指定时刻
ros::Time t2(20.312424);
ROS_INFO("t1=%.2f",t1.toSec());
ROS_INFO("t2=%.2f",t2.toSec());

ROS中的API:C++、Python(十)实现及理解_第29张图片

 编译执行

ROS中的API:C++、Python(十)实现及理解_第30张图片

 持续时间

ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());
ros::Duration du(10);持续10秒钟,参数是double类型的,以秒为单位
du.sleep();按照指定的持续时间休眠
ROS_INFO("持续时间:%.2f",du.toSec());//将持续时间换算成秒
ROS_INFO("当前时刻:%.2f",ros::Time::now().toSec());

复制以下代码编译执行

ROS中的API:C++、Python(十)实现及理解_第31张图片

 ROS中的API:C++、Python(十)实现及理解_第32张图片

休眠了4.5s

 持续时间与时刻运算

需求3:已知程序开始运行时刻,和程序运行时间,求运行结束时刻
1.获取开始运行时刻
2.模拟运行时间
3.计算时间=开始-持续时间

ROS_INFO("时间运算");
ros::Time now = ros::Time::now();
ros::Duration du1(10);
ros::Duration du2(20);
ROS_INFO("当前时刻:%.2f",now.toSec());
//1.time 与 duration 运算
ros::Time after_now = now + du1;
ros::Time before_now = now - du1;
ROS_INFO("当前时刻之后:%.2f",after_now.toSec());
ROS_INFO("当前时刻之前:%.2f",before_now.toSec());

//2.duration 之间相互运算
ros::Duration du3 = du1 + du2;
ros::Duration du4 = du1 - du2;
ROS_INFO("du3 = %.2f",du3.toSec());
ROS_INFO("du4 = %.2f",du4.toSec());
PS: time 与 time 不可以相加
比如 ros::Time nn = now + before_now;//异常

复制以下代码

ROS中的API:C++、Python(十)实现及理解_第33张图片

 编译运行

ROS中的API:C++、Python(十)实现及理解_第34张图片

 ROS中的API:C++、Python(十)实现及理解_第35张图片

编译运行

 ROS中的API:C++、Python(十)实现及理解_第36张图片

运行时间5s

 定时器

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

ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败
需求4:每隔1s再控制台输出一段文本

1.策略1:ros::Rate()
2.策略2:定时器
ros::Timer =nh.createTimer()
ros::Timer createTimer(ros::Duration period, 时间间隔---1s
                       const ros::TimerCallback &callback, 回调函数----封装业务(输出文本)
                       bool oneshot = false, 是否是一次性--根据bool值
                       bool autostart = true) 自动启动

 ROS中的API:C++、Python(十)实现及理解_第37张图片

编写回调函数(因为需求里就要求输出一段文本,所以就写了一个ROS_INFO

以下为定时器实现

ROS中的API:C++、Python(十)实现及理解_第38张图片

 打开终端测试

 ROS中的API:C++、Python(十)实现及理解_第39张图片

成功 

间隔一秒输出文本------

 下面是修改bool oneshot这个参数

ros::Timer createTimer(ros::Duration period, 时间间隔---1s
                       const ros::TimerCallback &callback, 回调函数----封装业务(输出文本)
                       bool oneshot = false, 是否是一次性--根据bool值

 ROS中的API:C++、Python(十)实现及理解_第40张图片

 ROS中的API:C++、Python(十)实现及理解_第41张图片

此时只输出一条消息 

再看最后一个参数

bool autostart = true) 自动启动

ROS中的API:C++、Python(十)实现及理解_第42张图片

 编译执行

ROS中的API:C++、Python(十)实现及理解_第43张图片

未发送消息,这是由于关闭了自动启动

 ROS中的API:C++、Python(十)实现及理解_第44张图片

此时可以调用上图的函数进行手动启动

 ROS中的API:C++、Python(十)实现及理解_第45张图片

定时器启动成功

 下图函数作用为显示函数被调用的时刻ROS中的API:C++、Python(十)实现及理解_第46张图片

 编译执行

ROS中的API:C++、Python(十)实现及理解_第47张图片

成功了

 注意:

创建:nh.createTimer()

参数一:时间间隔

参数二:回调函数(时间事件 TimerEvent)

参数三:是否只执行一次

参数四:是否自动启动(当设置为false 需要手动调用 time.start())

 定时器启动:ros::spin()

节点生命周期API

 在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常

  • 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
  • 同名节点启动,导致现有节点退出;
  • 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown()

ROS中的API:C++、Python(十)实现及理解_第48张图片

加上这一段

ROS中的API:C++、Python(十)实现及理解_第49张图片

编译执行

 ROS中的API:C++、Python(十)实现及理解_第50张图片

然后自动终止了

 日志输出API

日志相关的函数也是极其常用的,在ROS中日志被划分成如下级别:

  • DEBUG(调试):只在调试时使用,此类消息不会输出到控制台;
  • INFO(信息):标准消息,一般用于说明系统内正在执行的操作;
  • WARN(警告):提醒一些异常情况,但程序仍然可以执行;
  • ERROR(错误):提示错误信息,此类错误会影响程序运行;
  • FATAL(严重错误):此类错误将阻止节点继续运行。

 新建文件

并进行相关配置

ROS中的API:C++、Python(十)实现及理解_第51张图片

ROS中的日志
演示不同级别的日志基本使用

复制以下代码

ROS_DEBUG("hello,DEBUG"); //不会输出
ROS_INFO("hello,INFO"); //默认白色字体
ROS_WARN("Hello,WARN"); //默认黄色字体
ROS_ERROR("hello,ERROR");//默认红色字体
ROS_FATAL("hello,FATAL");//默认红色字体

ctrl + 】 组合键是右对齐

编译执行

ROS中的API:C++、Python(十)实现及理解_第52张图片

 Python

  • ROS节点的初始化相关API;
  • NodeHandle 的基本使用相关API;
  • 话题的发布方,订阅方对象相关API;
  • 服务的服务端,客户端对象相关API;
  • 时间相关API;
  • 日志输出相关API。

初始化API 

新建对应文件和修改配置添加权限

 ROS中的API:C++、Python(十)实现及理解_第53张图片

ros::init_node该API函数作用是初始化

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中的API:C++、Python(十)实现及理解_第54张图片

 当我们传参时

ROS中的API:C++、Python(十)实现及理解_第55张图片

可以看到参数传入成功 

anonymous

 anonymous=False, ----可以为节点名称生成随机后缀,可以解决重名问题

此时启动

ROS中的API:C++、Python(十)实现及理解_第56张图片

再开一个终端启动时

 ROS中的API:C++、Python(十)实现及理解_第57张图片

上面的节点被关闭

如果我们想让一个节点多次启动,就得让它的节点名称每次不一样

 ROS中的API:C++、Python(十)实现及理解_第58张图片

ROS中的API:C++、Python(十)实现及理解_第59张图片

 ROS中的API:C++、Python(十)实现及理解_第60张图片

原因是后缀了随机数,避免重复问题 

话题与服务对象相关API 

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

        """

latch:bool值,默认是Falese

如果设置为True,可以将最后一条发布数据保存,且后续有连接者时,会将该数据发送给订阅者

复制以下代码 

import rospy
from std_msgs.msg import String

if __name__ == "__main__":
    #2.初始化 ROS 节点:命名(唯一)
    rospy.init_node("talker_p",anonymous=True)
    #3.实例化 发布者 对象
    pub = rospy.Publisher("chatter",String,queue_size=10,latch=True)
    #4.组织被发布的数据,并编写逻辑发布数据
    msg = String()  #创建 msg 对象
    msg_front = "hello 你好"
    count = 0  #计数器 
    # 设置循环频率
    rate = rospy.Rate(1)
    while not rospy.is_shutdown():
        count += 1
        #拼接字符串
        if count<=10:
            msg.data = msg_front + str(count)
            pub.publish(msg)
            rate.sleep()
            rospy.loginfo("写出的数据:%s",msg.data)
        rate.sleep()
            

编译终端实现

ROS中的API:C++、Python(十)实现及理解_第61张图片

此时订阅了最后一条消息

但如果不改latch

结果如下

 

没有消息

 回旋函数

def spin():
    """
    进入循环处理回调 
    """

时间相关API

 ROS中时间相关的API是极其常用,比如:获取当前时刻、持续时间的设置、执行频率、休眠、定时器...都与时间相关。



'''
需求1:演示时间相关操作(获取当前时刻+设置指定时刻)

需求2:程序执行中停顿五秒


需求3:获取程序开始执行时刻,且已知持续运行时间,计算程序结束时间


需求4:创建定时器,实现类似于ros::Rate 的功能(隔某个时间间隔执行某种操作)



'''

 时刻API 

 ROS中的API:C++、Python(十)实现及理解_第62张图片

 新建文件并授权编译

 复制以下代码

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

import rospy


if __name__ == "__main__":

    rospy.init_node("hello_time")
    # 获取当前时刻
    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())

    pass
    

编译并且运行 

ROS中的API:C++、Python(十)实现及理解_第63张图片

持续时间(设置一个间隔)

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

加入以下代码

#需求2:程序执行中停顿5s

#1.封装一个时间持续对象



# 持续时间相关API
rospy.loginfo("持续时间测试开始.....")
du = rospy.Duration(3.3)
rospy.loginfo("du1 持续时间:%.2f",du.to_sec())
rospy.sleep(du) #休眠函数
rospy.loginfo("持续时间测试结束.....")



 ctrl+ 】时对齐代码

持续时间与时刻运算 

'''
需求3:获取程序开始执行的时刻,且已知持续运行时间,计算程序结束的时间

'''

#获取一个时间
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("+++++++++++++++")

定时器 (间隔某个时间间隔执行操作)

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

需求:创建定时器 

#定时器设置
"""    
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()

 ROS中的API:C++、Python(十)实现及理解_第64张图片

然后创建回调函数,传入一个变量event

ROS中的API:C++、Python(十)实现及理解_第65张图片

执行结果每隔1s发布info

 更改回调函数为

def doMsg(event):
    rospy.loginfo("+++++++++++")
    rospy.loginfo("当前时刻:%s",str(event.current_real))

 这就是里面变量的作用

可以调用回调函数的时刻

执行结果如下

ROS中的API:C++、Python(十)实现及理解_第66张图片

 节点相关API

  • 节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
  • 同名节点启动,导致现有节点退出;
  • 程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())

节点状态判断 

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

节点关闭函数

def signal_shutdown(reason):
    """
    关闭节点
    @param reason: 节点关闭的原因,是一个字符串
    @type  reason: str
    """

 ROS中的API:C++、Python(十)实现及理解_第67张图片

保存执行

 ROS中的API:C++、Python(十)实现及理解_第68张图片

被关闭 

def on_shutdown(h):
    """
    节点被关闭时调用的函数
    @param h: 关闭时调用的回调函数,此函数无参
    @type  h: fn()
    """

on_shutdown会在结束时调用回调函数

ROS中的API:C++、Python(十)实现及理解_第69张图片

 然后再写一个回调函数

ROS中的API:C++、Python(十)实现及理解_第70张图片

 保存执行

ROS中的API:C++、Python(十)实现及理解_第71张图片

 日志输出

rospy.logdebug("hello,debug")  #不会输出
rospy.loginfo("hello,info")  #默认白色字体
rospy.logwarn("hello,warn")  #默认黄色字体
rospy.logerr("hello,error")  #默认红色字体
rospy.logfatal("hello,fatal") #默认红色字体

 ROS中的API:C++、Python(十)实现及理解_第72张图片

编译并且执行

 ROS中的API:C++、Python(十)实现及理解_第73张图片

你可能感兴趣的:(#,ros,ros,ubuntu,linux)