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
9 changes: 8 additions & 1 deletion scripts/run_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@
help="Environment/task name.",
)
parser.add_argument("--workflow", type=str, default="pickup_beaker", help="Name of the workflow to run.")
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()

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

"""Rest everything else."""
Expand All @@ -66,6 +69,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 @@ -133,6 +138,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

env.close()

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 @@ -63,7 +63,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
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"]
)
]
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@
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_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,
)

##
# Register Gym environments.
Expand Down Expand Up @@ -47,3 +58,57 @@
},
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,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Shared helpers for capped DURAN and Falcon visual checkpoints.

The capped payloads are intentionally spawned as generic USD assets. Each USD
contains two rigid bodies joined by a ``PhysicsFixedJoint`` and does not expose
an articulation root, so it must not be registered as a ``RigidObject``.
"""

import torch

from matterix.envs import MatterixBaseEnvCfg, mdp
from matterix_assets import MATTERIX_ASSETS_DATA_DIR, MatterixStaticObjectCfg
from matterix_assets.infrastructure.tables import TABLE_SEATTLE_INST_Cfg
from matterix_assets.robots import FRANKA_PANDA_HIGH_PD_IK_CFG

from matterix.managers import EventManagerCfg
from matterix_sm import CloseGripperCfg, MoveRelativeCfg, OpenGripperCfg
from matterix_sm.primitive_actions.move_to_pose import MoveToPoseCfg
from matterix_sm.robot_action_spaces import FRANKA_IK_ACTION_SPACE

import isaaclab.envs.mdp as isaaclab_mdp
from isaaclab.managers import EventTermCfg as EventTerm
from isaaclab.managers import ObservationGroupCfg as ObsGroup
from isaaclab.managers import ObservationTermCfg as ObsTerm
from isaaclab.utils import configclass


@configclass
class EventCfg(EventManagerCfg):
"""Reset events for a capped labware checkpoint."""

reset_scene_to_default = EventTerm(
func=isaaclab_mdp.reset_scene_to_default,
mode="reset",
)


@configclass
class ObservationManagerCfg:
"""Robot observations used by the hard-coded visual manipulation sequence."""

@configclass
class ArticulationsGroup(ObsGroup):
robot__root_world_pos = ObsTerm(func=mdp.root_world_pos, params={"asset_name": "robot"})
robot__root_world_quat = ObsTerm(func=mdp.root_world_quat, params={"asset_name": "robot"})
robot__joint_pos = ObsTerm(func=mdp.joint_pos, params={"asset_name": "robot"})
robot__joint_vel = ObsTerm(func=mdp.joint_vel, params={"asset_name": "robot"})
robot__ee_world_pos = ObsTerm(func=mdp.ee_world_pos, params={"asset_name": "robot"})
robot__ee_world_quat = ObsTerm(func=mdp.ee_world_quat, params={"asset_name": "robot"})
robot__gripper_pos = ObsTerm(func=mdp.gripper_pos, params={"asset_name": "robot"})

def __post_init__(self):
self.enable_corruption = False
self.concatenate_terms = False

articulations: ArticulationsGroup = ArticulationsGroup()


def capped_payload_cfg(slug: str, pos: tuple[float, float, float]) -> MatterixStaticObjectCfg:
"""Build a generic scene asset for one two-body capped USD payload."""
return MatterixStaticObjectCfg(
usd_path=f"{MATTERIX_ASSETS_DATA_DIR}/labware/{slug}/{slug}-inst.usda",
pos=pos,
scale=(1.0, 1.0, 1.0),
)


def capped_pick_and_place(
vessel_pos: tuple[float, float, float],
pre_grasp_z: float,
grasp_z: float,
agent: str = "robot",
):
"""Return the visual checkpoint sequence for one fixed-jointed payload."""
x, y, z = vessel_pos
return [
OpenGripperCfg(agent_assets=agent, action_space_info=FRANKA_IK_ACTION_SPACE),
MoveToPoseCfg(
agent_assets=agent,
target_positions=torch.tensor([[x, y, z + pre_grasp_z]]),
action_space_info=FRANKA_IK_ACTION_SPACE,
),
MoveToPoseCfg(
agent_assets=agent,
target_positions=torch.tensor([[x, y, z + grasp_z]]),
action_space_info=FRANKA_IK_ACTION_SPACE,
),
CloseGripperCfg(agent_assets=agent, action_space_info=FRANKA_IK_ACTION_SPACE),
MoveRelativeCfg(
agent_assets=agent,
position_offset=(0.0, 0.0, 0.1),
orientation_offset=None,
action_space_info=FRANKA_IK_ACTION_SPACE,
),
MoveToPoseCfg(
agent_assets=agent,
target_positions=torch.tensor([[x, y, z + grasp_z + 0.005]]),
action_space_info=FRANKA_IK_ACTION_SPACE,
),
OpenGripperCfg(agent_assets=agent, action_space_info=FRANKA_IK_ACTION_SPACE),
MoveRelativeCfg(
agent_assets=agent,
position_offset=(0.0, 0.0, 0.15),
orientation_offset=None,
action_space_info=FRANKA_IK_ACTION_SPACE,
),
]


def capped_env_fields(
slug: str,
vessel_pos: tuple[float, float, float],
pre_grasp_z: float,
grasp_z: float,
workflow_name: str,
description: str,
) -> dict:
"""Return common config fields for a one-asset capped checkpoint."""
return {
"env_spacing": 10.0,
"objects": {
"capped_labware": capped_payload_cfg(slug, vessel_pos),
"table": TABLE_SEATTLE_INST_Cfg(pos=(0.5, 0, 0)),
},
"articulated_assets": {
"robot": FRANKA_PANDA_HIGH_PD_IK_CFG(pos=(0.0, 0, 0)),
},
"gripper_joint_names": ["panda_finger_joint1", "panda_finger_joint2"],
"observations": ObservationManagerCfg(),
"events": EventCfg(),
"record_path": "datasets/dataset.hdf5",
"workflows": {
workflow_name: {
"description": description,
"actions": capped_pick_and_place(vessel_pos, pre_grasp_z, grasp_z),
}
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Dedicated physical checkpoint for the DURAN 100 mL capped bottle."""

from matterix.envs import MatterixBaseEnvCfg
from isaaclab.utils import configclass

from .capped_labware_checkpoints import capped_env_fields


@configclass
class FrankaRigidLabwareDuran100EnvTestCfg(MatterixBaseEnvCfg):
"""One-environment-per-asset DURAN 100 mL visual checkpoint."""

locals().update(capped_env_fields(
slug="dwk-218012458",
vessel_pos=(0.55, 0.0, 0.0),
pre_grasp_z=0.145,
grasp_z=0.075,
workflow_name="pick_and_place_duran_100",
description="Pick up and place the fixed-jointed DURAN 100 mL GL45 bottle",
))
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Dedicated physical checkpoint for the DURAN 500 mL capped bottle."""

from matterix.envs import MatterixBaseEnvCfg
from isaaclab.utils import configclass

from .capped_labware_checkpoints import capped_env_fields


@configclass
class FrankaRigidLabwareDuran500EnvTestCfg(MatterixBaseEnvCfg):
"""One-environment-per-asset DURAN 500 mL visual checkpoint."""

locals().update(capped_env_fields(
slug="dwk-218014459",
vessel_pos=(0.55, 0.0, 0.0),
pre_grasp_z=0.231,
grasp_z=0.145,
workflow_name="pick_and_place_duran_500",
description="Pick up and place the fixed-jointed DURAN 500 mL GL45 bottle",
))
Loading
Loading