第三章 ROS通信机制进阶(3.1)

ROS通信机制进阶

**

3. 1常用API介绍

**

  1. ros::init()

    作用:ros初始化函数
    参数:
    	(1)argc:	封装实参个数(n+1,其中n为实参个数);
    	(2)argv:	封装参数的数组;
    	(3)name:	为节点命名(唯一性);
    	(4)options:	节点启动选项
    	返回值为void。
    使用:
    	(1)argc 和 argv 的使用:如果按照ROS中特定的格式传入实参,那么ROS可以加以使用,比如用来设置全局参数,给节点命名....
    	(2)options的使用:节点名称需要保证唯一,会导致一个问题:同一个节点不能重复启动。
    	结果:ROS中当有重名的节点启动时,之前的节点会被关闭。
    	需求:特定场景下,需要一个节点多次启动且能正常运行,怎么办?
    	解决:设置启动项ros::init_options::AnonymousName,当创建ros节点时,会在用户自定义的节点名称后缀随机数,从而避免重名问题。
    
  2. ros::NodeHandle nh;

    (1)advertise(): 作用:创建发布者对象; 模板:被发布的消息的类型,如:advertise<
    std_msgs::String >() 参数:1)话题名 
    ;2)队列长度;3)latch(可选):如果设置为true,会保存发布方的最后一条消息,并且新的订阅对象连接到发布方时,发布方会将这条消息发送给订阅者;
    使用:
    	latch设置为true的作用?以静态地图发布为例:方案1:可以使用固定频率发送地图数据,但是效率低;方案2:可以将地图发布对象的latch设置为true,并且发布方只发送一次数据,每当订阅者连接时,将地图数据发送给订阅者(只发送一次),这样提高了数据的发送效率。例如:advertise<std_msgs::String>("we",
    10, true).
    
  3. 回旋函数:ros::spinOnce() 和 ros::spin() 二者的返回值均为void,
    且无参数。相同点:二者都用于处理回调函数;不同点:ros::spin()是进入了循环执行回调函数,而ros::spinOnce()只会执行一次回调函数(没有循环),在ros::spin()后的语句不会执行到,而ros::spinOnce()后的语句可以执行;

  4. 时间相关的API:
    (1)时刻

#include <ros/ros.h>    //这里包含时间相关的头文件

/*
    需求:演示时间相关操作(获取当前时刻+设定指定时刻)
    实现:
        1.准备(头文件包含,节点初始化,节点句柄)
        2.获取当前时刻、
        3.设置指定时刻
*/
int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "hello_time");
    ros::NodeHandle  nh;  //注意:句柄是必须的,若没有句柄则时间API调用(在nodehangdle中初始化时间操作)失败

    //2.获取当前时刻
    //now函数会将当前时刻封装 并返回
    //当前时刻即调用now函数的时刻
    //参考系:1970年01月01日 00:00:00
    ros::Time right_now = ros::Time::now();//now函数的返回值也是Time类型的对象
    ROS_INFO("当前时刻:%.2f",right_now.toSec()); //将时刻值转化为秒,返回浮点型
    ROS_INFO("当前时刻:%d",right_now.sec);//将时刻值转化为秒,返回整型

    //3.设置指定时刻(通过构造函数设置,如下列:t1和t2)
    ros::Time  t1(20,312345678);    //构造函数第一个参数表示秒,第二个参数表示纳秒
    ros::Time  t2(100.35);       //构造函数里直接设置一个浮点数的时间
    ROS_INFO("t1=%.2f", t1.toSec());
    ROS_INFO("t1=%.2f", t2.toSec());

    return 0;
}

(2)持续时间:

    //持续时间
    ROS_INFO("-----------------持续时间-----------------");
    ros::Duration  du(4.5);   //创建一个持续时间对象,构造函数有多个,和时刻对象的构造相似
    ros::Time start = ros::Time::now();   //获取当前时刻
    ROS_INFO("开始休眠,当前时刻为:%.2f", start.toSec());

    du.sleep();      //使用sleep()函数休眠

    ros::Time end = ros::Time::now();   //获取当前时刻
    ROS_INFO("结束休眠,当前时刻为:%.2f", end.toSec());

(3)时间运算:时刻与持续时间(ros::Time和ros::Duration)、持续时间与持续时间(ros::Duration和ros::Duration)

    //时间运算--------------------------------------------------------
    ROS_INFO("-------------时刻与持续时间运算--------------");
    //获取开始时刻
    ros::Time  begin = ros::Time::now();
    //封装持续时间
    ros::Duration  du1(5.0);
    //计算
    ros::Time  stop = begin + du1;    //这里也可以进行减运算
    ROS_INFO("开始的时刻:%.2f", begin.toSec());
    ROS_INFO("结束的时刻:%.2f", stop.toSec());

    //持续时间也可以相互运算
    ros::Duration  du_sum = du + du1;    //4.5+5.0
    ROS_INFO("持续时间相加结果:%.2f", du_sum.toSec());    //持续时间也有toSec()函数来看时间

    //注意:时刻之间不能相互运算

下面是4.(1)-(3)的完整源代码:

#include <ros/ros.h>    //这里包含时间相关的头文件

/*
    需求1:演示时间相关操作(获取当前时刻+设定指定时刻)
    实现:
        1.准备(头文件包含,节点初始化,节点句柄)
        2.获取当前时刻、
        3.设置指定时刻
    需求2:程序执行中停顿5s
    实现:
        1.创建持续时间对象ros::Duration  du
        2.休眠  du.sleep()
    需求3:获取程序开始执行的时间,且已知程序运行的时间,计算程序结束的时间
    实现:
        1.ros::Time::now()获取开始时刻
        2.ros::Duration  封装持续时间
        3.结束时刻=开始时刻+持续时间
    注意:
        1.ros::Time 和 ros::Duration可以运算
        2.ros::Duration之间可以运算
        3.ros::Time之间不可以运算
*/
int main(int argc, char  *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc, argv, "hello_time");
    ros::NodeHandle  nh;  //注意:句柄是必须的,若没有句柄则时间API调用(在nodehangdle中初始化时间操作)失败

    //2.获取当前时刻
    //now函数会将当前时刻封装 并返回
    //当前时刻即调用now函数的时刻
    //参考系:1970年01月01日 00:00:00
    ros::Time right_now = ros::Time::now();//now函数的返回值也是Time类型的对象
    ROS_INFO("当前时刻:%.2f",right_now.toSec()); //将时刻值转化为秒,返回浮点型
    ROS_INFO("当前时刻:%d",right_now.sec);//将时刻值转化为秒,返回整型

    //3.设置指定时刻(通过构造函数设置,如下列:t1和t2)
    ros::Time  t1(20,312345678);    //构造函数第一个参数表示秒,第二个参数表示纳秒
    ros::Time  t2(100.35);       //构造函数里直接设置一个浮点数的时间
    ROS_INFO("t1=%.2f", t1.toSec());
    ROS_INFO("t1=%.2f", t2.toSec());

    //持续时间
    ROS_INFO("-----------------持续时间-----------------");
    ros::Duration  du(4.5);   //创建一个持续时间对象,构造函数有多个,和时刻对象的构造相似
    ros::Time start = ros::Time::now();   //获取当前时刻
    ROS_INFO("开始休眠,当前时刻为:%.2f", start.toSec());

    du.sleep();      //使用sleep()函数休眠

    ros::Time end = ros::Time::now();   //获取当前时刻
    ROS_INFO("结束休眠,当前时刻为:%.2f", end.toSec());


    //时间运算--------------------------------------------------------
    ROS_INFO("-------------时刻与持续时间运算--------------");
    //获取开始时刻
    ros::Time  begin = ros::Time::now();
    //封装持续时间
    ros::Duration  du1(5.0);
    //计算
    ros::Time  stop = begin + du1;    //这里也可以进行减运算
    ROS_INFO("开始的时刻:%.2f", begin.toSec());
    ROS_INFO("结束的时刻:%.2f", stop.toSec());

    //持续时间也可以相互运算
    ros::Duration  du_sum = du + du1;    //4.5+5.0
    ROS_INFO("持续时间相加结果:%.2f", du_sum.toSec());    //持续时间也有toSec()函数来看时间

    //注意:时刻之间不能相互运算
    return 0;
}

(4)设置运行频率:

    ros::Rate rate(1); //指定频率
    while(true){
        ROS_INFO("------code------");
        rate.sleep();//休眠,休眠时间 = 1/频率
    }

(5)定时器:
ROS中内置了专门的定时器,可以实现与ros::Rate 类似的效果:
基本使用如下:

	void cb(const ros::TimerEvent &event){
	    ROS_INFO("0-------------------0");
	    ROS_INFO("回调函数执行时刻:%.2f",event.current_real.toSec()); //current_real是ros::Time类型的
	}

    //创建一个定时器
    /*
        ros::Timer createTimer(Duration period,  //时间间隔(持续时间)
                const TimerCallback& callback, //回调函数
                bool oneshot = false,   //是否循环执行定时器
                bool autostart = true) const   //是否自动启动定时器
    */
    // ros::Timer timer1 = nh.createTimer(ros::Duration(1), cb); //cb是一个回调函数,每隔一s执行一次
    // ros::Timer timer2 = nh.createTimer(ros::Duration(1), cb,true); //cb是一个回调函数,每隔一s执行一次
    ros::Timer timer3 = nh.createTimer(ros::Duration(1), cb,false,false); //cb是一个回调函数,每隔一s执行一次
    timer3.start();    //手动启动定时器

    ros::spin();

5.其他函数:

在发布实现时,一般会循环发布消息,循环的判断条件一般由节点状态来控制,C++中可以通过 ros::ok() 来判断节点状态是否正常,而 python 中则通过 rospy.is_shutdown() 来实现判断,导致节点退出的原因主要有如下几种:
节点接收到了关闭信息,比如常用的 ctrl + c 快捷键就是关闭节点的信号;
同名节点启动,导致现有节点退出;
程序中的其他部分调用了节点关闭相关的API(C++中是ros::shutdown(),python中是rospy.signal_shutdown())
另外,日志相关的函数也是极其常用的,在ROS中日志被划分成如下级别:
DEBUG(调试):只在调试时使用,此类消息不会输出到控制台; INFO(信息):标准消息,一般用于说明系统内正在执行的操作; WARN(警告):提醒一些异常情况,但程序仍然可以执行; ERROR(错误):提示错误信息,此类错误会影响程序运行; FATAL(严重错误):此类错误将阻止节点继续运行。
C++节点判断:
(1)节点状态判断:

/** \brief 检查节点是否已经退出
 *
 *  ros::shutdown() 被调用且执行完毕后,该函数将会返回 false
 *
 * \return true 如果节点还健在, false 如果节点已经火化了。
 */
bool ok();

(2)节点关闭函数:

/*
*   关闭节点
*/
void shutdown();

(3)日志函数:

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值