ros::spin() 和 ros::spinOnce() 區別及詳解

版權聲明:本文爲博主原創文章,轉載請標明出處: http://www.cnblogs.com/liu-fa/p/5925381.html

博主提示:本文基於ROS Kinetic Kame,若有更(gèng)新版本,可能存在細微差異,請大兄弟以官方資料爲準。html

博主向來愚鈍,若大兄弟發現該文章有不妥之處,還請速速告知。函數

1 函數意義

首先要知道,這倆兄弟學名叫ROS消息回調處理函數。它倆一般會出如今ROS的主循環中,程序須要不斷調用ros::spin() 或 ros::spinOnce(),二者區別在於前者調用後不會再返回,也就是你的主程序到這兒就不往下執行了,然後者在調用後還能夠繼續執行以後的程序。oop

其實消息回調處理函數的原理很是簡單。咱們都知道,ROS存在消息發佈訂閱機制,什麼?不知道?不知道還不快去:http://wiki.ros.org/ROS/Tutorials (ROS官方基礎教程) 瞅瞅。設計

好,咱們繼續,若是你的程序寫了相關的消息訂閱函數,那麼程序在執行過程當中,除了主程序之外,ROS還會自動在後臺按照你規定的格式,接受訂閱的消息,可是所接到的消息並非馬上就被處理,而是必須要等到ros::spin()或ros::spinOnce()執行的時候才被調用,這就是消息回到函數的原理,怎麼樣,簡單吧,至於爲何這麼設計?咳咳,嗯,確定有他的道理。。。htm

2 區別

就像上面說的,ros::spin() 在調用後不會再返回,也就是你的主程序到這兒就不往下執行了,而 ros::spinOnce() 後者在調用後還能夠繼續執行以後的程序。blog

其實看函數名也能理解個差很少,一個是一直調用;另外一個是隻調用一次,若是還想再調用,就須要加上循環了。教程

這裏必定要記住,ros::spin()函數通常不會出如今循環中,由於程序執行到spin()後就不調用其餘語句了,也就是說該循環沒有任何意義,還有就是spin()函數後面必定不能有其餘語句(return 0 除外),有也是白搭,不會執行的。ros::spinOnce()的用法相對來講很靈活,但每每須要考慮調用消息的時機,調用頻率,以及消息池的大小,這些都要根據現實狀況協調好,否則會形成數據丟包或者延遲的錯誤。進程

3 常見使用方法

這裏須要特別強調一下,若是大兄弟你的程序寫了相關的消息訂閱函數,那千萬千萬千萬不要忘了在相應位置加上ros::spin()或者ros::spinOnce()函數,否則你是永遠都得不到另外一邊發出的數據或消息的,博主血的教訓,萬望緊記。。。get

3.1 ros::spin()

ros::spin()函數用起來比較簡單,通常都在主程序的最後,加入該語句就可。例子以下:回調函數

發送端:

#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 發送消息, 發送頻率爲10Hz(1秒發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() 將會進入循環, 一直調用回調函數chatterCallback(),每次調用1000個數據。
     * 當用戶輸入Ctrl+C或者ROS主進程關閉時退出,
     */
    ros::spin();
    return 0;
}

3.2 ros::spinOnce()

對於ros::spinOnce()的使用,雖然說比ros::spin()更自由,能夠出如今程序的各個部位,可是須要注意的因素也更多。好比:

1 對於有些傳輸特別快的消息,尤爲須要注意合理控制消息池大小和ros::spinOnce()執行頻率; 好比消息送達頻率爲10Hz, ros::spinOnce()的調用頻率爲5Hz,那麼消息池的大小就必定要大於2,才能保證數據不丟失,無延遲。

 

/**接收端**/
#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();
}
相關文章
相關標籤/搜索