ROS订阅消息时缓冲区的读取

  这篇blog主要是通过测试解释ROS在调用callback函数时如何读取缓冲区数据。问题源自于读知乎文章——从零开始做自动驾驶定位(三): 软件框架 1中的缓冲区机制部分产生的疑问。

问题描述

  知乎文章中对ROS缓冲区机制的部分描述如下:
在这里插入图片描述

  因为ROS一直在入门(从未踏入过),所以当时对“把缓冲区中的数据读完”产生了疑惑。回调函数每次只读取缓冲区中最旧的数据,而回调函数“返回”的指针也无法操作读取缓冲区中的其它数据,那如何实现把缓冲区的数据读完的?

  这个问题的大背景是GPS消息频率较高,而激光点云消息频率则相对较低,如果激光点云的回调函数处理时间过长,GPS消息可能会丢帧。为了保证不丢帧,需要把GPS的subscriber中的缓冲区大小设得大一点。但如果按照“回调函数每次只读取缓冲区中最旧的数据”思考会发现这并不能解决问题(当时仍思维定视地认为回调函数依旧定周期执行)。

测试

  测试用的数据是含IMU (500Hz)和PointCloud (10Hz)消息的bag。下面是测试代码的最终版。

#include <ros/ros.h>
#include <iostream>
#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/Imu.h>
#include <sys/times.h>

struct timeval start;

void imuHandle(const sensor_msgs::ImuConstPtr _imuMsg)
{
    gettimeofday(&start, NULL);
    std::cout << start.tv_sec * 1000000 + start.tv_usec << std::endl;
    std::cout << "imu message!" << "    ";
    //std::cout << _imuMsg->header.stamp.toNSec() << std::endl;
}

void laserPointHandle(const sensor_msgs::PointCloud2ConstPtr _laserPointMsg)
{
    std::cout << "laser message!" << "    ";
    std::cout << _laserPointMsg->header.stamp.toNSec() << std::endl;
    for(int i=0; i<20000; i++)
    {
        for(int j=0; j<1000; j++)
        {
            
        }
    }
    std::cout << "out" << std::endl;

}

int main(int argc, char **argv)
{
    ros::init(argc, argv, "bufferTest");
    ros::NodeHandle nh;
    ros::Subscriber subImu = nh.subscribe<sensor_msgs::Imu>("/imu_raw", 100, imuHandle);
    ros::Subscriber subLaserPoint = nh.subscribe<sensor_msgs::PointCloud2>("/points_raw", 1, laserPointHandle);
    ros::spin();
    return 0;
}

  当把imu的subscriber中的缓冲区大小设为1的时候,测试发现当laser的回调函数运行时,imu的回调函数是不会将其中断的。相邻两次laser回调函数之间,imu回调函数的执行次数低于50,这意味着imu丢消息了。
  把缓冲取的大小改大之后,会发现imu的回调函数仍然不会中断laser的回调函数,但相邻两次laser回调函数之间,imu回调函数的执行次数恢复到50左右了。之后在imu的回调函数中添加了系统时间显示,发现imu回调函数50次执行中,靠前的调用相邻两次之间的时间间隔非常短,之后的则恢复500Hz。
  下图是imu的callback函数的回调时间显示,单位是微秒。我们可以看出靠近前面的相邻两次回调之间的时间间隔很短(这和回调函数的运行时间有关),大概几微秒,而靠后的相邻两次回调之间的时间间隔则是几千微秒。
imu callback函数的回调时间打印(微秒)

总结

  结合前面图片中对缓冲区机制的解释,给出以下理解。callback函数得不到执行时,会把消息(实际上应该是callback函数)存放到缓冲区。当callback函数得以执行时,会快速连续调用callback函数,直到把缓冲区中的数据读完,然后恢复“正常频率”监听消息。

Reference

  1. https://zhuanlan.zhihu.com/p/105512661 ↩︎

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值