LCM-發、收

  • 這幾天寫了lcm發、收、錄、讀的小工具,之前都用過發和收,這次主要還是錄和讀,就是把接收到的lcm消息保存下來,再通過另外一個讀的程序把數據讀出來,用於離線分析等。
  • LCM(Lightweight Communications and
    Marshalling)在實時系統的數據傳遞中經常被使用。它具有高帶寬、低延遲的特點,支持多種編程語言,API使用方便。官方文檔提供了基本的使用說明,以及函數的具體代表。

發送消息

step1:定義結構體

1、將要發送的數據,定義在一個結構體中,寫在以 .lcm 結尾的文件中。比如:

struct example_t
{
    int64_t  timestamp;
    double   position[3];
    double   orientation[4]; 
    int32_t  num_ranges;
    int16_t  ranges[num_ranges];
    string   name;
    boolean  enabled;
}

其中,數據類型需要使用lcm支持的類型,如下:
在這裏插入圖片描述
定義好數據結構後,生成對應語言使用的頭文件。使用如下指令:
在這裏插入圖片描述
沒有問題的話就會生成example_t.hpp,這個文件是不允許修改的。

step2:LCM初始化

數據結構的頭文件生成後,在主程序代碼裏#include進來

#include <lcm/lcm-cpp.hpp>
#include “example_t.hpp"

定義lcm的對象並初始化

lcm::LCM lcm;
 if(!lcm.good())
	return 1;

初始化幹了什麼呢?我們進去看一下:

inline bool
LCM::good() const
{
    return this->lcm != NULL;
}

好像也沒幹什麼?

step3:賦值併發送

在程序中定義一個數據結構的對象,並在相應的位置進行賦值

example_t   my_data;
my_data.timestamp = 0;
my_data.position[0] = 1;
my_data.position[1] = 2;
my_data.position[2] = 3;

發送

lcm.publish("EXAMPLE", &my_data);

其中"EXAMPLE"爲通道名,可以自由定義,接收的時候也要指定,二者保持一致才能收到。
發送的數據較多時,可以定義多個數據結構,相應會有多個lcm生成的頭文件。用不同的通道名來區分他們。發送時也可靈活選擇對哪些通道的數據進行發送。

至此,lcm發送的方式就結束了,還是很簡潔的。整個發送代碼如下:

 	  #include <lcm/lcm-cpp.hpp>
     #include "exlcm/example_t.hpp"
     
     int main(int argc, char ** argv)
     {
         lcm::LCM lcm;
         if(!lcm.good())
             return 1;
     
        exlcm::example_t my_data;
        my_data.timestamp = 0;
    
        my_data.position[0] = 1;
        my_data.position[1] = 2;
        my_data.position[2] = 3;
    
    
        lcm.publish("EXAMPLE", &my_data);
    
        return 0;
    }

接收消息

消息接收主要利用lcm.subscribe()函數,函數定義如下:

LCM::subscribe(const std::string& channel,
    void (MessageHandlerClass::*handlerMethod)(const ReceiveBuffer* rbuf, const std::string& channel, const MessageType* msg),
    MessageHandlerClass* handler)

上面需要3個參數,分別是(通道名、回調函數、句柄對象)。
subscribe()還有另一種重載形式,其中回調函數只要2個參數,不用綁定具體的類對象。

LCM::subscribe(const std::string& channel,
    void (MessageHandlerClass::*handlerMethod)(const ReceiveBuffer* rbuf, const std::string& channel),
    MessageHandlerClass* handler)

step1:LCM初始化

 #include <stdio.h>
 #include <lcm/lcm-cpp.hpp>
#include "exlcm/example_t.hpp"

int main(int argc, char** argv)
	 {
        lcm::LCM lcm;
        if(!lcm.good())
            return 1;
      }

step2:定義句柄及回調函數

句柄類可以如下定義,其中handleMessage即爲subscribe時使用的回調函數:

class Handler 
     {
         public:
             ~Handler() {}
     
            void handleMessage(const lcm::ReceiveBuffer* rbuf,
                    const std::string& chan, 
                    const exlcm::example_t* msg)
            {
                int i;
                printf("Received message on channel \"%s\":\n", chan.c_str());
                printf("  timestamp   = %lld\n", (long long)msg->timestamp);
                printf("  position    = (%f, %f, %f)\n",
                        msg->position[0], msg->position[1], msg->position[2]);
                printf("\n");
            }
    };

上面代碼將接收到的內容打印出來,可以看到,使用了之前定義的消息類型example_t

step3:接收消息

定義句柄對象,調用subscribe()函數接收。其中lcm.handle()

Handler handlerObject;
lcm.subscribe("EXAMPLE", &Handler::handleMessage, &handlerObject);

while(0 == lcm.handle());

LCM自動解碼消息,再傳給回調函數,回調函數可以識別消息類型。因爲回調函數在lcm.handle()方法中調度,所以不需要併發執行,這些都在一個單線程中完成。
調用lcm.handle()非常重要,函數會保持阻塞直到有任務需要做。

完整的接收代碼如下,代碼只會接收通道名爲”EXAMPLE"的消息。

	1 #include <stdio.h>
    2 #include <lcm/lcm-cpp.hpp>
    3 #include "exlcm/example_t.hpp"
    4 
    5 class Handler 
    6 {
    7     public:
    8         ~Handler() {}
    9 
   10         void handleMessage(const lcm::ReceiveBuffer* rbuf,
   11                 const std::string& chan, 
   12                 const exlcm::example_t* msg)
   13         {
   14             int i;
   15             printf("Received message on channel \"%s\":\n", chan.c_str());
   16             printf("  timestamp   = %lld\n", (long long)msg->timestamp);
   17             printf("  position    = (%f, %f, %f)\n",
   18                     msg->position[0], msg->position[1], msg->position[2]);
   28         }
   29 };
   30 
   31 int main(int argc, char** argv)
   32 {
   33     lcm::LCM lcm;
   34     if(!lcm.good())
   35         return 1;
   36 
   37     Handler handlerObject;
   38     lcm.subscribe("EXAMPLE", &Handler::handleMessage, &handlerObject);
   39 
   40     while(0 == lcm.handle());
   41 
   42     return 0;
   43 }

一個lcm對象可以有無限個接收器,如果有多個通道的消息需要接收,可以

lcm.subscribe("AAAA", &Handler::handleMessage, &handlerObject);
lcm.subscribe("BBBB", &Handler::handleMessage, &handlerObject);

或者用下面方式接收所有通道

lcm.subscribe(".*", &Handler::handleMessage, &handlerObject);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章