首先spinOnce和spin区别:
spin就会进入一个循环体,一直检测消息队列的回调函数,而spinonce则只检测一遍消息队列,然后接着运行下面的语句。
首先要了解:
ROS中,publisher和subscriber都有一个消息队列用于数据收发时候的缓存。ROS话题的通信是异步的,也就是publisher只管不停的发但不管是否被接收,publisher是向Topic的消息队列发布信息,而不是publisher直接push给subscriber,publisher发送数据后,订阅该topic的subscriber则会过来读取,系统会通过位置指针管理不同subscriber读取消息队列的位置,因此可以支持任意多个subscriber对话题进行读取,每个subscriber读取到数据后,会将数据放置到自己的缓存队列上,然后通过回旋函数spin来调用回调函数。
综上所述,publisher不停将消息发布到消息队列上,直到队列占满,然后新到的消息把最老的消息挤出队列,因此,publisher消息队列的作用就是,缓存一定数量的历史信息,让不能及时订阅的subscriber能读取到之前发布的信息。如果没这个需求或是想让subscriber读取到最新的消息,那么让消息队列长度设为1 。
而subscriber的消息队列的作用是,将回调函数来不及处理的信息及时的缓存,这样不至于因为回调函数执行或调用过慢,导致数据的丢失。
两个消息队列都是先进先出的规则,即新到的消息把最老的消息挤出队列。然后subscriber的消息队列每个收到的消息还对应着回调函数,有几个消息就有即次回调函数。所有subscriber还有一个共用的全局消息队列用来存储待触发的回调函数,可以称其为回调队列。
注意:发布方和订阅方之间的通信和有没有回旋函数spin无关,即只要网络正常,发布方一发信息订阅方就会接收到并存储在消息队列中,当程序执行spin时,会按照订阅方的消息队列中的顺序来执行回调函数。
一些结论
每次调用ros::spinOnce()都会执行与消息队列中缓存的信息数量相同次数的回调函数,只要回调函数执行够快的话,就能清空队列。当多个不同的subscriber都需要调用回调函数时,则按顺序依次执行各个subscriber的回调函数。这个顺序就是全局消息队列里接收消息的顺序,所有subscriber有一个共用的全局消息队列,用来存放待执行的回调函数。
spinOnce()
在执行的那一刻,并不是只处理队列中的一个回调函数就返回了,而是会处理当时队列中存在的所有回调函数。
注意:执行回调函数的时候,每个订阅方自己的消息队列并不会被锁定住。每个订阅方不是对应两个消息队列吗,一个是每个订阅方自己储存收到的数据的消息队列,另一个则是所有订阅方共用的储存回调函数执行顺序的全局消息队列。当spinonce调用时,共用的回调函数的全局消息队列这个锁定,比如有七个回调函数要执行就按七个来,然后根据全局消息队列里的顺序依次执行回调函数,每个回调函数去寻找对应的订阅方自己的存储数据的消息队列,然后取得对应的数据。然而,每个订阅方自己的存储数据的消息队列是没有锁住的,即是在不断接收消息的,新的消息会将旧的消息顶出去,来替代旧的消息。也就是每个回调函数有一个指针指向订阅方自己的消息队列的固定的地址,但这个地址里的数据是会被新的顶掉的。
关于回调函数的定义
回调函数的形参的数据类型为发布方所发布的消息的数据类型。
例如发布方为:
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
则订阅方的回调函数为:
void doMsg(const std_msgs::String::ConstPtr& msg_p){
ROS_INFO("我听见:%s",msg_p->data.c_str());
}
标签:订阅,回调,函数,队列,subscriber,回旋,消息,ros
From: https://blog.csdn.net/weixin_43902341/article/details/137526462