Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
24 changes: 20 additions & 4 deletions source/isaaclab/isaaclab/actuators/actuator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,21 @@ class ActuatorBase(ABC):
effort_limit: torch.Tensor
"""The effort limit for the actuator group. Shape is (num_envs, num_joints).

For implicit actuators, the :attr:`effort_limit` and :attr:`effort_limit_sim` are the same.
This limit is used differently depending on the actuator type:

- **Explicit actuators**: Used for internal torque clipping within the actuator model
(e.g., motor torque limits in DC motor models).
- **Implicit actuators**: Same as :attr:`effort_limit_sim` (aliased for consistency).
"""

effort_limit_sim: torch.Tensor
"""The effort limit for the actuator group in the simulation. Shape is (num_envs, num_joints).

For implicit actuators, the :attr:`effort_limit` and :attr:`effort_limit_sim` are the same.

- **Explicit actuators**: Typically set to a large value (1.0e9) to avoid double-clipping,
since the actuator model already clips efforts using :attr:`effort_limit`.
- **Implicit actuators**: Same as :attr:`effort_limit` (both values are synchronized).
"""

velocity_limit: torch.Tensor
Expand Down Expand Up @@ -123,8 +131,11 @@ def __init__(
are not specified in the configuration, then their values provided in the constructor are used.

.. note::
The values in the constructor are typically obtained through the USD schemas corresponding
to the joints in the actuator model.
The values in the constructor are typically obtained through the USD values passed from the PhysX API calls
corresponding to the joints in the actuator model; these values serve as default values if the parameters
are not specified in the cfg.



Args:
cfg: The configuration of the actuator model.
Expand Down Expand Up @@ -196,7 +207,12 @@ def __init__(
)

self.velocity_limit = self._parse_joint_parameter(self.cfg.velocity_limit, self.velocity_limit_sim)
self.effort_limit = self._parse_joint_parameter(self.cfg.effort_limit, self.effort_limit_sim)
# Parse effort_limit with special default handling:
# - If cfg.effort_limit is None, use the original USD value (effort_limit parameter from constructor)
# - Otherwise, use effort_limit_sim as the default
# Please refer to the documentation of the effort_limit and effort_limit_sim parameters for more details.
effort_default = effort_limit if self.cfg.effort_limit is None else self.effort_limit_sim
self.effort_limit = self._parse_joint_parameter(self.cfg.effort_limit, effort_default)

# create commands buffers for allocation
self.computed_effort = torch.zeros(self._num_envs, self.num_joints, device=self._device)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1713,7 +1713,7 @@ def _process_actuators_cfg(self):
friction=self._data.default_joint_friction_coeff[:, joint_ids],
dynamic_friction=self._data.default_joint_dynamic_friction_coeff[:, joint_ids],
viscous_friction=self._data.default_joint_viscous_friction_coeff[:, joint_ids],
effort_limit=self._data.joint_effort_limits[:, joint_ids],
effort_limit=self._data.joint_effort_limits[:, joint_ids].clone(),
Comment thread
JuanaDd marked this conversation as resolved.
velocity_limit=self._data.joint_vel_limits[:, joint_ids],
)
# log information on actuator groups
Expand Down Expand Up @@ -1843,6 +1843,7 @@ def _apply_actuator_model(self):
"""
# process actions per group
for actuator in self.actuators.values():

Comment thread
kellyguo11 marked this conversation as resolved.
Outdated
# prepare input for actuator model based on cached data
# TODO : A tensor dict would be nice to do the indexing of all tensors together
control_action = ArticulationActions(
Expand Down
31 changes: 25 additions & 6 deletions source/isaaclab/test/assets/test_articulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1372,10 +1372,16 @@ def test_setting_velocity_limit_explicit(sim, num_articulations, device, vel_lim
@pytest.mark.parametrize("num_articulations", [1, 2])
@pytest.mark.parametrize("device", ["cuda:0", "cpu"])
@pytest.mark.parametrize("effort_limit_sim", [1e5, None])
@pytest.mark.parametrize("effort_limit", [1e2, None])
@pytest.mark.parametrize("effort_limit", [1e2, 80.0, None])
@pytest.mark.isaacsim_ci
def test_setting_effort_limit_implicit(sim, num_articulations, device, effort_limit_sim, effort_limit):
"""Test setting of the effort limit for implicit actuators."""
"""Test setting of effort limit for implicit actuators.

This test verifies the effort limit resolution logic for actuator models implemented in :class:`ActuatorBase`:
- Case 1: If USD value == actuator config value: values match correctly
- Case 2: If USD value != actuator config value: actuator config value is used
- Case 3: If actuator config value is None: USD value is used as default
"""
articulation_cfg = generate_articulation_cfg(
articulation_type="single_joint_implicit",
effort_limit_sim=effort_limit_sim,
Expand Down Expand Up @@ -1419,10 +1425,18 @@ def test_setting_effort_limit_implicit(sim, num_articulations, device, effort_li
@pytest.mark.parametrize("num_articulations", [1, 2])
@pytest.mark.parametrize("device", ["cuda:0", "cpu"])
@pytest.mark.parametrize("effort_limit_sim", [1e5, None])
@pytest.mark.parametrize("effort_limit", [1e2, None])
@pytest.mark.parametrize("effort_limit", [80.0, 1e2, None])
@pytest.mark.isaacsim_ci
def test_setting_effort_limit_explicit(sim, num_articulations, device, effort_limit_sim, effort_limit):
"""Test setting of effort limit for explicit actuators."""
"""Test setting of effort limit for explicit actuators.

This test verifies the effort limit resolution logic for actuator models implemented in :class:`ActuatorBase`:
- Case 1: If USD value == actuator config value: values match correctly
- Case 2: If USD value != actuator config value: actuator config value is used
- Case 3: If actuator config value is None: USD value is used as default

"""

articulation_cfg = generate_articulation_cfg(
articulation_type="single_joint_explicit",
effort_limit_sim=effort_limit_sim,
Expand All @@ -1436,6 +1450,9 @@ def test_setting_effort_limit_explicit(sim, num_articulations, device, effort_li
# Play sim
sim.reset()

# usd default effort limit is set to 80
usd_default_effort_limit = 80.0

# collect limit init values
physx_effort_limit = articulation.root_physx_view.get_dof_max_forces().to(device)
actuator_effort_limit = articulation.actuators["joint"].effort_limit
Expand All @@ -1452,8 +1469,9 @@ def test_setting_effort_limit_explicit(sim, num_articulations, device, effort_li
# check physx effort limit does not match the one explicit actuator has
assert not (torch.allclose(actuator_effort_limit, physx_effort_limit))
else:
# check actuator effort_limit is the same as the PhysX default
torch.testing.assert_close(actuator_effort_limit, physx_effort_limit)
# When effort_limit is None, actuator should use USD default values
expected_actuator_effort_limit = torch.full_like(physx_effort_limit, usd_default_effort_limit)
torch.testing.assert_close(actuator_effort_limit, expected_actuator_effort_limit)

# when using explicit actuators, the limits are set to high unless user overrides
if effort_limit_sim is not None:
Expand All @@ -1462,6 +1480,7 @@ def test_setting_effort_limit_explicit(sim, num_articulations, device, effort_li
limit = ActuatorBase._DEFAULT_MAX_EFFORT_SIM # type: ignore
# check physx internal value matches the expected sim value
expected_effort_limit = torch.full_like(physx_effort_limit, limit)
torch.testing.assert_close(actuator_effort_limit_sim, expected_effort_limit)
torch.testing.assert_close(physx_effort_limit, expected_effort_limit)


Expand Down
Loading