Implementation:Haosulab ManiSkill ActorBuilder TableSceneBuilder
| Field | Value |
|---|---|
| Page Type | Implementation (API Doc) |
| Title | ManiSkill ActorBuilder and TableSceneBuilder |
| Domain | Simulation, Robotics, Environment_Design, Physics_Simulation |
| Related Principle | Principle:Haosulab_ManiSkill_Scene_Object_Loading |
| Source Files | mani_skill/utils/building/actor_builder.py (L21-261), mani_skill/utils/scene_builder/table/scene_builder.py (L16-66)
|
| Date | 2026-02-15 |
| Repository | Haosulab/ManiSkill |
Overview
Description
ActorBuilder is a ManiSkill extension of SAPIEN's ActorBuilder that supports batched construction of rigid-body actors across multiple parallel sub-scenes. It provides a fluent API for adding collision shapes (box, sphere, capsule, convex mesh, etc.), visual shapes (from files or procedural geometry), and configuring physics body types (dynamic, kinematic, static). The built actor is automatically replicated across all parallel environments unless restricted via set_scene_idxs().
TableSceneBuilder is a pre-built scene template that creates a standard tabletop workspace. It loads a table mesh, positions it so that z=0 corresponds to the table surface, creates a ground plane, and provides robot-specific initialization for supported robot models (Panda, Fetch, xArm6, etc.).
Usage
ActorBuilder is obtained from the scene; TableSceneBuilder is imported directly.
# ActorBuilder -- obtained from scene
builder = self.scene.create_actor_builder()
# TableSceneBuilder -- imported directly
from mani_skill.utils.scene_builder.table import TableSceneBuilder
Code Reference
ActorBuilder (mani_skill/utils/building/actor_builder.py)
class ActorBuilder(SAPIENActorBuilder):
"""
ActorBuilder class to flexibly build actors in both CPU and GPU simulations.
Inherits the original SAPIEN ActorBuilder and changes the build functions
to support a batch of scenes and return a batch of Actors.
"""
scene: ManiSkillScene
def __init__(self):
super().__init__()
self.initial_pose = None # Pose: initial pose for the actor
self.scene_idxs = None # Optional[Tensor]: which parallel envs to build in
self._allow_overlapping_plane_collisions = False
def set_scene_idxs(self, scene_idxs=None):
"""Restrict this actor to specific parallel environment indices."""
...
def build(self, name: str) -> Actor:
"""Build the actor with the given unique name. Returns an Actor object."""
...
def build_static(self, name: str) -> Actor:
"""Build as a static (immovable) body."""
...
def build_kinematic(self, name: str) -> Actor:
"""Build as a kinematic (programmatically movable) body."""
...
def build_dynamic(self, name: str) -> Actor:
"""Build as a dynamic (physics-driven) body."""
...
Key inherited methods from SAPIEN ActorBuilder (used before calling .build()):
| Method | Description |
|---|---|
add_box_collision(half_size, ...) |
Add a box collision shape |
add_box_visual(half_size, color, ...) |
Add a box visual shape |
add_sphere_collision(radius, ...) |
Add a sphere collision shape |
add_sphere_visual(radius, color, ...) |
Add a sphere visual shape |
add_capsule_collision(radius, half_length, ...) |
Add a capsule collision shape |
add_convex_collision_from_file(filename, scale, ...) |
Add a convex mesh collision from file |
add_visual_from_file(filename, scale, ...) |
Add a visual mesh from file |
add_nonconvex_collision_from_file(filename, scale, ...) |
Add a triangle mesh collision from file |
set_mass(mass) |
Set mass of the actor |
set_physx_body_type(type) |
Set body type: "dynamic", "kinematic", or "static" |
TableSceneBuilder (mani_skill/utils/scene_builder/table/scene_builder.py)
class TableSceneBuilder(SceneBuilder):
"""A simple scene builder that adds a table to the scene such that
the height of the table is at z=0, and gives reasonable initial
poses for robots."""
def build(self):
"""Build the table, ground plane, and store references to
self.table, self.ground, self.scene_objects.
Also computes self.table_length, self.table_width, self.table_height."""
...
def initialize(self, env_idx: torch.Tensor):
"""Set initial poses for the table and robot based on the
robot_uids of the environment. Supports: panda, panda_wristcam,
fetch, xarm6 variants, panda_stick, widowxai, so100, and others."""
...
I/O Contract
ActorBuilder.build()
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
str |
Yes | Unique name for the actor within the scene |
Preconditions:
- The builder must be created via
self.scene.create_actor_builder()so thatbuilder.sceneis set. - At least one collision or visual shape should be added before building.
initial_poseshould be set to prevent physics instabilities (a warning is logged if omitted).- The name must be unique -- building with a duplicate name raises an
AssertionError.
Returns: An Actor object representing the built rigid body across all (or selected) parallel environments.
Side effects:
- Adds the actor to
self.scene.actors[name]. - Registers the actor in the scene's state dict registry.
- Creates SAPIEN entities in the appropriate sub-scenes.
TableSceneBuilder Constructor
| Parameter | Type | Required | Description |
|---|---|---|---|
env |
BaseEnv |
Yes | The environment instance |
robot_init_qpos_noise |
float |
No | Standard deviation of noise added to robot initial joint positions (default: 0.02) |
Usage Examples
Building a Custom Cube Actor
def _load_scene(self, options: dict):
# Use TableSceneBuilder for the workspace
self.table_scene = TableSceneBuilder(
env=self, robot_init_qpos_noise=self.robot_init_qpos_noise
)
self.table_scene.build()
# Build a custom cube using the raw ActorBuilder API
builder = self.scene.create_actor_builder()
builder.add_box_collision(half_size=[0.02, 0.02, 0.02])
builder.add_box_visual(
half_size=[0.02, 0.02, 0.02],
color=[1.0, 0.0, 0.0, 1.0],
)
builder.initial_pose = sapien.Pose(p=[0, 0, 0.02])
self.cube = builder.build("red_cube")
Using Convenience Build Functions
from mani_skill.utils.building import actors
def _load_scene(self, options: dict):
self.table_scene = TableSceneBuilder(env=self)
self.table_scene.build()
# Convenience function wraps the ActorBuilder pattern
self.obj = actors.build_cube(
self.scene,
half_size=0.02,
color=np.array([12, 42, 160, 255]) / 255,
name="cube",
body_type="dynamic",
initial_pose=sapien.Pose(p=[0, 0, 0.02]),
)
# Build a visual-only goal marker (no collision)
self.goal_region = actors.build_red_white_target(
self.scene,
radius=0.1,
thickness=1e-5,
name="goal_region",
add_collision=False,
body_type="kinematic",
initial_pose=sapien.Pose(p=[0, 0, 1e-3]),
)
Restricting an Actor to Specific Environments
def _load_scene(self, options: dict):
# Only create the obstacle in environments 0 and 2
builder = self.scene.create_actor_builder()
builder.add_box_collision(half_size=[0.1, 0.1, 0.1])
builder.add_box_visual(half_size=[0.1, 0.1, 0.1], color=[0.5, 0.5, 0.5, 1.0])
builder.set_scene_idxs([0, 2])
builder.initial_pose = sapien.Pose(p=[0.3, 0, 0.1])
self.obstacle = builder.build_static("obstacle")
Loading a Mesh-Based Actor from File
def _load_scene(self, options: dict):
builder = self.scene.create_actor_builder()
builder.add_convex_collision_from_file(
filename="path/to/mesh.obj",
scale=[0.01, 0.01, 0.01],
)
builder.add_visual_from_file(
filename="path/to/mesh.glb",
scale=[0.01, 0.01, 0.01],
)
builder.initial_pose = sapien.Pose(p=[0, 0, 0.05])
self.mesh_actor = builder.build("mesh_object")
Related Pages
- Principle:Haosulab_ManiSkill_Scene_Object_Loading -- The principle this implements
- Implementation:Haosulab_ManiSkill_Register_Env_Decorator -- Registering the environment class
- Implementation:Haosulab_ManiSkill_Initialize_Episode_Pattern -- Initializing object poses after scene loading
- Heuristic:Haosulab_ManiSkill_Initial_Pose_Performance