理解ROS Topic 通信频率背后的机制

2023-05-16

Topic是ROS的三种通信方式中最为基本、也是常用的一种。本文对于ROS的Topic通信背后的数据吞吐机制做一个较为详细、深入的介绍。

Publisher

ROS中发布一个topic的函数是这样的

ros::Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false);
Parameters:
topic:	Topic to advertise on
queue_size:	Maximum number of outgoing messages to be queued for delivery to subscribers
latch:	(optional) If true, the last message published on this topic will be saved and sent to new subscribers when they connect

有三个参数:topic就是我们要发布的话题,queue_size是publisher队列中可以存储的消息数量, latch是锁存,比如停止publish后保存最后一条message,如果有新的subscriber订阅的话,把之前保存的消息发给新的subscriber。
下面讲一讲和queue_size相关的,关系到我们发送的数据能否按照期望频率传输,会不会丢帧等。

案例分析

ros::init(argc, argv, "talker");
ros::NodeHandle handle;
ros::Publisher chatter_pub = handle.advertise<std_msgs::String>("chatter", 10);
ros::Rate loop_rate(100);
int count = 0;
std::stringstream ss;
std_msgs::String msg;
while (ros::ok())
{
    ss<<"Message ["<<count<<"]";
    msg.data = ss.str();
    ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);
    loop_rate.sleep();
    ++count;
    // ss.str("");
}

比如上面这个例子,我设置了publish()的循环频率为100Hz ,但是使用rostopic hz <topic> 查看这个topic的发布频率是不到100Hz的(下图),出现了实际发布频率和设置频率不一致的情况。

这是因为我用的stringstream没有清零,而是逐渐累积,字符串越来越长,所以发布的消息是越来越复杂的,publisher thread处理耗时越来越多,所以发布频率也在逐渐降低。但是如果发布的消息很简单的话,还是可以跟上的,一般而言发布频率和publish()频率是几乎相等的。

那么这种频率错位的背后是什么导致的呢?

Publisher 背后

在这里插入图片描述

The publisher queue is another queue like callback queue, but the queue is for queuing published message which is filled every time publish() function is called.
There is a separate thread (publisher thread) that is responsible for taking the message from the publisher queue and send it to subscribers of the topic if there are any. If you are calling the publish()more quickly than the publisher thread can send the messages, the messages start piling up in the publisher queue and if it reaches over the specified queue size (in this case 1000), the old message starts to get overwritten by new messages.

每调用一次publish()都会往 publisher message queue (以下简称PMQ) 当中放入一个message,然后有个单独的线程publisher thread 从PMQ当中取出message并真正发布。

我们叫publish()的循环频率为 期望发布频率,publisher thread处理的频率为 实际发布频率

一般而言,publisher thread的处理频率要比我们调用publish()的频率高,所以表现出来发布消息的频率就是我们publish()的循环速率。但是如果publish()的循环频率过高,以至于超过了publisher thread处理的速度(或者说publisher thread处理的速度低于publish()的速度)消息就会在PMQ中积累,超过了queue_size的话旧的消息便会丢失。

所以说如果发布的消息比较复杂,且publish()频率较高的话,最好将queue_size设置得大一点,要不然会丢失数据。当然也要看你应用的场景,有些场景丢帧也无所谓,反而是需要更新的数据。

Subscriber

ROS中订阅一个话题的函数如下

ros::Subscriber subscribe(const std::string& topic, uint32_t queue_size, <callback, which may involve multiple arguments>, const ros::TransportHints& transport_hints = ros::TransportHints());

topic还是很好理解,就是我们要订阅的话题。下面讲一讲callbackqueue_size

Callback

subscriber初始化的时候指明了回调函数callback,这就是回调函数的注册。

那么什么叫回调函数的注册呢?我的理解是下面这样,熟悉的小伙伴可以跳过。

回调就是告诉调用方你应该调用哪个回调函数,其实就是将这个函数的函数指针传给调用方,比如我们这里subscribe(callback),就是将callback的指针通过subscribe这个函数传给了subscriber。

打个日常生活的比方:好比你(回调函数)刚来一个单位,你跟上级(调用方)说“有事您叫我”,这个露脸的过程就是 回调函数的注册。然后领导有事真的找了你,就是调用了你这个回调函数。

在这里插入图片描述

ROS中与回调处理相关的有两类对象:callback queues,spinners 。

subscriber节点初始化之时,它就创建了一个用于接收消息的线程 (receiver thread),每个subscriber都有一个接收消息的队列 subscriber message queue (SMQ)。同样地还有一个callback queue用于存放回调函数。每当subscriber接收一个消息,就有一个callback放入callback queue当中。

A spinner is an object that has the ability to call the callbacks contained in a callback queue. A callback queue is the object in which each subscriber will add an element each time a message is received by resolving which kind of message should call which callbacks (and with which arguments).
Callback queues/spinning do not have any effect on the internal network communication in roscpp. They only affect when user callbacks occur. They will have an effect on the subscription queue, since how fast you process your callbacks and how quickly messages are arriving determines whether or not messages will be dropped.

roscpp支持三种回调

  1. function

    void callback(const std_msgs::StringConstPtr& str)
    {	}
    ros::Subscriber sub = nh.subscribe("my_topic", 1, callback);
    
  2. class methods

    void Foo::callback(const std_msgs::StringConstPtr& message)
    {	}
    
    Foo foo_object;
    ros::Subscriber sub = nh.subscribe("my_topic", 1, &Foo::callback, &foo_object);
    
  3. functor objects,这里就不做介绍了。

SMQ中的回调处理是由Spinner实现的

spinner

ros::spin()

ros::spin() 创建了一个单独的线程 spinner thread 不停循环地、依次将callback从队列中取出并执行 。

虽然主程序当中没有循环,但是ros::spin()里有个循环。subscriber接收到消息并向callback queue当中放入一个回调函数,但是真正执行是在spin()里执行的。跟上面publisher thread类似, spinner thread处理回调的速度是根据回调函数的复杂程度决定的。

ros:spin() 是一个单独的线程,这个线程是循环处理回调函数的,一次处理一个回调函数。具体循环时间要依据callback而决定,这个跟receiver接收信息的频率一般是不一致的。如果比receiver接收信息要快,callback queue可以设置得很小;如果spinner处理回调的速度比receiver接收信息要慢,那么callback queue当中就开始累积callbacks,这时候queue_size最好设置地比较大一点。

有时候spinner thread实在处理不过来怎么办呢?我们可以让spinner thread开影分身——使用多线程spinner,并行处理callbacks。

总共有三种实现spinner的方式,可以参考Three implementations of spinners

Multi-threaded spinning

多线程的有两种ros::MultiThreadedSpinner()ros::AsyncSpinner()。一般用AsyncSpinner()

Multi spinner thread

ros::MultiThreadedSpinner spinner(2); 	// 开两个spinner并行处理
spinner.spin();

Instead of a blocking spin() call, it has start() and stop() calls, and will automatically stop when it is destroyed. An equivalent use of AsyncSpinner to the MultiThreadedSpinner example above, is:

ros::AsyncSpinner spinner(4); // Use 4 threads
spinner.start();
ros::waitForShutdown();

Reference

ROS Spinning, Threading, Queuing

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

理解ROS Topic 通信频率背后的机制 的相关文章

  • kinova-jaco2使用Moveit!控制真实机械臂抓取固定点物体

    kinova jaco2使用Moveit 控制真实机械臂抓取固定点物体 一 机械臂坐标系 坐标系方向 位姿方向 轴的起始点 二 启动机械臂和Moveit 三 实现抓取 python代码 python文件建议直接用python启动 四 遇到的
  • ROS中使用VLP16激光雷达获取点云数据

    ROS中使用VLP16激光雷达获取点云数据 个人博客地址 本文测试环境为 Ubuntu20 04 ROS Noetic 需要将激光雷达与PC连接 然后在设置 gt 网络 gt 有线中将IPv4改为手动 并且地址为192 168 1 100
  • ROS与navigation教程——ACML参数配置

  • SLAM-hector_slam 简介与使用

    hector slam功能包使用高斯牛顿方法 不需要里程计数据 只根据激光信息便可构建地图 所以他的总体框架如下 hector slam功能包 hector slam的核心节点是hector mapping 它订阅 scan 话题以获取SL
  • 关于相机与激光雷达数据采集与标定

    最近在做一个关于车路协同的项目 需要做一个路侧系统 传感器有摄像头和激光雷达 相机和激光雷达联合标定费了老半天劲 在此记录一下 雷达时间戳不对 导致摄像头和雷达的数据无法对齐 解决办法 修改雷达驱动发布点云消息时的时间戳 相机内参标定可以使
  • GG-CNN代码学习

    文章目录 1 源码网址 https github com dougsm ggcnn 2 数据集格式转化 下载后的康奈尔数据集 解压完之后里面的格式 里面的 tiff图像通过 txt文件转化得到 python m utils dataset
  • ModuleNotFoundError: No module named ‘rosbag‘

    1 ModuleNotFoundError No module named rosbag File opt ros kinetic lib python2 7 dist packages roslib launcher py line 42
  • 解决ros安装 使用roscore命令测试问题

    本人安装教程完成ROS的安装后 在进行测试如图1命令 出现 解决办法输入完命令1后要输入命令2才行 即可测试成功 测试成功的界面如下
  • Raspberry Pi 上 ROS 服务器/客户端通过GPIO 驱动硬件

    ROS 服务 现在 想象一下你在你的电脑后面 你想从这个服务中获取天气 你 在你身边 被认为是客户端 在线天气服务是服务器 您将能够通过带有 URL 的 HTTP 请求访问服务器 将 HTTP URL 视为 ROS 服务 首先 您的计算机将
  • 服务数据的定义和使用

    1 自定义数据服务 在包下创建srv文件夹 在文件夹下创建Person srv 在Person srv下输入以下内容 代表数据类型 string name uint8 age uint8 sex uint8 unknown 0 uint8
  • ubuntu18.04命令安装ros2

    ROS2官方文档 本教程为apt get命令安装方式 官网教程有点问题 借鉴一下大佬的安装方式 文章目录 1 安装ROS2 1 1 安装秘钥相关指令 1 2 授权秘钥 1 3 添加ROS2软件源 1 4 安装 2 设置环境 可选但是推荐 2
  • 进入 docker 容器,exec 丢失 PATH 环境变量

    这是我的 Dockerfile FROM ros kinetic ros core xenial CMD bash 如果我跑docker build t ros docker run it ros 然后从容器内echo PATH 我去拿 o
  • 在 CLion 中设置 ROS 包

    我正在使用 CLion C IDE 来编辑 ROS 包 我可以通过打开CMakeLists txt文件 但是 我收到一个错误 FATAL ERROR find package catkin 失败 在工作区和 CMAKE PREFIX PAT
  • 什么是 void `std::allocator`?即:`std::allocator`

    自动生成ROS 机器人操作系统 message C 头文件包含如下类型定义 typedef std msgs Header
  • 从 pcl::PointCloud 中删除点

    我是 PCL 新手 我正在使用 PCL 库 并且正在寻找一种从点云中提取点或将特定点复制到新点的方法 我想验证每个点是否符合条件 并且我想获得仅包含优点的点云 谢谢 使用 ExtractIndices 类 将要删除的点添加到 PointIn
  • 错误状态:平台不允许不安全的 HTTP:http://0.0.0.0:9090

    我正在尝试从我的 flutter 应用程序连接到 ws local host 9090 使用 rosbridge 运行 的 Ros WebSocket 服务 但我在 Flutter 中收到以下错误 错误状态 平台不允许不安全的 HTTP h
  • 无法在 ROS 中使用本地安装的 Protocol Buffer

    我已经安装了协议缓冲区 https developers google com protocol buffers 本地 ROS包的目录结构如下 CMakeLists txt package xml include addressbook p
  • 如何访问 Heroku 中的 docker 容器?

    我已按照此处构建图像的说明进行操作 https devcenter heroku com articles container registry and runtime getting started https devcenter her
  • 如何订阅“/scan”主题、修改消息并发布到新主题?

    我想通过订阅message ranges来改进turtlebot3的LDS 01传感器 通过应用一些算法修改messange ranges并将其发布到新主题 如下所示 但是当我运行编码时出现错误 错误是 遇到溢出的情况 错误是 运行时警告
  • 如何使用一个凉亭同时创建两个地图?

    如下图所示 现在我的gazebo正在运行2个slam gmapping包 首先是 turtlebot slam gmapping 发布到 map 主题 第二个是 slam gmapping 发布到与第一个相同的 map 主题 我想创建一个新

随机推荐

  • 听劝,不要试图以编程为基础去学习网络安全

    目录 一 网络安全学习的误区1 不要试图以编程为基础去学习网络安全2 不要刚开始就深度学习网络安全3 收集适当的学习资料4 适当的报班学习 二 学习网络安全的些许准备1 硬件选择2 软件选择3 外语能力 三 网络安全学习路线第一阶段 xff
  • 集美大学 - 2840 - 实验8 - 编程题

    实验8 1 9 指针 输出学生成绩 本题要求编写程序 xff0c 根据输入学生的成绩 xff0c 统计并输出学生的平均成绩 最高成绩和最低成绩 建议使用动态内存分配来实现 输入格式 xff1a 输入第一行首先给出一个正整数N xff0c 表
  • vscode中文乱码问题及几种常见的解决方案

    问题及原因 问题原因 xff1a 代码文件的字符编码格式为UTF 8 xff0c 但是terminal的字符编码格式为GBK 解决思路 xff1a 统一代码文件和terminal的字符编码格式 解决办法 说明 xff1a 以下的解决方案是针
  • 集美大学 - 2840 - 实验11-2 - 函数题

    实验11 2 1 链表 建立学生信息链表 本题要求实现一个将输入的学生成绩组织成单向链表的简单函数 函数接口定义 xff1a span class token keyword void span span class token funct
  • python正则表达式

    python正则表达式 match函数 re match尝试从字符串的起始位置匹配一个模式 xff0c 如果不是起始位置匹配成功的话 xff0c match 就返回none 函数语法 xff1a re span class token pu
  • 轻松解决VS配置OpenCV环境及导出OpenCV的VS项目模板

    一 OpenCV配置 1 下载OpenCV 点击进入下载OpenCV的官网界面 这里以Windows为例 xff0c 其他同理 xff08 可直接下载最新 xff09 2 提取OpenCV 在这里浅说一句 xff0c 为了方便环境配置文件管
  • 最大子段和问题

    以下给出具体代码 xff1a span class token macro property span class token directive hash span span class token directive keyword i
  • 如何简单又好看地美化你的Ubuntu界面

    起因 最近使用Ubuntu界面实属是审美疲劳了 xff0c 使用老版本的一大问题就是 界面太难看了 秉持新手学习最好是用老一点的稳定版本的观念 Ubuntu旧版本使用或使用过的人非常非常多 xff0c 学习的过程中你一旦出现什么问题互联网上
  • 【实战】物联网安防监控项目【2】———boa服务器的移植

    一 boa服务器的移植 1 源码下载 1 1 boa简介 xff1a 其可执行代码只有大约60KB左右 xff0c Boa是一个单任务的HTTP服务器 xff0c Boa只能依次完成用户的请求 xff0c 而不会fork出新的进程来处理并发
  • 【实战】物联网安防监控项目【4】———从网页上控制A9的LED灯

    前言 学习了一个新知识 xff0c 当然要记录一下啦 这两天学习了boa服务器 cgic标准库和html标签语言 xff0c 又双叕解锁一个嵌入式的新玩法 cgic库是沟通C语言和html网页编程语言的一座桥梁 xff0c 通过在linux
  • 【实战】物联网安防监控项目【5】———把模拟数据传输到web网页、web显示mjpeg-streamer视频图像

    1 模拟数据传输到web 为了把硬件传感器上的数据上传到web网页 xff0c 我们需要在跑linux服务器的开发板上写一个应用程序 xff0c 并创建出几个线程来收集传感器检测到的数据 xff0c 通过进程 线程间通信 boa与cgic库
  • HttpGet Digest授权认证

    工具类 xff1a compile com burgstaller okhttp digest 1 13 import android span class hljs preprocessor content span span class
  • ubuntu下git push失败error: 无法推送一些引用到 ‘xxx ‘解决方法

    如果你在Ubuntu下使用git push上传你的代码到gitee xff0c 突然出现一行报错 xff1a To git 64 gitee com imysy twenty two thread pool test git rejecte
  • 【Linux驱动开发】并发控制机制:原子操作、自旋锁、信号量、互斥锁详解

    并发控制机制 首先我们来了解一下 操作系统的并发性 这个概念 xff1a 操作系统的并发性 concurrence xff1a 指的是两个或者两个以上事件在同一时间间隔内发生 xff0c 即这个设备一会执行这个事件一会执行那个事件 xff0
  • STM32F051K8U6按键中断实例

    引言 最近要开始做毕设了 xff0c 准备用STM32做一个平衡小车 xff0c 好久没做过STM32的裸机项目了 xff0c 做几个项目练练手 xff0c 复习一下 本例程使用STM32CubeMX配套hal库来实现按键中断和串口中断 芯
  • STM32库函数笔记分享

    之前刚开始自学的部分STM32笔记放出 xff0c 希望对新入门STM32和想要复习库函数的小伙伴们起到帮助 建立工程 1 寄存器操作方式 需要不断地查手册来了解每一位是干什么用的 优点 xff1a 代码简介 xff1b 缺点 xff1a
  • rc.exe not found.(完美解决,亲测有效)

    完美解决rc exe not found 报错出错原因解决方法完美解决 报错 这两天安装了vs2015和IVF2016 xff0c 安装完之后在运行程序的时候一直会出现rc exe not found xff0c 重新生成解决方案后还是一样
  • 单片机与上位机通过串口通信--笔记

    定义 先说什么是串口 xff1f xff08 1 xff09 他是一种通信接口 xff0c 单片机 IO 口上的复用功能 xff0c 上位机 xff08 电脑 xff09 和下位机 xff08 开发板 xff09 之间的数据传输 xff08
  • Qt 的Cmake方式如何创建资源文件

    传统的qmake创建的工程有pro qrc xff0c 但是如果使用cmake方式创建的工程就没有这两个东西 xff0c 我们公司就是在linux下使用cmake创建的Qt工程 xff0c 没有pro也看不到qrc xff0c 想在ui界面
  • 理解ROS Topic 通信频率背后的机制

    Topic是ROS的三种通信方式中最为基本 也是常用的一种 本文对于ROS的Topic通信背后的数据吞吐机制做一个较为详细 深入的介绍 Publisher ROS中发布一个topic的函数是这样的 ros span class token