LCM通讯的使用

本文主要介绍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 lcmadd_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 theseinline 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.mzph.cn/diannao/45652.shtml

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

相关文章

Cesium--获取当前相机中心与地面的射线焦点

本文记录获取当前相机中心与地面的射线焦点的方法&#xff0c;可用于视角缩放过程中&#xff0c;控制视角自动平滑切换到二维等场景&#xff1a; 方法一定是视角中心能与地面有交集&#xff0c;如果对着地平线或对着天空肯定是没效果的。直接放代码&#xff1a; //调整相机到正…

Objective-C 自定义渐变色Slider

文章目录 一、前情概要二、具体实现 一、前情概要 系统提供UISlider&#xff0c;但在开发过程中经常需要自定义&#xff0c;本次需求内容是实现一个拥有渐变色的滑动条&#xff0c;且渐变色随着手指touch的位置不同改变区域&#xff0c;类似如下 可以使用CAGradientLayer实现渐…

利用宝塔安装一套linux开发环境

更新yum&#xff0c;并且更换阿里镜像源 删除yum文件 cd /etc/yum.repos.d/ 进入yum核心目录 ls sun.repo rm -rf * 删除之前配置的本地源 ls 配置阿里镜像源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 配置扩展包 wge…

paloalto防火墙CLI修改MGT IP

怎么样通过Cli修改MGT口的IP、掩码、网关、DNS呢&#xff1f; 1&#xff09;console连接上CLi&#xff0c;输入configure进入系统视图 输入exit&#xff0c;退出到用户视图 2&#xff09;在CLI修改带外管理MGT的IP地址、掩码、网关、DNS&#xff0c;默认带外管理是开启https、…

[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…

FlinkModule加载HiveModule异常

HiveModule这个模块加载不出来 加在不出来这个模块&#xff0c;网上查说是要加下面这个依赖 <dependency><groupId>org.apache.flink</groupId><artifactId>flink-connector-hive_${scala.binary.version}</artifactId><version>${flink.…

Elasticsearch基础概念

Elasticsearch 是一款开源的,ESTful风格的 分布式搜索、存储、分析引擎; 常见的使用场景 网站搜索,代码搜索等日志管理与分析,应用系统性能分析,安全指标监控等数据库同步,将数据库某个表的数据同步到elasticsearch上然后提供搜索服务 ES基本概念 文档 我们向elasticsearch存…

【活动预告】Apache IoTDB TsFile 智慧能源应用“上会”啦!

2024 年&#xff0c;站在中国数字经济产业升级和数据要素市场化建设的时代交汇点上&#xff0c;为进一步推动全球数据库产业进步&#xff0c;由中国通信标准化协会、大数据技术标准推进委员会主办的“2024 可信数据库发展大会”将于 2024 年 7 月 16-17 日&#xff0c;在北京朝…

跟着李沐学AI:Softmax回归

回归 vs 分类 回归用于估计一个连续值 分类用于预测一个离散类别 两个经典的数据集&#xff1a; MNIST数据集&#xff1a; MNIST&#xff08;Modified National Institute of Standards and Technology&#xff09;数据集是一个广泛应用于机器学习和深度学习领域的手写数字…

车载视频监控管理方案:无人驾驶出租车安全出行的保障

近日&#xff0c;无人驾驶出租车“萝卜快跑”在武汉开放载人测试成为热门话题。随着科技的飞速发展&#xff0c;无人驾驶技术已逐渐从概念走向现实&#xff0c;特别是在出租车行业中&#xff0c;无人驾驶出租车的推出将为公众提供更为安全、便捷、高效的出行服务。 视频监控技…

【Linux杂货铺】2.进程优先级

1.进程优先级基本概念 进程优先级是操作系统中用于确定进程调度顺序的一个指标。每个进程都会被分配一个优先级&#xff0c;优先级较高的进程会在调度时优先被执行。进程优先级的设定通常根据进程的重要性、紧急程度、资源需求等因素来确定。操作系统会根据进程的优先级来决定进…

免费开源的工业物联网(IoT)解决方案

什么是 IoT&#xff1f; 物联网 (IoT) 是指由实体设备、车辆、电器和其他实体对象组成的网络&#xff0c;这些实体对象内嵌传感器、软件和网络连接&#xff0c;可以收集和共享数据。 IoT 设备&#xff08;也称为“智能对象”&#xff09;范围广泛&#xff0c;包括智能恒温器等…

PHP企业工商年报大师微信小程序系统源码

&#x1f31f;轻松搞定年报难题&#xff01;&#x1f4bc; &#x1f680;【一键直达&#xff0c;年报不再繁琐】 还在为每年的企业工商年报而头疼吗&#xff1f;繁琐的表格、复杂的流程&#xff0c;让人望而却步&#xff1f;现在有了“企业工商年报大师”微信小程序&#xff…

Docker:基础概念、架构与网络模式详解

1.Docker的基本概念 1.1.什么是docker Docker是一个用于开发,交付和运行应用程序的开放平台.docker使您能够将应用程序域基础框架分开,以便你可以快速开发交付软件.使用docker,你可以管理你的基础架构以管理应用程序相同的方式.通过利用docker用于交付,测试和部署代码的方法,你…

解决Ubuntu 22.04 vscode搜狗拼音输入无法输入中文

关闭vscode 编辑~/.bashrc&#xff0c;添加以下内容 export GTK_IM_MODULExim export QT_IM_MODULExim export XMODIFIERSimfcitx source ~/.bashrc && code 重新加载环境变量后启动code&#xff0c;即可以正常使用搜狗拼音输入法了

MongoDB - 查询操作符:比较查询、逻辑查询、元素查询、数组查询

文章目录 1. 构造数据2. MongoDB 比较查询操作符1. $eq 等于1.1 等于指定值1.2 嵌入式文档中的字段等于某个值1.3 数组元素等于某个值1.4 数组元素等于数组值 2. $ne 不等于3. $gt 大于3.1 匹配文档字段3.2 根据嵌入式文档字段执行更新 4. $gte 大于等于5. $lt 小于6. $lte 小于…

java内部类的本质

定义在类内部&#xff0c;可以实现对外部完全隐藏&#xff0c;可以有更好的封装性&#xff0c;代码实现上也往往更为简洁。 内部类可以方便地访问外部类的私有变量&#xff0c;可以声明为private从而实现对外完全隐藏。 在Java中&#xff0c;根据定义的位置和方式不同&#xf…

什么是IOT 可编程控制系统

IOT可编程控制系统GF-MAXCC是一种基于物联网&#xff08;Internet of Things, IoT&#xff09;技术的可编程中央控制主机。它集成了多种先进的技术和功能&#xff0c;能够在物联网系统中发挥关键作用&#xff0c;实现对多种设备的集中管理和控制。 一、定义与概述 定义&#x…

虚幻引擎ue5如何调节物体锚点

当发现锚点不在物体上时&#xff0c;如何调节瞄点在物体上。 步骤1&#xff1a;按住鼠标中键拖动锚点&#xff0c;在透视图中多次调节锚点位置。 步骤2:在物体上点击鼠标右键点击-》锚定--》“设置为枢轴偏移”即可。

219.贪心算法:柠檬水找零(力扣)

代码解决 class Solution { public:bool lemonadeChange(vector<int>& bills) {int num50, num100; // 初始化5美元和10美元的计数器for(int i0; i < bills.size(); i) // 遍历所有账单{if(bills[i]5) // 如果账单是5美元{num5; // 增加5美元的计数continue; // …