ROS中spinOnce的机制以及如何选择指定Topic进行Callback更新

12 篇文章 0 订阅
9 篇文章 0 订阅

0 背景

项目中有多个Sensor的Topic需要订阅,Topic的更新频率各不相同,假设Topic A为25Hz,Topic B为10Hz,Topic A为关键数据,需求是当收到TopicA后即进行数据的处理。这里就需要对Callback的调用机制进行区别。

1 SpinOnce机制

SpinOnce与Spin的区别比较简单,这里就不具体说明了。在ROS中,当收到可以激活Callback函数的时机时,会将其压入一个队列,这个队列就是ros::CallbackQueue。当调用Spin或SpinOnce时,就会检查这个队列,如果队列为空,也就是没收到注册的Topic,则返回继续执行主函数;如果不为空,则从其中取出Callback函数来invoke,也就是会依次处理收到的数据。在文档中,提到会处理Callback最新一次的数据,但在实际执行过程中,发现在一次SpinOnce中同一个Topic可能会收到多个数据后进行多次处理,这就背离了项目中需要及时处理TopicA的需求。

[ INFO] [1625046678.916048760, 1624994932.711482997]: 7-PubPart  = 0.006226
[ INFO] [1625046678.916618409, 1624994932.711482997]: PC1 Stamp = 1624994932.629655
[ INFO] [1625046678.916675676, 1624994932.711482997]: Current Laser Stamp = 1624994932.688399
[ INFO] [1625046678.916714987, 1624994932.711482997]: Ls Stamp = 1624994932.688399
Failed to find match for field 'rgb'.
[ INFO] [1625046678.983832186, 1624994932.785528414]: here 1
[ INFO] [1625046678.984639276, 1624994932.785528414]: PC2 Stamp = 1624994932.659626
[ INFO] [1625046678.984701021, 1624994932.785528414]: Current Laser Stamp = 1624994932.759400
[ INFO] [1625046678.984720163, 1624994932.785528414]: Ls Stamp = 1624994932.759400
Failed to find match for field 'rgb'.
[ INFO] [1625046679.037698877, 1624994932.839306189]: here 1
[ INFO] [1625046679.038953597, 1624994932.839306189]: PC1 Stamp = 1624994932.697870
[ INFO] [1625046679.039069509, 1624994932.839306189]: 8-SpinPart  = 0.129240
[ INFO] [1625046679.039086164, 1624994932.839306189]: Loop Time All  = 0.129265

7-PubPart 到 8-SpinPart中间为一次SpinOnce的执行过程,可以看到其中PC2的Callback执行了一次,但是Laser和PC1的Callback执行了两次。如果Laser或者PC1为关键TopicA的话则错过了执行数据分析的时间,因此需要通过拆分Topic进行执行,如果循环到TopicA则继续执行,这里TopicA即为Laser。

SpinOnce在源码中既是直接调用了

ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0));

既是循环执行了上文说的ros::CallbackQueue,为了执行关键Topic,在ros中可以选择一个队列中的项进行执行:
callOne(ros::WallDuration(0));
这个函数会执行队列中的最久的一项执行,并返回执行结果,结果类的可能为
enum CallOneResult { Called, TryAgain, Disabled, Empty }

2 单一Topic执行的实现

#include <ros/ros.h>
#include <ros/callback_queue.h>
#include <ros/callback_queue_interface.h>

#include <std_msgs/Empty.h>

void callback_1(const std_msgs::EmptyConstPtr& _msg)
{
    ROS_INFO("Called callback_1\n");
}

void callback_2(const std_msgs::EmptyConstPtr& _msg)
{
    ROS_INFO("Called callback_2\n");
}

int main(int argn, char* args[])
{
    ros::init(argn, args, "callback_q_subscriber");
    ros::NodeHandle nh_1;
    ros::NodeHandle nh_2;

    ros::CallbackQueue queue_1, queue_2;

    nh_1.setCallbackQueue(&queue_1);
    nh_2.setCallbackQueue(&queue_2);

    ros::Subscriber s_1 = nh_1.subscribe("/topic_1", 1, callback_1);
    ros::Subscriber s_2 = nh_2.subscribe("/topic_2", 1, callback_2);

    pthread_t id_1, id_2;

    int i = 20;
    while(i--)
    {
        queue_1.callOne(ros::WallDuration(1.0)); // can also be callAvailable()
    }

    i = 20;
    while(i--)
    {
        queue_2.callOne(ros::WallDuration(1.0)); // can also be callAvailable()
    }

    ROS_INFO("Hurray!");

    return 0;   
}

执行的结果如下:

[ INFO] [1625047229.986131037, 1624994923.101778726]: 7-PubPart  = 0.005340
[ INFO] [1625047229.986607337, 1624994923.101778726]: PC2 Stamp = 1624994922.929261
[ INFO] [1625047229.986667684, 1624994923.101778726]: Callback Once
[ INFO] [1625047229.986754915, 1624994923.101778726]: 7_1-Callback  = 0.005923
[ INFO] [1625047229.986993282, 1624994923.101778726]: PC1 Stamp = 1624994922.935428
[ INFO] [1625047229.987048776, 1624994923.101778726]: Callback Once
[ INFO] [1625047229.987085780, 1624994923.101778726]: 7_1-Callback  = 0.006297
[ INFO] [1625047229.987549144, 1624994923.101778726]: PC2 Stamp = 1624994922.996081
[ INFO] [1625047229.987695149, 1624994923.101778726]: Callback Once
[ INFO] [1625047229.987754998, 1624994923.101778726]: 7_1-Callback  = 0.006961
[ INFO] [1625047229.987884339, 1624994923.101778726]: Current Laser Stamp = 1624994923.035926
[ INFO] [1625047229.987943186, 1624994923.101778726]: Ls Stamp = 1624994923.035926
Failed to find match for field 'rgb'.
[ INFO] [1625047230.012284062, 1624994923.133694720]: here 1
[ INFO] [1625047230.012347083, 1624994923.133694720]: Callback Once
[ INFO] [1625047230.012382432, 1624994923.133694720]: 7_1-Callback  = 0.031593
[ INFO] [1625047230.012473806, 1624994923.133694720]: 8-SpinPart  = 0.031684

可以看到,PC2执行了两次,但关键Topic Laser只执行了一次,并且在执行后就结束了Spin的过程,符合项目中的需求。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MultiThreadedSpinner可以在ROS使用多个线程同时处理消息,可以提高程序的效率。 使用步骤如下: 1. 导入MultiThreadedSpinner头文件 ```cpp #include <ros/ros.h> #include <ros/spinner.h> #include <ros/callback_queue.h> ``` 2. 创建一个callback queue并将其与MultiThreadedSpinner绑定 ```cpp ros::CallbackQueue my_callback_queue; ros::AsyncSpinner spinner(2, &my_callback_queue); // 2个线程 ``` 3. 在需要使用MultiThreadedSpinner的地方,使用自定义的callback queue来处理消息 ```cpp ros::NodeHandle node_handle; ros::Subscriber sub = node_handle.subscribe("topic_name", 10, &callback_function, ros::TransportHints().tcpNoDelay()); spinner.start(); ros::spin(); ``` 完整的代码示例: ```cpp #include <ros/ros.h> #include <ros/spinner.h> #include <ros/callback_queue.h> #include <std_msgs/String.h> ros::CallbackQueue my_callback_queue; void callback_function(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, "multi_threaded_spinner"); ros::NodeHandle node_handle; ros::Subscriber sub = node_handle.subscribe("topic_name", 10, &callback_function, ros::TransportHints().tcpNoDelay()); ros::AsyncSpinner spinner(2, &my_callback_queue); // 2个线程 spinner.start(); ros::Rate r(10); while (ros::ok()) { ros::spinOnce(); // 使用自定义的callback queue来处理消息 r.sleep(); } return 0; } ``` 需要注意的是,在使用MultiThreadedSpinner时,需要使用ros::spinOnce()来处理消息,而不是ros::spin()。因为ros::spin()会阻塞当前线程,而MultiThreadedSpinner会使用多个线程来处理消息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值