1. 程式人生 > >ROS學習筆記-ros::spin()和spinonce()

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();
}