自己定义了一个点云结构PointXYZIL,定义格式如下
#ifndef PCL_NO_PRECOMPILE
#define PCL_NO_PRECOMPILE
#include <pcl/point_types.h>
#include <pcl/pcl_macros.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
struct EIGEN_ALIGN16 PointXYZIL // 强制SSE填充以获得正确的内存对齐
{
PCL_ADD_POINT4D; // 添加XYZ+填充类型的首选方式
PCL_ADD_INTENSITY; //添加强度
uint8_t label; //添加语义
PCL_MAKE_ALIGNED_OPERATOR_NEW; // 确保新的分配器内存是对齐的
};
POINT_CLOUD_REGISTER_POINT_STRUCT(PointXYZIL, // 注册点类型宏 XYZ + "test" (as fields)
(float, x, x)
(float, y, y)
(float, z, z)
(uint8_t, label, label)
)
#endif
然后将自定义的点云保存成pcd文件,现在把读取pcd文件,并想根据语义信息的不同给点云上色,写了下面一段程序:
#include<pcl/visualization/pcl_visualizer.h>
#include<pointxyzil.h>
#include <pcl/visualization/point_cloud_geometry_handlers.h>
#include <pcl/visualization/impl/point_cloud_geometry_handlers.hpp>
#include<iostream>
int color_map[5][3] ={{255,0,0},{0,255,0},{0,0,255},{0,255,255},{255,255,0}};
int main(){
pcl::PointCloud<pcl::PointXYZRGB>::Ptr rgb_pointcloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::PointCloud<PointXYZIL>::Ptr semantic_pointcloud (new pcl::PointCloud<PointXYZIL>());
pcl::PCDReader pcd_reader;
pcd_reader.read("/home/lho/slam_ws/src/LVI-SAM/result/semantic_map.pcd",*semantic_pointcloud);
//因为自定义点云没有对应的可视化函数,所以先将其转换成pcl中PointXYZRGB格式,然后再对其渲染着色
for(std::size_t i=0;i<semantic_pointcloud->size();i++){
pcl::PointXYZRGB point;
int label = semantic_pointcloud->points[i].label;
int index = label % 4;
int r = color_map[index][0];
int g = color_map[index][1];
int b = color_map[index][2];
point.x = semantic_pointcloud->points[i].x;
point.y = semantic_pointcloud->points[i].y;
point.z = semantic_pointcloud->points[i].z;
point.r = uint8_t(r);
point.g = uint8_t(g);
point.b = uint8_t(b);
rgb_pointcloud->push_back(std::move(point));
}
pcl::visualization::PCLVisualizer viewer;
viewer.setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb_color_handler(rgb_pointcloud);
viewer.addPointCloud<pcl::PointXYZRGB>(rgb_pointcloud,rgb_color_handler,"RGB PointCloud");//运行到这里报错
viewer.spin();
return 0;
}
但是运行到 viewer.addPointCloudpcl::PointXYZRGB(rgb_pointcloud,rgb_color_handler,“RGB PointCloud”);时,出现段错误 (核心已转储)。网上找了很久,也没有找到解决办法,所以决定自己一步步Debug。因为我是经常使用vscode,所以我就用它自带的gdb来Debug。
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++ - Build and debug active file",
"type": "cppdbg",
"request": "launch",
//需要Debug的程序的路径
"program": "${workspaceFolder}/build/pointcloud_visualiser",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
// gdb路径,
"miDebuggerPath": "/usr/bin/gdb"
//preLaunch这个参数我注释掉了,这个参数的作用是,每次Debug之前自动生成可执行程序
//参数的内容需要和task.json(编译程序的配置文件)的中label参数内容一致
//但是我自己是手动用CMakeLists.txt编译的,所以我注释掉了,意思是每次Debug就直接运行我已经生成的程序
//但是如果你源码有变动一定要重新编译一下,不然Debug运行的程序就和源码不匹配了
//CMakeLists.txt记得加set(CMAKE_BUILD_TYPE "Debug"),用Debug模式编译,不然肯能不能用gdb Debug
//"preLaunchTask": "build",
}
]
}
然后通过打断点的形式进入 viewer.addPointCloudpcl::PointXYZRGB(rgb_pointcloud,rgb_color_handler,“RGB PointCloud”);函数内部。通过层层跳转,终于进入到报错的地方,是vtk一个库函数报错。
void vtkObject::SetObjectName(const std::string& objectName)
{
vtkDebugMacro(<< vtkObjectBase::GetObjectDescription() << "set object name to '" << objectName
<< "'");
this->ObjectName = objectName;//报错的地方
}
然后在程序中打了一个Log Message断点,打印函数参数objectName,然后发现是无法访在这里插入代码片问的内存区域,所以才导致了段错误。然后我怀疑它可能就是空的,所以我决定返回上级,看看传入的参数是不是正确。
通过Debug模式的CALL STACK找到上一级的函数,发现上级函数根本没有调用这个函数,不知道怎么跳转到SetObjectName函数的。
找了好久才发现,他们不是同一个版本的库,头文件是7.1,源文件是9.2.2,我怀疑是库版本错误导致的。
所以我在CMakeLists.txt中加了下面一行,指定vtk库的版本,然后完美解决
find_package(VTK 9.2.2 EXACT REQUIRED)
自定义的语义点云,渲染上颜色效果如下图所示。
总结一下,这个是一个由于多版本第三方库导致的问题,当我们运行的程序多的时候,电脑上有时候不可避免要装多个版本的第三方库,所以在编译的时候,一定要指定好第三方库的版本,如下面所示。建议安装第三方库的是手动设置 CAMKE_INSTALL_PREFIX 参数,将第三方库安装到一个包中,这样方便管理。Linux文件管理方式和Windows是不一样的,windows是把同一个库装到一个包中,Linux是按照文件类型进行分类的,比如把不同的库的头文件都放到 /usr/include中,把库文件放到 /usr/lib中,比较杂。我一般是喜欢把第三方库都安装到一个包中,路径一般是/usr/local下。
find_package(VTK 9.2.2 EXACT REQUIRED)
或者
set(VTK_DIR /usr/local/vtk922)#指定库路径
find_package(VTK REQUIRED)