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: ...@@ -63,10 +63,12 @@ Guidelines for modifications:
* Muhong Guo * Muhong Guo
* Nuralem Abizov * Nuralem Abizov
* Özhan Özen * Özhan Özen
* Qian Wan
* Qinxi Yu * Qinxi Yu
* René Zurbrügg * René Zurbrügg
* Ritvik Singh * Ritvik Singh
* Rosario Scalise * Rosario Scalise
* Ryley McCarroll
* Shafeef Omar * Shafeef Omar
* Vladimir Fokow * Vladimir Fokow
* Wei Yang * Wei Yang
...@@ -75,8 +77,6 @@ Guidelines for modifications: ...@@ -75,8 +77,6 @@ Guidelines for modifications:
* Yujian Zhang * Yujian Zhang
* Zhengyu Zhang * Zhengyu Zhang
* Ziqi Fan * Ziqi Fan
* Qian Wan
* Wei Yang
## Acknowledgements ## Acknowledgements
......
...@@ -206,6 +206,7 @@ class ActuatorBase(ABC): ...@@ -206,6 +206,7 @@ class ActuatorBase(ABC):
TypeError: If the parameter value is not of the expected type. TypeError: If the parameter value is not of the expected type.
TypeError: If the default 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 parameter value is None and no default value is provided.
ValueError: If the default value tensor is the wrong shape.
""" """
# create parameter buffer # create parameter buffer
param = torch.zeros(self._num_envs, self.num_joints, device=self._device) param = torch.zeros(self._num_envs, self.num_joints, device=self._device)
...@@ -230,7 +231,14 @@ class ActuatorBase(ABC): ...@@ -230,7 +231,14 @@ class ActuatorBase(ABC):
param[:] = float(default_value) param[:] = float(default_value)
elif isinstance(default_value, torch.Tensor): elif isinstance(default_value, torch.Tensor):
# if tensor, then use the same tensor for all joints # 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: else:
raise TypeError( raise TypeError(
f"Invalid type for default value: {type(default_value)} for " f"Invalid type for default value: {type(default_value)} for "
......
...@@ -1105,12 +1105,10 @@ class Articulation(AssetBase): ...@@ -1105,12 +1105,10 @@ class Articulation(AssetBase):
self._has_implicit_actuators = False self._has_implicit_actuators = False
# cache the values coming from the usd # cache the values coming from the usd
usd_stiffness = self.root_physx_view.get_dof_stiffnesses().clone() self._data.default_joint_stiffness = self.root_physx_view.get_dof_stiffnesses().to(self.device).clone()
usd_damping = self.root_physx_view.get_dof_dampings().clone() self._data.default_joint_damping = self.root_physx_view.get_dof_dampings().to(self.device).clone()
usd_armature = self.root_physx_view.get_dof_armatures().clone() self._data.default_joint_armature = self.root_physx_view.get_dof_armatures().to(self.device).clone()
usd_friction = self.root_physx_view.get_dof_friction_coefficients().clone() self._data.default_joint_friction = self.root_physx_view.get_dof_friction_coefficients().to(self.device).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()
# iterate over all actuator configurations # iterate over all actuator configurations
for actuator_name, actuator_cfg in self.cfg.actuators.items(): for actuator_name, actuator_cfg in self.cfg.actuators.items():
...@@ -1134,12 +1132,12 @@ class Articulation(AssetBase): ...@@ -1134,12 +1132,12 @@ class Articulation(AssetBase):
), ),
num_envs=self.num_instances, num_envs=self.num_instances,
device=self.device, device=self.device,
stiffness=usd_stiffness[:, joint_ids], stiffness=self._data.default_joint_stiffness[:, joint_ids],
damping=usd_damping[:, joint_ids], damping=self._data.default_joint_damping[:, joint_ids],
armature=usd_armature[:, joint_ids], armature=self._data.default_joint_armature[:, joint_ids],
friction=usd_friction[:, joint_ids], friction=self._data.default_joint_friction[:, joint_ids],
effort_limit=usd_effort_limit[:, joint_ids], effort_limit=self.root_physx_view.get_dof_max_forces().to(self.device).clone()[:, joint_ids],
velocity_limit=usd_velocity_limit[:, joint_ids], velocity_limit=self.root_physx_view.get_dof_max_velocities().to(self.device).clone()[:, joint_ids],
) )
# log information on actuator groups # log information on actuator groups
omni.log.info( omni.log.info(
...@@ -1165,14 +1163,9 @@ class Articulation(AssetBase): ...@@ -1165,14 +1163,9 @@ class Articulation(AssetBase):
self.write_joint_effort_limit_to_sim(1.0e9, joint_ids=actuator.joint_indices) 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_armature_to_sim(actuator.armature, joint_ids=actuator.joint_indices)
self.write_joint_friction_to_sim(actuator.friction, joint_ids=actuator.joint_indices) self.write_joint_friction_to_sim(actuator.friction, joint_ids=actuator.joint_indices)
# Store the actual default stiffness and damping values for explicit actuators (not written the sim)
# set the default joint parameters based on the changes from the actuators self._data.default_joint_stiffness[:, actuator.joint_indices] = actuator.stiffness
self._data.default_joint_stiffness = self.root_physx_view.get_dof_stiffnesses().to(device=self.device).clone() self._data.default_joint_damping[:, actuator.joint_indices] = actuator.damping
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()
)
# perform some sanity checks to ensure actuators are prepared correctly # perform some sanity checks to ensure actuators are prepared correctly
total_act_joints = sum(actuator.num_joints for actuator in self.actuators.values()) total_act_joints = sum(actuator.num_joints for actuator in self.actuators.values())
......
...@@ -300,54 +300,57 @@ def randomize_actuator_gains( ...@@ -300,54 +300,57 @@ def randomize_actuator_gains(
.. tip:: .. tip::
For implicit actuators, this function uses CPU tensors to assign the actuator gains into the simulation. 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. 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] asset: Articulation = env.scene[asset_cfg.name]
# resolve environment ids # Resolve environment ids
if env_ids is None: if env_ids is None:
env_ids = torch.arange(env.scene.num_envs, device=asset.device) env_ids = torch.arange(env.scene.num_envs, device=asset.device)
# resolve joint indices def randomize(data: torch.Tensor, params: tuple[float, float]) -> torch.Tensor:
if asset_cfg.joint_ids == slice(None): return _randomize_prop_by_op(
joint_ids_list = range(asset.num_joints) data, params, dim_0_ids=None, dim_1_ids=actuator_indices, operation=operation, distribution=distribution
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."
) )
# sample joint properties from the given ranges and set into the physics simulation # Loop through actuators and randomize gains
# -- stiffness 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: if stiffness_distribution_params is not None:
stiffness = asset.data.default_joint_stiffness.to(asset.device).clone() stiffness = actuator.stiffness[env_ids].clone()
stiffness = _randomize_prop_by_op( stiffness[:, actuator_indices] = asset.data.default_joint_stiffness[env_ids][:, global_indices].clone()
stiffness, stiffness_distribution_params, env_ids, joint_ids, operation=operation, distribution=distribution randomize(stiffness, stiffness_distribution_params)
)[env_ids][:, joint_ids] actuator.stiffness[env_ids] = stiffness
asset.write_joint_stiffness_to_sim(stiffness, joint_ids=joint_ids, env_ids=env_ids) if isinstance(actuator, ImplicitActuator):
# -- damping 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: if damping_distribution_params is not None:
damping = asset.data.default_joint_damping.to(asset.device).clone() damping = actuator.damping[env_ids].clone()
damping = _randomize_prop_by_op( damping[:, actuator_indices] = asset.data.default_joint_damping[env_ids][:, global_indices].clone()
damping, damping_distribution_params, env_ids, joint_ids, operation=operation, distribution=distribution randomize(damping, damping_distribution_params)
)[env_ids][:, joint_ids] actuator.damping[env_ids] = damping
asset.write_joint_damping_to_sim(damping, joint_ids=joint_ids, env_ids=env_ids) if isinstance(actuator, ImplicitActuator):
asset.write_joint_damping_to_sim(damping, joint_ids=actuator.joint_indices, env_ids=env_ids)
def randomize_joint_parameters( def randomize_joint_parameters(
...@@ -967,6 +970,7 @@ def _randomize_prop_by_op( ...@@ -967,6 +970,7 @@ def _randomize_prop_by_op(
dim_0_ids = slice(None) dim_0_ids = slice(None)
else: else:
n_dim_0 = len(dim_0_ids) n_dim_0 = len(dim_0_ids)
if not isinstance(dim_1_ids, slice):
dim_0_ids = dim_0_ids[:, None] dim_0_ids = dim_0_ids[:, None]
# -- dim 1 # -- dim 1
if isinstance(dim_1_ids, slice): 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