ros::spin ros::spinOnce使用细节

本文涉及的产品
资源编排,不限时长
简介: ros::spin ros::spinOnce使用细节

1 ros::spin()

代码示例

Person.msg

string name
uint8  age
uint8  sex
uint8 unknown = 0
uint8 male    = 1
uint8 female  = 2

person_publisher.cpp

#include <ros/ros.h>
#include "learning_topic/Person.h"
int main(int argc, char **argv)
{
    // ROS节点初始化
    ros::init(argc, argv, "person_publisher");
    // 创建节点句柄
    ros::NodeHandle n;
    // 创建一个Publisher,发布名为/person_info的topic,消息类型为learning_topic::Person,队列长度10
    ros::Publisher person_info_pub = n.advertise<learning_topic::Person>("/person_info", 10);
    // 设置循环的频率
    ros::Rate loop_rate(1);
    int count = 0;
    while (ros::ok())
    {
        // 初始化learning_topic::Person类型的消息
      learning_topic::Person person_msg;
    person_msg.name = "Tom";
    person_msg.age  = 18;
    person_msg.sex  = learning_topic::Person::male;
        // 发布消息
    person_info_pub.publish(person_msg);
        ROS_INFO("Publish Person Info: name:%s  age:%d  sex:%d", 
          person_msg.name.c_str(), person_msg.age, person_msg.sex);
        // 按照循环频率延时
        loop_rate.sleep();
    }
    return 0;
}

person_subscriber.cpp

#include <ros/ros.h>
#include "learning_topic/Person.h"
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", 
       msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "person_subscriber");
    // 创建节点句柄
    ros::NodeHandle n;
    // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
    // 循环等待回调函数
    ros::spin();
    return 0;
}

程序执行流程分析:

  1. 消息发布器在一个while里面一直循环发送“person_msg”到话题(topic)/person_info上。
  2. 消息订阅器一旦知道/person_info上面有person_msg,就会将这person_msg作为参数传入callback函数中,但是此时还没有执行callback函数,而是把callback函数放到了一个回调函数队列中。
  3. 所以当发布器不断发送person_msg到/person_info上面时,就会有相应的callback函数进入队列中,它们函数名一样,只是实参不一样。

那什么时候处理回调函数队列中的回调函数了?这就是 ros::spin() 需要做的工作了。

  1. 对于spin函数,一旦进入spin函数,它就不会返回了,也不继续往后执行了,相当于它在自己的函数里面死循环了(直到ctrl+c 或者程序终止的时候才退出)。
  2. 主要的工作,就是不断的检查回调函数队列里面是否有callback函数存在,如果有的话,它就会马上去执行callback函数。如果没有的话,它就会阻塞,不会占用CPU。

2 ros::spinOnce()

消息publish跟上面一样,这里用ros::spinOnce改写subscriber程序

person_subscriber.cpp

#include <ros/ros.h>
#include "learning_topic/Person.h"
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
    // 将接收到的消息打印出来
    ROS_INFO("Subcribe Person Info: name:%s  age:%d  sex:%d", 
       msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
    // 初始化ROS节点
    ros::init(argc, argv, "person_subscriber");
    // 创建节点句柄
    ros::NodeHandle n;
    // 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
    ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
#if 0
    // 循环等待回调函数
    ros::spin();
#endif
  ros::Rate loop_rate(5);
    while (ros::ok())
    {
        ros::spinOnce();
        loop_rate.sleep(); //配合执行频率,sleep一段时间,然后进入下一个循环。
    }
    return 0;
}

回调函数的队列的创建和上面的分析是一样的,这里讲一讲ros::spinOnce() 的处理流程:

  1. 当spinOnce函数被调用时,spinOnce就会调用回调函数队列中第一个callback函数,此时callback函数被执行。
  2. spinOnce函数执行一次后,接着执行下面的语句。不像spin函数,进入到自己的内部循环,不往下执行。
  3. 等到下次spinOnce函数又被调用时,回调函数队列中第二个callback函数就会被调用,以此类推。

那我们如何控制执行的速度了,也就是执行的频率。从上面的实例中,我们看到了:

ros::Rate loop_rate(5); 设置执行频率

loop_rate.sleep(); 配合执行频率,sleep一段时间,然后进入下一个循环。

通过这样的设置,我们就可以设计自己的监听频率了,而不用完全被动的接收topic了。

3 区别

上面其实已经分析到了一个主要区别

  • ros::spin()被动的接收topic,或者说纯粹的接收topic.
  • ros::spinOnce()可以根据自己的需求设置接收频率。更加主动灵活。
ros::Rate loop_rate(10);
while(ros::ok())
{
// can add some function
ros::spinOnce();
loop_rate.sleep();
}


如果退化成下面这样的化,其实和ros::spin()是一样的。
```c
while(ros::ok())
{
    ros::spinOnce();
}

从上面的对比中,其实可以看出,ros::spin()和ros::spinOnce() 还有一个重要的区别就是:

ros::spinOnce() 可以配合其它函数,一起放在while循环中处理。也就是说,当程序中除了响应回调函数还有其他重复性工作的时候,那就在循环中做那些工作,然后调用ros::spinOnce()。


相关实践学习
使用ROS创建VPC和VSwitch
本场景主要介绍如何利用阿里云资源编排服务,定义资源编排模板,实现自动化创建阿里云专有网络和交换机。
阿里云资源编排ROS使用教程
资源编排(Resource Orchestration)是一种简单易用的云计算资源管理和自动化运维服务。用户通过模板描述多个云计算资源的依赖关系、配置等,并自动完成所有资源的创建和配置,以达到自动化部署、运维等目的。编排模板同时也是一种标准化的资源和应用交付方式,并且可以随时编辑修改,使基础设施即代码(Infrastructure as Code)成为可能。 产品详情:https://www.aliyun.com/product/ros/
相关文章
|
6月前
|
机器学习/深度学习 网络协议 中间件
[ROS2] --- ROS diff ROS2
[ROS2] --- ROS diff ROS2
151 0
|
3月前
|
存储 自然语言处理 机器人
ROS2教程06 ROS2行动
这篇文章是关于ROS2(Robot Operating System 2)行动(Action)通信机制的教程,包括行动的概念、特点、命令行工具的使用,以及如何编写行动的客户端和服务器代码,并介绍了如何测试行动通信。
116 4
ROS2教程06 ROS2行动
|
3月前
|
机器人 Shell Python
ROS2教程05 ROS2服务
这篇文章是关于ROS2(Robot Operating System 2)服务的教程,涵盖了服务的概念、特性、命令行工具的使用,以及如何编写服务的服务器和客户端代码,并提供了测试服务通信机制的示例。
97 4
ROS2教程05 ROS2服务
|
3月前
|
编解码 机器人 C++
ROS2教程07 ROS2自定义消息接口
这篇文章是关于如何在ROS2(Robot Operating System 2)中创建和使用自定义消息类型的教程,包括消息类型的定义、特点、命令行工具的使用,以及如何编写和测试自定义消息类型接口的步骤。
95 0
ROS2教程07 ROS2自定义消息接口
|
3月前
|
算法 数据可视化 机器人
ROS2教程01 ROS2介绍
本文是ROS2(机器人操作系统的下一代)的介绍教程,内容包括ROS2的诞生背景、核心功能、特点、框架以及与ROS1的比较。文章涵盖了ROS2的通信系统、框架和工具、生态系统、全球性社区支持、完全开源、跨平台特性、多机协同能力、实时系统支持和更强的稳定性。此外,还提供了ROS2架构的详细介绍资源链接,适合对ROS2感兴趣的读者学习和了解。
171 1
|
3月前
|
存储 机器人
01 ROS基本概念及命令
这篇文章介绍了ROS(机器人操作系统)的基本概念,如节点、ROS Master、参数、通信机制(话题和消息、服务)、功能包、命名空间和命名重映射,以及一些基本的ROS命令行工具的使用,例如rosnode、rostopic、rossrv、rosservice、rosparam和roslaunch。
58 0
|
Ubuntu 算法 网络协议
ROS简介
机器人是一个系统工程,它涉及机械、电子、控制、通信、软件等诸多学科。以前,开发一个机器人需要设计机械、画电路板、写驱动程序、设计通信架构、组装集成、调试、以及编写各种感知决策和控制算法,每一个任务都需要花费大量的时间。然而随着技术进步,机器人产业分工开始走向细致化、多层次化,如今的电机、底盘、激光雷达、摄像头、机械 臂等元器件都由不同厂家专门生产,社会分工加速了机器人行业的发展。而各个部件的集成就需要一个统一的软件平台,在机器人领域,这个平台就是机器人操作系统 ROS。
|
Ubuntu
ROS学习-了解ROS的文件结构
ROS学习-了解ROS的文件结构
147 0
|
文件存储 C++ Python
ROS学习-创建一个ROS msg和一个srv
ROS学习-创建一个ROS msg和一个srv
282 0
|
XML 机器人 定位技术
ROS:pluginlib
在计算机领域,插件是很常用的术语。插件是一种模块化的软件,可以在现有应用软件的基础上增加一些新的功能。
ROS:pluginlib

相关课程

更多