ROS通信机制进阶

常用API

初始化

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

1.roscore

2.rosrun plumbing_pub_sub demo01_pub _length:=2

3.rosparam list

2.options的使用
  节点名称需要保证唯一,会导致一个问题:同一个节点不能重复启动
  结果:ROS中当有重名的节点启动时,之前的节点会关闭
  需求:特定场景下,需要一个节点多次启动且能正常运行,怎么办
  解决:设置启动项 ros::init_options::AnonymousName
  当创建ROS节点时,会在用户自定义的节点名称后缀随机数,从而避免重名问题


ros::init(argc,argv,"erGouZi",ros::init_options::AnonymousName);

 此时可看到同一个节点后面有两个随机数,从而避免重名问题

话题与服务相关对象

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

NodeHandle有一个重要作用是可以用于设置命名空间

latch的使用

int main(int argc,char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"erGouZi",ros::init_options::AnonymousName);
    ros::NodeHandle nh;
    /*
        作用:创建发布者对象
        模板:被发布的消息类型
        参数:
                1.话题名称
                2.队列长度
                3.latch  如果设置为true,会保存发布方的最后一条消息,并且
                 新的订阅对象连接到发布方时,发布方会将这条消息发送给订阅者
        使用:
              latch设置为true的作用?
              以静态地图为例,方案1:可以使用固定频率发布数据,但是效率低;方案2:可以将
              地图发布对象的latch设置为true,并且发布方只发布一次数据,每当订阅者连接时,将地图数据发送给订阅者,这样提高了数据的发送效率
    */
    ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10,true);
    std_msgs::String msg;
    //要求以10HZ的频率发布数据
    ros::Rate rate(1);
    int count = 0;
    ros::Duration(3).sleep();
    while (ros::ok())
    {
    
    count++;
    //msg.data = "hello";
    std::stringstream ss;
    ss <<"hello ---> " << count;
    msg.data = ss.str();
 
    if(count <=10)
    {
         pub.publish(msg);
         ROS_INFO("send data:%s",ss.str().c_str());       
    }
    
    pub.publish(msg);
    ROS_INFO("send data:%s",ss.str().c_str()); 
    rate.sleep();

    ros::spinOnce();
    ROS_INFO("一轮回调执行完毕....");
    }
}

回旋函数

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

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

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

时间

注意事项都在代码中,如下

#include "ros/ros.h"

/*
     需求:演示时间相关操作(获取当前时刻 + 设置指定时刻)
     实现:
            1.准备(头文件、节点初始化、NodeHandle创建)
            2.获取当前时刻
            3.设置指定时刻

     需求2:程序执行中停顿5秒
     实现:
       1.创建持续时间对象
       2.休眠

     需求3:已知程序开始运行的时刻和程序运行的时间,求运行结束的时刻
     实现:
       1获取开始执行的时刻
       2模拟运行时间(N秒)
       3计算结束时刻 = 开始 + 持续时间

     注意:
       1.时刻与持续时间可以执行加减
       2.持续时间之间也可以执行加减
       3.时刻之间可以相减,不可以相加
     需求4:每隔1s,在控制台输出一段文本
     实现:
        策略1:ros::Rate()
        策略2:定时器

    注意:
      创建:nh.createTimer()
      参数1:时间间隔
       参数2:回调函数(时间事件 TimerEvent)
      参数3:是否执行一次
      参数4:是否自动启动(当设置为false,需要手动调用timer.start())
      定时器启动后:ros::spin()

*/

//回调函数
void cb(const ros::TimerEvent& event){
    ROS_INFO("-----------------");
    ROS_INFO("函数被调用的时刻:%.2f",event.current_real.toSec());
}

int main(int argc, char *argv[])
{
    //1.准备(头文件、节点初始化、NodeHandle创建)
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"hello_time");
    ros::NodeHandle nh;//注意:必须调用。否则会导致时间API调用失败(在NodeHandle会初始化时间操作)
    //2.获取当前时刻
    //now函数会将当前时刻封装并返回
    //当前时刻:now被调用执行的那一刻
    //参考系:1970年01月01日 00:00:00
    ros::Time right_now = ros::Time::now();
    ROS_INFO("当前时刻:%.2f",right_now.toSec());
    ROS_INFO("当前时刻:%d",right_now.sec);
    ros::Time t1(20,312345678);
    ros::Time t2(100.35);
    ROS_INFO("t1 = %.2f",t1.toSec());
    ROS_INFO("t2 = %.2f",t2.toSec());

    //----------------------------------------------------------------------
    ROS_INFO("------------------持续时间--------------------");
    ros::Time start = ros::Time::now();
    ROS_INFO("开始休眠:%.2f",start.toSec());
    ros::Duration du(4.5);
    du.sleep();
    ros::Time end = ros::Time::now();
    ROS_INFO("休眠结束:%.2f",end.toSec());

    //----------------------------------------------------------------------
    ROS_INFO("------------------时间运算--------------------");
    //1获取开始执行的时刻
    ros::Time begin = ros::Time::now();
    //2模拟运行时间(N秒)
     ros::Duration du1(5);
    //3计算结束时刻 = 开始 + 持续时间
    ros::Time stop = begin + du1;//也可以减
    ROS_INFO("开始时刻:%.2f",begin.toSec());
    ROS_INFO("结束时刻:%.2f",stop.toSec());

    //时刻与时刻运算
    ros::Duration du2 = begin - stop;
    ROS_INFO("时刻相减:%.2f",du2.toSec());
    //持续时间与持续时间的运算
    ros::Duration du3 = du1 + du2;
    ros::Duration du4 = du1 - du2;
    ROS_INFO("du1 + du2 = %.2f",du3.toSec());
    ROS_INFO("du1 - du2 = %.2f",du4.toSec());

    //----------------------------------------------------------------------
    ROS_INFO("------------------定时器--------------------");
    /*
    Timer createTimer(Duration period,                      //时间间隔     1s
     void(T::*callback)(const TimerEvent&) const, T* obj,   //回调函数  封装业务
                    bool oneshot = false,                   //是否是一次性  true只执行一次        false 循环执行
                    bool autostart = true) const            //自动启动
     */
    //ros::Timer timer = nh.createTimer(ros::Duration(1),cb,true);
    ros::Timer timer = nh.createTimer(ros::Duration(1),cb,false,false);
    timer.start();  //手动启动
    ros::spin();       //需要回旋
    return 0;
}

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

ros::NodeHandle nh;//必须创建句柄,否则时间没有初始化,导致后续API调用失败

 // ROS 定时器
 /**
* \brief 创建一个定时器,按照指定频率调用回调函数。
*
* \param period 时间间隔
* \param callback 回调函数
* \param oneshot 如果设置为 true,只执行一次回调函数,设置为 false,就循环执行。
* \param autostart 如果为true,返回已经启动的定时器,设置为 false,需要手动启动。
*/
 //Timer createTimer(Duration period, const TimerCallback& callback, bool oneshot = false,
 //                bool autostart = true) const;

 // ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing);
 ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,true);//只执行一次

 // ros::Timer timer = nh.createTimer(ros::Duration(0.5),doSomeThing,false,false);//需要手动启动
 // timer.start();
 ros::spin(); //必须 spin

注意:
      创建:nh.createTimer()
      参数1:时间间隔
      参数2:回调函数(时间事件 TimerEvent)
      参数3:是否执行一次
      参数4:是否自动启动(当设置为false,需要手动调用timer.start())
      定时器启动后:ros::spin()

节点退出原因

导致节点退出的原因主要有如下几种:

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

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

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

使用示例:

#include "ros/ros.h"

int main(int argc, char *argv[])
{
    setlocale(LC_ALL,"");
    ros::init(argc,argv,"hello_log");
    ros::NodeHandle nh;

    ROS_DEBUG("调试信息");   //不会打印在控制台
    ROS_INFO("一般信息");    //默认白色字体
    ROS_WARN("警告信息");    //默认黄色字体
    ROS_ERROR("错误信息");   //默认红色字体
    ROS_FATAL("严重错误");   //默认红色字体
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值