Untitled

 avatar
unknown
plain_text
22 days ago
5.4 kB
8
Indexable
import open3d as o3d
import numpy as np

def create_cube_cloud():
    # Create pointcloud filling a cube
    points = []
    size = 1
    resolution = 10

    for x in np.linspace(-size/2, size/2, resolution):
        for y in np.linspace(-size/2, size/2, resolution):
            for z in [-size/2, size/2]:
                points.append([x, y, z])  # sup and inf faces
                points.append([x, z, y])  # front and back
                points.append([z, y, x])  # left and right

    point_cloud = o3d.geometry.PointCloud()
    point_cloud.points = o3d.utility.Vector3dVector(np.array(points))

    return point_cloud


def generate_corridor_cloud(length=6, width=2, height=3, resolution=10, noise_level=0):
    shelf1_x = -width / 2  # Left wall
    shelf2_x = width / 2   # Right wall
    ground_z = 0          # Ground
    ceiling_z = height    # Ceilling

    points = []

    for x in np.linspace(shelf1_x, shelf2_x, resolution * width):
        for y in np.linspace(-length / 2, length / 2, resolution * length):
            z = ground_z + np.random.normal(0, noise_level)  # Ground
            points.append([x, y, z])
            z = ceiling_z + np.random.normal(0, noise_level)  # Ceiling
            points.append([x, y, z])

    # Walls
    for z in np.linspace(ground_z, ceiling_z, resolution * height):
        for y in np.linspace(-length / 2, length / 2, resolution * length):
            x = shelf1_x + np.random.normal(0, noise_level)  # Left
            points.append([x, y, z])
            x = shelf2_x + np.random.normal(0, noise_level)  # Right
            points.append([x, y, z])

    cloud = o3d.geometry.PointCloud()
    cloud.points = o3d.utility.Vector3dVector(np.array(points))

    return cloud

def estimate_motion_with_ransac(source_cloud, target_cloud, voxel_size=0.05, max_correspondence_distance=0.2):
    print("Downsampling the point clouds")
    source_down = source_cloud.voxel_down_sample(voxel_size)
    target_down = target_cloud.voxel_down_sample(voxel_size)

    print("Estimating normals")
    source_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=voxel_size * 2, max_nn=30))
    target_down.estimate_normals(search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=voxel_size * 2, max_nn=30))

    print("Computing FPFH features")
    source_fpfh = o3d.pipelines.registration.compute_fpfh_feature(
        source_down,
        o3d.geometry.KDTreeSearchParamHybrid(radius=voxel_size * 5, max_nn=100)
    )
    target_fpfh = o3d.pipelines.registration.compute_fpfh_feature(
        target_down,
        o3d.geometry.KDTreeSearchParamHybrid(radius=voxel_size * 5, max_nn=100)
    )

    print("Applying RANSAC")
    try:
        result = o3d.pipelines.registration.registration_ransac_based_on_feature_matching(
            source_down,
            target_down,
            source_fpfh,
            target_fpfh,
            mutual_filter=True,
            max_correspondence_distance=max_correspondence_distance,
            estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint(with_scaling=False),
            ransac_n=4,
            checkers=[
                o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9),
                o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(max_correspondence_distance)
            ],
            criteria=o3d.pipelines.registration.RANSACConvergenceCriteria(
                max_iteration=1000000,
                confidence=0.9999
            )
        )
    except RuntimeError as e:
        print(f"RANSAC failed: {e}")
        return None

    print("RANSAC result:")
    print(result)
    print("Estimated transformation:\n", result.transformation)
    return result

if __name__ == "__main__":
    print("Simulating point clouds...")

    #source_cloud = create_cube_cloud()
    source_cloud = generate_corridor_cloud()
    source_cloud.paint_uniform_color([1, 0, 0])  # Red

    # Define tranformation between the 2 frames
    angle = np.pi / 9
    transformation = np.array([
        [np.cos(angle), -np.sin(angle), 0, 0],
        [np.sin(angle), np.cos(angle), 0, 0.2],  # +transl
        [0, 0, 1, 0],
        [0, 0, 0, 1]
    ])

    print("Real transformation", transformation, sep='\n')

    # Generate target cloud
    target_cloud = o3d.geometry.PointCloud(source_cloud).transform(transformation)
    #target_points = np.asarray(target_cloud.points)
    #target_cloud.points = o3d.utility.Vector3dVector(target_points)
    target_cloud.paint_uniform_color([0, 1, 0])  # Green

    # Apply RANSAC to estimate movement
    result = estimate_motion_with_ransac(source_cloud, target_cloud)

    # Display
    if result is not None:
        aligned_cloud = o3d.geometry.PointCloud(source_cloud).transform(result.transformation)
        aligned_cloud.paint_uniform_color([0, 0, 1])  # Blue

        #o3d.visualization.draw_geometries([source_cloud, target_cloud, aligned_cloud])
        print("Visualizing source and target")
        o3d.visualization.draw_geometries([source_cloud, target_cloud])
        print("Visualizing source and aligned clouds...")
        o3d.visualization.draw_geometries([source_cloud, aligned_cloud])
        print("Visualizing target and aligned clouds...")
        o3d.visualization.draw_geometries([target_cloud, aligned_cloud])

    else:
        print("RANSAC did not converge or failed.")
Leave a Comment