Open3d点云处理之圆柱拟合

文章介绍了如何在Open3d中改进圆柱拟合算法,特别提到在y轴上的性能问题,建议结合聚类计算曲面长度,以更精确地估计中心点。作者提供了使用RANSAC方法进行圆柱拟合的Python代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于Open3d没有跟PCL一样的圆柱拟合,所以参考PCL代码

经测试,该算法在y轴上效果不是很好,请适当考虑利用聚类,计算出曲面的y轴总长度,取代他中心点坐标来代替算法预估的y坐标

点云处理工具:open3d==0.17

"""
拟合圆柱抓取中心
"""
import numpy as np
import open3d as o3d
import numpy
import random

center_location = np.array([])
#定义结构体
class Model:
    def __init__(self):
        self.point = np.array([0, 0, 0])
        self.direction = np.array([0, 0, 1])
        self.r = 0
        self.lIndices = []
        self.gIndices = []


def RANSAC_FIT_Cyliner(pcd,sigma,min_r,max_r,sample_num,iter):
    """

    :param pcd: 点云数据文件
    :param sigma: 距离拟合圆柱两侧的距离
    :param min_r: 设置圆柱最小半径
    :param max_r: 设置圆柱最大半径
    :param sample_num: 随机采样的点数
    :param iter: 迭代次数
    :return:
    """
    k = 50         #邻近点数,计算法向量
    if not pcd.has_normals():
        pcd.estimate_normals(o3d.geometry.KDTreeSearchParamKNN(k))

    # 法向量重定向
    pcd.orient_normals_consistent_tangent_plane(10)
    points = np.asarray(pcd.points)
    normals= np.asarray(pcd.normals)
    nums = points.shape[0]
    #print(nums)
    if sample_num>nums:
        print("采样点大于点云点数")
    range_index = [i for i in range(nums)]#获取每个点的索引数值
    model = Model()
    pretotal = 0  # 符合拟合模型的数据个数
    for i in range(iter):
        idx = random.sample(range_index, sample_num)
        sample_data = points[idx, :]
        normal_data = normals[idx, :]
        # 拟合圆柱
        p1 = sample_data[0, :]
        p2 = sample_data[1, :]
        n1 = normal_data[0, :]
        n2 = normal_data[1, :]

        # 计算圆柱系数
        w = n1 + p1 - p2
        a = np.dot(n1, n1)
        b = np.dot(n1, n2)
        c = np.dot(n2, n2)
        d = np.dot(n1, w)
        e = np.dot(n2, w)
        denominator = a * c - b * b
        if denominator < 1e-8:
            sc = 0
            if b > c:
                tc = d / b
            else:
                tc = e / c
        else:
            sc = (b * e - c * d) / denominator
            tc = (a * e - b * d) / denominator

        line_pt = p1 + n1 + sc * n1  # 轴线上一点
        line_dir = p2 + tc * n2 - line_pt  # 轴向
        line_dir = line_dir / np.linalg.norm(line_dir)  # 轴向归一化
        # 计算半径(点到线的距离)
        vec = p1 - line_pt
        r = np.linalg.norm(np.cross(vec, line_dir))
        if r < min_r or r > max_r:
            #print("{}:半径为{}不符合要求,跳过".format(i, r))
            continue

        # *****统计符合内点的数量*******
        dist = np.dot(line_pt, line_dir)  # 原点到平面距离
        t = points @ line_dir + dist
        tmpt = t.reshape(nums, 1)
        tmp_line_dir = line_dir.reshape(1, 3)
        proj_point = points - tmpt @ tmp_line_dir  # 投影
        # 计算中心点
        ct = np.dot(line_pt, line_dir) + dist
        center_point = line_pt - ct * line_dir
        proj_pcd = o3d.geometry.PointCloud(pcd)
        proj_pcd.points = o3d.utility.Vector3dVector(proj_point)
        pcd_tree = o3d.geometry.KDTreeFlann(proj_pcd)

        [k1, gIndices, _] = pcd_tree.search_radius_vector_3d(center_point, r + sigma)
        if r - sigma > 0:
            [k2, lIndices, _] = pcd_tree.search_radius_vector_3d(center_point, r - sigma)
        else:
            lIndices = []

        total = len(gIndices) - len(lIndices)
        #print("{}:半径为{},内部点数为:{}".format(i, r, total))

        if total > pretotal:
            pretotal = total
            model.point = line_pt
            model.direction = line_dir
            model.r = r
            model.lIndices = lIndices
            model.gIndices = gIndices


    print(f"圆柱中心点:{model.point[0], model.point[1],model.point[2]}\n"
          f"圆柱朝向:{model.direction}\n"
          f"圆柱半径:{ model.r}\n"
          f"内部点数为:{len(model.gIndices) - len(model.lIndices)}"
        )

    center_location = np.array([model.point[0],model.point[1],model.point[2]])

    return center_location,model.direction,model.r

# if __name__ == "__main__":
#     pcd = o3d.io.read_point_cloud('../111.pcd')
#     o3d.visualization.draw_geometries([pcd])
#     sigma = 10    # 距离拟合圆柱两侧的距离
#     min_r = 43      # 设置圆柱最小半径
#     max_r = 46      # 设置圆柱最大半径
#     sample_num = 3  # 随机采样的点数
#     RANSAC_FIT_Cyliner(pcd,sigma,min_r,max_r,sample_num)

### 使用 Open3D 实现点云数据的圆柱拟合 为了实现点云数据的圆柱拟合,可以采用 RANSAC (Random Sample Consensus) 算法。此方法通过迭代选取样本子集并评估模型的一致性来找到最佳拟合参数。 #### 准备工作 安装必要的 Python 库 `open3d` 和其他辅助工具包如 NumPy: ```bash pip install open3d numpy ``` #### 加载点云文件 读取要分析的目标对象的点云数据,并可视化这些数据以便观察其特征。 ```python import open3d as o3d import numpy as np # 从PCD/PLY等格式加载点云 pcd = o3d.io.read_point_cloud("path_to_your_pcd_file.ply") # 可视化原始点云 o3d.visualization.draw_geometries([pcd]) ``` #### 数据预处理 去除噪声和平滑处理有助于提高后续拟合过程的质量。这里可以通过体素下采样减少冗余点的数量。 ```python downsampled_pcd = pcd.voxel_down_sample(voxel_size=0.05) cl, ind = downsampled_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0) inlier_cloud = downsampled_pcd.select_by_index(ind) outlier_cloud = downsampled_pcd.select_by_index(ind, invert=True) o3d.visualization.draw_geometries([inlier_cloud]) # 显示去噪后的点云 ``` #### 圆柱拟合 调用 Open3D 提供的功能来进行基于 RANSAC 的圆柱拟合操作。注意,在某些情况下可能需要调整 y 轴方向上的估计策略以获得更精确的结果[^3]。 ```python cylinder_model, inliers = inlier_cloud.segment_cylinder(distance_threshold=0.05, ransac_n=3, num_iterations=1000) line_set = o3d.geometry.LineSet.create_from_cylinder(cylinder_model) colors = [[1, 0, 0] for i in range(len(line_set.lines))] line_set.colors = o3d.utility.Vector3dVector(colors) final_cloud = inlier_cloud.select_by_index(inliers).paint_uniform_color([0, 1, 0]) o3d.visualization.draw_geometries([final_cloud, line_set]) ``` 上述代码片段展示了如何使用 Open3D 来执行完整的圆柱拟合流程,包括但不限于点云加载、初步清理以及最终的几何形状匹配。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值