From bd47d328093966772ffd3ccde39f71cc68b19826 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Fri, 14 Feb 2025 10:53:51 +0100 Subject: [PATCH 1/9] adds initial files --- .../assets/articulation/articulation.py | 83 +++++++++++++++++++ .../assets/articulation/articulation_cfg.py | 6 ++ .../assets/articulation/articulation_data.py | 40 +++++++++ 3 files changed, 129 insertions(+) diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index 67e2e79a53..3c541c49fb 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -503,6 +503,21 @@ def write_joint_state_to_sim( self._data._body_state_w.timestamp = -1.0 self._data._body_link_state_w.timestamp = -1.0 self._data._body_com_state_w.timestamp = -1.0 + + if len(self._data.mimic_joint_names) > 0: + # check for mimic joints + controlled_joints = self._data.mimic_joint_assignements[joint_ids] + valid_mask = controlled_joints != -1 + + child_joint_ids = controlled_joints[valid_mask] + mimic_params = self._data.mimic_joint_infos[child_joint_ids] + ref_joint_pos = self._data.joint_pos[env_ids, joint_ids][:, valid_mask] + mimic_pos = mimic_params[:, 0] * ref_joint_pos + mimic_params[:, 1] + # broadcast env_ids if needed to allow double indexing + if not isinstance(env_ids, slice) and env_ids.ndim == 1: + env_ids = env_ids[:, None] + self._data.joint_pos[env_ids, child_joint_ids] = mimic_pos + # set into simulation self.root_physx_view.set_dof_positions(self._data.joint_pos, indices=physx_env_ids) self.root_physx_view.set_dof_velocities(self._data.joint_vel, indices=physx_env_ids) @@ -1153,6 +1168,74 @@ def _create_buffers(self): self._data.joint_names = self.joint_names self._data.body_names = self.body_names + # load actuated joint indices + actuated_joints_expr = self.cfg.actuated_joints_expr + + if actuated_joints_expr is None: + actuated_joints_expr = [".*"] + elif isinstance(actuated_joints_expr, str): + actuated_joints_expr = [actuated_joints_expr] + + mimic_joints = self.cfg.mimic_joints + if mimic_joints is None: + mimic_joints = {} + + self._data.actuated_joint_names = [] + self._data.actuated_joint_indices = [] + + self._data.mimic_joint_names = [] + self._data.mimic_joint_indices = [] + self._data.mimic_joint_parents_indices = [] + + self._data.mimic_joint_assignements = torch.zeros(self.num_joints, dtype=torch.long, device=self.device) - 1 + self._data.mimic_joint_infos = torch.zeros(self.num_joints, 2, device=self.device) + + import re + + for joint_name in self.joint_names: + for expr in actuated_joints_expr: + if re.fullmatch(expr, joint_name): + if joint_name in self._data.actuated_joint_names: + omni.log.warn( + f"Joint '{joint_name}' is already in the actuated joints list. Multiple expressions are" + " matching the same joint. Ignoring." + ) + continue + self._data.actuated_joint_names.append(joint_name) + self._data.actuated_joint_indices.append(self.joint_names.index(joint_name)) + + for mimic_joint_name in mimic_joints: + if mimic_joint_name == joint_name: + omni.log.info(f"Joint '{joint_name}' is a mimic joint.") + omni.log.info(f"Parent: {mimic_joints[mimic_joint_name]['parent']}") + omni.log.info(f"Multiplier: {mimic_joints[mimic_joint_name].get('multiplier', 1.0)}") + omni.log.info(f"Offset: {mimic_joints[mimic_joint_name].get('offset', 0.0)}") + + parent = mimic_joints[mimic_joint_name]["parent"] + parent_idx = self.joint_names.index(parent) + child_idx = self.joint_names.index(mimic_joint_name) + multiplier = mimic_joints[mimic_joint_name].get("multiplier", 1.0) + offset = mimic_joints[mimic_joint_name].get("offset", 0.0) + self._data.mimic_joint_names.append(mimic_joint_name) + self._data.mimic_joint_indices.append(child_idx) + self._data.mimic_joint_parents_indices.append(parent_idx) + + self._data.mimic_joint_infos[child_idx, 0] = multiplier + self._data.mimic_joint_infos[child_idx, 1] = offset + self._data.mimic_joint_assignements[parent_idx] = child_idx + + break + + if len(self._data.mimic_joint_names) != len(mimic_joints): + raise ValueError("Mimic joint names do not match the number of mimic joints.") + + # convert everything to tensors + self._data.actuated_joint_indices = torch.tensor(self._data.actuated_joint_indices, device=self.device) + self._data.mimic_joint_indices = torch.tensor(self._data.mimic_joint_indices, device=self.device) + self._data.mimic_joint_parents_indices = torch.tensor( + self._data.mimic_joint_parents_indices, device=self.device + ) + # -- bodies self._data.default_mass = self.root_physx_view.get_masses().clone() self._data.default_inertia = self.root_physx_view.get_inertias().clone() diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py b/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py index 8e6ce7e14f..54dd4b384d 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py @@ -48,5 +48,11 @@ class InitialStateCfg(AssetBaseCfg.InitialStateCfg): This is accessible in the articulation data through :attr:`ArticulationData.soft_joint_pos_limits` attribute. """ + actuated_joints_expr: list[str] | str | None = None + """Regular expression to specify the actuated joints. Defaults to None which means all joints are actuated.""" + + mimic_joints: dict[str, dict[str, float | str]] | None = None + """Mimic joints configuration for the articulation. Defaults to None.""" + actuators: dict[str, ActuatorBaseCfg] = MISSING """Actuators for the robot with corresponding joint names.""" diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation_data.py b/source/isaaclab/isaaclab/assets/articulation/articulation_data.py index 038602e1e1..2da5a55e1a 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation_data.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation_data.py @@ -5,6 +5,7 @@ import torch import weakref +from functools import cache import omni.physics.tensors.impl.api as physx @@ -91,6 +92,9 @@ def update(self, dt: float): joint_names: list[str] = None """Joint names in the order parsed by the simulation view.""" + mimic_joint_names: list[str] = None + """Mimic joint names in the order parsed by the simulation view.""" + fixed_tendon_names: list[str] = None """Fixed tendon names in the order parsed by the simulation view.""" @@ -221,6 +225,42 @@ def update(self, dt: float): joint_velocity_limits: torch.Tensor = None """Joint maximum velocity provided to simulation. Shape is (num_instances, num_joints).""" + ## + # Mimic joint properties. + ## + mimic_joint_indices: torch.Tensor = None + + mimic_joint_parents_indices: torch.Tensor = None + + mimic_joint_assignements: torch.Tensor = None + + mimic_joint_infos: torch.Tensor = None + actuated_joint_names: list[str] = None + """Actuated joint names in the order parsed by the simulation view.""" + + actuated_joint_indices: torch.Tensor = None + """Actuated joint indices in the order parsed by the simulation view.""" + + @property + @cache + def underactuated_joint_names(self): + """Underactuated joint names in the order parsed by the simulation view.""" + names = [] + for name in self.joint_names: + if name not in self.actuated_joint_names: + names.append(name) + return names + + @property + @cache + def underactuated_joint_indices(self): + """Underactuated joint indices in the order parsed by the simulation view.""" + indices = [] + for idx, name in enumerate(self.joint_names): + if name not in self.actuated_joint_names: + indices.append(idx) + return indices + ## # Fixed tendon properties. ## From 87809db62438073b7dc27224b9b7f96e36f87658 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 15 Feb 2025 21:57:17 +0100 Subject: [PATCH 2/9] cleans up the code --- .../assets/articulation/articulation.py | 199 +++++++++++------- .../assets/articulation/articulation_cfg.py | 26 ++- .../assets/articulation/articulation_data.py | 31 ++- 3 files changed, 162 insertions(+), 94 deletions(-) diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index 3c541c49fb..d1a56a8c60 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -12,6 +12,7 @@ from collections.abc import Sequence from prettytable import PrettyTable from typing import TYPE_CHECKING +import re import isaacsim.core.utils.stage as stage_utils import omni.log @@ -504,19 +505,8 @@ def write_joint_state_to_sim( self._data._body_link_state_w.timestamp = -1.0 self._data._body_com_state_w.timestamp = -1.0 - if len(self._data.mimic_joint_names) > 0: - # check for mimic joints - controlled_joints = self._data.mimic_joint_assignements[joint_ids] - valid_mask = controlled_joints != -1 - - child_joint_ids = controlled_joints[valid_mask] - mimic_params = self._data.mimic_joint_infos[child_joint_ids] - ref_joint_pos = self._data.joint_pos[env_ids, joint_ids][:, valid_mask] - mimic_pos = mimic_params[:, 0] * ref_joint_pos + mimic_params[:, 1] - # broadcast env_ids if needed to allow double indexing - if not isinstance(env_ids, slice) and env_ids.ndim == 1: - env_ids = env_ids[:, None] - self._data.joint_pos[env_ids, child_joint_ids] = mimic_pos + # apply mimic joints masking + self._apply_mimic_joints_masking(data=self._data.joint_pos, env_ids=env_ids, joint_ids=joint_ids) # set into simulation self.root_physx_view.set_dof_positions(self._data.joint_pos, indices=physx_env_ids) @@ -1145,6 +1135,7 @@ def _initialize_impl(self): self._create_buffers() # process configuration self._process_cfg() + self._process_mimic_joints_cfg() self._process_actuators_cfg() self._process_fixed_tendons() # validate configuration @@ -1168,74 +1159,6 @@ def _create_buffers(self): self._data.joint_names = self.joint_names self._data.body_names = self.body_names - # load actuated joint indices - actuated_joints_expr = self.cfg.actuated_joints_expr - - if actuated_joints_expr is None: - actuated_joints_expr = [".*"] - elif isinstance(actuated_joints_expr, str): - actuated_joints_expr = [actuated_joints_expr] - - mimic_joints = self.cfg.mimic_joints - if mimic_joints is None: - mimic_joints = {} - - self._data.actuated_joint_names = [] - self._data.actuated_joint_indices = [] - - self._data.mimic_joint_names = [] - self._data.mimic_joint_indices = [] - self._data.mimic_joint_parents_indices = [] - - self._data.mimic_joint_assignements = torch.zeros(self.num_joints, dtype=torch.long, device=self.device) - 1 - self._data.mimic_joint_infos = torch.zeros(self.num_joints, 2, device=self.device) - - import re - - for joint_name in self.joint_names: - for expr in actuated_joints_expr: - if re.fullmatch(expr, joint_name): - if joint_name in self._data.actuated_joint_names: - omni.log.warn( - f"Joint '{joint_name}' is already in the actuated joints list. Multiple expressions are" - " matching the same joint. Ignoring." - ) - continue - self._data.actuated_joint_names.append(joint_name) - self._data.actuated_joint_indices.append(self.joint_names.index(joint_name)) - - for mimic_joint_name in mimic_joints: - if mimic_joint_name == joint_name: - omni.log.info(f"Joint '{joint_name}' is a mimic joint.") - omni.log.info(f"Parent: {mimic_joints[mimic_joint_name]['parent']}") - omni.log.info(f"Multiplier: {mimic_joints[mimic_joint_name].get('multiplier', 1.0)}") - omni.log.info(f"Offset: {mimic_joints[mimic_joint_name].get('offset', 0.0)}") - - parent = mimic_joints[mimic_joint_name]["parent"] - parent_idx = self.joint_names.index(parent) - child_idx = self.joint_names.index(mimic_joint_name) - multiplier = mimic_joints[mimic_joint_name].get("multiplier", 1.0) - offset = mimic_joints[mimic_joint_name].get("offset", 0.0) - self._data.mimic_joint_names.append(mimic_joint_name) - self._data.mimic_joint_indices.append(child_idx) - self._data.mimic_joint_parents_indices.append(parent_idx) - - self._data.mimic_joint_infos[child_idx, 0] = multiplier - self._data.mimic_joint_infos[child_idx, 1] = offset - self._data.mimic_joint_assignements[parent_idx] = child_idx - - break - - if len(self._data.mimic_joint_names) != len(mimic_joints): - raise ValueError("Mimic joint names do not match the number of mimic joints.") - - # convert everything to tensors - self._data.actuated_joint_indices = torch.tensor(self._data.actuated_joint_indices, device=self.device) - self._data.mimic_joint_indices = torch.tensor(self._data.mimic_joint_indices, device=self.device) - self._data.mimic_joint_parents_indices = torch.tensor( - self._data.mimic_joint_parents_indices, device=self.device - ) - # -- bodies self._data.default_mass = self.root_physx_view.get_masses().clone() self._data.default_inertia = self.root_physx_view.get_inertias().clone() @@ -1369,6 +1292,89 @@ def _invalidate_initialize_callback(self, event): Internal helpers -- Actuators. """ + def _process_mimic_joints_cfg(self): + """Process and apply mimic joints configuration.""" + # cast to list if string + if isinstance(self.cfg.actuated_joint_names, str): + self.cfg.actuated_joint_names = [self.cfg.actuated_joint_names] + + if not isinstance(self.cfg.mimic_joints_info, dict): + raise TypeError("Mimic joints configuration must be a dictionary.") + + # create buffers for mimic joints + self._data.actuated_joint_names = [] + actuated_joint_indices = [] + + self._data.mimic_joint_names = [] + mimic_joint_indices = [] + + self._data.mimic_joint_assignments = torch.full((self.num_joints,), -1, dtype=torch.long, device=self.device) + self._data.mimic_joint_params = torch.zeros(self.num_joints, 2, device=self.device) + + for joint_idx, joint_name in enumerate(self.joint_names): + # check if joint is actuated + for expr in self.cfg.actuated_joint_names: + if re.fullmatch(expr, joint_name): + # check that joint is not already in the list + if joint_name in self._data.actuated_joint_names: + omni.log.warn( + f"Joint '{joint_name}' is already in the actuated joints list." + " Multiple expressions are matching the same joint. Ignoring...." + ) + continue + # add to actuated joints + self._data.actuated_joint_names.append(joint_name) + actuated_joint_indices.append(joint_idx) + + # we iterate over all mimic joints and check if the current joint is a mimic joint + # if it is, we store the parameters and log information + for mimic_joint_name, mimic_joint_params in self.cfg.mimic_joints_info.items(): + # check if joint is mimic joint + if mimic_joint_name == joint_name: + # parse parameters + parent_name: str = mimic_joint_params["parent"] # type: ignore + multiplier: float = mimic_joint_params["multiplier"] # type: ignore + offset: float = mimic_joint_params.get("offset", 0.0) # type: ignore + + # resolve the indices of the parent and child joints + parent_idx = self.joint_names.index(parent_name) + child_idx = joint_idx + + # add to mimic joints + self._data.mimic_joint_names.append(mimic_joint_name) + mimic_joint_indices.append(child_idx) + + # store parameters + self._data.mimic_joint_assignments[parent_idx] = child_idx + self._data.mimic_joint_params[child_idx, 0] = multiplier + self._data.mimic_joint_params[child_idx, 1] = offset + + # log information on mimic joint + omni.log.info( + f"Mimic joint found at: '{joint_name}' [{joint_idx}]" + f"\n\tParent: {parent_name} [{parent_idx}]" + f"\n\tMultiplier: {multiplier}" + f"\n\tOffset: {offset}" + ) + + break + + # check if we parsed all mimic joints + if len(self._data.mimic_joint_names) != len(self.cfg.mimic_joints_info): + raise ValueError( + "The parsed mimic joint names do not match the number of mimic joints." + f"Expected {len(self.cfg.mimic_joints_info)} but got {len(self._data.mimic_joint_names)}." + f" \tInput Mimic joints info: {list(self.cfg.mimic_joints_info.keys())}" + f" \tParsed Mimic joint names: {self._data.mimic_joint_names}" + ) + # convert everything to tensors for faster indexing + self._data.actuated_joint_indices = torch.tensor( + self._data.actuated_joint_indices, dtype=torch.long, device=self.device + ) + self._data.mimic_joint_indices = torch.tensor( + self._data.mimic_joint_indices, dtype=torch.long, device=self.device + ) + def _process_actuators_cfg(self): """Process and apply articulation joint properties.""" # create actuators @@ -1518,6 +1524,37 @@ def _apply_actuator_model(self): if hasattr(actuator, "gear_ratio"): self._data.gear_ratio[:, actuator.joint_indices] = actuator.gear_ratio + def _apply_mimic_joints_masking(self, data: torch.Tensor, env_ids: torch.Tensor, joint_ids: torch.Tensor): + """Apply mimic joints masking. + + This function applies mimic joints masking to the given data tensor. It checks for mimic joints + in the articulation and applies the mimic joint transformations to the specified joint indices. + + Args: + data: The data tensor to apply mimic joints masking to. Shape is (num_envs, num_joints). + env_ids: The environment ids to apply mimic joints masking to. + joint_ids: The joint ids to apply mimic joints masking to. + """ + # check for trivial case when no mimic joints are present + if len(self._data.mimic_joint_names) == 0: + return + + # check for mimic joints in the provided joint ids + controlled_joints = self._data.mimic_joint_assignments[joint_ids] + valid_mask = controlled_joints != -1 + + # get the child joint ids + child_joint_ids = controlled_joints[valid_mask] + mimic_params = self._data.mimic_joint_params[child_joint_ids] + + # obtain the reference data for the mimic joints (this corresponds to the parent joint data) + mimic_joint_ref_data = data[env_ids, joint_ids][:, valid_mask] + # apply the mimic joint transformations + mimic_joint_data = mimic_params[:, 0] * mimic_joint_ref_data + mimic_params[:, 1] + + # apply the mimic joint transformations + data[env_ids, child_joint_ids] = mimic_joint_data + """ Internal helpers -- Debugging. """ diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py b/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py index 54dd4b384d..8271ae3540 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation_cfg.py @@ -48,11 +48,29 @@ class InitialStateCfg(AssetBaseCfg.InitialStateCfg): This is accessible in the articulation data through :attr:`ArticulationData.soft_joint_pos_limits` attribute. """ - actuated_joints_expr: list[str] | str | None = None - """Regular expression to specify the actuated joints. Defaults to None which means all joints are actuated.""" + actuated_joint_names: list[str] | str = ".*" + """List of joint names or regular expression to specify the actuated joints. Defaults to '.*' which means all + joints are actuated.""" - mimic_joints: dict[str, dict[str, float | str]] | None = None - """Mimic joints configuration for the articulation. Defaults to None.""" + mimic_joints_info: dict[str, dict[str, float | str]] = {} + """Mimic joints configuration for the articulation. Defaults to an empty dictionary. + + The key indicates the name of the joint that is being mimicked. The value is a dictionary with the following keys: + + * ``"parent"``: The name of the parent joint. + * ``"multiplier"``: The multiplier for the mimic joint. + * ``"offset"``: The offset for the mimic joint. Defaults to 0.0. + + For example, the following configuration mimics the joint ``"joint_1"`` with the parent joint ``"joint_0"`` + with a multiplier of ``2.0`` and an offset of ``1.0``: + + .. code-block:: python + + mimic_joints_info = { + "joint_1": {"parent": "joint_0", "multiplier": 2.0, "offset": 1.0} + } + + """ actuators: dict[str, ActuatorBaseCfg] = MISSING """Actuators for the robot with corresponding joint names.""" diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation_data.py b/source/isaaclab/isaaclab/assets/articulation/articulation_data.py index 2da5a55e1a..c9b7dd206e 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation_data.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation_data.py @@ -92,9 +92,6 @@ def update(self, dt: float): joint_names: list[str] = None """Joint names in the order parsed by the simulation view.""" - mimic_joint_names: list[str] = None - """Mimic joint names in the order parsed by the simulation view.""" - fixed_tendon_names: list[str] = None """Fixed tendon names in the order parsed by the simulation view.""" @@ -228,19 +225,35 @@ def update(self, dt: float): ## # Mimic joint properties. ## - mimic_joint_indices: torch.Tensor = None - - mimic_joint_parents_indices: torch.Tensor = None - mimic_joint_assignements: torch.Tensor = None - - mimic_joint_infos: torch.Tensor = None actuated_joint_names: list[str] = None """Actuated joint names in the order parsed by the simulation view.""" actuated_joint_indices: torch.Tensor = None """Actuated joint indices in the order parsed by the simulation view.""" + mimic_joint_names: list[str] = None + """Mimic joint names in the order parsed by the simulation view.""" + + mimic_joint_indices: torch.Tensor = None + """Mimic joint indices in the order parsed by the simulation view.""" + + mimic_joint_assignments: torch.Tensor = None + """Mimic joint assignments in the order parsed by the simulation view. Shape is (num_joints,). + + The index of the tensor corresponds to the index of the parent joint. The value at that index is the index + of the mimic joint that is assigned to the parent joint. + + A value of ``-1`` indicates that the joint is not a parent of any mimic joint. + """ + + mimic_joint_params: torch.Tensor = None + """Mimic joint parameters in the order parsed by the simulation view. Shape is (num_joints, 2). + + The first column and second column correspond to the multiplier and offset for the mimic joint, respectively. + In case of no mimic joint, the values are set to 0.0. + """ + @property @cache def underactuated_joint_names(self): From 73c05ed742550483715d82a331ac8cb77a17efe6 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 15 Feb 2025 22:17:36 +0100 Subject: [PATCH 3/9] renames some funcs --- .../isaaclab/assets/articulation/articulation.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index d1a56a8c60..86f065349c 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -1134,8 +1134,8 @@ def _initialize_impl(self): # create buffers self._create_buffers() # process configuration + self._process_mimic_cfg() self._process_cfg() - self._process_mimic_joints_cfg() self._process_actuators_cfg() self._process_fixed_tendons() # validate configuration @@ -1276,6 +1276,11 @@ def _process_cfg(self): self._data.default_joint_limits = self.root_physx_view.get_dof_limits().to(device=self.device).clone() self._data.joint_limits = self._data.default_joint_limits.clone() + # apply mimic joints masking + self._apply_mimic_joints_masking( + data=self._data.default_joint_pos, env_ids=slice(None), joint_ids=slice(None) + ) + """ Internal simulation callbacks. """ @@ -1292,7 +1297,7 @@ def _invalidate_initialize_callback(self, event): Internal helpers -- Actuators. """ - def _process_mimic_joints_cfg(self): + def _process_mimic_cfg(self): """Process and apply mimic joints configuration.""" # cast to list if string if isinstance(self.cfg.actuated_joint_names, str): @@ -1524,12 +1529,17 @@ def _apply_actuator_model(self): if hasattr(actuator, "gear_ratio"): self._data.gear_ratio[:, actuator.joint_indices] = actuator.gear_ratio - def _apply_mimic_joints_masking(self, data: torch.Tensor, env_ids: torch.Tensor, joint_ids: torch.Tensor): + def _apply_mimic_joints_masking( + self, data: torch.Tensor, env_ids: torch.Tensor | slice, joint_ids: torch.Tensor | slice + ): """Apply mimic joints masking. This function applies mimic joints masking to the given data tensor. It checks for mimic joints in the articulation and applies the mimic joint transformations to the specified joint indices. + Although the function can be used to apply mimic joints masking to any data tensor, it is primarily + used to apply mimic joints masking to the joint positions and velocities. + Args: data: The data tensor to apply mimic joints masking to. Shape is (num_envs, num_joints). env_ids: The environment ids to apply mimic joints masking to. From 4331e6c3053a083dc6ac5582bc3f9479c21f1638 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 15 Feb 2025 22:23:57 +0100 Subject: [PATCH 4/9] fixes a bug --- source/isaaclab/isaaclab/assets/articulation/articulation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index 86f065349c..26aefcd1a9 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -1374,10 +1374,10 @@ def _process_mimic_cfg(self): ) # convert everything to tensors for faster indexing self._data.actuated_joint_indices = torch.tensor( - self._data.actuated_joint_indices, dtype=torch.long, device=self.device + actuated_joint_indices, dtype=torch.long, device=self.device ) self._data.mimic_joint_indices = torch.tensor( - self._data.mimic_joint_indices, dtype=torch.long, device=self.device + mimic_joint_indices, dtype=torch.long, device=self.device ) def _process_actuators_cfg(self): From d2a88362c673e0a2ece2bf696463dc55ccf14c39 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 15 Feb 2025 22:39:20 +0100 Subject: [PATCH 5/9] runs formatter --- .../isaaclab/assets/articulation/articulation.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index 26aefcd1a9..876006e3cc 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -8,11 +8,11 @@ from __future__ import annotations +import re import torch from collections.abc import Sequence from prettytable import PrettyTable from typing import TYPE_CHECKING -import re import isaacsim.core.utils.stage as stage_utils import omni.log @@ -1277,9 +1277,7 @@ def _process_cfg(self): self._data.joint_limits = self._data.default_joint_limits.clone() # apply mimic joints masking - self._apply_mimic_joints_masking( - data=self._data.default_joint_pos, env_ids=slice(None), joint_ids=slice(None) - ) + self._apply_mimic_joints_masking(data=self._data.default_joint_pos, env_ids=slice(None), joint_ids=slice(None)) """ Internal simulation callbacks. @@ -1373,12 +1371,8 @@ def _process_mimic_cfg(self): f" \tParsed Mimic joint names: {self._data.mimic_joint_names}" ) # convert everything to tensors for faster indexing - self._data.actuated_joint_indices = torch.tensor( - actuated_joint_indices, dtype=torch.long, device=self.device - ) - self._data.mimic_joint_indices = torch.tensor( - mimic_joint_indices, dtype=torch.long, device=self.device - ) + self._data.actuated_joint_indices = torch.tensor(actuated_joint_indices, dtype=torch.long, device=self.device) + self._data.mimic_joint_indices = torch.tensor(mimic_joint_indices, dtype=torch.long, device=self.device) def _process_actuators_cfg(self): """Process and apply articulation joint properties.""" From 50e83569fd692a0e37b142964d259e54fb77b76d Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 15 Feb 2025 23:31:42 +0100 Subject: [PATCH 6/9] adds demo script --- scripts/demos/mimic_grippers.py | 283 ++++++++++++++++++ .../assets/articulation/articulation.py | 2 + 2 files changed, 285 insertions(+) create mode 100644 scripts/demos/mimic_grippers.py diff --git a/scripts/demos/mimic_grippers.py b/scripts/demos/mimic_grippers.py new file mode 100644 index 0000000000..b6984af059 --- /dev/null +++ b/scripts/demos/mimic_grippers.py @@ -0,0 +1,283 @@ +# Copyright (c) 2022-2025, The Isaac Lab Project Developers. +# All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause + +"""Launch Isaac Sim Simulator first.""" + +import argparse + +from isaaclab.app import AppLauncher + +# add argparse arguments +parser = argparse.ArgumentParser(description="Tutorial on using the mimic joints.") +parser.add_argument("--num_envs", type=int, default=2, help="Number of environments to spawn.") +# append AppLauncher cli args +AppLauncher.add_app_launcher_args(parser) +# parse the arguments +args_cli = parser.parse_args() + +# launch omniverse app +app_launcher = AppLauncher(args_cli) +simulation_app = app_launcher.app + +"""Rest everything follows.""" + +import math + +import isaaclab.sim as sim_utils +from isaaclab.actuators.actuator_cfg import ImplicitActuatorCfg +from isaaclab.assets import Articulation, ArticulationCfg, AssetBaseCfg +from isaaclab.managers import SceneEntityCfg +from isaaclab.scene import InteractiveScene, InteractiveSceneCfg +from isaaclab.utils import configclass +from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR +from isaaclab.utils.math import subtract_frame_transforms + + +@configclass +class TableTopSceneCfg(InteractiveSceneCfg): + """Configuration for a cart-pole scene.""" + + # ground plane + ground = AssetBaseCfg( + prim_path="/World/defaultGroundPlane", + spawn=sim_utils.GroundPlaneCfg(), + init_state=AssetBaseCfg.InitialStateCfg(pos=(0.0, 0.0, -1.05)), + ) + + # lights + dome_light = AssetBaseCfg( + prim_path="/World/Light", spawn=sim_utils.DomeLightCfg(intensity=3000.0, color=(0.75, 0.75, 0.75)) + ) + + # mount + table = AssetBaseCfg( + prim_path="{ENV_REGEX_NS}/Table", + spawn=sim_utils.UsdFileCfg( + usd_path=f"{ISAAC_NUCLEUS_DIR}/Props/Mounts/Stand/stand_instanceable.usd", scale=(2.0, 2.0, 2.0) + ), + ) + + # articulation + robot = ArticulationCfg( + prim_path="/World/envs/env_.*/Robot", + spawn=sim_utils.UsdFileCfg( + usd_path=( + "omniverse://ov-isaac-dev/Projects/IsaacARM/Assets/UR10/iakinola/ur10e_robotiq_140_variant.usd" + ), + activate_contact_sensors=False, + rigid_props=sim_utils.RigidBodyPropertiesCfg( + disable_gravity=True, + max_depenetration_velocity=5.0, + ), + articulation_props=sim_utils.ArticulationRootPropertiesCfg( + enabled_self_collisions=False, solver_position_iteration_count=12, solver_velocity_iteration_count=1 + ), + ), + init_state=ArticulationCfg.InitialStateCfg( + joint_pos={ + "shoulder_pan_joint": 0.0, + "shoulder_lift_joint": -1.0, + "elbow_joint": 1.0, + "wrist_1_joint": 0.0, + "wrist_2_joint": 0.0, + "wrist_3_joint": 0.0, + }, + pos=(0.0, 0.0, 0.0), + # rot=(0.0, 0.0, 0.0, 1.0), + rot=(1.0, 0.0, 0.0, 0.0), + ), + actuators={ + # 'shoulder_pan_joint', 'shoulder_lift_joint', 'elbow_joint', 'wrist_1_joint', 'wrist_2_joint', 'wrist_3_joint' + "shoulder": ImplicitActuatorCfg( + joint_names_expr=["shoulder_.*"], + effort_limit=None, + velocity_limit=None, + stiffness=None, + damping=None, + friction=None, + armature=None, + # effort_limit=330.0, + # velocity_limit=2.175, + # stiffness=400.0, + # damping=40.0, + # friction=0.0, + # armature=0.0, # 0.57 + ), + "elbow": ImplicitActuatorCfg( + joint_names_expr=["elbow_joint"], + effort_limit=None, + velocity_limit=None, + stiffness=None, + damping=None, + friction=None, + armature=None, + # effort_limit=150.0, + # velocity_limit=2.175, + # stiffness=400.0, + # damping=40.0, + # friction=0.0, + # armature=0.0, # 0.57 + ), + "wrist": ImplicitActuatorCfg( + joint_names_expr=["wrist_.*"], + effort_limit=None, + velocity_limit=None, + stiffness=None, + damping=None, + friction=None, + armature=None, + # effort_limit=56.0, + # velocity_limit=2.175, + # stiffness=400.0, + # damping=40.0, + # friction=0.0, + # armature=0.0, # 0.57 + ), + "finger": ImplicitActuatorCfg( + joint_names_expr=["finger_joint"], + effort_limit=None, + velocity_limit=None, + stiffness=None, + damping=None, + friction=None, + armature=None, + # effort_limit=10.0 * 10, + # velocity_limit=130 / 180 * np.pi, + # stiffness=0.1125, + # damping=0.001, + # # stiffness=400.0, + # # damping=40.0, + # # stiffness=400.0*2, + # # damping=56.568542494923804, + # friction=0.0, + # armature=0.0, + ), + # "left_inner_finger_joint": ImplicitActuatorCfg( + # joint_names_expr=["left_inner_finger_joint"], + # effort_limit=1.0, + # velocity_limit=1e6, + # stiffness=0.002, + # damping=0.0001, + # friction=0.0, + # armature=0.0, + # ), + # "right_inner_finger_joint": ImplicitActuatorCfg( + # joint_names_expr=["right_inner_finger_joint"], + # effort_limit=1.0, + # velocity_limit=1e6, + # stiffness=0.002, + # damping=0.0001, + # friction=0.0, + # armature=0.0, + # ), + }, + actuated_joint_names=["shoulder_.*", "elbow_joint", "wrist_.*", "finger_joint"], + mimic_joints_info={ + "right_outer_knuckle_joint": { + "parent": "finger_joint", + "multiplier": -1.0, + "offset": 0.0, + }, + "right_inner_finger_joint": { + "parent": "finger_joint", + "multiplier": 1.0, + "offset": 0.0, + }, + "right_inner_finger_pad_joint": { + "parent": "finger_joint", + "multiplier": 1.0, + "offset": 0.0, + }, + "left_outer_finger_joint": { + "parent": "finger_joint", + "multiplier": 1.0, + "offset": 0.0, + }, + "left_inner_finger_joint": { + "parent": "finger_joint", + "multiplier": 1.0, + "offset": 0.0, + }, + "left_inner_finger_pad_joint": { + "parent": "finger_joint", + "multiplier": 1.0, + "offset": 0.0, + }, + }, + ) + + +def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene): + """Runs the simulation loop.""" + # Extract scene entities + # note: we only do this here for readability. + robot: Articulation = scene["robot"] + + # Specify robot-specific parameters + robot_entity_cfg = SceneEntityCfg("robot", joint_names=[".*"], body_names=["wrist_3_link"]) + + # Resolving the scene entities + robot_entity_cfg.resolve(scene) + + # Define simulation stepping + sim_dt = sim.get_physics_dt() + count = 0 + # Simulation loop + while simulation_app.is_running(): + # reset + if count % 150 == 0: + # reset time + count = 0 + # reset joint state + joint_pos = robot.data.default_joint_pos.clone() + joint_vel = robot.data.default_joint_vel.clone() + robot.write_joint_state_to_sim(joint_pos, joint_vel) + robot.reset() + # reset actions + joint_pos_des = joint_pos[:, robot_entity_cfg.joint_ids].clone() + + # set gripper joint position + gripper_joint_pos = joint_pos_des[:, -1:] * 0.0 + gripper_width = 0 + gripper_width = math.radians(45) # open: 0, close=45 degrees + gripper_joint_pos += gripper_width + + # apply actions + robot.set_joint_position_target(joint_pos_des[:, :6], joint_ids=robot_entity_cfg.joint_ids[:6]) + robot.set_joint_position_target(gripper_joint_pos, joint_ids=[6]) + + scene.write_data_to_sim() + # perform step + sim.step() + # update sim-time + count += 1 + # update buffers + # import ipdb; ipdb.set_trace() + scene.update(sim_dt) + + +def main(): + """Main function.""" + # Load kit helper + sim_cfg = sim_utils.SimulationCfg(dt=0.01, device=args_cli.device) + sim = sim_utils.SimulationContext(sim_cfg) + # Set main camera + sim.set_camera_view((2.5, 2.5, 2.5), (0.0, 0.0, 0.0)) + # Design scene + scene_cfg = TableTopSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0) + scene = InteractiveScene(scene_cfg) + # Play the simulator + sim.reset() + # Now we are ready! + print("[INFO]: Setup complete...") + # Run the simulator + run_simulator(sim, scene) + + +if __name__ == "__main__": + # run the main function + main() + # close sim app + simulation_app.close() diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index 876006e3cc..0a374be928 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -833,6 +833,8 @@ def set_joint_position_target( env_ids = env_ids[:, None] # set targets self._data.joint_pos_target[env_ids, joint_ids] = target + # apply mimic joints masking + self._apply_mimic_joints_masking(data=self._data.joint_pos_target, env_ids=env_ids, joint_ids=joint_ids) def set_joint_velocity_target( self, target: torch.Tensor, joint_ids: Sequence[int] | slice | None = None, env_ids: Sequence[int] | None = None From dc696588bdb5f0db74e3884e5d038f69586fa36e Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 15 Feb 2025 23:51:20 +0100 Subject: [PATCH 7/9] demo doc --- scripts/demos/mimic_grippers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/demos/mimic_grippers.py b/scripts/demos/mimic_grippers.py index b6984af059..6413333b56 100644 --- a/scripts/demos/mimic_grippers.py +++ b/scripts/demos/mimic_grippers.py @@ -10,7 +10,7 @@ from isaaclab.app import AppLauncher # add argparse arguments -parser = argparse.ArgumentParser(description="Tutorial on using the mimic joints.") +parser = argparse.ArgumentParser(description="Demo on using the mimic joints for Robotiq 140 gripper.") parser.add_argument("--num_envs", type=int, default=2, help="Number of environments to spawn.") # append AppLauncher cli args AppLauncher.add_app_launcher_args(parser) @@ -32,7 +32,6 @@ from isaaclab.scene import InteractiveScene, InteractiveSceneCfg from isaaclab.utils import configclass from isaaclab.utils.assets import ISAAC_NUCLEUS_DIR -from isaaclab.utils.math import subtract_frame_transforms @configclass From 2b6e4e269e5f6519a92d85c883af7d196592eb97 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Mon, 17 Feb 2025 12:08:10 +0100 Subject: [PATCH 8/9] adds limits to joints of gripper --- scripts/demos/mimic_grippers.py | 94 +++++++++---------- .../assets/articulation/articulation.py | 2 +- 2 files changed, 44 insertions(+), 52 deletions(-) diff --git a/scripts/demos/mimic_grippers.py b/scripts/demos/mimic_grippers.py index 6413333b56..d411f3fcea 100644 --- a/scripts/demos/mimic_grippers.py +++ b/scripts/demos/mimic_grippers.py @@ -11,7 +11,7 @@ # add argparse arguments parser = argparse.ArgumentParser(description="Demo on using the mimic joints for Robotiq 140 gripper.") -parser.add_argument("--num_envs", type=int, default=2, help="Number of environments to spawn.") +parser.add_argument("--num_envs", type=int, default=4, help="Number of environments to spawn.") # append AppLauncher cli args AppLauncher.add_app_launcher_args(parser) # parse the arguments @@ -24,7 +24,7 @@ """Rest everything follows.""" import math - +import torch import isaaclab.sim as sim_utils from isaaclab.actuators.actuator_cfg import ImplicitActuatorCfg from isaaclab.assets import Articulation, ArticulationCfg, AssetBaseCfg @@ -82,6 +82,14 @@ class TableTopSceneCfg(InteractiveSceneCfg): "wrist_1_joint": 0.0, "wrist_2_joint": 0.0, "wrist_3_joint": 0.0, + "finger_joint": 0.0, + "right_outer_knuckle_joint": 0.0, + "left_outer_finger_joint": 0.0, + "right_outer_finger_joint": 0.0, + "left_inner_finger_joint": 0.0, + "right_inner_finger_joint": 0.0, + "left_inner_finger_pad_joint": 0.0, + "right_inner_finger_pad_joint": 0.0, }, pos=(0.0, 0.0, 0.0), # rot=(0.0, 0.0, 0.0, 1.0), @@ -153,24 +161,6 @@ class TableTopSceneCfg(InteractiveSceneCfg): # friction=0.0, # armature=0.0, ), - # "left_inner_finger_joint": ImplicitActuatorCfg( - # joint_names_expr=["left_inner_finger_joint"], - # effort_limit=1.0, - # velocity_limit=1e6, - # stiffness=0.002, - # damping=0.0001, - # friction=0.0, - # armature=0.0, - # ), - # "right_inner_finger_joint": ImplicitActuatorCfg( - # joint_names_expr=["right_inner_finger_joint"], - # effort_limit=1.0, - # velocity_limit=1e6, - # stiffness=0.002, - # damping=0.0001, - # friction=0.0, - # armature=0.0, - # ), }, actuated_joint_names=["shoulder_.*", "elbow_joint", "wrist_.*", "finger_joint"], mimic_joints_info={ @@ -179,31 +169,6 @@ class TableTopSceneCfg(InteractiveSceneCfg): "multiplier": -1.0, "offset": 0.0, }, - "right_inner_finger_joint": { - "parent": "finger_joint", - "multiplier": 1.0, - "offset": 0.0, - }, - "right_inner_finger_pad_joint": { - "parent": "finger_joint", - "multiplier": 1.0, - "offset": 0.0, - }, - "left_outer_finger_joint": { - "parent": "finger_joint", - "multiplier": 1.0, - "offset": 0.0, - }, - "left_inner_finger_joint": { - "parent": "finger_joint", - "multiplier": 1.0, - "offset": 0.0, - }, - "left_inner_finger_pad_joint": { - "parent": "finger_joint", - "multiplier": 1.0, - "offset": 0.0, - }, }, ) @@ -220,13 +185,36 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene): # Resolving the scene entities robot_entity_cfg.resolve(scene) + # set the joint limits for the gripper + joint_limits = { + "finger_joint": (0.0, 0.7), + "right_outer_knuckle_joint": (0.0, math.radians(45)), + "left_outer_finger_joint": (0.0, math.radians(45)), + "right_outer_finger_joint": (0.0, math.radians(45)), + "left_inner_finger_pad_joint": (-math.radians(45), 0.0), + "right_inner_finger_pad_joint": (-math.radians(45), 0.0), + "left_inner_finger_joint": (-math.radians(45), 0.0), + "right_inner_finger_joint": (-math.radians(45), 0.0), + } + joint_indices = robot.find_joints(list(joint_limits.keys()), preserve_order=True)[0] + joint_limits_tensor = torch.tensor([joint_limits[joint_name] for joint_name in joint_limits], device=robot.device) + robot.write_joint_limits_to_sim(joint_limits_tensor, joint_ids=joint_indices) + + # in the original asset, some joints have stiffness and damping which leads to oscillations + # we set them to 0 to avoid that (besides the finger joint where we actually want some stiffness) + dummy_ones_tensor = torch.ones_like(joint_limits_tensor[:, 0]) + robot.write_joint_stiffness_to_sim(dummy_ones_tensor[1:] * 0.0, joint_ids=joint_indices[1:]) + robot.write_joint_damping_to_sim(dummy_ones_tensor[1:] * 0.001, joint_ids=joint_indices[1:]) + + # Initial gripper state + toggle_gripper = False # Define simulation stepping sim_dt = sim.get_physics_dt() count = 0 # Simulation loop while simulation_app.is_running(): # reset - if count % 150 == 0: + if count % 500 == 0: # reset time count = 0 # reset joint state @@ -237,16 +225,20 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene): # reset actions joint_pos_des = joint_pos[:, robot_entity_cfg.joint_ids].clone() + # flip the gripper command every 50 steps + if count % 100 == 0: + toggle_gripper = not toggle_gripper + print(f"[Step {count:04d}] Gripper state: {'open' if toggle_gripper else 'closed'}") + # set gripper joint position - gripper_joint_pos = joint_pos_des[:, -1:] * 0.0 - gripper_width = 0 - gripper_width = math.radians(45) # open: 0, close=45 degrees - gripper_joint_pos += gripper_width + gripper_width = 0.0 if toggle_gripper else math.radians(45) # open: 0, close=45 degrees + gripper_joint_pos = torch.full_like(joint_pos_des[:, -1:], gripper_width) # apply actions robot.set_joint_position_target(joint_pos_des[:, :6], joint_ids=robot_entity_cfg.joint_ids[:6]) robot.set_joint_position_target(gripper_joint_pos, joint_ids=[6]) + # write data to sim scene.write_data_to_sim() # perform step sim.step() @@ -265,7 +257,7 @@ def main(): # Set main camera sim.set_camera_view((2.5, 2.5, 2.5), (0.0, 0.0, 0.0)) # Design scene - scene_cfg = TableTopSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0) + scene_cfg = TableTopSceneCfg(num_envs=args_cli.num_envs, env_spacing=2.0, replicate_physics=False) scene = InteractiveScene(scene_cfg) # Play the simulator sim.reset() diff --git a/source/isaaclab/isaaclab/assets/articulation/articulation.py b/source/isaaclab/isaaclab/assets/articulation/articulation.py index 0a374be928..da0de583cb 100644 --- a/source/isaaclab/isaaclab/assets/articulation/articulation.py +++ b/source/isaaclab/isaaclab/assets/articulation/articulation.py @@ -696,7 +696,7 @@ def write_joint_friction_to_sim( def write_joint_limits_to_sim( self, - limits: torch.Tensor | float, + limits: torch.Tensor, joint_ids: Sequence[int] | slice | None = None, env_ids: Sequence[int] | None = None, warn_limit_violation: bool = True, From ee3e227515ee332f0b0cf4a384a95b23fb1cf226 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Mon, 17 Feb 2025 12:23:30 +0100 Subject: [PATCH 9/9] fixes the joint lmits --- scripts/demos/mimic_grippers.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/demos/mimic_grippers.py b/scripts/demos/mimic_grippers.py index d411f3fcea..4a4bae3088 100644 --- a/scripts/demos/mimic_grippers.py +++ b/scripts/demos/mimic_grippers.py @@ -189,12 +189,12 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene): joint_limits = { "finger_joint": (0.0, 0.7), "right_outer_knuckle_joint": (0.0, math.radians(45)), - "left_outer_finger_joint": (0.0, math.radians(45)), - "right_outer_finger_joint": (0.0, math.radians(45)), - "left_inner_finger_pad_joint": (-math.radians(45), 0.0), - "right_inner_finger_pad_joint": (-math.radians(45), 0.0), - "left_inner_finger_joint": (-math.radians(45), 0.0), - "right_inner_finger_joint": (-math.radians(45), 0.0), + "left_outer_finger_joint": (-math.radians(45), 0.0), + "right_outer_finger_joint": (-math.radians(45), 0.0), + "left_inner_finger_pad_joint": (0.0, math.radians(45)), + "right_inner_finger_pad_joint": (0.0, math.radians(45)), + "left_inner_finger_joint": (-math.radians(45), math.radians(45)), + "right_inner_finger_joint": (-math.radians(45), math.radians(45)), } joint_indices = robot.find_joints(list(joint_limits.keys()), preserve_order=True)[0] joint_limits_tensor = torch.tensor([joint_limits[joint_name] for joint_name in joint_limits], device=robot.device) @@ -203,7 +203,7 @@ def run_simulator(sim: sim_utils.SimulationContext, scene: InteractiveScene): # in the original asset, some joints have stiffness and damping which leads to oscillations # we set them to 0 to avoid that (besides the finger joint where we actually want some stiffness) dummy_ones_tensor = torch.ones_like(joint_limits_tensor[:, 0]) - robot.write_joint_stiffness_to_sim(dummy_ones_tensor[1:] * 0.0, joint_ids=joint_indices[1:]) + robot.write_joint_stiffness_to_sim(dummy_ones_tensor[1:] * 0.01, joint_ids=joint_indices[1:]) robot.write_joint_damping_to_sim(dummy_ones_tensor[1:] * 0.001, joint_ids=joint_indices[1:]) # Initial gripper state