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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed
^^^^^

* Fixed direct rigid-body path queries so nested rigid-body descendants are not selected when the requested body matches.
8 changes: 8 additions & 0 deletions source/isaaclab/isaaclab/sim/utils/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ def resolve_matching_prims_from_source(
env_regex_ns: str = "/World/envs/env_[^/]+",
raise_if_no_matches: bool = True,
traverse_instance_prims: bool = True,
prefer_direct_matches: bool = False,
) -> list[tuple[Usd.Prim, str]]:
"""Resolve matching prims from a single(source) instance when multiple instances are present.

Expand All @@ -402,6 +403,8 @@ def resolve_matching_prims_from_source(
env_regex_ns: Namespace pattern that marks one instance root when no clone plan applies.
raise_if_no_matches: Whether to raise if no prim matches ``path_expr``. Defaults to True.
traverse_instance_prims: Whether to traverse instance prims when applying ``predicate``.
prefer_direct_matches: Prefer matching prims named by ``path_expr`` over their matching descendants.
Descendants remain a fallback when no direct prim satisfies ``predicate``.

Returns:
A list of ``(source_prim, destination_expr)`` pairs. Empty only when
Expand Down Expand Up @@ -481,6 +484,11 @@ def resolve_matching_prims_from_source(
unique_matches.setdefault(child_path, (child, dest + child_path[len(source_path) :]))
results = list(unique_matches.values())

if prefer_direct_matches:
direct_matches = [pair for pair in results if re.fullmatch(path_expr, pair[0].GetPath().pathString)]
if direct_matches:
results = direct_matches

if expected_num_matches is not None and len(results) != expected_num_matches:
raise RuntimeError(f"Expected {expected_num_matches} prims at '{path_expr}', found {len(results)}.")
if raise_if_no_matches and not results:
Expand Down
8 changes: 8 additions & 0 deletions source/isaaclab/test/sim/test_cloner.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,11 @@ def test_resolve_matching_prims_from_source(sim, with_clone_plan):
"/World/envs/env_[^/]+/Robot/foo/bar",
"/World/envs/env_[^/]+/Robot/other/bar",
]

matches = queries.resolve_matching_prims_from_source(
r"/World/envs/env_[^/]+/Robot/foo",
predicate=lambda prim: prim.GetName() in {"foo", "bar"},
expected_num_matches=1,
prefer_direct_matches=True,
)
assert [prim.GetPath().pathString for prim, _ in matches] == ["/World/envs/env_0/Robot/foo"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Added
^^^^^

* Added ``FRANKA_PANDA_FLAT_CFG`` and ``FRANKA_PANDA_FLAT_HIGH_PD_CFG`` for the shared flat Franka
asset. These configurations selected gripper-only colliders and the ``panda_arm`` actuator group;
select the ``Physics`` variant for the target backend.

Deprecated
^^^^^^^^^^

* Deprecated ``FRANKA_PANDA_CFG``, ``FRANKA_PANDA_HIGH_PD_CFG``, and
``FRANKA_PANDA_MENAGERIE_CFG``. Their previous asset and actuator contracts remained available
during the deprecation window. Use the corresponding ``FRANKA_PANDA_FLAT_*`` configuration to
migrate to the shared flat asset, and replace shoulder/forearm actuator overrides with ``panda_arm``.
6 changes: 6 additions & 0 deletions source/isaaclab_assets/isaaclab_assets/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ __all__ = [
"GR1T2_CFG",
"GR1T2_HIGH_PD_CFG",
"FRANKA_PANDA_CFG",
"FRANKA_PANDA_FLAT_CFG",
"FRANKA_PANDA_FLAT_HIGH_PD_CFG",
"FRANKA_PANDA_HIGH_PD_CFG",
"FRANKA_PANDA_LEGACY_CFG",
"FRANKA_PANDA_MENAGERIE_CFG",
"FRANKA_ROBOTIQ_GRIPPER_CFG",
"FOURBAR_POLE_CFG",
Expand Down Expand Up @@ -89,7 +92,10 @@ from .robots import (
GR1T2_CFG,
GR1T2_HIGH_PD_CFG,
FRANKA_PANDA_CFG,
FRANKA_PANDA_FLAT_CFG,
FRANKA_PANDA_FLAT_HIGH_PD_CFG,
FRANKA_PANDA_HIGH_PD_CFG,
FRANKA_PANDA_LEGACY_CFG,
FRANKA_PANDA_MENAGERIE_CFG,
FRANKA_ROBOTIQ_GRIPPER_CFG,
FOURBAR_POLE_CFG,
Expand Down
6 changes: 6 additions & 0 deletions source/isaaclab_assets/isaaclab_assets/robots/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ __all__ = [
"GR1T2_CFG",
"GR1T2_HIGH_PD_CFG",
"FRANKA_PANDA_CFG",
"FRANKA_PANDA_FLAT_CFG",
"FRANKA_PANDA_FLAT_HIGH_PD_CFG",
"FRANKA_PANDA_HIGH_PD_CFG",
"FRANKA_PANDA_LEGACY_CFG",
"FRANKA_PANDA_MENAGERIE_CFG",
"FRANKA_ROBOTIQ_GRIPPER_CFG",
"FOURBAR_POLE_CFG",
Expand Down Expand Up @@ -82,7 +85,10 @@ from .cassie import CASSIE_CFG
from .fourier import GR1T2_CFG, GR1T2_HIGH_PD_CFG
from .franka import (
FRANKA_PANDA_CFG,
FRANKA_PANDA_FLAT_CFG,
FRANKA_PANDA_FLAT_HIGH_PD_CFG,
FRANKA_PANDA_HIGH_PD_CFG,
FRANKA_PANDA_LEGACY_CFG,
FRANKA_PANDA_MENAGERIE_CFG,
FRANKA_ROBOTIQ_GRIPPER_CFG,
)
Expand Down
124 changes: 104 additions & 20 deletions source/isaaclab_assets/isaaclab_assets/robots/franka.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@

The following configurations are available:

* :obj:`FRANKA_PANDA_CFG`: Franka Emika Panda robot with Panda hand
* :obj:`FRANKA_PANDA_MENAGERIE_CFG`: Franka Emika Panda robot converted from MuJoCo Menagerie
* :obj:`FRANKA_PANDA_HIGH_PD_CFG`: Franka Emika Panda robot with Panda hand with stiffer PD control
* :obj:`FRANKA_PANDA_FLAT_CFG`: Shared flat asset for maintained Franka tasks
* :obj:`FRANKA_PANDA_FLAT_HIGH_PD_CFG`: Flat asset with stiffer PD control
* :obj:`FRANKA_PANDA_LEGACY_CFG`: Legacy Franka Emika Panda asset configuration
* :obj:`FRANKA_PANDA_CFG`: Deprecated alias retaining the legacy asset and actuators
* :obj:`FRANKA_PANDA_HIGH_PD_CFG`: Deprecated legacy high-PD configuration
* :obj:`FRANKA_PANDA_MENAGERIE_CFG`: Deprecated nested-instance Menagerie configuration
* :obj:`FRANKA_ROBOTIQ_GRIPPER_CFG`: Franka robot with Robotiq_2f_85 gripper

The old public names retain their original configuration contracts for the deprecation window. Use
``FRANKA_PANDA_FLAT_CFG`` for new code and select its ``Physics`` variant for the chosen backend.

Reference: https://github.com/frankaemika/franka_ros
"""

import warnings
from typing import TYPE_CHECKING

from isaaclab_newton.sim.schemas import NewtonArticulationCfg
from isaaclab_physx.sim.schemas import PhysxArticulationCfg, PhysxRigidBodyCfg

Expand All @@ -28,7 +37,7 @@
# Configuration
##

FRANKA_PANDA_CFG = ArticulationCfg(
FRANKA_PANDA_LEGACY_CFG = ArticulationCfg(
spawn=sim_utils.UsdFileCfg(
usd_path=f"{ISAACLAB_NUCLEUS_DIR}/Robots/FrankaEmika/Legacy/panda_instanceable.usd",
activate_contact_sensors=False,
Expand Down Expand Up @@ -77,45 +86,120 @@
},
soft_joint_pos_limit_factor=1.0,
)
"""Configuration of Franka Emika Panda robot."""
"""Configuration of the legacy Franka Emika Panda robot asset."""


FRANKA_PANDA_MENAGERIE_CFG = clone(FRANKA_PANDA_CFG)
FRANKA_PANDA_MENAGERIE_CFG.spawn.usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Robots/FrankaEmika/franka_panda.usda"
FRANKA_PANDA_MENAGERIE_CFG.actuators = {
FRANKA_PANDA_FLAT_CFG = clone(FRANKA_PANDA_LEGACY_CFG)
FRANKA_PANDA_FLAT_CFG.spawn.usd_path = f"{ISAACLAB_NUCLEUS_DIR}/Robots/FrankaEmika/franka_panda.usda"
FRANKA_PANDA_FLAT_CFG.spawn.variants = {"Physics": "physx", "Colliders": "gripper_only"}
next(
props for props in FRANKA_PANDA_FLAT_CFG.spawn.articulation_props if isinstance(props, PhysxArticulationCfg)
).enabled_self_collisions = False
next(
props for props in FRANKA_PANDA_FLAT_CFG.spawn.articulation_props if isinstance(props, NewtonArticulationCfg)
).self_collision_enabled = False
FRANKA_PANDA_FLAT_CFG.actuators = {
"panda_arm": ImplicitActuatorCfg(
joint_names_expr=["panda_joint[1-7]"],
joint_effort_limit={"panda_joint[1-4]": 100.0, "panda_joint[5-7]": 12.0},
joint_velocity_limit={"panda_joint[1-4]": 20.0, "panda_joint[5-7]": 25.0},
stiffness=None,
damping=None,
viscous_friction=0.0,
),
"panda_hand": ImplicitActuatorCfg(
joint_names_expr=["panda_finger_joint.*"],
joint_names_expr=["panda_finger_joint1"],
joint_effort_limit=200.0,
stiffness=None,
damping=None,
viscous_friction=0.0,
),
"panda_finger2_passive": ImplicitActuatorCfg(
joint_names_expr=["panda_finger_joint2"],
joint_effort_limit=200.0,
stiffness=0.0,
damping=0.0,
viscous_friction=0.0,
),
}
"""Configuration of the MuJoCo Menagerie-derived Franka Emika Panda robot.
"""Configuration of the Franka Emika Panda robot.

The converted model has different inertial and drive authoring from the legacy asset used by
:attr:`FRANKA_PANDA_CFG`. The solver velocity limits provide consistent behavior across physics
backends, while the arm and hand retain their USD-authored drives.
The flat asset contains PhysX and MuJoCo physics variants and gripper-only, primitive, and convex-hull
collider variants. The gripper-only collider variant is the default. Explicit solver properties keep
the actuator contract consistent across physics payloads. Only the leading finger has an active drive;
the authored mimic constraint moves the passive follower. The standalone configuration selects the PhysX
payload by default; direct Newton consumers must select the ``mujoco`` physics variant explicitly.
"""


FRANKA_PANDA_HIGH_PD_CFG = clone(FRANKA_PANDA_CFG)
FRANKA_PANDA_HIGH_PD_CFG.spawn.rigid_props.disable_gravity = True
FRANKA_PANDA_HIGH_PD_CFG.actuators["panda_shoulder"].stiffness = 400.0
FRANKA_PANDA_HIGH_PD_CFG.actuators["panda_shoulder"].damping = 80.0
FRANKA_PANDA_HIGH_PD_CFG.actuators["panda_forearm"].stiffness = 400.0
FRANKA_PANDA_HIGH_PD_CFG.actuators["panda_forearm"].damping = 80.0
FRANKA_PANDA_FLAT_HIGH_PD_CFG = clone(FRANKA_PANDA_FLAT_CFG)
FRANKA_PANDA_FLAT_HIGH_PD_CFG.spawn.rigid_props.disable_gravity = True
FRANKA_PANDA_FLAT_HIGH_PD_CFG.actuators["panda_arm"].stiffness = 400.0
FRANKA_PANDA_FLAT_HIGH_PD_CFG.actuators["panda_arm"].damping = 80.0
"""Configuration of Franka Emika Panda robot with stiffer PD control.

This configuration is useful for task-space control using differential IK.
"""


FRANKA_ROBOTIQ_GRIPPER_CFG = clone(FRANKA_PANDA_CFG)
_FRANKA_PANDA_COMPAT_CFG = clone(FRANKA_PANDA_LEGACY_CFG)


_FRANKA_PANDA_LEGACY_HIGH_PD_CFG = clone(FRANKA_PANDA_LEGACY_CFG)
_FRANKA_PANDA_LEGACY_HIGH_PD_CFG.spawn.rigid_props.disable_gravity = True
for actuator in ("panda_shoulder", "panda_forearm"):
_FRANKA_PANDA_LEGACY_HIGH_PD_CFG.actuators[actuator].stiffness = 400.0
_FRANKA_PANDA_LEGACY_HIGH_PD_CFG.actuators[actuator].damping = 80.0


_FRANKA_PANDA_NESTED_MENAGERIE_CFG = clone(FRANKA_PANDA_LEGACY_CFG)
_FRANKA_PANDA_NESTED_MENAGERIE_CFG.spawn.usd_path = (
f"{ISAACLAB_NUCLEUS_DIR}/Robots/FrankaEmika/franka_panda_nestedInstance.usda"
)
_FRANKA_PANDA_NESTED_MENAGERIE_CFG.actuators = {
"panda_arm": ImplicitActuatorCfg(
joint_names_expr=["panda_joint[1-7]"],
joint_velocity_limit={"panda_joint[1-4]": 20.0, "panda_joint[5-7]": 25.0},
stiffness=None,
damping=None,
),
"panda_hand": ImplicitActuatorCfg(
joint_names_expr=["panda_finger_joint.*"],
stiffness=None,
damping=None,
),
}


_DEPRECATED_FRANKA_CFGS = {
"FRANKA_PANDA_CFG": (_FRANKA_PANDA_COMPAT_CFG, "FRANKA_PANDA_FLAT_CFG"),
"FRANKA_PANDA_HIGH_PD_CFG": (_FRANKA_PANDA_LEGACY_HIGH_PD_CFG, "FRANKA_PANDA_FLAT_HIGH_PD_CFG"),
"FRANKA_PANDA_MENAGERIE_CFG": (_FRANKA_PANDA_NESTED_MENAGERIE_CFG, "FRANKA_PANDA_FLAT_CFG"),
}

if TYPE_CHECKING:
FRANKA_PANDA_CFG: ArticulationCfg
FRANKA_PANDA_HIGH_PD_CFG: ArticulationCfg
FRANKA_PANDA_MENAGERIE_CFG: ArticulationCfg


def __getattr__(name: str) -> ArticulationCfg:
if name not in _DEPRECATED_FRANKA_CFGS:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
cfg, replacement = _DEPRECATED_FRANKA_CFGS[name]
warnings.warn(
f"{name} is deprecated; use {replacement} for the flat Franka asset.",
FutureWarning,
stacklevel=2,
)
return cfg


def __dir__() -> list[str]:
return sorted(set(globals()) | _DEPRECATED_FRANKA_CFGS.keys())


FRANKA_ROBOTIQ_GRIPPER_CFG = clone(FRANKA_PANDA_LEGACY_CFG)
FRANKA_ROBOTIQ_GRIPPER_CFG.spawn.usd_path = f"{ISAAC_NUCLEUS_DIR}/Robots/FrankaRobotics/FrankaPanda/franka.usd"
FRANKA_ROBOTIQ_GRIPPER_CFG.spawn.variants = {"Gripper": "Robotiq_2F_85"}
FRANKA_ROBOTIQ_GRIPPER_CFG.spawn.rigid_props.disable_gravity = True
Expand Down
20 changes: 20 additions & 0 deletions source/isaaclab_assets/test/test_valid_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@
from isaaclab.test.utils import DeviceScope, test_devices

import isaaclab_assets as lab_assets # noqa: F401
import isaaclab_assets.robots.franka as franka_assets


def test_franka_legacy_configs_warn_and_keep_their_contract() -> None:
"""Deprecated Franka names retain their previous asset and actuator contracts."""
with pytest.warns(FutureWarning, match="FRANKA_PANDA_CFG.*deprecated"):
legacy_cfg = franka_assets.FRANKA_PANDA_CFG
assert legacy_cfg.spawn.usd_path.endswith("/Legacy/panda_instanceable.usd")
assert set(legacy_cfg.actuators) == {"panda_shoulder", "panda_forearm", "panda_hand"}

with pytest.warns(FutureWarning, match="FRANKA_PANDA_HIGH_PD_CFG.*deprecated"):
high_pd_cfg = franka_assets.FRANKA_PANDA_HIGH_PD_CFG
assert high_pd_cfg.spawn.usd_path == legacy_cfg.spawn.usd_path
assert high_pd_cfg.actuators["panda_shoulder"].stiffness == 400.0
assert high_pd_cfg.actuators["panda_forearm"].stiffness == 400.0

with pytest.warns(FutureWarning, match="FRANKA_PANDA_MENAGERIE_CFG.*deprecated"):
menagerie_cfg = franka_assets.FRANKA_PANDA_MENAGERIE_CFG
assert menagerie_cfg.spawn.usd_path.endswith("/franka_panda_nestedInstance.usda")
assert set(menagerie_cfg.actuators) == {"panda_arm", "panda_hand"}


@pytest.fixture(scope="module")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed
^^^^^

* Fixed Newton articulation material randomization for bodies whose shapes are interleaved in the native shape array.
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ def num_bodies(self) -> int:

@property
def num_shapes_per_body(self) -> list[int]:
"""Number of collision shapes per body in public body-name order.
"""Number of shapes per body in public body-name order.

Each element corresponds to the body at the same index in
:attr:`body_names`. Backend-order counts are cached; a nonidentity body
Expand All @@ -287,11 +287,11 @@ def num_shapes_per_body(self) -> list[int]:

@property
def backend_num_shapes_per_body(self) -> list[int]:
"""Number of collision shapes per body in active backend solver-view order.
"""Number of shapes per body in active backend solver-view order.

Each element corresponds to the body at the same index in
:attr:`backend_body_names`, matching the shape axis of the backend
solver arrays. The counts are cached on first access. Use
:attr:`backend_body_names`. Shapes belonging to one body need not be
contiguous in the backend shape arrays. The counts are cached on first access. Use
:attr:`num_shapes_per_body` for public body order.

Returns:
Expand Down
10 changes: 3 additions & 7 deletions source/isaaclab_newton/isaaclab_newton/envs/mdp/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,10 @@ def __init__(self, cfg: EventTermCfg, env: ManagerBasedEnv) -> None:
self._restitution_binding = asset._root_view.get_attribute("shape_material_restitution", model)[:, 0] # type: ignore

if isinstance(asset, assets.Articulation) and asset_cfg.body_ids != slice(None):
# Shape counts use backend body order.
num_shapes_per_body = asset.backend_num_shapes_per_body
shape_indices_list = []
backend_body_ids = asset.map_body_ids_to_backend(asset_cfg.body_ids)
for body_id in backend_body_ids:
start_idx = sum(num_shapes_per_body[:body_id])
end_idx = start_idx + num_shapes_per_body[body_id]
shape_indices_list.extend(range(start_idx, end_idx))
shape_indices_list = [
shape for body_id in backend_body_ids for shape in asset.root_view.body_shapes[body_id]
]
self._shape_indices = torch.tensor(shape_indices_list, dtype=torch.long)
else:
self._shape_indices = torch.arange(self._friction_binding.shape[1], dtype=torch.long)
Expand Down
Loading
Loading