Datasets#

Semantic Digital Twin can load datasets from internet resources. The results of the loaded datasets are completely function digital twins (World instances including Semantic Annotations, Kinematics, etc.).

Sage#

Scenes from Sage can be loaded with:

from semantic_digital_twin.adapters.sage_10k_dataset.loader import Sage10kDatasetLoader

loader = Sage10kDatasetLoader()
scene = loader.create_scene(scene_url=Sage10kDatasetLoader.available_scenes()[0])
world = scene.create_world()

Sapien / PartNet#

Articulated assets from the PartNet-Mobility dataset can be loaded with:

from semantic_digital_twin.adapters.partnet_mobility_dataset.loader import PartNetMobilityDatasetLoader

loader = PartNetMobilityDatasetLoader()
world = loader.load(model_id=179) # model_id can be found at https://sapien.ucsd.edu/browse

Note that this requires the sapien library to be installed and the SAPIEN_ACCESS_TOKEN environment variable to be set.

RoboCasa#

Objects, fixtures, full kitchen scenes, and manipulation tasks from RoboCasa can be loaded with:

from semantic_digital_twin.adapters.robocasa_dataset.loader import RoboCasaDatasetLoader
from semantic_digital_twin.adapters.robocasa_dataset.semantics import (
    RoboCasaKitchenApplianceCategory,
    RoboCasaObjectCategory,
)

loader = RoboCasaDatasetLoader()

kitchen_world = loader.load_kitchen(layout_id=..., style_id=...)  # a full kitchen scene
appliance_world = loader.load_kitchen_appliance(RoboCasaKitchenApplianceCategory.CABINET)  # a single appliance
object_world = loader.load_object(RoboCasaObjectCategory.APPLE)  # a single object

A RoboCasa task (for example "TurnOnMicrowave") can be loaded together with the scene it is defined over. load_task returns a RoboCasaTask binding the World to the task’s natural-language instruction, the bodies to be manipulated, and the pose the robot should start at. RoboCasa’s own robot is stripped from the world, since semantic_digital_twin owns the robot.

task = loader.load_task("TurnOnMicrowave", layout_id=..., style_id=...)
task.instruction          # e.g. "Press the start button on the microwave."
task.manipulated_objects  # the bodies the task requires the robot to interact with
task.robot_base_pose      # where to spawn the semantic_digital_twin-owned robot

Note that this requires the robocasa and robosuite libraries to be installed (robosuite must be installed from git, pip install git+https://github.com/ARISE-Initiative/robosuite.git), and the fixture/object assets to be downloaded via python -m robocasa.scripts.download_kitchen_assets (pointed at by RoboCasaDatasetLoader.directory, ~/robocasa-assets by default).

ArtVIP#

Professionally modelled, articulated CAD furniture and appliances (including a dedicated IKEA furniture category) from ArtVIP can be loaded with:

from semantic_digital_twin.adapters.artvip_dataset.loader import ArtVipDatasetLoader
from semantic_digital_twin.adapters.artvip_dataset.schema import ArtVipCategory

loader = ArtVipDatasetLoader()
loader.available_objects(ArtVipCategory.IKEA_FURNITURE)  # every object name in a category

obj = loader.load(ArtVipCategory.IKEA_FURNITURE, "EKET_Cabinet_with_door_brown_walnut_effect_35x35x35cm")
obj.world  # one Body per rigid link

Objects are USD stages parsed by semantic_digital_twin.adapters.usd.parser.USDParser (the USD counterpart to URDFParser/MJCFParser): RevoluteConnection/PrismaticConnection per USD Physics joint of the matching type, FixedConnection otherwise. The catalog is 450 objects across the 9 ArtVipCategory values; available_objects returns each object’s path relative to its category, occasionally nested a subcategory deeper (e.g. major_appliances/refrigerator/fridge/fridge_01).

ArtVipDatasetLoader.load always loads exactly one named object - the ArtVIP catalog itself is structured as one USD file per object, not per scene. USDParser itself is not limited to that: a stage with several unconnected top-level prims and no physics joints between them parses into one World with a separate Body per prim, so a multi-object USD scene composed outside ArtVIP (e.g. authored by hand or exported from a DCC tool) loads the same way, just without ArtVipDatasetLoader’s category/name bookkeeping.

The dataset is public (Apache 2.0), no gated access. Requires the usd-core library (pxr).

GraspClutter6D#

Real, densely cluttered bin/shelf/table scenes from GraspClutter6D (1000 scenes, ~14 objects/scene, 200 object models plus the standard YCB-Video objects), annotated with real per-frame camera parameters and 6D object ground-truth poses in the BOP dataset format:

from semantic_digital_twin.adapters.grasp_clutter_6d_dataset.loader import (
    GraspClutter6DDatasetLoader,
    GraspClutter6DModelVariant,
    GraspClutter6DObjectSet,
    GraspClutter6DSplit,
)

loader = GraspClutter6DDatasetLoader()
scene_id = loader.available_scene_ids(
    object_set=GraspClutter6DObjectSet.GRASP, split=GraspClutter6DSplit.TRAIN
)[0]
models_directory = loader.download_models(GraspClutter6DModelVariant.EVAL)

# scene = loader.load_scene(scene_id)  # only after download_scenes() - see below

Unlike this package’s other dataset loaders, GraspClutter6D does not store its scenes as separate repository files - all 1000 are packed into one combined, 5-volume, ~203 GB scenes.7z archive, so GraspClutter6DDatasetLoader.load_scene needs download_scenes() to have downloaded and extracted the whole thing first; there is no way to fetch a single scene. download_split_info() (scene id lists) and download_models() (object meshes) are comparatively small and safe to call freely.

GraspClutter6DScene.from_directory/load_scene only parse a scene’s ground truth (scene_camera.json/scene_gt.json) - not its RGB/depth/mask images, and not a World. Call .create_world(image_id, models_directory) on the parsed scene to build one for a given frame, with one Body per object placed at its ground-truth pose relative to the camera (and, with with_world_frame=True, a map root body placing the camera itself, for frames that carry a world-to-camera transform).

Requires the huggingface_hub and py7zr packages.

Reading a dataset from a server#

The loaders above download a dataset and keep it locally, which stops working once a corpus is measured in terabytes. Such a dataset can be served over http instead and read an entry at a time, with a local cache holding only what has actually been used.

from semantic_digital_twin.adapters.dataset_server import DatasetServer
from semantic_digital_twin.world_description.mesh_file_storage import MeshFileSources

MeshFileSources().use(DatasetServer.from_environment())

DatasetServer.from_environment reads the server’s address from SEMANTIC_DIGITAL_TWIN_DATASET_SERVER, the dataset’s location on the machine serving it from SEMANTIC_DIGITAL_TWIN_DATASET_ROOT, and where to keep its files from SEMANTIC_DIGITAL_TWIN_MESH_CACHE, which defaults to the directory this package keeps everything else it downloads in. A world loaded afterwards needs nothing further: a mesh’s files are fetched the first time something asks for its geometry, and never again.

For a description parsed from a file, pass the server as a path resolver instead, which needs no registration:

WorldSpecification.from_urdf(path, path_resolver=CompositePathResolver([server]))

Anything that serves a directory tree and answers a directory with a json listing can be the server. nginx does both without code, through autoindex and autoindex_format.