You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于已知尺寸的点云多3D圆拟合及误差优化技术咨询

解决方案:基于已知尺寸拟合3D圆并生成洁净点云

核心思路

要实现三个3D圆的精准拟合并结合已知尺寸修正误差,需按以下三步执行:

  • 点云特征分离:从混合点云中拆分出外圆和两个内圆的点集合
  • 带约束的拟合优化:先用RANSAC得到初始参数,再固定已知半径用最小二乘法优化中心与法向量,最小化误差
  • 生成洁净点云:基于最终拟合参数生成无噪声的完美圆点云

一、点云特征分离

先通过RANSAC拟合外圆,提取外圆内点并从原始点云中移除;再对剩余点用DBSCAN聚类,分离出两个内圆的点云:

# 拟合外圆并移除外圆点云
cir_outer = pyrsc.Circle()
center_outer, normal_outer, radius_outer, inliers_outer = cir_outer.fit(points, thresh=0.4, maxIteration=10000)
# 提取非外圆点云(即两个内圆的点)
outliers_outer = np.setdiff1d(np.arange(len(points)), inliers_outer)
inner_points = points[outliers_outer]

# 用DBSCAN聚类分离两个内圆
pcd_inner = o3d.geometry.PointCloud()
pcd_inner.points = o3d.utility.Vector3dVector(inner_points)
labels = np.array(pcd_inner.cluster_dbscan(eps=1.0, min_points=100))
# 分离两个内圆的点云
inner1_points = inner_points[labels == 0]
inner2_points = inner_points[labels == 1]

二、带已知半径约束的拟合优化

对于每个圆,先通过RANSAC获取初始参数,再固定已知半径,用最小二乘法优化中心和法向量,使点到圆的距离误差最小:

def fit_circle_with_fixed_radius(points, fixed_radius):
    # 先RANSAC获取初始参数
    cir = pyrsc.Circle()
    center_init, normal_init, _, _ = cir.fit(points, thresh=0.3, maxIteration=5000)
    
    # 定义误差函数:点到圆的距离平方和
    def error_func(params):
        center = params[:3]
        normal = params[3:6]
        normal = normal / np.linalg.norm(normal)  # 保证法向量单位化
        total_error = 0.0
        for p in points:
            # 点到圆心向量
            vec = p - center
            # 投影到法向量方向的距离(点到圆所在平面的距离)
            dist_plane = np.abs(np.dot(vec, normal))
            # 投影到圆平面内的向量长度
            proj_len = np.linalg.norm(vec - np.dot(vec, normal)*normal)
            # 点到圆的距离(平面内到圆周的距离 + 平面外距离)
            dist_circle = np.abs(proj_len - fixed_radius)
            total_error += dist_plane**2 + dist_circle**2
        return total_error
    
    # 初始参数:中心+法向量
    init_params = np.concatenate([center_init, normal_init])
    # 优化求解
    from scipy.optimize import minimize
    result = minimize(error_func, init_params, method='L-BFGS-B')
    
    # 提取优化后的参数
    center_opt = result.x[:3]
    normal_opt = result.x[3:6]
    normal_opt = normal_opt / np.linalg.norm(normal_opt)
    return center_opt, normal_opt, fixed_radius

# 已知三个圆的真实半径
KNOWN_RADII = [25, 3, 5]
# 拟合外圆(带约束)
center_outer_opt, normal_outer_opt, radius_outer_opt = fit_circle_with_fixed_radius(points[inliers_outer], KNOWN_RADII[0])
# 拟合第一个内圆
center_inner1_opt, normal_inner1_opt, radius_inner1_opt = fit_circle_with_fixed_radius(inner1_points, KNOWN_RADII[1])
# 拟合第二个内圆
center_inner2_opt, normal_inner2_opt, radius_inner2_opt = fit_circle_with_fixed_radius(inner2_points, KNOWN_RADII[2])

三、生成洁净点云

根据优化后的参数,生成无噪声的完美3D圆点云:

def generate_clean_circle_points(center, normal, radius, num_points=1000):
    # 生成圆所在平面的正交基
    if np.allclose(normal, [0,0,1]) or np.allclose(normal, [0,0,-1]):
        u = np.array([1,0,0])
        v = np.array([0,1,0])
    else:
        u = np.cross(normal, [0,0,1])
        u = u / np.linalg.norm(u)
        v = np.cross(normal, u)
        v = v / np.linalg.norm(v)
    
    theta = np.linspace(0, 2*np.pi, num_points)
    points = center + radius * (np.cos(theta)[:,None]*u + np.sin(theta)[:,None]*v)
    return points

# 生成三个洁净圆的点云
clean_outer = generate_clean_circle_points(center_outer_opt, normal_outer_opt, radius_outer_opt)
clean_inner1 = generate_clean_circle_points(center_inner1_opt, normal_inner1_opt, radius_inner1_opt)
clean_inner2 = generate_clean_circle_points(center_inner2_opt, normal_inner2_opt, radius_inner2_opt)

# 合并并可视化
clean_points = np.concatenate([clean_outer, clean_inner1, clean_inner2])
clean_pcd = o3d.geometry.PointCloud()
clean_pcd.points = o3d.utility.Vector3dVector(clean_points)
clean_pcd.paint_uniform_color([0,1,0])
o3d.visualization.draw_geometries([clean_pcd])

完整可运行代码

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import pyransac3d as pyrsc
import open3d as o3d
from scipy.optimize import minimize

### 创建模拟点云
# 参数设置
radius = 25
num_points = 1000
small_circle_points = 500
noise_std_xy = 0.1
noise_std_z = 0.1
num_small_circles = 2

# 生成外圆点云
theta = np.linspace(0, 2*np.pi, num_points)
x = radius * np.cos(theta)
y = radius * np.sin(theta)
z = np.zeros_like(x)
x += np.random.normal(0, noise_std_xy, num_points)
y += np.random.normal(0, noise_std_xy, num_points)
z += np.random.normal(0, noise_std_z, num_points)

# 生成两个内圆点云
for i in range(num_small_circles):
    small_radius = 3 if i ==0 else 5
    small_theta = np.linspace(0, 2*np.pi, small_circle_points)
    small_x = small_radius * np.cos(small_theta)
    small_y = small_radius * np.sin(small_theta)
    small_z = np.zeros_like(small_x)
    
    small_x += np.random.normal(0, noise_std_xy, small_circle_points)
    small_y += np.random.normal(0, noise_std_xy, small_circle_points)
    small_z += np.random.normal(0, noise_std_z, small_circle_points)
    
    if i ==0:
        shift =12
        small_x += shift
        small_y += shift
    
    x = np.concatenate((x, small_x))
    y = np.concatenate((y, small_y))
    z = np.concatenate((z, small_z))

points = np.column_stack((x, y, z))

### 1. 点云特征分离
# 拟合外圆并提取外圆内点
cir_outer = pyrsc.Circle()
center_outer, normal_outer, radius_outer, inliers_outer = cir_outer.fit(points, thresh=0.4, maxIteration=10000)
# 获取内圆点云(移除外圆点)
outliers_outer = np.setdiff1d(np.arange(len(points)), inliers_outer)
inner_points = points[outliers_outer]

# DBSCAN聚类分离两个内圆
pcd_inner = o3d.geometry.PointCloud()
pcd_inner.points = o3d.utility.Vector3dVector(inner_points)
labels = np.array(pcd_inner.cluster_dbscan(eps=1.0, min_points=100))
inner1_points = inner_points[labels ==0]
inner2_points = inner_points[labels ==1]

### 2. 带固定半径约束的拟合优化
def fit_circle_with_fixed_radius(points, fixed_radius):
    # RANSAC初始拟合
    cir = pyrsc.Circle()
    center_init, normal_init, _, _ = cir.fit(points, thresh=0.3, maxIteration=5000)
    
    # 误差函数:点到圆的距离平方和
    def error_func(params):
        center = params[:3]
        normal = params[3:6]
        normal = normal / np.linalg.norm(normal)
        total_error =0.0
        for p in points:
            vec = p - center
            dist_plane = np.abs(np.dot(vec, normal))
            proj_len = np.linalg.norm(vec - np.dot(vec, normal)*normal)
            dist_circle = np.abs(proj_len - fixed_radius)
            total_error += dist_plane**2 + dist_circle**2
        return total_error
    
    # 优化求解
    init_params = np.concatenate([center_init, normal_init])
    result = minimize(error_func, init_params, method='L-BFGS-B')
    
    center_opt = result.x[:3]
    normal_opt = result.x[3:6]
    normal_opt = normal_opt / np.linalg.norm(normal_opt)
    return center_opt, normal_opt, fixed_radius

# 已知三个圆的真实半径
KNOWN_RADII = [25, 3, 5]
# 拟合三个圆
center_outer_opt, normal_outer_opt, radius_outer_opt = fit_circle_with_fixed_radius(points[inliers_outer], KNOWN_RADII[0])
center_inner1_opt, normal_inner1_opt, radius_inner1_opt = fit_circle_with_fixed_radius(inner1_points, KNOWN_RADII[1])
center_inner2_opt, normal_inner2_opt, radius_inner2_opt = fit_circle_with_fixed_radius(inner2_points, KNOWN_RADII[2])

### 3. 生成洁净点云
def generate_clean_circle_points(center, normal, radius, num_points=1000):
    # 构建圆平面的正交基
    if np.allclose(normal, [0,0,1]) or np.allclose(normal, [0,0,-1]):
        u = np.array([1,0,0])
        v = np.array([0,1,0])
    else:
        u = np.cross(normal, [0,0,1])
        u = u / np.linalg.norm(u)
        v = np.cross(normal, u)
        v = v / np.linalg.norm(v)
    
    theta = np.linspace(0, 2*np.pi, num_points)
    circle_points = center + radius * (np.cos(theta)[:,None]*u + np.sin(theta)[:,None]*v)
    return circle_points

# 生成洁净点云
clean_outer = generate_clean_circle_points(center_outer_opt, normal_outer_opt, radius_outer_opt)
clean_inner1 = generate_clean_circle_points(center_inner1_opt, normal_inner1_opt, radius_inner1_opt)
clean_inner2 = generate_clean_circle_points(center_inner2_opt, normal_inner2_opt, radius_inner2_opt)

# 可视化原始点云与洁净点云
sourcepcd = o3d.geometry.PointCloud()
sourcepcd.points = o3d.utility.Vector3dVector(points)
sourcepcd.paint_uniform_color([0.5,0.5,0.5])

clean_pcd = o3d.geometry.PointCloud()
clean_pcd.points = o3d.utility.Vector3dVector(np.concatenate([clean_outer, clean_inner1, clean_inner2]))
clean_pcd.paint_uniform_color([0,1,0])

o3d.visualization.draw_geometries([sourcepcd, clean_pcd])

内容的提问来源于stack exchange,提问作者Gururaj Horakeri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 10:35:07