一文搞懂PCL中自定义点云类型的构建与函数使用

上周猛男快乐开发时遇到个bug,要用pcl的函数对自定义的点云进行处理。一起解决问题时遇到了很多问题,解决后整理出来分享给各位参考,以免踩一样的坑😊。文章中自定义的点我用PointT来表示,自定义点云一般指的是pcl::PointCloud<PointXYZITNormalVelocity> cloud

(修这个bug真的烧了很多脑子🧠)

文章目录

  • 1. 构造自定义PointT类型
  • 2. 用PCL的函数处理自定义点云
  • 3. 解决PCL和OpenCV同时使用导致的"no member named 'serialize' "问题
  • 4. 小结

1. 构造自定义PointT类型

为了涵盖日常开发中可能使用的点的类型,PCL中已经定义了很多种数据类型。因此在确定要构造自定义类型时,可以先去头文件pcl/impl/point_types.hpp里查看下是否有满足自己需求的点类型。

利用下面这个模板可以完成自定义PointT类型的定义,其中EIGEN_ALIGN16PCL_MAKE_ALIGNED_OPERATOR_NEW切记要加上。PCL中大量利用SSE指令集来加速,所以内存对齐是很必要的。

struct EIGEN_ALIGN16 _PointT // EIGEN_ALIGN16表示16字节对齐,[必须项]
{// 添加自定义点内部的字段信息  PCL_ADD_RGB;// ...// 必须项,确保内存正确对其,旧版本是EIGEN_MAKE_ALIGNED_OPERATOR_NEW PCL_MAKE_ALIGNED_OPERATOR_NEW
};struct EIGEN_ALIGN16 PointT: public _PointT
{// 参考下面的方式补充构造参数,当输入参数是constexpr,那么产生的对象的所有成员都是constexpr。// inline constexpr PointT (/*输入参数列表*/): /*初始化参数列表*/ {}// 重载运算符 <<,这样就可以通过std::cout << point 来查看点的信息。friend std::ostream& operator << (std::ostream& os, const PointT& p);PCL_MAKE_ALIGNED_OPERATOR_NEW
};// 点云注册,这里的字段名影响PointCloud<PointT>等相关函数的拷贝、保存或加载功能。
POINT_CLOUD_REGISTER_POINT_STRUCT (_PointT,// 输入要注册的字段名,比如// (float, x, x)// (float, vx, vx)
)
POINT_CLOUD_REGISTER_POINT_WRAPPER(PointT, _PointT)

基于这个模板,我们来定义一个点PointXYZITNormalVelocity,其包含位置、法向、强度、速度、时间信息。

  • 第一步:定义基础类 _PointXYZITNormalVelocity。在添加字段时,参考如下规则:
    • 每组信息,满足16字节的优先构建。比如位置,法向,速度,这些数据组。其他信息比如强度,时间等放在后面定义。
    • 尽可能利用PCL提供的数据组构建方式。PCL中提供的数据组构建方式列举如下,一般16字节的数据组,都有个float [4]来填充,这个对应的字段名在定义的时候要记得防止冲突。切记:尽可能使用已有的,且不要尝试往字段的预留区域添加信息,比如位置信息PCL_ADD_POINT4D这个,pcl在做点云运算时,可能会将第四个字节也就是data[3]这里赋值为0或1来加速运算。
      • PCL_ADD_POINT4D [16字节]:定义字段float x,y,z。16字节对应的字段名为float data[4]
      • PCL_ADD_NORMAL4D [16字节]:定义字段float normal_x, normal_y, normal_z,或者可以通过字段float normal[3]来访问。16字节对应的字段名为float data_n[4]
      • PCL_ADD_RGB:定义字段uint8_t b, g, r, a,或者可以通过字段uint32_t rgba来访问。
      • PCL_ADD_INTENSITY:定义字段float intensity
      • PCL_ADD_INTENSITY_8U:定义字段uint8_t intensity
      • PCL_ADD_INTENSITY_32U:定义字段uint32_t intensity

基于上述信息,对应定义代码如下:

struct EIGEN_ALIGN16 _PointXYZITNormalVelocity
{PCL_ADD_POINT4D; // XYZ [16 bytes]PCL_ADD_NORMAL4D; // normal [16 bytes]union // Velocity [16 bytes]{float data_v[4];float velocity[3];struct{float v_x;float v_y;float v_z;};};PCL_ADD_INTENSITY;double time;
};
  • 第二步:构造最终点云类型PointXYZITNormalVelocity
    • 完善构造函数,补充几种可能用到的赋值情况。至少得定义个所有字段得赋值方式。
    • 补充运算符<<的重载。
struct EIGEN_ALIGN16 PointXYZITNormalVelocity: public _PointXYZITNormalVelocity
{// 几种可能用得到的构造函数,根据需求来定义即可inline constexpr PointXYZITNormalVelocity (const _PointXYZITNormalVelocity &p) :PointXYZITNormalVelocity {p.x, p.y, p.z, p.normal_x, p.normal_y, p.normal_z, p.v_x, p.v_y, p.v_z, p.intensity, p.time} {}inline constexpr PointXYZITNormalVelocity (float _x, float _y, float _z, float _nx, float _ny, float _nz,float _vx, float _vy, float _vz, float _intensity = 0.f, double _time = 0.0) :_PointXYZITNormalVelocity{{{_x, _y, _z, 1.0f}}, {{_nx, _ny, _nz, 0.0f}}, {{_vx, _vy, _vz}}, _intensity, _time} {}inline constexpr PointXYZITNormalVelocity (float _x, float _y, float _z):PointXYZITNormalVelocity (_x, _y, _z, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f) {}inline constexpr PointXYZITNormalVelocity (float _x, float _y, float _z,float _vx, float _vy, float _vz):PointXYZITNormalVelocity (_x, _y, _z, 0.0f, 0.0f, 0.0f, _vx, _vy, _vz) {}// 运算符重载friend std::ostream& operator << (std::ostream& os, const PointXYZITNormalVelocity& p){os << "(" << "xyz: [" << p.x << "," << p.y << "," << p.z << "], ";os << "v: [" << p.v_x << "," << p.v_y << "," << p.v_z << "], ";os << "int: " << p.intensity << ", time: " << p.time << ")";return (os);}PCL_MAKE_ALIGNED_OPERATOR_NEW
};
  • 第三步:注册点云。这步是最简单得了,把所有需要用的字段按序注册即可
POINT_CLOUD_REGISTER_POINT_STRUCT (_PointXYZITNormalVelocity,(float, x, x)(float, y, y)(float, z, z)(float, normal_x, normal_x)(float, normal_y, normal_y)(float, normal_z, normal_z)(float, v_x, v_x)(float, v_y, v_y)(float, v_z, v_z)(float, intensity, intensity)(double, time, time)
)
POINT_CLOUD_REGISTER_POINT_WRAPPER(PointXYZITNormalVelocity, _PointXYZITNormalVelocity)

2. 用PCL的函数处理自定义点云

PCL很多函数是模板函数,但我们在使用PCL库时,动态库里面封装好的是预定义的点云类型的实现。任何用户代码都不需要编译模板化代码,从而加快了编译时间。

基于上述的定义方式,我们已经得到了一个点云pcl::PointCloud<PointXYZITNormalVelocity>::Ptr cloud。如果我们想直接调用函数
pcl::CropBox<PointXYZITNormalVelocity> crop时,会出现类似undefined reference to 'pcl::PCLBase<PointXYZITNormalVelocity>::setInputCloud(std::shared_ptr<pcl::PointCloud<PointXYZITNormalVelocity> const> const&)'的错误。

我分析了下代码细节,发现我们include的头文件里面的函数尽管是模板函数,但是函数实现部分并没有提供。函数细节对应的头文件放置在impl文件夹下。在include的头文件的最后,有个关键代码,即如果你没有定义PCL_NO_PRECOMPILE的话,编译代码时是无法获取函数的实现部分的,这也就会导致编译时出现undefined reference的问题。

在这里插入图片描述

知道了原因,解决起来就非常容易了,最好的办法是在项目的CMakeLists.txt里面添加add_definitions(-DPCL_NO_PRECOMPILE)。当然,如果项目不大的话也可以pcl头文件的前面添加#define PCL_NO_PRECOMPILE

除此之外,为了提高编译速度,我们还需要在定义点云的头文件的后面,跟上所使用函数的模板类显式实例化声明。(模板隐式实例化没啥问题,但会导致每个cpp编译时,文件较大,尽管link时候会删除,但会影响编译速度)

以我们要使用的pcl::CropBox为例,假如我们定义的点云放在头文件custom_types.h中,那么我们在该文件的末尾添加目标函数的实例化声明:template class pcl::CropBox<PointXYZITNormalVelocity>;。这样在实例化一次后,其他cpp引用这个无需重复实例化。

PS:各位对显示/隐式实例化感兴趣的可以参考另一个人的博客《C++11模板隐式实例化、显式实例化声明、定义》。

3. 解决PCL和OpenCV同时使用导致的"no member named ‘serialize’ "问题

再解决自定义点云的使用之后,编译项目时又出现了error: 'class std::unordered_map<unsigned int, std::vector<unsigned int>>' has no member name 'serialize'的错误。
在这里插入图片描述

经过大量的查阅资料,才搞懂问题原因,PCL和OpenCV都基于flann这个库,这个库
opencv是连接自己的flann库,而pcl是连接的系统的pcl库。
在这里插入图片描述
在这里插入图片描述
如果想正常编译,在调用pcl时必须得让USE_UNORDERED_MAP这个值为0。由于先调用的opencv,然后调用了pcl的库,进而触发了这个bug。

解决办法也很简单,在出问题的pcl头文件前,强行定义#define USE_UNORDERED_MAP 0即可(有的人是定义为1解决的,实际使用时候可以试试)。当然最优的办法是自己调整好头文件调用方式,把opencv和pcl的调用尽可能分开,然后在cpp里调用。但这样成本比较高,我们还是等待opencv优化下这个问题吧😆。
在这里插入图片描述
此外,我研究了下__GXX_EXPERIMENTAL_CXX0X__这个宏的来源。在Common-Predefined-Macros这里找到了对这个宏的说明。就是当编译时使用了c++11的特征时,即除了-std=C++98-std=gnu++98之外,这个宏就会被定义。这个宏已经过时了,在GCC 4.7.0中定义了__cplusplus宏,因此相关的代码最好用__cplusplus>=201103L这个方式来处理。
在这里插入图片描述

4. 小结

为了解决这个bug,特意去翻了PCL官网提供的教程《adding_custom_ptype》,讲得很不错,感兴趣可以看看。关于自定义点云中一些宏函数的理解,可以看看《PCL-common-定义点类型
》。GCC相关参数的理解还是得去看官方文档GCC文档。

你们猜猜我为啥分享这么多资料→_→,因为为了解决这个bug,我几乎把全网能翻的都翻了😭。

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

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

相关文章

Linux命令行学习之操作文件和目录

目录 通配符 mkdir – 创建目录 cp – 复制文件和目录 mv – 移动和重命名文件 rm – 删除文件和目录 ln – 创建链接 硬链接 符号链接 本文介绍Linux用于操作文件和目录的命令&#xff0c;介绍的命令有&#xff1a; cp – 复制文件和目录mv – 移动/重命名文件和目录…

文件上传漏洞------一句话木马原理解析

目录 一、实验环境 二、实验过程 构造一句话木马 一句话木马的使用: 木马原理解析: 一、实验环境 小皮面板搭建:upload-labs靶场 二、实验过程 构造一句话木马 这是一个最简单的一句话木马&#xff0c;我们用GET传参接受了两个参数&#xff0c;其最终目的是构造出:ass…

【SQLite数据库】的使用

SQLite数据库 文章目录 SQLite数据库0、linux基础知识补充1、SQLite简介1.1 SQLite & MySQL 的优缺点比较1.2 常见的嵌入式数据库 2、数据库的基本命令和用法2.1 创建数据库2.2 创建一张表格2.3 插入一条数据2.4 增加一列2.5 删2.6 改2.7 查看数据库 3、数据库编程3.1 实验…

牛客网-SQL大厂面试题-1.各个视频的平均完播率

题目&#xff1a;各个视频的平均完播率 DROP TABLE IF EXISTS tb_user_video_log, tb_video_info; CREATE TABLE tb_user_video_log (id INT PRIMARY KEY AUTO_INCREMENT COMMENT 自增ID,uid INT NOT NULL COMMENT 用户ID,video_id INT NOT NULL COMMENT 视频ID,start_time dat…

小车倒立摆系统线性化,离散化处理

一、线性化 从上一篇文章小车倒立摆物理建模与simulink仿真-CSDN博客 我们推导出了倒立摆小车的运动微分方程&#xff1a; 方程里包含了正弦&#xff0c;余弦运算&#xff0c;因此这个系统是非线性的&#xff0c;不容易控制。 我们的控制目标是把倒立摆直立在小车上(角度在0附…

移动云行动:5.5G技术引领数字化转型

刚刚结束的全国两会上&#xff0c;有人大代表建议应尽快发挥5G-A&#xff08;5.5G&#xff09;优势&#xff0c;加快试点城市布局。此前&#xff0c;中国移动已宣布将在300多个城市启动5.5G商用部署。在通信技术的历史长河中&#xff0c;4G改变了我们的生活方式&#xff0c;而5…

Navicat 面试题及答案整理,最新面试题

Navicat 在数据库管理中的主要用途有哪些&#xff1f; Navicat 是一款数据库管理工具&#xff0c;其主要用途包括&#xff1a; 1、多数据库支持&#xff1a; Navicat 支持多种数据库连接&#xff0c;包括 MySQL、Oracle、PostgreSQL、SQLite、SQL Server 等&#xff0c;方便用…

基于springboot+vue实现疫情防控物资调配系统项目【项目源码】计算机毕业设计

基于springbootvue实现疫情防控物资调配系统演示 B/S结构的介绍 在确定了项目的主题和研究背景之后&#xff0c;就要确定本系统的架构了。主流的架构有两种&#xff0c;一种是B/S架构&#xff0c;一种是C/S架构。C/S的全称是Client/Server&#xff0c;Client是客户端的意思&am…

JVM虚拟机:通过jconsole远程连接解决JVM报错

本文重点 前面我们介绍过的一些工具都是使用命令行的方式来帮助我们完成&#xff0c;本文我们将使用一种图形化界面的方式来远程连接&#xff0c;然后完成关于JVM的检测任务。 jconsole jconsole是一个JVM的检测工具&#xff0c;这个工具任何安装了Java的电脑上都有的&#…

Apache Paimon系列之:主键表

Apache Paimon系列之&#xff1a;主键表 一、主键表1.Bucket2.LSM Trees3.Compaction 二、数据分布1.固定Bucket2.动态Bucket3.正常动态Bucket模式4.跨分区更新插入动态存储桶模式 三、Merge Engine1.Deduplicate2.部分更新3.序列组4.聚合部分更新5.聚合6.Retract7.First Row 四…

SwiftUI的 特性 - ViewModify

SwiftUI的 特性 - ViewModify 记录一下SwiftUI的 特性 - ViewModify的使用方式 可以通过viewModify来管理视图的样式&#xff0c;结合extension来完成封装达到解偶效果 import SwiftUI/// 我们可以通过viewModify来管理视图的样式&#xff0c;来达到解偶效果 struct DefaultB…

【C语言】【时间复杂度】Leetcode 153. 寻找旋转排序数组中的最小值

文章目录 题目时间复杂度概念时间复杂度的计算 解题思路代码呈现 题目 链接: link 时间复杂度 概念 时间复杂度是一种函数&#xff0c;定量地描述了该算法运行的时间。既然是一种函数&#xff0c;就涉及到自变量与因变量。因变量代表是时间复杂的规模&#xff0c;自变量是…

易方达产品亏损仍存,“老鼠仓”阴影犹在,如何突出重围?

近日&#xff0c;易方达基金宣布易方达沪深300 ETF跻身“千亿规模ETF”行列&#xff0c;成为国内“ETF千亿俱乐部”的第三位成员。截至3月8日&#xff0c;该基金的规模增长112.21亿元&#xff0c;涨幅9.45%&#xff0c;规模增量在10亿以上的股票型ETF产品中排名第一。 回望202…

小兔鲜鲜项目(前端vue3)

成果图 大家喜欢给一个赞被&#xff0c; 项目地址&#xff1a;gitee 注意&#xff1a;项目克隆下去之后先运行 npm i之后安装项目插件包之后在npm run dev 运行就可以了

友塔游戏测试开发笔面经验

题目一 给定任意非负整数M&#xff0c;判断其能否表达为 M 2 ^a 2 ^b(a和b为非负整数)&#xff0c;若可以输出a和b&#xff0c;若不能输出-1&#xff1b; 例如&#xff1a; 输入&#xff1a;6 输出: “1 2” 分析&#xff1a; void findAB(int M){} 为解决问题的主函数 …

Avalonia学习1:下载通用皮肤SukiUI,并在windows上启动成功

目录 1、引言 2、碰到的问题 1、下载下拉VS2022老版本的用不了。 2、升级后&#xff0c;发现没有装wsl&#xff0c;导致启动不了&#xff0c;但wsl又由于国内的关系安装不了&#xff0c;怎么办呢&#xff0c; 1、引言 最近在想有没有什么可以开发在Linux下运行…

SQLite数据库使用指南以及相关API编程

SQLite介绍 SQLite是一种基于C语言开发的轻量级、快速、自包含、高可靠性和全功能的SQL数据库引擎。它是全球范围内使用最为广泛的数据库引擎&#xff0c;被嵌入到所有移动设备和大部分计算机中&#xff0c;并且伴随着无数日常使用的应用程序一起提供。SQLite的文件格式具有稳…

渗透测试框架权限维持技术——Persistence模块

测试环境&#xff1a; kali win7 测试步骤&#xff1a; 1.利用MSF编写远控程序 msfvenom -p windows/meterpreter/reverse_tcp lhost10.0.0.163 lport55555 -f exe -o 5555.exe-p 漏洞利用payload lhost 监听地址&#xff08;kali地址&#xff09; lport 监听端口&#xf…

Pikachu 靶场搭建

文章目录 环境说明1 Pikachu 简介2 Pikachu 安装 环境说明 操作系统&#xff1a;Windows 10PHPStudy 版本: 8.1.1.3Apache 版本&#xff1a;2.4.39MySQL 版本 5.7.26 1 Pikachu 简介 Pikachu是一个使用“PHP MySQL” 开发、包含常见的Web安全漏洞、适合Web渗透测试学习人员练…

Leet code 179 最大数

解题思路 贪心算法 贪心算法就是走一步看一步 每一步都取当前位置的最优解 这题我们该如何贪呢&#xff1f; 我们先把int数组转换为string数组 以示例2为例 3 30 34 5 9 排序哪个在前哪个在后&#xff1f; 3 30 &#xff08;330&#xff09;> 30 3 &#xff08;30…