コード例 #1
0
    def __init__(self,
                 env: iGibsonEnv = None,
                 base_mp_algo: str = 'birrt',
                 arm_mp_algo: str = 'birrt',
                 optimize_iter: int = 0,
                 fine_motion_plan: bool = True):
        """
        Get planning related parameters.
        """
        self.env = env
        assert 'occupancy_grid' in self.env.output
        # get planning related parameters from env
        self.robot_id = self.env.robots[0].robot_ids[0]
        # self.mesh_id = self.scene.mesh_body_id
        # mesh id should not be used
        self.map_size = self.env.scene.trav_map_original_size * \
            self.env.scene.trav_map_default_resolution

        self.grid_resolution = self.env.grid_resolution
        self.occupancy_range = self.env.sensors['scan_occ'].occupancy_range
        self.robot_footprint_radius = self.env.sensors[
            'scan_occ'].robot_footprint_radius
        self.robot_footprint_radius_in_map = self.env.sensors[
            'scan_occ'].robot_footprint_radius_in_map
        self.robot = self.env.robots[0]
        self.base_mp_algo = base_mp_algo
        self.arm_mp_algo = arm_mp_algo
        self.base_mp_resolutions = np.array([0.05, 0.05, 0.05])
        self.optimize_iter = optimize_iter
        self.mode = self.env.mode
        self.initial_height = self.env.initial_pos_z_offset
        self.fine_motion_plan = fine_motion_plan
        self.robot_type = self.env.config['robot']

        if self.env.simulator.viewer is not None:
            self.env.simulator.viewer.setup_motion_planner(self)

        if self.robot_type in ['Fetch', 'Movo']:
            self.setup_arm_mp()

        self.arm_interaction_length = 0.2

        self.marker = None
        self.marker_direction = None

        if self.mode in ['gui', 'iggui']:
            self.marker = VisualMarker(radius=0.04, rgba_color=[0, 0, 1, 1])
            self.marker_direction = VisualMarker(visual_shape=p.GEOM_CAPSULE,
                                                 radius=0.01,
                                                 length=0.2,
                                                 initial_offset=[0, 0, -0.1],
                                                 rgba_color=[0, 0, 1, 1])
            self.env.simulator.import_object(self.marker, use_pbr=False)
            self.env.simulator.import_object(self.marker_direction,
                                             use_pbr=False)
コード例 #2
0
ファイル: viewer.py プロジェクト: monarchdemon/iGibson
    def create_visual_object(self):
        """
        Create visual objects to visualize interaction
        """
        self.constraint_marker = VisualMarker(radius=0.04,
                                              rgba_color=[0, 0, 1, 1])
        self.constraint_marker2 = VisualMarker(visual_shape=p.GEOM_CAPSULE,
                                               radius=0.01,
                                               length=3,
                                               initial_offset=[0, 0, -1.5],
                                               rgba_color=[0, 0, 1, 1])
        if self.simulator is not None:
            self.simulator.import_object(self.constraint_marker2,
                                         use_pbr=False)
            self.simulator.import_object(self.constraint_marker, use_pbr=False)

        self.constraint_marker.set_position([0, 0, -1])
        self.constraint_marker2.set_position([0, 0, -1])
コード例 #3
0
    def load_visualization(self, env):
        """
        Load visualization, such as initial and target position, shortest path, etc

        :param env: environment instance
        """
        if env.mode != 'gui':
            return

        cyl_length = 0.2
        self.initial_pos_vis_obj = VisualMarker(
            visual_shape=p.GEOM_CYLINDER,
            rgba_color=[1, 0, 0, 0.3],
            radius=self.dist_tol,
            length=cyl_length,
            initial_offset=[0, 0, cyl_length / 2.0])
        self.target_pos_vis_obj = VisualMarker(
            visual_shape=p.GEOM_CYLINDER,
            rgba_color=[0, 0, 1, 0.3],
            radius=self.dist_tol,
            length=cyl_length,
            initial_offset=[0, 0, cyl_length / 2.0])

        if self.target_visual_object_visible_to_agent:
            env.simulator.import_object(self.initial_pos_vis_obj)
            env.simulator.import_object(self.target_pos_vis_obj)
        else:
            self.initial_pos_vis_obj.load()
            self.target_pos_vis_obj.load()

        if env.scene.build_graph:
            self.num_waypoints_vis = 250
            self.waypoints_vis = [
                VisualMarker(visual_shape=p.GEOM_CYLINDER,
                             rgba_color=[0, 1, 0, 0.3],
                             radius=0.1,
                             length=cyl_length,
                             initial_offset=[0, 0, cyl_length / 2.0])
                for _ in range(self.num_waypoints_vis)
            ]
            for waypoint in self.waypoints_vis:
                waypoint.load()
コード例 #4
0
class PointNavFixedTask(BaseTask):
    """
    Point Nav Fixed Task
    The goal is to navigate to a fixed goal position
    """
    def __init__(self, env):
        super(PointNavFixedTask, self).__init__(env)
        self.reward_type = self.config.get('reward_type', 'l2')
        self.termination_conditions = [
            MaxCollision(self.config),
            Timeout(self.config),
            OutOfBound(self.config),
            PointGoal(self.config),
        ]
        self.reward_functions = [
            PotentialReward(self.config),
            CollisionReward(self.config),
            PointGoalReward(self.config),
        ]

        self.initial_pos = np.array(self.config.get('initial_pos', [0, 0, 0]))
        self.initial_orn = np.array(self.config.get('initial_orn', [0, 0, 0]))
        self.target_pos = np.array(self.config.get('target_pos', [5, 5, 0]))
        self.goal_format = self.config.get('goal_format', 'polar')
        self.dist_tol = self.termination_conditions[-1].dist_tol

        self.visual_object_at_initial_target_pos = self.config.get(
            'visual_object_at_initial_target_pos', True)
        self.target_visual_object_visible_to_agent = self.config.get(
            'target_visual_object_visible_to_agent', False)
        self.floor_num = 0

        self.load_visualization(env)

    def load_visualization(self, env):
        """
        Load visualization, such as initial and target position, shortest path, etc

        :param env: environment instance
        """
        if env.mode != 'gui':
            return

        cyl_length = 0.2
        self.initial_pos_vis_obj = VisualMarker(
            visual_shape=p.GEOM_CYLINDER,
            rgba_color=[1, 0, 0, 0.3],
            radius=self.dist_tol,
            length=cyl_length,
            initial_offset=[0, 0, cyl_length / 2.0])
        self.target_pos_vis_obj = VisualMarker(
            visual_shape=p.GEOM_CYLINDER,
            rgba_color=[0, 0, 1, 0.3],
            radius=self.dist_tol,
            length=cyl_length,
            initial_offset=[0, 0, cyl_length / 2.0])

        if self.target_visual_object_visible_to_agent:
            env.simulator.import_object(self.initial_pos_vis_obj)
            env.simulator.import_object(self.target_pos_vis_obj)
        else:
            self.initial_pos_vis_obj.load()
            self.target_pos_vis_obj.load()

        if env.scene.build_graph:
            self.num_waypoints_vis = 250
            self.waypoints_vis = [
                VisualMarker(visual_shape=p.GEOM_CYLINDER,
                             rgba_color=[0, 1, 0, 0.3],
                             radius=0.1,
                             length=cyl_length,
                             initial_offset=[0, 0, cyl_length / 2.0])
                for _ in range(self.num_waypoints_vis)
            ]
            for waypoint in self.waypoints_vis:
                waypoint.load()

    def get_geodesic_potential(self, env):
        """
        Get potential based on geodesic distance

        :param env: environment instance
        :return: geodesic distance to the target position
        """
        _, geodesic_dist = self.get_shortest_path(env)
        return geodesic_dist

    def get_l2_potential(self, env):
        """
        Get potential based on L2 distance

        :param env: environment instance
        :return: L2 distance to the target position
        """
        return l2_distance(env.robots[0].get_position()[:2],
                           self.target_pos[:2])

    def get_potential(self, env):
        """
        Compute task-specific potential: distance to the goal

        :param env: environment instance
        :return: task potential
        """
        if self.reward_type == 'l2':
            return self.get_l2_potential(env)
        elif self.reward_type == 'geodesic':
            return self.get_geodesic_potential(env)

    def reset_scene(self, env):
        """
        Task-specific scene reset: reset scene objects or floor plane

        :param env: environment instance
        """
        if isinstance(env.scene, InteractiveIndoorScene):
            env.scene.reset_scene_objects()
        elif isinstance(env.scene, StaticIndoorScene):
            env.scene.reset_floor(floor=self.floor_num)

    def reset_agent(self, env):
        """
        Task-specific agent reset: land the robot to initial pose, compute initial potential

        :param env: environment instance
        """
        env.land(env.robots[0], self.initial_pos, self.initial_orn)
        self.path_length = 0.0
        self.robot_pos = self.initial_pos[:2]
        self.geodesic_dist = self.get_geodesic_potential(env)
        for reward_function in self.reward_functions:
            reward_function.reset(self, env)

    def get_termination(self, env, collision_links=[], action=None, info={}):
        """
        Aggreate termination conditions and fill info
        """
        done, info = super(PointNavFixedTask,
                           self).get_termination(env, collision_links, action,
                                                 info)

        info['path_length'] = self.path_length
        if done:
            info['spl'] = float(info['success']) * \
                min(1.0, self.geodesic_dist / self.path_length)
        else:
            info['spl'] = 0.0

        return done, info

    def global_to_local(self, env, pos):
        """
        Convert a 3D point in global frame to agent's local frame

        :param env: environment instance
        :param pos: a 3D point in global frame
        :return: the same 3D point in agent's local frame
        """
        return rotate_vector_3d(pos - env.robots[0].get_position(),
                                *env.robots[0].get_rpy())

    def get_task_obs(self, env):
        """
        Get task-specific observation, including goal position, current velocities, etc.

        :param env: environment instance
        :return: task-specific observation
        """
        task_obs = self.global_to_local(env, self.target_pos)[:2]
        if self.goal_format == 'polar':
            task_obs = np.array(cartesian_to_polar(task_obs[0], task_obs[1]))

        # linear velocity along the x-axis
        linear_velocity = rotate_vector_3d(env.robots[0].get_linear_velocity(),
                                           *env.robots[0].get_rpy())[0]
        # angular velocity along the z-axis
        angular_velocity = rotate_vector_3d(
            env.robots[0].get_angular_velocity(), *env.robots[0].get_rpy())[2]
        task_obs = np.append(task_obs, [linear_velocity, angular_velocity])

        return task_obs

    def get_shortest_path(self,
                          env,
                          from_initial_pos=False,
                          entire_path=False):
        """
        Get the shortest path and geodesic distance from the robot or the initial position to the target position

        :param env: environment instance
        :param from_initial_pos: whether source is initial position rather than current position
        :param entire_path: whether to return the entire shortest path
        :return: shortest path and geodesic distance to the target position
        """
        if from_initial_pos:
            source = self.initial_pos[:2]
        else:
            source = env.robots[0].get_position()[:2]
        target = self.target_pos[:2]
        return env.scene.get_shortest_path(self.floor_num,
                                           source,
                                           target,
                                           entire_path=entire_path)

    def step_visualization(self, env):
        """
        Step visualization

        :param env: environment instance
        """
        if env.mode != 'gui':
            return

        self.initial_pos_vis_obj.set_position(self.initial_pos)
        self.target_pos_vis_obj.set_position(self.target_pos)

        if env.scene.build_graph:
            shortest_path, _ = self.get_shortest_path(env, entire_path=True)
            floor_height = env.scene.get_floor_height(self.floor_num)
            num_nodes = min(self.num_waypoints_vis, shortest_path.shape[0])
            for i in range(num_nodes):
                self.waypoints_vis[i].set_position(pos=np.array(
                    [shortest_path[i][0], shortest_path[i][1], floor_height]))
            for i in range(num_nodes, self.num_waypoints_vis):
                self.waypoints_vis[i].set_position(
                    pos=np.array([0.0, 0.0, 100.0]))

    def step(self, env):
        """
        Perform task-specific step: step visualization and aggregate path length

        :param env: environment instance
        """
        self.step_visualization(env)
        new_robot_pos = env.robots[0].get_position()[:2]
        self.path_length += l2_distance(self.robot_pos, new_robot_pos)
        self.robot_pos = new_robot_pos
コード例 #5
0
ファイル: viewer.py プロジェクト: monarchdemon/iGibson
class Viewer:
    def __init__(
        self,
        initial_pos=[0, 0, 1.2],
        initial_view_direction=[1, 0, 0],
        initial_up=[0, 0, 1],
        simulator=None,
        renderer=None,
        min_cam_z=-1e6,
    ):
        """
        iGibson GUI (Viewer) for navigation, manipulation and motion planning / execution

        :param initial_pos: position of the camera
        :param initial_view_direction: viewing direction of the camera
        :param initial_up: up direction
        :param simulator: iGibson simulator
        :param renderer: iGibson renderer
        :param min_cam_z: minimum camera z
        """
        self.px = initial_pos[0]
        self.py = initial_pos[1]
        self.pz = initial_pos[2]
        self.theta = np.arctan2(initial_view_direction[1],
                                initial_view_direction[0])
        self.phi = np.arctan2(
            initial_view_direction[2],
            np.sqrt(initial_view_direction[0]**2 +
                    initial_view_direction[1]**2))
        self.min_cam_z = min_cam_z
        self.show_help = 0

        self._mouse_ix, self._mouse_iy = -1, -1
        self.left_down = False
        self.middle_down = False
        self.right_down = False
        self.view_direction = np.array(initial_view_direction)
        self.up = initial_up
        self.renderer = renderer
        self.simulator = simulator
        self.cid = []

        # Flag to control if the mouse interface is in navigation, manipulation
        # or motion planning/execution mode
        self.manipulation_mode = 0

        # Video recording
        self.recording = False  # Boolean if we are recording frames from the viewer
        self.pause_recording = False  # Flag to pause/resume recording
        self.video_folder = ""

        cv2.namedWindow('ExternalView')
        cv2.moveWindow("ExternalView", 0, 0)
        cv2.namedWindow('RobotView')
        cv2.setMouseCallback('ExternalView', self.mouse_callback)
        self.create_visual_object()
        self.planner = None
        self.block_command = False

    def setup_motion_planner(self, planner=None):
        """
        Store the motion planner that is passed in

        :param planner: motion planner
        """
        self.planner = planner

    def create_visual_object(self):
        """
        Create visual objects to visualize interaction
        """
        self.constraint_marker = VisualMarker(radius=0.04,
                                              rgba_color=[0, 0, 1, 1])
        self.constraint_marker2 = VisualMarker(visual_shape=p.GEOM_CAPSULE,
                                               radius=0.01,
                                               length=3,
                                               initial_offset=[0, 0, -1.5],
                                               rgba_color=[0, 0, 1, 1])
        if self.simulator is not None:
            self.simulator.import_object(self.constraint_marker2,
                                         use_pbr=False)
            self.simulator.import_object(self.constraint_marker, use_pbr=False)

        self.constraint_marker.set_position([0, 0, -1])
        self.constraint_marker2.set_position([0, 0, -1])

    def apply_push_force(self, x, y, force):
        """
        Apply pushing force to a 3D point. Given a pixel location (x, y),
        compute the 3D location of that point, and then apply a virtual force
        of a given magnitude towards the negative surface normal at that point

        :param x: image pixel x coordinate
        :param y: image pixel y coordinate
        :param force: force magnitude
        """
        camera_pose = np.array([self.px, self.py, self.pz])
        self.renderer.set_camera(camera_pose,
                                 camera_pose + self.view_direction, self.up)
        position_cam = np.array([
            (x - self.renderer.width / 2) / float(self.renderer.width / 2) *
            np.tan(self.renderer.horizontal_fov / 2.0 / 180.0 * np.pi),
            -(y - self.renderer.height / 2) / float(self.renderer.height / 2) *
            np.tan(self.renderer.vertical_fov / 2.0 / 180.0 * np.pi), -1, 1
        ])
        position_cam[:3] *= 5

        position_world = np.linalg.inv(self.renderer.V).dot(position_cam)
        position_eye = camera_pose
        res = p.rayTest(position_eye, position_world[:3])
        if len(res) > 0 and res[0][0] != -1:
            # there is hit
            object_id, link_id, _, hit_pos, hit_normal = res[0]
            p.changeDynamics(object_id,
                             -1,
                             activationState=p.ACTIVATION_STATE_WAKE_UP)
            p.applyExternalForce(object_id, link_id,
                                 -np.array(hit_normal) * force, hit_pos,
                                 p.WORLD_FRAME)

    def create_constraint(self, x, y, fixed=False):
        """
        Create a constraint between the constraint marker and the object
        at pixel location (x, y). This is used for human users' mouse
        interaction with the objects in the scenes.

        :param x: image pixel x coordinate
        :param y: image pixel y coordinate
        :param fixed: whether to create a fixed joint. Otherwise, it's a point2point joint.
        """
        camera_pose = np.array([self.px, self.py, self.pz])
        self.renderer.set_camera(camera_pose,
                                 camera_pose + self.view_direction, self.up)

        position_cam = np.array([
            (x - self.renderer.width / 2) / float(self.renderer.width / 2) *
            np.tan(self.renderer.horizontal_fov / 2.0 / 180.0 * np.pi),
            -(y - self.renderer.height / 2) / float(self.renderer.height / 2) *
            np.tan(self.renderer.vertical_fov / 2.0 / 180.0 * np.pi), -1, 1
        ])
        position_cam[:3] *= 5

        position_world = np.linalg.inv(self.renderer.V).dot(position_cam)
        position_eye = camera_pose
        res = p.rayTest(position_eye, position_world[:3])
        if len(res) > 0 and res[0][0] != -1:
            object_id, link_id, _, hit_pos, hit_normal = res[0]
            p.changeDynamics(object_id,
                             -1,
                             activationState=p.ACTIVATION_STATE_WAKE_UP)
            link_pos, link_orn = None, None
            if link_id == -1:
                link_pos, link_orn = p.getBasePositionAndOrientation(object_id)
            else:
                link_state = p.getLinkState(object_id, link_id)
                link_pos, link_orn = link_state[:2]

            child_frame_pos, child_frame_orn = \
                p.multiplyTransforms(*p.invertTransform(link_pos, link_orn),
                                     hit_pos,
                                     [0, 0, 0, 1])
            self.constraint_marker.set_position(hit_pos)
            self.constraint_marker2.set_position(hit_pos)
            self.dist = np.linalg.norm(np.array(hit_pos) - camera_pose)
            cid = p.createConstraint(
                parentBodyUniqueId=self.constraint_marker.body_id,
                parentLinkIndex=-1,
                childBodyUniqueId=object_id,
                childLinkIndex=link_id,
                jointType=[p.JOINT_POINT2POINT, p.JOINT_FIXED][fixed],
                jointAxis=(0, 0, 0),
                parentFramePosition=(0, 0, 0),
                childFramePosition=child_frame_pos,
                childFrameOrientation=child_frame_orn,
            )
            p.changeConstraint(cid, maxForce=100)
            self.cid.append(cid)
            self.interaction_x, self.interaction_y = x, y

    def get_hit(self, x, y):
        """
        Shoot a ray through pixel location (x, y) and returns the position and
        normal that this ray hits

        :param x: image pixel x coordinate
        :param y: image pixel y coordinate
        """
        camera_pose = np.array([self.px, self.py, self.pz])
        self.renderer.set_camera(camera_pose,
                                 camera_pose + self.view_direction, self.up)
        position_cam = np.array([
            (x - self.renderer.width / 2) / float(self.renderer.width / 2) *
            np.tan(self.renderer.horizontal_fov / 2.0 / 180.0 * np.pi),
            -(y - self.renderer.height / 2) / float(self.renderer.height / 2) *
            np.tan(self.renderer.vertical_fov / 2.0 / 180.0 * np.pi), -1, 1
        ])
        position_cam[:3] *= 5

        position_world = np.linalg.inv(self.renderer.V).dot(position_cam)
        position_eye = camera_pose
        res = p.rayTest(position_eye, position_world[:3])
        hit_pos = None
        hit_normal = None
        if len(res) > 0 and res[0][0] != -1:
            object_id, link_id, _, hit_pos, hit_normal = res[0]
        return hit_pos, hit_normal

    def remove_constraint(self):
        """
        Remove constraints created by create_constraint
        """
        for cid in self.cid:
            p.removeConstraint(cid)
        self.cid = []
        self.constraint_marker.set_position([0, 0, 100])
        self.constraint_marker2.set_position([0, 0, 100])

    def move_constraint(self, x, y):
        """
        Move the constraint marker (when the mouse is moved during interaction)

        :param x: image pixel x coordinate
        :param y: image pixel y coordinate
        """
        camera_pose = np.array([self.px, self.py, self.pz])
        self.renderer.set_camera(camera_pose,
                                 camera_pose + self.view_direction, self.up)

        position_cam = np.array([
            (x - self.renderer.width / 2) / float(self.renderer.width / 2) *
            np.tan(self.renderer.horizontal_fov / 2.0 / 180.0 * np.pi),
            -(y - self.renderer.height / 2) / float(self.renderer.height / 2) *
            np.tan(self.renderer.vertical_fov / 2.0 / 180.0 * np.pi), -1, 1
        ])
        position_cam[:3] = position_cam[:3] / \
            np.linalg.norm(position_cam[:3]) * self.dist
        position_world = np.linalg.inv(self.renderer.V).dot(position_cam)
        position_world /= position_world[3]
        self.constraint_marker.set_position(position_world[:3])
        self.constraint_marker2.set_position(position_world[:3])
        self.interaction_x, self.interaction_y = x, y

    def move_constraint_z(self, dy):
        """
        Move the constraint marker closer or further away from the camera
        (when the mouse is moved during interaction)

        :param dy: delta y coordinate in the pixel space
        """
        x, y = self.interaction_x, self.interaction_y
        camera_pose = np.array([self.px, self.py, self.pz])
        self.renderer.set_camera(camera_pose,
                                 camera_pose + self.view_direction, self.up)

        self.dist *= (1 - dy)
        if self.dist < 0.1:
            self.dist = 0.1
        position_cam = np.array([
            (x - self.renderer.width / 2) / float(self.renderer.width / 2) *
            np.tan(self.renderer.horizontal_fov / 2.0 / 180.0 * np.pi),
            -(y - self.renderer.height / 2) / float(self.renderer.height / 2) *
            np.tan(self.renderer.vertical_fov / 2.0 / 180.0 * np.pi), -1, 1
        ])
        position_cam[:3] = position_cam[:3] / \
            np.linalg.norm(position_cam[:3]) * self.dist
        position_world = np.linalg.inv(self.renderer.V).dot(position_cam)
        position_world /= position_world[3]
        self.constraint_marker.set_position(position_world[:3])
        self.constraint_marker2.set_position(position_world[:3])

    def mouse_callback(self, event, x, y, flags, params):
        """
        Mouse callback that handles all the mouse events

        :param event: OpenCV mouse event
        :param x: image pixel x coordinate
        :param y: image pixel y coordinate
        :param flags: any relevant flags passed by OpenCV.
        :param params: any extra parameters supplied by OpenCV
        """

        # Navigation mode
        if self.manipulation_mode == 0:
            # Only once, when pressing left mouse while ctrl key is pressed
            if flags == cv2.EVENT_FLAG_LBUTTON + cv2.EVENT_FLAG_CTRLKEY and not self.right_down:
                self._mouse_ix, self._mouse_iy = x, y
                self.right_down = True

            # Middle mouse button press or only once, when pressing left
            # mouse while shift key is pressed (Mac compatibility)
            elif (event == cv2.EVENT_MBUTTONDOWN) or (
                    flags == cv2.EVENT_FLAG_LBUTTON + cv2.EVENT_FLAG_SHIFTKEY
                    and not self.middle_down):
                self._mouse_ix, self._mouse_iy = x, y
                self.middle_down = True

            # left mouse button press
            elif event == cv2.EVENT_LBUTTONDOWN:
                self._mouse_ix, self._mouse_iy = x, y
                self.left_down = True

            # left mouse button released
            elif event == cv2.EVENT_LBUTTONUP:
                self.left_down = False
                self.right_down = False
                self.middle_down = False

            # middle mouse button released
            elif event == cv2.EVENT_MBUTTONUP:
                self.middle_down = False

            # moving mouse location on the window
            if event == cv2.EVENT_MOUSEMOVE:
                # if left button was pressed we change orientation of camera
                if self.left_down:
                    dx = (x - self._mouse_ix) / 100.0
                    dy = (y - self._mouse_iy) / 100.0
                    self._mouse_ix = x
                    self._mouse_iy = y
                    if not ((flags & cv2.EVENT_FLAG_CTRLKEY
                             and flags & cv2.EVENT_FLAG_SHIFTKEY) or
                            (flags & cv2.EVENT_FLAG_CTRLKEY
                             and flags & cv2.EVENT_FLAG_ALTKEY)):
                        self.phi += dy
                        self.phi = np.clip(self.phi, -np.pi / 2 + 1e-5,
                                           np.pi / 2 - 1e-5)
                        self.theta += dx
                        self.view_direction = np.array([
                            np.cos(self.theta) * np.cos(self.phi),
                            np.sin(self.theta) * np.cos(self.phi),
                            np.sin(self.phi)
                        ])

                # if middle button was pressed we get closer/further away in the viewing direction
                elif self.middle_down:
                    d_vd = (y - self._mouse_iy) / 100.0
                    self._mouse_iy = y

                    motion_along_vd = d_vd * self.view_direction
                    self.px += motion_along_vd[0]
                    self.py += motion_along_vd[1]
                    self.pz += motion_along_vd[2]
                    self.pz = max(self.min_cam_z, self.pz)

                # if right button was pressed we change translation of camera
                elif self.right_down:
                    zz = self.view_direction / \
                        np.linalg.norm(self.view_direction)
                    xx = np.cross(zz, np.array([0, 0, 1]))
                    xx = xx / np.linalg.norm(xx)
                    yy = np.cross(xx, zz)
                    motion_along_vx = -((x - self._mouse_ix) / 100.0) * xx
                    motion_along_vy = ((y - self._mouse_iy) / 100.0) * yy
                    self._mouse_ix = x
                    self._mouse_iy = y

                    self.px += (motion_along_vx[0] + motion_along_vy[0])
                    self.py += (motion_along_vx[1] + motion_along_vy[1])
                    self.pz += (motion_along_vx[2] + motion_along_vy[2])
                    self.pz = max(self.min_cam_z, self.pz)

        # Manipulation mode
        elif self.manipulation_mode == 1:
            # Middle mouse button press or only once, when pressing left mouse
            # while shift key is pressed (Mac compatibility)
            if (event == cv2.EVENT_MBUTTONDOWN) or (
                    flags == cv2.EVENT_FLAG_LBUTTON + cv2.EVENT_FLAG_SHIFTKEY
                    and not self.middle_down):
                self._mouse_ix, self._mouse_iy = x, y
                self.middle_down = True
                self.create_constraint(x, y, fixed=True)
            elif event == cv2.EVENT_LBUTTONDOWN:  # left mouse button press
                self._mouse_ix, self._mouse_iy = x, y
                self.left_down = True
                self.create_constraint(x, y, fixed=False)
            elif event == cv2.EVENT_LBUTTONUP:  # left mouse button released
                self.left_down = False
                self.right_down = False
                self.middle_down = False
                self.remove_constraint()
            elif event == cv2.EVENT_MBUTTONUP:  # middle mouse button released
                self.left_down = False
                self.right_down = False
                self.middle_down = False
                self.remove_constraint()
            if event == cv2.EVENT_MOUSEMOVE:  # moving mouse location on the window
                if (self.left_down or self.middle_down
                    ) and not flags & cv2.EVENT_FLAG_CTRLKEY:
                    self._mouse_ix = x
                    self._mouse_iy = y
                    self.move_constraint(x, y)
                elif (self.left_down
                      or self.middle_down) and flags & cv2.EVENT_FLAG_CTRLKEY:
                    dy = (y - self._mouse_iy) / 500.0
                    self.move_constraint_z(dy)

        # Motion planning / execution mode
        elif self.manipulation_mode == 2 and not self.block_command:
            # left mouse button press
            if event == cv2.EVENT_LBUTTONDOWN:
                self._mouse_ix, self._mouse_iy = x, y
                self.left_down = True
                self.hit_pos, _ = self.get_hit(x, y)

            # Base motion
            if event == cv2.EVENT_LBUTTONUP:
                hit_pos, _ = self.get_hit(x, y)
                target_yaw = np.arctan2(hit_pos[1] - self.hit_pos[1],
                                        hit_pos[0] - self.hit_pos[0])
                self.planner.set_marker_position_yaw(self.hit_pos, target_yaw)
                self.left_down = False
                if hit_pos is not None:
                    self.block_command = True
                    plan = self.planner.plan_base_motion(
                        [self.hit_pos[0], self.hit_pos[1], target_yaw])
                    if plan is not None and len(plan) > 0:
                        self.planner.dry_run_base_plan(plan)
                    self.block_command = False

            # Visualize base subgoal orientation
            if event == cv2.EVENT_MOUSEMOVE:
                if self.left_down:
                    hit_pos, _ = self.get_hit(x, y)
                    target_yaw = np.arctan2(hit_pos[1] - self.hit_pos[1],
                                            hit_pos[0] - self.hit_pos[0])
                    self.planner.set_marker_position_yaw(
                        self.hit_pos, target_yaw)

            # Arm motion
            if event == cv2.EVENT_MBUTTONDOWN:
                hit_pos, hit_normal = self.get_hit(x, y)
                if hit_pos is not None:
                    self.block_command = True
                    plan = self.planner.plan_arm_push(hit_pos,
                                                      -np.array(hit_normal))
                    self.planner.execute_arm_push(plan, hit_pos,
                                                  -np.array(hit_normal))
                    self.block_command = False

    def show_help_text(self, frame):
        """
        Show help text
        """
        if self.show_help < 0:
            return

        if self.show_help >= 150:
            first_color = (255, 0, 0)
            help_text = "Keyboard cheatsheet:"
            cv2.putText(frame, help_text, (10, 80), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'w','a','s','d': up, left, down, right (any mode)"
            cv2.putText(frame, help_text, (10, 100), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'q','e': turn left, turn right (any mode)"
            cv2.putText(frame, help_text, (10, 120), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'m': switch control mode across navigation, manipulation, and planning"
            cv2.putText(frame, help_text, (10, 140), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'r': Start/stop recording frames (results in \\tmp folder)"
            cv2.putText(frame, help_text, (10, 160), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'p': Pause/resume recording"
            cv2.putText(frame, help_text, (10, 180), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'h': Show this help on screen"
            cv2.putText(frame, help_text, (10, 200), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
            help_text = "'ESC': Quit"
            cv2.putText(frame, help_text, (10, 220), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, first_color, 1, cv2.LINE_AA)
        else:
            second_color = (255, 0, 255)
            help_text = "Mouse control in navigation mode:"
            cv2.putText(frame, help_text, (10, 80), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Left click and drag: rotate camera"
            cv2.putText(frame, help_text, (10, 100), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "CTRL + left click and drag: translate camera"
            cv2.putText(frame, help_text, (10, 120), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Middle click and drag (linux) or left SHIFT + left click and drag:"
            cv2.putText(frame, help_text, (10, 140), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "translate camera closer/further away in the viewing direction"
            cv2.putText(frame, help_text, (10, 160), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Mouse control in manipulation mode:"
            cv2.putText(frame, help_text, (10, 180), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Left click and drag: create ball-joint connection to clicked object and move it"
            cv2.putText(frame, help_text, (10, 200), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Middle click and drag (linux) or left SHIFT + left click and drag: create rigid connection"
            cv2.putText(frame, help_text, (10, 220), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = " to object and move it"
            cv2.putText(frame, help_text, (10, 240), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "CTRL + click and drag: up/down of the mouse moves object further/closer"
            cv2.putText(frame, help_text, (10, 260), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Mouse control in planning mode:"
            cv2.putText(frame, help_text, (10, 280), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Left click: create (click), visualize (drag) and plan / execute (release) a base motion subgoal"
            cv2.putText(frame, help_text, (10, 300), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "for the robot base to reach the physical point that corresponds to the clicked pixel"
            cv2.putText(frame, help_text, (10, 320), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "Middle click: create, and plan / execute an arm motion subgoal"
            cv2.putText(frame, help_text, (10, 340), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
            help_text = "for the robot end-effector to reach the physical point that corresponds to the clicked pixel"
            cv2.putText(frame, help_text, (10, 360), cv2.FONT_HERSHEY_SIMPLEX,
                        0.5, second_color, 1, cv2.LINE_AA)
        self.show_help -= 1

    def update(self):
        """
        Update images of Viewer
        """
        camera_pose = np.array([self.px, self.py, self.pz])
        if self.renderer is not None:
            self.renderer.set_camera(camera_pose,
                                     camera_pose + self.view_direction,
                                     self.up)

        if self.renderer is not None:
            frame = cv2.cvtColor(
                np.concatenate(self.renderer.render(modes=('rgb')), axis=1),
                cv2.COLOR_RGB2BGR)
        else:
            frame = np.zeros((300, 300, 3)).astype(np.uint8)

        # Text with the position and viewing direction of the camera of the external viewer
        text_color = (0, 0, 0)
        cv2.putText(
            frame,
            "px {:1.1f} py {:1.1f} pz {:1.1f}".format(self.px, self.py,
                                                      self.pz), (10, 20),
            cv2.FONT_HERSHEY_SIMPLEX, 0.5, text_color, 1, cv2.LINE_AA)
        cv2.putText(frame,
                    "[{:1.1f} {:1.1f} {:1.1f}]".format(*self.view_direction),
                    (10, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.5, text_color, 1,
                    cv2.LINE_AA)
        cv2.putText(frame, ["nav mode", "manip mode",
                            "planning mode"][self.manipulation_mode], (10, 60),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, text_color, 1, cv2.LINE_AA)
        self.show_help_text(frame)

        cv2.imshow('ExternalView', frame)

        # We keep some double functinality for "backcompatibility"
        q = cv2.waitKey(1)
        move_vec = self.view_direction[:2]
        # step size is 0.05m
        move_vec = move_vec / np.linalg.norm(move_vec) * 0.05

        # show help text
        if q == ord('h'):
            self.show_help = 300

        # move
        elif q in [ord('w'), ord('s'), ord('a'), ord('d')]:
            if q == ord('w'):
                yaw = 0.0
            elif q == ord('s'):
                yaw = np.pi
            elif q == ord('a'):
                yaw = -np.pi / 2.0
            elif q == ord('d'):
                yaw = np.pi / 2.0
            move_vec = rotate_vector_2d(move_vec, yaw)
            self.px += move_vec[0]
            self.py += move_vec[1]
            if self.manipulation_mode == 1:
                self.move_constraint(self._mouse_ix, self._mouse_iy)

        # turn left
        elif q == ord('q'):
            self.theta += np.pi / 36
            self.view_direction = np.array([
                np.cos(self.theta) * np.cos(self.phi),
                np.sin(self.theta) * np.cos(self.phi),
                np.sin(self.phi)
            ])
            if self.manipulation_mode == 1:
                self.move_constraint(self._mouse_ix, self._mouse_iy)

        # turn right
        elif q == ord('e'):
            self.theta -= np.pi / 36
            self.view_direction = np.array([
                np.cos(self.theta) * np.cos(self.phi),
                np.sin(self.theta) * np.cos(self.phi),
                np.sin(self.phi)
            ])
            if self.manipulation_mode == 1:
                self.move_constraint(self._mouse_ix, self._mouse_iy)

        # quit (Esc)
        elif q == 27:
            if self.video_folder != "":
                logging.info(
                    "You recorded a video. To compile the frames into a mp4 go to the corresponding subfolder"
                    + " in /tmp and execute: ")
                logging.info(
                    "ffmpeg -i %5d.png -y -c:a copy -c:v libx264 -crf 18 -preset veryslow -r 30 video.mp4"
                )
                logging.info(
                    "The last folder you collected images for a video was: " +
                    self.video_folder)
            exit()

        # Start/Stop recording. Stopping saves frames to files
        elif q == ord('r'):
            if self.recording:
                self.recording = False
                self.pause_recording = False
            else:
                logging.info("Start recording*****************************")
                # Current time string to use to save the temporal urdfs
                timestr = time.strftime("%Y%m%d-%H%M%S")
                # Create the subfolder
                self.video_folder = os.path.join(
                    "/tmp", '{}_{}_{}'.format(timestr, random.getrandbits(64),
                                              os.getpid()))
                os.makedirs(self.video_folder, exist_ok=True)
                self.recording = True
                self.frame_idx = 0

        # Pause/Resume recording
        elif q == ord('p'):
            if self.pause_recording:
                self.pause_recording = False
            else:
                self.pause_recording = True

        # Switch amoung navigation, manipulation, motion planning / execution modes
        elif q == ord('m'):
            self.left_down = False
            self.middle_down = False
            self.right_down = False
            self.manipulation_mode = (self.manipulation_mode + 1) % 3

        if self.recording and not self.pause_recording:
            cv2.imwrite(
                os.path.join(self.video_folder,
                             '{:05d}.png'.format(self.frame_idx)),
                (frame * 255).astype(np.uint8))
            self.frame_idx += 1

        if self.renderer is not None:
            frames = self.renderer.render_robot_cameras(modes=('rgb'))
            if len(frames) > 0:
                frame = cv2.cvtColor(np.concatenate(frames, axis=1),
                                     cv2.COLOR_RGB2BGR)
                cv2.imshow('RobotView', frame)
コード例 #6
0
def render_physics_gifs(main_urdf_file_and_offset):
    step_per_sec = 100
    s = Simulator(mode='headless',
                  image_width=512,
                  image_height=512,
                  physics_timestep=1 / float(step_per_sec))

    root_dir = '/cvgl2/u/chengshu/ig_dataset_v5/objects'
    obj_count = 0
    for i, obj_class_dir in enumerate(sorted(os.listdir(root_dir))):
        obj_class = obj_class_dir
        # if obj_class not in SELECTED_CLASSES:
        #     continue
        obj_class_dir = os.path.join(root_dir, obj_class_dir)
        for obj_inst_dir in os.listdir(obj_class_dir):
            # if obj_inst_dir != '14402':
            #     continue

            imgs = []
            scene = EmptyScene()
            s.import_scene(scene, render_floor_plane=True)
            obj_inst_name = obj_inst_dir
            # urdf_path = obj_inst_name + '.urdf'
            obj_inst_dir = os.path.join(obj_class_dir, obj_inst_dir)
            urdf_path, offset = main_urdf_file_and_offset[obj_inst_dir]
            # urdf_path = os.path.join(obj_inst_dir, urdf_path)
            # print('urdf_path', urdf_path)

            obj = ArticulatedObject(urdf_path)
            s.import_object(obj)

            push_visual_marker = VisualMarker(radius=0.1)
            s.import_object(push_visual_marker)
            push_visual_marker.set_position([100, 100, 0.0])
            z = stable_z_on_aabb(obj.body_id, [[0, 0, 0], [0, 0, 0]])

            # offset is translation from the bounding box center to the base link frame origin
            # need to add another offset that is the translation from the base link frame origin to the inertial frame origin
            # p.resetBasePositionAndOrientation() sets the inertial frame origin instead of the base link frame origin
            # Assuming the bounding box center is at (0, 0, z) where z is half of the extent in z-direction
            base_link_to_inertial = p.getDynamicsInfo(obj.body_id, -1)[3]
            obj.set_position([
                offset[0] + base_link_to_inertial[0],
                offset[1] + base_link_to_inertial[1], z
            ])

            center, extent = get_center_extent(obj.body_id)
            # the bounding box center should be at (0, 0) on the xy plane and the bottom of the bounding box should touch the ground
            if not (np.linalg.norm(center[:2]) < 1e-3
                    and np.abs(center[2] - extent[2] / 2.0) < 1e-3):
                print('-' * 50)
                print('WARNING:', urdf_path, 'xy error',
                      np.linalg.norm(center[:2]), 'z error',
                      np.abs(center[2] - extent[2] / 2.0))

            height = extent[2]

            max_half_extent = max(extent[0], extent[1]) / 2.0
            px = max_half_extent * 2
            py = max_half_extent * 2
            pz = extent[2] * 1.2

            camera_pose = np.array([px, py, pz])
            # camera_pose = np.array([0.01, 0.01, 3])

            s.renderer.set_camera(camera_pose, [0, 0, 0], [0, 0, 1])
            # drop 1 second
            for _ in range(step_per_sec):
                s.step()
                frame = s.renderer.render(modes=('rgb'))
                imgs.append(
                    Image.fromarray(
                        (frame[0][:, :, :3] * 255).astype(np.uint8)))

            ray_start = [max_half_extent * 1.5, max_half_extent * 1.5, 0]
            ray_end = [-100.0, -100.0, 0]
            unit_force = np.array([-1.0, -1.0, 0.0])
            unit_force /= np.linalg.norm(unit_force)
            force_mag = 100.0

            ray_zs = [height * 0.5]
            valid_ray_z = False
            for trial in range(5):
                for ray_z in ray_zs:
                    ray_start[2] = ray_z
                    ray_end[2] = ray_z
                    res = p.rayTest(ray_start, ray_end)
                    if res[0][0] == obj.body_id:
                        valid_ray_z = True
                        break
                if valid_ray_z:
                    break
                increment = 1 / (2**(trial + 1))
                ray_zs = np.arange(increment / 2.0, 1.0, increment) * height

            # push 4 seconds
            for i in range(step_per_sec * 4):
                res = p.rayTest(ray_start, ray_end)
                object_id, link_id, _, hit_pos, hit_normal = res[0]
                if object_id != obj.body_id:
                    break
                push_visual_marker.set_position(hit_pos)
                p.applyExternalForce(object_id, link_id,
                                     unit_force * force_mag, hit_pos,
                                     p.WORLD_FRAME)
                s.step()
                # print(hit_pos)
                frame = s.renderer.render(modes=('rgb'))
                rgb = frame[0][:, :, :3]
                # add red border
                border_width = 10
                border_color = np.array([1.0, 0.0, 0.0])
                rgb[:border_width, :, :] = border_color
                rgb[-border_width:, :, :] = border_color
                rgb[:, :border_width, :] = border_color
                rgb[:, -border_width:, :] = border_color

                imgs.append(Image.fromarray((rgb * 255).astype(np.uint8)))

            gif_path = '{}/visualizations/{}_cm_physics_v3.gif'.format(
                obj_inst_dir, obj_inst_name)
            imgs = imgs[::4]
            imgs[0].save(gif_path,
                         save_all=True,
                         append_images=imgs[1:],
                         optimize=True,
                         duration=40,
                         loop=0)
            obj_count += 1
            # print(obj_count, gif_path, len(imgs), valid_ray_z, ray_z)
            print(obj_count, gif_path)
            s.reload()
コード例 #7
0
class MotionPlanningWrapper(object):
    """
    Motion planner wrapper that supports both base and arm motion
    """
    def __init__(self,
                 env: iGibsonEnv = None,
                 base_mp_algo: str = 'birrt',
                 arm_mp_algo: str = 'birrt',
                 optimize_iter: int = 0,
                 fine_motion_plan: bool = True):
        """
        Get planning related parameters.
        """
        self.env = env
        assert 'occupancy_grid' in self.env.output
        # get planning related parameters from env
        self.robot_id = self.env.robots[0].robot_ids[0]
        # self.mesh_id = self.scene.mesh_body_id
        # mesh id should not be used
        self.map_size = self.env.scene.trav_map_original_size * \
            self.env.scene.trav_map_default_resolution

        self.grid_resolution = self.env.grid_resolution
        self.occupancy_range = self.env.sensors['scan_occ'].occupancy_range
        self.robot_footprint_radius = self.env.sensors[
            'scan_occ'].robot_footprint_radius
        self.robot_footprint_radius_in_map = self.env.sensors[
            'scan_occ'].robot_footprint_radius_in_map
        self.robot = self.env.robots[0]
        self.base_mp_algo = base_mp_algo
        self.arm_mp_algo = arm_mp_algo
        self.base_mp_resolutions = np.array([0.05, 0.05, 0.05])
        self.optimize_iter = optimize_iter
        self.mode = self.env.mode
        self.initial_height = self.env.initial_pos_z_offset
        self.fine_motion_plan = fine_motion_plan
        self.robot_type = self.env.config['robot']

        if self.env.simulator.viewer is not None:
            self.env.simulator.viewer.setup_motion_planner(self)

        if self.robot_type in ['Fetch', 'Movo']:
            self.setup_arm_mp()

        self.arm_interaction_length = 0.2

        self.marker = None
        self.marker_direction = None

        if self.mode in ['gui', 'iggui']:
            self.marker = VisualMarker(radius=0.04, rgba_color=[0, 0, 1, 1])
            self.marker_direction = VisualMarker(visual_shape=p.GEOM_CAPSULE,
                                                 radius=0.01,
                                                 length=0.2,
                                                 initial_offset=[0, 0, -0.1],
                                                 rgba_color=[0, 0, 1, 1])
            self.env.simulator.import_object(self.marker, use_pbr=False)
            self.env.simulator.import_object(self.marker_direction,
                                             use_pbr=False)

    def set_marker_position(self, pos):
        """
        Set subgoal marker position

        :param pos: position
        """
        self.marker.set_position(pos)

    def set_marker_position_yaw(self, pos, yaw):
        """
        Set subgoal marker position and orientation

        :param pos: position
        :param yaw: yaw angle 
        """
        quat = quatToXYZW(seq='wxyz', orn=euler.euler2quat(0, -np.pi / 2, yaw))
        self.marker.set_position(pos)
        self.marker_direction.set_position_orientation(pos, quat)

    def set_marker_position_direction(self, pos, direction):
        """
        Set subgoal marker position and orientation

        :param pos: position
        :param direction: direction vector
        """
        yaw = np.arctan2(direction[1], direction[0])
        self.set_marker_position_yaw(pos, yaw)

    def setup_arm_mp(self):
        """
        Set up arm motion planner
        """
        if self.robot_type == 'Fetch':
            self.arm_default_joint_positions = (0.10322468280792236,
                                                -1.414019864768982,
                                                1.5178184935241699,
                                                0.8189625336474915,
                                                2.200358942909668,
                                                2.9631312579803466,
                                                -1.2862852996643066,
                                                0.0008453550418615341)
            self.arm_joint_ids = joints_from_names(self.robot_id, [
                'torso_lift_joint', 'shoulder_pan_joint',
                'shoulder_lift_joint', 'upperarm_roll_joint',
                'elbow_flex_joint', 'forearm_roll_joint', 'wrist_flex_joint',
                'wrist_roll_joint'
            ])
        elif self.robot_type == 'Movo':
            self.arm_default_joint_positions = (0.205, -1.50058731470836,
                                                -1.3002625076695704,
                                                0.5204845864369407,
                                                -2.6923805472917626,
                                                -0.02678584326934146,
                                                0.5065742552588746,
                                                -1.562883631882778)
            self.arm_joint_ids = joints_from_names(self.robot_id, [
                "linear_joint",
                "right_shoulder_pan_joint",
                "right_shoulder_lift_joint",
                "right_arm_half_joint",
                "right_elbow_joint",
                "right_wrist_spherical_1_joint",
                "right_wrist_spherical_2_joint",
                "right_wrist_3_joint",
            ])
        self.arm_joint_ids_all = get_moving_links(self.robot_id,
                                                  self.arm_joint_ids)
        self.arm_joint_ids_all = [
            item for item in self.arm_joint_ids_all
            if item != self.robot.end_effector_part_index()
        ]
        self.arm_ik_threshold = 0.05

        self.mp_obstacles = []
        if type(self.env.scene) == StaticIndoorScene:
            if self.env.scene.mesh_body_id is not None:
                self.mp_obstacles.append(self.env.scene.mesh_body_id)
        elif type(self.env.scene) == InteractiveIndoorScene:
            self.mp_obstacles.extend(self.env.scene.get_body_ids())

    def plan_base_motion(self, goal):
        """
        Plan base motion given a base subgoal

        :param goal: base subgoal
        :return: waypoints or None if no plan can be found
        """
        if self.marker is not None:
            self.set_marker_position_yaw([goal[0], goal[1], 0.05], goal[2])

        state = self.env.get_state()
        x, y, theta = goal
        grid = state['occupancy_grid']

        yaw = self.robot.get_rpy()[2]
        half_occupancy_range = self.occupancy_range / 2.0
        robot_position_xy = self.robot.get_position()[:2]
        corners = [
            robot_position_xy + rotate_vector_2d(local_corner, -yaw)
            for local_corner in [
                np.array([half_occupancy_range, half_occupancy_range]),
                np.array([half_occupancy_range, -half_occupancy_range]),
                np.array([-half_occupancy_range, half_occupancy_range]),
                np.array([-half_occupancy_range, -half_occupancy_range]),
            ]
        ]
        path = plan_base_motion_2d(
            self.robot_id, [x, y, theta],
            (tuple(np.min(corners, axis=0)), tuple(np.max(corners, axis=0))),
            map_2d=grid,
            occupancy_range=self.occupancy_range,
            grid_resolution=self.grid_resolution,
            robot_footprint_radius_in_map=self.robot_footprint_radius_in_map,
            resolutions=self.base_mp_resolutions,
            obstacles=[],
            algorithm=self.base_mp_algo,
            optimize_iter=self.optimize_iter)

        return path

    def simulator_sync(self):
        """Sync the simulator to renderer"""
        self.env.simulator.sync()

    def simulator_step(self):
        """Step the simulator and sync the simulator to renderer"""
        self.env.simulator.step()
        self.simulator_sync()

    def dry_run_base_plan(self, path):
        """
        Dry run base motion plan by setting the base positions without physics simulation

        :param path: base waypoints or None if no plan can be found
        """
        if path is not None:
            if self.mode in ['gui', 'iggui', 'pbgui']:
                for way_point in path:
                    set_base_values_with_z(
                        self.robot_id,
                        [way_point[0], way_point[1], way_point[2]],
                        z=self.initial_height)
                    self.simulator_sync()
                    # sleep(0.005) # for animation
            else:
                set_base_values_with_z(self.robot_id,
                                       [path[-1][0], path[-1][1], path[-1][2]],
                                       z=self.initial_height)

    def get_ik_parameters(self):
        """
        Get IK parameters such as joint limits, joint damping, reset position, etc

        :return: IK parameters
        """
        max_limits, min_limits, rest_position, joint_range, joint_damping = None, None, None, None, None
        if self.robot_type == 'Fetch':
            max_limits = [0., 0.] + \
                get_max_limits(self.robot_id, self.arm_joint_ids)
            min_limits = [0., 0.] + \
                get_min_limits(self.robot_id, self.arm_joint_ids)
            # increase torso_lift_joint lower limit to 0.02 to avoid self-collision
            min_limits[2] += 0.02
            rest_position = [0., 0.] + \
                list(get_joint_positions(self.robot_id, self.arm_joint_ids))
            joint_range = list(np.array(max_limits) - np.array(min_limits))
            joint_range = [item + 1 for item in joint_range]
            joint_damping = [0.1 for _ in joint_range]

        elif self.robot_type == 'Movo':
            max_limits = get_max_limits(self.robot_id, self.robot.all_joints)
            min_limits = get_min_limits(self.robot_id, self.robot.all_joints)
            rest_position = list(
                get_joint_positions(self.robot_id, self.robot.all_joints))
            joint_range = list(np.array(max_limits) - np.array(min_limits))
            joint_range = [item + 1 for item in joint_range]
            joint_damping = [0.1 for _ in joint_range]

        return (max_limits, min_limits, rest_position, joint_range,
                joint_damping)

    def get_arm_joint_positions(self, arm_ik_goal):
        """
        Attempt to find arm_joint_positions that satisfies arm_subgoal
        If failed, return None

        :param arm_ik_goal: [x, y, z] in the world frame
        :return: arm joint positions
        """
        ik_start = time()

        max_limits, min_limits, rest_position, joint_range, joint_damping = \
            self.get_ik_parameters()

        n_attempt = 0
        max_attempt = 75
        sample_fn = get_sample_fn(self.robot_id, self.arm_joint_ids)
        base_pose = get_base_values(self.robot_id)
        state_id = p.saveState()
        #p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, False)
        # find collision-free IK solution for arm_subgoal
        while n_attempt < max_attempt:
            if self.robot_type == 'Movo':
                self.robot.tuck()

            set_joint_positions(self.robot_id, self.arm_joint_ids, sample_fn())
            arm_joint_positions = p.calculateInverseKinematics(
                self.robot_id,
                self.robot.end_effector_part_index(),
                targetPosition=arm_ik_goal,
                # targetOrientation=self.robots[0].get_orientation(),
                lowerLimits=min_limits,
                upperLimits=max_limits,
                jointRanges=joint_range,
                restPoses=rest_position,
                jointDamping=joint_damping,
                solver=p.IK_DLS,
                maxNumIterations=100)

            if self.robot_type == 'Fetch':
                arm_joint_positions = arm_joint_positions[2:10]
            elif self.robot_type == 'Movo':
                arm_joint_positions = arm_joint_positions[:8]

            set_joint_positions(self.robot_id, self.arm_joint_ids,
                                arm_joint_positions)

            dist = l2_distance(self.robot.get_end_effector_position(),
                               arm_ik_goal)
            # print('dist', dist)
            if dist > self.arm_ik_threshold:
                n_attempt += 1
                continue

            # need to simulator_step to get the latest collision
            self.simulator_step()

            # simulator_step will slightly move the robot base and the objects
            set_base_values_with_z(self.robot_id,
                                   base_pose,
                                   z=self.initial_height)
            # self.reset_object_states()
            # TODO: have a princpled way for stashing and resetting object states

            # arm should not have any collision
            if self.robot_type == 'Movo':
                collision_free = is_collision_free(
                    body_a=self.robot_id, link_a_list=self.arm_joint_ids_all)
                # ignore linear link
            else:
                collision_free = is_collision_free(
                    body_a=self.robot_id, link_a_list=self.arm_joint_ids)

            if not collision_free:
                n_attempt += 1
                # print('arm has collision')
                continue

            # gripper should not have any self-collision
            collision_free = is_collision_free(
                body_a=self.robot_id,
                link_a_list=[self.robot.end_effector_part_index()],
                body_b=self.robot_id)
            if not collision_free:
                n_attempt += 1
                print('gripper has collision')
                continue

            #self.episode_metrics['arm_ik_time'] += time() - ik_start
            #p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, True)
            p.restoreState(state_id)
            p.removeState(state_id)
            return arm_joint_positions

        #p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, True)
        p.restoreState(state_id)
        p.removeState(state_id)
        #self.episode_metrics['arm_ik_time'] += time() - ik_start
        return None

    def plan_arm_motion(self, arm_joint_positions):
        """
        Attempt to reach arm arm_joint_positions and return arm trajectory
        If failed, reset the arm to its original pose and return None

        :param arm_joint_positions: final arm joint position to reach
        :return: arm trajectory or None if no plan can be found
        """
        disabled_collisions = {}
        if self.robot_type == 'Fetch':
            disabled_collisions = {
                (link_from_name(self.robot_id, 'torso_lift_link'),
                 link_from_name(self.robot_id, 'torso_fixed_link')),
                (link_from_name(self.robot_id, 'torso_lift_link'),
                 link_from_name(self.robot_id, 'shoulder_lift_link')),
                (link_from_name(self.robot_id, 'torso_lift_link'),
                 link_from_name(self.robot_id, 'upperarm_roll_link')),
                (link_from_name(self.robot_id, 'torso_lift_link'),
                 link_from_name(self.robot_id, 'forearm_roll_link')),
                (link_from_name(self.robot_id, 'torso_lift_link'),
                 link_from_name(self.robot_id, 'elbow_flex_link'))
            }
        elif self.robot_type == 'Movo':
            disabled_collisions = {
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id, 'right_shoulder_link')),
                (link_from_name(self.robot_id, 'right_base_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id, 'right_arm_half_1_link')),
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id, 'right_arm_half_2_link')),
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id, 'right_forearm_link')),
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id,
                                'right_wrist_spherical_1_link')),
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id,
                                'right_wrist_spherical_2_link')),
                (link_from_name(self.robot_id, 'linear_actuator_link'),
                 link_from_name(self.robot_id, 'right_wrist_3_link')),
                (link_from_name(self.robot_id, 'right_wrist_spherical_2_link'),
                 link_from_name(self.robot_id, 'right_robotiq_coupler_link')),
                (link_from_name(self.robot_id, 'right_shoulder_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'left_base_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'left_shoulder_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'left_arm_half_2_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'right_arm_half_2_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'right_arm_half_1_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
                (link_from_name(self.robot_id, 'left_arm_half_1_link'),
                 link_from_name(self.robot_id, 'linear_actuator_fixed_link')),
            }

        if self.fine_motion_plan:
            self_collisions = True
            mp_obstacles = self.mp_obstacles
        else:
            self_collisions = False
            mp_obstacles = []

        plan_arm_start = time()
        p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, False)
        state_id = p.saveState()

        allow_collision_links = []
        if self.robot_type == 'Fetch':
            allow_collision_links = [19]
        elif self.robot_type == 'Movo':
            allow_collision_links = [23, 24]

        arm_path = plan_joint_motion(
            self.robot_id,
            self.arm_joint_ids,
            arm_joint_positions,
            disabled_collisions=disabled_collisions,
            self_collisions=self_collisions,
            obstacles=mp_obstacles,
            algorithm=self.arm_mp_algo,
            allow_collision_links=allow_collision_links,
        )
        p.configureDebugVisualizer(p.COV_ENABLE_RENDERING, True)
        p.restoreState(state_id)
        p.removeState(state_id)
        return arm_path

    def dry_run_arm_plan(self, arm_path):
        """
        Dry run arm motion plan by setting the arm joint position without physics simulation

        :param arm_path: arm trajectory or None if no plan can be found
        """
        base_pose = get_base_values(self.robot_id)
        if arm_path is not None:
            if self.mode in ['gui', 'iggui', 'pbgui']:
                for joint_way_point in arm_path:
                    set_joint_positions(self.robot_id, self.arm_joint_ids,
                                        joint_way_point)
                    set_base_values_with_z(self.robot_id,
                                           base_pose,
                                           z=self.initial_height)
                    self.simulator_sync()
                    # sleep(0.02)  # animation
            else:
                set_joint_positions(self.robot_id, self.arm_joint_ids,
                                    arm_path[-1])
        else:
            # print('arm mp fails')
            if self.robot_type == 'Movo':
                self.robot.tuck()
            set_joint_positions(self.robot_id, self.arm_joint_ids,
                                self.arm_default_joint_positions)

    def plan_arm_push(self, hit_pos, hit_normal):
        """
        Attempt to reach a 3D position and prepare for a push later

        :param hit_pos: 3D position to reach
        :param hit_normal: direction to push after reacehing that position
        :return: arm trajectory or None if no plan can be found
        """
        if self.marker is not None:
            self.set_marker_position_direction(hit_pos, hit_normal)
        joint_positions = self.get_arm_joint_positions(hit_pos)

        #print('planned JP', joint_positions)
        set_joint_positions(self.robot_id, self.arm_joint_ids,
                            self.arm_default_joint_positions)
        self.simulator_sync()
        if joint_positions is not None:
            plan = self.plan_arm_motion(joint_positions)
            return plan
        else:
            return None

    def interact(self, push_point, push_direction):
        """
        Move the arm starting from the push_point along the push_direction
        and physically simulate the interaction

        :param push_point: 3D point to start pushing from
        :param push_direction: push direction
        """
        push_vector = np.array(push_direction) * self.arm_interaction_length

        max_limits, min_limits, rest_position, joint_range, joint_damping = \
            self.get_ik_parameters()
        base_pose = get_base_values(self.robot_id)

        steps = 50
        for i in range(steps):
            push_goal = np.array(push_point) + \
                push_vector * (i + 1) / float(steps)

            joint_positions = p.calculateInverseKinematics(
                self.robot_id,
                self.robot.end_effector_part_index(),
                targetPosition=push_goal,
                # targetOrientation=self.robots[0].get_orientation(),
                lowerLimits=min_limits,
                upperLimits=max_limits,
                jointRanges=joint_range,
                restPoses=rest_position,
                jointDamping=joint_damping,
                solver=p.IK_DLS,
                maxNumIterations=100)

            if self.robot_type == 'Fetch':
                joint_positions = joint_positions[2:10]
            elif self.robot_type == 'Movo':
                joint_positions = joint_positions[:8]

            control_joints(self.robot_id, self.arm_joint_ids, joint_positions)

            # set_joint_positions(self.robot_id, self.arm_joint_ids, joint_positions)
            achieved = self.robot.get_end_effector_position()
            # print('ee delta', np.array(achieved) - push_goal, np.linalg.norm(np.array(achieved) - push_goal))

            # if self.robot_type == 'Movo':
            #    self.robot.control_tuck_left()
            self.simulator_step()
            set_base_values_with_z(self.robot_id,
                                   base_pose,
                                   z=self.initial_height)

            if self.mode in ['pbgui', 'iggui', 'gui']:
                sleep(0.02)  # for visualization

    def execute_arm_push(self, plan, hit_pos, hit_normal):
        """
        Execute arm push given arm trajectory
        Should be called after plan_arm_push()

        :param plan: arm trajectory or None if no plan can be found
        :param hit_pos: 3D position to reach
        :param hit_normal: direction to push after reacehing that position
        """
        if plan is not None:
            self.dry_run_arm_plan(plan)
            self.interact(hit_pos, hit_normal)
            set_joint_positions(self.robot_id, self.arm_joint_ids,
                                self.arm_default_joint_positions)
            self.simulator_sync()