LCM中间件入门(1):工作原理核心概念及Ubuntu环境下的C++实践

发布于:2025-08-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、LCM中间件简介

LCM(Lightweight Communications and Marshalling)是一款轻量级通信与编组中间件,专为实时系统设计,尤其适用于机器人、自动驾驶、航空航天等对低延迟、高可靠性有要求的领域。其核心优势在于轻量化设计(无集中式服务器)、高效数据传输(基于UDP组播)和跨语言支持(C/C++、Python、Java等),能轻松实现多进程/多设备间的实时数据交互。

二、基本工作原理与核心概念
  1. 通信模型
    LCM采用发布-订阅(Publish-Subscribe)模式

    • 发布者(Publisher):向指定“通道(Channel)”发送消息。
    • 订阅者(Subscriber):监听感兴趣的“通道”,接收并处理消息。
    • 通道(Channel):字符串标识的消息传输路径(类似“话题”),用于区分不同类型的消息。
  2. 数据编组(Marshalling)
    LCM通过自定义消息类型实现跨平台数据传输。用户需定义.lcm格式的消息结构,工具会自动生成对应语言的编解码代码,解决不同设备/语言间的数据格式差异(如大小端、数据类型长度)。

  3. 传输机制
    基于UDP组播实现消息广播,支持同一网络内的多节点通信,无需中心节点转发,减少延迟。同时支持单播模式,适用于点对点通信。

三、Ubuntu下安装LCM
  1. 通过包管理器安装(推荐)
    适用于Ubuntu 18.04及以上版本:

    sudo apt update
    sudo apt install liblcm-dev
    
  2. 源码编译安装
    若需最新版本,可从源码编译:

    # 安装依赖
    sudo apt install build-essential cmake libglib2.0-dev
    
    # 克隆源码
    git clone https://github.com/lcm-proj/lcm.git
    cd lcm
    
    # 编译安装
    mkdir build && cd build
    cmake ..
    make -j4
    sudo make install
    
  3. 验证安装
    执行以下命令,若输出版本信息则安装成功:

    lcm-gen --version
    
四、C++使用示例

以下通过“温度传感器数据传输”案例,展示LCM的完整使用流程。

1. 定义消息类型(.lcm文件)

创建temperature.lcm,定义温度消息结构:

package example;  // 消息包名

struct Temperature {
    int64_t timestamp;  // 时间戳(毫秒)
    float value;        // 温度值(摄氏度)
    string sensor_id;   // 传感器ID
}
2. 生成C++代码

使用lcm-gen工具将.lcm文件转换为C++代码:

lcm-gen -x temperature.lcm  # -x 表示生成C++代码

执行后会生成example/Temperature.hpp文件,包含消息的结构体定义和编解码逻辑。

3. 发布者代码(publisher.cpp)
#include <lcm/lcm-cpp.hpp>
#include "example/Temperature.hpp"
#include <chrono>
#include <thread>

int main() {
    // 创建LCM实例(自动绑定网络)
    lcm::LCM lcm;
    if (!lcm.good()) {
        std::cerr << "LCM初始化失败!" << std::endl;
        return 1;
    }

    // 构造消息
    example::Temperature msg;
    msg.timestamp = 0;
    msg.sensor_id = "sensor_001";

    // 循环发布消息(模拟传感器数据)
    while (true) {
        msg.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
            std::chrono::system_clock::now().time_since_epoch()
        ).count();
        msg.value = 25.0 + (rand() % 100) / 10.0;  // 随机温度值(25.0~35.0℃)
        
        // 向通道"THERMOMETER"发布消息
        lcm.publish("THERMOMETER", &msg);
        std::cout << "发布温度: " << msg.value << "℃(时间戳: " << msg.timestamp << ")" << std::endl;
        
        std::this_thread::sleep_for(std::chrono::seconds(1));  // 每秒发布一次
    }

    return 0;
}
4. 订阅者代码(subscriber.cpp)
#include <lcm/lcm-cpp.hpp>
#include "example/Temperature.hpp"
#include <iostream>

// 消息处理类(继承自消息对应的Listener)
class TemperatureHandler {
public:
    // 回调函数:接收消息时自动调用
    void handleMessage(const lcm::ReceiveBuffer* rbuf,
                       const std::string& channel,
                       const example::Temperature* msg) {
        std::cout << "\n收到通道 [" << channel << "] 的消息:" << std::endl;
        std::cout << "传感器ID: " << msg->sensor_id << std::endl;
        std::cout << "温度值: " << msg->value << "℃" << std::endl;
        std::cout << "时间戳: " << msg->timestamp << "ms" << std::endl;
    }
};

int main() {
    lcm::LCM lcm;
    if (!lcm.good()) {
        std::cerr << "LCM初始化失败!" << std::endl;
        return 1;
    }

    // 创建消息处理器并订阅通道"THERMOMETER"
    TemperatureHandler handler;
    lcm.subscribe("THERMOMETER", &TemperatureHandler::handleMessage, &handler);

    std::cout << "等待接收温度数据..." << std::endl;
    while (true) {
        lcm.handle();  // 阻塞等待消息,收到后调用回调函数
    }

    return 0;
}
5. 编译与运行

创建CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(lcm_example)

# -------------------------- 手动指定 LCM 路径 --------------------------
# 替换为你的 LCM 头文件实际路径(根据上面的查询结果修改)
set(LCM_INCLUDE_DIR "/usr/include/lcm")  # 包管理器安装
# set(LCM_INCLUDE_DIR "/usr/local/include/lcm")  # 源码安装

# 替换为你的 LCM 库文件实际路径(根据上面的查询结果修改)
set(LCM_LIB_DIR "/usr/lib/x86_64-linux-gnu")  # 包管理器安装
# set(LCM_LIB_DIR "/usr/local/lib")  # 源码安装

# 指定 LCM 库文件名(通常是 liblcm.so,去掉前缀 "lib" 和后缀 ".so" 即为库名)
set(LCM_LIB "lcm")
# ----------------------------------------------------------------------

# 引入 LCM 头文件
include_directories(${LCM_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR})

# 引入 LCM 库文件路径
link_directories(${LCM_LIB_DIR})

# 编译发布者和订阅者,并链接 LCM 库
add_executable(publisher publisher.cpp example/Temperature.hpp)
target_link_libraries(publisher ${LCM_LIB})

add_executable(subscriber subscriber.cpp example/Temperature.hpp)
target_link_libraries(subscriber ${LCM_LIB})

编译并运行:

# 编译
mkdir build && cd build
cmake ..
make

# 打开两个终端,分别运行
./subscriber  # 订阅者
./publisher   # 发布者

运行后,订阅者会实时接收并打印发布者发送的温度数据。

五、总结

LCM凭借轻量化、低延迟的特性,在实时系统通信中占据重要地位。其核心是通过发布-订阅模式和自定义消息类型,实现跨进程/跨设备的高效数据交互。在Ubuntu环境下,通过简单的安装和代码生成流程,即可快速集成到C++项目中,特别适合机器人、无人机等领域的实时数据传输需求。


网站公告

今日签到

点亮在社区的每一天
去签到