【ROS】的单线程Spinning和多线程Spinning

2023-05-16

参考:

  1. https://www.cnblogs.com/feixiao5566/p/5288206.html
  2. https://www.freesion.com/article/9499126134/
  3. https://blog.csdn.net/yaked/article/details/50776224

单线程Spinning

  • 单线程回调函数 ros::spin() 与 ros::spinOnce()

    这两个函数的学名叫ROS消息回调处理函数。它俩通常会出现在ROS的主循环中,程序需要不断调用ros::spin() 或 ros::spinOnce(),两者区别在于前者调用后不会再返回,也就是你的主程序到这儿就不往下执行了,而后者在调用后还可以继续执行之后的程序

    关于消息接收回调机制在ROS官网上略有说明 (callbacks and spinning)。总体来说其原理是这样的:除了用户的主程序以外,ROS的socket连接控制进程会在后台接收订阅的消息,所有接收到的消息并不是立即处理,而是等到spin()或者spinOnce()执行时才集中处理。所以为了保证消息可以正常接收,需要尤其注意spinOnce()函数的使用 (对于spin()来说则不涉及太多的人为因素)。

1. ros::spin()

  • ros::spin()是最简单的单线程自旋, 它会一直调用直到结束。

    用法: ros::spin()

#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);
    /**
     * ros::spin() 将会进入循环, 一直调用回调函数chatterCallback(),每次调用1000个数据。
     * 当用户输入Ctrl+C或者ROS主进程关闭时退出,
     */
    ros::spin(); 
    return 0;
}
  • 我们自己实现一个简单的与ros::spin()用法相同的例子:
ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0.1));

2. ros::spinOnce()

  • ros::spinOnce()定期调用等待在那个点上的所有回调;
  • 对于ros::spinOnce()的使用,虽说比ros::spin()更自由,可以出现在程序的各个部位,但是需要注意的因素也更多。比如:如果对于用户自己的周期性任务,最好和spinOnce()并列调用。即使该任务是周期性的对于数据进行处理,例如对接收到的IMU数据进行Kalman滤波,也不建议直接放在回调函数中:因为存在通信接收的不确定性,不能保证该回调执行在时间上的稳定性。

;对于有些传输特别快的消息,尤其需要注意合理控制消息池大小和ros::spinOnce()执行频率。比如消息送达频率为10Hz, ros::spinOnce()的调用频率为5Hz,那么消息池的大小就一定要大于2,才能保证数据不丢失,无延迟。

用法: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);
    while (ros::ok())
    {
        /*...TODO...*/ 

        ros::spinOnce();  // 只调用一次, 必须放在一个 while循环中
        loop_rate.sleep();
    }
    return 0;
}
  • 我们自己实现一个简单的与ros::spinonce()用法相同的例子:
ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0));

3. ros::spin()和ros::spinOnce()

  • ros::spin() 在调用后不会再返回,也就是你的主程序到这儿就不往下执行了,而 ros::spinOnce() 后者在调用后还可以继续执行之后的程序。在使用ros::spin()的情况下,一般来说在初始化时已经设置好所有消息的回调,并且不需要其他背景程序运行。这样以来,每次消息到达时会执行用户的回调函数进行操作,相当于程序是消息事件驱动的;而在使用ros::spinOnce()的情况下,一般来说仅仅使用回调不足以完成任务,还需要其他辅助程序的执行:比如定时任务、数据处理、用户界面等。

    其实看函数名也能理解个差不多,一个是一直调用;另一个是只调用一次,如果还想再调用,就需要加上循环了。

    这里一定要记住,ros::spin()函数一般不会出现在循环中,因为程序执行到spin()后就不调用其他语句了,也就是说该循环没有任何意义,还有就是spin()函数后面一定不能有其他语句(return 0 除外),有也是白搭,不会执行的。ros::spinOnce()的用法相对来说很灵活,但往往需要考虑调用消息的时机,调用频率,以及消息池的大小,这些都要根据现实情况协调好,不然会造成数据丢包或者延迟的错误。

  • 以上是它们的基础用法,那么spin到底做了什么呢?

    首先, 当我们调用ros::spin时, 会有一个互斥锁, 把你的回调队列加锁, 防止执行混乱;

    然后, 检测如果回调队列不为空, 则读取回调队列;

    最后,当while(nh.ok())为true时, 调用当前队列中的所有函数,如果有不满足的,会重新放回队列中。

    所以listener中, 就一直执行着ros::spin来监听话题了。

    从这样看来,spin和spinOnce的区别之一,就是while(nh::ok())执行块的大小了。另一个是等待时间,spin在执行时, 会指定一个返回前可以等待调用的时间。spin会等待0.1s,而spinonce不会。

  • 什么时候用ros::spin()和ros::spinOnce()呢?

    如果仅仅只是响应topic,就用ros::spin()。当程序中除了响应回调函数还有其他重复性工作的时候,那就在循环中做那些工作,然后调用ros::spinOnce()。

    1. ros::spin():下面的打印输出不会更新,相当于只执行了一次,但是它会不断处理ROS 的message queue(下图右边的subscriber 开头的打印函数只调用了一次,然后一直响应订阅的消息)。
      在这里插入图片描述
    2. ros::spinOnce():一直在打印 while 中的部分,循环调用print( )函数,并且处理message queue。
      在这里插入图片描述
      spinOnce( )较常用的做法是while里放publisher所要发布的msg的赋值处理,然后一直循环发布topic。
ros::Rate loop_rate(10);
 
  while (ros::ok())
  {
    std_msgs::String msg;
 
    std::stringstream ss;
    ss << "hello world " << count;
    msg.data = ss.str();
 
    chatter_pub.publish(msg);
 
    ros::spinOnce();
    loop_rate.sleep();
  }
  • spinOnce使得pub/sub为非阻塞锁;spin是客户端的, 因此是阻塞的

    这样就很好理解talker要用SpinOnce。有需要talk的时候发出,没有的时候不发送。而listener一直在阻塞着听。

  • 这样,再来说之前很流传的一句关于解释spin的话,“所有的回调函数都是spin调用的”。这是一句形象而不准确的话。回调函数一直等待在回调队列中, 只要条件一满足就会发生回调, 而spin的作用, 只是创建了线程给这个回调函数去执行它, 这样多线程就不会影响其他的作业。

    之所以用spin, 是因为rospy不愿指定线程模型, 在程序中将线程暴露出来, 而用spin来把它封装起来. 但你可以用多线程调用任意数量的回调函数.

    没有用户订阅, 服务和回调是不会被调用的.

多线程Spinning

  • 防阻塞多线程回调函数 ros::MultiThreadedSpinner 和 ros::AsyncSpinner

    对于一些只订阅一个话题的简单节点来说,我们使用ros::spin()进入接收循环,每当有订阅的话题发布时,进入回调函数接收和处理消息数据。但是更多的时候,一个节点往往要接收和处理不同来源的数据,并且这些数据的产生频率也各不相同,当我们在一个回调函数里耗费太多时间时,会导致其他回调函数被阻塞,导致数据丢失。这种场合需要给一个节点开辟多个线程,保证数据流的畅通

  • 为了观察不同话题的消息被阻塞的情况,可以参考以下实验代码:

  1. 多topic发布:https://github.com/GuoXiaoxiao1/wlh_ros_demo/blob/master/multi_thread_demo/src/multi_topic_pub.cpp

  2. 多topic接收:https://github.com/GuoXiaoxiao1/wlh_ros_demo/blob/master/multi_thread_demo/src/multi_topic_sub.cpp

    可以看到,发布程序中,以10hz的频率发布了chatter1和chatter2两个话题;在订阅程序中,回调函数1中加入了2s的延时,导致了回调函数2也只能2s才能接收到一个数据。为了使回调函数2能正常接收数据,使用在一个ROS节点中开辟多个线程的方法。

  • 在ROS中,有两种方法可以在一个节点中开辟多个线程:

1. ros::MultiThreadedSpinner

  • MultiThreadedSpinner是阻塞式的spinner,类似ros::spin()。

  • 在构造过程中可以指定它所用线程数,但如果不指定线程数或者线程数设置为0,它将在每个cpu内核开辟一个线程。

    用法如下:

ros::MultiThreadedSpinnerspinner(4); // Use 4 threads

spinner.spin();// spin() will not return until the node has been shutdown

2. ros::AsyncSpinner

  • ros::AsyncSpinner (since 0.10):很有用的线程spinner是AsyncSpinner,它不是阻塞式的spin()调用;

  • AsyncSpinner比MultiThreadedSpinner更优,它有start() 和stop() 函数,并且在销毁的时候会自动停止。

    下面的用法等价于上面的MultiThreadedSpinner例子:

ros::AsyncSpinner spinner(4); // Use4 threads

spinner.start();

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

【ROS】的单线程Spinning和多线程Spinning 的相关文章

  • 修改svn默认端口

    Subversion有两种不同的配置方式 xff0c 一种基于它自带的轻量级服务器svnserve xff0c 一种基于非常流行的Web服务器Apache 根据不同的配置方式 xff0c Subversion使用不同的端口对外提供服务 基于
  • 项目、系统开发中的需求分析说明书和需求规格说明书的区别

    项目组成员在针对要开发的系统做需求调研后 xff0c 就要编写对应的需求说明书 作为软件工程师 xff0c 你就得知道需求分析说明书和需求规格说明书的区别 xff0c 以期在正确的时候编写正确的需求文档 两者有何不同 xff1a xff08
  • 全景视频拼接的关键技术与步骤

    全景视频拼接是一种利用实景图像组成全景空间的技术 xff0c 它将多幅图像拼接成一幅大尺度图像或360度全景图 全景视频技术涉及到计算机视觉 计算机图形学 数字图像处理以及一些数学工具等技术 全景拼接基本步骤主要包括 xff1a 摄像机的标
  • Ubuntu18.04+ROS Realsense的安装与使用

    文章目录 前言一 安装软件包与librealsense1 内核检查2 Installing the packages 2022 11 21更新 xff1a 在换了ubuntu20 04 xff08 带有 xff09 5 15的内核报错后 x
  • C/C++内存管理详解[转载]

    我觉得这是一篇很不错的文章 xff0c 对C和C 43 43 的程序员来说 xff0c 很有实用价值 xff0c 故推荐给大家 作者 xff1a PingPong 文档来源 xff1a CSDN 伟大的Bill Gates 曾经失言 xff
  • 项目启动会应该注意的几点

    摘要 xff1a 开个好头 xff0c 万事不难 项目启动会作为项目建设生命周期的开始 xff0c 其意义和难度不言而喻 作为项目管理办公室的负责人 xff0c 需要特别重视项目启动会的召开 xff0c 杜绝走过场 xff0c 避免虽然知道
  • 飞机的航班代码/航班号码的编号规则

    以下内容来源于网络 xff0c 并整理而得 一 国内航班 中国国内航班号的编号规则 xff1a 航空公司的两字代码 43 4位数字 其中 xff0c 后面四位数字的第一位代表航空公司的基地所在地区 第二位代表航班基地外终点所在地区 xff0
  • 机场生产运行数据统计指标-第四篇-机场运行保障类

    机场运行保障类 1 民航航班正常统计 1 1 统计说明 xff08 1 xff09 统计范围的相关说明 xff1a 1 民航航班正常统计范围 xff1a 国内外运输航空公司执行的客货运航班 xff0c 包括正班 加班 包机 港澳台地区及国际
  • STM32CubeMAX入门篇

    要求使用单片机STM32F407IGT6 1 时钟配置 STM32F407外部高速晶振为25MHz xff0c 分别连接到PH0和PH1引脚 2 SWD配置 STM32F407仿真接口SWD分别连接到PA13和PA14引脚 xff01 开始
  • 无人机仿真XTDrone学习一:Mavros基础知识与作用

    XTDrone等无人机仿真平台 xff0c 利用ROS 43 Gazbo 43 PX4进行SITL xff08 软件在环仿真 xff09 xff0c 主要利用PX4飞控的offboard模式 xff0c 在此模式下上位机程序发布期望运动 x
  • 无人机仿真XTDrone学习二:常用的mavros消息类型

    mavros用于无人机通信 xff0c 可以将飞控与主控的信息进行交换 本次记录常用的mavros消息类型 官方Wiki最正确 xff0c 如有疑问首先查阅Wiki mavros wiki CSDN参考 常用话题 数传 用于查看数传状态 x
  • 无人机仿真XTDrone学习三:MAVRos功能包在offboard模式控制例程

    本教程介绍了使用 Gazbo SITL 中模拟的四轴飞行器 Offboard 控制缓慢起飞到2米的高度 61 61 注意 xff1a 61 61 使用 Offboard 模式来控制无人机有危险性的 如果你是在一个真正的实物平台上进行试验 x
  • 无人机仿真XTDrone学习四:XTDrone键盘控制无人机程序分析(MAVRos)一

    在XTDrone安装完成后 xff0c 运行一个键盘控制无人机程序测试XTDrone安装是否存在问题 通过分析该例程 xff0c 理解ROS对无人机的控制方法与控制过程 XTDrone键盘控制无人机例程仿真 XTDrone键盘控制无人机例程
  • 无人机仿真XTDrone学习五:XTDrone键盘控制无人机indoor1.launch程序分析二

    launch程序如下 这个文件启动了Gazebo仿真环境 xff0c 配置MAVROS功能包和PX4 SITL功能包 span class token prolog lt xml version 61 34 1 0 34 gt span s
  • 接收机灵敏度的计算公式推导和分析

    接收机灵敏度定义的接收机能够接收到的并且还能正常工作的最低电平强度 接收机灵敏度跟很多东西有关 xff0c 如噪声系数 信号带宽 解调信噪比等 xff0c 灵敏度一般来说越高 xff08 数值越低 xff09 xff0c 说明其接收微弱信号
  • 无人机仿真XTDrone学习六:XTDrone键盘控制无人机multirotor_communication.py程序分析三

    multirotor communication py程序 这个程序是实现对无人机控制的主要程序 xff0c 需要重点分析 可以实现对无人机的位置 xff0c 速度 xff0c 加速度的控制 主要节点 xff1a sys span clas
  • 无人机仿真XTDrone学习七:XTDrone键盘控制无人机 multirotor_keyboard_control.py程序分析四

    multirotor keyboard control py 该程序可以通过键盘对一个或者多个无人机进行速度或者加速度的控制并更改无人机或领导者无人机飞行状态 这个脚本通过发布状态命令和速度命令话题与通讯脚本进行通讯 xff0c 通讯脚本进
  • XTDrone仿真平台与Prometheus仿真平台

    常见的无人机仿真平台大都是用于对底层飞控算法的仿真与调试 xff0c XTDrone仿真平台与Prometheus仿真平台可以针对无人机上层算法进行仿真或者进行编队仿真 xff0c 这两者都采用ROS 43 PX4 43 Gazebo开源平
  • ROS中功能包二进制安装改为源码安装

    在学习XTDrone的二维路径规划时需要使用navigation功能包 xff0c 使用文档安装方式为二进制安装 sudo apt install span class token operator span y ros span clas
  • 2016晚安 2017你好

    不知不觉开通CSDN账号已有三年多的时间 xff0c 三年多以前抱着学习坚持的态度想要在CSDN上记录自己学习的点滴 结果三年多过去了 xff0c 2016年也随着过去了 xff0c 回顾2016年主要的三件事情就是 xff1a 1 从大学

随机推荐

  • Python入门学习--环境配置

    工作将近两年了 xff0c 做过B S结构的项目 xff0c 也做过android xff0c 也做过C S结构的项目 xff0c 相信无论是那种项目都是基于Java 学习运用Java也已经好多年了 xff0c 虽然也接触过C C 43 4
  • Python入门学习-数据类型

    一 类型的概念 首先 xff0c 对于一个数据1011100 xff0c 改怎么解释呢 xff1f 同Java等变成语言类型 xff0c 首先我们要明确数据的类型 xff0c 程序设计语言不允许语法歧义 xff0c 因此需要明确数据的类型
  • 流年似水 启航2019

    凌晨1点无意间看到一个演讲视频 感谢你给我机会上场 xff0c 很久之前的一个演讲视频 xff0c 看完除了羡慕还是羡慕吧 xff0c 也许就是一句话吧 xff0c 开挂的人生不需要解释 30多年的生活他做了很多事 xff0c 也做成了很多
  • Matplotlib 可视化必备神书,附pdf下载

    出品 xff1a Python数据之道 大家好 xff0c 我是阳哥 大家知道 xff0c 在利用Python进行数据可视化过程中 xff0c 基本上是很难绕开 Matplotlib 的 xff0c 因为 不少其他的可视化库多多少少是建立在
  • ubuntu学习笔记02

    1 sudo sh 与sudo bash sh区别 以超级用户身份运行 34 sh 34 xff0c sh实用程序是一个命令语言解释器 以超级用户身份运行 34 bash 34 xff0c Bash是shell或命令语言解释器 xff0c
  • 史上最浅显易懂的Git教程!

    从零起步的Git教程 xff0c 让你无痛苦上手世界上最流行的分布式版本控制系统Git xff01 既然号称史上最浅显易懂的Git教程 xff0c 那这个教程有什么让你怦然心动的特点呢 xff1f 首先 xff0c 本教程绝对面向初学者 x
  • 数据库死锁原因及解决办法

    死锁 xff08 Deadlock xff09 所谓死锁 xff1a 是指两个或两个以上的进程在执行过程中 xff0c 因争夺资源而造成的一种互相等待的现象 xff0c 若无外力作用 xff0c 它们都将无法推进下去 此时称系统处于死锁状态
  • spring和springmvc父子容器的关系

    大家都知道 xff0c 在spring的配置中要分开配置service层的注解扫描 xff0c 以及springmvc变现层的注解扫描 xff0c 如下 xff1a lt 扫描加载Service实现类 gt lt context compo
  • pageHelper分页插件实现原理及使用方法

    插件官方网站 xff1a https github com pagehelper Mybatis PageHelper tree master src main java com github pagehelper 实现原理 xff1a 使
  • 虚拟机Linux系统安装nginx服务器并启动的步骤

    工作前的准备 xff1a 1 装有Linux的虚拟机 2 nginx安装包 xff0c 注意是gz结尾的压缩文件 具体步骤1 xff1a 1 nginx安装环境 nginx是 C 语言开发 xff0c 建议在 linux 上运行 xff0c
  • 什么是反射机制,有什么作用

    1 反射机制定义 反射的概念是由Smith在1982年首次提出的 xff0c 主要是指程序可以访问 检测和修改其本身状态或行为的一种能力 在Java环境中 xff0c 反射机制允许程序在执行时获取某个类自身的定义信息 xff0c 例如熟悉和
  • 写给2016

    你不能期待着遇见怎样的自己 xff0c 但你可以选择成为怎样的自己 转眼16年就迎来了它的落幕 xff0c 不论怎样华丽的开场 xff0c 总有归于平静散场的结束 xff0c 不早不晚 xff0c 于清晨到傍晚 xff0c 于四季的轮回 x
  • 模块化建立项目流程(Maven聚合模块)

    先说项目使用Maven的好处 1 项目构建 Maven定义了软件开发的整套流程体系 xff0c 并进行了封装 xff0c 开发人员只需要指定项目的构建流程 xff0c 无需针对每个流程编写自己的构建脚本 2 依赖管理 除了项目构建 xff0
  • 如何在linux下判断web服务是否开启?

    对于web服务的开启的判断有以下几种常用方法 xff1a 1 端口查看 xff1a 本地 xff1a ss xff0c netstat xff0c lsof 1 2 3 4 5 6 7 8 9 10
  • git基本命令

    最近再写一些项目上传到github xff0c 所以要用到git命令 本地需要先安装git客户端 xff0c 然后指定一个git地址为本地仓库 然后右键git bash here打开git命令界面 首先服务端需要创建一个项目以便clone到
  • jps查看Java线程,jstack查看具体线程堆状态

    想要使用jps需要配置环境变量 xff0c 在classpath后在加一个指定Java bin目录 具体命令如下 t2挂起了 xff0c 堆里面显示t2为RUNNABLE xff0c suspend xff0c resume废弃使用 IBM
  • heap_1.c详解--------FreeRTOS内存管理

    heap 1源码分析 include lt stdlib h gt Defining MPU WRAPPERS INCLUDED FROM API FILE prevents task h from redefining all the A
  • 记录一个类加载变量引发的问题

    类加载变量导致的问题 类加载变量导致的问题 类加载变量导致的问题 因为项目需要 xff0c 银行要求使用weblogic部署并且启动所有项目 xff0c 不允许项目单独开服务启动一般都有这样的要求 xff0c 我所在的项目组有两个单独mai
  • Ubuntu16.04安装intel RealSense D435i驱动并在ROS中使用

    参考 xff1a https blog csdn net qq 43265072 article details 106437287https blog csdn net zhangfenger article details 849980
  • 【ROS】的单线程Spinning和多线程Spinning

    参考 xff1a https www cnblogs com feixiao5566 p 5288206 htmlhttps www freesion com article 9499126134 https blog csdn net y