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

文章目录

一、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及以上版本:

    bash 复制代码
    sudo apt update
    sudo apt install liblcm-dev
  2. 源码编译安装

    若需最新版本,可从源码编译:

    bash 复制代码
    # 安装依赖
    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. 验证安装

    执行以下命令,若输出版本信息则安装成功:

    bash 复制代码
    lcm-gen --version
四、C++使用示例

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

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

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

lcm 复制代码
package example;  // 消息包名

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

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

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

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

3. 发布者代码(publisher.cpp)
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)
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 复制代码
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})

编译并运行:

bash 复制代码
# 编译
mkdir build && cd build
cmake ..
make

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

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

五、总结

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

相关推荐
科大饭桶12 分钟前
C++入门自学Day5-- C/C++内存管理(续)
c语言·开发语言·c++
lzb_kkk26 分钟前
【实习总结】Qt通过Qt Linguist(语言家)实现多语言支持
开发语言·c++·qt·1024程序员节·qt linguist·qt 语言家
ZLRRLZ44 分钟前
【数据结构】哈希表实现
数据结构·c++·散列表
TTBIGDATA2 小时前
【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step6—本地apt源
ubuntu·ambari·hdp·bigtop·edp·ambari3·hidataplus
weixin_307779132 小时前
ClickHouse Windows迁移方案与测试
linux·c++·数据仓库·windows·clickhouse
爱掉发的小李3 小时前
Linux 环境下 Docker 安装与简单使用指南
java·linux·运维·c++·python·docker·php
澄澈i3 小时前
设计模式学习[17]---组合模式
c++·学习·设计模式·组合模式
1白天的黑夜14 小时前
前缀和-1314.矩阵区域和-力扣(LeetCode)
c++·leetcode·前缀和
jdlxx_dongfangxing5 小时前
2023 年 NOI 最后一题题解
c++·noi