如果工程只用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
#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
#include
#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
#include
#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
#include
#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学习(五)问题小结 -