ros::spin() 和 ros::spinOnce()函數(shù)的區(qū)別及詳解
1 函數(shù)意義
首先要知道,這倆兄弟學(xué)名叫ROS消息回調(diào)處理函數(shù)。它倆通常會出現(xiàn)在ROS的主循環(huán)中,程序需要不斷調(diào)用ros::spin() 或 ros::spinOnce(),兩者區(qū)別在于前者調(diào)用后不會再返回,也就是你的主程序到這兒就不往下執(zhí)行了,而后者在調(diào)用后還可以繼續(xù)執(zhí)行之后的程序。
其實消息回調(diào)處理函數(shù)的原理非常簡單。我們都知道,ROS存在消息發(fā)布訂閱機制,什么?不知道?不知道還不快去:http://wiki.ros.org/ROS/Tutorials (ROS官方基礎(chǔ)教程) 瞅瞅。
好,我們繼續(xù),如果你的程序?qū)懥讼嚓P(guān)的消息訂閱函數(shù),那么程序在執(zhí)行過程中,除了主程序以外,ROS還會自動在后臺按照你規(guī)定的格式,接受訂閱的消息,但是所接到的消息并不是立刻就被處理,而是必須要等到ros::spin()或ros::spinOnce()執(zhí)行的時候才被調(diào)用,這就是消息回到函數(shù)的原理,怎么樣,簡單吧,至于為什么這么設(shè)計?咳咳,嗯,肯定有他的道理。。。
2 區(qū)別
就像上面說的,ros::spin() 在調(diào)用后不會再返回,也就是你的主程序到這兒就不往下執(zhí)行了,而 ros::spinOnce() 后者在調(diào)用后還可以繼續(xù)執(zhí)行之后的程序。
其實看函數(shù)名也能理解個差不多,一個是一直調(diào)用;另一個是只調(diào)用一次,如果還想再調(diào)用,就需要加上循環(huán)了。
這里一定要記住,ros::spin()函數(shù)一般不會出現(xiàn)在循環(huán)中,因為程序執(zhí)行到spin()后就不調(diào)用其他語句了,也就是說該循環(huán)沒有任何意義,還有就是spin()函數(shù)后面一定不能有其他語句(return 0 除外),有也是白搭,不會執(zhí)行的。ros::spinOnce()的用法相對來說很靈活,但往往需要考慮調(diào)用消息的時機,調(diào)用頻率,以及消息池的大小,這些都要根據(jù)現(xiàn)實情況協(xié)調(diào)好,不然會造成數(shù)據(jù)丟包或者延遲的錯誤。
3 常見使用方法
這里需要特別強調(diào)一下,如果大兄弟你的程序?qū)懥讼嚓P(guān)的消息訂閱函數(shù),那千萬千萬千萬不要忘了在相應(yīng)位置加上ros::spin()或者ros::spinOnce()函數(shù),不然你是永遠(yuǎn)都得不到另一邊發(fā)出的數(shù)據(jù)或消息的,博主血的教訓(xùn),萬望緊記。。。
3.1 ros::spin()
ros::spin()函數(shù)用起來比較簡單,一般都在主程序的最后,加入該語句就可。例子如下:
發(fā)送端:
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
/**
* 向 Topic: chatter 發(fā)送消息, 發(fā)送頻率為10Hz(1秒發(fā)10次);消息池最大容量1000。
*/
chatter_pub.publish(msg);
loop_rate.sleep();
++count;
}
return 0;
}
接收端:
#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() 將會進(jìn)入循環(huán), 一直調(diào)用回調(diào)函數(shù)chatterCallback(),每次調(diào)用1000個數(shù)據(jù)。
* 當(dāng)用戶輸入Ctrl+C或者ROS主進(jìn)程關(guān)閉時退出,
*/
ros::spin();
return 0;
}
3.2 ros::spinOnce()
對于ros::spinOnce()的使用,雖說比ros::spin()更自由,可以出現(xiàn)在程序的各個部位,但是需要注意的因素也更多。比如:
1 對于有些傳輸特別快的消息,尤其需要注意合理控制消息池大小和ros::spinOnce()執(zhí)行頻率; 比如消息送達(dá)頻率為10Hz, ros::spinOnce()的調(diào)用頻率為5Hz,那么消息池的大小就一定要大于2,才能保證數(shù)據(jù)不丟失,無延遲。
/**接收端**/<br>#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();
loop_rate.sleep();
}
return 0;
}
2 ros::spinOnce()用法很靈活,也很廣泛,具體情況需要具體分析。但是對于用戶自定義的周期性的函數(shù),最好和ros::spinOnce并列執(zhí)行,不太建議放在回調(diào)函數(shù)中;
/*...TODO...*/
ros::Rate loop_rate(100);
while (ros::ok())
{
/*...TODO...*/
user_handle_events_timeout(...);
ros::spinOnce();
loop_rate.sleep();
}
相關(guān)文章
詳談innerHTML innerText的使用和區(qū)別
下面小編就為大家?guī)硪黄斦刬nnerHTML innerText的使用和區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
JS如何獲取未來n天的時間(返回日期:yyyy-mm-dd,并且判斷是否是今天和星期)
開發(fā)中經(jīng)常遇到獲取時間的業(yè)務(wù),將常用的方法做個筆記記錄下,對JS如何獲取未來n天的時間相關(guān)知識感興趣的朋友一起看看吧2024-03-03
js es6系列教程 - 基于new.target屬性與es5改造es6的類語法
下面小編就為大家?guī)硪黄猨s es6系列教程 - 基于new.target屬性與es5改造es6的類語法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09
在JavaScript中調(diào)用OpenAI?API的詳細(xì)步驟
在?JavaScript?中調(diào)用?OpenAI?API?也非常簡單,下面我將結(jié)合具體代碼示例以及使用場景,詳細(xì)講解如何使用?JavaScript?調(diào)用?OpenAI?API,需要的朋友可以參考下2025-04-04
[js高手之路]HTML標(biāo)簽解釋成DOM節(jié)點的實現(xiàn)方法
下面小編就為大家?guī)硪黄猍js高手之路]HTML標(biāo)簽解釋成DOM節(jié)點的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
JavaScript展開運算符和剩余運算符的區(qū)別詳解
本文主要介紹了JavaScript展開運算符和剩余運算符的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01

