Unverified Commit d7da02da authored by James Tigue's avatar James Tigue Committed by GitHub

Fixes default effort limit behavior for implicit actuators (#2098)

# Description

This MR fixes the default behavior of implicit actuators if no effort limit is set. Previously, the check
was using the class variable value instead of self which led to the wrong values getting propogated.

Fixes #2054 

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------
Signed-off-by: 's avatarJames Tigue <166445701+jtigue-bdai@users.noreply.github.com>
Co-authored-by: 's avatarMayank Mittal <12863862+Mayankm96@users.noreply.github.com>
parent 84e73359
[package]
# Note: Semantic Versioning is used: https://semver.org/
version = "0.36.2"
version = "0.36.3"
# Description
title = "Isaac Lab framework for Robot Learning"
......
Changelog
---------
0.36.3 (2025-03-17)
~~~~~~~~~~~~~~~~~~~
Fixed
^^^^^
* Fixed default behavior of :class:`~isaaclab.actuators.ImplicitActuator` if no :attr:`effort_limits_sim` or
:attr:`effort_limit` is set.
0.36.2 (2025-03-12)
~~~~~~~~~~~~~~~~~~~
......
......@@ -88,6 +88,13 @@ class ActuatorBase(ABC):
friction: torch.Tensor
"""The joint friction of the actuator joints. Shape is (num_envs, num_joints)."""
_DEFAULT_MAX_EFFORT_SIM: ClassVar[float] = 1.0e9
"""The default maximum effort for the actuator joints in the simulation. Defaults to 1.0e9.
If the :attr:`ActuatorBaseCfg.effort_limit_sim` is not specified and the actuator is an explicit
actuator, then this value is used.
"""
def __init__(
self,
cfg: ActuatorBaseCfg,
......@@ -140,8 +147,8 @@ class ActuatorBase(ABC):
# For explicit models, we do not want to enforce the effort limit through the solver
# (unless it is explicitly set)
if not ActuatorBase.is_implicit_model and self.cfg.effort_limit_sim is None:
self.cfg.effort_limit_sim = 1.0e9
if not self.is_implicit_model and self.cfg.effort_limit_sim is None:
self.cfg.effort_limit_sim = self._DEFAULT_MAX_EFFORT_SIM
# parse joint stiffness and damping
self.stiffness = self._parse_joint_parameter(self.cfg.stiffness, stiffness)
......
......@@ -6,6 +6,8 @@
# needed to import for allowing type-hinting: Usd.Stage | None
from __future__ import annotations
import math
import isaacsim.core.utils.stage as stage_utils
import omni.log
import omni.physx.scripts.utils as physx_utils
......@@ -529,7 +531,7 @@ Joint drive properties.
@apply_nested
def modify_joint_drive_properties(
prim_path: str, drive_props: schemas_cfg.JointDrivePropertiesCfg, stage: Usd.Stage | None = None
prim_path: str, cfg: schemas_cfg.JointDrivePropertiesCfg, stage: Usd.Stage | None = None
) -> bool:
"""Modify PhysX parameters for a joint prim.
......@@ -552,7 +554,7 @@ def modify_joint_drive_properties(
Args:
prim_path: The prim path where to apply the joint drive schema.
drive_props: The configuration for the joint drive.
cfg: The configuration for the joint drive.
stage: The stage where to find the prim. Defaults to None, in which case the
current stage is used.
......@@ -587,10 +589,43 @@ def modify_joint_drive_properties(
usd_drive_api = UsdPhysics.DriveAPI(prim, drive_api_name)
if not usd_drive_api:
usd_drive_api = UsdPhysics.DriveAPI.Apply(prim, drive_api_name)
# check if prim has Physx joint drive applied on it
physx_joint_api = PhysxSchema.PhysxJointAPI(prim)
if not physx_joint_api:
physx_joint_api = PhysxSchema.PhysxJointAPI.Apply(prim)
# mapping from configuration name to USD attribute name
cfg_to_usd_map = {
"max_velocity": "max_joint_velocity",
"max_effort": "max_force",
"drive_type": "type",
}
# convert to dict
cfg = cfg.to_dict()
# change the drive type to input
if drive_props.drive_type is not None:
usd_drive_api.CreateTypeAttr().Set(drive_props.drive_type)
# check if linear drive
is_linear_drive = prim.IsA(UsdPhysics.PrismaticJoint)
# convert values for angular drives from radians to degrees units
if not is_linear_drive:
if cfg["max_velocity"] is not None:
# rad / s --> deg / s
cfg["max_velocity"] = cfg["max_velocity"] * 180.0 / math.pi
if cfg["stiffness"] is not None:
# N-m/rad --> N-m/deg
cfg["stiffness"] = cfg["stiffness"] * math.pi / 180.0
if cfg["damping"] is not None:
# N-m-s/rad --> N-m-s/deg
cfg["damping"] = cfg["damping"] * math.pi / 180.0
# set into PhysX API
for attr_name in ["max_velocity"]:
value = cfg.pop(attr_name, None)
attr_name = cfg_to_usd_map[attr_name]
safe_set_attribute_on_usd_schema(physx_joint_api, attr_name, value, camel_case=True)
# set into USD API
for attr_name, attr_value in cfg.items():
attr_name = cfg_to_usd_map.get(attr_name, attr_name)
safe_set_attribute_on_usd_schema(usd_drive_api, attr_name, attr_value, camel_case=True)
return True
......
......@@ -199,6 +199,36 @@ class JointDrivePropertiesCfg:
then the joint is driven by an acceleration (usually used for kinematic joints).
"""
max_effort: float | None = None
"""Maximum effort that can be applied to the joint (in kg-m^2/s^2)."""
max_velocity: float | None = None
"""Maximum velocity of the joint.
The unit depends on the joint model:
* For linear joints, the unit is m/s.
* For angular joints, the unit is rad/s.
"""
stiffness: float | None = None
"""Stiffness of the joint drive.
The unit depends on the joint model:
* For linear joints, the unit is kg-m/s^2 (N/m).
* For angular joints, the unit is kg-m^2/s^2/rad (N-m/rad).
"""
damping: float | None = None
"""Damping of the joint drive.
The unit depends on the joint model:
* For linear joints, the unit is kg-m/s (N-s/m).
* For angular joints, the unit is kg-m^2/s/rad (N-m-s/rad).
"""
@configclass
class FixedTendonPropertiesCfg:
......
......@@ -43,7 +43,14 @@ class FileCfg(RigidObjectSpawnerCfg, DeformableObjectSpawnerCfg):
"""Properties to apply to the fixed tendons (if any)."""
joint_drive_props: schemas.JointDrivePropertiesCfg | None = None
"""Properties to apply to a joint."""
"""Properties to apply to a joint.
.. note::
The joint drive properties set the USD attributes of all the joint drives in the asset.
We recommend using this attribute sparingly and only when necessary. Instead, please use the
:attr:`~isaaclab.assets.ArticulationCfg.actuators` parameter to set the joint drive properties
for specific joints in an articulation.
"""
visual_material_path: str = "material"
"""Path to the visual material to use for the prim. Defaults to "material".
......
......@@ -12,6 +12,7 @@ simulation_app = AppLauncher(headless=True).app
"""Rest everything follows."""
import math
import unittest
import isaacsim.core.utils.prims as prim_utils
......@@ -71,7 +72,9 @@ class TestPhysicsSchema(unittest.TestCase):
torsional_patch_radius=1.0,
)
self.mass_cfg = schemas.MassPropertiesCfg(mass=1.0, density=100.0)
self.joint_cfg = schemas.JointDrivePropertiesCfg(drive_type="acceleration")
self.joint_cfg = schemas.JointDrivePropertiesCfg(
drive_type="acceleration", max_effort=80.0, max_velocity=10.0, stiffness=10.0, damping=0.1
)
def tearDown(self) -> None:
"""Stops simulator after each test."""
......@@ -347,17 +350,44 @@ class TestPhysicsSchema(unittest.TestCase):
# skip names we know are not present
if attr_name == "func":
continue
# manually check joint type
# resolve the drive (linear or angular)
drive_model = "linear" if joint_prim.IsA(UsdPhysics.PrismaticJoint) else "angular"
# manually check joint type since it is a string type
if attr_name == "drive_type":
if joint_prim.IsA(UsdPhysics.PrismaticJoint):
prim_attr_name = "drive:linear:physics:type"
elif joint_prim.IsA(UsdPhysics.RevoluteJoint):
prim_attr_name = "drive:angular:physics:type"
else:
raise ValueError(f"Unknown joint type for prim {joint_prim.GetPrimPath()}")
prim_attr_name = f"drive:{drive_model}:physics:type"
# check the value
self.assertEqual(attr_value, joint_prim.GetAttribute(prim_attr_name).Get())
continue
# non-string attributes
if attr_name == "max_velocity":
prim_attr_name = "physxJoint:maxJointVelocity"
elif attr_name == "max_effort":
prim_attr_name = f"drive:{drive_model}:physics:maxForce"
else:
prim_attr_name = f"drive:{drive_model}:physics:{to_camel_case(attr_name, to='cC')}"
# obtain value from USD API (for angular, these follow degrees unit)
prim_attr_value = joint_prim.GetAttribute(prim_attr_name).Get()
# for angular drives, we expect user to set in radians
# the values reported by USD are in degrees
if drive_model == "angular":
if attr_name == "max_velocity":
# deg / s --> rad / s
prim_attr_value = prim_attr_value * math.pi / 180.0
elif attr_name in ["stiffness", "damping"]:
# N-m/deg or N-m-s/deg --> N-m/rad or N-m-s/rad
prim_attr_value = prim_attr_value * 180.0 / math.pi
# validate the values
self.assertAlmostEqual(
prim_attr_value,
attr_value,
places=5,
msg=f"Failed setting for {prim_attr_name}",
)
elif verbose:
print(f"Skipping prim {joint_prim.GetPrimPath()} as it is not a joint drive api.")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment