ros::spin()和ros::spinOnce()的详解

一、共同点

两者的作用都是处理当前节点队列中现有的所有回调函数。

我们在订阅话题前需要实例化订阅者对象。subscribe类的模板是消息的类型,一共有3个参数:订阅话题,回调函数队列长度,回调函数。

ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter", 10, doMsg);

订阅话题和回调函数大家都不陌生,我们主要来说下这个回调函数队列长度。任何一个话题的发布和订阅,都会有其对应的发布缓存区和订阅缓存区,正如advertise和subscribe类中都有缓存数量的参数(回调函数队列长度),为什么有这个参数呢?

这是因为在订阅过程中,如果发布方频率过快,而订阅方在回调函数中处理时间很长,则后面发布的话题的callback就会进入队列,这个队列就是订阅缓冲区,而等到上一次的callback结束后,subscribe会在回调函数队列中找到时间戳最早的那一个进行回调,这样可以防止遗漏。

通常在使用时,如果对实时性要求高,想每次只处理这一时刻的发布信息,那么queue_size最好设置为1,这样每一次的回调函数,回调的都是当前发布的话题,来不及处理的话题内容会被覆盖掉。如果不想错过所有发布的话题内容,那么就可以将queue_size设置的稍微大一点。如果queue_size是0,则表明回调函数队列是无穷大,可以一直存储后面传来的话题的callback。

ros::Subscriber sub = nh.subscribe<std_msgs::String>("person", 1, callback);

当我们要订阅一个话题时,我们必须要用ros::spin()或者ros::spinOnce()来调用回调函数。一旦进行回调,会依次执行队列中现有的所有回调函数。

二、不同点

ros::spin

当处理到ros::spin()时,会一直去话题订阅缓冲区中查看有没有回调函数,如果有则去处理回调函数,如果没有则继续查看并且等待。所以main函数中一旦执行到了ros::spin()就不会执行除了回调函数以外的逻辑了,节点此时只会等待并且处理回调函数。比如下面的订阅方代码,一直在循环读取接收的数据,并调用回调函数处理,永远不会执行到return 0。

// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
    
    
    ROS_INFO("我听见:%s",msg_p->data.c_str());
    // ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char  *argv[])
{
    
    
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;

    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)

    //6.设置循环调用回调函数
    ros::spin();//循环读取接收的数据,并调用回调函数处理

    return 0;
}

ros::spinOnce

当处理到ros::spinOnce()时,会去话题订阅缓冲区中查看有没有回调函数,如果有则去处理回调函数,如果没有则继续往下执行,ros::spinOnce()不会一直卡在回调函数里面,只回调一次。

一般是在while循环中和ros::Rate共同出现,ros::Rate定义频率对象r,参数为每s执行的循环次数,r.sleep()就是根据执行频率计算每个循环的时间,如果当前循环执行到r.sleep()这条代码还有剩余时间,则剩下的时间就睡眠过去。比如下面的例子我设置了1s执行一次循环,也就是一个循环1000ms,处理回调用了600ms,然后执行到r.sleep()将睡眠400ms什么都不做,完成本次循环。

// 1.包含头文件 
#include "ros/ros.h"
#include "std_msgs/String.h"

void doMsg(const std_msgs::String::ConstPtr& msg_p){
    
    
    ROS_INFO("我听见:%s",msg_p->data.c_str());
    // ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char  *argv[])
{
    
    
    setlocale(LC_ALL,"");
    //2.初始化 ROS 节点:命名(唯一)
    ros::init(argc,argv,"listener");
    //3.实例化 ROS 句柄
    ros::NodeHandle nh;
    //4.实例化 订阅者 对象
    ros::Subscriber sub = nh.subscribe<std_msgs::String>("chatter",10,doMsg);
    //5.处理订阅的消息(回调函数)
    //设置执行频率(1秒1次)
    ros::Rate r(1);
    while (ros::ok()) {
    
    
    	//6.设置循环调用回调函数
    	ros::spinOnce();
    	//根据前面制定的发送频率自动休眠 休眠时间 = 1/频率(s);
        r.sleep();
    }
    return 0;
}

春心一动弃前般,只晓偷来片刻欢,损德招灾都不管,爱河浪起自相残。

猜你喜欢

转载自blog.csdn.net/qq_42257666/article/details/131491005
ROS