示例#1
0
 def __init__(self, s=None, v=None, check=True, norm=True):
     """        
     A unit quaternion is one for which M{s^2+vx^2+vy^2+vz^2 = 1}.
     A quaternion can be considered as a rotation about a vector in space where
     q = cos (theta/2) sin(theta/2) <vx vy vz>
     where <vx vy vz> is a unit vector.
     :param s: scalar
     :param v: vector
     """
     if s is None and v is None:
         self.data = [ quat.qone() ]
         
     elif argcheck.isscalar(s) and argcheck.isvector(v,3):
         self.data = [ np.r_[s, argcheck.getvector(v)] ]
         
     elif argcheck.isvector(s,4):
         self.data = [ argcheck.getvector(s) ]
         
     elif type(s) is list:
         if check:
             assert argcheck.isvectorlist(s,4), 'list must comprise 4-vectors'
         self.data = s
     
     elif isinstance(s, np.ndarray) and s.shape[1] == 4:
         self.data = [x for x in s]
         
     else:
         raise ValueError('bad argument to Quaternion constructor')
    def __init__(self, value):
        """
        Create a new spatial vector (abstract superclass)

        :param value: Value of the

        - ``SpatialVector(vec)`` is a spatial vector constructed from the 6-element array-like ``vec``
        - ``SpatialVector([V1, V2, ... VN])`` is a spatial vector array with N elements, constructed from the 6-element
           array-like values ``Vi``
        - ``SpatialVector(A)`` is a spatial vector array with N elements, constructed from the columns of the 6xN
          array ``A``.

        :seealso: :func:`SpatialVelocity`, :func:`SpatialAcceleration`, :func:`SpatialForce`, :func:`SpatialMomentum`.
        """
        print('spatialVec6 init')
        super().__init__()

        if value is None:
            self.data = [np.zeros((6,))]
        elif arg.isvector(value, 6):
            self.data = [np.array(value)]
        elif isinstance(value, list):
            assert all(map(lambda x: arg.isvector(x, 6), value)), 'all elements of list must have valid shape and value for the class'
            self.data = [np.array(x) for x in value]
        elif arg.ismatrix(value, (6, None)):
            self.data = [x for x in value.T]
        else:
            raise ValueError('bad arguments to constructor')
示例#3
0
    def I(self, I_new):  # noqa

        if ismatrix(I_new, (3, 3)):
            # 3x3 matrix passed
            if np.any(np.abs(I_new - I_new.T) > 1e-8):
                raise ValueError('3x3 matrix is not symmetric')

        elif isvector(I_new, 9):
            # 3x3 matrix passed as a 1d vector
            I_new = I_new.reshape(3, 3)
            if np.any(np.abs(I_new - I_new.T) > 1e-8):
                raise ValueError('3x3 matrix is not symmetric')

        elif isvector(I_new, 6):
            # 6-vector passed, moments and products of inertia,
            # [Ixx Iyy Izz Ixy Iyz Ixz]
            I_new = np.array([
                [I_new[0], I_new[3], I_new[5]],
                [I_new[3], I_new[1], I_new[4]],
                [I_new[5], I_new[4], I_new[2]]
            ])

        elif isvector(I_new, 3):
            # 3-vector passed, moments of inertia [Ixx Iyy Izz]
            I_new = np.diag(I_new)

        else:
            raise ValueError('invalid shape passed: must be (3,3), (6,), (3,)')

        self._I = I_new
示例#4
0
    def __init__(self, x=None, y=None, theta=None, *, unit='rad', check=True):
        """
        Construct new SE(2) object

        :param unit: angular units 'deg' or 'rad' [default] if applicable
        :type unit: str, optional
        :param check: check for valid SE(2) elements if applicable, default to True
        :type check: bool
        :return: homogeneous rigid-body transformation matrix
        :rtype: SE2 instance
        
        - ``SE2()`` is an SE2 instance representing a null motion -- the identity matrix
        - ``SE2(x, y)`` is an SE2 instance representing a pure translation of (``x``, ``y``)
        - ``SE2(t)`` is an SE2 instance representing a pure translation of (``x``, ``y``) where``t``=[x,y] is a 2-element array_like
        - ``SE2(x, y, theta)`` is an SE2 instance representing a translation of (``x``, ``y``) and a rotation of ``theta`` radians
        - ``SE2(x, y, theta, unit='deg')`` is an SE2 instance representing a translation of (``x``, ``y``) and a rotation of ``theta`` degrees
        - ``SE2(t)`` is an SE2 instance representing a translation of (``x``, ``y``) and a rotation of ``theta`` where ``t``=[x,y,theta] is a 3-element array_like
        - ``SE2(T)`` is an SE2 instance with rigid-body motion described by the SE(2) matrix T which is a 3x3 numpy array.  If ``check``
          is ``True`` check the matrix belongs to SE(2).
        - ``SE2([T1, T2, ... TN])`` is an SE2 instance containing a sequence of N rigid-body motions, each described by an SE(2) matrix
          Ti which is a 3x3 numpy array. If ``check`` is ``True`` then check each matrix belongs to SE(2).
        - ``SE2([X1, X2, ... XN])`` is an SE2 instance containing a sequence of N rigid-body motions, where each Xi is an SE2 instance.
        
        """
        super().__init__()  # activate the UserList semantics

        if x is None and y is None and theta is None:
            # SE2()
            # empty constructor
            self.data = [np.eye(3)]

        elif x is not None:
            if y is not None and theta is None:
                # SE2(x, y)
                self.data = [tr.transl2(x, y)]
            elif y is not None and theta is not None:
                # SE2(x, y, theta)
                self.data = [tr.trot2(theta, t=[x, y], unit=unit)]
            elif y is None and theta is None:
                if argcheck.isvector(x, 2):
                    # SE2([x,y])
                    self.data = [tr.transl2(x)]
                elif argcheck.isvector(x, 3):
                    # SE2([x,y,theta])
                    self.data = [tr.trot2(x[2], t=x[:2], unit=unit)]
                else:
                    super()._arghandler(x, check=check)
        else:
            raise ValueError('bad arguments to constructor')
示例#5
0
    def __init__(self, v=None, w=None):
        """
        Create a Plucker 3D line object
        
        :param v: Plucker vector, Plucker object, Plucker moment
        :type v: 6-element array_like, Plucker instance, 3-element array_like
        :param w: Plucker direction, optional
        :type w: 3-element array_like, optional
        :raises ValueError: bad arguments
        :return: Plucker line
        :rtype: Plucker

        - ``L = Plucker(X)`` creates a Plucker object from the Plucker coordinate vector
          ``X`` = [V,W] where V (3-vector) is the moment and W (3-vector) is the line direction.

        - ``L = Plucker(L)`` creates a copy of the Plucker object ``L``.
        
        - ``L = Plucker(V, W)`` creates a Plucker object from moment ``V`` (3-vector) and
          line direction ``W`` (3-vector).
          
        Notes:
            
        - The Plucker object inherits from ``collections.UserList`` and has list-like
          behaviours.
        - A single Plucker object contains a 1D array of Plucker coordinates.
        - The elements of the array are guaranteed to be Plucker coordinates.
        - The number of elements is given by ``len(L)``
        - The elements can be accessed using index and slice notation, eg. ``L[1]`` or
          ``L[2:3]``
        - The Plucker instance can be used as an iterator in a for loop or list comprehension.
        - Some methods support operations on the internal list.
          
        :seealso: Plucker.PQ, Plucker.Planes, Plucker.PointDir
        """
        super().__init__()  # enable list powers
        if w is None:
            # single parameter
            if isinstance(v, Plucker):
                self.data = [v.A]
            elif arg.isvector(v, 6):
                pl = arg.getvector(v)
                self.data = [pl]
            else:
                raise ValueError('bad argument')
        else:
            assert arg.isvector(v, 3) and arg.isvector(
                w, 3), 'expecting two 3-vectors'
            self.data = [np.r_[v, w]]
示例#6
0
    def contains(self, x, tol=50 * _eps):
        """
        Test if points are on the line
        
        :param x: 3D point
        :type x: 3-element array_like, or numpy.ndarray, shape=(3,N)
        :param tol: Tolerance, defaults to 50*_eps
        :type tol: float, optional
        :raises ValueError: Bad argument
        :return: Whether point is on the line
        :rtype: bool or numpy.ndarray(N) of bool

        ``line.contains(X)`` is true if the point ``X`` lies on the line defined by
        the Plucker object self.
        
        If ``X`` is an array with 3 rows, the test is performed on every column and
        an array of booleans is returned.
        """
        if arg.isvector(x, 3):
            x = arg.getvector(x)
            return np.linalg.norm(np.cross(x - self.pp, self.w)) < tol
        elif arg.ismatrix(x, (3, None)):
            return [
                np.linalg.norm(np.cross(_ - self.pp, self.w)) < tol
                for _ in x.T
            ]
        else:
            raise ValueError('bad argument')
示例#7
0
    def RPY(cls, angles, order='zyx', unit='rad'):
        """
        Create an SO(3) pure rotation from roll-pitch-yaw angles

        :param angles: 3-vector of roll-pitch-yaw angles
        :type angles: array_like or numpy.ndarray with shape=(N,3)
        :param unit: angular units: 'rad' [default], or 'deg'
        :type unit: str
        :param unit: rotation order: 'zyx' [default], 'xyz', or 'yxz'
        :type unit: str
        :return: 4x4 homogeneous transformation matrix
        :rtype: SE3 instance

        ``SE3.RPY(ANGLES)`` is an SE(3) rotation defined by a 3-vector of roll, pitch, yaw angles :math:`(r, p, y)`
          which correspond to successive rotations about the axes specified by ``order``:

            - 'zyx' [default], rotate by yaw about the z-axis, then by pitch about the new y-axis,
              then by roll about the new x-axis.  Convention for a mobile robot with x-axis forward
              and y-axis sideways.
            - 'xyz', rotate by yaw about the x-axis, then by pitch about the new y-axis,
              then by roll about the new z-axis. Covention for a robot gripper with z-axis forward
              and y-axis between the gripper fingers.
            - 'yxz', rotate by yaw about the y-axis, then by pitch about the new x-axis,
              then by roll about the new z-axis. Convention for a camera with z-axis parallel
              to the optic axis and x-axis parallel to the pixel rows.
              
        If ``angles`` is an Nx3 matrix then the result is a sequence of rotations each defined by RPY angles
        correponding to the rows of angles.

        :seealso: :func:`~spatialmath.pose3d.SE3.rpy`, :func:`~spatialmath.pose3d.SE3.RPY`, :func:`spatialmath.base.transforms3d.rpy2r`
        """
        if argcheck.isvector(angles, 3):
            return cls(tr.rpy2tr(angles, order=order, unit=unit))
        else:
            return cls([tr.rpy2tr(a, order=order, unit=unit) for a in angles])
示例#8
0
    def Eul(cls, angles, *, unit='rad'):
        r"""
        Create an SE(3) pure rotation from Euler angles

        :param angles: 3-vector of Euler angles
        :type angles: array_like or numpy.ndarray with shape=(N,3)
        :param unit: angular units: 'rad' [default], or 'deg'
        :type unit: str
        :return: 4x4 homogeneous transformation matrix
        :rtype: SE3 instance

        ``SE3.Eul(ANGLES)`` is an SO(3) rotation defined by a 3-vector of Euler
        angles :math:`(\phi, \theta, \psi)` which correspond to consecutive
        rotations about the Z, Y, Z axes respectively.

        If ``angles`` is an Nx3 matrix then the result is a sequence of
        rotations each defined by Euler angles corresponding to the rows of
        angles.

        :seealso: :func:`~spatialmath.pose3d.SE3.eul`, :func:`~spatialmath.pose3d.SE3.Eul`, :func:`spatialmath.base.transforms3d.eul2r`
        """
        if argcheck.isvector(angles, 3):
            return cls(tr.eul2tr(angles, unit=unit), check=False)
        else:
            return cls([tr.eul2tr(a, unit=unit) for a in angles], check=False)
示例#9
0
    def __mul__(left, right):
        """
        multiply quaternion
        
        :arg left: left multiplicand
        :type left: Quaternion, UnitQuaternion
        :arg right: right multiplicand
        :type left: Quaternion, UnitQuaternion, 3-vector, float
        :return: product
        :rtype: Quaternion, UnitQuaternion
        :raises: ValueError
        
        ==============   ==============   ==============  ================
                   Multiplicands                   Product
        -------------------------------   --------------------------------
            left             right            type           result
        ==============   ==============   ==============  ================
        Quaternion       Quaternion       Quaternion      Hamilton product
        Quaternion       UnitQuaternion   Quaternion      Hamilton product
        Quaternion       scalar           Quaternion      scalar product
        UnitQuaternion   Quaternion       Quaternion      Hamilton product
        UnitQuaternion   UnitQuaternion   UnitQuaternion  Hamilton product
        UnitQuaternion   scalar           Quaternion      scalar product
        UnitQuaternion   3-vector         3-vector        vector rotation
        ==============   ==============   ==============  ================

        Any other input combinations result in a ValueError.
        
        Note that left and right can have a length greater than 1 in which case:
        
        ====   =====   ====  ================================
        left   right   len     operation
        ====   =====   ====  ================================
         1      1       1    ``prod = left * right``
         1      N       N    ``prod[i] = left * right[i]``
         N      1       N    ``prod[i] = left[i] * right``
         N      N       N    ``prod[i] = left[i] * right[i]``
         N      M       -    ``ValueError``
        ====   =====   ====  ================================

        A scalar of length N is a list, tuple or numpy array.
        A 3-vector of length N is a 3xN numpy array, where each column is a 3-vector.
        """
        if type(left) == type(right):
            # quaternion * quaternion case (same class)
            return left.__class__( left._op2(right, lambda x, y: quat.qqmul(x, y) ) )
        elif isinstance(other, Quaternion):
            # quaternion * quaternion case (different class)
            return Quaternion( left._op2(right, lambda x, y: quat.qqmul(x, y) ) )
        elif argcheck.isscalar(right):
            # quaternion * scalar case
            print('scalar * quat')
            return Quaternion([right*q._A for q in left])
        elif isinstance(self, UnitQuaternion) and argcheck.isvector(right,3):
            # scalar * vector case
            return quat.qvmul(left._A, argcheck.getvector(right,3))
        else:
            raise ValueError('operands to * are of different types')
            
        return left._op2(right, lambda x, y: x @ y )
    def __init__(self, m=None, c=None, I=None):
        """
        Create a new spatial inertia

        :param m: mass
        :type m: float
        :param c: centre of mass relative to link frame
        :type c: 3-element array_like
        :param I: inertia about the centre of mass, axes aligned with link frame
        :type I: numpy.array, shape=(6,6)

        - ``SpatialInertia(M, C, I)`` is a spatial inertia object for a rigid-body
          with mass ``M``, centre of mass at ``C`` relative to the link frame, and an
          inertia matrix ``I`` (3x3) about the centre of mass.

        - ``SpatialInertia(I)`` is a spatial inertia object with a value equal
          to ``I`` (6x6).
        """
        if m is not None and c is not None:
            assert arg.isvector(c, 3), 'c must be 3-vector'
            if I is None:
                I = np.zeros((3,3))
            else:
                assert arg.ismatrix(I, (3,3)), 'I must be 3x3 matrix'
            C = tr.skew(c)
            self.I = np.array([
                                [m * np.eye(3), m @ C.T],
                                [m @ C,         I + m * C @ C.T]
                              ])
        elif m is None and c is None and I is not None:
            assert arg.ismatrix(I, (6, 6)), 'I must be 6x6 matrix'
示例#11
0
    def __init__(self, pl=None):
        """
        Plucker.Plucker Create Plucker line object

        P = Plucker(P1, P2) create a Plucker object that represents
        the line joining the 3D points P1 (3x1) and P2 (3x1). The direction
        is from P2 to P1.

        P = Plucker(X) creates a Plucker object from X (6x1) = [V,W] where
        V (3x1) is the moment and W (3x1) is the line direction.

        P = Plucker(L) creates a copy of the Plucker object L.

        Notes:
            
        - Planes are given by the 4-vector [a b c d] to represent ax+by+cz+d=0.\
        """

        if isinstance(pl, Plucker):
            self.v = pl.v
            self.w = pl.w
        elif arg.isvector(pl, 6):
            pl = arg.getvector(pl)
            self.v = pl[0:3]
            self.w = pl[3:6]
        else:
            raise ValueError('bad argument')
示例#12
0
    def __init__(self, x=None, y=None, theta=None, *, unit='rad'):
        super().__init__()  # activate the UserList semantics

        if x is None and y is None and theta is None:
            # SE2()
            # empty constructor
            self.data = [np.eye(3)]

        elif x is not None:
            if y is not None and theta is not None:
                # SE2(x, y, theta)
                self.data = [tr.trot2(theta, t=[x, y], unit=unit)]

            elif y is None and theta is None:
                if argcheck.isvector(x, 3):
                    # SE2( [x,y,theta])
                    self.data = [tr.trot2(x[2], t=x[:2], unit=unit)]
                elif isinstance(x, np.ndarray):
                    if x.shape == (3, 3):
                        # SE2( 3x3 matrix )
                        self.data = [x]
                    elif x.shape[1] == 3:
                        # SE2( Nx3 )
                        self.data = [tr.trot2(T.theta, t=T.t) for T in x]
                else:
                    super().arghandler(x)
        else:
            raise ValueError('bad arguments to constructor')
示例#13
0
    def __init__(self, arg=None, *, unit='rad', check=True):
        """
        Construct new SO(2) object

        :param unit: angular units 'deg' or 'rad' [default] if applicable
        :type unit: str, optional
        :param check: check for valid SO(2) elements if applicable, default to True
        :type check: bool
        :return: SO(2) rotation
        :rtype: SO2 instance

        - ``SO2()`` is an SO2 instance representing a null rotation -- the identity matrix.
        - ``SO2(θ)`` is an SO2 instance representing a rotation by ``θ`` radians.  If ``θ`` is array_like
          `[θ1, θ2, ... θN]` then an SO2 instance containing a sequence of N rotations.
        - ``SO2(θ, unit='deg')`` is an SO2 instance representing a rotation by ``θ`` degrees.  If ``θ`` is array_like
          `[θ1, θ2, ... θN]` then an SO2 instance containing a sequence of N rotations.
        - ``SO2(R)`` is an SO2 instance with rotation described by the SO(2) matrix R which is a 2x2 numpy array.  If ``check``
          is ``True`` check the matrix belongs to SO(2).
        - ``SO2([R1, R2, ... RN])`` is an SO2 instance containing a sequence of N rotations, each described by an SO(2) matrix
          Ri which is a 2x2 numpy array. If ``check`` is ``True`` then check each matrix belongs to SO(2).
        - ``SO2([X1, X2, ... XN])`` is an SO2 instance containing a sequence of N rotations, where each Xi is an SO2 instance.

        """
        if  super().arghandler(arg, check=check):
            return

        elif argcheck.isscalar(arg):
            self.data = [tr.rot2(arg, unit=unit)]

        elif argcheck.isvector(arg):
            self.data = [tr.rot2(x, unit=unit) for x in argcheck.getvector(arg)]

        else:
            raise ValueError('bad argument to constructor')
示例#14
0
 def omega(cls, w):
     assert argcheck.isvector(w, 3), 'w must be a 3-vector'
     w = argcheck.getvector(w)
     theta = tr.norm(w)
     s = math.cos(theta / 2)
     v = math.sin(theta / 2) * tr.unitvec(w)
     return cls(s=s, v=v)
示例#15
0
    def __init__(self, s: Any = None, v=None, check=True, norm=True):
        """
        A zero quaternion is one for which M{s^2+vx^2+vy^2+vz^2 = 1}.
        A quaternion can be considered as a rotation about a vector in space where
        q = cos (theta/2) sin(theta/2) <vx vy vz>
        where <vx vy vz> is a unit vector.
        :param s: scalar
        :param v: vector
        """
        super().__init__()

        if s is None and v is None:
            self.data = [np.array([0, 0, 0, 0])]

        elif argcheck.isscalar(s) and argcheck.isvector(v, 3):
            self.data = [np.r_[s, argcheck.getvector(v)]]

        elif argcheck.isvector(s, 4):
            self.data = [argcheck.getvector(s)]

        elif isinstance(s, list):
            if isinstance(s[0], np.ndarray):
                if check:
                    assert argcheck.isvectorlist(
                        s, 4), 'list must comprise 4-vectors'
                self.data = s
            elif isinstance(s[0], self.__class__):
                # possibly a list of objects of same type
                assert all(map(lambda x: isinstance(x, self.__class__),
                               s)), 'all elements of list must have same type'
                self.data = [x._A for x in s]
            else:
                raise ValueError('incorrect list')

        elif isinstance(s, np.ndarray) and s.shape[1] == 4:
            self.data = [x for x in s]

        elif isinstance(s, Quaternion):
            self.data = s.data

        else:
            raise ValueError('bad argument to Quaternion constructor')
示例#16
0
def ctraj(T0, T1, s):
    """
    Cartesian trajectory between two poses

    :param T0: initial pose
    :type T0: SE3
    :param T1: final pose
    :type T1: SE3
    :return T0: smooth path from ``T0`` to ``T1``
    :rtype: SE3

    ``ctraj(T0, T1, n)`` is a Cartesian trajectory from SE3 pose ``T0`` to
    ``T1`` with ``n`` points that follow a trapezoidal velocity profile along
    the path. The Cartesian trajectory is an SE3 instance containing ``n``
    values.

    ``ctraj(T0, T1, s)`` as above but the elements of ``s`` specify the
    fractional distance  along the path, and these values are in the
    range [0 1]. The i'th point corresponds to a distance ``s[i]`` along
    the path.

    Examples::

        >>> tg = ctraj(SE3.Rand(), SE3.Rand(), 20)
        >>> len(tg)
        20

    Notes:

    - In the second case ``s`` could be generated by a scalar trajectory
      generator such as ``tpoly`` or ``lspb`` (default).
    - Orientation interpolation is performed using unit-quaternion
      interpolation.

    Reference:

    - Robotics, Vision & Control, Sec 3.1.5,
      Peter Corke, Springer 2011

    :seealso: :func:`~roboticstoolbox.trajectory.lspb`,
        :func:`~spatialmath.unitquaternion.interp`
    """

    if isinstance(s, int):
        s = lspb(0, 1, s).y
    elif isvector(s):
        s = getvector(s)
    else:
        raise TypeError('bad argument for time, must be int or vector')

    return T0.interp(T1, s)
示例#17
0
 def __init__(self, arg = None, *, unit='rad'):
     super().__init__()  # activate the UserList semantics
     
     if arg is None:
         # empty constructor
         self.data = [np.eye(2)]
     
     elif argcheck.isvector(arg):
         # SO2(value)
         # SO2(list of values)
         self.data = [transforms.rot2(x, unit) for x in argcheck.getvector(arg)]
         
     else:
         super().arghandler(arg)
示例#18
0
 def isvalid(v, check=True):
     if argcheck.isvector(v, 3):
         return True
     elif argcheck.ismatrix(v, (3, 3)):
         # maybe be an se(2)
         if not all(v.diagonal() == 0):  # check diagonal is zero
             return False
         if not all(v[2, :] == 0):  # check bottom row is zero
             return False
         if not tr.isskew(v[:2, :2]):
             # top left 2x2is skew symmetric
             return False
         return True
     return False
示例#19
0
    def _getq(self, q=None):
        """
        Get joint coordinates (Robot superclass)

        :param q: passed value, defaults to None
        :type q: array_like, optional
        :return: passed or value from robot state
        :rtype: ndarray(n,)
        """
        if q is None:
            return self.q
        elif isvector(q, self.n):
            return getvector(q, self.n)
        else:
            return getmatrix(q, (None, self.n))
示例#20
0
 def __init__(self, x = None, y = None, theta = None, *, unit='rad'):
     super().__init__()  # activate the UserList semantics
     
     if x is None:
         # empty constructor
         self.data = [np.eye(3)]
     
     elif all(map(lambda x: isinstance(x, (int,float)), [x, y, theta])):
         # SE2(x, y, theta)
         self.data = [transforms.trot2(theta, t=[x,y], unit=unit)]
         
     elif argcheck.isvector(x) and argcheck.isvector(y) and argcheck.isvector(theta):
         # SE2(xvec, yvec, tvec)
         xvec = argcheck.getvector(x)
         yvec = argcheck.getvector(y, dim=len(xvec))
         tvec = argcheck.getvector(theta, dim=len(xvec))
         self.data = [transforms.trot2(_t, t=[_x, _y]) for (_x, _y, _t) in zip(xvec, yvec, argcheck.getunit(tvec, unit))]
         
     elif isinstance(x, np.ndarray) and y is None and theta is None:
         assert x.shape[1] == 3, 'array argument must be Nx3'
         self.data = [transforms.trot2(_t, t=[_x, _y], unit=unit) for (_x, _y, _t) in x]
         
     else:
         super().arghandler(x)
示例#21
0
    def contains(self, x, tol=50 * _eps):
        """
        Plucker.contains  Test if point is on the line

        ``line.contains(X)`` is true if the point X (3x1) lies on the line defined by
        the Plucker object self.
        """
        if arg.isvector(x, 3):
            return np.linalg.norm(np.cross(x - self.pp, self.w)) < tol
        elif arg.ismatrix(x, (3, None)):
            return [
                np.linalg.norm(np.cross(_ - self.pp, self.w)) < tol
                for _ in x.T
            ]
        else:
            raise ValueError('bad argument')
示例#22
0
    def __init__(self, arg=None, *, unit='rad'):
        super().__init__()  # activate the UserList semantics

        if arg is None:
            # empty constructor
            if type(self) is SO2:
                self.data = [np.eye(2)]

        elif argcheck.isvector(arg):
            # SO2(value)
            # SO2(list of values)
            self.data = [tr.rot2(x, unit) for x in argcheck.getvector(arg)]

        elif isinstance(arg, np.ndarray) and arg.shape == (2, 2):
            self.data = [arg]
        else:
            super().arghandler(arg)
示例#23
0
    def __init__(self, x=None, y=None, z=None, *, check=True):
        """
        Construct new SE(3) object

        :param x: translation distance along the X-axis
        :type x: float
        :param y: translation distance along the Y-axis
        :type y: float
        :param z: translation distance along the Z-axis
        :type z: float
        :return: 4x4 homogeneous transformation matrix
        :rtype: SE3 instance
        
        - ``SE3()`` is a null motion -- the identity matrix
        - ``SE3(x, y, z)`` is a pure translation of (x,y,z)
        - ``SE3(T)`` where T is a 4x4 numpy array representing an SE(3) matrix.  If ``check``
          is ``True`` check the matrix belongs to SE(3).
        - ``SE3([T1, T2, ... TN])`` where each Ti is a 4x4 numpy array representing an SE(3) matrix, is
          an SE3 instance containing N rotations. If ``check`` is ``True``
          check the matrix belongs to SE(3).
        - ``SE3([X1, X2, ... XN])`` where each Xi is an SE3 instance, is an SE3 instance containing N rotations.
        """
        super().__init__()  # activate the UserList semantics

        if x is None:
            # SE3()
            # empty constructor
            self.data = [np.eye(4)]
        elif y is not None and z is not None:
            # SE3(x, y, z)
            self.data = [tr.transl(x, y, z)]
        elif y is None and z is None:
            if argcheck.isvector(x, 3):
                # SE3( [x, y, z] )
                self.data = [tr.transl(x)]
            elif isinstance(x, np.ndarray) and x.shape[1] == 3:
                # SE3( Nx3 )
                self.data = [tr.transl(T) for T in x]
            else:
                super()._arghandler(x, check=check)
        else:
            raise ValueError('bad argument to constructor')
示例#24
0
    def __init__(self, x=None, y=None, z=None, *, check=True):
        """
        Construct new SE(3) object

        :rtype: SE3 instance

        There are multiple call signatures:

        - ``SE3()`` is an ``SE3`` instance with one value  -- a 4x4 identity
          matrix which corresponds to a null motion.
        - ``SE3(x, y, z)`` is a pure translation of (x,y,z)
        - ``SE3(T)`` is an ``SE3`` instance with the value ``T`` which is a 4x4
          numpy array representing an SE(3) matrix.  If ``check`` is ``True``
          check the matrix belongs to SE(3).
        - ``SE3([T1, T2, ... TN])`` is an ``SE3`` instance with ``N`` values
          given by the elements ``Ti`` each of which is a 4x4 NumPy array
          representing an SE(3) matrix. If ``check`` is ``True`` check the
          matrix belongs to SE(3).
        - ``SE3([X1, X2, ... XN])`` is an ``SE3`` instance with ``N`` values
          given by the elements ``Xi`` each of which is an SE3 instance.
        
        :SymPy: supported
        """
        if y is None and z is None:
            # just one argument passed

            if super().arghandler(x, check=check):
                return
            elif argcheck.isvector(x, 3):
                # SE3( [x, y, z] )
                self.data = [tr.transl(x)]
            elif isinstance(x, np.ndarray) and x.shape[1] == 3:
                # SE3( Nx3 )
                self.data = [tr.transl(T) for T in x]

            else:
                raise ValueError('bad argument to constructor')

        elif y is not None and z is not None:
            # SE3(x, y, z)
            self.data = [tr.transl(x, y, z)]
def transl(x, y=None, z=None):
    """
    Create SE(3) pure translation, or extract translation from SE(3) matrix

    :param x: translation along X-axis
    :type x: float
    :param y: translation along Y-axis
    :type y: float
    :param z: translation along Z-axis
    :type z: float
    :return: 4x4 homogeneous transformation matrix
    :rtype: numpy.ndarray, shape=(4,4)

    Create a translational SE(3) matrix:

    - ``T = transl( X, Y, Z )`` is an SE(3) homogeneous transform (4x4) representing a
      pure translation of X, Y and Z.
    - ``T = transl( V )`` as above but the translation is given by a 3-element
      list, dict, or a numpy array, row or column vector.


    Extract the translational part of an SE(3) matrix:

    - ``P = TRANSL(T)`` is the translational part of a homogeneous transform T as a
      3-element numpy array.
    
    :seealso: :func:`~spatialmath.base.transforms2d.transl2`
   """

    if np.isscalar(x):
        T = np.identity(4)
        T[:3, 3] = [x, y, z]
        return T
    elif argcheck.isvector(x, 3):
        T = np.identity(4)
        T[:3, 3] = argcheck.getvector(x, 3, out='array')
        return T
    elif argcheck.ismatrix(x, (4, 4)):
        return x[:3, 3]
    else:
        ValueError('bad argument')
示例#26
0
    def __init__(self, arg=None, *, unit='rad', check=True):
        """
        Construct new SO(2) object

        :param unit: angular units 'deg' or 'rad' [default] if applicable
        :type unit: str, optional
        :param check: check for valid SO(2) elements if applicable, default to True
        :type check: bool
        :return: SO(2) rotation
        :rtype: SO2 instance

        - ``SO2()`` is an SO2 instance representing a null rotation -- the identity matrix.
        - ``SO2(theta)`` is an SO2 instance representing a rotation by ``theta`` radians.  If ``theta`` is array_like
          `[theta1, theta2, ... thetaN]` then an SO2 instance containing a sequence of N rotations.
        - ``SO2(theta, unit='deg')`` is an SO2 instance representing a rotation by ``theta`` degrees.  If ``theta`` is array_like
          `[theta1, theta2, ... thetaN]` then an SO2 instance containing a sequence of N rotations.
        - ``SO2(R)`` is an SO2 instance with rotation described by the SO(2) matrix R which is a 2x2 numpy array.  If ``check``
          is ``True`` check the matrix belongs to SO(2).
        - ``SO2([R1, R2, ... RN])`` is an SO2 instance containing a sequence of N rotations, each described by an SO(2) matrix
          Ri which is a 2x2 numpy array. If ``check`` is ``True`` then check each matrix belongs to SO(2).
        - ``SO2([X1, X2, ... XN])`` is an SO2 instance containing a sequence of N rotations, where each Xi is an SO2 instance.
        
        """
        super().__init__()  # activate the UserList semantics

        if arg is None:
            # empty constructor
            if type(self) is SO2:
                self.data = [np.eye(2)]
        elif argcheck.isvector(arg):
            # SO2(value)
            # SO2(list of values)
            self.data = [tr.rot2(x, unit) for x in argcheck.getvector(arg)]

        elif isinstance(arg, np.ndarray) and arg.shape == (2, 2):
            self.data = [arg]
        else:
            super()._arghandler(arg, check=check)
示例#27
0
    def RPY(cls, angles, *, order='zyx', unit='rad'):
        r"""
        Create an SE(3) pure rotation from roll-pitch-yaw angles

        :param 𝚪: roll-pitch-yaw angles
        :type 𝚪: array_like or numpy.ndarray with shape=(N,3)
        :param unit: angular units: 'rad' [default], or 'deg'
        :type unit: str
        :param order: rotation order: 'zyx' [default], 'xyz', or 'yxz'
        :type order: str
        :return: SE(3) matrix
        :rtype: SE3 instance

        ``SE3.RPY(𝚪)`` is an SE(3) rotation defined by a 3-vector of roll,
        pitch, yaw angles :math:`\Gamma=(r, p, y)` which correspond to
        successive rotations about the axes specified by ``order``:

            - ``'zyx'`` [default], rotate by yaw about the z-axis, then by pitch about the new y-axis,
              then by roll about the new x-axis.  This is the **convention** for a mobile robot with x-axis forward
              and y-axis sideways.
            - ``'xyz'``, rotate by yaw about the x-axis, then by pitch about the new y-axis,
              then by roll about the new z-axis. This is the **convention** for a robot gripper with z-axis forward
              and y-axis between the gripper fingers.
            - ``'yxz'``, rotate by yaw about the y-axis, then by pitch about the new x-axis,
              then by roll about the new z-axis. This is the **convention** for a camera with z-axis parallel
              to the optical axis and x-axis parallel to the pixel rows.

        If ``𝚪`` is an Nx3 matrix then the result is a sequence of rotations each defined by RPY angles
        corresponding to the rows of ``𝚪``.

        :seealso: :func:`~spatialmath.pose3d.SE3.rpy`, :func:`~spatialmath.base.transforms3d.rpy2r`
        :SymPy: supported
        """
        if argcheck.isvector(angles, 3):
            return cls(tr.rpy2tr(angles, order=order, unit=unit), check=False)
        else:
            return cls([tr.rpy2tr(a, order=order, unit=unit) for a in angles],
                       check=False)
示例#28
0
    def isvalid(v, check=True):
        """
        Test if matrix is valid twist

        :param x: array to test
        :type x: numpy.ndarray
        :return: true of the matrix is a 6-vector or a 4x4 se(3) element
        :rtype: bool

        """
        if argcheck.isvector(v, 6):
            return True
        elif argcheck.ismatrix(v, (4, 4)):
            # maybe be an se(3)
            if not all(v.diagonal() == 0):  # check diagonal is zero
                return False
            if not all(v[3, :] == 0):  # check bottom row is zero
                return False
            if not tr.isskew(v[:3, :3]):
                # top left 3x3 is skew symmetric
                return False
            return True
        return False
def e2h(v):
    """
    Convert from Euclidean to homogeneous form
    
    :param v: Euclidean vector or matrix
    :type v: array_like
    :return: homogeneous vector
    :rtype: numpy.ndarray

    - If ``v`` is an array, shape=(N,), return an array shape=(N+1,) where a value of 1 has
      been appended
    - If ``v`` is a matrix, shape=(N,M), return a matrix shape=(N+1,N), where each column has
      been appended with a value of 1, ie. a row of ones has been appended to the matrix.
      
    :seealso: e2h
    """
    if argcheck.isvector(v):
        # dealing with shape (N,) array
        v = argcheck.getvector(v)
        return np.r_[v, 1]
    elif isinstance(v, np.ndarray) and len(v.shape) == 2:
        # dealing with matrix
        return np.vstack([v, np.ones((1, v.shape[1]))])
def h2e(v):
    """
    Convert from homogeneous to Euclidean form
    
    :param v: homogeneous vector or matrix
    :type v: array_like
    :return: Euclidean vector
    :rtype: numpy.ndarray

    - If ``v`` is an array, shape=(N,), return an array shape=(N-1,) where the elements have
      all been scaled by the last element of ``v``.
    - If ``v`` is a matrix, shape=(N,M), return a matrix shape=(N-1,N), where each column has
      been scaled by its last element.
      
    :seealso: e2h
    """
    if argcheck.isvector(v):
        # dealing with shape (N,) array
        v = argcheck.getvector(v)
        return v[0:-1] / v[-1]
    elif isinstance(v, np.ndarray) and len(v.shape) == 2:
        # dealing with matrix
        return v[:-1, :] / matlib.repmat(v[-1, :], v.shape[0] - 1, 1)