Source code for rofunc.simulator.base_sim

#  Copyright (C) 2024, Junjia Liu
# 
#  This file is part of Rofunc.
# 
#  Rofunc is licensed under the GNU General Public License v3.0.
#  You may use, distribute, and modify this code under the terms of the GPL-3.0.
# 
#  Additional Terms for Commercial Use:
#  Commercial use requires sharing 50% of net profits with the copyright holder.
#  Financial reports and regular payments must be provided as agreed in writing.
#  Non-compliance results in revocation of commercial rights.
# 
#  For more details, see <https://www.gnu.org/licenses/>.
#  Contact: skylark0924@gmail.com

import math
import os
from typing import List

import matplotlib.pyplot as plt
import numpy as np
import torch
from PIL import Image as Im

import rofunc as rf
from rofunc.utils.logger.beauty_logger import beauty_print


[docs]class PlaygroundSim: def __init__(self, args): self.args = args self.up_axis = None self.init_sim() self.init_viewer() # self.init_plane() self.init_terrain() self.init_light()
[docs] def init_sim(self): from isaacgym import gymapi self.up_axis = self.args.sim.up_axis.upper() # Initialize gym self.gym = gymapi.acquire_gym() # Configure sim self.sim_params = gymapi.SimParams() self.sim_params.dt = self.args.sim.dt self.sim_params.substeps = self.args.sim.substeps self.sim_params.gravity = gymapi.Vec3(*self.args.sim.gravity) self.sim_params.up_axis = gymapi.UP_AXIS_Y if self.up_axis == "Y" else gymapi.UP_AXIS_Z if self.args.physics_engine == "flex": self.physics_engine = gymapi.SIM_FLEX for flex_param in self.args.sim.flex: setattr(self.args.sim.flex, flex_param, self.args.sim.flex[flex_param]) elif self.args.physics_engine == "physx": self.physics_engine = gymapi.SIM_PHYSX for physx_param in self.args.sim.physx: setattr(self.args.sim.physx, physx_param, self.args.sim.physx[physx_param]) else: raise ValueError("The physics engine should be in [flex, physx]") self.sim_params.use_gpu_pipeline = self.args.sim.use_gpu_pipeline if self.sim_params.use_gpu_pipeline: beauty_print("WARNING: Forcing CPU pipeline.", type="warning") split_device = self.args.sim_device.split(":") self.device_id = int(split_device[1]) if len(split_device) > 1 else 0 self.sim = self.gym.create_sim(self.device_id, self.args.graphics_device_id, self.physics_engine, self.sim_params) if self.sim is None: beauty_print("Failed to create sim", type="warning") quit()
[docs] def init_viewer(self): from isaacgym import gymapi if self.up_axis == "Y": cam_pos = self.args.get("cam_pos", (3.0, 2.0, 0.0)) cam_target = self.args.get("cam_target", (0.0, 0.0, 0.0)) elif self.up_axis == "Z": cam_pos = self.args.get("cam_pos", (-3.0, 0.0, 2.0)) cam_target = self.args.get("cam_target", (1.0, 0.0, 1.0)) # Create viewer camera_props = gymapi.CameraProperties() camera_props.horizontal_fov = self.args.get("camera_horizontal_fov", 20.0) camera_props.width = self.args.get("camera_width", 1920) camera_props.height = self.args.get("camera_height", 1080) camera_props.use_collision_geometry = self.args.get("camera_use_collision_geometry", False) self.viewer = self.gym.create_viewer(self.sim, camera_props) if self.viewer is None: beauty_print("Failed to create viewer", type="warning") quit() # Point camera at environments self.gym.viewer_camera_look_at(self.viewer, None, gymapi.Vec3(*cam_pos), gymapi.Vec3(*cam_target))
[docs] def init_plane(self): from isaacgym import gymapi # Add ground plane plane_params = gymapi.PlaneParams() if self.up_axis == "Y": plane_params.normal = gymapi.Vec3(0, 1, 0) elif self.up_axis == "Z": plane_params.normal = gymapi.Vec3(0, 0, 1) self.gym.add_ground(self.sim, plane_params)
[docs] def init_terrain(self): from isaacgym import gymapi from isaacgym.terrain_utils import SubTerrain, convert_heightfield_to_trimesh, sloped_terrain heightfield = np.zeros((256, 256), dtype=np.int16) horizontal_scale = 1 # [m] vertical_scale = 1 # [m] # def new_sub_terrain(): return SubTerrain() heightfield = sloped_terrain(SubTerrain(), slope=-0).height_field_raw vertices, triangles = convert_heightfield_to_trimesh(heightfield, horizontal_scale=horizontal_scale, vertical_scale=vertical_scale, slope_threshold=1.5) tm_params = gymapi.TriangleMeshParams() tm_params.nb_vertices = vertices.shape[0] tm_params.nb_triangles = triangles.shape[0] tm_params.transform.p.x = -128. tm_params.transform.p.y = -128. self.gym.add_triangle_mesh(self.sim, vertices.flatten(), triangles.flatten(), tm_params)
[docs] def init_light(self): from isaacgym import gymapi l_color = gymapi.Vec3(1, 1, 1) l_ambient = gymapi.Vec3(0.12, 0.12, 0.12) l_direction = gymapi.Vec3(-1, 1, 1) self.gym.set_light_parameters(self.sim, 0, l_color, l_ambient, l_direction)
[docs]class RobotSim: def __init__(self, args): """ Initialize the robot-centered simulator :param args: arguments """ self.args = args self.num_envs = self.args.env.numEnvs self.num_envs_per_row = self.args.env.numEnvPerRow # Initial gym, sim, and viewer self.PlaygroundSim = PlaygroundSim(self.args) self.gym = self.PlaygroundSim.gym self.sim = self.PlaygroundSim.sim self.viewer = self.PlaygroundSim.viewer self.robot_controller = self.args.env.controller_type self.collision_mode = self.args.env.collision_mode self.create_env() self.setup_robot_dof_prop() if self.args.env.object_asset is not None: self.add_object() if hasattr(self.args.env, "table"): if self.args.env.table is not None: self.add_table()
[docs] def create_env(self): from isaacgym import gymapi # Load robot asset self.robot_asset_root = self.args.env.asset.assetRoot or os.path.join(rf.oslab.get_rofunc_path(), "simulator/assets") self.robot_asset_file = self.args.env.asset.assetFile asset_options = gymapi.AssetOptions() asset_options.fix_base_link = self.args.env.asset.fix_base_link asset_options.disable_gravity = self.args.env.asset.disable_gravity asset_options.flip_visual_attachments = self.args.env.asset.flip_visual_attachments asset_options.armature = self.args.env.asset.armature asset_options.slices_per_cylinder = self.args.env.asset.slices_per_cylinder init_pose = self.args.env.asset.init_pose self.robot_name = self.args.env.asset.robot_name beauty_print("Loading robot asset {} from {}".format(self.robot_asset_file, self.robot_asset_root), type="info") self.robot_asset = self.gym.load_asset(self.sim, self.robot_asset_root, self.robot_asset_file, asset_options) # Set up the env grid spacing = self.args.env.envSpacing env_lower = gymapi.Vec3(-spacing, 0.0, -spacing) env_upper = gymapi.Vec3(spacing, spacing, spacing) envs = [] robot_handles = [] # configure env grid print("Creating %d environments" % self.num_envs) num_per_row = int(self.num_envs_per_row) if self.num_envs_per_row is not None else int(math.sqrt(self.num_envs)) pose = gymapi.Transform() pose.p = gymapi.Vec3(*init_pose[:3]) pose.r = gymapi.Quat(*init_pose[3:7]) for i in range(self.num_envs): # create env env = self.gym.create_env(self.sim, env_lower, env_upper, num_per_row) envs.append(env) # add robot # Create actor # param1 (Env) – Environment Handle. # param2 (Asset) – Asset Handle # param3 (isaacgym.gymapi.Transform) – transform transform of where the actor will be initially placed # param4 (str) – name of the actor # param5 (int) – collision group that actor will be part of. The actor will not collide with anything # outside of the same collisionGroup # param6 (int) – bitwise filter for elements in the same collisionGroup to mask off collision # param7 (int) – segmentation ID used in segmentation camera sensors robot_handle = self.gym.create_actor(env, self.robot_asset, pose, self.robot_name, i, int(self.collision_mode), 0) self.gym.enable_actor_dof_force_sensors(env, robot_handle) robot_handles.append(robot_handle) self.envs = envs self.robot_handles = robot_handles
# self._set_color()
[docs] def set_colors_for_parts(self, handles, wb_decompose_param_rb_ids): # colors = [[255 / 255., 165 / 255., 0 / 255.], [0.54, 0.85, 0.2], [0.5, 0.5, 0.5], [0.35, 0.35, 0.35]] # colors = np.array( # [[0.54 * 255, 0.85 * 255, 0.2 * 255], (210, 105, 30), (106, 90, 205), (138, 43, 226), # (135, 206, 250), (70, 130, 180), (72, 209, 204), (139, 69, 19), (238, 130, 238), # (221, 160, 221), (218, 112, 214), (188, 143, 143), (119, 136, 153), # (153, 50, 204)]) / 255 # (238, 130, 238) colors = np.array([[0.54 * 255, 0.85 * 255, 0.2 * 255], (218, 112, 214), (210, 105, 30)]) / 255 # color_list_used = colors[np.random.randint(0, len(colors), size=len(wb_decompose_param_rb_ids))] for part_i in range(self.num_parts): self.set_char_color(handles, wb_decompose_param_rb_ids[part_i], colors[part_i])
[docs] def set_char_color(self, handles, body_ids, col): """ Set the color of the character's body parts :param body_ids: list of body ids :param col: color in RGB :return: """ from isaacgym import gymapi if isinstance(body_ids, int): body_ids = np.arange(body_ids) for i, env_ptr in enumerate(self.envs): handle = handles[i] for j in body_ids: self.gym.set_rigid_body_color(env_ptr, handle, j, gymapi.MESH_VISUAL, gymapi.Vec3(col[0], col[1], col[2]))
def _set_color(self): from isaacgym import gymapi self.num_bodies = self.gym.get_asset_rigid_body_count(self.robot_asset) col = np.array([70, 70, 70]) / 255 for i in range(self.num_envs): env_ptr = self.envs[i] handle = self.robot_handles[i] for j in range(self.num_bodies): self.gym.set_rigid_body_color(env_ptr, handle, j, gymapi.MESH_VISUAL, gymapi.Vec3(col[0], col[1], col[2]))
[docs] def setup_robot_dof_prop(self): from isaacgym import gymapi gym = self.gym envs = self.envs robot_asset = self.robot_asset robot_handles = self.robot_handles # configure robot dofs robot_dof_props = gym.get_asset_dof_properties(robot_asset) robot_lower_limits = robot_dof_props["lower"] robot_upper_limits = robot_dof_props["upper"] robot_ranges = robot_upper_limits - robot_lower_limits robot_mids = 0.3 * (robot_upper_limits + robot_lower_limits) robot_dof_props["driveMode"][:].fill(gymapi.DOF_MODE_POS) # if "stiffness" not in robot_dof_props.dtype.names: # robot_dof_props["stiffness"][:] = 300 # robot_dof_props["damping"][:] = 30 # elif robot_dof_props["stiffness"][0] == 0: # robot_dof_props["stiffness"][:] = 300 # robot_dof_props["damping"][:] = 30 robot_dof_props["stiffness"][:] = 300 robot_dof_props["damping"][:] = 30 # default dof states and position targets robot_num_dofs = gym.get_asset_dof_count(robot_asset) default_dof_pos = np.zeros(robot_num_dofs, dtype=np.float32) # default_dof_pos[:] = robot_mids[:] default_dof_state = np.zeros(robot_num_dofs, gymapi.DofState.dtype) default_dof_state["pos"] = default_dof_pos # # send to torch # default_dof_pos_tensor = to_torch(default_dof_pos, device=device) for env, robot_handle in zip(envs, robot_handles): # set dof properties gym.set_actor_dof_properties(env, robot_handle, robot_dof_props) # set initial dof states gym.set_actor_dof_states(env, robot_handle, default_dof_state, gymapi.STATE_ALL) # set initial position targets gym.set_actor_dof_position_targets(env, robot_handle, default_dof_pos)
def _init_attractor(self, attracted_rigid_body, attr_type=None, verbose=True): """ Initialize the attractor for tracking the trajectory using the embedded Isaac Gym PID controller :param attracted_rigid_body: the joint to be attracted :param attr_type: the type of the attractor :param verbose: if True, visualize the attractor spheres :return: """ from isaacgym import gymapi from isaacgym import gymutil # Attractor setup attractor_handles = [] attractor_properties = gymapi.AttractorProperties() # Make attractor in all axes attractor_properties.axes = attr_type attractor_properties.stiffness = 5e5 if attr_type == gymapi.AXIS_ALL or attr_type == gymapi.AXIS_TRANSLATION else 5000 attractor_properties.damping = 5e3 if attr_type == gymapi.AXIS_ALL or attr_type == gymapi.AXIS_TRANSLATION else 500 # Create helper geometry used for visualization # Create a wireframe axis axes_geom = gymutil.AxesGeometry(0.1) # Create a wireframe sphere sphere_rot = gymapi.Quat.from_euler_zyx(0.5 * math.pi, 0, 0) sphere_pose = gymapi.Transform(r=sphere_rot) if attr_type == gymapi.AXIS_ALL: sphere_geom = gymutil.WireframeSphereGeometry(0.003, 12, 12, sphere_pose, color=(1, 0, 0)) elif attr_type == gymapi.AXIS_ROTATION: sphere_geom = gymutil.WireframeSphereGeometry(0.003, 12, 12, sphere_pose, color=(0, 1, 0)) elif attr_type == gymapi.AXIS_TRANSLATION: sphere_geom = gymutil.WireframeSphereGeometry(0.003, 12, 12, sphere_pose, color=(0, 0, 1)) else: sphere_geom = gymutil.WireframeSphereGeometry(0.003, 12, 12, sphere_pose, color=(1, 1, 1)) for i in range(len(self.envs)): env = self.envs[i] handle = self.robot_handles[i] body_dict = self.gym.get_actor_rigid_body_dict(env, handle) # beauty_print(f"get_actor_rigid_body_dict: {body_dict}") props = self.gym.get_actor_rigid_body_states(env, handle, gymapi.STATE_POS) attracted_rigid_body_handle = self.gym.find_actor_rigid_body_handle(env, handle, attracted_rigid_body) # Initialize the attractor attractor_properties.target = props['pose'][:][body_dict[attracted_rigid_body]] attractor_properties.rigid_handle = attracted_rigid_body_handle if verbose: # Draw axes and sphere at attractor location gymutil.draw_lines(axes_geom, self.gym, self.viewer, env, attractor_properties.target) gymutil.draw_lines(sphere_geom, self.gym, self.viewer, env, attractor_properties.target) attractor_handle = self.gym.create_rigid_body_attractor(env, attractor_properties) attractor_handles.append(attractor_handle) return attractor_handles, axes_geom, sphere_geom def _setup_attractors(self, traj, attr_rbs, attr_types, verbose=True): """ Setup the attractors for tracking the trajectory using the embedded Isaac Gym PID controller :param traj: the trajectory to be tracked :param attr_rbs: link names to be attracted, same dim as traj :param attr_types: the type of the attractors :param verbose: if True, visualize the attractor spheres :return: """ assert isinstance(attr_rbs, list), "The attracted joints should be a list" assert len(attr_rbs) == len(traj), "The first dimension of trajectory should equal to attr_rbs" assert len(attr_rbs) == len(attr_types), "The first dimension of attr_types should equal to attr_rbs" attractor_handles, axes_geoms, sphere_geoms = [], [], [] for i in range(len(attr_rbs)): attractor_handle, axes_geom, sphere_geom = self._init_attractor(attr_rbs[i], attr_type=attr_types[i], verbose=verbose) attractor_handles.append(attractor_handle) axes_geoms.append(axes_geom) sphere_geoms.append(sphere_geom) return attr_rbs, attractor_handles, axes_geoms, sphere_geoms
[docs] def add_object(self): from isaacgym import gymapi asset_root = self.args.env.object_asset.assetRoot or os.path.join(rf.oslab.get_rofunc_path(), "simulator/assets") asset_files = self.args.env.object_asset.assetFiles asset_options = gymapi.AssetOptions() asset_options.fix_base_link = self.args.env.object_asset.fix_base_link asset_options.use_mesh_materials = True asset_options.mesh_normal_mode = gymapi.COMPUTE_PER_VERTEX asset_options.override_com = True asset_options.override_inertia = True asset_options.vhacd_enabled = True asset_options.disable_gravity = False asset_options.vhacd_params = gymapi.VhacdParams() asset_options.vhacd_params.resolution = 100000 asset_options.default_dof_drive_mode = gymapi.DOF_MODE_NONE init_poses = self.args.env.object_asset.init_poses object_names = self.args.env.object_asset.object_names self.object_assets = [] for asset_file in asset_files: beauty_print("Loading object asset {} from {}".format(asset_file, asset_root), type="info") self.object_assets.append(self.gym.load_asset(self.sim, asset_root, asset_file, asset_options)) self.object_idxs = [] self.object_handles = [] for j in range(len(self.object_assets)): self.object_idxs.append([]) self.object_handles.append([]) for i, env_ptr in enumerate(self.envs): object_start_pose = gymapi.Transform() object_start_pose.p = gymapi.Vec3(*init_poses[j][:3]) object_start_pose.r = gymapi.Quat(*init_poses[j][3:7]) object_handle = self.gym.create_actor(env_ptr, self.object_assets[j], object_start_pose, object_names[j], i, 0, 0) self.object_handles[j].append(object_handle) object_idx = self.gym.get_actor_rigid_body_index(env_ptr, object_handle, 0, gymapi.DOMAIN_SIM) self.object_idxs[j].append(object_idx) return self.object_handles, self.object_idxs
[docs] def add_table(self): from isaacgym import gymapi table_size = self.args.env.table.size init_pose = self.args.env.table.init_pose asset_options = gymapi.AssetOptions() asset_options.fix_base_link = self.args.env.table.fix_base_link self.table_asset = self.gym.create_box(self.sim, table_size[0], table_size[1], table_size[2], asset_options) for i, env_ptr in enumerate(self.envs): table_pose = gymapi.Transform() table_pose.p = gymapi.Vec3(*init_pose[:3]) table_pose.r = gymapi.Quat(*init_pose[3:7]) self.gym.create_actor(env_ptr, self.table_asset, table_pose, "table", i, 0, 0)
[docs] def add_tracking_target_sphere_axes(self): """ Visualize the tracking target as a sphere with axes """ from isaacgym import gymapi, gymutil # Create helper geometry used for visualization # Create a wireframe axis self.axes_geom = gymutil.AxesGeometry(0.1) # Create a wireframe sphere sphere_rot = gymapi.Quat.from_euler_zyx(0.5 * math.pi, 0, 0) sphere_pose = gymapi.Transform(r=sphere_rot) self.sphere_geom = gymutil.WireframeSphereGeometry(0.03, 12, 12, sphere_pose, color=(1, 0, 0))
[docs] def add_marker(self, marker_pose): from isaacgym import gymapi asset_file = "mjcf/location_marker.urdf" asset_options = gymapi.AssetOptions() asset_options.angular_damping = 0.01 asset_options.linear_damping = 0.01 asset_options.max_angular_velocity = 100.0 asset_options.density = 1.0 asset_options.fix_base_link = True asset_options.default_dof_drive_mode = gymapi.DOF_MODE_NONE self._marker_asset = self.gym.load_asset(self.sim, self.asset_root, asset_file, asset_options) self.marker_handles = [] for i in range(self.num_envs): marker_handle = self.gym.create_actor(self.envs[i], self._marker_asset, marker_pose, "marker", i, 2, 0) self.gym.set_rigid_body_color(self.envs[i], marker_handle, 0, gymapi.MESH_VISUAL, gymapi.Vec3(0.8, 0.0, 0.0)) self.marker_handles.append(marker_handle)
[docs] def add_body_attached_camera(self, camera_props=None, attached_body=None, local_transform=None): from isaacgym import gymapi self.camera_handle = self.gym.create_camera_sensor(self.envs[0], camera_props) body_handle = self.gym.find_actor_rigid_body_handle(self.envs[0], self.robot_handles[0], attached_body) self.gym.attach_camera_to_body(self.camera_handle, self.envs[0], body_handle, local_transform, gymapi.FOLLOW_TRANSFORM)
[docs] def monitor_rigid_body_states(self): from isaacgym import gymtorch self._rb_states = self.gym.acquire_rigid_body_state_tensor(self.sim) self.rb_states = gymtorch.wrap_tensor(self._rb_states) self.default_rb_states = self.rb_states.clone()
[docs] def monitor_actor_root_states(self): from isaacgym import gymtorch self._root_states = self.gym.acquire_actor_root_state_tensor(self.sim) self.root_states = gymtorch.wrap_tensor(self._root_states) self.default_root_states = self.root_states.clone()
[docs] def monitor_dof_states(self): from isaacgym import gymtorch self._dof_states = self.gym.acquire_dof_state_tensor(self.sim) self.dof_states = gymtorch.wrap_tensor(self._dof_states)
[docs] def monitor_robot_jacobian(self, robot_name=None): if robot_name is None: robot_name = self.robot_name from isaacgym import gymtorch self._jacobian = self.gym.acquire_jacobian_tensor(self.sim, robot_name) self.jacobian = gymtorch.wrap_tensor(self._jacobian)
[docs] def monitor_robot_mass_matrix(self, robot_name=None): if robot_name is None: robot_name = self.robot_name from isaacgym import gymtorch self._massmatrix = self.gym.acquire_mass_matrix_tensor(self.sim, robot_name) self.massmatrix = gymtorch.wrap_tensor(self._massmatrix)
[docs] def show(self, visual_obs_flag=False): """ Visualize the robot in an interactive viewer :param visual_obs_flag: If True, show the visual observation """ from isaacgym import gymapi beauty_print("Show the {} simulator in the interactive mode".format(self.robot_name), type="module") if visual_obs_flag: fig = plt.figure("Visual observation", figsize=(8, 8)) while not self.gym.query_viewer_has_closed(self.viewer): # Step the physics self.gym.simulate(self.sim) self.gym.fetch_results(self.sim, True) # Step rendering self.gym.step_graphics(self.sim) if visual_obs_flag: # digest image self.gym.render_all_camera_sensors(self.sim) self.gym.start_access_image_tensors(self.sim) cam_img = self.gym.get_camera_image(self.sim, self.envs[0], self.camera_handle, gymapi.IMAGE_COLOR).reshape( 1280, 1280, 4) cam_img = Im.fromarray(cam_img) plt.imshow(cam_img) plt.axis('off') plt.pause(1e-9) fig.clf() self.gym.end_access_image_tensors(self.sim) self.gym.draw_viewer(self.viewer, self.sim, False) self.gym.sync_frame_time(self.sim) beauty_print("Done") self.gym.destroy_viewer(self.viewer) self.gym.destroy_sim(self.sim)
[docs] def get_num_bodies(self, robot_asset): num_bodies = self.gym.get_asset_rigid_body_count(robot_asset) beauty_print("The number of bodies in the robot asset is {}".format(num_bodies), type="info") return num_bodies
[docs] def get_actor_rigid_body_info(self, actor_handle): rigid_body_dict = self.gym.get_actor_rigid_body_dict(self.envs[0], actor_handle) return rigid_body_dict
[docs] def get_dof_info(self): # Gets number of Degree of Freedom for an actor dof_count = self.gym.get_actor_dof_count(self.envs[0], self.robot_handles[0]) # maps degree of freedom names to actor-relative indices dof_dict = self.gym.get_actor_dof_dict(self.envs[0], self.robot_handles[0]) # Gets forces for the actor’s degrees of freedom # dof_forces = self.gym.get_actor_dof_forces(self.envs[0], self.robot_handles[0]) # Gets Frames for Degrees of Freedom of actor # dof_frames = self.gym.get_actor_dof_frames(self.envs[0], self.robot_handles[0]) # Gets names of all degrees of freedom on actor dof_names = self.gym.get_actor_dof_names(self.envs[0], self.robot_handles[0]) # Gets target position for the actor’s degrees of freedom. # dof_position_targets = self.gym.get_actor_dof_position_targets(self.envs[0], self.robot_handles[0]) # Gets properties for all Dofs on an actor. dof_properties = self.gym.get_actor_dof_properties(self.envs[0], self.robot_handles[0]) # Gets state for the actor’s degrees of freedom # dof_states = self.gym.get_actor_dof_states(self.envs[0], self.robot_handles[0], gymapi.STATE_ALL) # Gets target velocity for the actor’s degrees of freedom # dof_velocity_targets = self.gym.get_actor_dof_velocity_targets(self.envs[0], self.robot_handles[0]) return {'dof_count': dof_count, 'dof_dict': dof_dict, 'dof_names': dof_names, 'dof_properties': dof_properties}
[docs] def get_robot_state(self, mode): from isaacgym import gymtorch if mode == 'dof_force': # One force value per each DOF robot_dof_force = np.array(gymtorch.wrap_tensor(self.gym.acquire_dof_force_tensor(self.sim))) beauty_print('DOF forces:\n {}'.format(robot_dof_force), 2) return robot_dof_force elif mode == 'dof_state': # Each DOF state contains position and velocity and force sensor value for i in range(len(self.envs)): # TODO: multi envs robot_dof_force = np.array(self.gym.get_actor_dof_forces(self.envs[i], self.robot_handles[i])).reshape( (-1, 1)) robot_dof_pose_vel = np.array(gymtorch.wrap_tensor(self.gym.acquire_dof_state_tensor(self.sim))) robot_dof_state = np.hstack((robot_dof_pose_vel, robot_dof_force)) beauty_print('DOF states:\n {}'.format(robot_dof_state), 2) return robot_dof_state elif mode == 'dof_pose_vel': # Each DOF state contains position and velocity robot_dof_pose_vel = np.array(gymtorch.wrap_tensor(self.gym.acquire_dof_state_tensor(self.sim))) beauty_print('DOF poses and velocities:\n {}'.format(robot_dof_pose_vel), 2) return robot_dof_pose_vel elif mode == 'dof_pose': # Each DOF pose contains position robot_dof_pose_vel = np.array(gymtorch.wrap_tensor(self.gym.acquire_dof_state_tensor(self.sim))) beauty_print('DOF poses:\n {}'.format(robot_dof_pose_vel[:, 0]), 2) return robot_dof_pose_vel[:, 0] elif mode == 'dof_vel': # Each DOF velocity contains velocity robot_dof_pose_vel = np.array(gymtorch.wrap_tensor(self.gym.acquire_dof_state_tensor(self.sim))) beauty_print('DOF velocities:\n {}'.format(robot_dof_pose_vel[:, 1]), 2) return robot_dof_pose_vel[:, 1] elif mode == 'dof_force_np': for i in range(len(self.envs)): # TODO: multi envs robot_dof_force = self.gym.get_actor_dof_forces(self.envs[i], self.robot_handles[i]) beauty_print('DOF force s:\n {}'.format(robot_dof_force), 2) return robot_dof_force else: raise ValueError("The mode {} is not supported".format(mode))
[docs] def update_robot(self, traj, attractor_handles, axes_geom, sphere_geom, index, verbose=True, index_list=None): from isaacgym import gymutil for i in range(self.num_envs): # Update attractor target from current franka state attractor_properties = self.gym.get_attractor_properties(self.envs[i], attractor_handles[i]) pose = attractor_properties.target # pose.p: (x, y, z), pose.r: (w, x, y, z) if index_list is not None: index = index_list[i] pose.p.x = traj[index, 0] pose.p.y = traj[index, 1] pose.p.z = traj[index, 2] pose.r.w = traj[index, 6] pose.r.x = traj[index, 3] pose.r.y = traj[index, 4] pose.r.z = traj[index, 5] self.gym.set_attractor_target(self.envs[i], attractor_handles[i], pose) if verbose: # Draw axes and sphere at attractor location gymutil.draw_lines(axes_geom, self.gym, self.viewer, self.envs[i], pose) gymutil.draw_lines(sphere_geom, self.gym, self.viewer, self.envs[i], pose)
[docs] def update_object(self, object_handles, object_poses, state_type): """ Update the object pose :param object_handles: :param object_poses: :param state_type: gymapi.STATE_ALL, gymapi.STATE_POS, gymapi.STATE_VEL :return: """ from isaacgym import gymapi for i in range(len(self.envs)): state = self.gym.get_actor_rigid_body_states(self.envs[i], object_handles[i], gymapi.STATE_NONE) state['pose']['p'].fill((object_poses[i][0], object_poses[i][1], object_poses[i][2])) state['pose']['r'].fill((object_poses[i][3], object_poses[i][4], object_poses[i][5], object_poses[i][6])) state['vel']['linear'].fill((0, 0, 0)) state['vel']['angular'].fill((0, 0, 0)) self.gym.set_actor_rigid_body_states(self.envs[i], object_handles[i], state, state_type)
[docs] def run_traj_multi_rigid_bodies(self, traj: List, attr_rbs: List = None, attr_types=None, object_start_pose: List = None, object_end_pose: List = None, object_related_joints: List = None, root_state=None, update_freq=0.001, verbose=True, index_list=None, recursive_play=False ): """ Set multiple attractors to let the robot run the trajectory with multiple rigid bodies. :param traj: a list of trajectories, each trajectory is a numpy array of shape (N, 7) :param attr_rbs: [list], e.g. ["panda_left_hand", "panda_right_hand"] :param attr_types: [list], e.g. [gymapi.AXIS_ALL, gymapi.AXIS_ROTATION, gymapi.AXIS_TRANSLATION] :param object_start_pose: the initial pose of the object :param object_end_pose: the final pose of the object :param object_related_joints: the related joints of the object :param root_state: the root state of the robot :param update_freq: the frequency of updating the robot pose :param verbose: if True, visualize the attractor spheres :param index_list: :param recursive_play: :return: """ from isaacgym import gymtorch assert isinstance(traj, list) and len(traj) > 0, "The trajectory should be a list of numpy arrays" beauty_print('Execute multi rigid bodies trajectory') self.gym.prepare_sim(self.sim) self.monitor_rigid_body_states() self.monitor_actor_root_states() self.monitor_dof_states() if root_state is not None: root_state = root_state.repeat(self.num_envs, 1, 1).reshape((-1, self.num_envs, 13)) self.robot_root_states = self.root_states.view(self.num_envs, 1, -1)[..., 0, :] # Create the attractor attr_rbs, attr_handles, axes_geoms, sphere_geoms = self._setup_attractors(traj, attr_rbs, attr_types, verbose=verbose) # Time to wait in seconds before moving robot next_update_time = 1 index = 0 dof_states = torch.zeros((traj[0].shape[0], self.dof_states.shape[0], self.dof_states.shape[1]), dtype=torch.float32) save_dof_states = True while not self.gym.query_viewer_has_closed(self.viewer): # Every 0.01 seconds the pose of the attractor is updated t = self.gym.get_sim_time(self.sim) if t >= next_update_time: self.gym.refresh_rigid_body_state_tensor(self.sim) self.gym.refresh_actor_root_state_tensor(self.sim) self.gym.refresh_dof_state_tensor(self.sim) if save_dof_states: dof_states[index] = self.dof_states.clone() self.gym.clear_lines(self.viewer) for i in range(len(attr_rbs)): self.update_robot(traj[i], attr_handles[i], axes_geoms[i], sphere_geoms[i], index, verbose, index_list=index_list) if root_state is not None: if index_list is not None: root_state_tmp = torch.vstack([root_state[idx, 0] for idx in index_list]) self.gym.set_actor_root_state_tensor(self.sim, gymtorch.unwrap_tensor( root_state_tmp.reshape(self.root_states.shape))) else: self.gym.set_actor_root_state_tensor(self.sim, gymtorch.unwrap_tensor( root_state[index].reshape(self.root_states.shape))) # Deprecated API for object pose update by attaching to dual robot hands # if self.object_handles is not None: # if index <= 1: # self.object_poses = object_start_pose # self.update_object(self.object_handles, object_start_pose, gymapi.STATE_ALL) # # object_poses = self.object_poses # # get global index of hand in rigid body state tensor # left_hand_idx = self.gym.find_actor_rigid_body_index(self.envs[0], self.robot_handles[0], # object_related_joints[0], gymapi.DOMAIN_SIM) # right_hand_idx = self.gym.find_actor_rigid_body_index(self.envs[0], self.robot_handles[0], # object_related_joints[1], gymapi.DOMAIN_SIM) # left_hand_pos = self.rigid_body_states[left_hand_idx, :3] # left_hand_rot = self.rigid_body_states[left_hand_idx, 3:7] # left_hand_vel = self.rigid_body_states[left_hand_idx, 7:] # right_hand_pos = self.rigid_body_states[right_hand_idx, :3] # right_hand_rot = self.rigid_body_states[right_hand_idx, 3:7] # right_hand_vel = self.rigid_body_states[right_hand_idx, 7:] # # center_pos = (left_hand_pos + right_hand_pos) / 2 # euclidean_dist = np.linalg.norm(np.array(center_pos) - self.current_poses[0][:3]) # # euclidean_dist = np.linalg.norm(left_hand_pos - right_hand_pos) # if euclidean_dist < 0.1: # left_hand_rot_euler = rf.robolab.euler_from_quaternion(left_hand_rot) # right_hand_rot_euler = rf.robolab.euler_from_quaternion(right_hand_rot) # # tmp = left_hand_rot_euler[1] # # object_rot = rf.robolab.quaternion_from_euler(0, left_hand_rot_euler[1], 0) # # object_rot = rf.robolab.quaternion_multiply(left_hand_rot, [0.707, 0, 0.707, 0]) # object_rot = left_hand_rot # # object_pos = (left_hand_pos + right_hand_pos) / 2 # object_poses = np.array([[*object_pos, *object_rot]]) # # done_euclidean_dist = np.linalg.norm( # np.array(self.current_poses[0][:3]) - object_end_pose[:3]) # if done_euclidean_dist < 0.05: # object_poses = self.current_poses # self.object_poses = object_poses # # self.current_poses = object_poses # self.update_object(self.object_handles, object_poses, gymapi.STATE_ALL) next_update_time += update_freq index += 1 if index >= len(traj[i]): if recursive_play: index = 0 else: break # stop the simulation # Step the physics self.gym.simulate(self.sim) self.gym.fetch_results(self.sim, True) # Step rendering self.gym.step_graphics(self.sim) self.gym.draw_viewer(self.viewer, self.sim, False) self.gym.sync_frame_time(self.sim) print("Done") self.gym.destroy_viewer(self.viewer) self.gym.destroy_sim(self.sim) return dof_states