def camera_intrinsics(self, T_camera_obj, f, cx, cy):
        """ Generate shifted camera intrinsics to simulate cropping """
        # form intrinsics
        camera_intr = CameraIntrinsics(self.frame,
                                       fx=f,
                                       fy=f,
                                       cx=cx,
                                       cy=cy,
                                       skew=0.0,
                                       height=self.im_height,
                                       width=self.im_width)

        # compute new camera center by projecting object 0,0,0 into the camera
        center_obj_obj = Point(np.zeros(3), frame='obj')
        center_obj_camera = T_camera_obj * center_obj_obj
        u_center_obj = camera_intr.project(center_obj_camera)
        camera_shifted_intr = copy.deepcopy(camera_intr)
        camera_shifted_intr.cx = 2 * camera_intr.cx - float(u_center_obj.x)
        camera_shifted_intr.cy = 2 * camera_intr.cy - float(u_center_obj.y)
        return camera_shifted_intr
Exemple #2
0
class MultiSuctionPoint2D(object):
    """Multi-Cup Suction grasp in image space.

    Equivalent to projecting a 6D pose to image space.

    Attributes
    ----------
    pose : :obj:`autolab_core.RigidTransform`
        Pose in 3D camera space.
    camera_intr : :obj:`perception.CameraIntrinsics`
        Frame of reference for camera that the suction point corresponds to.
    """
    def __init__(self, pose, camera_intr=None):
        self._pose = pose

        # TODO(vsatish): Confirm that this is really not needed.
        #        frame = "image"
        #        if camera_intr is not None:
        #            frame = camera_intr.frame

        # If `camera_intr` is `None` use default primesense camera intrinsics.
        if not camera_intr:
            self.camera_intr = CameraIntrinsics("primesense_overhead",
                                                fx=525,
                                                fy=525,
                                                cx=319.5,
                                                cy=239.5,
                                                width=640,
                                                height=480)
        else:
            self.camera_intr = camera_intr

    def pose(self):
        return self._pose

    @property
    def frame(self):
        """The name of the frame of reference for the grasp."""
        if self.camera_intr is None:
            raise ValueError("Must specify camera intrinsics")
        return self.camera_intr.frame

    @property
    def center(self):
        center_camera = Point(self._pose.translation,
                              frame=self.camera_intr.frame)
        center_px = self.camera_intr.project(center_camera)
        return center_px

    @property
    def axis(self):
        return self._pose.x_axis

    @property
    def approach_axis(self):
        return self.axis

    @property
    def approach_angle(self):
        """The angle between the grasp approach axis and camera optical axis.
        """
        dot = max(min(self.axis.dot(np.array([0, 0, 1])), 1.0), -1.0)
        return np.arccos(dot)

    @property
    def angle(self):
        g_axis = self._pose.y_axis
        g_axis_im = np.array([g_axis[0], g_axis[1], 0])
        if np.linalg.norm(g_axis_im) == 0:
            return 0
        theta = np.arctan2(g_axis[1], g_axis[0])
        return theta

    @property
    def depth(self):
        return self._pose.translation[2]

    @property
    def orientation(self):
        x_axis = self.axis
        y_axis = np.array([x_axis[1], -x_axis[0], 0])
        if np.linalg.norm(y_axis) == 0:
            y_axis = np.array([1, 0, 0])
        y_axis = y_axis / np.linalg.norm(y_axis)
        z_axis = np.cross(x_axis, y_axis)
        R = np.array([x_axis, y_axis, z_axis]).T
        delta_R = R.T.dot(self._pose.rotation)
        orientation = np.arccos(delta_R[1, 1])
        if delta_R[1, 2] > 0:
            orientation = 2 * np.pi - orientation
        return orientation

    @property
    def feature_vec(self):
        """Returns the feature vector for the suction point.

        Note
        ----
        `v = [center, axis, depth]`
        """
        return np.r_[self.center.data,
                     np.cos(self.orientation),
                     np.sin(self.orientation)]

    @staticmethod
    def from_feature_vec(v,
                         camera_intr=None,
                         angle=None,
                         depth=None,
                         axis=None):
        """Creates a `SuctionPoint2D` instance from a feature vector and
        additional parameters.

        Parameters
        ----------
        v : :obj:`numpy.ndarray`
            Feature vector, see `Grasp2D.feature_vec`.
        camera_intr : :obj:`perception.CameraIntrinsics`
            Frame of reference for camera that the grasp corresponds to.
        depth : float
            Hard-set the depth for the suction grasp.
        axis : :obj:`numpy.ndarray`
            Normalized 3-vector specifying the approach direction.
        """
        # Read feature vec.
        center_px = v[:2]

        grasp_angle = 0
        if v.shape[0] > 2 and angle is None:
            # grasp_angle = v[2]
            grasp_vec = v[2:]
            grasp_vec = grasp_vec / np.linalg.norm(grasp_vec)
            grasp_angle = np.arctan2(grasp_vec[1], grasp_vec[0])
        elif angle is not None:
            grasp_angle = angle

        grasp_axis = np.array([1, 0, 0])
        if axis is not None:
            grasp_axis = axis

        grasp_depth = 0.5
        if depth is not None:
            grasp_depth = depth

        x_axis = grasp_axis
        y_axis = np.array([grasp_axis[1], -grasp_axis[0], 0])
        if np.linalg.norm(y_axis) == 0:
            y_axis = np.array([1, 0, 0])
        y_axis = y_axis / np.linalg.norm(y_axis)
        z_axis = np.cross(x_axis, y_axis)

        R = np.array([x_axis, y_axis, z_axis]).T
        R = R.dot(RigidTransform.x_axis_rotation(grasp_angle))
        t = camera_intr.deproject_pixel(
            grasp_depth, Point(center_px, frame=camera_intr.frame)).data
        T = RigidTransform(rotation=R,
                           translation=t,
                           from_frame="grasp",
                           to_frame=camera_intr.frame)

        # Compute center and angle.
        return MultiSuctionPoint2D(T, camera_intr=camera_intr)

    @staticmethod
    def image_dist(g1, g2, alpha=1.0):
        """Computes the distance between grasps in image space.

        Uses Euclidean distance with alpha weighting of angles.

        Parameters
        ----------
        g1 : :obj:`SuctionPoint2D`
            First suction point.
        g2 : :obj:`SuctionPoint2D`
            Second suction point.
        alpha : float
            Weight of angle distance (rad to meters).

        Returns
        -------
        float
            Distance between grasps.
        """
        # Point to point distances.
        point_dist = np.linalg.norm(g1.center.data - g2.center.data)

        # Axis distances.
        dot = max(min(np.abs(g1.axis.dot(g2.axis)), 1.0), -1.0)
        axis_dist = np.arccos(dot)

        return point_dist + alpha * axis_dist
Exemple #3
0
class Grasp2D(object):
    """Parallel-jaw grasp in image space.

    Attributes
    ----------
    center : :obj:`autolab_core.Point`
        Point in image space.
    angle : float
        Grasp axis angle with the camera x-axis.
    depth : float
        Depth of the grasp center in 3D space.
    width : float
        Distance between the jaws in meters.
    camera_intr : :obj:`perception.CameraIntrinsics`
        Frame of reference for camera that the grasp corresponds to.
    contact_points : list of :obj:`numpy.ndarray`
        Pair of contact points in image space.
    contact_normals : list of :obj:`numpy.ndarray`
        Pair of contact normals in image space.
    """
    def __init__(self,
                 center,
                 angle=0.0,
                 depth=1.0,
                 width=0.0,
                 camera_intr=None,
                 contact_points=None,
                 contact_normals=None):
        self.center = center
        self.angle = angle
        self.depth = depth
        self.width = width
        # If `camera_intr` is none use default primesense camera intrinsics.
        if not camera_intr:
            self.camera_intr = CameraIntrinsics("primesense_overhead",
                                                fx=525,
                                                fy=525,
                                                cx=319.5,
                                                cy=239.5,
                                                width=640,
                                                height=480)
        else:
            self.camera_intr = camera_intr
        self.contact_points = contact_points
        self.contact_normals = contact_normals

        frame = "image"
        if camera_intr is not None:
            frame = camera_intr.frame
        if isinstance(center, np.ndarray):
            self.center = Point(center, frame=frame)

    @property
    def axis(self):
        """Returns the grasp axis."""
        return np.array([np.cos(self.angle), np.sin(self.angle)])

    @property
    def approach_axis(self):
        return np.array([0, 0, 1])

    @property
    def approach_angle(self):
        """The angle between the grasp approach axis and camera optical axis.
        """
        return 0.0

    @property
    def frame(self):
        """The name of the frame of reference for the grasp."""
        if self.camera_intr is None:

            raise ValueError("Must specify camera intrinsics")
        return self.camera_intr.frame

    @property
    def width_px(self):
        """Returns the width in pixels."""
        if self.camera_intr is None:
            missing_camera_intr_msg = ("Must specify camera intrinsics to"
                                       " compute gripper width in 3D space.")
            raise ValueError(missing_camera_intr_msg)
        # Form the jaw locations in 3D space at the given depth.
        p1 = Point(np.array([0, 0, self.depth]), frame=self.frame)
        p2 = Point(np.array([self.width, 0, self.depth]), frame=self.frame)

        # Project into pixel space.
        u1 = self.camera_intr.project(p1)
        u2 = self.camera_intr.project(p2)
        return np.linalg.norm(u1.data - u2.data)

    @property
    def endpoints(self):
        """Returns the grasp endpoints."""
        p1 = self.center.data - (self.width_px / 2) * self.axis
        p2 = self.center.data + (self.width_px / 2) * self.axis
        return p1, p2

    @property
    def feature_vec(self):
        """Returns the feature vector for the grasp.

        `v = [p1, p2, depth]` where `p1` and `p2` are the jaw locations in
        image space.
        """
        p1, p2 = self.endpoints
        return np.r_[p1, p2, self.depth]

    @staticmethod
    def from_feature_vec(v, width=0.0, camera_intr=None):
        """Creates a `Grasp2D` instance from a feature vector and additional
        parameters.

        Parameters
        ----------
        v : :obj:`numpy.ndarray`
            Feature vector, see `Grasp2D.feature_vec`.
        width : float
            Grasp opening width, in meters.
        camera_intr : :obj:`perception.CameraIntrinsics`
            Frame of reference for camera that the grasp corresponds to.
        """
        # Read feature vec.
        p1 = v[:2]
        p2 = v[2:4]
        depth = v[4]

        # Compute center and angle.
        center_px = (p1 + p2) // 2
        center = Point(center_px, camera_intr.frame)
        axis = p2 - p1
        if np.linalg.norm(axis) > 0:
            axis = axis / np.linalg.norm(axis)
        if axis[1] > 0:
            angle = np.arccos(axis[0])
        else:
            angle = -np.arccos(axis[0])
        return Grasp2D(center,
                       angle,
                       depth,
                       width=width,
                       camera_intr=camera_intr)

    def pose(self, grasp_approach_dir=None):
        """Computes the 3D pose of the grasp relative to the camera.

        If an approach direction is not specified then the camera
        optical axis is used.

        Parameters
        ----------
        grasp_approach_dir : :obj:`numpy.ndarray`
            Approach direction for the grasp in camera basis (e.g. opposite to
            table normal).

        Returns
        -------
        :obj:`autolab_core.RigidTransform`
            The transformation from the grasp to the camera frame of reference.
        """
        # Check intrinsics.
        if self.camera_intr is None:
            raise ValueError(
                "Must specify camera intrinsics to compute 3D grasp pose")

        # Compute 3D grasp center in camera basis.
        grasp_center_im = self.center.data
        center_px_im = Point(grasp_center_im, frame=self.camera_intr.frame)
        grasp_center_camera = self.camera_intr.deproject_pixel(
            self.depth, center_px_im)
        grasp_center_camera = grasp_center_camera.data

        # Compute 3D grasp axis in camera basis.
        grasp_axis_im = self.axis
        grasp_axis_im = grasp_axis_im / np.linalg.norm(grasp_axis_im)
        grasp_axis_camera = np.array([grasp_axis_im[0], grasp_axis_im[1], 0])
        grasp_axis_camera = grasp_axis_camera / np.linalg.norm(
            grasp_axis_camera)

        # Convert to 3D pose.
        grasp_rot_camera, _, _ = np.linalg.svd(grasp_axis_camera.reshape(3, 1))
        grasp_x_camera = grasp_approach_dir
        if grasp_approach_dir is None:
            grasp_x_camera = np.array([0, 0, 1])  # Align with camera Z axis.
        grasp_y_camera = grasp_axis_camera
        grasp_z_camera = np.cross(grasp_x_camera, grasp_y_camera)
        grasp_z_camera = grasp_z_camera / np.linalg.norm(grasp_z_camera)
        grasp_y_camera = np.cross(grasp_z_camera, grasp_x_camera)
        grasp_rot_camera = np.array(
            [grasp_x_camera, grasp_y_camera, grasp_z_camera]).T
        if np.linalg.det(grasp_rot_camera) < 0:  # Fix reflections due to SVD.
            grasp_rot_camera[:, 0] = -grasp_rot_camera[:, 0]
        T_grasp_camera = RigidTransform(rotation=grasp_rot_camera,
                                        translation=grasp_center_camera,
                                        from_frame="grasp",
                                        to_frame=self.camera_intr.frame)
        return T_grasp_camera

    @staticmethod
    def image_dist(g1, g2, alpha=1.0):
        """Computes the distance between grasps in image space.

        Uses Euclidean distance with alpha weighting of angles

        Parameters
        ----------
        g1 : :obj:`Grasp2D`
            First grasp.
        g2 : :obj:`Grasp2D`
            Second grasp.
        alpha : float
            Weight of angle distance (rad to meters).

        Returns
        -------
        float
            Distance between grasps.
        """
        # Point to point distances.
        point_dist = np.linalg.norm(g1.center.data - g2.center.data)

        # Axis distances.
        dot = max(min(np.abs(g1.axis.dot(g2.axis)), 1.0), -1.0)
        axis_dist = np.arccos(dot)
        return point_dist + alpha * axis_dist
Exemple #4
0
class Grasp2D(object):
    """
    Parallel-jaw grasp in image space.

    Attributes
    ----------
    center : :obj:`autolab_core.Point`
        point in image space
    angle : float
        grasp axis angle with the camera x-axis
    depth : float
        depth of the grasp center in 3D space
    width : float
        distance between the jaws in meters
    camera_intr : :obj:`perception.CameraIntrinsics`
        frame of reference for camera that the grasp corresponds to
    """
    def __init__(self, center, angle, depth, width=0.0, camera_intr=None):
        self.center = center
        self.angle = angle
        self.depth = depth
        self.width = width
        # if camera_intr is none use default primesense camera intrinsics
        if not camera_intr:
            self.camera_intr = CameraIntrinsics('primesense_overhead', fx=525, fy=525, cx=319.5, cy=239.5, width=640, height=480)
        else: 
            self.camera_intr = camera_intr

    @property
    def axis(self):
        """ Returns the grasp axis. """
        return np.array([np.cos(self.angle), np.sin(self.angle)])        
        
    @property
    def frame(self):
        """ The name of the frame of reference for the grasp. """
        if self.camera_intr is None:
            raise ValueError('Must specify camera intrinsics')
        return self.camera_intr.frame

    @property
    def width_px(self):
        """ Returns the width in pixels. """
        if self.camera_intr is None:
            raise ValueError('Must specify camera intrinsics to compute gripper width in 3D space')
        # form the jaw locations in 3D space at the given depth
        p1 = Point(np.array([0, 0, self.depth]), frame=self.frame)
        p2 = Point(np.array([self.width, 0, self.depth]), frame=self.frame)
        
        # project into pixel space
        u1 = self.camera_intr.project(p1)
        u2 = self.camera_intr.project(p2)
        return np.linalg.norm(u1.data - u2.data)

    @property
    def endpoints(self):
        """ Returns the grasp endpoints """
        p1 = self.center.data - (float(self.width_px) / 2) * self.axis
        p2 = self.center.data + (float(self.width_px) / 2) * self.axis
        return p1, p2

    @property
    def feature_vec(self):
        """ Returns the feature vector for the grasp.
        v = [p1, p2, depth]
        where p1 and p2 are the jaw locations in image space
        """
        p1, p2 = self.endpoints
        return np.r_[p1, p2, self.depth]

    @staticmethod
    def from_feature_vec(v, width=0.0, camera_intr=None):
        """ Creates a Grasp2D obj from a feature vector and additional parameters.

        Parameters
        ----------
        v : :obj:`numpy.ndarray`
            feature vector, see Grasp2D.feature_vec
        width : float
            grasp opening width, in meters
        camera_intr : :obj:`perception.CameraIntrinsics`
            frame of reference for camera that the grasp corresponds to
        """
        # read feature vec
        p1 = v[:2]
        p2 = v[2:4]
        depth = v[4]

        # compute center and angle
        center_px = (p1 + p2) / 2
        center = Point(center_px, camera_intr.frame)
        axis = p2 - p1
        if np.linalg.norm(axis) > 0:
            axis = axis / np.linalg.norm(axis)
        if axis[1] > 0:
            angle = np.arccos(axis[0])
        else:
            angle = -np.arccos(axis[0])
        return Grasp2D(center, angle, depth, width=width, camera_intr=camera_intr)

    def pose(self, grasp_approach_dir=None):
        """ Computes the 3D pose of the grasp relative to the camera.
        If an approach direction is not specified then the camera
        optical axis is used.
     
        Parameters
        ----------
        grasp_approach_dir : :obj:`numpy.ndarray`
            approach direction for the grasp in camera basis (e.g. opposite to table normal)

        Returns
        -------
        :obj:`autolab_core.RigidTransform`
            the transformation from the grasp to the camera frame of reference
        """
        # check intrinsics
        if self.camera_intr is None:
            raise ValueError('Must specify camera intrinsics to compute 3D grasp pose')

        # compute 3D grasp center in camera basis
        grasp_center_im = self.center.data
        center_px_im = Point(grasp_center_im, frame=self.camera_intr.frame)
        grasp_center_camera = self.camera_intr.deproject_pixel(self.depth, center_px_im)
        grasp_center_camera = grasp_center_camera.data

        # compute 3D grasp axis in camera basis
        grasp_axis_im = self.axis
        grasp_axis_im = grasp_axis_im / np.linalg.norm(grasp_axis_im)
        grasp_axis_camera = np.array([grasp_axis_im[0], grasp_axis_im[1], 0])
        grasp_axis_camera = grasp_axis_camera / np.linalg.norm(grasp_axis_camera)
        
        # convert to 3D pose
        grasp_rot_camera, _, _ = np.linalg.svd(grasp_axis_camera.reshape(3,1))
        grasp_x_camera = grasp_approach_dir
        if grasp_approach_dir is None:
            grasp_x_camera = np.array([0,0,1]) # aligned with camera Z axis
        grasp_y_camera = grasp_axis_camera
        grasp_z_camera = np.cross(grasp_x_camera, grasp_y_camera)
        grasp_x_camera = np.cross(grasp_z_camera, grasp_y_camera)
        grasp_rot_camera = np.array([grasp_x_camera, grasp_y_camera, grasp_z_camera]).T
        if np.linalg.det(grasp_rot_camera) < 0: # fix possible reflections due to SVD
            grasp_rot_camera[:,0] = -grasp_rot_camera[:,0]
        T_grasp_camera = RigidTransform(rotation=grasp_rot_camera,
                                        translation=grasp_center_camera,
                                        from_frame='grasp',
                                       to_frame=self.camera_intr.frame)
        return T_grasp_camera

    @staticmethod
    def image_dist(g1, g2, alpha=1.0):
        """ Computes the distance between grasps in image space.
        Euclidean distance with alpha weighting of angles

        Parameters
        ----------
        g1 : :obj:`Grasp2D`
            first grasp
        g2 : :obj:`Grasp2D`
            second grasp
        alpha : float
            weight of angle distance (rad to meters)

        Returns
        -------
        float
            distance between grasps
        """
        # point to point distances
        point_dist = np.linalg.norm(g1.center.data - g2.center.data)

        # axis distances
        axis_dist = np.arccos(np.abs(g1.axis.dot(g2.axis)))

        return point_dist + alpha * axis_dist