Skip to content

simulo.SurfaceGripperActuator

Surface gripper actuator.

A surface gripper uses physics constraints to attach to nearby objects. The gripper has three states: OPEN, CLOSING, CLOSED.

Command Interface:

  • command < -0.3: Open the gripper
  • -0.3 ≤ command ≤ 0.3: Idle (hold state)
  • command > 0.3: Close the gripper

Pass a GripperCommand for scripted intent or a raw float for a policy’s continuous action — see that enum for which to use when.

This band is the engine’s and is not configurable here. Unlike ParallelGripperActuator, whose dead band this SDK evaluates itself, a surface gripper’s command is buffered and classified inside the engine, against ±0.3 written into the engine’s own source. This class therefore takes no open_threshold / close_threshold: it used to accept both, assign both, and read neither, which made the knob a promise nothing kept. Passing either now raises TypeError rather than being silently ignored.

State Interface:

  • OPEN (-1): Gripper is open, not grasping
  • CLOSING (0): Gripper is attempting to grasp
  • CLOSED (1): Gripper has grasped an object

Example:

import simulo
# Create gripper
gripper = simulo.SurfaceGripperActuator(
max_grip_distance=0.1,
shear_force_limit=500.0
)
# Add to scene (path is set here, consistent with robots)
scene.add(gripper, at="/World/Robot/ee_link/Gripper")
# Control gripper (commands are automatically flushed before world.step())
gripper.set_command(simulo.GripperCommand.CLOSE)
# No write_to_sim() needed - automatically flushed before physics step
world.step()
gripper.update(dt)
states = gripper.get_state()

Backend Mapping: IsaacLab: Maps to isaaclab.assets.SurfaceGripper. Commands are forwarded verbatim to set_grippers_command, which documents the fixed band quoted above and hands the raw value on to Isaac Sim’s gripper view — which is why this class does not rescale or re-band a command on the way through.

Note: Path patterns (e.g., /World/Robot.*/gripper) are a special optimization for batch operations and RL training scenarios. For typical applications with independent control per robot, create separate actuator instances:

# Typical pattern: one actuator per robot
gripper_1 = simulo.SurfaceGripperActuator(...)
scene.add(gripper_1, at="/World/Robot1/gripper")
gripper_2 = simulo.SurfaceGripperActuator(...)
scene.add(gripper_2, at="/World/Robot2/gripper")

Attributes:

  • path — Scene path where gripper is attached. Supports wildcard patterns (e.g., .*) for multi-instance batch operations, but typically use one actuator per robot.
  • max_grip_distance — Maximum distance to grasp objects (meters)
  • shear_force_limit — Force limit perpendicular to gripper axis (N)
  • coaxial_force_limit — Force limit along gripper axis (N)
  • retry_interval — Time gripper stays in grasping state (seconds)
simulo.SurfaceGripperActuator(
path: Optional[str] = None,
max_grip_distance: float = 0.1,
shear_force_limit: float = 500.0,
coaxial_force_limit: float = 500.0,
retry_interval: float = 0.1,
enabled: bool = True,
)
SurfaceGripperActuator.set_command(command: Union[GripperCommand, float]) -> None

Set gripper command for single instance.

Command behavior (the engine’s fixed band — see the class docstring):

  • command < -0.3: Open gripper
  • -0.3 ≤ command ≤ 0.3: Idle (maintain state)
  • command > 0.3: Close gripper

Args:

  • command — A GripperCommand (OPEN / IDLE / CLOSE) for scripted intent, or a raw command value in range [-1, 1] — typically a policy’s continuous action, which is why the dead band above exists. GripperCommand is an IntEnum, so it travels the same float path with no conversion.

Raises:

  • TypeError — If given a GripperState. Both enums are IntEnum, so a state passed where a command belongs would otherwise be accepted and silently mean something else.
SurfaceGripperActuator.set_commands(commands: Sequence[Union[GripperCommand, float]]) -> None

Set commands for multiple gripper instances.

Args:

  • commands — One command value per instance — each a GripperCommand or a raw float (the two may be mixed).

Raises:

  • ValueError — If the count does not match the instance count. The check is unconditional: it used to be skipped whenever the count was one, which is also what “not attached yet” reports, so an over-long batch reached the engine and failed there as a tensor-shape error naming a mask instead of the input.
  • TypeError — If any element is a GripperState.
SurfaceGripperActuator.get_state() -> List[GripperState]

Get current gripper state for all instances.

Returns:

List of GripperState values, one per instance: - OPEN (-1): Not grasping - CLOSING (0): Attempting to grasp - CLOSED (1): Grasping object Before the first update() the list is the pre-attach placeholder — one OPEN per instance the actuator knows about, which is a single entry until the engine attaches it. Read it after update(dt), not before, if you need one entry per environment.

SurfaceGripperActuator.is_open(instance: int = 0) -> bool

Check if gripper instance is open.

Args:

  • instance — Gripper instance index

Returns:

True if gripper is open

SurfaceGripperActuator.is_closed(instance: int = 0) -> bool

Check if gripper instance is closed (grasping).

Args:

  • instance — Gripper instance index

Returns:

True if gripper is closed/grasping

SurfaceGripperActuator.update(dt: float) -> None

Update gripper state from simulation.

Args:

  • dt — Time step in seconds
SurfaceGripperActuator.reset(env_ids: Optional[Any] = None) -> None

Reset gripper instances to the open state.

Args:

  • env_ids — Instance indices to reset — a tensor, a sequence of ints, or None for all of them. Pass the ids. RL environments reset per-environment (Task.reset_idx(env_ids)), and this actuator is not registered with the scene, so nothing resets it on your behalf; an all-instances reset triggered by one environment terminating physically opens every other gripper mid-grasp. The engine’s own SurfaceGripper.reset(indices=...) has taken indices all along — this signature is what makes them reachable.