回旋函数ros::spin()及ros::spinOnce()相关理解(结合实例)
个人理解:当每次执行到回调函数时,并不会立即调用并处理,而是将其放到一个回调函数的消息队列中,spin就是循环监听这个队列,等待新消息的到来,当队列中有了新的回调函数就处理,而spinonce是处理一次消息队列中的所有消息,然后就去执行其他代码。,此时消息队列里面存放了过去1s两个订阅方收到的2*5条消息(除第1次,可能发布方发布消息时订阅方已经开始计时),程序会处理当时队列中存在的所有回调函数,
回旋函数ros::spin()及ros::spinOnce()相关理解(结合实例)
回旋函数
/*
ros::spinOnce()
作用:处理一轮回调;在循环体内,处理所有可用的回调函数
*/
/*
ros::spin()
作用:进入循环处理回调
*/
相同点:二者都用于处理回调函数;
不同点:
这里先了解ros如何处理回调函数
ROS:回调函数处理与回调队列_ros 回调函数队列-CSDN博客
A.ros::spin()
当函数执行到ros::spin()
,就不再继续向下执行,而是循环且监听消息队列。
ros::spin() 是进入了循环监听消息队列,且执行对应回调函数,在 ros::spin() 后的语句不会执行到; ros::spin() 一般不会放在循环体里执行(会使循环失去意义),常放到程序的末尾执行。
B.ros::spinOnce()
ros::spinOnce()
只会执行一次回调函数,处理结束后继续执行ros::spinOnce()
后的代码
个人理解:当每次执行到回调函数时,并不会立即调用并处理,而是将其放到一个回调函数的消息队列中,spin就是循环监听这个队列,等待新消息的到来,当队列中有了新的回调函数就处理,而spinonce是处理一次消息队列中的所有消息,然后就去执行其他代码。
示例:
# 发布方
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h" // ros中文本类型
#include <sstream>
int main(int argc, char *argv[])
{
// 2.初始化ros节点
ros::init(argc,argv,"pub");
// 3.创建节点句柄
ros::NodeHandle nh;
// 4.创建发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("pos",10);
// 5.编写发布逻辑并发布数据
// 要求以10hz的频率发布数据,并且文本后添加编号
// 先创建被发布消息
std_msgs::String msg;
// 发布频率
ros::Rate rate(5); // 该参数为指定频率
// 设置编号
int count = 0;
// 程序休眠,防止节点还未在roscore中注册时就发送消息
ros::Duration(3).sleep();
// 编写循环,循环中发布数据
while(ros::ok())
{
// 输出中文
setlocale(LC_ALL,"");
count++;
// 实现字符串拼接数字
// msg.data = "here";
std::stringstream ss;
ss << "hello ---> " << count;
msg.data = ss.str();
pub.publish(msg);
ROS_INFO("发布的数据是:%s",ss.str().c_str());
// 根据前面制定的发送频率自动休眠 休眠时间 = 1/频率;
rate.sleep();
}
return 0;
}
# 订阅方
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h" // ros中文本类型
void domsg1(const std_msgs::String::ConstPtr &msg)
{
// 通过msg获取并操作订阅到的数据
//ros::Duration(1);
ROS_INFO("A接受到的数据是:%s",msg->data.c_str());
}
void domsg2(const std_msgs::String::ConstPtr &msg)
{
// 通过msg获取并操作订阅到的数据
ROS_INFO("B接受到的数据是:%s",msg->data.c_str());
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
// 2.初始化ros节点
ros::init(argc,argv,"sub");
// 3.创建节点句柄
ros::NodeHandle nh;
// 4.创建订阅者对象
ros::Subscriber sub1 = nh.subscribe("pos",10,domsg1);
ros::Subscriber sub2 = nh.subscribe("pos",10,domsg2);
int count_tmp = 0;
while(ros::ok()){
count_tmp++;
ros::Duration(1).sleep();
ROS_INFO("这是第%d次接受数据。",count_tmp);
ros::spinOnce();
}
// 5.处理订阅到的数据
// 6.循环读取接收的数据,并调用回调函数处理
// 如果要用回调函数必须用spin
ROS_INFO("I'm here!");
return 0;
}
运行结果:
分析:
发布方的发布频率是每秒5个包,订阅方每1秒执行一次ros::spinOnce()
,此时消息队列里面存放了过去1s两个订阅方收到的2*5条消息(除第1次,可能发布方发布消息时订阅方已经开始计时),程序会处理当时队列中存在的所有回调函数,处理完毕后继续向下执行。
理解过程中遇到的问题:
发布方代码如上,订阅方代码刚开始如下:
#include "ros/ros.h"
#include "std_msgs/String.h" // ros中文本类型
void domsg1(const std_msgs::String::ConstPtr &msg)
{
//ros::Duration(1);
ROS_INFO("A接受到的数据是:%s",msg->data.c_str());
}
void domsg2(const std_msgs::String::ConstPtr &msg)
{
ROS_INFO("B接受到的数据是:%s",msg->data.c_str());
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"sub");
ros::NodeHandle nh;
ros::Subscriber sub1 = nh.subscribe("pos",10,domsg1);
ros::Subscriber sub2 = nh.subscribe("pos",10,domsg2);
//!!!注意这里注释了延时函数
//ros::Duration(1).sleep();
ros::spinOnce();
ROS_INFO("I'm here!");
return 0;
}
运行结果:
在这种状态下多次执行订阅方代码并没有订阅到数据,预期应该是会执行一次回调函数,所以一直不理解ros::spinOnce()
的工作原理(其实这里应该是对消息订阅的工作模式没有理解)
此时,解开这句注释 ros::Duration(1).sleep();
再次执行:
分析:
在执行以下两行代码时,应该是打开对于话题"pos"的订阅状态,如果不延时,程序很快(执行一两行代码的时间)就跑到ros::spinOnce()
,而发布方的发布频率是每秒5个包,在这么短时间,确实没有订阅到话题"pos"的相关消息,导致相关回调函数也没有得到执行;而在代码后延时1s,在这1s过程中,就可以接收到"pos"话题相关的消息,从而使回调函数得到执行。
ros::Subscriber sub1 = nh.subscribe("pos",10,domsg1);
ros::Subscriber sub2 = nh.subscribe("pos",10,domsg2);
这里确实花了点心思去理解回旋函数,希望一点记录可以帮到同样有困惑的朋友,特此单发一条
更多推荐
所有评论(0)