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

news2024/11/17 17:29:54

上周猛男快乐开发时遇到个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.coloradmin.cn/o/1521926.html

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

相关文章

C++:类之六脉神剑——默认成员函数

个人主页&#xff1a;日刷百题 系列专栏&#xff1a;〖C/C小游戏〗〖Linux〗〖数据结构〗 〖C语言〗 &#x1f30e;欢迎各位→点赞&#x1f44d;收藏⭐️留言&#x1f4dd; ​ ​ 一、默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为 空类 。 空类中真的什么都…

linux最佳入门(笔记)

1、内核的主要功能 2、常用命令 3、通配符&#xff1a;这个在一些启动文件中很常见 4、输入/输出重定向 意思就是将结果输出到别的地方&#xff0c;例如&#xff1a;ls标准会输出文件&#xff0c;默认是输出到屏幕&#xff0c;但是用>dir后&#xff0c;是将结果输出到dir文…

复习 --- windows 上安装 git,使用相关命令

文章目录 很少使用windows的git工具&#xff0c;这次借助这个任务&#xff0c;记录下使用过程&#xff0c;其他的等有空在整理。 其中&#xff0c;还使用了浏览器的AI小助手&#xff0c;复习了git相关的命令&#xff1a;图片放最后

Python中字符串知识点汇总,以及map()函数的使用

1.字符串的定义 字符串&#xff1a;字符串就是一系列字符。在python中&#xff0c;用引号括起来的都是字符串&#xff0c;其中的引号可以是单引号&#xff0c;也可以是双引号。 2.使用方法修改字符串的大小写 ①将字符串的字母全部改为大写&#xff1a;upper()函数 实例&…

集合系列(四) -LinkedHashMap详解

一、摘要 在集合系列的第一章&#xff0c;咱们了解到&#xff0c;Map的实现类有HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties等等。 本文主要从数据结构和算法层面&#xff0c;探讨LinkedHashMap的实现。 二、简介 LinkedHashMap可…

欧科云链:ETH Dencun升级倒计时,哪些数据需要重点关注?

2024年3月13日 21:55&#xff08;epoch 269,568&#xff09;&#xff0c;以太坊将完成坎昆-德内布升级 &#xff08;Dencun 升级&#xff09;&#xff0c;OKLink 专题数据页传送门 &#x1f449; oklink.com/eth/dencun-upgrade 此次升级的主要目标是提升 Layer 2 网络的可扩展…

排序链表的三种写法

题目链接&#xff1a;https://leetcode.cn/problems/sort-list/?envTypestudy-plan-v2&envIdtop-100-liked 第一种&#xff0c;插入排序&#xff0c;会超时 class Solution {public ListNode sortList(ListNode head) {//插入排序&#xff0c;用较为简单的方式解决ListNo…

实现MySQL分页查询的三种方式~

首先我们先来查看一下表中的所有数据&#xff1a; select * from user;如下所示&#xff0c;有5条&#xff1a; 第一种方法&#xff1a; 使用LIMIT和OFFSET关键字 -- 从第1条开始取3条记录&#xff08;第一页&#xff09; SELECT * FROM user LIMIT 3 OFFSET 0; 输出如下所…

RTP 控制协议 (RTCP) 反馈用于拥塞控制

摘要 有效的 RTP 拥塞控制算法&#xff0c;需要比标准 RTP 控制协议(RTCP)发送方报告(SR)和接收方报告(RR)数据包提供的关于数据包丢失、定时和显式拥塞通知 (ECN) 标记的更细粒度的反馈。 本文档描述了 RTCP 反馈消息&#xff0c;旨在使用 RTP 对交互式实时流量启用拥塞控制…

Hack The Box-Jab

目录 信息收集 nmap enum4linux 服务信息收集 Pidgin kerbrute hashcat 反弹shell & get user 提权 系统信息收集 端口转发 漏洞利用 get root 信息收集 nmap 端口探测┌──(root㉿ru)-[~/kali/hackthebox] └─# nmap -p- 10.10.11.4 --min-rate 10000 -oA…

Linux中udp服务端,客户端的开发

UDP通信相关函数&#xff1a; ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); 函数说明&#xff1a;接收信息 参数说明&#xff1a;sockfd:套接字buf:要接收的缓冲区len:缓冲区…

UCORE 清华大学os实验 lab0 环境配置

打卡 lab 0 &#xff1a; 环境配置 &#xff1a; 首先在ubt 上的环境&#xff0c;可以用虚拟机或者直接在windows 上面配置 然后需要很多工具 如 qemu gdb cmake git 就是中间犯了错误&#xff0c;误以为下载的安装包&#xff0c;一直解压不掉&#xff0c;结果用gpt 检查 结…

基于springboot实现小区物业管理系统项目【项目源码+论文说明】

基于springboot实现小区物业管理系统演示 摘要 随着城镇人口居住的集中化加剧 &#xff0c;传统人工小区管理模式逐渐跟不上时代的潮流。这就要求我们提供一个专门的管理系统。来提高物管的工作效率、为住户提供更好的服务。 物业管理系统运用现代化的计算机管理手段,使物业的…

应用程序开发教学:医保购药系统源码搭建实战

医保购药系统作为医疗服务的重要组成部分&#xff0c;其开发不仅能够为患者提供更加便捷的购药服务&#xff0c;还能够提高医疗机构的管理效率。接下来&#xff0c;小编将为您讲解医保购药系统的源码搭建过程&#xff0c;介绍应用程序开发的基本步骤和技巧。 一、系统设计 我…

手搭手RocketMQ重试机制

环境介绍 技术栈 springbootmybatis-plusmysqlrocketmq 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 17 Spring Boot 3.1.7 dynamic-datasource 3.6.1 mybatis-plus 3.5.3.2 rocketmq 4.9.4 加入依赖 <dependencies><dependency><…

柚见十三期(优化)

前端优化 加载匹配功能与加载骨架特效 骨架屏 : vant-skeleton index.vue中 /** * 加载数据 */ const loadData async () > { let userListData; loading.value true; //心动模式 if (isMatchMode.value){ const num 10;//推荐人数 userListData await myA…

【MySQL】5. 数据类型

数据类型 1. 数据类型分类 2. 数值类型 2.1 tinyint类型 数值越界测试&#xff1a; mysql> use tt; Database changed mysql> create table t1(-> num tinyint-> ); Query OK, 0 rows affected (0.01 sec)mysql> insert into t1 values(-128); Query OK, 1 r…

【JVM】GCRoot

GC root原理 通过对枚举GCroot对象做引用可达性分析&#xff0c;即从GC root对象开始&#xff0c;向下搜索&#xff0c;形成的路径称之为 引用链。如果一个对象到GC roots对象没有任何引用&#xff0c;没有形成引用链&#xff0c;那么该对象等待GC回收。 可以作为GC Roots的对…

倒计时30,28天

1.队列Q (nowcoder.com) //1. #include<bits/stdc.h> using namespace std; #define int long long const int N2e56; const int inf0x3f3f3f3f; int dir[13]{0,31,28,31,30,31,30,31,31,30,31,30,31}; const double piacos(-1.0); int a[N],b[N]; bool cmp(int xx,int …

一学就会 | ChatGPT提示词-[简历指令库]-有爱AI实战教程(八)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、导读&#xff1a; 在使用 ChatGPT 时&#xff0c;当你给的指令越精确&#xff0c;它的回答会越到位&#xff0c;举例来说&#xff0c;假如你要请它帮忙写文案&#xf…