0 引言
最近項目中用到了基於PCL開發的基於平面的點雲和CAD模型的配准算法,點雲平面提取采用的算法如下。
1 基於PCL的點雲平面分割擬合算法
2 參數及其意義介紹
(1)點雲下采樣
1. 參數:leafsize
2. 意義:Voxel Grid的leafsize參數,物理意義是下采樣網格的大小,直接影響處理后點雲密集程度,並對后期各種算法的處理速度產生直接影響。
3. 值越大,點雲密度越低,處理速度越快;值越小,點雲密度越高,處理速度越慢。通常保持這個值,使得其他的與點數有關的參數可以比較穩定而不作大的改動。
4. 對應的代碼:
PointCloudPtr cloud(new pointCloud); ParameterReader pd(ParameterFilePath); double leafsize = stod(pd.getData("leafsize")); pcl::VoxelGrid<PointT> sor; sor.setInputCloud(CRTP::cloud_org); sor.setLeafSize(leafsize, leafsize, leafsize); sor.filter(*cloud);
(2)點雲法線估計
1. 參數:Ksearch
2. 意義:估計法線時鄰域內點的個數
3. 值越小,對點雲的輪廓描述越精細;值越大,對點雲的輪廓描述越粗糙。
4. 對應的代碼:
ParameterReader pd(ParameterFilePath); pcl::NormalEstimation<PointT, pcl::Normal> ne; pcl::PointCloud<pcl::Normal>::Ptr mynormals(new pcl::PointCloud<pcl::Normal>); pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>); tree->setInputCloud(cloud_filter); ne.setInputCloud(cloud_filter); ne.setSearchMethod(tree); ne.setKSearch(stoi(pd.getData("Ksearch"))); ne.compute(*mynormals);
(3)RegionGrowing生長聚類算法對可能是平面的點雲進行分割
算法步驟:
1. 算法首先計算所有點的曲率值,並將曲率最小的點作為種子(seeds),開始進行生長
2. 以法線夾角閾值(Angle threshold)作為判斷標准,對鄰域內的點進行遍歷判斷 ,符合條件則加入當前點集,不符合則reject,並加入reject點集
3. 以曲率閾值(Curvature threshold)作為判斷標准,將鄰域內符合條件的點加入到種子隊列中
4. 移除當前種子
5. 如果當前種子隊列空了,表明當前子區域分割停止,遍歷其他種子區域,直到停止整個點雲均被遍歷完為止生長
參數分析:
1. 參數:MinClusterSize(最小聚類點雲數目),MaxClusterSize(最大聚類點雲數據)
NumberOfNeighbours(尋找種子seed點最近的點判斷是否為同類),SmoothnessThreshold(聚類的法線夾角閾值)
CurvatureThreshold(聚類的曲率閾值,可以直觀地將圓柱面等區別開)
2. 對應的代碼
ParameterReader pd(ParameterFilePath); pcl::RegionGrowing<PointT, pcl::Normal> reg; pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>); reg.setMinClusterSize(stoi(pd.getData("MinClusterSize"))); reg.setMaxClusterSize(stoi(pd.getData("MaxClusterSize"))); reg.setSearchMethod(tree); reg.setNumberOfNeighbours(stoi(pd.getData("NumberOfNeighbours"))); reg.setInputCloud(CloudFilter); reg.setInputNormals(Normals); reg.setSmoothnessThreshold(stod(pd.getData("SmoothnessThreshold")) / 180.0 * M_PI); reg.setCurvatureThreshold(stod(pd.getData("CurvatureThreshold"))); std::vector <pcl::PointIndices> clusters; reg.extract(clusters); /* wk 添加: 可視化調試 */ pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_segmented(new pcl::PointCloud<pcl::PointXYZRGB>()); cloud_segmented = reg.getColoredCloud(); pcl::visualization::CloudViewer viewer("Cluster viewer"); viewer.showCloud(cloud_segmented); while (!viewer.wasStopped()) { } /* wk 添加: 可視化調試 */
(4)SACSegmentation 利用RANSAC算法對平面點雲進行分割並擬合
1. 參數:MaxIterations(最大迭代次數),threshold(距離閾值,判斷點是否為當前擬合平面的內點,理論上該值越大平面越粗糙)
2. 代碼
/*RanSAC擬合平面,並將平面內點分割出來*/ pcl::SACSegmentation<PointT> seg; pcl::PointIndices::Ptr inliers(new pcl::PointIndices); pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients); seg.setOptimizeCoefficients(true); seg.setModelType(pcl::SACMODEL_PLANE); seg.setMethodType(pcl::SAC_RANSAC); seg.setMaxIterations(stoi(pd.getData("Maxci"))); seg.setDistanceThreshold(stod(pd.getData("threshold"))); seg.setInputCloud(cloud); seg.segment(*inliers, *coefficients); // 分割內點,另存 pcl::ExtractIndices<PointT> extract; PointCloudPtr cloud_plane(new pointCloud); extract.setInputCloud(cloud); extract.setIndices(inliers); extract.setNegative(false); extract.filter(*cloud_plane);
3 部分效果圖展示
(1)原圖
(2)RegionGrowing分割效果圖
4 算法的局限性
區域生長算法分割平面步驟及問題分析:針對分辨率低、掃描質量比較差的點雲,如圖所示,算法無法將破碎、扭曲的大塊區域識別為平面區域,只能將這部分點判斷為非平面點集舍棄掉。
區域生長算法通常在分割細節處比較平滑的平面點雲時,具有相當的優勢。但是在處理“波紋”狀點雲時,就沒什么優勢了。而實際掃描點雲的細節部位很多時候是如上圖所示的,為了將曲率較小的曲面區別開,而調低平滑及曲率閾值時,這類從大視角上看明顯是平面的點雲會被rejected,從而導致分割失效。如下圖所示,RegionGrowing更適合處理接近理想點雲的這類點雲,而不適合處理波動起伏狀的、掃描精度較差的點雲。