ROS:回调函数处理与回调队列

ROS:回调函数处理与消息队列

参考资料:

在ROS节点的执行过程中,如果程序中存在订阅器和回调函数,就必然会面临两个问题:

  • 订阅器的队列长度选择多大?
  • 该使用spinOnce()还是spin()处理回调函数?

1.话题回调机制

我在查找相关资料时得知,ROS在处理回调函数时,并不是消息传来就立刻进行处理的,特别是在使用spinOnce()处理回调函数时。即便使用spin()处理时,若发布器的发布频率过快,同样会导致无法及时处理回调函数的问题。此时,就需要用到我们在定义订阅器时的队列长度了。

当节点无法及时处理回调函数时,这些未被处理的回调函数就会按照先后的顺序放入一个消息队列,该回调队列的长度就是定义订阅器时的队列长度了。节点会先处理时间辍最小的回调函数,然后依次处理队列中所有的回调函数。但是,当发布器的频率过快时,会出现未被处理的回调函数数量超过队列长度,它会自动丢弃时间辍最长(最老的)的回调函数。那么,如果我们只想要处理当前最新的消息时,只需要把发布器和订阅器的队列长度设置为1即可。

spinOnce()spin()的区别这里就不再赘述,需要注意的是,spinOnce()在执行的那一刻,并不是只处理队列中的一个回调函数就返回了,而是会处理当时队列中存在的所有回调函数。

ros::spinOnce() will call all the callbacks waiting to be called at that point in time.

ros::Rate r(10); // 10 hz
while (should_continue)
{
  ... do some work, publish some messages, etc. ...
  ros::spinOnce();
  r.sleep();
}

下图是使用spinOnce()进行回调函数处理,在回调函数中打印"turtle pose",在主循环中打印"counter"。从该图可以发现,spinOnce()处理了多个回调函数才返回主循环。这也证明了上述的论断。

在这里插入图片描述

官方文档中,spin()spinOnce()可以等价于以下程序:

// spin()
#include <ros/callback_queue.h>
ros::NodeHandle n;
while (ros::ok())
{
  ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0.1));
}

// spinOnce()
#include <ros/callback_queue.h>
ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0));

函数callAvailable()说明会处理队列中所有可用的消息。ros::WallDuration(0)则表示只处理在当时时刻的队列消息,不会等待,若当时无任何消息,则直接返回。

还有一个函数callOne()也会处理队列中的消息,但它只会处理队列中时间辍最长的那一个消息。

The CallbackQueue class has two ways of invoking the callbacks inside it: callAvailable() and callOne(). callAvailable() will take everything currently in the queue and invoke all of them. callOne() will simply invoke the oldest callback on the queue.

Both callAvailable() and callOne() can take in an optional timeout, which is the amount of time they will wait for a callback to become available before returning. If this is zero and there are no callbacks in the queue the method will return immediately.

2.多话题回调机制

当节点中只有一个订阅器和对应回调函数时,整个过程都很清晰。但如果节点中存在多个话题订阅器和多个回调函数时,整个过程又是什么样的呢?

spin() and spinOnce() are really meant for single-threaded applications, and are not optimized for being called from multiple threads at once.

在官方文档中写的十分清楚,spin()spinOnce()只是单线程函数,并不适用于多线程处理。也就是说,只要使用了这两个函数来处理回调函数,那么程序必然是单线程的。

在单话题回调时,必然只存在一个消息队列。但若是多话题回调,又该如何呢?在这里,我就想到了一些问题:

  • 多个话题的订阅器是否共享一个消息队列?;
  • 若它们共享一个消息队列,那为什么每个订阅器在初始化时都可以设置队列长度?
  • 是否可以自定义设置多个消息队列?

2.1.问题一:多个话题的订阅器是否共享一个消息队列?

关于第一个问题,官方文档中有说明。在默认情况下,所有的回调函数都会被分配到一个全局队列中,然后由spin()或其它函数进行处理。也就是说,多话题的订阅的消息是共享一个全局消息队列的。

By default, all callbacks get assigned into that global queue, which is then processed by ros::spin() or one of the alternatives.

那么,根据上文spinOnce()的描述,在执行spinOnce()时会处理当时在全局队列中所有的消息。若当时队列中存在多个话题消息,那么就会处理多个话题的回调函数。但由于是单线程的,因此处理的过程是串行的。如这里所描述的。

2.2.问题二:若它们共享一个消息队列,那为什么每个订阅器在初始化时都可以设置队列长度?

关于第二个问题,我还没有在官方文档中找到相关描述。
但我猜想,各个订阅器设置的队列长度是指该订阅话题在整个全局队列中所占的长度。也就是说,全局队列的长度至少不会小于所有话题订阅器设置的队列之和。

2.3.问题三:是否可以自定义设置多个消息队列?

可以自定义设置消息队列,roscpp中提供了相应函数和类。详情可参照官方文档

  • 23
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
ROS的Python代码中,回调函数是一种常见的处理机制。在代码中,我们可以使用ro.Subscriber()函数来订阅指定的话题,并指定一个回调函数处理接收到的消息。当有消息到达订阅的话题时,ROS会自动调用回调函数处理消息。 回调函数通常在接收到消息后执行一系列的操作,包括对数据进行处理、更新地图等等。在处理回调函数A后,如果还需要进行其他的处理,可以在回调函数后添加一个while循环来执行需要执行的操作,然后在循环内调用rospy.spin()函数。这样做的好处是,rospy.spin()函数会持续监听话题,只要有新的消息到达,就会立即调用回调函数处理消息。同时,由于rospy.spin()函数并不会占用主线程,所以代码可以继续执行其他的操作。 需要注意的是,一旦进入rospy.spin()函数,它就会进入一个死循环,不会返回。只要回调函数队列中有回调函数等待执行,rospy.spin()函数就会立即去执行回调函数。如果回调函数队列为空,rospy.spin()函数会阻塞,不会占用CPU。这样可以保证回调函数能够及时响应消息,而不会错过任何消息。 综上所述,回调函数对于处理ROS中的消息非常重要。可以通过订阅指定的话题并指定回调函数处理接收到的消息,并可以在回调函数后添加while循环来执行其他的操作,同时通过rospy.spin()函数来保证回调函数能够及时响应消息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ros下的python代码的编写与回调函数](https://blog.csdn.net/weixin_28930461/article/details/106444518)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [ROS回调函数与spin()方法](https://blog.csdn.net/qq_33898609/article/details/105935613)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值