ROS學習筆記-ros::spin()和spinonce()
在寫作業過程中,需要訂閱一個話題,結果無論如何嘗試都無法進入回撥函式,原因是因為沒有使用ros::spin()。如果寫的程式使用了訊息訂閱函式,千萬千萬不要忘了在相應位置加ros::spin()或ros::spinonec(),具體情況看要怎麼使用。
關於ros訊息釋出器和訂閱器的教程: http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber(c++)
訊息釋出器在一個while裡面一直迴圈傳送“hello world”到話題(topic)chatter上。訊息訂閱器一旦知道chatter上面有data,就會將這data作為引數傳入callback函式中,但是此時還沒有執行callback函式,而是把callback函式放到了一個回撥函式佇列中。所以當釋出器不斷髮送data到chatter上面時,就會有相應的callback函式進入佇列中,它們函式名一樣,只是實參不一樣。
那麼什麼時候才會執行callback函式呢?
就是ros::spin()和ros::spinOnce()的事了。
當spinOnce函式被呼叫時,spinOnce就會呼叫回撥函式佇列中第一個callback函式,此時callback函式才被執行,然後等到下次spinOnce函式又被呼叫時,回撥函式佇列中第二個callback函式就會被呼叫,以此類推。
所以,這會有一個問題。因為回撥函式佇列的長度是有限的,如果釋出器傳送資料的速度太快,spinOnce函式呼叫的頻率太少,就會導致佇列溢位,一些callback函式就會被擠掉,導致沒被執行到。
而對於spin函式,一旦進入spin函式,它就不會返回了,相當於它在自己的函式裡面死迴圈了。只要回撥函式佇列裡面有callback函式在,它就會馬上去執行callback函式。如果沒有的話,它就會阻塞,不會佔用CPU。
1.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; }
2.ros::spinonce()
對於ros::spinOnce()的使用,雖說比ros::spin()更自由,可以出現在程式的各個部位,但是需要注意的因素也更多。比如:
1 對於有些傳輸特別快的訊息,尤其需要注意合理控制訊息池大小和ros::spinOnce()執行頻率; 比如訊息送達頻率為10Hz, ros::spinOnce()的呼叫頻率為5Hz,那麼訊息池的大小就一定要大於2,才能保證資料不丟失,無延遲
/**接收端**/<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()用法很靈活,也很廣泛,具體情況需要具體分析。但是對於使用者自定義的週期性的函式,最好和ros::spinOnce並列執行,不太建議放在回撥函式中;
/*...TODO...*/
ros::Rate loop_rate(100);
while (ros::ok())
{
/*...TODO...*/
user_handle_events_timeout(...);
ros::spinOnce();
loop_rate.sleep();
}