Skip to content
Open
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
Expand Up @@ -42,6 +42,11 @@ def __init__(self) -> None:
def initialize_articulation(self) -> None:
"""Create the articulation handle for the selected robot prim and mark the state initialized."""
self._articulation = Articulation(self.robot_prim)
self.dof_names = None
self.dof_indices = None
self._dof_names = []
self._dof_indices = None
self._link_indices = None
self.initialized = True

def pick_dofs(self, dof_names: Any, dof_indices: Any) -> None:
Expand Down Expand Up @@ -129,17 +134,16 @@ def compute(db: Any) -> bool:
"""
state = db.per_instance_state
try:
if not state.initialized:
if len(db.inputs.robotPath) != 0:
state.robot_prim = db.inputs.robotPath
else:
if not len(db.inputs.targetPrim):
db.log_error("No robot prim found for the articulation state")
return False
else:
state.robot_prim = db.inputs.targetPrim[0].GetString()

# initialize the articulation handle for the robot
if len(db.inputs.robotPath) != 0:
robot_prim = db.inputs.robotPath
else:
if not len(db.inputs.targetPrim):
db.log_error("No robot prim found for the articulation state")
return False
robot_prim = db.inputs.targetPrim[0].GetString()

if not state.initialized or state.robot_prim != robot_prim:
state.robot_prim = robot_prim
state.initialize_articulation()

# pick the articulation DOFs to be queried, they can be different at every step
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Regression coverage for IsaacArticulationState robot retargeting."""

from types import SimpleNamespace

import numpy as np
import omni.kit.test
from isaacsim.core.nodes.ogn.python.nodes.OgnIsaacArticulationState import OgnIsaacArticulationState


class _FakeState:
def __init__(self) -> None:
self.initialized = True
self.robot_prim = "/RobotA"
self.dof_names = []
self.dof_indices = np.array([], dtype=np.int64)
self.initialized_paths = []
self.pick_calls = 0

def initialize_articulation(self) -> None:
self.initialized_paths.append(self.robot_prim)
self.dof_names = None
self.dof_indices = None
self.initialized = True

def pick_dofs(self, dof_names, dof_indices) -> None:
self.pick_calls += 1
self.dof_names = dof_names
self.dof_indices = dof_indices

def get_dof_names(self):
return []

def get_articulation_state(self):
empty = np.array([], dtype=np.float32)
return empty, empty, empty, empty, empty


class TestArticulationStateRetarget(omni.kit.test.AsyncTestCase):
"""Verify changing the robot input refreshes the cached articulation state."""

async def test_robot_path_change_reinitializes_articulation_and_selection(self) -> None:
"""A new robotPath should rebuild the handle and re-resolve DOF selection."""
state = _FakeState()
db = SimpleNamespace(
per_instance_state=state,
inputs=SimpleNamespace(robotPath="/RobotB", targetPrim=[], jointNames=[], jointIndices=[]),
outputs=SimpleNamespace(),
log_error=lambda _message: None,
log_warn=lambda _message: None,
)

self.assertTrue(OgnIsaacArticulationState.compute(db))
self.assertEqual(state.robot_prim, "/RobotB")
self.assertEqual(state.initialized_paths, ["/RobotB"])
self.assertEqual(state.pick_calls, 1)