Skip to content

Filter Point Cloud Using Plane Defined by Point Normal Proximity

SUMMARY

Filter Point Cloud Using Plane Defined by Point Normal Proximity extracts points that lie within a specified distance from a plane defined by a reference point and a normal vector.

This Skill is particularly useful in industrial, mobile, and humanoid robotics pipelines for isolating planar surfaces or features. For example, it can extract a tabletop or work surface in industrial settings, detect floors or walls in mobile robot mapping, or identify flat regions for humanoid robot interactions. By focusing on points near a plane, robots can simplify perception and enable accurate pose estimation, segmentation, and manipulation.

Use this Skill when you want to filter a point cloud to points close to a specific plane for downstream perception, alignment, or manipulation tasks.

The Skill

python
from telekinesis import vitreous
import numpy as np

filtered_point_cloud = vitreous.filter_point_cloud_using_plane_defined_by_point_normal_proximity(
    point_cloud=point_cloud,
    plane_point=np.array([0.0, 0.0, 0.5]),
    plane_normal=np.array([0.0, 0.0, 1.0]),
    distance_threshold=0.01,
)

API Reference

Data Transfer Notice

There is no longer a fixed limit of 1 million points per request. However, very large datasets may result in slower data transfer and processing times. We are continuously optimizing performance as part of our beta program, with ongoing improvements to enhance speed and reliability.

Example

Raw Sensor Input

Unprocessed point cloud captured directly from the sensor.

Plane for Extraction

Input point cloud overlayed with the plane for extraction.

Extracted Points with a Small Distance

Only points within the specified distance of the plane are retained and points beyond this threshold are removed on either side. This filters out both the cans and the outliers below the plane.
Parameters: distance_threshold = 4 (scene units).

Extracted Points with a Moderate Distance

Raising the distance threshold filters out fewer points. Outliers below the plane are removed, but the cans’ lower-half points remain.
Parameters: distance_threshold = 50 (scene units).

The Code

python
from telekinesis import vitreous
from datatypes import datatypes, io
import pathlib
import numpy as np

# Optional for logging
from loguru import logger

DATA_DIR = pathlib.Path("path/to/telekinesis-data")

# Load point cloud
filepath = str(DATA_DIR / "point_clouds" / "can_vertical_3_downsampled.ply")
point_cloud = io.load_point_cloud(filepath=filepath)
logger.success(f"Loaded point cloud with {len(point_cloud.positions)} points")

# Execute operation
# Define plane using point and normal from coefficients
plane_coefficients = [0.028344755192329624, -0.5747207168510667, -0.8178585895344518, 555.4890362620131]
a, b, c, d = plane_coefficients
plane_point = -d / (a**2 + b**2 + c**2) * np.array([a, b, c])

# Filter point cloud using plane defined by point and normal
filtered_point_cloud = vitreous.filter_point_cloud_using_plane_defined_by_point_normal_proximity(
  point_cloud=point_cloud,
  plane_point=plane_point,
  plane_normal=[a, b, c],
  distance_threshold=50.0,
)
logger.success("Filtered points using plane defined by point and normal")

The Explanation of the Code

This example begins by importing the necessary modules for point cloud processing, numerical operations, data handling, and optional logging. The key modules include vitreous, datatypes, io, numpy, pathlib, and loguru.

python
from telekinesis import vitreous
from datatypes import datatypes, io
import pathlib
import numpy as np

# Optional for logging
from loguru import logger

Next, a point cloud is loaded from a .ply file. Logging confirms the number of points in the cloud, ensuring that the data is ready for processing and analysis.

python
DATA_DIR = pathlib.Path("path/to/telekinesis-data")

# Load point cloud
filepath = str(DATA_DIR / "point_clouds" / "can_vertical_3_downsampled.ply")
point_cloud = io.load_point_cloud(filepath=filepath)
logger.success(f"Loaded point cloud with {len(point_cloud.positions)} points")

Finally, the filter_point_cloud_using_plane_defined_by_point_normal_proximity Skill is applied. A plane is first defined from coefficients, converted into a point on the plane and a normal vector. The Skill then filters out points that lie farther than a specified distance from this plane. This is especially useful in robotics pipelines for isolating surfaces, segmenting planar features, or focusing on regions of interest for manipulation, inspection, or 3D perception tasks.

python
# Execute operation
# Define plane using point and normal from coefficients
plane_coefficients = [0.028344755192329624, -0.5747207168510667, -0.8178585895344518, 555.4890362620131]
a, b, c, d = plane_coefficients
plane_point = -d / (a**2 + b**2 + c**2) * np.array([a, b, c])

# Filter point cloud using plane defined by point and normal
filtered_point_cloud = vitreous.filter_point_cloud_using_plane_defined_by_point_normal_proximity(
  point_cloud=point_cloud,
  plane_point=plane_point,
  plane_normal=[a, b, c],
  distance_threshold=50.0,
)
logger.success("Filtered points using plane defined by point and normal")

Running the Example

Runnable examples are available in the Telekinesis examples repository. Follow the README in that repository to set up the environment. Once set up, you can run this specific example with:

bash
cd telekinesis-examples
python examples/vitreous_examples.py --example filter_point_cloud_using_plane_defined_by_point_normal_proximity

How to Tune the Parameters

The filter_point_cloud_using_plane_defined_by_point_normal_proximity Skill has three parameters that control the filtering:

plane_point (required):

  • A 3D point that lies on the plane
  • Units: Uses the same units as your point cloud (e.g., if point cloud is in meters, point is in meters; if in millimeters, point is in millimeters)
  • Defines the plane's position in space
  • Any point on the plane can be used
  • Typically set to a point you know lies on the plane (e.g., from plane fitting or manual selection)

plane_normal (required):

  • The normal vector of the plane (perpendicular to the plane surface)
  • Should be normalized (unit vector)
  • Defines the plane's orientation
  • For horizontal planes, use [0, 0, 1] or [0, 0, -1]
  • For vertical planes, use [1, 0, 0], [0, 1, 0], etc.
  • The direction (positive/negative) determines which side of the plane is considered "positive"

distance_threshold (required):

  • The maximum perpendicular distance from the plane to keep a point
  • Units: Uses the same units as your point cloud (e.g., if point cloud is in meters, threshold is in meters; if in millimeters, threshold is in millimeters)
  • Increase to keep points farther from the plane, including points on nearby parallel surfaces
  • Decrease to keep only points very close to the plane
  • Typical range: 0.001-0.1 in point cloud units
  • For precise plane extraction, use 0.001-0.01
  • For filtering near a plane, use 0.01-0.1

TIP

Best practice: Ensure plane_normal is normalized (unit vector). Use calculate_point_cloud_centroid to get a reference point on the plane if needed. Use segment_point_cloud_using_plane to detect planes first.

Where to Use the Skill in a Pipeline

Plane proximity filtering (point+normal) is commonly used in the following pipelines:

  • Planar surface extraction
  • Ground plane removal
  • Tabletop or work surface detection
  • Wall or floor identification

A typical pipeline for planar surface extraction looks as follows:

python
# Example pipeline using plane proximity filtering (point+normal) (parameters omitted).

from telekinesis import vitreous
import numpy as np

# 1. Load point cloud
point_cloud = vitreous.load_point_cloud(...)

# 2. Preprocess: remove outliers and downsample
filtered_cloud = vitreous.filter_point_cloud_using_statistical_outlier_removal(...)
downsampled_cloud = vitreous.filter_point_cloud_using_voxel_downsampling(...)

# 3. Segment plane to get plane information
plane_points = vitreous.segment_point_cloud_using_plane(
    point_cloud=downsampled_cloud,
    distance_threshold=0.01,
    keep_outliers=False,
)

# 4. Get reference point (e.g., centroid of plane)
plane_centroid = vitreous.calculate_point_cloud_centroid(point_cloud=plane_points)

# 5. Get plane normal (from plane coefficients or estimation)
plane_normal = np.array([0.0, 0.0, 1.0])  # Example: vertical normal

# 6. Filter point cloud using plane proximity
filtered_cloud = vitreous.filter_point_cloud_using_plane_defined_by_point_normal_proximity(
    point_cloud=downsampled_cloud,
    plane_point=plane_centroid,
    plane_normal=plane_normal,
    distance_threshold=0.01,
)

# 7. Process the filtered point cloud

Related skills to build such a pipeline:

  • segment_point_cloud_using_plane: detect planes and get plane information
  • calculate_point_cloud_centroid: get reference point on the plane
  • calculate_plane_normal: extract normal vector from plane coefficients
  • filter_point_cloud_using_statistical_outlier_removal: clean input before filtering
  • filter_point_cloud_using_plane_proximity: alternative method using plane coefficients

Alternative Skills

Skillvs. Filter Point Cloud Using Plane (Point+Normal)
filter_point_cloud_using_plane_proximityUse plane coefficients when you have plane equation coefficients [a, b, c, d]. Use point+normal when you have a point on the plane and its normal vector.
filter_point_cloud_using_plane_splittingUse plane splitting when you want to keep one side of the plane. Use plane proximity when you want to keep points within a distance threshold on both sides.
segment_point_cloud_using_planeUse plane segmentation to detect and extract the largest plane. Use plane proximity filtering to extract points near a known plane.

When Not to Use the Skill

Do not use filter point cloud using plane (point+normal) when:

  • You have plane coefficients (use filter_point_cloud_using_plane_proximity instead)
  • The plane normal is not normalized (ensure it's a unit vector)
  • You want to keep only one side of the plane (use filter_point_cloud_using_plane_splitting instead)
  • You need to detect planes (use segment_point_cloud_using_plane first to find planes)
  • The point is not on the plane (the filtering may not be as expected)
  • You need to filter based on 3D box regions (use bounding box or passthrough filter instead)