# Copyright 2023, Junjia LIU, jjliu@mae.cuhk.edu.hk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import math
import os
from typing import List
import matplotlib.pyplot as plt
import numpy as np
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()
[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", (0.0, 0.0, 0.0))
# Create viewer
camera_props = gymapi.CameraProperties()
camera_props.horizontal_fov = self.args.get("camera_horizontal_fov", 75.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]class RobotSim:
def __init__(self, args):
"""
Initialize the robot-centered simulator
:param args: arguments
"""
self.args = args
self.num_envs = self.args.env.numEnvs
# 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.self_collision_flag = self.args.env.selfCollisionFlag
self.create_env()
self.setup_robot_dof_prop()
if self.args.env.object_asset is not None:
self.add_object()
[docs] def create_env(self):
from isaacgym import gymapi
# Load robot asset
asset_root = self.args.env.asset.assetRoot or os.path.join(rf.oslab.get_rofunc_path(), "simulator/assets")
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(asset_file, asset_root), type="info")
self.robot_asset = self.gym.load_asset(self.sim, asset_root, 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(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, -1 refers to open self-collision detection
robot_handle = self.gym.create_actor(env, self.robot_asset, pose, self.robot_name, i,
-1 if self.self_collision_flag else 2)
self.gym.enable_actor_dof_force_sensors(env, robot_handle)
robot_handles.append(robot_handle)
self.envs = envs
self.robot_handles = robot_handles
[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)
robot_dof_props["stiffness"][:].fill(300.0)
robot_dof_props["damping"][:].fill(30.0)
# 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, 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 verbose: if True, visualize the attractor spheres
:return:
"""
from isaacgym import gymapi
from isaacgym import gymutil
# Attractor setup
attractor_handles = []
attractor_properties = gymapi.AttractorProperties()
attractor_properties.stiffness = 5e5
attractor_properties.damping = 5e3
# Make attractor in all axes
attractor_properties.axes = gymapi.AXIS_ALL
# 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)
sphere_geom = gymutil.WireframeSphereGeometry(0.03, 12, 12, sphere_pose, color=(1, 0, 0))
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.target.p.y -= 0.1
attractor_properties.target.p.z = 0.1
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, attracted_rigid_bodies, verbose=True):
assert isinstance(attracted_rigid_bodies, list), "The attracted joints should be a list"
assert len(attracted_rigid_bodies) > 0, "The length of the attracted joints should be greater than 0"
assert len(attracted_rigid_bodies) == len(
traj), "The first dimension of trajectory should equal to attracted_rigid_bodies"
attractor_handles, axes_geoms, sphere_geoms = [], [], []
for i in range(len(attracted_rigid_bodies)):
attractor_handle, axes_geom, sphere_geom = self._init_attractor(attracted_rigid_bodies[i], verbose=verbose)
attractor_handles.append(attractor_handle)
axes_geoms.append(axes_geom)
sphere_geoms.append(sphere_geom)
return attracted_rigid_bodies, attractor_handles, axes_geoms, sphere_geoms
[docs] def add_object(self):
# TODO
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, 2, 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_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)
[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):
from isaacgym import gymapi
robot_asset = self.gym.load_asset(self.sim, self.asset_root, self.asset_file, gymapi.AssetOptions())
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), 2)
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):
raise NotImplementedError
[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, attracted_rigid_bodies: List = None,
object_start_pose: List = None, object_end_pose: List = None,
object_related_joints: List = None,
update_freq=0.001, verbose=True):
"""
Run the trajectory with multiple rigid bodies, the default is to run the trajectory with the left and right hand of
bimanual robot.
:param traj: a list of trajectories, each trajectory is a numpy array of shape (N, 7)
:param attracted_rigid_bodies: [list], e.g. ["panda_left_hand", "panda_right_hand"]
:param update_freq: the frequency of updating the robot pose
:param verbose: if True, visualize the attractor spheres
:return:
"""
assert isinstance(traj, list) and len(traj) > 0, "The trajectory should be a list of numpy arrays"
beauty_print('Execute multi rigid bodies trajectory')
# Create the attractor
attracted_rigid_bodies, attractor_handles, axes_geoms, sphere_geoms = self._setup_attractors(traj,
attracted_rigid_bodies,
verbose=verbose)
# Time to wait in seconds before moving robot
next_update_time = 1
index = 0
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)
self.gym.refresh_rigid_body_state_tensor(self.sim)
if t >= next_update_time:
self.gym.clear_lines(self.viewer)
for i in range(len(attracted_rigid_bodies)):
self.update_robot(traj[i], attractor_handles[i], axes_geoms[i], sphere_geoms[i], index, verbose)
# 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]):
index = 0
# 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)