LCM通信


一、LCM的介绍与安装

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

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

(2)LCM安装

# 安装依赖
sudo apt install build-essential libglib2.0-dev cmake
# 拷贝源码
git clone https://github.com/lcm-proj/lcm.git
cd lcm
# 编译并安装
mkdir build && cd build
cmake ..
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安装配置完成。

二、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;
}

1.c语言示例

在lcm文件下编译,生成对应的文件

lcm-gen -c example_t.lcm

在这里插入图片描述
(1)新建lcm_client.c

#include <lcm/lcm.h>
#include "exlcm_example_t.h"

int main(void)
{
    lcm_t * lcm = lcm_create(NULL);
    if(!lcm)
        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] = 3;
    my_data.orientation[2] = 5;
    my_data.orientation[3] = 7;
    my_data.num_ranges = 15;

    exlcm_example_t_publish(lcm, "EXAMPLE", &my_data);
    lcm_destroy(lcm);
    return 0;
}

(2)新建lcm_server.c

#include <stdio.h>
#include <inttypes.h>
#include <lcm/lcm.h>
#include "exlcm_example_t.h"

static void my_handler(const lcm_recv_buf_t *rbuf, const char * chan, 
        const exlcm_example_t * msg, void * user)
{

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

int main(int argc, char ** argv)
{
    lcm_t * lcm = lcm_create(NULL);
    if(!lcm)
        return 1;

    exlcm_example_t_subscribe(lcm, "EXAMPLE", &my_handler, NULL);

    while(1)
        lcm_handle(lcm);

    lcm_destroy(lcm);
    return 0;
}

(3)编译运行

gcc exlcm_example_t.c lcm_client.c -o client -llcm
gcc exlcm_example_t.c lcm_server.c -o server -llcm

在这里插入图片描述

2.cpp示例

在lcm文件下编译,生成对应的文件

lcm-gen -x example_t.lcm

在这里插入图片描述
lcm_client.cpp

#include <iostream>
#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] = 3;
    my_data.orientation[2] = 5;
    my_data.orientation[3] = 7;
    my_data.num_ranges = 15;

    lcm.publish("EXAMPLE", &my_data);
    return 0;
}

lcm_server.cpp

#include <iostream>
#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)
        {
            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]);
        }
};

int main(int argc, char** argv)

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

    lcm.subscribe("EXAMPLE", &Handler::handleMessage, &handlerObject);
	
    while (true)
	 lcm.handle();
    return 0;

}

编译运行

g++ lcm_client.cpp -o clinet -llcm
g++ lcm_server.cpp -o server -llcm

三、ROS系统下LCM通信示例

建立lcm功能包

catkin_create_pkg lcm std_msgs roscpp rospy

将上述cpp示例生成的lcm文件复制到src下
在这里插入图片描述

lcm_client.cpp

#include <ros/ros.h>
#include <iostream>
#include "lcm/lcm-cpp.hpp"
#include "string.h"
#include "exlcm/example_t.hpp"
int main(int argc, char** argv)
{
  ros::init(argc, argv, "image_publisher");
  ros::NodeHandle nh;
 
  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] = 3;
  my_data.orientation[2] = 5;
  my_data.orientation[3] = 7;
  my_data.num_ranges = 15;

  lcm.publish("EXAMPLE", &my_data);
  std::cout << "发送成功!"<<std::endl;
 
  ros::spinOnce();
}

lcm_server.cpp

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

class MyMessageHandler 
{
public:
  void onMessage(const lcm::ReceiveBuffer* rbuf,const std::string& chan,const exlcm::example_t* msg)
    {
	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]);
        }
};
 
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();
}

在CMakeList.txt添加

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)
target_link_libraries(lcm_server ${catkin_LIBRARIES})

编译运行即可

catkin_make

四、总结

上述的所生成的可执行文件 lcm_client和lcm_server均可以互相通信,前提保证发布和订阅的通道名称相同(示例中为 EXAMPLE)。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值