FoundationPose Technical Reference¶
spatialhub.models.foundationpose provides an ONNX Runtime adapter for FoundationPose, a unified model-based 6D object pose estimation and tracking pipeline for novel and known objects using RGB-D observations and 3D CAD models.
Supported Model Components¶
FoundationPose decomposes 6D object pose estimation into two neural network stages:
| Component | ONNX File | Default Hugging Face Source | Description |
|---|---|---|---|
RefineNet (refine_net) |
refine_net.onnx |
SpatialHub/foundationpose |
Iterative neural pose refiner predicting egocentric \(SE(3)\) transformation deltas \((\Delta R, \Delta t)\). |
ScoreNet (score_net) |
score_net.onnx |
SpatialHub/foundationpose |
Pairwise comparison scoring network that ranks candidate pose hypotheses via progressive tournament elimination. |
Overview & Mathematical Pipeline¶
FoundationPose operates on RGB images, metric depth maps, 2D segmentation masks, 3D CAD models, and camera intrinsic matrices.
Candidate Pose Initialization & Rotation Grid¶
During registration, a 2D bounding box is extracted from the segmentation mask. Translation \(t_{\text{guess}}\) is initialized by reprojecting the mask centroid and median depth. A rotation grid of \(N\) viewpoints is sampled across \(SO(3)\) to initialize candidate poses \(T_i = [R_i \mid t_{\text{guess}}]\).
Iterative Egocentric Pose Refinement (PoseRefinePredictor)¶
At each iteration \(k\), the candidate pose \(T^{(k)} \in SE(3)\) is used to render synthetic RGB and 3D coordinate G-Buffer templates \((A)\) using ModernGL. Observed crops \((B)\) and synthetic templates \((A)\) are passed to RefineNet to regress egocentric delta poses \((\Delta r, \Delta t)\):
where \(\Delta T = [ \mathbf{R}(\Delta r) \mid \Delta t ]\), converting the predicted rotation vector \(\Delta r \in \mathbb{R}^3\) to a rotation matrix \(\mathbf{R}(\Delta r)\).
Tournament Selection (ScorePredictor)¶
Candidate pose hypotheses are evaluated in batched tournament comparisons using ScoreNet. Pairwise score logits determine progressive candidate elimination until the global top-scoring pose is identified:
Headless ModernGL Rendering & Filtering¶
- Atlas Rendering (
Renderer): Batches \(N\) candidate viewpoints into multi-target G-Buffer attachments (RGBA + XYZ coordinates) in a single instanced GPU draw call. - Depth Filtering (
DepthFilter): Executes GPU-accelerated morphological erosion and bilateral smoothing shader passes over observed depth maps to mitigate sensor noise and edge artifacts.
ONNX Export Guide¶
Export FoundationPose RefineNet and ScoreNet PyTorch models to ONNX format using the centralized export utility:
uv run tools/export/export_foundationpose.py \
--weights-dir ./upstream/foundationpose/weights \
--output-folder onnx_weight \
--opset 18
| Parameter | Type | Default | Description |
|---|---|---|---|
--weights-dir |
str |
"./weights" |
Directory containing model checkpoint folders. |
--refine-run-name |
str |
"2023-10-28-18-33-37" |
RefineNet checkpoint directory name. |
--score-run-name |
str |
"2024-01-11-20-02-45" |
ScoreNet checkpoint directory name. |
--output-folder |
str |
onnx_weight |
Destination directory for exported .onnx model files. |
--opset |
int |
18 |
ONNX Operator Set version. |
SpatialHub Adapter API & Usage¶
6D Pose Registration¶
import numpy as np
from spatialhub import FoundationPose
# Camera intrinsic matrix
K = np.array([
[572.41, 0.0, 325.26],
[ 0.0, 573.57, 242.04],
[ 0.0, 0.0, 1.0]
], dtype=np.float32)
# Initialize FoundationPose adapter
with FoundationPose(
mesh_file_path="cad_models/object.ply",
camera_intrinsic=K,
model_unit="mm",
providers=["CUDAExecutionProvider", "CPUExecutionProvider"],
) as engine:
# Register 6D pose from RGB, Depth, and 2D Segmentation Mask
result = engine.register(
rgb="scene_rgb.png",
depth="scene_depth.png", # Supports .npy or 16-bit PNG (mm)
mask="object_mask.png",
iteration=5,
)
print(f"Estimated Pose Matrix:\n{result.best_pose}")
print(f"Confidence Score: {result.best_score}")
# Visualize projected 3D bounding box and XYZ axes
result.visualize(draw_bbox=True, draw_axes=True, save_path="pose_estimation.png")
6D Pose Tracking¶
# Track pose across consecutive video frames
tracked_result = engine.track(
rgb="frame_0002_rgb.png",
depth="frame_0002_depth.png",
previous_pose=result.best_pose,
iteration=2,
)
tracked_result.visualize(save_path="tracked_pose.png")
Returned Result Data Structure¶
Returns a PoseEstimationResult dataclass:
| Attribute | Type | Shape | Description |
|---|---|---|---|
image |
np.ndarray |
(H, W, 3) uint8 |
Input RGB image array. |
poses |
np.ndarray |
(N, 4, 4) float32 |
Estimated 4x4 object-to-camera transformation matrices. |
intrinsics |
np.ndarray |
(3, 3) float32 |
Camera intrinsic matrix. |
scores |
np.ndarray | None |
(N,) float32 |
Confidence scores associated with candidate poses. |
labels |
list[str] | None |
Length N |
Object model identifiers. |
bbox_3d |
np.ndarray | None |
(N, 8, 3) or (8, 3) float32 |
3D bounding box corners in canonical centered mesh space. |
to_origin |
np.ndarray | None |
(N, 4, 4) or (4, 4) float32 |
Centering transform matrix for coordinate axis positioning. |