ros::spin()、ros::spinOnce():使用细节、区别

ros::spin()、ros::spinOnce():使用细节、区别

摘要:本文主要讲解下,ros::spin()和ros::spinOnce()的使用细节,以及他们的区别。

一、ros::spin()

先来一个直观的例子,然后根据例子来分析。

消息的发送:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
 
int main(int argc, char **argv)
{
    ros::init(argc, argv, "talker");
    ros::NodeHandle n;
    //1000 为queue_size大小,设置为1:实时性,只保留最新的。0:表示为无穷大。
    //关于queue_size的详解:https://blog.csdn.net/handsome_for_kill/article/details/81984428
    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
    ros::Rate loop_rate(10);
 
    while (ros::ok())
    {
        std_msgs::String msg;
        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();
        ROS_INFO("%s", msg.data.c_str());
        
        // 向 Topic: chatter 发送消息, 发送频率为10Hz(1秒发10次);消息池最大容量1000。
        chatter_pub.publish(msg);
        loop_rate.sleep();
    }
    return 0;
}
消息的订阅:
#include "ros/ros.h"
#include "std_msgs/String.h"
 
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
    ROS_INFO("I heard: [%s]", msg->data.c_str());
}
 
int main(int argc, char **argv)
{
    ros::init(argc, argv, "listener");
    ros::NodeHandle n;
    ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);//1000为回调函数的队列长度
    ros::spin();
    return 0;
}

根据上面的实例,我们来捋一捋处理流程

  1. 消息发布器在一个while里面一直循环发送“hello world”到话题(topic)chatter上。
  2. 消息订阅器一旦知道chatter上面有data,就会将这data作为参数传入callback函数中,但是此时还没有执行callback函数,而是把callback函数放到了一个回调函数队列中。
  3. 所以当发布器不断发送data到chatter上面时,就会有相应的callback函数进入队列中,它们函数名一样,只是实参不一样。

那什么时候处理回调函数队列中的回调函数了?这就是 ros::spin() 需要做的工作了。

  • 对于spin函数,一旦进入spin函数,它就不会返回了,也不继续往后执行了,相当于它在自己的函数里面死循环了(直到ctrl+c 或者程序终止的时候才退出)。
  • 主要的工作,就是不断的检查回调函数队列里面是否有callback函数存在,如果有的话,它就会马上去执行callback函数。如果没有的话,它就会阻塞,不会占用CPU。

二、ros::spinOnce()

消息的发送和上面是一样的,这里只说一下消息的订阅。

消息的订阅:
#include "ros/ros.h"
#include "std_msgs/String.h"
  
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
    /*...TODO...*/ 
}
  
int main(int argc, char **argv)
{
    ros::init(argc, argv, "listener");
    ros::NodeHandle n;
    ros::Subscriber sub = n.subscribe("chatter", 2, chatterCallback);
  
    ros::Rate loop_rate(5); //频率为5hz
    while (ros::ok())
    {
        /*...TODO...*/ 
        ros::spinOnce();
        loop_rate.sleep(); //配合执行频率,sleep一段时间,然后进入下一个循环。
    }
    return 0;
}

回调函数的队列的创建和上面的分析是一样的,这里讲一讲ros::spinOnce() 的处理流程:

  1. 当spinOnce函数被调用时,spinOnce就会调用回调函数队列中第一个callback函数,此时callback函数被执行。
  2. spinOnce函数执行一次后,接着执行下面的语句。不像spin函数,进入到自己的内部循环,不往下执行。
  3. 等到下次spinOnce函数又被调用时,回调函数队列中第二个callback函数就会被调用,以此类推。

那我们如何控制执行的速度了,也就是执行的频率。从上面的实例中,我们看到多了:

  • ros::Rate loop_rate(5); 设置执行频率
  • loop_rate.sleep(); 配合执行频率,sleep一段时间,然后进入下一个循环。

通过这样的设置,我们就可以设计自己的监听频率了,而不用完全被动的接收topic了。

注意事项:
  • 如果我们只是想接收部分消息,我们根据自己的需求设置合适的ros::Rate 和回调函数队列长度。
  • 如果我们想完整的接收所有的消息,那就要合适的设置ros::Rate和回调函数队列长度了。比如上面的消息发送频率为10hz,接收频率为5hz,那回调函数的队列就至少为2(5x2=10)。

三、区别

上面其实已经分析到了一个主要区别

  • ros::spin()被动的接收topic,或者说纯粹的接收topic.
  • ros::spinOnce()可以根据自己的需求设置接收频率。更加主动灵活。
ros::Rate loop_rate(10);
while(ros::ok())
{
	// can add some function
    ros::spinOnce();
    loop_rate.sleep();
}

如果退化成下面这样的化,其实和ros::spin()是一样的。

while(ros::ok())
{
    ros::spinOnce();
}

从上面的对比中,其实可以看出,ros::spin()和ros::spinOnce() 还有一个重要的区别就是:

  • ros::spinOnce() 可以配合其它函数,一起放在while循环中处理。也就是说,当程序中除了响应回调函数还有其他重复性工作的时候,那就在循环中做那些工作,然后调用ros::spinOnce()。

实例:
在这里插入图片描述可以看出,不仅仅在处理回调函数,与此同时,也可以处理print()函数。

参考资料:
  1. rosspin、rosspinOnce及多线程订阅
  2. ros::spin()和ros::spinOnce()函数
  3. ros::spin() 和 ros::spinOnce() 区别及详解
  • 107
    点赞
  • 313
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值