ROS学习(四)ROS系统下LCM通信

如果工程只用ROS搭建,那么是不需要用到LCM的。但如果ROS只是整体系统的一部分,则需要利用内存共享或者网络传输等方式与其他程序进行数据交互,这时候LCM就是一种比较简单的选择,例如自动驾驶中车内利用ROS进行数据流处理控制,车间利用LCM通信。

1. LCM的介绍与安装

(1)LCM 简介

自动驾驶领域有很多进程间通信的方式,如ROS、Apollo的Cyber RT以及一些自动驾驶初创公司对ROS进行改进的通信协议,今天介绍一种适用于高速自动驾驶场景的LCM通信协议,其特点是轻量化、传输速度快,易封装。

LCM(Lightweight Communications and Marshalling)是一组用于消息传递和数据编组的库和工具,其基于UDP传输的属性,传输速度较快,其目标是高带宽和低延迟的实时系统。它提供了一种发布/订阅消息传递模型以及带有各种编程语言C++、Java、python等应用程序绑定的自动编组/解组代码生成,LCM通过将消息封装在不同的Channel中进行通信,这点类似于ROS中的Topic。

(2)LCM安装

Releases · lcm-proj/lcm · GitHub下载源码包,本文下载的是lcm-1.4.0.zip。打开terminal并cd到解压后的文件夹,依次执行:

mkdir build

cd build

cmake ..

make

编译完成后执行:

sudo make install

完成LCM的安装。然后告诉系统lib的库所在位置:

export LCM_INSTALL_DIR=/usr/local/lib

sudo sh -c "echo$LCM_INSTALL_DIR> /etc/ld.so.conf.d/lcm.conf"

sudo ldconfig

配置pkgconfig:

export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$LCM_INSTALL_DIR/pkgconfig

至此LCM安装配置完成。

2. LCM 通信示例

LCM可以将数据进行封装和发送,使用UDP组播的方式发送出去。首先我们需要有我们自己的数据。LCM给我们提供了一个程序,可以很简单的将我们的结构体转变为LCM需要的格式(这里必须转变,否则LCM将无法发送或封装),这个程序就是lcm-gen。

新建文件夹lcm_example用于存放LCM工程,进入lcm_example文件夹,新建example_t.lcm空白文档来定义一个我们自己想要的结构体:

package exlcm;

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-gen -x example_t.lcm

生成一个文件夹exlcm,并包含一个文件example_t.hpp,到这里lcm结构体定义完成。

接下来我们建立收发此结构体类型message的LCM收发机。

(1)建立publisher

在lcm_example目录下,新建send_message.cpp复制以下内容:

#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;

    my_data.orientation[0] = 1;

    my_data.orientation[1] = 0;

    my_data.orientation[2] = 0;

    my_data.orientation[3] = 0;

    my_data.num_ranges = 15;

    my_data.ranges.resize(my_data.num_ranges);

    for(int i = 0; i < my_data.num_ranges; i++)

        my_data.ranges[i] = i;

    my_data.name = "example string";

    my_data.enabled = true;

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

    return 0;

}

(2)建立subscriber

在lcm_example目录下,新建listener.cpp复制以下内容:

#include <stdio.h>

#include <lcm/lcm-cpp.hpp>

#include "exlcm/example_t.hpp"

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("  orientation = (%f, %f, %f, %f)\n",

                    msg->orientation[0], msg->orientation[1],

                    msg->orientation[2], msg->orientation[3]);

            printf("  ranges:");

            for(i = 0; i < msg->num_ranges; i++)

                printf(" %d", msg->ranges[i]);

            printf("\n");

            printf("  name        = '%s'\n", msg->name.c_str());

            printf("  enabled    = %d\n", msg->enabled);

        }

};

int main(int argc, char** argv)

{

    lcm::LCM lcm;

    if(!lcm.good())

        return 1;

    Handler handlerObject;

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

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

    return 0;

}

(3)编辑CMakeLists.txt文件

在lcm_example目录下,新建CMakeLists.txt文件:

project(lcm_test)

set(CMAKE_CXX_STANDARD 11)

add_executable(send_message send_message.cpp)

target_link_libraries(send_message lcm)

add_executable(listener listener.cpp)

target_link_libraries(listener lcm)

编译CMakeLists.txt文件成功后,生成可执行文件send_message和listener。

分别在两个terminal中cd到可执行文件所在位置,分别运行:

./listener

./send_message

便可在listerner窗口中看见由send_message发送的信息。

3. ROS系统下LCM通信示例

接下来把LCM连接到ROS系统中使用。

(1)建立LCM_Client.cpp文件

#include <ros/ros.h>

#include <iostream>

#include "lcm/lcm-cpp.hpp"

int main(int argc, char** argv)

{

  ros::init(argc, argv, "image_publisher");

  ros::NodeHandle nh;

  lcm::LCM lcm;

  if (!lcm.good())

  {

    return 1;

  }

  char data[5];

  data[0] = 1;

  data[1] = 5;

  data[2] = 1;

  data[3] = 2;

  data[4] = 1;

  lcm.publish("EXAMPLE", data,5);//第一个参数是通道名,第二个参数是数据指针,第三个参数是长度

  std::cout << "发送成功!";

  ros::spinOnce();

}

(2)建立LCM_Server.cpp文件

#include <ros/ros.h>

#include <iostream>

#include "lcm/lcm-cpp.hpp"

class MyMessageHandler

{

public:

  void onMessage(const lcm::ReceiveBuffer* rbuf, const std::string& channel)

  {

    std::cout << (int)((unsigned char*)rbuf->data)[0] << std::endl;

    std::cout << (int)((unsigned char*)rbuf->data)[1] << std::endl;

    std::cout << (int)((unsigned char*)rbuf->data)[2] << std::endl;

    std::cout << (int)((unsigned char*)rbuf->data)[3] << std::endl;

    std::cout << (int)((unsigned char*)rbuf->data)[4] << std::endl;

    std::cout << "接收成功!";

  }

};

int main(int argc, char** argv)

{

  ros::init(argc, argv, "image_publisher");

  ros::NodeHandle nh;

  lcm::LCM lcm;

  MyMessageHandler handler;

  lcm.subscribe("EXAMPLE", &MyMessageHandler::onMessage, &handler);

  while (true)

    lcm.handle();

  ros::spin();

}

(3)修改CmakeLists.txt文件

include_directories(${catkin_INCLUDE_DIRS})

add_executable(LCM_Client src/LCM_Client.cpp)

target_link_libraries(LCM_Client lcm)

target_link_libraries(LCM_Client ${catkin_LIBRARIES})

add_executable(LCM_Server src/LCM_Server.cpp)

target_link_libraries(LCM_Server lcm) //necessary for lcm message transmission

target_link_libraries(LCM_Server ${catkin_LIBRARIES}

编译运行即可。简单来说,在ROS中使用LCM,需要将lcm相应头文件include进来,并且生成lcm变量,使用lcm.publish和lcm.subscribe即可。

如有问题欢迎联系讨论。

参考文档:自动驾驶消息传输机制LCM的安装与使用_python_snail_zcx的博客-CSDN博客ROS系统学习7---LCM数据的发送和接收_matlab_weixinhum-CSDN博客

下一篇:ROS学习(五)问题小结 - 简书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,270评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,489评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,630评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,906评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,928评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,718评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,442评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,345评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,802评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,984评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,117评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,810评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,462评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,011评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,139评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,377评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,060评论 2 355