VS 2022,配置PCL 1.12.0,C#使用C++/CLI调用
总体思路:
- 安装PCL的安装包,包括依赖项;
- 配置系统的环境变量;
- 配置VS中项目的环境变量,Debug和Release都要配置;
- 然后才可以使用PCL库;
■下载PCL安装包
首先去 https://github.com/PointCloudLibrary/pcl/releases 下载点云库的安装包,我们需要的是AllInOne和pdb两个包。我下载的是1.12.0版本,如下图所示。
在这里需要根据自己机器的情况下载相应的版本(操作系统的位数,以及Visual Studio的版本)。我这里选择PCL-1.12.0-AllInOne-msvc2019-win64.exe(这是预编译的安装文件)和pcl-1.12.0-pdb-msvc2019-win64.zip(PDB文件,后期单步调试用)两个文件(这个也可以安装在VS2022上)。
下载结束之后,运行我们下载得到的PCL-1.12.0-AllInOne-msvc2019-win64.exe文件进行安装。下面几幅图显示出了几个必要的步骤,未做说明的步骤直接下一步或者根据自己的喜好配置即可。
■安装PCL
接下来点击安装便可以开始漫长的安装过程。安装主要是把对应的PCL库文件解压到指定的目录,同时安装第三方依赖项。
安装后,约2G,可以更改安装路径,放在其他盘中。
一段时间之后,会弹出来一个OpenNI2的安装窗口,它是一个依赖包,此时直接一路下一步就好,但是一定要记住自己设置的安装路径。有时没有弹出来,自动安装到C:\Program file目录中,安装的过程中会把所有的第三方库(除OpenNI2之外)全部安装在点云安装目录下的3rdparty文件夹里。因此,为了保持文件夹的整洁,便于调用,要卸载后重新安装。
首先进入E:\ PCL 1.12.0\3rdParty \OpenNI2,这里存放了安装包,双击安装包,如果让提示安装,就安装,如果已安装,出现下面的画面,选卸载,即从默认的C:\Program file里卸载。
然后重新安装,把安装目录设置成PCL的主目录的3rdparty文件夹里,即E:\PCL 1.12.0\3rdParty\OpenNI2。
安装后,OpenNI2的目录如下类似:
OpenNI2安装结束之后就完成了点云库的完整安装,安装目录下的样子和3rdparty文件夹下的样子如下图:
可以看到,所有的第三方库全部安装到了3rdparty文件夹中。
1、 Boost库,用于共享指针和线程操作,必需。
2、 Eigen库,用于矩阵、向量等数据操作,必需。
3、 FLANN库,在kd树模块中用于快速近邻搜索,必需。
4、 OpenNI版本,用于与OpenNI兼容的设备获取点云,可选。
5、 QHull库,用于凸或凹曲面的外包求解,可选。
6、 VTK(Visualization ToolKit)库,用于在可视化模块中用于3D点云渲染和可视化,必需。
解压pcl-1.12.0-pdb-msvc2019-win64.zip,将得到的pdb文件全部复制到:E:\PCL 1.12.0\bin中。
如果安装中出现下面的报警,表示字符数量超过了系统环境变量长度的要求。表示有一部分路径设置失败,就需要手动设置。
■系统环境变量配置
环境变量配置
点云库安装完成之后,还有一个比较重要的步骤,就是配置Windows的环境变量。
右键点击此电脑(我的电脑,计算机,这台电脑等名字),选择属性,接着点击左侧的高级系统设置,然后选择高级选项卡,点击下面的环境变量…按钮,此时便打开了环境变量的配置窗口。
此时,我们可以看到PCL的根目录以及OpenNI2的目录已经添加到了系统变量中。这只是一部分,还需要完整设置。
注意,由于我们在安装完毕后把OpenNI2放在3rdparty文件夹里,因此我们需要将OPENNI2_REDIST64的系统路径修改为E:\PCL 1.12.0\3rdParty\OpenNI2。改之前的路径是在C盘。
然后,我们双击系统变量中的Path,再将以下的目录添加到Path中(注意最后一个OpenNI2的路径,要和自己刚刚安装的路径一样才行),:
%PCL_ROOT%\bin
%PCL_ROOT%\3rdParty\VTK\bin
%PCL_ROOT%\3rdParty\FLANN\bin
%OPENNI2_REDIST64%
%PCL_ROOT%\3rdParty\Qhull\bin
%PCL_ROOT%\3rdParty\OpenNI2\Tools
以上步骤搞定之后,重启电脑来应用环境变量。这一步我没有做,也可以生效。
■Visual Studio开发环境配置
Visual Studio提供了两种编译模式:Debug和Release。Debug模式下,生成的应用程序中会插入许多的调试指令,因此调试起来及其方便,但是大幅度降低了程序的运行速度;Release模式便会提高程序的速度,但是调试相对较困难一些。本教程将对两种模式分别进行配置,适应不同的需求。
在VS中新建一个CLR C++类库项目,用来封装PCL库,便于C#来调用。
接下来,配置工程的属性管理器。
我们先来配置Debug模式。点击该窗口左侧的VC++ 目录,然后点击右侧的包含目录右侧的下拉菜单,选择编辑…,将以下目录填入窗口中:
E:\PCL 1.12.0\3rdParty\Boost\include\boost-1_76
E:\PCL 1.12.0\3rdParty\Eigen\eigen3
E:\PCL 1.12.0\3rdParty\FLANN\include
E:\PCL 1.12.0\3rdParty\Qhull\include
E:\PCL 1.12.0\3rdParty\VTK\include\vtk-9.0
E:\PCL 1.12.0\include\pcl-1.12
E:\PCL 1.12.0\3rdParty\OpenNI2\Include
注意:这里的E:\PCL 1.12.0是刚刚安装点云库的路径,根据刚刚安装点云的路径进行修改。同时还要注意高亮字体的版本问题。要试一下是否每个路径都可以打开,不同的PCL版本可能有差别。配置好之后点击确定即可。
然后点击右侧的库目录右侧的下拉菜单,选择编辑…,将以下目录填入窗口中:
E:\PCL 1.12.0\lib
E:\PCL 1.12.0\3rdParty\Boost\lib
E:\PCL 1.12.0\3rdParty\FLANN\lib
E:\PCL 1.12.0\3rdParty\Qhull\lib
E:\PCL 1.12.0\3rdParty\VTK\lib
E:\PCL 1.12.0\3rdParty\OpenNI2\Lib
同样需要注意路径问题,配置好之后点击确定即可。
然后点击属性页中的C/C++下的预处理器,点击右侧的预处理器定义右侧的下拉菜单,选择编辑…,添加如下两条定义:(这两条不懂什么意思,很多教程也用别的,这个可能是个坑,编译报错可能和这个有关,目前还没搞懂,学习中)
_SCL_SECURE_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
然后再点击预编译头,将右侧的预编译头设置为不使用预编译头。这个设置可以依据自己的习惯决定,可以省略。如果使用编译头,要自己比较熟悉如何操作,如果不熟悉,就不使用。
接着点击属性页中的链接器下的输入,点击右侧的附加依赖项右侧的下拉菜单,选择编辑…,将pcl和3rdParty相关lib文件填写到窗口中:(注意:Debug模式下的lib和Release里面的lib必须输入不同的lib,不能混在一起,否则程序运行,会报内存有关的各种错误,还有link链接错误之类的)
这时,点击确定,我们在Debug模式下的点云开发环境就配置完成了。去网上找个实例代码粘贴,如果成功编译运行就代表配置成功了。
接下来说一下Release模式的配置。Release模式的配置和Debug模式一模一样,除了附加依赖项。两个模式必须都要配置,否则会报莫名其妙的错误,这里说一句,NND PCL。
这样,我们的Debug模式和Release模式就全部配置完成了。
■CLR项目编程
新建一个CLR项目,新建一个头文件PclProcessor.h,内容如下:
#pragma once
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/filters/passthrough.h>
#include <pcl/sample_consensus/ransac.h>
#include <pcl/sample_consensus/sac_model_line.h>
#include <msclr/marshal_cppstd.h>
using namespace System;
using namespace System::Collections::Generic;
namespace PclBridge {
public ref class PclProcessor {
public:
// 处理点云数据并返回直线参数
List<float>^ ProcessPointCloud(List<System::Numerics::Vector2>^ points);
};
}
新建一个cpp文件,内容如下:
#include "PclProcessor.h"
#include <msclr/marshal_cppstd.h>
#include <pcl/common/common.h>
using namespace pcl;
using namespace msclr::interop;
using namespace System::Numerics;
namespace PclBridge {
// 非托管部分: 执行 PCL 库的算法(滤波、RANSAC 等)
void ProcessPointCloudNative(std::vector<Eigen::Vector2f>& points, std::vector<float>& result)
{
PointCloud<PointXYZ>::Ptr cloud(new PointCloud<PointXYZ>);
for (auto& pt : points) {
cloud->push_back(PointXYZ(pt.x(), pt.y(), 0.0f)); // 将 Vector2 转为 PointXYZ
}
// 1. 直通滤波(X轴范围切割)
pcl::PassThrough<PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("x");
pass.setFilterLimits(-5.0, 5.0); // 根据实际雷达范围调整
pass.filter(*cloud);
// 2. RANSAC 直线拟合(2D 数据需投影到 XY 平面)
pcl::SampleConsensusModelLine<PointXYZ>::Ptr model(new pcl::SampleConsensusModelLine<PointXYZ>(cloud));
pcl::RandomSampleConsensus<PointXYZ> ransac(model);
ransac.setDistanceThreshold(0.05); // 设置距离阈值(单位:米)
ransac.computeModel();
// 获取直线参数 ax + by + c = 0
Eigen::VectorXf coefficients;
ransac.getModelCoefficients(coefficients);
// 提取方向向量和一个点,计算直线方程
float dir_x = coefficients[3];
float dir_y = coefficients[4];
float point_x = coefficients[0];
float point_y = coefficients[1];
// 计算直线方程的系数 a, b, c
float a = dir_y;
float b = -dir_x;
float c = dir_x * point_y - dir_y * point_x;
result.push_back(a);
result.push_back(b);
result.push_back(c);
}
// 托管部分: 将 C# List<Vector2> 转为 C++ 的 std::vector,并调用非托管代码
List<System::Single>^ PclProcessor::ProcessPointCloud(List<System::Numerics::Vector2>^ points)
{
// 转换 C# 点云 List<Vector2> 到 std::vector<Eigen::Vector2f>
std::vector<Eigen::Vector2f> nativePoints;
for each (Vector2 pt in points) {
nativePoints.push_back(Eigen::Vector2f(pt.X, pt.Y));
}
执行非托管处理
std::vector<float> nativeResult;
ProcessPointCloudNative(nativePoints, nativeResult);
// 将结果转换回托管 List<System::Single>
List<System::Single>^ result = gcnew List<System::Single>();
for (float val : nativeResult) {
result->Add(val);
}
return result;
}
}
因为要在CLR项目中使用断点调试,需要确认符号文件(PDB)生成:
确认 PclBridge 项目的属性中已生成 PDB 文件:
代码优化关闭:
确保 PclBridge 的代码优化已禁用(避免断点失效):
如果出现无法查找ntdll.pdb 之类的报警(可能有问题):
■C#项目编程
目标平台默认为AnyCPU,可能系统自动选择了AMD64,为了避免不一致,统一选为x64.
在C#项目属性这里打勾,启用混合调试,这样就可以在C++/CLI项目中使用断点了。
public void ProcessData(List<Vector2> points)
{
// 调用 PCL 处理
List<float> coefficients = _processor.ProcessPointCloud(points);
// 解析直线参数 ax + by + c = 0
float a = coefficients[0];
float b = coefficients[1];
float c = coefficients[2];
// 计算雷达中心点到直线的距离和角度
Vector2 radarCenter = new Vector2(0, 0); // 假设雷达中心在原点
float distance = (a * radarCenter.X + b * radarCenter.Y + c) / MathF.Sqrt(a * a + b * b);
float angle = MathF.Atan2(b, a) * (180 / MathF.PI);
// 更新 UI 或日志
Console.WriteLine($"距离: {distance}, 角度: {angle}°");
}