Unverified Commit 281f9b0b authored by MoreTorque's avatar MoreTorque Committed by GitHub

Adds event term to randomize gains of explicit actuators (#1005)

# Description
resolves: https://github.com/isaac-sim/IsaacLab/issues/1000
resolves: https://github.com/isaac-sim/IsaacLab/issues/1011
depends: https://github.com/isaac-sim/IsaacLab/issues/1006

## Type of change
- New feature (non-breaking change which adds functionality)
- This change may require a documentation update

## Checklist

- [x] have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] My changes generate no new warnings
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there
- [x] I have made corresponding changes to the documentation
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
parent b3b45484
......@@ -63,10 +63,12 @@ Guidelines for modifications:
* Muhong Guo
* Nuralem Abizov
* Özhan Özen
* Qian Wan
* Qinxi Yu
* René Zurbrügg
* Ritvik Singh
* Rosario Scalise
* Ryley McCarroll
* Shafeef Omar
* Vladimir Fokow
* Wei Yang
......@@ -75,8 +77,6 @@ Guidelines for modifications:
* Yujian Zhang
* Zhengyu Zhang
* Ziqi Fan
* Qian Wan
* Wei Yang
## Acknowledgements
......
......@@ -206,6 +206,7 @@ class ActuatorBase(ABC):
TypeError: If the parameter value is not of the expected type.
TypeError: If the default value is not of the expected type.
ValueError: If the parameter value is None and no default value is provided.
ValueError: If the default value tensor is the wrong shape.
"""
# create parameter buffer
param = torch.zeros(self._num_envs, self.num_joints, device=self._device)
......@@ -230,7 +231,14 @@ class ActuatorBase(ABC):
param[:] = float(default_value)
elif isinstance(default_value, torch.Tensor):
# if tensor, then use the same tensor for all joints
param[:] = default_value.float()
if default_value.shape == (self._num_envs, self.num_joints):
param = default_value.float()
else:
raise ValueError(
"Invalid default value tensor shape.\n"
f"Got: {default_value.shape}\n"
f"Expected: {(self._num_envs, self.num_joints)}"
)
else:
raise TypeError(
f"Invalid type for default value: {type(default_value)} for "
......
......@@ -1105,12 +1105,10 @@ class Articulation(AssetBase):
self._has_implicit_actuators = False
# cache the values coming from the usd
usd_stiffness = self.root_physx_view.get_dof_stiffnesses().clone()
usd_damping = self.root_physx_view.get_dof_dampings().clone()
usd_armature = self.root_physx_view.get_dof_armatures().clone()
usd_friction = self.root_physx_view.get_dof_friction_coefficients().clone()
usd_effort_limit = self.root_physx_view.get_dof_max_forces().clone()
usd_velocity_limit = self.root_physx_view.get_dof_max_velocities().clone()
self._data.default_joint_stiffness = self.root_physx_view.get_dof_stiffnesses().to(self.device).clone()
self._data.default_joint_damping = self.root_physx_view.get_dof_dampings().to(self.device).clone()
self._data.default_joint_armature = self.root_physx_view.get_dof_armatures().to(self.device).clone()
self._data.default_joint_friction = self.root_physx_view.get_dof_friction_coefficients().to(self.device).clone()
# iterate over all actuator configurations
for actuator_name, actuator_cfg in self.cfg.actuators.items():
......@@ -1134,12 +1132,12 @@ class Articulation(AssetBase):
),
num_envs=self.num_instances,
device=self.device,
stiffness=usd_stiffness[:, joint_ids],
damping=usd_damping[:, joint_ids],
armature=usd_armature[:, joint_ids],
friction=usd_friction[:, joint_ids],
effort_limit=usd_effort_limit[:, joint_ids],
velocity_limit=usd_velocity_limit[:, joint_ids],
stiffness=self._data.default_joint_stiffness[:, joint_ids],
damping=self._data.default_joint_damping[:, joint_ids],
armature=self._data.default_joint_armature[:, joint_ids],
friction=self._data.default_joint_friction[:, joint_ids],
effort_limit=self.root_physx_view.get_dof_max_forces().to(self.device).clone()[:, joint_ids],
velocity_limit=self.root_physx_view.get_dof_max_velocities().to(self.device).clone()[:, joint_ids],
)
# log information on actuator groups
omni.log.info(
......@@ -1165,14 +1163,9 @@ class Articulation(AssetBase):
self.write_joint_effort_limit_to_sim(1.0e9, joint_ids=actuator.joint_indices)
self.write_joint_armature_to_sim(actuator.armature, joint_ids=actuator.joint_indices)
self.write_joint_friction_to_sim(actuator.friction, joint_ids=actuator.joint_indices)
# set the default joint parameters based on the changes from the actuators
self._data.default_joint_stiffness = self.root_physx_view.get_dof_stiffnesses().to(device=self.device).clone()
self._data.default_joint_damping = self.root_physx_view.get_dof_dampings().to(device=self.device).clone()
self._data.default_joint_armature = self.root_physx_view.get_dof_armatures().to(device=self.device).clone()
self._data.default_joint_friction = (
self.root_physx_view.get_dof_friction_coefficients().to(device=self.device).clone()
)
# Store the actual default stiffness and damping values for explicit actuators (not written the sim)
self._data.default_joint_stiffness[:, actuator.joint_indices] = actuator.stiffness
self._data.default_joint_damping[:, actuator.joint_indices] = actuator.damping
# perform some sanity checks to ensure actuators are prepared correctly
total_act_joints = sum(actuator.num_joints for actuator in self.actuators.values())
......
......@@ -300,54 +300,57 @@ def randomize_actuator_gains(
.. tip::
For implicit actuators, this function uses CPU tensors to assign the actuator gains into the simulation.
In such cases, it is recommended to use this function only during the initialization of the environment.
Raises:
NotImplementedError: If the joint indices are in explicit motor mode. This operation is currently
not supported for explicit actuator models.
"""
# extract the used quantities (to enable type-hinting)
# Extract the used quantities (to enable type-hinting)
asset: Articulation = env.scene[asset_cfg.name]
# resolve environment ids
# Resolve environment ids
if env_ids is None:
env_ids = torch.arange(env.scene.num_envs, device=asset.device)
# resolve joint indices
if asset_cfg.joint_ids == slice(None):
joint_ids_list = range(asset.num_joints)
joint_ids = slice(None) # for optimization purposes
else:
joint_ids_list = asset_cfg.joint_ids
joint_ids = torch.tensor(asset_cfg.joint_ids, dtype=torch.int, device=asset.device)
# check if none of the joint indices are in explicit motor mode
for joint_index in joint_ids_list:
for act_name, actuator in asset.actuators.items():
# if joint indices are a slice (i.e., all joints are captured) or the joint index is in the actuator
if actuator.joint_indices == slice(None) or joint_index in actuator.joint_indices:
if not isinstance(actuator, ImplicitActuator):
raise NotImplementedError(
"Event term 'randomize_actuator_stiffness_and_damping' is performed on asset"
f" '{asset_cfg.name}' on the joint '{asset.joint_names[joint_index]}' ('{joint_index}') which"
f" uses an explicit actuator model '{act_name}<{actuator.__class__.__name__}>'. This operation"
" is currently not supported for explicit actuator models."
def randomize(data: torch.Tensor, params: tuple[float, float]) -> torch.Tensor:
return _randomize_prop_by_op(
data, params, dim_0_ids=None, dim_1_ids=actuator_indices, operation=operation, distribution=distribution
)
# sample joint properties from the given ranges and set into the physics simulation
# -- stiffness
# Loop through actuators and randomize gains
for actuator in asset.actuators.values():
if isinstance(asset_cfg.joint_ids, slice):
# we take all the joints of the actuator
actuator_indices = slice(None)
if isinstance(actuator.joint_indices, slice):
global_indices = slice(None)
else:
global_indices = torch.tensor(actuator.joint_indices, device=asset.device)
elif isinstance(actuator.joint_indices, slice):
# we take the joints defined in the asset config
global_indices = actuator_indices = torch.tensor(asset_cfg.joint_ids, device=asset.device)
else:
# we take the intersection of the actuator joints and the asset config joints
actuator_joint_indices = torch.tensor(actuator.joint_indices, device=asset.device)
asset_joint_ids = torch.tensor(asset_cfg.joint_ids, device=asset.device)
# the indices of the joints in the actuator that have to be randomized
actuator_indices = torch.nonzero(torch.isin(actuator_joint_indices, asset_joint_ids)).view(-1)
if len(actuator_indices) == 0:
continue
# maps actuator indices that have to be randomized to global joint indices
global_indices = actuator_joint_indices[actuator_indices]
# Randomize stiffness
if stiffness_distribution_params is not None:
stiffness = asset.data.default_joint_stiffness.to(asset.device).clone()
stiffness = _randomize_prop_by_op(
stiffness, stiffness_distribution_params, env_ids, joint_ids, operation=operation, distribution=distribution
)[env_ids][:, joint_ids]
asset.write_joint_stiffness_to_sim(stiffness, joint_ids=joint_ids, env_ids=env_ids)
# -- damping
stiffness = actuator.stiffness[env_ids].clone()
stiffness[:, actuator_indices] = asset.data.default_joint_stiffness[env_ids][:, global_indices].clone()
randomize(stiffness, stiffness_distribution_params)
actuator.stiffness[env_ids] = stiffness
if isinstance(actuator, ImplicitActuator):
asset.write_joint_stiffness_to_sim(stiffness, joint_ids=actuator.joint_indices, env_ids=env_ids)
# Randomize damping
if damping_distribution_params is not None:
damping = asset.data.default_joint_damping.to(asset.device).clone()
damping = _randomize_prop_by_op(
damping, damping_distribution_params, env_ids, joint_ids, operation=operation, distribution=distribution
)[env_ids][:, joint_ids]
asset.write_joint_damping_to_sim(damping, joint_ids=joint_ids, env_ids=env_ids)
damping = actuator.damping[env_ids].clone()
damping[:, actuator_indices] = asset.data.default_joint_damping[env_ids][:, global_indices].clone()
randomize(damping, damping_distribution_params)
actuator.damping[env_ids] = damping
if isinstance(actuator, ImplicitActuator):
asset.write_joint_damping_to_sim(damping, joint_ids=actuator.joint_indices, env_ids=env_ids)
def randomize_joint_parameters(
......@@ -967,6 +970,7 @@ def _randomize_prop_by_op(
dim_0_ids = slice(None)
else:
n_dim_0 = len(dim_0_ids)
if not isinstance(dim_1_ids, slice):
dim_0_ids = dim_0_ids[:, None]
# -- dim 1
if isinstance(dim_1_ids, slice):
......
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