收藏 分享(赏)

《点云库PCL学习教程》第3章 PCL基础.pptx

上传人:HR专家 文档编号:5088406 上传时间:2019-02-07 格式:PPTX 页数:104 大小:351.56KB
下载 相关 举报
《点云库PCL学习教程》第3章 PCL基础.pptx_第1页
第1页 / 共104页
《点云库PCL学习教程》第3章 PCL基础.pptx_第2页
第2页 / 共104页
《点云库PCL学习教程》第3章 PCL基础.pptx_第3页
第3页 / 共104页
《点云库PCL学习教程》第3章 PCL基础.pptx_第4页
第4页 / 共104页
《点云库PCL学习教程》第3章 PCL基础.pptx_第5页
第5页 / 共104页
点击查看更多>>
资源描述

1、第3章 PCL基础,本章首先简述了PCL C+编程规范,为以后章节的阅读和读者编写出PCL风格的代码做一定铺垫,为了让用户基于PCL开发出自己的扩展,紧接着通过实例详细介绍了在PCL框架下如何建立用户自定义类,最后介绍了PCL中点的已有类型以及如何自定义点类型以满足扩展需求。掌握第3章的内容之后,读者可轻松阅读PCL源码和后续章节中的例子程序,也为读者后续自行开发奠定了基础。,本章各小节目录,3.1 PCL C+编程规范 3.2 如何编写新的PCL类 3.3 PCL已有点类型介绍和增加自定义的点类型 3.4 PCL中异常处理机制,3.1 PCL C+编程规范,架构师为了确保在PCL中所有代码风

2、格的一致性,使得其他开发者及用户容易理解源码,PCL开发者制定并遵循着一套严格的编写规范,PCL的开发者都默认此规范,除非有充足的理由才可以不遵循这些规范。当然这些规范也并不是一成不变的,但提出和更改规则的人需要考虑兼容性,那就是让新加的规则与现有的所有代码相适应。,3.1.1 PCL推荐的命名规范1. 文件命名 (1)所有的文件名单词之间应该用下划线隔开,例如unordered_map.hpp。 (2)头文件的扩展名为.h。 (3)模板类实现文件的扩展名是.hpp。 (4)源文件的扩展名是.cpp。 2. 目录命名 所有的目录及其子目录命名应该符合,如果由多个单词组成,其之间用下划线隔开,P

3、CL中各个目录遵循以下规则: (1)头文件都应放在源码目录树中的include/下。 (2)模板类实现文件都应放在目录树中的include/,impl/下。 (3)源文件都应放在目录树中的src/下。 3. Include语句 当文件在同一目录下时Include指示语句用双引号,在其他情况下则用尖括号,例如: #include #include #include “file_name.cpp” /在同一目录下4. 宏定义命名 宏定义中字母都采用大写格式,为头文件所定义的宏最后面还需要加上下划线,并且名称从include下目录开始,例如pcl/filters/bilateral.h对应PCL_F

4、ILTERS_BILATERL_H_。#ifndef和#define定义放在,BSD协议后面代码前面。#endif定义一直在文件结尾,并且加上一句注释掉的宏对应头文件的宏定义,例如: /the BSD license #ifndef PCL_MODULE_NAME_IMPL_FILE_NAME_HPP_/为避免重复包含头文件而定义的宏 #define PCL_MODULE_NAME_IMPL_FILE_NAME_HPP_ /the code #endif /PCL_MODULE_NAME_IMPL_FILE_NAME_HPP_ 5. 命名空间命名 命名空间多于一个单词的,单词之间应该用下划线连

5、接,例如: namespace pcl_io ,6. 类/结构命名 类名(和其他自定义类型的名称)应该是CamelCased(驼峰命名)命名规范,也就是连写单词组成命名,每个单词首字母大写。但是有例外:如果类名包含一个缩写,这个缩写应该全部大写,类名和结构名最好是名词组成的名字,例如PFHEstimation代替了EstimatePFH,下面是正确的命名代码例子: class ExampleClass; class PFHEstimation; 7. 函数/成员函数命名 函数和类的成员函数的命名应该采用camelCased,也就是连写单词组成命名,除了首个单词首字母小写其他单词首字母大写,它们

6、的参数命名单词之间用下划线隔开,函数和类的成员函数命名最好采用动词,应该,确保这些名字能清楚的表达函数和类成员函数的功能,例如,checkForErrors()而不是errorCheck(),dumpDataToFile()而不是dataFiledump(),正确的用法: int applyExample(int example_arg); 8. 变量命名 变量的命名应该单词之间用下划线隔开例如: int my_variable; (1)迭代子变量命名。迭代子变量应该反应出它们迭代的对象,例如: std:list pid_list; std:list:iterator pid_it; /指示迭

7、代的对象为点的索引 (2)常量命名。常量的名字应该是全大写,例如: const static int MY_CONSTANT = 1000;,(3)成员变量命名。类的成员变量命名单词之间用下划线隔开并且以下划线结尾,例如: int example_int_; /对阅读PCL源码很有帮助,可明显区分成员变量与局部变量 9. Return语句 return语句需要在圆括号中设返回值,即规定return语句必须有返回值,大家知道,return如果没有返回值也会编译,例如: int main() return (0); ,3.1.2 PCL推荐的缩进与格式在PCL中每个代码块的标准缩进是两个空格,在任

8、何情况下可以用制表符或者其他空格间隔的方式进行格式化代码,PCL利用多样化的GNU类型的格式。 命名空间缩进格式 在头文件里,命名空间的内容应该缩进两个空格,例如: namespace pcl class Foo; ,在一个实现文件里,对每一个类成员函数或函数的命名必须添加命名空间限定,例如: void pcl:Foo:bar() 2. 类格式 一个模板类的模板参数必须与类定义在不同行,例如: template class Foo 3. 函数/类成员函数格式,每一个函数的返回类型声明必须与函数声明放在不同的行,例如: void bar(); 在函数实现的时候也一样,返回类型声明必须与函数声明放

9、在不同的行,例如: void bar() 或者: void Foo:bar() ,或者: templatevoid Foo:bar() 4. 花括号 花括号成对出现,与上一句代码另起一行定义,必须闭合才组成合理的程序块,例如: if(ab) else ,下面的情况花括号可以省略,例如: if(ab)x=2*a; 5. 空格格式 让我们再来强调一次,在PCL中的每一个代码块的标准缩进是两个空格,这里用单个空格来隔开函数/类成员函数名字与其参数列表,例如: int exampleMethod(int example_arg); 如果在头文件内嵌套应用了命名空间名,需要将其缩进两个空格,例如: na

10、mespace foo namespace bar, void method(int my_var); 类和结构成员采用两个空格进行缩进,访问权限限定(public, private and protected)与类成员一级,而在其限定下的成员则需要缩进两个空格。例如: namepsace foo class Bar int i; public: void,baz(); 6. 自动格式化代码 PCL提供下面一套规则文件通过多种不同的集成开发环境、编辑器等可以自动格式化编码。 (1)Emacs,可以利用PCL C/C+配置文件(http:/dev.pointclouds.org/attachme

11、nts/download/748/pcl-c-style.el),下载并存储此文件,再按如下操作进行:,3.1.3 设计结构1. 类和应用程序接口 对于PCL的大多数类而言,调用接口(所有public成员)是不含公开成员变量而只有采用两种成员方法(不排除有部分类公开成员): (1)固定的类型,它允许通过get/set修改或添加参数以及输入数据。 (2)实际实现功能的函数,例如运算、滤波、分割、配准等处理功能。 2. 参数传递 get/set类型的方式遵循下面的规则: (1)如果大量的数据需要传送(常见的例子是在PCL中,输入数据)优先采用boost共享指针,而不是传送实际的数据。 (2)成对的

12、get与set类型成员函数总是需要采用一致的数据类型。 (3)对于get类型成员函数而言,如果只有一个参数需要被传递则会通过返回值,如果是两个或两个以上的参数需要传递,则通过引用方式进行传递。 对于运算、滤波、分割等类型的参数遵循以下规则: (1)无论传递数据的大小,返回参数最好是非指针型参数。 (2)总是通过引用方式来传递输出参数。,3.2 如何编写新的PCL类,把代码转换成符合PCL思路和句法的代码,对于第一次接触该基础架构的人会显得比较困难,会提出若干疑问。本小节介绍如何编写新的PCL类以及经常碰到的问题,也解释了在PCL目录树下与全球PCL用户共享你的代码有哪些优势,这里提倡共享,读者

13、也可以把这种观念应用于其他类似的项目中,无论读者是自己直接写还是改写已有代码,本节的内容都很有帮助,最重要的是可以帮助读者快速阅读了解PCL中的源码。,3.2.1 优势:为什么加入PCL开源开发模式开发者不可能事先考虑他们编写的代码片段可能所有的用途,但也奠定了一定基础 由于有限的资源和时间,发现解决方案以及应用程序中所有可能出现的漏洞是困难的,由于资源的限制,可能开始的时候进行处理也是不合适的。 除了上面介绍到的,用户的贡献可能会有以下众多的作用: (1)别人以用户的代码为基础建立新的项目。 (2)学习其他人新的用法。 (3)无忧无虑的维护者身份。 (4)在社区名声会提高。,为了举例说明代码

14、转换过程,我们选择下面的例子实现对给定点云中的强度数据应用双边滤波器,把结果保存到磁盘。 首先,在本书提供光盘的第3章例1文件夹中,打开名为mainBilateralFilter.cpp的代码文件,这里打开的源代码段包括以下步骤: (1)输入/输出代码块:从磁盘读数据,向磁盘写数据; (2)初始化代码块:用kd树建立一种搜索最近邻的方法; (3)实际算法代码块:对每个点进行双边滤波处理。 我们的目的是把给出的源码程序转换成PCL的类,以便能够在其他地方重复使用。,3.2.2 建立文件结构有两种不同的方法来建立文件结构:分别编写代码,作为独立的PCL类在PCL代码树之外;直接把文件建立的PCL代

15、码目录树中,我们来阐述后者的操作方式,因为后者是最终结果有利于PCL库发展壮大,也是因为它有一点复杂(也就是,它包含几个附加的步骤)。对于前者,可以同样操作,只是不需要在PCL代码目录树中建立对应的文件组织形式,也不需要了解CMake的使用。 假设我们想要新的算法成为PCL滤波库的一部分,我们开始先在代码树目录filters下新建3个不同的文件: (1)include/pcl/filters/bilateral.h包含所有的定义和声明;,(2)include/pcl/filters/impl/bilateral.hpp包含模板类的具体实现; (3)src/bilateral.cpp包含具体的不

16、同点类型的模板类实例化。 我们需要给新的类命名,把它称做BilateralFilter,PCL滤波器接口规定每个算法必须有两个声明和实现可供使用:一个操作PointCloud,一个操作PointCloud2。本小节只讲前者操作PointCloud的实现。 1. bilateral.h 前面提到过,bilateral.h头文件包含所有和BilateralFilter类相关的声明,下面是最小的框架: #ifndef PCL_FILTERS_BILATERAL_H_ #define PCL_FILTERS_BILATERAL_H_,#include namespace pcl template cl

17、ass BilateralFilter:public Filter/Filter类; #endif /PCL_FILTERS_BILATERAL_H_ 2. bilateral.hpp 新建bilateral.hpp和bilateral.cpp两个文件,首先是bilateral.hpp: #ifndef PCL_FILTERS_BILATERAL_IMPL_H_#define PCL_FILTERS_BILATERAL_IMPL_H_#include #endif /PCL_FILTERS_BILATERAL_H_,这个应该是明确的,还没有给BilateralFilter声明任何方法,因此它没

18、有任何具体实现。 3. bilateral.cpp 再编写bilateral.cpp文件: #include #include 因为在PCL(1.X)中编写模板代码,这里模板参数是点的类型,我们要显式地在bilateral.cpp中说明所有点类型对应模板类的定义,因此用户在使用我们定义的BilateralFilter编译代码的时候不必花费额外的编译时间。为此,我们需要添加include模板类实例化头文件(bilateral.hpp)。 4. CMakeLists.txt 把所有新建的文件增加到PCL滤波器的CMakeLists.txt(在,PCL源码根目录filters下)文件中,就可以开始编

19、译链接过程,对于CMakeLists.txt的更改就完成了,剩下就是下面对类相关文件内容的填充了。 #Find “set(srcs”, and add a new entry there, e.g., set(srcs src/conditional_removal.cpp#. src/bilateral.cpp /为该类而添加的语句 ) #Find “set (incs”, and add a new entry there, e.g., set(incs include pcl/$SUBSYS_NAME/conditional_removal.h#. include pcl/$SUBSYS

20、_NAME/bilateral.h /为该类而添加的语句),将编写的类添加进源码,#Find “set(impl_incs”, and add a new entry there, e.g., set(impl_incsinclude/pcl/$SUBSYS_NAME/impl/conditional_removal.hpp#.include/pcl/$SUBSYS_NAME/impl/bilateral.hpp/为该类而添加的语句 ),3.2.3 填写类的内容如果用户正确无误地编辑了以上所有文件,在适当的地方使用新的滤波器类重编译PCL源码应该没有问题。这一小节中开始填充每一个文件中的代码,

21、我们从bilateral.cpp文件开始,因为它的内容是最短的。 1. bilateral.cpp 如前所述,我们准备为BilateralFilter类实例化并预编译实例化若干模板。尽管这可能增加PCL滤波库的编译时间,但是在用户编写的代码中使用该类的时候,能够使编译模板速度提高。为此最简单的做法像下面的在bilateral.cpp文件中手工添加模板实例化代码,每行对应一个需要手工预编译的模板实例: #include #include #include,template class PCL_EXPORTS pcl:BilateralFilter; template class PCL_EXPO

22、RTS pcl:BilateralFilter; template class PCL_EXPORTS pcl:BilateralFilter; / 然而,随着PCL支持的点的类型增加,上面一一对应的定义方式会使文件迅速增大,随后在维护PCL更新时变的更麻烦。因此,我们准备使用一个特殊的叫做PCL_INSTANTIATE的宏,并对以上代码做如下改变: #include #include #include #include PCL_INSTANTIATE(BilateralFilter, PCL_XYZ_POINT_TYPES); 这个例子可以对所有在point_types.h文件中定义的XYZ

23、点类型对应的BilateralFilter模板类进行实例化,继而在,编译类的时候就省去了PCL用户使用时的编译时间。 仔细看例子中的代码,我们注意到像cloud-pointspoint_id.intensity这样的引用表明滤波器需要在点类型中有强度成员,因此,使用PCL_XYZ_POINT_TYPES就不起作用,因为不是所有声明了的类型都有强度数据。实际上,很容易注意到只有两个类型包含强度成员,也就是PointXYZI和PointXYZINormal,因此替换掉PCL_XYZ_POINT_TYPES,最后bilateral.cpp文件见本章源码文件1.0文件夹下的该文件。 注意:现在还没有为

24、BilateralFilter声明PCL_INSTANTIATE模板类,实际上也没有实现抽象类pcl:Filter中的纯虚函数,因此如果试图编译代码将产生如下错误: filters/src/bilateral.cpp:6:32:error:expected constructor, destructor, or type conversion before C token,2. bilateral.h 开始填充BilateralFilter类的内容,首先声明构造函数和它的成员变量。因为该双边滤波器算法有两个参数,存储起来作为类的成员,并按照PCL 1.X的API范例来为它们实现各种设值函数(s

25、etters)和取值函数(getters),代码如下: namespace pcl template class BilateralFilter:public Filter public: BilateralFilter():sigma_s_(0), sigma_r_(std:numeric_limits:max() ,void setSigmaS(const double sigma_s) sigma_s_ =sigma_s; double getSigmaS() return (sigma_s_); void setSigmaR(const double sigma_r) sigma_r_

26、=sigma_r; double getSigmaR() return (sigma_r_); ,private: double sigma_s_; double sigma_r_; #endif /PCL_FILTERS_BILATERAL_H_ 都现在为止都是最普通的C+代码,除了构造函数BilateralFilter(),这里给了两个参数默认值。因为我们的类是从pcl:Filter继承的,而pcl:Filter是从pcl:PCLBase继承的,可以用setInputCloud()方法给算法传递输入数据(作为input_存储),因此下面增加了一个using声明: template clas

27、s BilateralFilter:public Filter ,using Filter:input_; public:BilateralFilter():sigma_s_(0), 使用using声明确保我们的类能够不需输入完整引用方式就可以访问成员其父类的成员变量input_,下面我们定义applyFilter函数,由于其是纯虚函数,所有从pcl:Filter继承的类都必须实现该方法成员,因此有如下声明: using Filter:input_; typedef typename Filter:PointCloud PointCloud; public:BilateralFilter():

28、sigma_s_(0), sigma_r_(std:numeric_limits:max(),void applyFilter(PointCloud ,此外,我们注意到例子中构造了一个Kd树对象用于获取给定点的近邻,因此我们增加下面的内容: #include using Filter:input_; typedef typename Filter:PointCloud PointCloud; typedef typename pcl:KdTree:Ptr KdTreePtr; public: void setSearchMethod(const KdTreePtr private: ,KdTr

29、eePtr tree_; 最后,增加核心计算函数G(float x, float sigma)为内联(inline)函数,以便能够加速滤波的计算,因为该方法仅仅在该算法的环境里有用,我们把它设置为私有,完整的头文件见本章源码文件1.0文件夹下bilateral.h。 3. bilateral.hpp 最后需要编写的是.hpp文件,完成对声明函数的具体实现,这里需要实现两个方法,就是applyFilter和computePointWeight。 template double pcl:BilateralFilter:computePointWeight(const int pid, const std:vector &indices, const std:vector &distances) ,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 实用文档 > 简明教程

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报