Reference/API

Grasp Optimization

graspkit.stages.grasp_optim.WarpGraspOptimizer(
cfg: GraspOptimConfig,
robot_config: RobotConfig,
device: Device,
fixed_base_robot_config: Optional[RobotConfig] = None,
ik_oversample_ratio: int = 16
)

Batched Warp grasp optimizer: samples init grasps per scene, then runs GraspOptimConfig.optimizer_backend.

Parameters
cfgGraspOptimConfigrequired
robot_configRobotConfigrequired
deviceDevicerequired
fixed_base_robot_configOptional[RobotConfig]= None
ik_oversample_ratioint= 16
Members
attribute[source]
WarpGraspOptimizer.last_snapshots: Optional[torch.Tensor]
method[source]
WarpGraspOptimizer.optimize(
scene_descriptions: List[SceneDescription],
num_samples_per_scene: int,
record: bool = False
) -> torch.Tensor

Optimize grasps for each scene; returns (n_scene, num_samples_per_scene, dof) grasp variables.

Parameters
scene_descriptionsList[SceneDescription]required
num_samples_per_sceneintrequired
recordbool= False
Returns
torch.Tensor
method[source]
WarpGraspOptimizer.compute_costs(
scene_descriptions: List[SceneDescription],
grasps: torch.Tensor
) -> Dict[str, torch.Tensor]

Per-term 0.5·Σr² cost of (n_scene, n, dof) grasps, with contacts re-picked nearest the object.

Parameters
scene_descriptionsList[SceneDescription]required
graspstorch.Tensorrequired
Returns
Dict[str, torch.Tensor]
graspkit.stages.grasp_optim.GraspOptimConfig(
num_iters: int = 6000,
num_contact_points: int = 4,
switch_possibility: float = 0.5,
mu: float = 0.98,
step_size: float = 0.005,
stepsize_period: int = 50,
starting_temperature: float = 18.0,
annealing_period: int = 30,
temperature_decay: float = 0.95,
spen_threshold: float = 0.01,
spen_representation: Literal['capsule', 'sphere'] = 'sphere',
w_fc: float = 1.0,
w_dis: float = 100.0,
w_fingertip_dis: float = 0.0,
w_pen: float = 100.0,
w_spen: float = 10.0,
w_joints: float = 1.0,
w_angle_z: float = 0.0,
angle_z_min: float = math.pi / 2,
hand_axis: Optional[HandAxis] = None,
hand_axis_target: Tuple[float, float, float] = (0.0, 0.0, 1.0),
filter_hand_axis_max_angle_deg: float = 30.0,
optimizer_backend: Literal['warp_gd', 'warp_lm'] = 'warp_gd',
scene_geom_backend: Literal['mesh', 'sdf_volume'] = 'sdf_volume',
sdf_bake_padding: float = 0.25,
sdf_resolution: int = 64,
sdf_voxel_size: Optional[float] = None,
use_link_bounding_sphere_filter: bool = False,
lm_w_pen: float = 150.0,
lm_w_joints: float = 10.0,
lm_w_fingertip_dis: float = 10.0,
lm_collision_margin: float = 0.005,
lm_residual_eps: float = 1e-06,
lm_base_step_scale: float = 0.001,
lm_init_sample_range_q: float = 0.2,
lm_init_sample_range_base: float = 0.05,
enable_ik_scoring: bool = False,
evaluator: Optional[EnvConfig] = None,
sampler_type: Literal['fixed_base', 'wbc', 'floating_base'] = 'fixed_base',
distance_lower: float = 0.02,
distance_upper: float = 0.2,
front_theta_lower: float = -math.pi / 2,
front_theta_upper: float = 0.0,
side_theta_lower: float = -math.pi / 18,
side_theta_upper: float = math.pi / 18,
hand_qpos_jitter_strength: float = 0.05,
hand_qpos_mu: Optional[Tuple[float, ...]] = None,
init_ik_position_tolerance: float = 0.05,
init_ik_orientation_tolerance: float = 0.3,
init_ik_prefer_rest: bool = True,
init_rest_pose_max_dist: float = 3.0,
init_num_ik_candidates_per_sample: int = 50,
init_num_points_per_sample: int = 1000,
init_ik_batch_size: int = 512,
num_samples_per_scene: int = 64,
num_scenes_per_batch: int = 16,
min_grasps_threshold: int = 16,
num_attempts: int = 1,
max_scenes: Optional[int] = None,
save_labels: bool = False
)

Grasp optimizer algorithm and runtime parameters, with hand-independent defaults.

The config of the optimize_grasps stage (ExperimentConfig.grasp_optim); every field is a CLI flag, e.g. --grasp_optim.num_iters 2000. The annealing fields drive warp_gd only and the lm_* fields warp_lm only. The init-sampler fields feed stages/grasp_optim/sampler.py; the runtime fields feed optimize_pipeline.py, not a bare optimize() call.

Parameters
num_itersint= 6000
num_contact_pointsint= 4
switch_possibilityfloat= 0.5
mufloat= 0.98
step_sizefloat= 0.005
stepsize_periodint= 50
starting_temperaturefloat= 18.0
annealing_periodint= 30
temperature_decayfloat= 0.95
spen_thresholdfloat= 0.01
spen_representationLiteral['capsule', 'sphere']= 'sphere'
w_fcfloat= 1.0
w_disfloat= 100.0
w_fingertip_disfloat= 0.0
w_penfloat= 100.0
w_spenfloat= 10.0
w_jointsfloat= 1.0
w_angle_zfloat= 0.0
angle_z_minfloat= math.pi / 2
hand_axisOptional[HandAxis]= None
hand_axis_targetTuple[float, float, float]= (0.0, 0.0, 1.0)
filter_hand_axis_max_angle_degfloat= 30.0
optimizer_backendLiteral['warp_gd', 'warp_lm']= 'warp_gd'
scene_geom_backendLiteral['mesh', 'sdf_volume']= 'sdf_volume'
sdf_bake_paddingfloat= 0.25
sdf_resolutionint= 64
sdf_voxel_sizeOptional[float]= None
use_link_bounding_sphere_filterbool= False
lm_w_penfloat= 150.0
lm_w_jointsfloat= 10.0
lm_w_fingertip_disfloat= 10.0
lm_collision_marginfloat= 0.005
lm_residual_epsfloat= 1e-06
lm_base_step_scalefloat= 0.001
lm_init_sample_range_qfloat= 0.2
lm_init_sample_range_basefloat= 0.05
enable_ik_scoringbool= False
evaluatorOptional[EnvConfig]= None
sampler_typeLiteral['fixed_base', 'wbc', 'floating_base']= 'fixed_base'
distance_lowerfloat= 0.02
distance_upperfloat= 0.2
front_theta_lowerfloat= -math.pi / 2
front_theta_upperfloat= 0.0
side_theta_lowerfloat= -math.pi / 18
side_theta_upperfloat= math.pi / 18
hand_qpos_jitter_strengthfloat= 0.05
hand_qpos_muOptional[Tuple[float, ...]]= None
init_ik_position_tolerancefloat= 0.05
init_ik_orientation_tolerancefloat= 0.3
init_ik_prefer_restbool= True
init_rest_pose_max_distfloat= 3.0
init_num_ik_candidates_per_sampleint= 50
init_num_points_per_sampleint= 1000
init_ik_batch_sizeint= 512
num_samples_per_sceneint= 64
num_scenes_per_batchint= 16
min_grasps_thresholdint= 16
num_attemptsint= 1
max_scenesOptional[int]= None
save_labelsbool= False
Members
attribute[source]
GraspOptimConfig.num_iters: int = 6000

Annealing iterations (warp_gd). Each replays one CUDA graph; fewer trade grasp quality for speed, and 2000 suits smoke tests.

attribute[source]
GraspOptimConfig.num_contact_points: int = 4

Active contact points per grasp, drawn from the robot's contact candidates; they parameterize the distance and force-closure terms. 4-5 fits most five-finger hands.

attribute[source]
GraspOptimConfig.switch_possibility: float = 0.5

Per-grasp probability, each iteration, of re-drawing the whole contact set; the Metropolis test accepts or rejects it together with the pose step.

attribute[source]
GraspOptimConfig.mu: float = 0.98

RMSprop weight on the current squared gradient, ema = mu * g^2 + (1 - mu) * ema. Near 1 the step approaches the gradient's sign times step_size.

attribute[source]
GraspOptimConfig.step_size: float = 0.005

Base RMSprop step; the effective step is step_size * temperature_decay^(t // stepsize_period).

attribute[source]
GraspOptimConfig.stepsize_period: int = 50

Iterations between step-size decays.

attribute[source]
GraspOptimConfig.starting_temperature: float = 18.0

Initial Metropolis temperature T: a proposal that worsens the energy by d is still accepted with probability exp(-d / T).

attribute[source]
GraspOptimConfig.annealing_period: int = 30

Iterations per temperature decay, T = starting_temperature * temperature_decay^(t // annealing_period).

attribute[source]
GraspOptimConfig.temperature_decay: float = 0.95

Decay factor shared by the temperature and step-size schedules; closer to 1 cools slower and explores more.

attribute[source]
GraspOptimConfig.spen_threshold: float = 0.01

Self-collision margin (m) between sphere/capsule pairs; 2 cm pinned the middle finger of most hands off the object.

attribute[source]
GraspOptimConfig.spen_representation: Literal['capsule', 'sphere'] = 'sphere'

Self-collision geometry for warp_gd: per-link capsules or the collision spheres. warp_lm always uses capsules.

attribute[source]
GraspOptimConfig.w_fc: float = 1.0

Force-closure weight: the active contact normals must balance external wrenches. A zero weight disables any term.

attribute[source]
GraspOptimConfig.w_dis: float = 100.0

Weight pulling each active contact point onto the target surface.

attribute[source]
GraspOptimConfig.w_fingertip_dis: float = 0.0

Weight of the fingertip term (warp_gd only; requires fingertip links set): each fingertip link's closest collision sphere is drawn onto the object. 0 disables; a hand should touch without it, this is a last resort.

attribute[source]
GraspOptimConfig.w_pen: float = 100.0

warp_gd scene-penetration weight: hand collision spheres against the target and table, with a fixed 5 mm margin. warp_lm uses lm_w_pen.

attribute[source]
GraspOptimConfig.w_spen: float = 10.0

Self-penetration weight between robot links; zero skips building the term.

attribute[source]
GraspOptimConfig.w_joints: float = 1.0

warp_gd joint-limit weight; q is also clamped to its limits every iteration. warp_lm uses lm_w_joints.

attribute[source]
GraspOptimConfig.w_angle_z: float = 0.0

Weight of the hand-orientation prior (HandUpAngleTask, both backends).

attribute[source]
GraspOptimConfig.angle_z_min: float = math.pi / 2

Minimum angle between the hand-up axis and world +z; tabletop configs use it to forbid underhand grasps.

attribute[source]
GraspOptimConfig.hand_axis: Optional[HandAxis] = None

Hand-axis cone filter on init samples: which hand_coord_spec axis to check, after IK for fixed-base samplers. None disables it. The cone on optimized grasps lives on GraspTrajBuilderConfig.

attribute[source]
GraspOptimConfig.hand_axis_target: Tuple[float, float, float] = (0.0, 0.0, 1.0)

Direction hand_axis should point, in the robot base frame (world for floating hands).

attribute[source]
GraspOptimConfig.filter_hand_axis_max_angle_deg: float = 30.0

Hand-axis cone half-angle in degrees.

attribute[source]
GraspOptimConfig.optimizer_backend: Literal['warp_gd', 'warp_lm'] = 'warp_gd'

warp_gd: simulated-annealing gradient descent for floating or fixed-base robots. warp_lm: multi-seed Levenberg-Marquardt, floating base only, typically 2-3x faster and needs fingertip links; to target an arm, set enable_ik_scoring and let GraspTrajBuilder IK-convert.

attribute[source]
GraspOptimConfig.scene_geom_backend: Literal['mesh', 'sdf_volume'] = 'sdf_volume'

sdf_volume: a cached SDF of the target mesh plus analytic table boxes; fast, accuracy bounded by voxel size, and the target must be the scene's only mesh. mesh: per-scene BVHs, exact but slower.

attribute[source]
GraspOptimConfig.sdf_bake_padding: float = 0.25

SDF bake padding, in mesh units (robokit bakes the unscaled mesh and applies scales at query time).

attribute[source]
GraspOptimConfig.sdf_resolution: int = 64

Voxel grid resolution along the longest axis when sdf_voxel_size is None.

attribute[source]
GraspOptimConfig.sdf_voxel_size: Optional[float] = None

Voxel size in mesh units (before scales). None = derive voxel size from sdf_resolution.

attribute[source]
GraspOptimConfig.lm_w_pen: float = 150.0

warp_lm counterpart of w_pen: scene-collision term weight (LM raw-residual scale).

attribute[source]
GraspOptimConfig.lm_w_joints: float = 10.0

warp_lm counterpart of w_joints: position-limit term weight (LM raw-residual scale).

attribute[source]
GraspOptimConfig.lm_w_fingertip_dis: float = 10.0

warp_lm counterpart of w_fingertip_dis: fingertip-distance term weight (LM raw-residual scale).

attribute[source]
GraspOptimConfig.lm_collision_margin: float = 0.005

warp_lm scene-collision margin (meters).

attribute[source]
GraspOptimConfig.lm_residual_eps: float = 1e-06

warp_lm residual epsilon for distance / position-limit / self-penetration terms.

attribute[source]
GraspOptimConfig.lm_base_step_scale: float = 0.001

warp_lm floating-base step relative to the joint step; keeps base updates from overshooting.

attribute[source]
GraspOptimConfig.lm_init_sample_range_q: float = 0.2

warp_lm per-seed joint half-range: the 24 seeds are the init qpos plus Roberts offsets in [-range, range], clamped to limits.

attribute[source]
GraspOptimConfig.lm_init_sample_range_base: float = 0.05

warp_lm per-seed base-translation sampling half-range (floating base only).

attribute[source]
GraspOptimConfig.enable_ik_scoring: bool = False

warp_lm floating base only: oversample inits 16x, keep those an arm IK can reach, and rank solutions by energy plus reachability. Use when GraspTrajBuilder will IK-convert onto an arm.

attribute[source]
GraspOptimConfig.evaluator: Optional[EnvConfig] = None

In-loop simulation filter: each optimized batch is rolled out (e.g. with perturbation or a heavy object) and only survivors are saved. None gates on motion-plan success only.

attribute[source]
GraspOptimConfig.sampler_type: Literal['fixed_base', 'wbc', 'floating_base'] = 'fixed_base'

fixed_base: look-at poses solved to collision-free arm IK. wbc: world-rooted arms, any azimuth, never from below the table. floating_base: no IK, emits [xyz, euler_rxyz, hand_qpos]; floating optimizers force it.

attribute[source]
GraspOptimConfig.distance_lower: float = 0.02

Lower bound (m) of the hand-to-surface standoff, sampled uniformly. Tight bounds are the usual cause of starved init sampling.

attribute[source]
GraspOptimConfig.distance_upper: float = 0.2

Upper bound (m) of the hand-to-surface standoff.

attribute[source]
GraspOptimConfig.front_theta_lower: float = -math.pi / 2

Lower bound of the roll jitter about the look-at approach axis.

attribute[source]
GraspOptimConfig.front_theta_upper: float = 0.0

Upper bound of the roll jitter about the look-at approach axis.

attribute[source]
GraspOptimConfig.side_theta_lower: float = -math.pi / 18

Lower bound of the tilt jitter about the two axes perpendicular to the approach.

attribute[source]
GraspOptimConfig.side_theta_upper: float = math.pi / 18

Upper bound of the tilt jitter about the two axes perpendicular to the approach.

attribute[source]
GraspOptimConfig.hand_qpos_jitter_strength: float = 0.05

Std of the per-joint Gaussian jitter added to hand_qpos_mu.

attribute[source]
GraspOptimConfig.hand_qpos_mu: Optional[Tuple[float, ...]] = None

Mean hand qpos for init sampling, typically half-open; None uses the robot config's init qpos.

attribute[source]
GraspOptimConfig.init_ik_position_tolerance: float = 0.05

Position tolerance (m) for the init-time arm IK solve (FixedBaseSampler / WBCSampler).

attribute[source]
GraspOptimConfig.init_ik_orientation_tolerance: float = 0.3

Orientation tolerance (rad) for that solve.

attribute[source]
GraspOptimConfig.init_ik_prefer_rest: bool = True

That solve keeps the solution nearest the rest pose; False keeps its best-scoring random seed.

attribute[source]
GraspOptimConfig.init_rest_pose_max_dist: float = 3.0

Reject init IK solutions whose arm-qpos L2 distance from the rest pose exceeds this.

attribute[source]
GraspOptimConfig.init_num_ik_candidates_per_sample: int = 50

IK candidates per requested sample before collision/rest filtering (this × num_samples_per_scene).

attribute[source]
GraspOptimConfig.init_num_points_per_sample: int = 1000

Empty-space points sampled per requested sample when seeding approach directions.

attribute[source]
GraspOptimConfig.init_ik_batch_size: int = 512

Chunk size of the IK + collision sweep over candidates (memory only).

attribute[source]
GraspOptimConfig.num_samples_per_scene: int = 64

Grasps optimized per scene per attempt.

attribute[source]
GraspOptimConfig.num_scenes_per_batch: int = 16

Scenes batched into one solve: num_scenes_per_batch * num_samples_per_scene grasps on the GPU.

attribute[source]
GraspOptimConfig.min_grasps_threshold: int = 16

A scene is done once its output holds this many grasps; scenes below it are re-attempted.

attribute[source]
GraspOptimConfig.num_attempts: int = 1

Top-up passes over unfinished scenes; attempt i runs with seed + i.

attribute[source]
GraspOptimConfig.max_scenes: Optional[int] = None

If set, caps scenes processed per attempt (after sort/shuffle/rank slicing). Useful for quick testing.

attribute[source]
GraspOptimConfig.save_labels: bool = False

Save all candidates and their filter/plan/simulation verdicts to <grasp_output_dir>.labels/.

graspkit.stages.grasp_optim.GraspRobot(
spec: RobotSpec,
config: Optional[RobotConfig] = None,
device: Device = 'cpu'
)
Bases: Robot

Robot equipped with the grasp-pipeline torch facade and contact-point candidates.

Parameters
specRobotSpecrequired
configOptional[RobotConfig]= None
deviceDevice= 'cpu'
Members
property[source]
GraspRobot.actuated_joint_names: list[str]
method[source]
GraspRobot.from_config(
config: RobotConfig,
base_link_name: Optional[str] = None,
ee_link_names: Optional[Union[Tuple[str, ...], str]] = None,
load_meshes: bool = False,
load_collision_spheres: bool = False,
device: Device = 'cpu',
prune: bool = True,
self_collision_representation: Literal['sphere', 'capsule'] = 'sphere'
) -> GraspRobot

Build (and cache) a GraspRobot from RobotConfig; pruned to ee_link_names, prune=False for the full robot.

Parameters
configRobotConfigrequired
base_link_nameOptional[str]= None
ee_link_namesOptional[Union[Tuple[str, ...], str]]= None
load_meshesbool= False
load_collision_spheresbool= False
deviceDevice= 'cpu'
prunebool= True
self_collision_representationLiteral['sphere', 'capsule']= 'sphere'
Returns
GraspRobot
method[source]
GraspRobot.self_collision_ignored_pairs(
representation: Literal['sphere', 'capsule']
) -> Int[np.ndarray, 'pairs 2']

Load representation-specific pairs against this robot's pruned link names.

Parameters
representationLiteral['sphere', 'capsule']required
Returns
Int[np.ndarray, 'pairs 2']
property[source]
GraspRobot.root_pose_7d: Float[torch.Tensor, 7]
property[source]
GraspRobot.root_pose_mat: Float[torch.Tensor, '4 4']
property[source]
GraspRobot.actuated_joint_limits_torch: Float[torch.Tensor, 'num_dofs 2']
property[source]
GraspRobot.init_qpos: Float[torch.Tensor, num_dofs]
method[source]
GraspRobot.forward_kinematics_torch(
variables: Float[torch.Tensor, '... num_dofs'],
root_poses: Optional[Float[torch.Tensor, '... 4 4']] = None
) -> Float[torch.Tensor, '... num_links 4 4']

Torch FK. Two cases:

  1. floating base: variables packs free qpos (xyz + rxyz or xyz + 6d) then actuated qpos (root_poses must not be provided).
  2. fixed base: variables is just qpos (root_poses optional).
Parameters
variablesFloat[torch.Tensor, '... num_dofs']required
root_posesOptional[Float[torch.Tensor, '... 4 4']]= None
Returns
Float[torch.Tensor, '... num_links 4 4']
method[source]
GraspRobot.inverse_kinematics(
target_poses: Float[torch.Tensor, '... 4 4'],
init_qpos: Optional[Float[torch.Tensor, '... num_arm_dofs']] = None,
position_tolerance: float = 0.05,
orientation_tolerance: float = 0.3,
init_sample_range: float = 0.5,
use_score_rest: bool = False,
locked_joint_indices: Optional[Tuple[int, ...]] = None
) -> Tuple[Float[torch.Tensor, '... num_arm_dofs'], Bool[torch.Tensor, ...]]
Parameters
target_posesFloat[torch.Tensor, '... 4 4']required
init_qposOptional[Float[torch.Tensor, '... num_arm_dofs']]= None
position_tolerancefloat= 0.05
orientation_tolerancefloat= 0.3
init_sample_rangefloat= 0.5
use_score_restbool= False
locked_joint_indicesOptional[Tuple[int, ...]]= None
Returns
Tuple[Float[torch.Tensor, '... num_arm_dofs'], Bool[torch.Tensor, ...]]
property[source]
GraspRobot.arm_joint_indices: torch.Tensor
property[source]
GraspRobot.hand_joint_indices: torch.Tensor
property[source]
GraspRobot.ignore_joint_indices: torch.Tensor

Actuated joints that are neither arm nor hand (complement of arm ∪ hand indices).

property[source]
GraspRobot.canonical_collision_spheres: Tuple[torch.Tensor, torch.Tensor]

Link-local collision spheres from collision_spheres_path: (num_spheres,4) centers+radius, link indices.

method[source]
GraspRobot.get_collision_spheres(
link_poses: Float[torch.Tensor, '... num_links 4 4']
) -> Float[torch.Tensor, '... num_spheres 4']
Parameters
link_posesFloat[torch.Tensor, '... num_links 4 4']required
Returns
Float[torch.Tensor, '... num_spheres 4']
method[source]
GraspRobot.get_contact_points(
link_poses: Float[torch.Tensor, '... num_links 4 4'],
contact_point_indices: Int[torch.Tensor, '... num_contact_points']
) -> Float[torch.Tensor, '... num_contact_points 3']
Parameters
link_posesFloat[torch.Tensor, '... num_links 4 4']required
contact_point_indicesInt[torch.Tensor, '... num_contact_points']required
Returns
Float[torch.Tensor, '... num_contact_points 3']
method[source]
GraspRobot.get_contact_candidates(
config: RobotConfig,
device: Device = 'cpu'
) -> Tuple[torch.Tensor, torch.Tensor]

Canonical contact candidates: (N,3) link-local centers, (N,) link indices in the chain robot's link order.

Parameters
configRobotConfigrequired
deviceDevice= 'cpu'
Returns
Tuple[torch.Tensor, torch.Tensor]