lcm通信库介绍与使用指南
1. LCM 简介
LCM 全称是 Lightweight Communications and Marshalling,它是一个为 机器人系统、多进程通信 等场景设计的 高效消息传输与数据打包框架。
它最早由麻省理工学院(MIT)为 DARPA Urban Challenge 项目开发,用于在多个进程、多个计算机之间进行低延迟的消息传输和类型安全的数据序列化。
主要特点:
- 轻量级:只提供核心的通信和序列化功能,没有太多依赖。
- 跨语言、跨平台:支持 C、C++、Java、Python、Lua、MATLAB 等。
- 类型安全:消息格式通过专用的
.lcm
描述文件定义,并自动生成对应编程语言的代码。 - 低延迟:采用 UDP 多播(也可以配置 TCP/Unicast)。
- 支持多进程和多机通信。
2. LCM 架构概述
LCM 由几部分组成:
-
消息类型定义
- 用户通过
.lcm
文件定义消息类型(类似于IDL/ProtoBuf) - 编译后自动生成各语言的类/结构体,以及序列化/反序列化代码。
- 用户通过
-
订阅-发布模型(pub/sub)
- Publisher:发布者,把消息编码后发送到特定的信道(channel)。
- Subscriber:订阅者,监听某个信道收到消息时触发回调处理函数。
- 信道是按名字划分的(字符串),比如
"EXAMPLE"
。
-
传输层
- 默认使用 UDP multicast (避免单独的Broker,分布式无中心结构)。
- 也可以使用 TCP 单播(需要手动配置)。
3. 安装 LCM
Ubuntu/Debian
sudo apt-get update
sudo apt-get install liblcm-dev lcm
但是我的lcm其实是源码安装的,我是ubuntu20.04,还需要根据README.md回退到一个支持的commit,然后编译安装才行
macOS (brew)
brew install lcm
Python
pip install lcm
(需要先系统安装核心库)
4. 定义消息类型
LCM 的消息描述文件用 .lcm
扩展名,例如 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;
}
解释:
package
指定包名(对应代码生成的命名空间/模块)struct
定义消息结构- 支持基础类型 (
int8_t
,int16_t
,int32_t
,int64_t
,float
,double
,string
,boolean
) 以及数组(定长/变长) - 变长数组需前面有一个字段表示长度
5. 生成代码
假设你想用 Python 和 C++:
lcm-gen -p example_t.lcm # 生成 Python 代码
lcm-gen -x example_t.lcm # 生成 C++ 代码
生成后会在对应目录下出现:
- Python:
exlcm/example_t.py
- C++:
exlcm/example_t.hpp
6. Python 使用示例
发布端 publish.py
import lcm
from exlcm import example_t
import timelc = lcm.LCM()
msg = example_t()msg.timestamp = int(time.time() * 1e6)
msg.position = [1.0, 2.0, 3.5]
msg.orientation = [0, 0, 0, 1]
msg.num_ranges = 3
msg.ranges = [100, 200, 300]
msg.name = "Hello LCM"
msg.enabled = Truelc.publish("EXAMPLE", msg.encode())
订阅端 subscribe.py
import lcm
from exlcm import example_tdef my_handler(channel, data):msg = example_t.decode(data)print("Received message on channel \"%s\"" % channel)print(" timestamp = %s" % msg.timestamp)print(" position = %s" % list(msg.position))print(" orientation = %s" % list(msg.orientation))print(" ranges: %s" % list(msg.ranges))print(" name = %s" % msg.name)print(" enabled = %s" % msg.enabled)lc = lcm.LCM()
subscription = lc.subscribe("EXAMPLE", my_handler)try:while True:lc.handle()
except KeyboardInterrupt:pass
运行:
- 启动订阅端
python subscribe.py
- 发布一条消息
python publish.py
7. C++ 使用示例
publisher.cpp
#include <lcm/lcm-cpp.hpp>
#include "exlcm/example_t.hpp"
#include <iostream>
#include <ctime>int main()
{lcm::LCM lcm;if(!lcm.good())return 1;exlcm::example_t msg;msg.timestamp = static_cast<int64_t>(time(NULL) * 1000000);msg.position[0] = 1.0;msg.position[1] = 2.0;msg.position[2] = 3.5;msg.orientation[0] = 0;msg.orientation[1] = 0;msg.orientation[2] = 0;msg.orientation[3] = 1;msg.num_ranges = 3;msg.ranges.resize(3);msg.ranges[0] = 100;msg.ranges[1] = 200;msg.ranges[2] = 300;msg.name = "Hello LCM";msg.enabled = true;lcm.publish("EXAMPLE", &msg);return 0;
}
subscriber.cpp
#include <lcm/lcm-cpp.hpp>
#include "exlcm/example_t.hpp"
#include <iostream>class Handler
{
public:void handleMessage(const lcm::ReceiveBuffer* rbuf,const std::string& chan,const exlcm::example_t* msg){std::cout << "Received message on channel " << chan << std::endl;std::cout << " timestamp = " << msg->timestamp << std::endl;}
};int main()
{lcm::LCM lcm;if(!lcm.good())return 1;Handler handler;lcm.subscribe("EXAMPLE", &Handler::handleMessage, &handler);while(0 == lcm.handle());return 0;
}
编译链接需要 -llcm
。
8. 调试工具
LCM 提供了很多可视化工具(基于 Java Swing):
- lcm-spy:查看当前网络中传输的消息
lcm-spy
- lcm-logplayer:回放记录文件
- lcm-logger:记录通信到日志文件
9. 环境变量与配置
常用环境变量:
LCM_DEFAULT_URL
:设定默认通信URL,格式:- UDP 多播:
udpm://239.255.76.67:7667?ttl=1
- TCP:
tcpq://<host>:<port>
- UDP 多播:
- 更改多播组 IP 与端口可避免冲突。
10. 使用注意事项
- UDP 多播可能受交换机路由限制,在不同网络段要确保多播可达。
- 大消息最好分片或使用 TCP 传输模式。
- Python 下,
lc.handle()
会阻塞等待一个消息,可以用lc.handle_timeout(ms)
实现超时。 - 订阅和发布的数据类型必须匹配,否则解码会失败。
如果你愿意的话,我可以帮你再画一张 LCM 工作流程图,帮助你更直观地理解它的运行过程。
要我画吗?