1 介紹
Fitting trimmed B-splines(修剪B樣條曲線擬合)是一種用于對給定的點云數據進行曲線擬合的算法。該算法使用B樣條曲線模型來逼近給定的點云數據,并通過對模型進行修剪來提高擬合的精度和準確性。
B樣條曲線是一種常用的曲線表示方法,它通過一組控制點和節點向量來定義曲線的形狀。B樣條曲線具有局部控制性和平滑性,因此在曲線擬合問題中被廣泛應用。
修剪B樣條曲線擬合算法的基本步驟如下:
初始化:定義一個B樣條曲線模型,并設置模型的階數、節點向量和控制點。
迭代優化:通過迭代優化的方法,不斷調整模型的控制點,使得模型更好地逼近給定的點云數據。在每次迭代中,根據給定的擬合參數和優化目標函數,計算出新的控制點位置。
修剪:根據給定的修剪參數,對擬合的B樣條曲線進行修剪。修剪可以通過刪除不需要的曲線段或調整曲線段的權重來實現。修剪的目的是提高擬合的準確性和精度。
終止條件:根據設定的終止條件,判斷是否終止迭代優化過程。終止條件可以是達到最大迭代次數、擬合精度滿足要求或其他自定義條件。
輸出結果:輸出最終的修剪B樣條曲線擬合結果。結果可以是修剪后的B樣條曲線模型,也可以是曲線的控制點和節點向量等表示。
2 效果
3 說明
在進行B樣條曲面擬合時,通常需要先對曲面進行擬合,然后再對曲面上的曲線進行擬合。這是因為曲面上的曲線通常是曲面的邊界或者用于修剪曲面的曲線,它們與曲面的形狀緊密相關,因此需要單獨進行擬合和調整。
首先,進行B樣條曲面擬合時,目標是通過一組控制點和節點向量來逼近給定的點云數據,以生成一個平滑的曲面模型。這個曲面模型可以用來表示曲面的整體形狀。
然后,在曲面擬合的基礎上,進行B樣條曲線擬合。曲線擬合通常是對曲面的邊界曲線或者修剪曲線進行擬合。這些曲線與曲面的形狀緊密相關,因此需要單獨進行擬合和調整。通過對曲線進行擬合,可以更好地捕捉曲面的邊界形狀或者修剪曲線的形狀,從而提高擬合的精度和準確性。
總結起來,進行B樣條曲面擬合后,還需要對曲面上的曲線進行擬合的原因是曲線與曲面的形狀緊密相關,需要單獨進行擬合和調整以提高擬合的精度和準確性。通過分別擬合曲面和曲線,可以更好地捕捉曲面的整體形狀和邊界形狀,從而得到更好的擬合結果。
曲面擬合時的參數
order
:曲面的階數。該參數定義了B樣條曲面的階數,即每個控制點的影響范圍。較高的階數可以提供更大的自由度,但也會增加計算時間和內存消耗。
refinement
:細化次數。該參數定義了在擬合過程中進行的細化次數。通過細化,可以在擬合過程中增加新的控制點,以提高擬合的精度和準確性。
iterations
:迭代次數。該參數定義了擬合過程中的迭代次數。增加迭代次數可以提高擬合的精度,但也會增加計算時間和內存消耗。
mesh_resolution
:網格分辨率。該參數定義了生成曲面網格的分辨率。較高的分辨率會導致更細致的曲面表示,但也會增加計算時間和內存消耗。
params.interior_smoothness
:內部平滑度。該參數用于調整曲面內部的平滑度。較大的值會使曲面更平滑。
params.interior_weight
:內部權重。該參數用于調整曲面內部點對擬合結果的權重。較大的權重會使內部點對擬合結果的影響更大。
params.boundary_smoothness
:邊界平滑度。該參數用于調整曲面邊界的平滑度。較大的值會使曲面邊界更平滑。
params.boundary_weight
:邊界權重。該參數用于調整曲面邊界點對擬合結果的權重。較大的權重會使邊界點對擬合結果的影響更大。
曲線擬合的參數
curve_params.addCPsAccuracy
:添加控制點的精度。控制點是用于定義B樣條曲線形狀的關鍵點。該參數指定了在擬合過程中添加新的控制點的精度。較小的值會導致更精確的擬合結果,但可能會增加計算時間和內存消耗。
curve_params.addCPsIteration
:添加控制點的迭代次數。在擬合過程中,可以通過迭代的方式不斷添加新的控制點來改進擬合結果。該參數指定了添加控制點的迭代次數。增加迭代次數可以提高擬合的精度,但也會增加計算時間和內存消耗。
curve_params.maxCPs
:最大控制點數。該參數限制了擬合過程中允許的最大控制點數。超過這個數目的控制點將被丟棄。通過調整這個參數,可以控制擬合結果的復雜度和平滑度。
curve_params.accuracy
:擬合的精度。該參數指定了擬合結果與原始數據之間的誤差閾值。較小的值會導致更精確的擬合結果,但可能會增加計算時間和內存消耗。
curve_params.iterations
:迭代次數。該參數指定了擬合過程中的迭代次數。增加迭代次數可以提高擬合的精度,但也會增加計算時間和內存消耗。
curve_params.param.closest_point_resolution
:最近點的分辨率。該參數用于計算曲線上最近點的分辨率。較小的值會導致更精確的最近點計算,但可能會增加計算時間和內存消耗。
curve_params.param.closest_point_weight
:最近點的權重。該參數用于計算曲線上最近點的權重。較大的權重會使最近點對擬合結果的影響更大。
curve_params.param.closest_point_sigma2
:最近點的方差。該參數用于計算曲線上最近點的方差。較小的方差會使最近點對擬合結果的影響更大。
curve_params.param.interior_sigma2
:內部點的方差。該參數用于計算曲線上內部點的方差。較小的方差會使內部點對擬合結果的影響更大。
curve_params.param.smooth_concavity
:平滑凹度。該參數用于調整曲線的平滑凹度。較大的值會使曲線更平滑。
curve_params.param.smoothness
:平滑度。該參數用于調整曲線的平滑度。較大的值會使曲線更平滑。
4 代碼
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/surface/on_nurbs/fitting_surface_tdm.h>
#include <pcl/surface/on_nurbs/fitting_curve_2d_asdm.h>
#include <pcl/surface/on_nurbs/triangulation.h>typedef pcl::PointXYZ Point;
std::ostringstream os;// 將點云數據中的有效點(即非NaN值)提取出來,并以Eigen::Vector3d類型的形式保存起來
void PointCloud2Vector3d(pcl::PointCloud<Point>::Ptr cloud, pcl::on_nurbs::vector_vec3d & data)
{for (unsigned i = 0; i< cloud->size(); i++){Point &p = cloud->at(i);if(!std::isnan(p.x) && !std::isnan(p.z) && !std::isnan(p.z))data.push_back(Eigen::Vector3d(p.x, p.y, p.z));}
}// 將ON_NurbsCurve和ON_NurbsSurface的曲線和控制點可視化顯示在PCLVisualizer中,方便用戶觀察和分析曲線的形狀和控制點的位置。
void visualizeCurve (ON_NurbsCurve &curve,ON_NurbsSurface &surface,pcl::visualization::PCLVisualizer &viewer)
{// 將曲線轉換為點云數據pcl::PointCloud<pcl::PointXYZRGB>::Ptr curve_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);pcl::on_nurbs::Triangulation::convertCurve2PointCloud (curve, surface, curve_cloud, 4);//該函數會將曲線上的點均勻地采樣,并將采樣點作為點云數據的點。// 可視化for(std::size_t i=0; i< curve_cloud->size() - 1; i++){pcl::PointXYZRGB &p1 = curve_cloud->at(i);pcl::PointXYZRGB &p2 = curve_cloud->at(i+1);os << "line" << i;viewer.removeShape(os.str());viewer.addLine<pcl::PointXYZRGB>(p1, p2, 1.0, 0.0, 0.0, os.str());}//pcl::PointCloud<pcl::PointXYZRGB>::Ptr curve_cps (new pcl::PointCloud<pcl::PointXYZRGB>);for(int i=0; i< curve.CVCount(); i++){ON_3dPoint p1;curve.GetCV(i, p1); // 曲線的一個控制點double pnt[3];surface.Evaluate(p1.x ,p1.y, 0, 3, pnt); // 加usn曲面上對應的點的坐標pcl::PointXYZRGB p2;p2.x = float (pnt[0]);p2.y = float (pnt[1]);p2.z = float (pnt[2]);p2.r = 255;p2.g = 0;p2.b = 0;curve_cps->push_back (p2);}viewer.removePointCloud ("cloud_cps");viewer.addPointCloud (curve_cps, "cloud_cps");
}int main()
{pcl::visualization::PCLVisualizer viewer("B-spline surface fitting");viewer.setSize(800, 600);pcl::PCLPointCloud2 cloud2;pcl::PointCloud<Point>::Ptr cloud(new pcl::PointCloud<Point>);if(pcl::io::loadPCDFile("/home/lrj/work/pointCloudData/bun0.pcd", cloud2) == -1)throw std::runtime_error(" PCD file not found.");pcl::fromPCLPointCloud2(cloud2, *cloud);pcl::on_nurbs::NurbsDataSurface data;PointCloud2Vector3d(cloud, data.interior);pcl::visualization::PointCloudColorHandlerCustom<Point> handler(cloud, 0, 255, 0);viewer.addPointCloud<Point>(cloud, handler, "cloud_cylinder");std::printf(" %lu points in data set\n", cloud->size());// ############################################################################// fit B-spline surface/** 整個過程的目的是通過ON-Nurbs算法對給定的點云數據進行曲面擬合,并將擬合結果以三角網格的形式可視化顯示出來。* 通過多次細化和迭代,逐步優化曲面擬合結果,使其更加接近原始點云數據。*/// parametersunsigned order(3); // 曲面的階數unsigned refinement(5); // 細化次數unsigned iterations(10); // 迭代次數unsigned mesh_resolution(256); // 網格分辨率pcl::on_nurbs::FittingSurface::Parameter params;params.interior_smoothness = 0.2; // 內部平滑度params.interior_weight = 1.0; // 內部權重params.boundary_smoothness = 0.2; // 邊界平滑度params.boundary_weight = 0.0; // 邊界權重// 生成初始的曲面擬合結果printf(" surface fitting ...\n");ON_NurbsSurface nurbs = pcl::on_nurbs::FittingSurface::initNurbsPCABoundingBox(order, &data);pcl::on_nurbs::FittingSurface fit(&data, nurbs);// fit.setQuiet (false); // enable/disable debug output// 將擬合結果轉為三角網格,并將其添加到可視化窗口進行現實pcl::PolygonMesh mesh;pcl::PointCloud<pcl::PointXYZ>::Ptr mesh_cloud(new pcl::PointCloud<pcl::PointXYZ>);std::vector<pcl::Vertices> mesh_vertices;std::string mesh_id = "mesh_nurbs";pcl::on_nurbs::Triangulation::convertSurface2PolygonMesh(fit.m_nurbs, mesh, mesh_resolution);viewer.addPolygonMesh(mesh, mesh_id);// 進行多次曲面細化和求解for (unsigned i=0; i< refinement; i++){fit.refine(0);fit.refine(1);fit.assemble(params);fit.solve();pcl::on_nurbs::Triangulation::convertSurface2Vertices (fit.m_nurbs, mesh_cloud, mesh_vertices, mesh_resolution); // 將曲面轉為頂點數據viewer.updatePolygonMesh<pcl::PointXYZ> (mesh_cloud, mesh_vertices, mesh_id); // 視窗刷新,以便觀察到曲面擬合的過程viewer.spinOnce ();}// 進行一定次數的曲面擬合迭代for (unsigned i = 0; i < iterations; i++){fit.assemble (params);fit.solve ();pcl::on_nurbs::Triangulation::convertSurface2Vertices (fit.m_nurbs, mesh_cloud, mesh_vertices, mesh_resolution);viewer.updatePolygonMesh<pcl::PointXYZ> (mesh_cloud, mesh_vertices, mesh_id);viewer.spinOnce();}// ############################################################################// fit B-spline curve/** 整個曲線擬合的過程的目的是使用ON-Nurbs算法對給定的點云數據進行曲線擬合,并將擬合結果可視化顯示出來。* 通過調整擬合參數,可以控制擬合的精度和平滑度,以得到最優的擬合結果。*/// parameterspcl::on_nurbs::FittingCurve2dAPDM::FitParameter curve_params;curve_params.addCPsAccuracy = 5e-2; // 添加控制點的精度curve_params.addCPsIteration = 3; // 添加控制點的迭代次數curve_params.maxCPs = 200; // 最大控制點數curve_params.accuracy = 1e-3; // 擬合的精度curve_params.iterations = 100; // 迭代次數curve_params.param.closest_point_resolution = 0; // 最近點的分辨率curve_params.param.closest_point_weight = 1.0; // 最近點的權重curve_params.param.closest_point_sigma2 = 0.1; // 最近點的方差curve_params.param.interior_sigma2 = 0.00001; // 內部點的方差curve_params.param.smooth_concavity = 1.0; // 平滑凹度curve_params.param.smoothness = 1.0; // 平滑度// initialisation (circular)printf (" curve fitting ...\n");pcl::on_nurbs::NurbsDataCurve2d curve_data;curve_data.interior = data.interior_param;curve_data.interior_weight_function.push_back (true);ON_NurbsCurve curve_nurbs = pcl::on_nurbs::FittingCurve2dAPDM::initNurbsCurve2D (order, curve_data.interior);// curve fittingpcl::on_nurbs::FittingCurve2dASDM curve_fit (&curve_data, curve_nurbs);// curve_fit.setQuiet (false); // enable/disable debug outputcurve_fit.fitting (curve_params);visualizeCurve (curve_fit.m_nurbs, fit.m_nurbs, viewer);// ############################################################################// triangulation of trimmed surfaceprintf (" triangulate trimmed surface ...\n");viewer.removePolygonMesh (mesh_id);pcl::on_nurbs::Triangulation::convertTrimmedSurface2PolygonMesh (fit.m_nurbs, curve_fit.m_nurbs, mesh,mesh_resolution); // 將擬合的曲面、曲線轉為三角網格viewer.addPolygonMesh (mesh, mesh_id);// save trimmed B-spline surfaceif ( fit.m_nurbs.IsValid() ){ONX_Model model;ONX_Model_Object& surf = model.m_object_table.AppendNew();surf.m_object = new ON_NurbsSurface(fit.m_nurbs);surf.m_bDeleteObject = true;surf.m_attributes.m_layer_index = 1;surf.m_attributes.m_name = "surface";ONX_Model_Object& curv = model.m_object_table.AppendNew();curv.m_object = new ON_NurbsCurve(curve_fit.m_nurbs);curv.m_bDeleteObject = true;curv.m_attributes.m_layer_index = 2;curv.m_attributes.m_name = "trimming curve";// model.Write(file_3dm.c_str());
// printf(" model saved: %s\n", file_3dm.c_str());}printf (" ... done.\n");viewer.spin();return 0;
}