Skip to content

Operators

The geometric operators the models are built from, in their pure-PyTorch reference form. Compiled backends, conventions and speed: Operators and backends.

grasppanda.ops

Geometric operators.

Every function here has a pure-PyTorch reference implementation that runs on CPU and defines correct behaviour. Furthest point sampling, the ball and cylinder queries and kNN can also run on optional compiled kernels, which are used only after reproducing the reference exactly; :mod:grasppanda.ops.backends selects and reports them.

VoxelGrid

Bases: NamedTuple

Result of :func:voxelize.

Attributes:

Name Type Description
voxels Tensor

(V, 4) integer [batch, x, y, z] of the occupied voxels, sorted.

inverse Tensor

(N,) voxel row of every input point; voxel_feats[inverse] maps voxel features back to exactly the points that were voxelised.

first Tensor

(V,) index of the first input point falling in each voxel.

inverse_distance_weights(distance, *, power=1.0, eps=1e-08)

Normalised weights w ∝ 1 / (distance ** power + eps) over the last axis.

Parameters:

Name Type Description Default
distance Tensor

(..., K) Euclidean distances to the K neighbours, as returned by :func:grasppanda.ops.knn.

required
power float

Exponent p. 1.0 is the rule of the PyTorch PointNet++ ports; 2.0 is the rule of the paper.

1.0
eps float

Added to the denominator so that a coincident neighbour (distance 0) receives almost all the weight instead of dividing by zero.

1e-08

Returns:

Type Description
Tensor

(..., K) weights that sum to one.

three_interpolate(feats, index, weight)

Weighted sum of neighbour features.

out[b, n] = Σₖ weight[b, n, k] · feats[b, index[b, n, k]]. The name is the PointNet++ one; any number of neighbours K is accepted.

Parameters:

Name Type Description Default
feats Tensor

(B, M, C) features of the known points.

required
index Tensor

(B, N, K) indices into the known points.

required
weight Tensor

(B, N, K) weights, usually from :func:inverse_distance_weights.

required

Returns:

Type Description
Tensor

(B, N, C) interpolated features.

grasp_nms(translation, rotation, score, *, translation_threshold=0.03, rotation_threshold=math.radians(30.0))

Greedy suppression of near-duplicate grasps.

Grasps are visited in descending score order (stable, so ties keep input order). A kept grasp suppresses every later grasp whose translation distance is strictly below translation_threshold and whose rotation angle is strictly below rotation_threshold.

This operator is a model post-processor. Benchmark evaluators keep their own pinned copy of the rule, so changing this function can never change a reported AP.

Parameters:

Name Type Description Default
translation Tensor

(G, 3) metres.

required
rotation Tensor

(G, 3, 3).

required
score Tensor

(G,).

required

Returns:

Type Description
Tensor

Indices of the kept grasps, in descending score order.

ball_query(query, support, radius, nsample, *, chunk=256)

Support points strictly within radius of every query point.

Returns:

Type Description
Tensor

(index, hits): (B, M, nsample) indices and (B, M) hit counts

Tensor

(capped at nsample).

box_query(query, support, rotation, lower, upper, nsample, *, chunk=256)

Support points inside an oriented box at every query point.

The box is axis-aligned in the frame of its query: a support point p is inside when d = Rᵀ (p - q) satisfies lower < d < upper on all three axes (strict). Column 0 of rotation is the approach axis, as for :func:cylinder_query, and the conventions for the returned members are the same (first hits in index order, missing slots repeat the first, no hit returns index 0).

Parameters:

Name Type Description Default
query Tensor

(B, M, 3) box origins.

required
support Tensor

(B, N, 3) points to search.

required
rotation Tensor

(B, M, 3, 3) frame per query.

required
lower tuple[float, float, float]

Lower corner of the box in its frame, metres.

required
upper tuple[float, float, float]

Upper corner of the box in its frame, metres.

required

Returns:

Type Description
Tensor

(index, hits): (B, M, nsample) indices and (B, M) hit counts

Tensor

(capped at nsample).

cylinder_query(query, support, rotation, radius, hmin, hmax, nsample, *, chunk=256)

Support points inside an oriented cylinder at every query point.

The cylinder axis is column 0 of rotation (the approach axis). A support point p is inside when, with d = Rᵀ (p - q), hmin < d.x < hmax and d.y² + d.z² < radius² (all strict).

Parameters:

Name Type Description Default
query Tensor

(B, M, 3) cylinder origins.

required
support Tensor

(B, N, 3) points to search.

required
rotation Tensor

(B, M, 3, 3) frame per query.

required

Returns:

Type Description
Tensor

(index, hits): (B, M, nsample) indices and (B, M) hit counts

Tensor

(capped at nsample).

gather_points(values, index)

Select (B, M, C) rows of values (B, N, C) with index (B, M).

group_points(values, index)

Select (B, M, K, C) neighbourhoods of values (B, N, C) with index (B, M, K).

knn(query, support, k, *, chunk=1024)

k nearest support points of every query point.

Indices are 0-based and equidistant neighbours are ordered by index.

Returns:

Type Description
tuple[Tensor, Tensor]

(distance, index), both (B, M, k); distances are Euclidean.

furthest_point_sample(points, num_samples, *, skip_near_origin=False, tie_break='lowest_index')

Iterative furthest point sampling.

The first sample is always index 0 and ties are resolved by a fixed rule, so the result is deterministic and the same on every device. Which points become seeds decides whether a ported checkpoint reproduces, so faster backends are held to exactly this behaviour (:mod:grasppanda.ops.backends).

Parameters:

Name Type Description Default
points Tensor

(B, N, 3) or (N, 3).

required
num_samples int

Number of points to pick, at most N.

required
skip_near_origin bool

Never pick points whose squared norm is ≤ 1e-3 (apart from index 0), as the widely used PointNet++ CUDA kernel does. Needed only for parity with checkpoints trained against that kernel.

False
tie_break Literal['lowest_index', 'pointnet2_cuda']

Which point wins when several are equally far from the selected set. lowest_index takes the first. pointnet2_cuda reproduces the order of the PointNet++ CUDA kernel (bit-reversed thread id, then index; the kernel runs min(512, 2^floor(log2 N)) threads). Exact ties are not exotic: clouds from a depth camera lie on a lattice, and 3 of 64 sampled GraspNet-1Billion frames had one among their first 1024 samples.

'lowest_index'

Returns:

Type Description
Tensor

(B, num_samples) or (num_samples,) indices into points.

generalized_furthest_point_sample(coords, num_samples, *, feats=None, metric='concat', coord_weight=1.0, weights=None, start=None)

Furthest point sampling of one cloud under a feature distance and with point weights.

The generalisation the detection literature uses to make the sampler choose instead of only spread: a distance that includes features (F-FPS of 3DSSD) and a weight on the running distance of every point (S-FPS of SASA). Without features and weights it is :func:furthest_point_sample with tie_break="lowest_index". A picked point is never picked again, so the indices are distinct even on duplicated coordinates.

Parameters:

Name Type Description Default
coords Tensor

(N, 3) coordinates.

required
num_samples int

Points to pick, at most N.

required
feats Tensor | None

(N, C) features that enter the distance; None for coordinates only.

None
metric Literal['concat', 'sum']

With features. concat: squared L2 distance of the concatenation [coord_weight * xyz, feats] (what the released 3DSSD code computes). sum: coord_weight * |Δxyz| + |Δfeats| with L2 norms (Eq. 1 of the 3DSSD paper, its λ being coord_weight).

'concat'
coord_weight float

Weight of the coordinates against the features.

1.0
weights Tensor | None

(N,) non-negative weights; the next pick maximises weight × running distance. The running distance is the squared one unless metric="sum" (the convention of the released SASA kernel).

None
start int | None

Index of the first pick. None: the heaviest point when weights are given (SASA), otherwise index 0.

None

Returns:

Type Description
Tensor

(num_samples,) distinct indices into coords, in the order they were picked.

hilbert_code(grid, depth)

Hilbert-curve index of non-negative integer coordinates.

Parameters:

Name Type Description Default
grid Tensor

(N, 3) integer coordinates in [0, 2**depth).

required
depth int

Number of bits per axis. The curve depends on it: the same cell has a different index on a deeper curve.

required

Returns:

Type Description
Tensor

(N,) int64 indices in [0, 8**depth).

serialization_code(grid, depth, order='z', batch_index=None)

Sort key of every voxel along one space-filling curve.

Parameters:

Name Type Description Default
grid Tensor

(N, 3) integer coordinates in [0, 2**depth).

required
depth int

Number of bits per axis.

required
order SerializationOrder

Curve. The -trans variants swap x and y before encoding.

'z'
batch_index Tensor | None

(N,) sample index of every voxel. When given it occupies the bits above the curve code, so sorting the codes keeps samples contiguous.

None

Returns:

Type Description
Tensor

(N,) int64 codes.

z_order_code(grid, depth)

Morton code of non-negative integer coordinates.

Parameters:

Name Type Description Default
grid Tensor

(N, 3) integer coordinates in [0, 2**depth).

required
depth int

Number of bits per axis.

required

Returns:

Type Description
Tensor

(N,) int64 codes in [0, 8**depth).

approach_to_rotation(approach, angle=None)

Gripper frame from an approach direction and an in-plane rotation.

Column 0 of the result is the normalised approach axis. Column 1 is horizontal ([-a_y, a_x, 0] normalised, or [0, 1, 0] when the approach is vertical), column 2 completes the right-handed frame, and the frame is then rotated by angle about the approach axis.

Parameters:

Name Type Description Default
approach Tensor

(..., 3) direction from the gripper towards the object.

required
angle Tensor | None

(...) in-plane rotation in radians; zero when omitted.

None

Returns:

Type Description
Tensor

(..., 3, 3) rotation matrices.

fibonacci_views(num_views=300, *, dtype=torch.float32)

Viewpoints on the unit sphere from a Fibonacci lattice.

This is the view grid of the GraspNet-1Billion grasp labels: view i of the label tensors is row i here. The approach direction of a view is the vector from the viewpoint to the origin, i.e. -views[i].

Returns:

Type Description
Tensor

(num_views, 3) unit vectors.

healpix_views(nside=4, *, dtype=torch.float32)

Viewpoints at the pixel centres of a HEALPix grid (through healpix).

HEALPix cuts the sphere into 12 · nside**2 pixels of exactly equal area on rings of constant latitude: nside 2, 4, 5, 8 give 48, 192, 300, 768 views. Pixels are in RING order.

Returns:

Type Description
Tensor

(V, 3) unit vectors.

icosphere_views(subdivisions=2, *, dtype=torch.float32)

Viewpoints at the vertices of a subdivided icosahedron (through trimesh).

subdivisions 0, 1, 2, 3, 4 give 12, 42, 162, 642, 2562 views; the count is 10 · 4**subdivisions + 2. The vertices are almost evenly spaced and, unlike a Fibonacci lattice, come with a mesh (every view knows its neighbours).

Returns:

Type Description
Tensor

(V, 3) unit vectors.

nearest_view(directions, views)

(...) index of the row of views closest in angle to each direction.

Parameters:

Name Type Description Default
directions Tensor

(..., 3) vectors (any length above zero).

required
views Tensor

(V, 3) unit vectors.

required

rotation_angle(first, second)

Angle in radians of the rotation taking first to second ((..., 3, 3)).

two_vector_rotation(first, second)

Rotation whose first column is first and whose second lies towards second.

Gram-Schmidt on two free 3-vectors: first is normalised, second loses its component along it and is normalised, the third column is their cross product. This is the mapping of the 6-D rotation representation (Zhou et al., CVPR 2019), and, with the roles of the two vectors exchanged, how Contact-GraspNet builds a grasp frame from a predicted baseline and approach.

Returns:

Type Description
Tensor

(..., 3, 3) rotation matrices with columns first, second', first × second'.

voxelize(coords, voxel_size, batch_index=None)

Quantise points to a regular grid by flooring coords / voxel_size.