Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pip install --upgrade pip
# Install a CUDA-enabled PyTorch
pip install -U torch==2.7.0 torchvision==0.22.0 --index-url https://download.pytorch.org/whl/cu128
# Install the Isaac Lab packages along with Isaac Sim:
pip install "isaaclab[isaacsim,all]==2.3.0" --extra-index-url https://pypi.nvidia.com
python -m pip install "isaaclab[isaacsim,all]==2.3.2.post1" --extra-index-url https://pypi.nvidia.com
```
For advanced installation options, refer to [installation guide](https://isaac-sim.github.io/IsaacLab/main/source/setup/installation/index.html).

Expand Down
8 changes: 7 additions & 1 deletion scripts/run_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,16 @@
default="out/videos",
help="Directory to save recorded videos (default: out/videos).",
)
parser.add_argument("--record_path", type=str, default=None, help="Optional unique HDF5 recorder path for this run.")
parser.add_argument("--episodes", type=int, default=0, help="Stop after this many episodes; 0 keeps the existing continuous behavior.")
AppLauncher.add_app_launcher_args(parser)
args_cli = parser.parse_args()

if args_cli.record_video and args_cli.headless and not args_cli.enable_cameras:
parser.error("--record_video in headless mode requires --enable_cameras (the RTX renderer must be loaded for frame capture).")

# Launch omniverse app
# Forward the complete parsed launcher configuration so --livestream reaches WebRTC.
app_launcher = AppLauncher(args_cli)
simulation_app = app_launcher.app

Expand All @@ -79,6 +82,8 @@ def main():
num_envs=args_cli.num_envs,
use_fabric=not args_cli.disable_fabric,
)
if args_cli.record_path is not None:
env_cfg.record_path = args_cli.record_path

# Validate workflow exists
if not hasattr(env_cfg, "workflows") or not env_cfg.workflows:
Expand Down Expand Up @@ -155,7 +160,8 @@ def main():
sm.print_status(step=step_count, episode=episode_count)

sm.print_status(step=step_count, episode=episode_count)

if args_cli.episodes > 0 and episode_count >= args_cli.episodes:
break
if args_cli.record_video:
video_path = os.path.join(
args_cli.video_dir, f"{task_slug}_{workflow_slug}_ep{episode_count}_{run_ts}.mp4"
Expand Down
2 changes: 1 addition & 1 deletion source/matterix/matterix/envs/matterix_base_env_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class MatterixBaseEnvCfg:

sim: SimulationCfg = SimulationCfg(
render=RenderCfg(
carb_settings={"rtx_translucency_enabled": True, "rtx_raytracing_fractionalCutoutOpacity": True}
carb_settings={"rtx_translucency_enabled": True}
)
)
"""Physics simulation configuration. Default is SimulationCfg()."""
Expand Down
2 changes: 1 addition & 1 deletion source/matterix_assets/data
Submodule data updated 40 files
+41 −0 labware/corning-4980-250/PROVENANCE.md
+3 −0 labware/corning-4980-250/corning-4980-250-inst.usda
+3 −0 labware/corning-4980-250/corning-4980-250.usda
+3 −0 labware/corning-4980-250/files/corning-4980-250_flask_mesh.usda
+3 −0 labware/corning-4980-250/files/materials.usda
+31 −0 labware/corning-4980-250/frames.json
+41 −0 labware/corning-4980-50/PROVENANCE.md
+3 −0 labware/corning-4980-50/corning-4980-50-inst.usda
+3 −0 labware/corning-4980-50/corning-4980-50.usda
+3 −0 labware/corning-4980-50/files/corning-4980-50_flask_mesh.usda
+3 −0 labware/corning-4980-50/files/materials.usda
+31 −0 labware/corning-4980-50/frames.json
+46 −0 labware/dwk-218012458/PROVENANCE.md
+3 −0 labware/dwk-218012458/dwk-218012458-inst.usda
+3 −0 labware/dwk-218012458/dwk-218012458.usda
+3 −0 labware/dwk-218012458/files/dwk-218012458_bottle_mesh.usda
+3 −0 labware/dwk-218012458/files/dwk-218012458_cap_mesh.usda
+3 −0 labware/dwk-218012458/files/materials.usda
+62 −0 labware/dwk-218012458/frames.json
+35 −0 labware/dwk-218014459/PROVENANCE.md
+3 −0 labware/dwk-218014459/dwk-218014459-inst.usda
+3 −0 labware/dwk-218014459/dwk-218014459.usda
+3 −0 labware/dwk-218014459/files/dwk-218014459_bottle_mesh.usda
+3 −0 labware/dwk-218014459/files/dwk-218014459_cap_mesh.usda
+3 −0 labware/dwk-218014459/files/materials.usda
+62 −0 labware/dwk-218014459/frames.json
+33 −0 labware/falcon-352070/PROVENANCE.md
+3 −0 labware/falcon-352070/falcon-352070-inst.usda
+3 −0 labware/falcon-352070/falcon-352070.usda
+3 −0 labware/falcon-352070/files/falcon-352070_cap_mesh.usda
+3 −0 labware/falcon-352070/files/falcon-352070_tube_mesh.usda
+3 −0 labware/falcon-352070/files/materials.usda
+62 −0 labware/falcon-352070/frames.json
+33 −0 labware/falcon-352096/PROVENANCE.md
+3 −0 labware/falcon-352096/falcon-352096-inst.usda
+3 −0 labware/falcon-352096/falcon-352096.usda
+3 −0 labware/falcon-352096/files/falcon-352096_cap_mesh.usda
+3 −0 labware/falcon-352096/files/falcon-352096_tube_mesh.usda
+3 −0 labware/falcon-352096/files/materials.usda
+62 −0 labware/falcon-352096/frames.json
2 changes: 2 additions & 0 deletions source/matterix_assets/matterix_assets/labware/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@
##

from .beakers import *
from .vials import *
from .vialplates import *
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright (c) 2022-2026, The Matterix Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""LOCAL-ONLY configs for the six Batch 1 labware checkpoint candidates.

These configs are test-environment plumbing. The reusable USD payloads live in
the Matterix_assets data submodule; the capped payloads are spawned as generic
USD scene assets by their dedicated checkpoint environments because each has
two rigid bodies joined by a fixed joint.
"""

import json
import os

from matterix_assets import MATTERIX_ASSETS_DATA_DIR
from matterix.managers.semantics.primitive_semantics import IsInContactPhysicsCfg

from isaaclab.utils import configclass

from ..matterix_rigid_object import MatterixRigidObjectCfg


default_prim_path = "{ENV_REGEX_NS}/RigidObjects_Labware"

CORNING_4980_50_MASS_KG = 0.0305
CORNING_4980_250_MASS_KG = 0.1122


def _load_frames(slug: str) -> dict[str, tuple[float, float, float]]:
"""Load authored interface frames from the staged payload."""
path = os.path.join(MATTERIX_ASSETS_DATA_DIR, "labware", slug, "frames.json")
with open(path) as handle:
contract = json.load(handle)
frames = contract.get("frames")
if not isinstance(frames, dict) or not frames:
raise ValueError(f"{path} carries no frames")
missing = {"grasp", "pre_grasp", "post_grasp"} - set(frames)
if missing:
raise ValueError(f"{path} is missing required frames: {sorted(missing)}")
return {name: tuple(offset) for name, offset in frames.items()}


@configclass
class CORNING_4980_50_LOCAL_ONLY_CFG(MatterixRigidObjectCfg):
"""Corning PYREX 4980-50, 50 mL narrow-mouth Erlenmeyer flask."""

prim_path = default_prim_path
usd_path = f"{MATTERIX_ASSETS_DATA_DIR}/labware/corning-4980-50/corning-4980-50-inst.usda"
scale = (1.0, 1.0, 1.0)
mass = CORNING_4980_50_MASS_KG
activate_contact_sensors = True
frames = _load_frames("corning-4980-50")
semantic_tags = [("class", "flask")]
semantics = [
IsInContactPhysicsCfg(
filter_prim_paths_expr=["robot/panda_leftfinger", "robot/panda_rightfinger"]
)
]


@configclass
class CORNING_4980_250_LOCAL_ONLY_CFG(MatterixRigidObjectCfg):
"""Corning PYREX 4980-250, 250 mL narrow-mouth Erlenmeyer flask."""

prim_path = default_prim_path
usd_path = f"{MATTERIX_ASSETS_DATA_DIR}/labware/corning-4980-250/corning-4980-250-inst.usda"
scale = (1.0, 1.0, 1.0)
mass = CORNING_4980_250_MASS_KG
activate_contact_sensors = True
frames = _load_frames("corning-4980-250")
semantic_tags = [("class", "flask")]
semantics = [
IsInContactPhysicsCfg(
filter_prim_paths_expr=["robot/panda_leftfinger", "robot/panda_rightfinger"]
)
]
49 changes: 49 additions & 0 deletions source/matterix_assets/matterix_assets/labware/vialplates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright (c) 2022-2026, The Matterix Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause

"""Configuration and data paths for the promoted 3_5 vial holder."""

import json
from pathlib import Path

from isaaclab.sensors import OffsetCfg
from isaaclab.utils import configclass

from matterix_assets import MATTERIX_ASSETS_DATA_DIR

from ..matterix_rigid_object import MatterixRigidObjectCfg

VIALPLATE_3_5_DATA_DIR = f"{MATTERIX_ASSETS_DATA_DIR}/labware/vialplate_3_5"
VIALPLATE_3_5_USD_PATH = f"{VIALPLATE_3_5_DATA_DIR}/3_5_vialplate_free_standing_frames.usda"
VIALPLATE_3_5_FRAME_CONTRACT_PATH = f"{VIALPLATE_3_5_DATA_DIR}/holder-hole-frame-contract.json"


def _load_holder_hole_frames() -> dict[str, OffsetCfg]:
"""Load the public 15-hole frame family from the promoted contract."""
contract = json.loads(Path(VIALPLATE_3_5_FRAME_CONTRACT_PATH).read_text(encoding="utf-8"))
frames = contract.get("frames")
if not isinstance(frames, list) or len(frames) != 15:
raise ValueError("promoted vial-holder frame contract must contain exactly 15 frames")
orientation = tuple(float(value) for value in contract["frame_orientation_wxyz"])
return {
frame["name"]: OffsetCfg(pos=tuple(float(value) for value in frame["position_m"]), rot=orientation)
for frame in frames
}


VIALPLATE_3_5_HOLE_FRAMES = _load_holder_hole_frames()


@configclass
class VIALPLATE_3_5_CFG(MatterixRigidObjectCfg):
"""Dynamic 15-well holder with the promoted frame-bearing USD payload."""

prim_path = "{ENV_REGEX_NS}/RigidObjects_Labware"
usd_path = VIALPLATE_3_5_USD_PATH
scale = (1.0, 1.0, 1.0)
mass = 0.070350472
activate_contact_sensors = True
frames = VIALPLATE_3_5_HOLE_FRAMES
semantic_tags = [("class", "vial_holder"), ("asset", "3_5_vialplate")]
37 changes: 37 additions & 0 deletions source/matterix_assets/matterix_assets/labware/vials.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (c) 2022-2026, The Matterix Project Developers.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
# Configuration for the self-modelled Fisherbrand 03-339-21F reference vial.

from isaaclab.sensors import OffsetCfg
from isaaclab.utils import configclass

from matterix_assets import MATTERIX_ASSETS_DATA_DIR

from ..matterix_rigid_object import MatterixRigidObjectCfg

FISHERBRAND_03_339_21F_DATA_DIR = (
f"{MATTERIX_ASSETS_DATA_DIR}/labware/fisherbrand_03-339-21f"
)
FISHERBRAND_03_339_21F_USD_PATH = (
f"{FISHERBRAND_03_339_21F_DATA_DIR}/fisherbrand_03-339-21f_z_up_fixed.usda"
)
FISHERBRAND_03_339_21F_FRAME_OFFSETS = {
"grasp": OffsetCfg(pos=(0.0, 0.0, 0.0649)),
"pre_grasp": OffsetCfg(pos=(0.0, 0.0, 0.1649)),
"post_grasp": OffsetCfg(pos=(0.0, 0.0, 0.1649)),
}


@configclass
class FISHERBRAND_03_339_21F_CFG(MatterixRigidObjectCfg):
# Dynamic closed Fisherbrand 03-339-21F vial configuration.

prim_path = "{ENV_REGEX_NS}/RigidObjects_Labware"
usd_path = FISHERBRAND_03_339_21F_USD_PATH
scale = (1.0, 1.0, 1.0)
mass = 0.017734
activate_contact_sensors = True
frames = FISHERBRAND_03_339_21F_FRAME_OFFSETS
semantic_tags = [("class", "vial"), ("asset", "fisherbrand_03-339-21f")]
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __init__(
timeout: float = None,
position_threshold: float = None,
orientation_threshold: float = None,
settling_time: float = 0.05,
action_space_info: ActionSpaceInfo | None = None,
):
"""
Expand All @@ -75,6 +76,7 @@ def __init__(
timeout=timeout,
position_threshold=position_threshold,
orientation_threshold=orientation_threshold,
settling_time=settling_time,
action_space_info=action_space_info,
)

Expand Down Expand Up @@ -176,5 +178,6 @@ def from_cfg(cls, cfg: MoveRelativeCfg):
timeout=cfg.timeout,
position_threshold=cfg.position_threshold,
orientation_threshold=cfg.orientation_threshold,
settling_time=cfg.settling_time,
action_space_info=cfg.action_space_info,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,20 @@
import gymnasium as gym
import os

from . import test_franka_beaker_lift, test_franka_beakers, test_particle_systems, test_semantics_heat_transfer
from . import (
test_franka_beaker_lift,
test_franka_beakers,
test_franka_vialplate,
test_franka_rigid_labware_duran_100,
test_franka_rigid_labware_duran_500,
test_franka_rigid_labware_falcon_15,
test_franka_rigid_labware_falcon_50,
test_franka_rigid_labware_flask_50,
test_franka_rigid_labware_flask_250,
test_particle_systems,
test_semantics_heat_transfer,
test_ticket0c_small_vessel,
)

##
# Register Gym environments.
Expand Down Expand Up @@ -39,6 +52,15 @@
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Vialplate-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_vialplate.FrankaVialplateEnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Semantics-Heat-Transfer-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
Expand All @@ -47,3 +69,66 @@
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Rigid-Labware-Flask-50-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_rigid_labware_flask_50.FrankaRigidLabwareFlask50EnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Rigid-Labware-Flask-250-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_rigid_labware_flask_250.FrankaRigidLabwareFlask250EnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Rigid-Labware-Duran-100-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_rigid_labware_duran_100.FrankaRigidLabwareDuran100EnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Rigid-Labware-Duran-500-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_rigid_labware_duran_500.FrankaRigidLabwareDuran500EnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Rigid-Labware-Falcon-15-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_rigid_labware_falcon_15.FrankaRigidLabwareFalcon15EnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Test-Rigid-Labware-Falcon-50-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_franka_rigid_labware_falcon_50.FrankaRigidLabwareFalcon50EnvTestCfg,
},
disable_env_checker=True,
)

gym.register(
id="Matterix-Ticket0c-Small-Vessel-Franka-v1",
entry_point="matterix.envs:MatterixBaseEnv",
kwargs={
"env_cfg_entry_point": test_ticket0c_small_vessel.Ticket0CSmallVesselEnvCfg,
},
disable_env_checker=True,
)
Loading
Loading