Skip to content

Register Point Clouds Using Cuboid Translation Sampler ICP

SUMMARY

Register Point Clouds Using Cuboid Translation Sampler ICP finds the best alignment between two point clouds by trying many candidate translations on a 3D grid, refining each one with point-to-point ICP.

It samples translations on a regular grid within [x_min, x_max] x [y_min, y_max] x [z_min, z_max] (relative to initial_transformation_matrix), runs ICP starting from each sampled translation, and keeps whichever run converged to the best fitness score. This is useful when source_point_cloud and target_point_cloud are already roughly rotationally aligned but the translation between them isn't known precisely enough for plain ICP to converge on its own. Compare with register_point_clouds_using_rotation_sampler_icp, which instead searches over rotations. The function returns the 4x4 transform that produced the best result, not an already-moved point cloud — apply it with apply_transform_to_point_cloud.

Use this Skill when you want to register two point clouds whose relative translation is uncertain but whose relative rotation is already roughly known.

The Skill

python
from telekinesis import vitreous
import numpy as np

# Stage 1: coarse search over a wide cuboid
coarse_transform = vitreous.register_point_clouds_using_cuboid_translation_sampler_icp(
    source_point_cloud=source_point_cloud,
    target_point_cloud=target_point_cloud,
    initial_transformation_matrix=np.eye(4),
    step_size=5,
    x_min=-20,
    x_max=20,
    y_min=-20,
    y_max=20,
    z_min=-20,
    z_max=20,
    early_stop_fitness_score=0.7,
    min_fitness_score=0.3,
    max_iterations=15,
    max_correspondence_distance=4,
    estimate_scaling=False,
)

# Stage 2: fine search, seeded with the coarse result
fine_transform = vitreous.register_point_clouds_using_cuboid_translation_sampler_icp(
    source_point_cloud=source_point_cloud,
    target_point_cloud=target_point_cloud,
    initial_transformation_matrix=coarse_transform,
    step_size=1,
    x_min=-3,
    x_max=3,
    y_min=-3,
    y_max=3,
    z_min=-3,
    z_max=3,
    early_stop_fitness_score=0.85,
    min_fitness_score=0.48,
    max_iterations=40,
    max_correspondence_distance=2,
    estimate_scaling=False,
)

aligned_point_cloud = vitreous.apply_transform_to_point_cloud(
    point_cloud=source_point_cloud,
    transformation_matrix=fine_transform,
)
API Reference
Full parameter and return type documentation for register_point_clouds_using_cuboid_translation_sampler_icp.
View 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

Source and Target Point Clouds

Raw sensor input i.e. target point cloud in green and object model in red

Registered Point Clouds

Registered source point cloud (red) aligned to target point cloud (green) using cuboid translation sampler ICP with 3D grid translation search

The Code

python
"""
Demonstrates finding the best alignment by sampling translations in a 3D grid (cuboid) with ICP.
"""

import numpy as np
from loguru import logger
import rerun as rr

from telekinesis import vitreous, datatypes


def register_point_clouds_using_cuboid_translation_sampler_icp_example():
    """
    Finds best alignment by sampling translations in a 3D grid (cuboid) with ICP.

    Tries translations on a regular 3D grid within specified x/y/z ranges, runs ICP
    for each, and keeps best result.
    """
    # ===================== Load Data ==========================================
    source_point_cloud_url = "https://assets.telekinesis.ai/examples/v1/point_clouds/weld_clamp_model_shifted.ply"
    target_point_cloud_url = "https://assets.telekinesis.ai/examples/v1/point_clouds/weld_clamp_cluster_0_centroid_registered.ply"
    source_point_cloud = datatypes.PointCloud.from_url(
        url=source_point_cloud_url, use_cache=True
    )
    target_point_cloud = datatypes.PointCloud.from_url(
        url=target_point_cloud_url, use_cache=True
    )

    # ===================== Run Skill ==========================================
    coarse_transform = (
        vitreous.register_point_clouds_using_cuboid_translation_sampler_icp(
            step_size=5,
            x_min=-20,
            x_max=20,
            y_min=-20,
            y_max=20,
            z_min=-20,
            z_max=20,
            early_stop_fitness_score=0.7,
            min_fitness_score=0.3,
            max_iterations=15,
            max_correspondence_distance=4,
            estimate_scaling=False,
            source_point_cloud=source_point_cloud,
            target_point_cloud=target_point_cloud,
            initial_transformation_matrix=np.eye(4),
        )
    )

    # ===================== Stage 2: Fine Search =================================
    fine_transform = (
        vitreous.register_point_clouds_using_cuboid_translation_sampler_icp(
            step_size=1,
            x_min=-3,
            x_max=3,
            y_min=-3,
            y_max=3,
            z_min=-3,
            z_max=3,
            early_stop_fitness_score=0.85,
            min_fitness_score=0.48,
            max_iterations=40,
            max_correspondence_distance=2,
            estimate_scaling=False,
            source_point_cloud=source_point_cloud,
            target_point_cloud=target_point_cloud,
            initial_transformation_matrix=coarse_transform,
        )
    )
    # ===================== Log ================================================
    logger.success(
        f"Registered {source_point_cloud} to {target_point_cloud} using cuboid translation sampler ICP"
    )
    logger.success(f"Results: {fine_transform}")
    logger.info(f"Transformation matrix data: {fine_transform.data}")
    logger.info(f"Transformation matrix shape: {fine_transform.shape}")
    logger.info(f"Transformation matrix ndim: {fine_transform.ndim}")
    logger.info(f"Transformation matrix dtype: {fine_transform.dtype}")

    # ===================== Visualization  (Optional) ===========================
    aligned_source_point_cloud = vitreous.apply_transform_to_point_cloud(
        point_cloud=source_point_cloud,
        transformation_matrix=fine_transform,
    )

    rr.init(
        "register_point_clouds_using_cuboid_translation_sampler_icp_example", spawn=True
    )
    datatypes.visualize(source_point_cloud, entity_path="/1-before_registration_source")
    datatypes.visualize(target_point_cloud, entity_path="/2-before_registration_target")
    datatypes.visualize(
        aligned_source_point_cloud, entity_path="/3-after_registration_source_aligned"
    )


if __name__ == "__main__":
    register_point_clouds_using_cuboid_translation_sampler_icp_example()

Runnable examples are available in the Telekinesis examples repository.

Follow the README in that repository to set up the environment, run this specific example with:

bash
cd telekinesis-examples
python examples/point_cloud/register_point_clouds_using_cuboid_translation_sampler_icp.py

Parameter Configuration

ParameterTypeDefaultDescription
source_point_clouddatatypes.PointCloudrequiredThe point cloud to align
target_point_clouddatatypes.PointCloudrequiredThe point cloud to align to
initial_transformation_matrixdatatypes.Mat4x4 | np.ndarray | list[list[float]]np.eye(4)A 4x4 transform applied to source_point_cloud before the search — the translation search happens relative to this initial alignment
step_sizedatatypes.Float | float | int0.001Spacing, in the point clouds' coordinate units, between sampled translations on the search grid. Must be > 0
x_mindatatypes.Float | float | int-0.01Lower bound of the translation search along x, relative to initial_transformation_matrix. Must be less than x_max
x_maxdatatypes.Float | float | int0.01Upper bound of the translation search along x. Must be greater than x_min
y_mindatatypes.Float | float | int-0.01Lower bound of the translation search along y. Same behavior as x_min
y_maxdatatypes.Float | float | int0.01Upper bound of the translation search along y. Same behavior as x_max
z_mindatatypes.Float | float | int-0.01Lower bound of the translation search along z. Same behavior as x_min
z_maxdatatypes.Float | float | int0.01Upper bound of the translation search along z. Same behavior as x_max
early_stop_fitness_scoredatatypes.Float | float | int0.5A fitness score in [0, 1] at which the grid search stops early instead of trying every remaining sample
min_fitness_scoredatatypes.Float | float | int0.9Minimum fitness score in [0, 1] the best result must reach to be accepted (see the Returns section below for what happens if it never is)
max_iterationsdatatypes.Int | int50Maximum number of ICP iterations run for each sampled translation. Must be > 0
max_correspondence_distancedatatypes.Float | float | int0.02Maximum distance, in the point clouds' coordinate units, at which two points from the source/target clouds are considered a match during ICP. Must be > 0
estimate_scalingdatatypes.Bool | boolFalseWhether to also estimate and apply a uniform scale factor between the two point clouds, instead of assuming they're at the same scale

Returns

TypeDescription
datatypes.Mat4x4The best-scoring 4x4 transform found by the grid search — this is the transform itself, not an already-transformed point cloud. Pass it to apply_transform_to_point_cloud (as transformation_matrix) to actually move source_point_cloud's points. Use .data for the raw (4, 4) array. If no sampled translation reaches min_fitness_score, this silently returns the identity transform instead of raising — since the winning fitness score isn't exposed, this can't be distinguished from a case where the identity genuinely is the best alignment.

Raises

ExceptionCondition
TypeErrorA parameter's value does not match its expected type (see the Parameter Configuration table above)
ValueErrorinitial_transformation_matrix is not shape (4, 4) (or, for a list input, doesn't contain only numeric elements)
ConfigurationErrorThe TELEKINESIS_API_KEY environment variable is not set
SerializationErrorThe request input failed to serialize, the response was not returned as an Arrow stream, or the response failed to deserialize
RequestTimeoutErrorThe request to the Vitreous service timed out
TransportErrorA network failure occurred before a response was received
ClientErrorThe Vitreous service rejected the request due to invalid or malformed input (HTTP 400/422), an unrecognized endpoint (HTTP 404), or another unexpected 4xx response
AuthenticationErrorThe API key was rejected as invalid or expired (HTTP 401)
AuthenticationServiceErrorThe authentication service returned an invalid response, was temporarily unavailable, or timed out (HTTP 502/503/504)
ServerErrorThe Vitreous service returned a 5xx or otherwise unexpected error response

How to Tune the Parameters

The register_point_clouds_using_cuboid_translation_sampler_icp Skill exposes thirteen tunable parameters that define the translation search grid and control the ICP refinement run at each grid point.

initial_transformation_matrix

  • Controls: The seed transform the translation grid is centered on — every sampled translation is relative to this.
  • Default: np.eye(4) (identity — no pre-alignment)
  • Provide a rough rotation-only alignment here if source_point_cloud and target_point_cloud aren't already close in orientation; this Skill only searches over translation, not rotation.

step_size

  • Controls: Spacing, in the point clouds' coordinate units, between sampled translations on the search grid.
  • Units: The point clouds' coordinate units (e.g. meters or millimeters, whatever unit the input data uses)
  • Default: 0.001
  • Must be > 0
  • Increase → coarser grid — fewer samples, faster, but may miss the optimal alignment
  • Decrease → finer grid — more samples, slower, more thorough
  • Set to roughly 0.5-2x the alignment accuracy you need
  • Typical range: 0.0005-0.01 (in coordinate units matching a meter-scale point cloud) — use 0.0005-0.001 for a precise search, 0.001-0.005 for balanced, 0.005-0.01 for coarse

x_min / x_max, y_min / y_max, z_min / z_max

  • Controls: The bounds of the translation search cuboid along each axis, in the point clouds' coordinate units, relative to initial_transformation_matrix.
  • Units: The point clouds' coordinate units
  • Default: -0.01 / 0.01 on all three axes
  • Each _min bound must be less than its matching _max bound (x_min < x_max, etc.); the _min bound should be negative and the _max bound positive if the true translation could lie in either direction along that axis
  • Typical range: -0.1 to 0.1 (in coordinate units matching a meter-scale point cloud) per bound

early_stop_fitness_score

  • Controls: A fitness score in [0, 1] at which the grid search stops early instead of trying every remaining sample.
  • Units: Dimensionless (fitness score)
  • Default: 0.5
  • Increase → accepts stopping sooner (faster), but may settle for a suboptimal alignment
  • Decrease → requires a better fit before stopping early
  • Typical range: 0.3-0.7 — use 0.3-0.5 for a fast search, 0.5-0.7 for higher quality

min_fitness_score

  • Controls: The minimum fitness score in [0, 1] the best result must reach to be accepted.
  • Units: Dimensionless (fitness score)
  • Default: 0.9
  • If no sampled translation reaches this threshold, the function silently returns the identity transform instead of raising — see the Returns section above
  • Increase → requires higher-quality alignment
  • Decrease → accepts lower quality
  • Typical range: 0.7-0.99 — use 0.7-0.85 for lenient, 0.85-0.95 for balanced, 0.95-0.99 for strict

max_iterations

  • Controls: The maximum number of ICP iterations run for each sampled translation.
  • Units: Iterations (integer)
  • Default: 50
  • Must be > 0
  • Increase → more refinement per sample, but slower overall
  • Decrease → faster
  • Typical range: 10-200 — use 10-30 for fast, 30-50 for balanced, 50-200 for high precision

max_correspondence_distance

  • Controls: The maximum distance, in the point clouds' coordinate units, at which two points from the source/target clouds are considered a match during ICP.
  • Units: The point clouds' coordinate units
  • Default: 0.02
  • Must be > 0
  • Increase → allows matching more distant points, risking incorrect matches
  • Decrease → requires closer matches
  • Set to roughly 2-5x the point spacing
  • Typical range: 0.01-0.1 (in coordinate units matching a meter-scale point cloud)

estimate_scaling

  • Controls: Whether to also estimate and apply a uniform scale factor between the two point clouds, instead of assuming they're at the same scale.
  • Default: False
  • Set to True only if the clouds might genuinely be at different scales.

TIP

Pick x_min/x_max/y_min/y_max/z_min/z_max to bracket your expected translation uncertainty first, then set step_size to roughly 0.5-2x the precision you need within that range. If the clouds aren't already roughly rotationally aligned, resolve that separately (e.g. with register_point_clouds_using_rotation_sampler_icp or a known fixed rotation) — this Skill only searches over translation.

Where to Use the Skill

Common pipelines include:

  • 6D pose estimation with uncertain translation – aligning a reference/CAD model to a sensor scan when the object's position, but not its orientation, on a fixture or conveyor is unknown
  • Multi-view point cloud registration – merging scans captured from positions that are only approximately known
  • Robotic pick-and-place setup – locating a known part whose position varies from scan to scan while its orientation stays consistent
  • Refining a coarse translation guess – following up register_point_clouds_using_centroid_translation when a single centroid match isn't precise enough

Alternative Skills

Skillvs. Register Point Clouds Using Cuboid Translation Sampler ICP
register_point_clouds_using_rotation_sampler_icpSearches over rotations instead of translations. Use it when the clouds are already close in position but the relative rotation is unknown; use this Skill when it's the other way around.
register_point_clouds_using_point_to_point_icpPlain point-to-point ICP from a single initial guess, with no sampling. Use it once you already have a translation estimate good enough for direct convergence; use this Skill first if you don't.
register_point_clouds_using_fast_global_registrationFeature-based global registration that doesn't require any rough pre-alignment at all. A good alternative starting point when neither the translation nor the rotation between the clouds is known.

register_point_clouds_using_centroid_translation is a fast, translation-only coarse-alignment step commonly run before this Skill (their docstrings reference each other directly).

When Not to Use the Skill

Do not use Register Point Clouds Using Cuboid Translation Sampler ICP when:

  • The clouds are already roughly aligned in translation – plain register_point_clouds_using_point_to_point_icp (or register_point_clouds_using_point_to_plane_icp) converges directly, without the extra grid search
  • The rotation between the clouds is unknown, not the translation – use register_point_clouds_using_rotation_sampler_icp instead, which searches rotations
  • You have no rough alignment at all, in rotation or translation – use register_point_clouds_using_fast_global_registration first, since it tolerates large initial misalignment
  • You need a fast result – this Skill runs a full ICP pass for every sampled translation, so it is slower than a single ICP call
  • The translation uncertainty spans a very large volume – the grid may need too many samples at a workable step_size to search effectively
  • The point clouds may be at different scales – set estimate_scaling=True, or pre-scale the clouds; otherwise the search assumes matching scale

TIP

Keep the search cuboid (x_min/x_max/y_min/y_max/z_min/z_max) as tight as your actual translation uncertainty allows — a smaller cuboid at a given step_size means fewer ICP runs and a faster search.