目录
假设你有一堆 3D 点(可能属于多个物体+地面),想找出那些属于一个平面的点(比如地面)。
地面检测流程:
1.RANSAC 平面拟合
2.对地面点云 (ground_pcd) 进行聚类,筛选出最大一簇(最主要的地面区域)
RANSAC 平面拟合原理
假设你有一堆 3D 点(可能属于多个物体+地面),想找出那些属于一个平面的点(比如地面)。
✅ 步骤如下:
-
随机选 3 个点(因为 3 个点能确定一个平面);
-
用这 3 个点拟合一个平面(即求平面方程 ax + by + cz + d = 0);
-
计算所有点到这个平面的距离;
-
统计“内点”数量:
-
如果某点到平面的距离 <
threshold
,就认为它在这个平面上(是“内点”);
-
-
记录这个平面内点数;
-
重复以上过程 N 次(比如 500 次),每次选不同的 3 个点;
-
选择内点最多的平面作为最终拟合结果;
-
(可选)用找到的内点重新精确拟合平面。
四、RANSAC 平面拟合的优缺点
优点:
- 抗噪声能力强:能有效排除外点对拟合结果的影响。
- 无需先验知识:无需提前知道内点和外点的分布。
- 实现简单:算法流程清晰,易于编程实现。
缺点:
- 计算复杂度高:迭代次数随内点比例降低而增加,大数据集下耗时较长。
- 参数敏感性:阈值 t 和迭代次数 N 的设定影响结果精度。
- 依赖初始抽样:若最优平面的内点未被首次抽样选中,可能需要更多迭代。
点云聚类分割 + 每个局部区域做平面拟合(PCA)
import open3d as o3d
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
def pca_fit_plane(points):
# PCA 拟合平面,返回平面法向量和中心
centroid = np.mean(points, axis=0)
pca = PCA(n_components=3)
pca.fit(points)
normal = pca.components_[-1] # 最小特征值方向是法向量
return normal, centroid
def segment_and_fit_planes(pcd, eps=0.05, min_points=30):
# 使用 DBSCAN 进行聚类
labels = np.array(pcd.cluster_dbscan(eps=eps, min_points=min_points, print_progress=True))
max_label = labels.max()
print(f"Detected {max_label + 1} clusters")
planes = []
colors = plt.get_cmap("tab20")(labels / (max_label + 1 if max_label > 0 else 1))
colors[labels < 0] = 0 # 噪声为黑色
pcd.colors = o3d.utility.Vector3dVector(colors[:, :3])
for i in range(max_label + 1):
indices = np.where(labels == i)[0]
cluster = pcd.select_by_index(indices)
if len(cluster.points) < 3:
continue
pts = np.asarray(cluster.points)
normal, center = pca_fit_plane(pts)
planes.append((normal, center))
print(f"[Cluster {i}] Center: {center}, Normal: {normal}")
return planes, pcd
if __name__ == "__main__":
# 加载点云(你可以替换为自己的文件)
pcd = o3d.io.read_point_cloud("example.ply")
# 可选:下采样加快处理
pcd = pcd.voxel_down_sample(voxel_size=0.02)
planes, colored_pcd = segment_and_fit_planes(pcd, eps=0.05, min_points=50)
# 可视化聚类结果
o3d.visualization.draw_geometries([colored_pcd])