LCM通讯的使用

news2024/9/22 13:32:50

本文主要介绍LCM通讯的基本使用,内容主要整理自官网

https://lcm-proj.github.io/lcm/index.html

LCM,即Library for Communication and Marshalling,是一组用于消息传递与数据封装的库和工具,它主要的面向对象是要求高带宽、低延迟的实时系统。

它使用publish/subscribe的方式进行进程间通信,和ROS的话题有点类似。

既然涉及进程间通信,那么使用什么编程语言就无关紧要了,即不同编程语言编写的程序都可以互相通信。

LCM主要具有以下特点:

  • 低延迟进程间通信

  • 使用UDP组播的高效广播机制

  • 类型安全的编组/解组

  • 用户友好的日志记录和回放功能

  • 点对点通信

  • 无需守护进程

  • 依赖少

本文介绍LCM的基本使用。本文环境为ubuntu20.04

LCM下载

进入官网,下载安装包

https://github.com/lcm-proj/lcm/releases

image-20240712223302612

环境构建

首先安装LCM的依赖:

sudo apt install build-essential cmake libglib2.0-dev

然后将包放到自己的目录下解压,执行以下步骤:

mkdir build

cd build

cmake ..

make

sudo make install

至此环境构建完成。

简单程序编写

简单体验下LCM的通讯方式,此处使用C++语言,使用官网例程做示范。

  • 选择一个路径创建项目的文件夹

    image-20240712224402425

  • 定义消息类型,创建发布方、接收方,编写CMakeLists文件

    image-20240712224622049

    四个文件的内容如下:

    • 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_publisher.cpp

      #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] = 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;
      }
      
    • lcm_receiver.cpp

      
      #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)
              {
                  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;
      }
      
    • CMakeLists.txt

      cmake_minimum_required(VERSION 3.1)
      
      project(lcm_cpp_example)
      
      find_package(lcm REQUIRED)
      include(${LCM_USE_FILE})
      
      # Put all message definition files in the type directory in one list
      FILE(GLOB example_message_definitions "${CMAKE_CURRENT_LIST_DIR}/../types/*.lcm")
      
      # Generate headers from message definition
      lcm_wrap_types(CPP_HEADERS cpp_headers
        ${example_message_definitions})
      
      # Create library from all the messages
      lcm_add_library(example_messages-cpp CPP ${cpp_headers})
      target_include_directories(example_messages-cpp INTERFACE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
      
      # Create executables for the three example programs, linking all of them to our
      # messages library and lcm
      
      add_executable(lcm_receiver "lcm_receiver.cpp")
      lcm_target_link_libraries(lcm_receiver example_messages-cpp ${LCM_NAMESPACE}lcm)
      
      add_executable(lcm_publisher "lcm_publisher.cpp")
      lcm_target_link_libraries(lcm_publisher example_messages-cpp ${LCM_NAMESPACE}lcm)
      

    以上文件中头文件exlcm/example_t.hpp是缺失的,这时我们在目录下执行lcm-gen -x example_t.lcm,则可以看到当前目录下生成了exlcm文件夹,下面自动生成了example_t.hpp

    image-20240712225454876

    example_t.hpp的内容如下:

    /** THIS IS AN AUTOMATICALLY GENERATED FILE.  DO NOT MODIFY
     * BY HAND!!
     *
     * Generated by lcm-gen
     **/
    
    #ifndef __exlcm_example_t_hpp__
    #define __exlcm_example_t_hpp__
    
    #include <lcm/lcm_coretypes.h>
    
    #include <vector>
    #include <string>
    
    namespace exlcm
    {
    
    class example_t
    {
        public:
            int64_t    timestamp;
    
            double     position[3];
    
            double     orientation[4];
    
            int32_t    num_ranges;
    
            std::vector< int16_t > ranges;
    
            std::string name;
    
            int8_t     enabled;
    
        public:
            /**
             * Encode a message into binary form.
             *
             * @param buf The output buffer.
             * @param offset Encoding starts at thie byte offset into @p buf.
             * @param maxlen Maximum number of bytes to write.  This should generally be
             *  equal to getEncodedSize().
             * @return The number of bytes encoded, or <0 on error.
             */
            inline int encode(void *buf, int offset, int maxlen) const;
    
            /**
             * Check how many bytes are required to encode this message.
             */
            inline int getEncodedSize() const;
    
            /**
             * Decode a message from binary form into this instance.
             *
             * @param buf The buffer containing the encoded message.
             * @param offset The byte offset into @p buf where the encoded message starts.
             * @param maxlen The maximum number of bytes to read while decoding.
             * @return The number of bytes decoded, or <0 if an error occured.
             */
            inline int decode(const void *buf, int offset, int maxlen);
    
            /**
             * Retrieve the 64-bit fingerprint identifying the structure of the message.
             * Note that the fingerprint is the same for all instances of the same
             * message type, and is a fingerprint on the message type definition, not on
             * the message contents.
             */
            inline static int64_t getHash();
    
            /**
             * Returns "example_t"
             */
            inline static const char* getTypeName();
    
            // LCM support functions. Users should not call these
            inline int _encodeNoHash(void *buf, int offset, int maxlen) const;
            inline int _getEncodedSizeNoHash() const;
            inline int _decodeNoHash(const void *buf, int offset, int maxlen);
            inline static uint64_t _computeHash(const __lcm_hash_ptr *p);
    };
    
    int example_t::encode(void *buf, int offset, int maxlen) const
    {
        int pos = 0, tlen;
        int64_t hash = getHash();
    
        tlen = __int64_t_encode_array(buf, offset + pos, maxlen - pos, &hash, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = this->_encodeNoHash(buf, offset + pos, maxlen - pos);
        if (tlen < 0) return tlen; else pos += tlen;
    
        return pos;
    }
    
    int example_t::decode(const void *buf, int offset, int maxlen)
    {
        int pos = 0, thislen;
    
        int64_t msg_hash;
        thislen = __int64_t_decode_array(buf, offset + pos, maxlen - pos, &msg_hash, 1);
        if (thislen < 0) return thislen; else pos += thislen;
        if (msg_hash != getHash()) return -1;
    
        thislen = this->_decodeNoHash(buf, offset + pos, maxlen - pos);
        if (thislen < 0) return thislen; else pos += thislen;
    
        return pos;
    }
    
    int example_t::getEncodedSize() const
    {
        return 8 + _getEncodedSizeNoHash();
    }
    
    int64_t example_t::getHash()
    {
        static int64_t hash = static_cast<int64_t>(_computeHash(NULL));
        return hash;
    }
    
    const char* example_t::getTypeName()
    {
        return "example_t";
    }
    
    int example_t::_encodeNoHash(void *buf, int offset, int maxlen) const
    {
        int pos = 0, tlen;
    
        tlen = __int64_t_encode_array(buf, offset + pos, maxlen - pos, &this->timestamp, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __double_encode_array(buf, offset + pos, maxlen - pos, &this->position[0], 3);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __double_encode_array(buf, offset + pos, maxlen - pos, &this->orientation[0], 4);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __int32_t_encode_array(buf, offset + pos, maxlen - pos, &this->num_ranges, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        if(this->num_ranges > 0) {
            tlen = __int16_t_encode_array(buf, offset + pos, maxlen - pos, &this->ranges[0], this->num_ranges);
            if(tlen < 0) return tlen; else pos += tlen;
        }
    
        char* name_cstr = const_cast<char*>(this->name.c_str());
        tlen = __string_encode_array(
            buf, offset + pos, maxlen - pos, &name_cstr, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __boolean_encode_array(buf, offset + pos, maxlen - pos, &this->enabled, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        return pos;
    }
    
    int example_t::_decodeNoHash(const void *buf, int offset, int maxlen)
    {
        int pos = 0, tlen;
    
        tlen = __int64_t_decode_array(buf, offset + pos, maxlen - pos, &this->timestamp, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __double_decode_array(buf, offset + pos, maxlen - pos, &this->position[0], 3);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __double_decode_array(buf, offset + pos, maxlen - pos, &this->orientation[0], 4);
        if(tlen < 0) return tlen; else pos += tlen;
    
        tlen = __int32_t_decode_array(buf, offset + pos, maxlen - pos, &this->num_ranges, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        if(this->num_ranges) {
            this->ranges.resize(this->num_ranges);
            tlen = __int16_t_decode_array(buf, offset + pos, maxlen - pos, &this->ranges[0], this->num_ranges);
            if(tlen < 0) return tlen; else pos += tlen;
        }
    
        int32_t __name_len__;
        tlen = __int32_t_decode_array(
            buf, offset + pos, maxlen - pos, &__name_len__, 1);
        if(tlen < 0) return tlen; else pos += tlen;
        if(__name_len__ > maxlen - pos) return -1;
        this->name.assign(
            static_cast<const char*>(buf) + offset + pos, __name_len__ - 1);
        pos += __name_len__;
    
        tlen = __boolean_decode_array(buf, offset + pos, maxlen - pos, &this->enabled, 1);
        if(tlen < 0) return tlen; else pos += tlen;
    
        return pos;
    }
    
    int example_t::_getEncodedSizeNoHash() const
    {
        int enc_size = 0;
        enc_size += __int64_t_encoded_array_size(NULL, 1);
        enc_size += __double_encoded_array_size(NULL, 3);
        enc_size += __double_encoded_array_size(NULL, 4);
        enc_size += __int32_t_encoded_array_size(NULL, 1);
        enc_size += __int16_t_encoded_array_size(NULL, this->num_ranges);
        enc_size += this->name.size() + 4 + 1;
        enc_size += __boolean_encoded_array_size(NULL, 1);
        return enc_size;
    }
    
    uint64_t example_t::_computeHash(const __lcm_hash_ptr *)
    {
        uint64_t hash = 0x1baa9e29b0fbaa8bLL;
        return (hash<<1) + ((hash>>63)&1);
    }
    
    }
    
    #endif
    

测试

创建build文件夹,进入,执行以下命令:
cd build

cmake ..

make

可以看到当前目录下生成了对应的可执行文件:

image-20240712225838113

image-20240712230131481

运行lcm_receiver

重新开一个终端运行lcm_publisher,可以看到:

image-20240712230045459

通信成功。

小结

本文主要介绍了机器人中LCM通讯的入门使用。感兴趣的朋友可以去官网进一步学习。

实际上,LCM和ROS的话题通讯方式在使用上十分类似,不过,正如开头所说,LCM的面向对象是要求高带宽、低延迟的实时系统,如机械臂的控制系统;与之相比ROS的通信机制更复杂,通信延时会更高一点,在这一点上LCM比ROS1的表现更好。

但是ROS有更强大的生态社区,在做一些复杂的功能时,ROS有更丰富的工具帮助快速搭建系统,如rvizrqt等。因此二者也是各有优劣。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1922860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Elasticsearch]ES近似实时搜索的原因|ES非实时搜索的原因|ES Near real-time search

Elasticsearch-专栏&#x1f448;️ 往期回顾&#xff1a; ES单一查询定义&#x1f448;️ ES深分页问题&#x1f448;️ ES商城搜索实战&#x1f448;️ ES环境搭建:单节点模式/集群模式&#x1f448;️ ES开启认证&#x1f448;️ 近似实时搜索&#xff08;Near real-t…

稀疏辅助信号平滑方法在一维信号降噪和旋转机械故障诊断中的应用(MATLAB)

基于形态成分分析理论&#xff08;MCA&#xff09;的稀疏辅助信号分解方法是由信号的形态多样性来分解信号中添加性的混合信号成分&#xff0c;它最早被应用在图像处理领域&#xff0c;后来被引入到一维信号的处理中。在基于MCA稀疏辅助的信号分析模型中&#xff0c;总变差方法…

自定义“请求头”

请求头是HTTP请求的重要部分&#xff0c;可以用“头部字典”按需定制请求头。 (笔记模板由python脚本于2024年07月12日 19:28:44创建&#xff0c;本篇笔记适合喜欢钻研web知识点的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free…

JavaDS —— 栈 Stack 和 队列 Queue

栈的概念 栈是一种先进后出的线性表&#xff0c;只允许在固定的一端进行插入和删除操作。 进行插入和删除操作的一端被称为栈顶&#xff0c;另一端被称为栈底 栈的插入操作叫做进栈/压栈/入栈 栈的删除操作叫做出栈 现实生活中栈的例子&#xff1a; 栈的模拟实现 下面是Jav…

mysql和oracle数据库的备份和迁移

本文主要讲述了mysql数据库的备份和迁移&#xff1b;oracle的肯定是备份和数据迁移&#xff0c;同时介绍了linux的定时任务。 一、mysql Docker容器下mysql备份方法 1、数据库备份 # 进入容器docker exec -it 《containerId容器id》 /bin/bash# mysqldump命令备份并生成数据…

02MFC画笔/画刷/画椭圆/圆/(延时)文字

文章目录 画实心矩形自定义画布设计及使用连续画线及自定义定义变量扇形画椭圆/圆输出颜色文本定时器与定时事件 画实心矩形 自定义画布设计及使用 连续画线及自定义定义变量 扇形 画椭圆/圆 输出颜色文本 定时器与定时事件

近期matlab学习笔记,学习是一个记录,反复的过程

近期matlab学习笔记&#xff0c;学习是一个记录&#xff0c;反复的过程 matlab的mlx文件在运行的时候&#xff0c;不需要在文件夹路径下&#xff0c;也能运行&#xff0c;但是需要调用子函数时&#xff0c;就需要在文件所在路径下运行 那就先运行子函数&#xff0c;把路径换过来…

【STM32CubeMX安装教程】

【STM32CubeMX安装教程】 1. 前言2. 下载软件3. 安装配置4. 测试5. 小结 1. 前言 STM32CubeMX是一款图形化工具&#xff0c;可以非常轻松地配置STM32微控制器和微处理器&#xff0c;以及为Arm Cortex-M内核生成相应的初始化C代码&#xff0c;或为Arm Cortex-A内核生成部分Linu…

电子签章 签到 互动 打卡 创意印章 支持小程序 H5 App

电子签章 签到 互动 打卡 创意印章 支持小程序 H5 App 定制化

Mybatis-plus 集成 PostgreSQL 数据库自增序列问题记录

1.创建序列并绑定id CREATE SEQUENCE biz_factory_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;"id" int4 NOT NULL DEFAULT nextval(sys_user_seq::regclass), 2.实体设置KeySequence和TableId注解 注意IdType.INPUT 和 KeySequence(value …

docker inspect 如何提取容器的ip和端口 网络信息?

目录 通过原生Linux命令过滤找到IP 通过jq工具找到IP 使用docker -f 的过滤&#xff08;模板&#xff09; 查找端口映射信息 查看容器内部细节 docker inspect 容器ID或容器名 通过原生Linux命令过滤找到IP 通过jq工具找到IP jq 是一个轻量级且灵活的命令行工具&#xf…

通过FallbackFactory接口实现降级逻辑

触发限流或熔断后的请求不一定要直接报错&#xff0c;也可以返回一些默认数据或者友好提示&#xff0c;用户体验会更好。 给FeignClient编写失败后的降级逻辑有两种方式&#xff1a; 方式一&#xff1a;FallbackClass&#xff0c;无法对远程调用的异常做处理方式二&#xff1a…

GenAl如何改变 DevOps 中的软件测试?

TestComplete 是一款自动化UI测试工具&#xff0c;这款工具目前在全球范围内被广泛应用于进行桌面、移动和Web应用的自动化测试。 TestComplete 集成了一种精心设计的自动化引擎&#xff0c;可以自动记录和回放用户的操作&#xff0c;方便用户进行UI&#xff08;用户界面&…

读人工智能全传11人工智能会出什么错

1. 人工智能会出什么错 1.1. 一些报道是公正合理的&#xff0c;不过坦白地说&#xff0c;大部分报道都愚蠢得无可救药 1.2. 一些报道颇有知识性和引导性&#xff0c;而大部分则是杞人忧天式的恐吓 1.3. 滑稽的报道迎合了大众对人工智能的“终结者式恐惧” 1.3.1. 我们创造出…

python库(13):Tablib库简化数据处理

1 Tablib简介 数据处理是一个常见且重要的任务。无论是数据科学、机器学习&#xff0c;还是日常数据分析&#xff0c;都需要处理和管理大量的数据。然而&#xff0c;标准库中的工具有时显得不够直观和简便。这时&#xff0c;我们可以借助第三方库来简化数据处理流程。Tablib 就…

一.3.(1)结型MOSFET场效应管的结构和工作原理;

N沟道结型MOSFET的结构 看完上图&#xff0c;会发现其实本质上二极管&#xff0c;三极管&#xff0c;MOS管都是PN结&#xff0c;如果第一章一.1.&#xff08;1&#xff09;半导体基础知识_木头的最外层电子是多少个-CSDN博客 理解消化以后&#xff0c;发现底层原理都一样。 例如…

Linux进程编程(使用fork函数创建进程以及fork函数的实际引用场景)

目录 一、进程以及相关概念和指令 1.什么是进程、什么是程序、有什么区别&#xff1f; 2.如何查看系统中有哪些进程&#xff1f; 3.什么是进程标识符&#xff1f; 4.什么叫父进程&#xff0c;什么叫子进程&#xff1f; 二、进程的创建 三、fork函数的实际应用场景 总结…

[每周一更]-(第105期):SSL证书过期后引发的DNS缓存问题

问题回顾&#xff1a; ​ 上班路上收到ZeroSSL邮件通知我们清点项目的SSL证书到期了&#xff0c;到公司还是登录网址查看信息&#xff0c;一看果然是7.10也就是今天到期&#xff0c;开始看下acme.sh的定制任务为何没生效&#xff0c;一看crontab脚本&#xff0c;日志任务丢垃圾…

Java高频面试基础知识点整理13

干货分享&#xff0c;感谢您的阅读&#xff01;背景​​​​​​高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09; 最全文章见&#xff1a;Java高频面试基础知识点整理 &#xff08;一&#xff09;Java基础高频知识考点 针对人员&#xff1a; 1.全部人员都…

CSS实现table表格:隔行换色的效果

表格是网页中十分重要的组成元素。表格用来存储数据&#xff0c;包含标题、表头、行和单元格。在HTML语言中&#xff0c;表格标记使用符号<table>表示。定义表格光使用<table>是不够的&#xff0c;还需要定义表格中的行、列、标题等内容。推荐博文&#xff1a;《HT…