1. 程式人生 > >ROS學習之 cpp訊息釋出者和訊息訂閱者

ROS學習之 cpp訊息釋出者和訊息訂閱者

wiki連結: http://wiki.ros.org/roscpp/Overview/Publishers%20and%20Subscribers

一、釋出一個話題

    其它相關連結:
        ros::NodeHandle,ros::NodeHandle::advertise() API:
            http://docs.ros.org/api/roscpp/html/classros_1_1NodeHandle.html
        ros::Publisher API:
            http://docs.ros.org/api/roscpp/html/classros_1_1Publisher.html
    建立一個控制代碼來向話題釋出訊息,使用ros::NodeHandle類,參見: http://wiki.ros.org/roscpp/Overview/NodeHandles
    NodeHandle::advertise()方法被用來建立一個ros::Publisher物件,該物件用於在話題上釋出訊息。
        ros::Publisher pub = nh.advertise<std_msgs::String>("topic_name", 5);
        std_msgs::String str;
        str.data = "hello world";
        pub.publish(str);

    1、內部執行緒釋出

        當一個節點內部存在對於同一個話題的訊息釋出者和訂閱者時,roscpp會跳過序列化和去序列化的過程(節省了處理過程和延遲)。
        該方法只適用於以共享指標釋出的訊息
            ros::Publisher pub = nh.advertise<std_msgs::String>("topic_name", 5);
            std_msgs::StringPtr str(new std_msgs::String);   //訊息指標
            str->data = "hello world";
            pub.publish(str);

    2、釋出訊息可選項

        advertise()簡單版的原型:
            template<class M>
            ros::Publisher advertise(const std::string& topic, uint32_t queue_size, bool latch = false);        
        引數說明: M,必須,這是一個模版引數,用於指定釋出話題的具體訊息的型別
              topic,必須,釋出話題的名稱
              queue_size,必須,訊息佇列的長度。如果釋出訊息的速度超過roscpp可以釋出訊息的速度,roscpp將丟棄最開始的訊息
              latch,可選,使能連線的latching(門閂,彈簧鎖)。當一個連線被latched,最後被髮布的訊息將被儲存,自動釋出給以後再訂閱該話題的訂閱者。

    3、釋出者控制代碼的其它操作

        ros::Publisher 是內部引用的,也就是可以快速複製,而無需建立新的ros::Publisher.當所有ros::Publisher副本被銷燬時,話題將被關閉。
        幾個例外:
            1、ros::shutdown()呼叫,它將關閉所有publishers
            2、ros::Publisher::shutdown()呼叫,將關閉這一個話題,如果有的話。
            3、對同一話題,同一訊息的多次呼叫NodeHandle::advertise()將產生對該話題的很多Publisher,它們被視為相互的副本。
        ros::Publisher實現了==,!=,和<操作符,也能用std::map,和std::set等
        可以使用ros::Publisher::getTopic()函式獲取一個topic話題

    4、publish()行為和訊息佇列

        roscpp中的publish()是非同步的,只有當話題有訂閱者時才會工作(釋出訊息)。
        publish()自己本身很快,它1.將訊息序列化進一個buffer;2.將buffer壓入一個佇列以備後續處理。
        這個佇列之後被roscpp的內部執行緒處理,它將此佇列送入每個連線此話題的subscriber,這一步中的佇列長度取決於advertise()呼叫queue_size引數.
        如果佇列長度溢位,最老的訊息將在新訊息加入佇列之前被丟棄。
        
        注意:還有一個OS級的佇列在傳輸層,比如TCP/UDP傳送buffer.


二、訂閱一個話題

    其它相關連結:
        ros::NodeHandle,ros::NodeHandle::subscribe() API:
            http://docs.ros.org/api/roscpp/html/classros_1_1NodeHandle.html
        ros::Subscriber API:
            http://docs.ros.org/api/roscpp/html/classros_1_1Subscriber.html
    建立一個控制代碼來向話題訂閱訊息,使用ros::NodeHandle類,參見: http://wiki.ros.org/roscpp/Overview/NodeHandles
    一個簡單的訂閱的全域性函式:
    void callback(const std_msgs::StringConstPtr& str)
    {
        ...
    }

    ...
    ros::Subscriber sub = nh.subscribe("my_topic", 1, callback);    
    

    1、對訂閱者的操作

        一個簡單的ros::NodeHandle::subscribe()函式的原型如下:
        template<class M>
        ros::Subscriber subscribe(const std::string& topic, uint32_t queue_size, <callback, which may involve multiple arguments>, const ros::TransportHints& transport_hints = ros::TransportHints());
    引數說明:M,不必要,模版引數,說明訊息的型別。對於subscribe()函式,不必顯式定義訊息的型別,編譯器可以從回撥函式的引數中來推測出訊息的型別。
        topic,訂閱的話題
        queue_size,roscpp使用該引數將此長度的訊息佇列傳遞給回撥函式。
        <callback>,訊息處理回撥函式
        transport_hints,對roscpp傳輸層的具體指定


    2、回撥函式原型

        一個回撥函式的原型如下:
        void callback(const boost::shared_ptr<Message const>&);

    3、回撥函式的型別

        1)一般函式
            一般函式的使用最為簡單。
            例子如上。
        2)類成員方法
            類成員方法的使用也很簡單,在呼叫時要加入類物件的引數
            void Foo::callback(const std_msgs::StringConstPtr& message)
            {
            }

            ...
            Foo foo_object;
            ros::Subscriber sub = nh.subscribe("my_topic", 1, &Foo::callback, &foo_object);
        3)仿函式物件(一個類的使用看上去像一個函式,包括 boost::bind)
            一個仿函式物件是一個定義了operator()函式的類。
            class Foo
            {
                public:
                  void operator()(const std_msgs::StringConstPtr& message)
                  {
                  }
            };
            傳遞給subscribe()函式的仿函式必須copyable,使用下述語法呼叫:
                ros::Subscriber sub = nh.subscribe<std_msgs::String>("my_topic", 1, Foo());

            注意:使用仿函式時,必須顯式地宣告訊息型別模版引數,因為在此種情況下,編譯器不會推測出訊息型別。

    4、訊息事件類[ROS 1.1+]


        MessageEvent API: http://docs.ros.org/api/roscpp_traits/html/classros_1_1MessageEvent.html
        MessageEvent類允許從一個訂閱回撥函式中提取訊息的元資料資訊。    
        
        使用語法如下:
            void callback(const ros::MessageEvent<std_msgs::String const>& event)
            {
              const std::string& publisher_name = event.getPublisherName();  //一些關於此訊息事件的元資料資訊
              const ros::M_string& header = event.getConnectionHeader();
              ros::Time receipt_time = event.getReceiptTime();

              const std_msgs::StringConstPtr& msg = event.getMessage();  //從訊息事件中獲取訊息,然後可以進行相應處理
            }