Jacky Liang
correctly handling state
922e0e8
import pybullet
from pybullet_utils.bullet_client import BulletClient
import pybullet_data
import threading
from time import sleep
import numpy as np
import os
from consts import BOUNDS, COLORS, PIXEL_SIZE, CORNER_POS
from shapely.geometry import box
# Gripper (Robotiq 2F85) code
class Robotiq2F85:
"""Gripper handling for Robotiq 2F85."""
def __init__(self, robot, tool, p):
self.robot = robot
self.tool = tool
self._p = p
pos = [0.1339999999999999, -0.49199999999872496, 0.5]
rot = self._p.getQuaternionFromEuler([np.pi, 0, np.pi])
urdf = 'robotiq_2f_85/robotiq_2f_85.urdf'
self.body = self._p.loadURDF(urdf, pos, rot)
self.n_joints = self._p.getNumJoints(self.body)
self.activated = False
# Connect gripper base to robot tool.
self._p.createConstraint(self.robot, tool, self.body, 0, jointType=self._p.JOINT_FIXED, jointAxis=[0, 0, 0], parentFramePosition=[0, 0, 0], childFramePosition=[0, 0, -0.07], childFrameOrientation=self._p.getQuaternionFromEuler([0, 0, np.pi / 2]))
# Set friction coefficients for gripper fingers.
for i in range(self._p.getNumJoints(self.body)):
self._p.changeDynamics(self.body, i, lateralFriction=10.0, spinningFriction=1.0, rollingFriction=1.0, frictionAnchor=True)
# Start thread to handle additional gripper constraints.
self.motor_joint = 1
self.constraints_thread = threading.Thread(target=self.step)
self.constraints_thread.daemon = True
self.constraints_thread.start()
# Control joint positions by enforcing hard contraints on gripper behavior.
# Set one joint as the open/close motor joint (other joints should mimic).
def step(self):
while True:
try:
currj = [self._p.getJointState(self.body, i)[0] for i in range(self.n_joints)]
indj = [6, 3, 8, 5, 10]
targj = [currj[1], -currj[1], -currj[1], currj[1], currj[1]]
self._p.setJointMotorControlArray(self.body, indj, self._p.POSITION_CONTROL, targj, positionGains=np.ones(5))
except:
return
sleep(0.001)
# Close gripper fingers.
def activate(self):
self._p.setJointMotorControl2(self.body, self.motor_joint, self._p.VELOCITY_CONTROL, targetVelocity=1, force=10)
self.activated = True
# Open gripper fingers.
def release(self):
self._p.setJointMotorControl2(self.body, self.motor_joint, self._p.VELOCITY_CONTROL, targetVelocity=-1, force=10)
self.activated = False
# If activated and object in gripper: check object contact.
# If activated and nothing in gripper: check gripper contact.
# If released: check proximity to surface (disabled).
def detect_contact(self):
obj, _, ray_frac = self.check_proximity()
if self.activated:
empty = self.grasp_width() < 0.01
cbody = self.body if empty else obj
if obj == self.body or obj == 0:
return False
return self.external_contact(cbody)
# else:
# return ray_frac < 0.14 or self.external_contact()
# Return if body is in contact with something other than gripper
def external_contact(self, body=None):
if body is None:
body = self.body
pts = self._p.getContactPoints(bodyA=body)
pts = [pt for pt in pts if pt[2] != self.body]
return len(pts) > 0 # pylint: disable=g-explicit-length-test
def check_grasp(self):
while self.moving():
sleep(0.001)
success = self.grasp_width() > 0.01
return success
def grasp_width(self):
lpad = np.array(self._p.getLinkState(self.body, 4)[0])
rpad = np.array(self._p.getLinkState(self.body, 9)[0])
dist = np.linalg.norm(lpad - rpad) - 0.047813
return dist
def check_proximity(self):
ee_pos = np.array(self._p.getLinkState(self.robot, self.tool)[0])
tool_pos = np.array(self._p.getLinkState(self.body, 0)[0])
vec = (tool_pos - ee_pos) / np.linalg.norm((tool_pos - ee_pos))
ee_targ = ee_pos + vec
ray_data = self._p.rayTest(ee_pos, ee_targ)[0]
obj, link, ray_frac = ray_data[0], ray_data[1], ray_data[2]
return obj, link, ray_frac
# Gym-style environment code
class PickPlaceEnv():
def __init__(self, render=False, high_res=False, high_frame_rate=False):
self.dt = 1/480
self.sim_step = 0
# Configure and start PyBullet
# self._p = pybullet.connect(pybullet.DIRECT)
self._p = BulletClient(connection_mode=pybullet.DIRECT)
self._p.configureDebugVisualizer(self._p.COV_ENABLE_GUI, 0)
self._p.setPhysicsEngineParameter(enableFileCaching=0)
assets_path = os.path.dirname(os.path.abspath(""))
self._p.setAdditionalSearchPath(assets_path)
self._p.setAdditionalSearchPath(pybullet_data.getDataPath())
self._p.setTimeStep(self.dt)
self.home_joints = (np.pi / 2, -np.pi / 2, np.pi / 2, -np.pi / 2, 3 * np.pi / 2, 0) # Joint angles: (J0, J1, J2, J3, J4, J5).
self.home_ee_euler = (np.pi, 0, np.pi) # (RX, RY, RZ) rotation in Euler angles.
self.ee_link_id = 9 # Link ID of UR5 end effector.
self.tip_link_id = 10 # Link ID of gripper finger tips.
self.gripper = None
self.render = render
self.high_res = high_res
self.high_frame_rate = high_frame_rate
def reset(self, object_list):
self._p.resetSimulation(self._p.RESET_USE_DEFORMABLE_WORLD)
self._p.setGravity(0, 0, -9.8)
self.cache_video = []
# Temporarily disable rendering to load URDFs faster.
self._p.configureDebugVisualizer(self._p.COV_ENABLE_RENDERING, 0)
# Add robot.
self._p.loadURDF("plane.urdf", [0, 0, -0.001])
self.robot_id = self._p.loadURDF("ur5e/ur5e.urdf", [0, 0, 0], flags=self._p.URDF_USE_MATERIAL_COLORS_FROM_MTL)
self.ghost_id = self._p.loadURDF("ur5e/ur5e.urdf", [0, 0, -10]) # For forward kinematics.
self.joint_ids = [self._p.getJointInfo(self.robot_id, i) for i in range(self._p.getNumJoints(self.robot_id))]
self.joint_ids = [j[0] for j in self.joint_ids if j[2] == self._p.JOINT_REVOLUTE]
# Move robot to home configuration.
for i in range(len(self.joint_ids)):
self._p.resetJointState(self.robot_id, self.joint_ids[i], self.home_joints[i])
# Add gripper.
if self.gripper is not None:
while self.gripper.constraints_thread.is_alive():
self.constraints_thread_active = False
self.gripper = Robotiq2F85(self.robot_id, self.ee_link_id, self._p)
self.gripper.release()
# Add workspace.
plane_shape = self._p.createCollisionShape(self._p.GEOM_BOX, halfExtents=[0.3, 0.3, 0.001])
plane_visual = self._p.createVisualShape(self._p.GEOM_BOX, halfExtents=[0.3, 0.3, 0.001])
plane_id = self._p.createMultiBody(0, plane_shape, plane_visual, basePosition=[0, -0.5, 0])
self._p.changeVisualShape(plane_id, -1, rgbaColor=[0.2, 0.2, 0.2, 1.0])
# Load objects according to config.
self.object_list = object_list
self.obj_name_to_id = {}
obj_xyz = np.zeros((0, 3))
for obj_name in object_list:
if ('block' in obj_name) or ('bowl' in obj_name):
# Get random position 15cm+ from other objects.
while True:
rand_x = np.random.uniform(BOUNDS[0, 0] + 0.1, BOUNDS[0, 1] - 0.1)
rand_y = np.random.uniform(BOUNDS[1, 0] + 0.1, BOUNDS[1, 1] - 0.1)
rand_xyz = np.float32([rand_x, rand_y, 0.03]).reshape(1, 3)
if len(obj_xyz) == 0:
obj_xyz = np.concatenate((obj_xyz, rand_xyz), axis=0)
break
else:
nn_dist = np.min(np.linalg.norm(obj_xyz - rand_xyz, axis=1)).squeeze()
if nn_dist > 0.15:
obj_xyz = np.concatenate((obj_xyz, rand_xyz), axis=0)
break
object_color = COLORS[obj_name.split(' ')[0]]
object_type = obj_name.split(' ')[1]
object_position = rand_xyz.squeeze()
if object_type == 'block':
object_shape = self._p.createCollisionShape(self._p.GEOM_BOX, halfExtents=[0.02, 0.02, 0.02])
object_visual = self._p.createVisualShape(self._p.GEOM_BOX, halfExtents=[0.02, 0.02, 0.02])
object_id = self._p.createMultiBody(0.01, object_shape, object_visual, basePosition=object_position)
elif object_type == 'bowl':
object_position[2] = 0
object_id = self._p.loadURDF("bowl/bowl.urdf", object_position, useFixedBase=1)
self._p.changeVisualShape(object_id, -1, rgbaColor=object_color)
self.obj_name_to_id[obj_name] = object_id
# Re-enable rendering.
self._p.configureDebugVisualizer(self._p.COV_ENABLE_RENDERING, 1)
for _ in range(200):
self._p.stepSimulation()
# record object positions at reset
self.init_pos = {name: self.get_obj_pos(name) for name in object_list}
return self.get_observation()
def servoj(self, joints):
"""Move to target joint positions with position control."""
self._p.setJointMotorControlArray(
bodyIndex=self.robot_id,
jointIndices=self.joint_ids,
controlMode=self._p.POSITION_CONTROL,
targetPositions=joints,
positionGains=[0.01]*6)
def movep(self, position):
"""Move to target end effector position."""
joints = self._p.calculateInverseKinematics(
bodyUniqueId=self.robot_id,
endEffectorLinkIndex=self.tip_link_id,
targetPosition=position,
targetOrientation=self._p.getQuaternionFromEuler(self.home_ee_euler),
maxNumIterations=100)
self.servoj(joints)
def get_ee_pos(self):
ee_xyz = np.float32(self._p.getLinkState(self.robot_id, self.tip_link_id)[0])
return ee_xyz
def step(self, action=None):
"""Do pick and place motion primitive."""
pick_pos, place_pos = action['pick'].copy(), action['place'].copy()
# Set fixed primitive z-heights.
hover_xyz = np.float32([pick_pos[0], pick_pos[1], 0.2])
if pick_pos.shape[-1] == 2:
pick_xyz = np.append(pick_pos, 0.025)
else:
pick_xyz = pick_pos
pick_xyz[2] = 0.025
if place_pos.shape[-1] == 2:
place_xyz = np.append(place_pos, 0.15)
else:
place_xyz = place_pos
place_xyz[2] = 0.15
# Move to object.
ee_xyz = self.get_ee_pos()
while np.linalg.norm(hover_xyz - ee_xyz) > 0.01:
self.movep(hover_xyz)
self.step_sim_and_render()
ee_xyz = self.get_ee_pos()
while np.linalg.norm(pick_xyz - ee_xyz) > 0.01:
self.movep(pick_xyz)
self.step_sim_and_render()
ee_xyz = self.get_ee_pos()
# Pick up object.
self.gripper.activate()
for _ in range(240):
self.step_sim_and_render()
while np.linalg.norm(hover_xyz - ee_xyz) > 0.01:
self.movep(hover_xyz)
self.step_sim_and_render()
ee_xyz = self.get_ee_pos()
for _ in range(50):
self.step_sim_and_render()
# Move to place location.
while np.linalg.norm(place_xyz - ee_xyz) > 0.01:
self.movep(place_xyz)
self.step_sim_and_render()
ee_xyz = self.get_ee_pos()
# Place down object.
while (not self.gripper.detect_contact()) and (place_xyz[2] > 0.03):
place_xyz[2] -= 0.001
self.movep(place_xyz)
for _ in range(3):
self.step_sim_and_render()
self.gripper.release()
for _ in range(240):
self.step_sim_and_render()
place_xyz[2] = 0.2
ee_xyz = self.get_ee_pos()
while np.linalg.norm(place_xyz - ee_xyz) > 0.01:
self.movep(place_xyz)
self.step_sim_and_render()
ee_xyz = self.get_ee_pos()
place_xyz = np.float32([0, -0.5, 0.2])
while np.linalg.norm(place_xyz - ee_xyz) > 0.01:
self.movep(place_xyz)
self.step_sim_and_render()
ee_xyz = self.get_ee_pos()
observation = self.get_observation()
reward = self.get_reward()
done = False
info = {}
return observation, reward, done, info
def set_alpha_transparency(self, alpha: float) -> None:
for id in range(20):
visual_shape_data = self._p.getVisualShapeData(id)
for i in range(len(visual_shape_data)):
object_id, link_index, _, _, _, _, _, rgba_color = visual_shape_data[i]
rgba_color = list(rgba_color[0:3]) + [alpha]
self._p.changeVisualShape(
self.robot_id, linkIndex=i, rgbaColor=rgba_color)
self._p.changeVisualShape(
self.gripper.body, linkIndex=i, rgbaColor=rgba_color)
def step_sim_and_render(self):
self._p.stepSimulation()
self.sim_step += 1
interval = 40 if self.high_frame_rate else 60
# Render current image at 8 FPS.
if self.sim_step % interval == 0 and self.render:
self.cache_video.append(self.get_camera_image())
def get_camera_image(self):
if not self.high_res:
image_size = (240, 240)
intrinsics = (120., 0, 120., 0, 120., 120., 0, 0, 1)
else:
image_size=(360, 360)
intrinsics=(180., 0, 180., 0, 180., 180., 0, 0, 1)
color, _, _, _, _ = self.render_image(image_size, intrinsics)
return color
def get_reward(self):
return None
def get_observation(self):
observation = {}
# Render current image.
color, depth, position, orientation, intrinsics = self.render_image()
# Get heightmaps and colormaps.
points = self.get_pointcloud(depth, intrinsics)
position = np.float32(position).reshape(3, 1)
rotation = self._p.getMatrixFromQuaternion(orientation)
rotation = np.float32(rotation).reshape(3, 3)
transform = np.eye(4)
transform[:3, :] = np.hstack((rotation, position))
points = self.transform_pointcloud(points, transform)
heightmap, colormap, xyzmap = self.get_heightmap(points, color, BOUNDS, PIXEL_SIZE)
observation["image"] = colormap
observation["xyzmap"] = xyzmap
return observation
def render_image(self, image_size=(720, 720), intrinsics=(360., 0, 360., 0, 360., 360., 0, 0, 1)):
# Camera parameters.
position = (0, -0.85, 0.4)
orientation = (np.pi / 4 + np.pi / 48, np.pi, np.pi)
orientation = self._p.getQuaternionFromEuler(orientation)
zrange = (0.01, 10.)
noise=True
# OpenGL camera settings.
lookdir = np.float32([0, 0, 1]).reshape(3, 1)
updir = np.float32([0, -1, 0]).reshape(3, 1)
rotation = self._p.getMatrixFromQuaternion(orientation)
rotm = np.float32(rotation).reshape(3, 3)
lookdir = (rotm @ lookdir).reshape(-1)
updir = (rotm @ updir).reshape(-1)
lookat = position + lookdir
focal_len = intrinsics[0]
znear, zfar = (0.01, 10.)
viewm = self._p.computeViewMatrix(position, lookat, updir)
fovh = (image_size[0] / 2) / focal_len
fovh = 180 * np.arctan(fovh) * 2 / np.pi
# Notes: 1) FOV is vertical FOV 2) aspect must be float
aspect_ratio = image_size[1] / image_size[0]
projm = self._p.computeProjectionMatrixFOV(fovh, aspect_ratio, znear, zfar)
# Render with OpenGL camera settings.
_, _, color, depth, segm = self._p.getCameraImage(
width=image_size[1],
height=image_size[0],
viewMatrix=viewm,
projectionMatrix=projm,
shadow=1,
flags=self._p.ER_SEGMENTATION_MASK_OBJECT_AND_LINKINDEX,
renderer=self._p.ER_BULLET_HARDWARE_OPENGL)
# Get color image.
color_image_size = (image_size[0], image_size[1], 4)
color = np.array(color, dtype=np.uint8).reshape(color_image_size)
color = color[:, :, :3] # remove alpha channel
if noise:
color = np.int32(color)
color += np.int32(np.random.normal(0, 3, color.shape))
color = np.uint8(np.clip(color, 0, 255))
# Get depth image.
depth_image_size = (image_size[0], image_size[1])
zbuffer = np.float32(depth).reshape(depth_image_size)
depth = (zfar + znear - (2 * zbuffer - 1) * (zfar - znear))
depth = (2 * znear * zfar) / depth
if noise:
depth += np.random.normal(0, 0.003, depth.shape)
intrinsics = np.float32(intrinsics).reshape(3, 3)
return color, depth, position, orientation, intrinsics
def get_pointcloud(self, depth, intrinsics):
"""Get 3D pointcloud from perspective depth image.
Args:
depth: HxW float array of perspective depth in meters.
intrinsics: 3x3 float array of camera intrinsics matrix.
Returns:
points: HxWx3 float array of 3D points in camera coordinates.
"""
height, width = depth.shape
xlin = np.linspace(0, width - 1, width)
ylin = np.linspace(0, height - 1, height)
px, py = np.meshgrid(xlin, ylin)
px = (px - intrinsics[0, 2]) * (depth / intrinsics[0, 0])
py = (py - intrinsics[1, 2]) * (depth / intrinsics[1, 1])
points = np.float32([px, py, depth]).transpose(1, 2, 0)
return points
def transform_pointcloud(self, points, transform):
"""Apply rigid transformation to 3D pointcloud.
Args:
points: HxWx3 float array of 3D points in camera coordinates.
transform: 4x4 float array representing a rigid transformation matrix.
Returns:
points: HxWx3 float array of transformed 3D points.
"""
padding = ((0, 0), (0, 0), (0, 1))
homogen_points = np.pad(points.copy(), padding,
'constant', constant_values=1)
for i in range(3):
points[Ellipsis, i] = np.sum(transform[i, :] * homogen_points, axis=-1)
return points
def get_heightmap(self, points, colors, bounds, pixel_size):
"""Get top-down (z-axis) orthographic heightmap image from 3D pointcloud.
Args:
points: HxWx3 float array of 3D points in world coordinates.
colors: HxWx3 uint8 array of values in range 0-255 aligned with points.
bounds: 3x2 float array of values (rows: X,Y,Z; columns: min,max) defining
region in 3D space to generate heightmap in world coordinates.
pixel_size: float defining size of each pixel in meters.
Returns:
heightmap: HxW float array of height (from lower z-bound) in meters.
colormap: HxWx3 uint8 array of backprojected color aligned with heightmap.
xyzmap: HxWx3 float array of XYZ points in world coordinates.
"""
width = int(np.round((bounds[0, 1] - bounds[0, 0]) / pixel_size))
height = int(np.round((bounds[1, 1] - bounds[1, 0]) / pixel_size))
heightmap = np.zeros((height, width), dtype=np.float32)
colormap = np.zeros((height, width, colors.shape[-1]), dtype=np.uint8)
xyzmap = np.zeros((height, width, 3), dtype=np.float32)
# Filter out 3D points that are outside of the predefined bounds.
ix = (points[Ellipsis, 0] >= bounds[0, 0]) & (points[Ellipsis, 0] < bounds[0, 1])
iy = (points[Ellipsis, 1] >= bounds[1, 0]) & (points[Ellipsis, 1] < bounds[1, 1])
iz = (points[Ellipsis, 2] >= bounds[2, 0]) & (points[Ellipsis, 2] < bounds[2, 1])
valid = ix & iy & iz
points = points[valid]
colors = colors[valid]
# Sort 3D points by z-value, which works with array assignment to simulate
# z-buffering for rendering the heightmap image.
iz = np.argsort(points[:, -1])
points, colors = points[iz], colors[iz]
px = np.int32(np.floor((points[:, 0] - bounds[0, 0]) / pixel_size))
py = np.int32(np.floor((points[:, 1] - bounds[1, 0]) / pixel_size))
px = np.clip(px, 0, width - 1)
py = np.clip(py, 0, height - 1)
heightmap[py, px] = points[:, 2] - bounds[2, 0]
for c in range(colors.shape[-1]):
colormap[py, px, c] = colors[:, c]
xyzmap[py, px, c] = points[:, c]
colormap = colormap[::-1, :, :] # Flip up-down.
xv, yv = np.meshgrid(np.linspace(BOUNDS[0, 0], BOUNDS[0, 1], height),
np.linspace(BOUNDS[1, 0], BOUNDS[1, 1], width))
xyzmap[:, :, 0] = xv
xyzmap[:, :, 1] = yv
xyzmap = xyzmap[::-1, :, :] # Flip up-down.
heightmap = heightmap[::-1, :] # Flip up-down.
return heightmap, colormap, xyzmap
def on_top_of(self, obj_a, obj_b):
"""
check if obj_a is on top of obj_b
condition 1: l2 distance on xy plane is less than a threshold
condition 2: obj_a is higher than obj_b
"""
obj_a_pos = self.get_obj_pos(obj_a)
obj_b_pos = self.get_obj_pos(obj_b)
xy_dist = np.linalg.norm(obj_a_pos[:2] - obj_b_pos[:2])
if obj_b in CORNER_POS:
is_near = xy_dist < 0.06
return is_near
elif 'bowl' in obj_b:
is_near = xy_dist < 0.06
is_higher = obj_a_pos[2] > obj_b_pos[2]
return is_near and is_higher
else:
is_near = xy_dist < 0.04
is_higher = obj_a_pos[2] > obj_b_pos[2]
return is_near and is_higher
def get_obj_id(self, obj_name):
try:
if obj_name in self.obj_name_to_id:
obj_id = self.obj_name_to_id[obj_name]
else:
obj_name = obj_name.replace('circle', 'bowl').replace('square', 'block').replace('small', '').strip()
obj_id = self.obj_name_to_id[obj_name]
return obj_id
except:
raise Exception('Object name "{}" not found'.format(obj_name))
def get_obj_pos(self, obj_name):
obj_name = obj_name.replace('the', '').replace('_', ' ').strip()
if obj_name in CORNER_POS:
position = np.float32(np.array(CORNER_POS[obj_name]))
else:
pick_id = self.get_obj_id(obj_name)
pose = self._p.getBasePositionAndOrientation(pick_id)
position = np.float32(pose[0])
return position
def get_bounding_box(self, obj_name):
obj_id = self.get_obj_id(obj_name)
return self._p.getAABB(obj_id)
class LMP_wrapper():
def __init__(self, env, cfg, render=False):
self.env = env
self._cfg = cfg
self.object_names = list(self._cfg['env']['init_objs'])
self._min_xy = np.array(self._cfg['env']['coords']['bottom_left'])
self._max_xy = np.array(self._cfg['env']['coords']['top_right'])
self._range_xy = self._max_xy - self._min_xy
self._table_z = self._cfg['env']['coords']['table_z']
self.render = render
def is_obj_visible(self, obj_name):
return obj_name in self.object_names
def get_obj_names(self):
return self.object_names[::]
def denormalize_xy(self, pos_normalized):
return pos_normalized * self._range_xy + self._min_xy
def get_corner_positions(self):
unit_square = box(0, 0, 1, 1)
normalized_corners = np.array(list(unit_square.exterior.coords))[:4]
corners = np.array(([self.denormalize_xy(corner) for corner in normalized_corners]))
return corners
def get_side_positions(self):
side_xs = np.array([0, 0.5, 0.5, 1])
side_ys = np.array([0.5, 0, 1, 0.5])
normalized_side_positions = np.c_[side_xs, side_ys]
side_positions = np.array(([self.denormalize_xy(corner) for corner in normalized_side_positions]))
return side_positions
def get_obj_pos(self, obj_name):
# return the xy position of the object in robot base frame
return self.env.get_obj_pos(obj_name)[:2]
def get_obj_position_np(self, obj_name):
return self.get_pos(obj_name)
def get_bbox(self, obj_name):
# return the axis-aligned object bounding box in robot base frame (not in pixels)
# the format is (min_x, min_y, max_x, max_y)
bbox = self.env.get_bounding_box(obj_name)
return bbox
def get_color(self, obj_name):
for color, rgb in COLORS.items():
if color in obj_name:
return rgb
def pick_place(self, pick_pos, place_pos):
pick_pos_xyz = np.r_[pick_pos, [self._table_z]]
place_pos_xyz = np.r_[place_pos, [self._table_z]]
pass
def put_first_on_second(self, arg1, arg2):
# put the object with obj_name on top of target
# target can either be another object name, or it can be an x-y position in robot base frame
pick_pos = self.get_obj_pos(arg1) if isinstance(arg1, str) else arg1
place_pos = self.get_obj_pos(arg2) if isinstance(arg2, str) else arg2
self.env.step(action={'pick': pick_pos, 'place': place_pos})
def get_robot_pos(self):
# return robot end-effector xy position in robot base frame
return self.env.get_ee_pos()
def goto_pos(self, position_xy):
# move the robot end-effector to the desired xy position while maintaining same z
ee_xyz = self.env.get_ee_pos()
position_xyz = np.concatenate([position_xy, ee_xyz[-1]])
while np.linalg.norm(position_xyz - ee_xyz) > 0.01:
self.env.movep(position_xyz)
self.env.step_sim_and_render()
ee_xyz = self.env.get_ee_pos()
def follow_traj(self, traj):
for pos in traj:
self.goto_pos(pos)
def get_corner_positions(self):
normalized_corners = np.array([
[0, 1],
[1, 1],
[0, 0],
[1, 0]
])
return np.array(([self.denormalize_xy(corner) for corner in normalized_corners]))
def get_side_positions(self):
normalized_sides = np.array([
[0.5, 1],
[1, 0.5],
[0.5, 0],
[0, 0.5]
])
return np.array(([self.denormalize_xy(side) for side in normalized_sides]))
def get_corner_name(self, pos):
corner_positions = self.get_corner_positions()
corner_idx = np.argmin(np.linalg.norm(corner_positions - pos, axis=1))
return ['top left corner', 'top right corner', 'bottom left corner', 'botom right corner'][corner_idx]
def get_side_name(self, pos):
side_positions = self.get_side_positions()
side_idx = np.argmin(np.linalg.norm(side_positions - pos, axis=1))
return ['top side', 'right side', 'bottom side', 'left side'][side_idx]