From d4f56bf21f5b9336639c2aeeb31bfcb213a03b64 Mon Sep 17 00:00:00 2001 From: Peter Corke Date: Mon, 27 Jul 2026 12:13:39 +1000 Subject: [PATCH] docs: fix Sphinx docstring formatting and reST syntax errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix LaTeX escaping in docstrings (add missing braces, convert to raw strings) - Correct reST indentation for documentation lists and notes - Fix code example imports and function references - Update cross-references using proper Sphinx format (:meth:, ~seealso:) - Fix parameter name references in docstrings (S→s, math.pi→np.pi) - Add missing type annotations in parameter documentation - Remove obsolete documentation files (classes-2d.rst, classes-3d.rst) - Improve reference formatting for external links Fixes 19+ files with sphinx build warnings and documentation clarity issues. All changes are non-functional; improve rendering and consistency only. --- docs/source/classes-2d.rst | 16 ----------- docs/source/classes-3d.rst | 10 ------- docs/source/intro.rst | 7 +++-- spatialmath/DualQuaternion.py | 2 +- spatialmath/base/argcheck.py | 2 +- spatialmath/base/graphics.py | 39 +++++++++++++------------- spatialmath/base/quaternions.py | 3 +- spatialmath/base/symbolic.py | 4 +-- spatialmath/base/transforms2d.py | 24 ++++++++-------- spatialmath/base/transforms3d.py | 4 ++- spatialmath/base/vectors.py | 4 +-- spatialmath/baseposematrix.py | 34 ++++++++++++++-------- spatialmath/geom2d.py | 4 +-- spatialmath/geom3d.py | 21 +++++++------- spatialmath/pose2d.py | 3 ++ spatialmath/pose3d.py | 45 ++++++++++++++++-------------- spatialmath/quaternion.py | 23 ++++++++------- spatialmath/twist.py | 48 +++++++++++++++++--------------- 18 files changed, 149 insertions(+), 144 deletions(-) delete mode 100644 docs/source/classes-2d.rst delete mode 100644 docs/source/classes-3d.rst diff --git a/docs/source/classes-2d.rst b/docs/source/classes-2d.rst deleted file mode 100644 index c51c3d9f..00000000 --- a/docs/source/classes-2d.rst +++ /dev/null @@ -1,16 +0,0 @@ - - - - -Geometry --------- - -.. automodule:: spatialmath.geom2d - :members: - :undoc-members: - :show-inheritance: - :inherited-members: - :special-members: __mul__, __rmul__, __eq__, __ne__, __init__, __or__, __xor__ - - - diff --git a/docs/source/classes-3d.rst b/docs/source/classes-3d.rst deleted file mode 100644 index 10b7a65a..00000000 --- a/docs/source/classes-3d.rst +++ /dev/null @@ -1,10 +0,0 @@ - -Geometry --------- - -.. automodule:: spatialmath.geom3d - :members: - :undoc-members: - :show-inheritance: - :inherited-members: - :special-members: __mul__, __rmul__, __eq__, __ne__, __init__, __or__, __xor__ \ No newline at end of file diff --git a/docs/source/intro.rst b/docs/source/intro.rst index 8f3d1297..4c0100d8 100644 --- a/docs/source/intro.rst +++ b/docs/source/intro.rst @@ -262,6 +262,7 @@ The classes ``SE3``, ``SO3``, ``SE2`` and ``SO2`` can provide colorized text out .. image:: ../figs/colored_output.png :width: 300 + with rotational elements in red, translational elements in blue and constants in grey. The foreground and background colors can be controlled using the following @@ -538,8 +539,10 @@ A numpy column vector has shape ``(N,1)`` and a row vector has shape ``(1,N)`` but functions also accept row ``(1,N)`` and column ``(N,1)`` where a vector argument is required. -.. warning:: For a user transitioning from MATLAB the most significant - differences are: +.. warning:: + + For a user transitioning from MATLAB the most significant differences are: + - the use of 1D arrays -- all MATLAB arrays have two dimensions, even if one of them is equal to one. - Iterating over a 1D NumPy array (N,) returns consecutive elements diff --git a/spatialmath/DualQuaternion.py b/spatialmath/DualQuaternion.py index f8ee0f7d..5e606dcd 100644 --- a/spatialmath/DualQuaternion.py +++ b/spatialmath/DualQuaternion.py @@ -338,7 +338,7 @@ def SE3(self) -> SE3: .. runblock:: pycon - >>> from spatialmath import DualQuaternion, SE3 + >>> from spatialmath import UnitDualQuaternion, SE3 >>> T = SE3.Rand() >>> print(T) >>> d = UnitDualQuaternion(T) diff --git a/spatialmath/base/argcheck.py b/spatialmath/base/argcheck.py index 9db91817..0db73497 100644 --- a/spatialmath/base/argcheck.py +++ b/spatialmath/base/argcheck.py @@ -62,7 +62,7 @@ def isinteger(x: Any) -> bool: .. runblock:: pycon - >>> from spatialmath.base import isscalar + >>> from spatialmath.base import isinteger >>> isinteger(1) >>> isinteger(1.2) diff --git a/spatialmath/base/graphics.py b/spatialmath/base/graphics.py index c51d7f94..31207267 100644 --- a/spatialmath/base/graphics.py +++ b/spatialmath/base/graphics.py @@ -115,42 +115,41 @@ def plot_point( Plot one or more points, with optional text label. - The color of the marker can be different to the color of the text, the - marker color is specified by a single letter in the marker string. + marker color is specified by a single letter in the marker string. - A point can have multiple markers, given as a list, which will be - overlaid, for instance ``["rx", "ro"]`` will give a ⨂ symbol. + overlaid, for instance ``["rx", "ro"]`` will give a ⨂ symbol. - The optional text label is placed to the right of the marker, and - vertically aligned. + vertically aligned. - Multiple points can be marked if ``pos`` is a 2xn array or a list of - coordinate pairs. In this case: + coordinate pairs. In this case: - all points have the same ``text`` label - ``text`` can include the format string {} which is susbstituted for the - point index, starting at zero + point index, starting at zero - ``text`` can be a tuple containing a format string followed by vectors - of shape(n). For example:: + of shape(n). For example:: - ``("#{0} a={1:.1f}, b={2:.1f}", a, b)`` + ("#{0} a={1:.1f}, b={2:.1f}", a, b) will label each point with its index (argument 0) and consecutive elements of ``a`` and ``b`` which are arguments 1 and 2 respectively. Example:: - >>> from spatialmath.base import plotvol2, plot_text + >>> from spatialmath.base import plotvol2, plot_point >>> plotvol2(5) - >>> plot_point((0, 0)) # plot default marker at coordinate (1,2) + >>> plot_point((0,0)) # plot default marker at coordinate (1,2) >>> plot_point((1,1), 'r*') # plot red star at coordinate (1,2) - >>> plot_point((2,2), 'r*', 'foo') # plot red star at coordinate (1,2) and - label it as 'foo' + >>> plot_point((2,2), 'r*', 'foo') # plot red star at coordinate (1,2) and label it as 'foo' .. plot:: - from spatialmath.base import plotvol2, plot_text + from spatialmath.base import plotvol2, plot_point ax = plotvol2(5) - plot_point((0, 0)) + plot_point((0,0)) plot_point((1,1), 'r*') plot_point((2,2), 'r*', 'foo') ax.grid() @@ -846,13 +845,15 @@ def plot_ellipse( The ellipse is defined by :math:`x^T \mat{E} x = s^2` where :math:`x \in \mathbb{R}^2` and :math:`s` is the scale factor. - .. note:: For some common cases we require :math:`\mat{E}^{-1}`, for example + .. note:: + For some common cases we require :math:`\mat{E}^{-1}`, for example: + - for robot manipulability - :math:`\nu (\mat{J} \mat{J}^T)^{-1} \nu` i + :math:`\nu (\mat{J} \mat{J}^T)^{-1} \nu` i - a covariance matrix - :math:`(x - \mu)^T \mat{P}^{-1} (x - \mu)` - so to avoid inverting ``E`` twice to compute the ellipse, we flag that - the inverse is provided using ``inverted``. + :math:`(x - \mu)^T \mat{P}^{-1} (x - \mu)` + so to avoid inverting ``E`` twice to compute the ellipse, we flag that + the inverse is provided using ``inverted``. Returns a set of ``resolution`` that lie on the circumference of a circle of given ``center`` and ``radius``. @@ -1101,7 +1102,7 @@ def plot_ellipsoid( .. note:: - If a confidence interval is given then ``E`` is interpretted as a covariance - matrix and the ellipse size is computed using an inverse chi-squared function. + matrix and the ellipse size is computed using an inverse chi-squared function. :seealso: :func:`~matplotlib.pyplot.plot_surface`, :func:`~matplotlib.pyplot.plot_wireframe` """ diff --git a/spatialmath/base/quaternions.py b/spatialmath/base/quaternions.py index 364a5ea8..24a9cadd 100755 --- a/spatialmath/base/quaternions.py +++ b/spatialmath/base/quaternions.py @@ -1119,7 +1119,8 @@ def q2str( fmt: Optional[str] = "{: .4f}", ) -> str: """ - Format a quaternion as a string + Convert quaternion to compact single-line string + :arg q: unit-quaternion :type q: array_like(4) diff --git a/spatialmath/base/symbolic.py b/spatialmath/base/symbolic.py index a95aec4a..aebb827a 100644 --- a/spatialmath/base/symbolic.py +++ b/spatialmath/base/symbolic.py @@ -61,7 +61,7 @@ def symbol( - symbols named after greek letters will appear as greek letters - underscore means subscript as it does in LaTex, so the symbols ``q`` - above will be subscripted. + above will be subscripted. :seealso: :func:`sympy.symbols` """ @@ -347,7 +347,7 @@ def det(x): >>> R = rot2(theta) >>> print(R) >>> print(det(R)) - >>> simplify(print(det(R))) + >>> print(simplify(det(R))) .. note:: Converts to a SymPy ``Matrix`` and then back again. """ diff --git a/spatialmath/base/transforms2d.py b/spatialmath/base/transforms2d.py index ac0696cd..bd336725 100644 --- a/spatialmath/base/transforms2d.py +++ b/spatialmath/base/transforms2d.py @@ -715,13 +715,13 @@ def trnorm2(T: SE2Array) -> SE2Array: The steps in normalization are: #. If :math:`\mathbf{R} = [a, b]` - #. Form unit vectors :math:`\hat{b} + #. Form unit vectors :math:`\hat{b}` #. Form the orthogonal planar vector :math:`\hat{a} = [\hat{b}_y -\hat{b}_x]` #. Form the normalized SO(2) matrix :math:`\mathbf{R} = [\hat{a}, \hat{b}]` .. runblock:: pycon - >>> from spatialmath.base import trnorm, troty + >>> from spatialmath.base import trnorm2, trot2 >>> from numpy import linalg >>> T = trot2(45, 'deg', t=[3, 4]) >>> linalg.det(T[:2,:2]) - 1 # is a valid SO(3) @@ -796,8 +796,8 @@ def tradjoint2(T): >>> tr2adjoint2(T) :Reference: - - Robotics, Vision & Control for Python, Section 3.1, P. Corke, Springer 2023. - - `Lie groups for 2D and 3D Transformations _ + - *Robotics, Vision & Control for Python*, Section 3.1, P. Corke, Springer 2023. + - `*Lie groups for 2D and 3D Transformations* `_ :SymPy: supported """ @@ -882,14 +882,14 @@ def trinterp2(start, end, s, shortest: bool = True): :rtype: ndarray(3,3) or ndarray(2,2) :raises ValueError: bad arguments - - ``trinterp2(None, T, S)`` is an SE(2) matrix interpolated - between identity when `S`=0 and `T` when `S`=1. - - ``trinterp2(T0, T1, S)`` as above but interpolated - between `T0` when `S`=0 and `T1` when `S`=1. - - ``trinterp2(None, R, S)`` is an SO(2) matrix interpolated - between identity when `S`=0 and `R` when `S`=1. - - ``trinterp2(R0, R1, S)`` as above but interpolated - between `R0` when `S`=0 and `R1` when `S`=1. + - ``trinterp2(None, T, s)`` is an sE(2) matrix interpolated + between identity when `s=0` and `T` when `s=1`. + - ``trinterp2(T0, T1, s)`` as above but interpolated + between `T0` when `s=0` and `T1` when `s=1`. + - ``trinterp2(None, R, s)`` is an sO(2) matrix interpolated + between identity when `s=0` and `R` when `s=1`. + - ``trinterp2(R0, R1, s)`` as above but interpolated + between `R0` when `s=0` and `R1` when `s=1`. .. note:: Rotation angle is linearly interpolated. diff --git a/spatialmath/base/transforms3d.py b/spatialmath/base/transforms3d.py index 04f9e1b8..1bc90600 100644 --- a/spatialmath/base/transforms3d.py +++ b/spatialmath/base/transforms3d.py @@ -326,6 +326,7 @@ def transl(x, y=None, z=None): .. note:: This function is compatible with the MATLAB version of the Toolbox. It is unusual/weird in doing two completely different things inside the one function. + :seealso: :func:`~spatialmath.base.transforms2d.transl2` :SymPy: supported """ @@ -3390,7 +3391,8 @@ def tranimate(T: Union[SO3Array, SE3Array], **kwargs) -> str: :type wait: bool :param movie: name of file to write MP4 movie into :type movie: str - :param **kwargs: arguments passed to ``trplot`` + :param kwargs: arguments passed to ``trplot`` + :type kwargs: dict - ``tranimate(T)`` where ``T`` is an SO(3) or SE(3) matrix, animates a 3D coordinate frame moving from the world frame to the frame ``T`` in diff --git a/spatialmath/base/vectors.py b/spatialmath/base/vectors.py index bf95283f..b0055e9a 100644 --- a/spatialmath/base/vectors.py +++ b/spatialmath/base/vectors.py @@ -458,7 +458,7 @@ def unittwist2(S: ArrayLike3, tol: float = 20) -> Union[R3, None]: .. runblock:: pycon >>> from spatialmath.base import * - >>> unittwist2([2, 4, 2) + >>> unittwist2([2, 4, 2]) >>> unittwist2([2, 0, 0]) .. note:: Returns None if the twist has zero magnitude @@ -501,7 +501,7 @@ def unittwist2_norm( .. runblock:: pycon >>> from spatialmath.base import * - >>> unittwist2([2, 4, 2) + >>> unittwist2([2, 4, 2]) >>> unittwist2([2, 0, 0]) .. note:: Returns (None, None) if the twist has zero magnitude diff --git a/spatialmath/baseposematrix.py b/spatialmath/baseposematrix.py index 87071df3..c798c817 100644 --- a/spatialmath/baseposematrix.py +++ b/spatialmath/baseposematrix.py @@ -337,11 +337,14 @@ def log(self, twist: Optional[bool] = False) -> Union[NDArray, List[NDArray]]: """ Logarithm of pose (superclass method) + :param twist: return twist coordinates vector rather than matrix, defaults to False + :type twist: bool, optional :return: logarithm :rtype: ndarray :raises: ValueError - An efficient closed-form solution of the matrix logarithm. + An efficient closed-form solution of the matrix logarithm. By default the + result is a structured matrix ===== ====== =============================== Input Output @@ -352,19 +355,22 @@ def log(self, twist: Optional[bool] = False) -> Union[NDArray, List[NDArray]]: SO3 (3,3) skew-symmetric SE3 (4,4) augmented skew-symmetric ===== ====== =============================== + but if ``twist=True`` the result is a vector, the twist coordinates, containing + the unique elements with translational elements first. The rotational + elements are also known as the exponential coordinates of the rotation. + Example:: - >>> x = SE3.Rx(0.3) - >>> y = x.log() - >>> y - array([[ 0. , -0. , 0. , 0. ], - [ 0. , 0. , -0.3, 0. ], - [-0. , 0.3, 0. , 0. ], - [ 0. , 0. , 0. , 0. ]]) + .. runblock:: pycon + >>> from spatialmath import SO3, SE3 + >>> T = SE3.Rx(0.3) + >>> T.log() + >>> T.log(twist=True) + >>> R = SO3.Rx(0.3) + >>> R.log(twist=True) # exponential coordinates - :seealso: :func:`~spatialmath.base.transforms2d.trlog2`, - :func:`~spatialmath.base.transforms3d.trlog` + :seealso: :func:`~spatialmath.base.transforms2d.trlog2` :func:`~spatialmath.base.transforms3d.trlog` :SymPy: not supported """ @@ -395,6 +401,9 @@ def interp( :return: interpolated pose :rtype: same as ``self`` + This interpolation is given by a scalar or the number of equally spaced + steps: + - ``X.interp(Y, s)`` interpolates pose between X between when s=0 and Y when s=1. - ``X.interp(Y, N)`` interpolates pose between X and Y in ``N`` steps. @@ -417,6 +426,7 @@ def interp( - For SO3 and SE3 rotation is interpolated using quaternion spherical linear interpolation (slerp). - Values of ``s`` outside the range [0,1] are silently clipped + :seealso: :func:`interp1`, :func:`~spatialmath.base.transforms3d.trinterp`, :func:`~spatialmath.base.quaternions.qslerp`, :func:`~spatialmath.base.transforms2d.trinterp2` :SymPy: not supported @@ -707,8 +717,8 @@ def printline(self, *args, **kwargs) -> None: >>> x.printline() >>> x = SE3.Rx([0.2, 0.3]) >>> x.printline() - >>> x.printline('angvec') - >>> x.printline(orient='angvec', fmt="{:.6f}") + >>> x.printline(orient="angvec") + >>> x.printline(orient="angvec", fmt="{:.6f}") >>> x = SE2(1, 2, 0.3) >>> x.printline() diff --git a/spatialmath/geom2d.py b/spatialmath/geom2d.py index 55eccb2a..f04e9539 100755 --- a/spatialmath/geom2d.py +++ b/spatialmath/geom2d.py @@ -653,9 +653,9 @@ def vertices(self, unique: bool = True) -> Points2: .. runblock:: pycon >>> from spatialmath import Polygon2 - >>> p = Polygon2([(1, 2), (3, 2), (2, 4)]) + >>> p = Polygon2([(1, 2), (3, 2), (2, 4), (1,2)]) >>> p.vertices() - >>> p.vertices(closed=True) + >>> p.vertices(unique=True) """ vertices = self.path.vertices.T if unique: diff --git a/spatialmath/geom3d.py b/spatialmath/geom3d.py index 896192dc..8340811c 100755 --- a/spatialmath/geom3d.py +++ b/spatialmath/geom3d.py @@ -512,8 +512,8 @@ def skew(self) -> R4x4: :rtype: ndarray(4,4) ``line.skew()`` is the Plucker matrix, a 4x4 skew-symmetric matrix - representation of the line whose six unique elements are the - Plucker coordinates of the line. + representation of the line whose six unique elements are the Plucker coordinates + of the line. .. math:: @@ -524,11 +524,11 @@ def skew(self) -> R4x4: .. note:: - - For two homogeneous points P and Q on the line, :math:`PQ^T-QP^T` is - also skew symmetric. - - The projection of Plucker line by a perspective camera is a - homogeneous line (3x1) given by :math:`\vee C M C^T` where :math:`C - \in \mathbf{R}^{3 \times 4}` is the camera matrix. + - For two homogeneous points P and Q on the line, :math:`PQ^T-QP^T` is also + skew symmetric. + - The projection of Plucker line by a perspective camera is a homogeneous + line (3x1) given by :math:`\vee C M C^T` where :math:`C \in \mathbf{R}^{3 + \times 4}` is the camera matrix. """ v = self.v @@ -838,7 +838,7 @@ def distance( :return: Closest distance between lines :rtype: float - ``l1.distance(l2) is the minimum distance between two lines. + ``l1.distance(l2)`` is the minimum distance between two lines. .. note:: Works for parallel, skew and intersecting lines. @@ -1215,7 +1215,8 @@ def plot( :param bounds: Bounds of an axis-aligned rectangular cuboid as [xmin xmax ymin ymax zmin zmax], optional :type plane: 6-element array_like - :param **kwargs: Extra arguents passed to `Line2D `_ + :param kwargs: Extra arguments passed to `Line2D `_ + :type kwargs: dict :return: Plotted line :rtype: Matplotlib artists @@ -1227,7 +1228,7 @@ def plot( The line color or style is specified by: - - a MATLAB-style linestyle like 'k--' + - a MATLAB-style linestyle like ``"k--"`` - additional arguments passed to `Line2D `_ :seealso: :meth:`intersect_volume` diff --git a/spatialmath/pose2d.py b/spatialmath/pose2d.py index 57f1b6b7..62ac8510 100644 --- a/spatialmath/pose2d.py +++ b/spatialmath/pose2d.py @@ -432,6 +432,7 @@ def Rot(cls, theta, unit="rad"): .. runblock:: pycon + >>> from spatialmath import SE2 >>> SE2.Rot(0.3) >>> SE2.Rot([0.2, 0.3]) @@ -460,6 +461,7 @@ def Tx(cls, x): .. runblock:: pycon + >>> from spatialmath import SE2 >>> SE2.Tx(2) >>> SE2.Tx([2,3]) @@ -485,6 +487,7 @@ def Ty(cls, y): .. runblock:: pycon + >>> from spatialmath import SE2 >>> SE2.Ty(2) >>> SE2.Ty([2,3]) diff --git a/spatialmath/pose3d.py b/spatialmath/pose3d.py index b8d8d5de..4f4bb6e2 100644 --- a/spatialmath/pose3d.py +++ b/spatialmath/pose3d.py @@ -386,7 +386,7 @@ def Rx(cls, theta: float, unit: str = "rad") -> Self: >>> from spatialmath import SO3 >>> import numpy as np - >>> x = SO3.Rx(np.linspace(0, math.pi, 20)) + >>> x = SO3.Rx(np.linspace(0, np.pi, 20)) >>> len(x) >>> x[7] @@ -417,7 +417,7 @@ def Ry(cls, theta, unit: str = "rad") -> Self: >>> from spatialmath import SO3 >>> import numpy as np - >>> x = SO3.Ry(np.linspace(0, math.pi, 20)) + >>> x = SO3.Ry(np.linspace(0, np.pi, 20)) >>> len(x) >>> x[7] @@ -448,7 +448,7 @@ def Rz(cls, theta, unit: str = "rad") -> Self: >>> from spatialmath import SO3 >>> import numpy as np - >>> x = SO3.Rz(np.linspace(0, math.pi, 20)) + >>> x = SO3.Rz(np.linspace(0, np.pi, 20)) >>> len(x) >>> x[7] @@ -580,9 +580,9 @@ def RPY(cls, *angles, unit="rad", order="zyx"): 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. + - ``'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. Convention for a robot gripper with z-axis forward and y-axis between the gripper fingers. @@ -621,7 +621,7 @@ def RPY(cls, *angles, unit="rad", order="zyx"): @classmethod def OA(cls, o: ArrayLike3, a: ArrayLike3) -> Self: - """ + r""" Construct a new SO(3) from two vectors :param o: 3-vector parallel to Y- axis @@ -631,19 +631,19 @@ def OA(cls, o: ArrayLike3, a: ArrayLike3) -> Self: :return: SO(3) rotation :rtype: SO3 instance - ``SO3.OA(O, A)`` is an SO(3) rotation defined in terms of - vectors parallel to the Y- and Z-axes of its reference frame. In robotics these axes are + ``SO3.OA(O, A)`` is an SO(3) rotation defined in terms of vectors parallel to + the Y- and Z-axes of its reference frame. In robotics these axes are respectively called the *orientation* and *approach* vectors defined such that - R = [N, O, A] and N = O x A. + :math:`\mat{R}=[\vec{n}, \vec{o}, \vec{a}]` and :math:`\vec{n} = \vec{o} \times \vec{a}`. .. note:: - Only the ``A`` vector is guaranteed to have the same direction in the resulting - rotation matrix + rotation matrix - ``O`` and ``A`` do not have to be unit-length, they are normalized - - ``O`` and ``A` do not have to be orthogonal, so long as they are not parallel + - ``O`` and ``A`` do not have to be orthogonal, so long as they are not parallel - :seealso: :func:`spatialmath.base.transforms3d.oa2r` + :seealso: :func:`~base.transforms3d.oa2r` """ return cls(smb.oa2r(o, a), check=False) @@ -881,9 +881,10 @@ def Exp( of twist vectors, one per row. .. note:: - - if :math:`\theta \eq 0` the result in an identity matrix - - an input 3x3 matrix is ambiguous, it could be the first or third case above. In this - case the parameter `so3` is the decider. + + - if :math:`\theta \eq 0` the result in an identity matrix + - an input 3x3 matrix is ambiguous, it could be the first or third case above. In this + case the parameter `so3` is the decider. :seealso: :func:`spatialmath.base.transforms3d.trexp`, :func:`spatialmath.base.transformsNd.skew` """ @@ -1064,15 +1065,17 @@ def __init__(self, x=None, y=None, z=None, *, check=True): - ``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])`` has ``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(X)`` where ``X`` is: - - ``SE3`` is a copy of ``X`` - - ``SO3`` is the rotation of ``X`` with zero translation - - ``SE2`` is the z-axis rotation and x- and y-axis translation of - ``X`` + - ``SE3`` is a copy of ``X`` + - ``SO3`` is the rotation of ``X`` with zero translation + - ``SE2`` is the z-axis rotation and x- and y-axis translation of ``X`` + - ``SE3([X1, X2, ... XN])`` has ``N`` values given by the elements ``Xi`` each of which is an SE3 instance. @@ -1818,7 +1821,7 @@ def OA(cls, o: ArrayLike3, a: ArrayLike3) -> SE3: @classmethod def AngleAxis( - cls, theta: float, v: ArrayLike3, *, unit: Optional[unit] = "rad" + cls, theta: float, v: ArrayLike3, *, unit: Optional[str] = "rad" ) -> SE3: r""" Create an SE(3) pure rotation matrix from rotation angle and axis diff --git a/spatialmath/quaternion.py b/spatialmath/quaternion.py index 2994b6e6..54ea1362 100644 --- a/spatialmath/quaternion.py +++ b/spatialmath/quaternion.py @@ -482,8 +482,9 @@ def inner(self, other) -> float: .. runblock:: pycon >>> from spatialmath import Quaternion + >>> import numpy as np >>> Quaternion([1,2,3,4]).inner(Quaternion([5,6,7,8])) - >>> numpy.dot([1,2,3,4], [5,6,7,8]) + >>> np.dot([1,2,3,4], [5,6,7,8]) :seealso: :func:`~spatialmath.base.quaternions.qinner` """ @@ -503,7 +504,8 @@ def __eq__( :return: Equality of two operands :rtype: bool or list of bool - ``q1 == q2`` is True if ``q1` is elementwise equal to ``q2``. + + ``q1 == q2`` is True if ``q1` is elementwise-equal to ``q2``. Example: @@ -673,7 +675,7 @@ def __pow__(self, n: int) -> Quaternion: :rtype: Quaternion instance ``q ** N`` computes the product of ``q`` with itself ``N-1`` times, where ``N`` must be - an integer. If ``N``<0 the result is conjugated. + an integer. If ``N`` < 0 the result is conjugated. Example: @@ -1392,10 +1394,11 @@ def OA(cls, o: ArrayLike3, a: ArrayLike3) -> UnitQuaternion: :return: unit-quaternion :rtype: UnitQuaternion instance - ``UnitQuaternion.OA(O, A)`` is a unit quaternion that describes the 3D rotation defined in terms of - vectors parallel to the Y- and Z-axes of its reference frame. In robotics these axes are - respectively called the orientation and approach vectors defined such that - R = [N O A] and N = O x A. + ``UnitQuaternion.OA(O, A)`` is a unit quaternion that describes the 3D rotation + defined in terms of vectors parallel to the Y- and Z-axes of its reference + frame, respectively :math:`\vec{o}` and :math:`\vec{a}`. In robotics these axes + are respectively called the orientation and approach vectors defined such that + :math:`\mat{R}=[\vec{n}, \vec{o}, \vec{a}]` and :math:`\vec{n} = \vec{o} \times \vec{a}`. Example: @@ -1407,7 +1410,7 @@ def OA(cls, o: ArrayLike3, a: ArrayLike3) -> UnitQuaternion: .. note:: - Only the ``A`` vector is guaranteed to have the same direction in the resulting - rotation matrix + rotation matrix - ``O`` and ``A`` do not have to be unit-length, they are normalized - ``O`` and ``A` do not have to be orthogonal, so long as they are not parallel @@ -1531,7 +1534,7 @@ def inv(self) -> UnitQuaternion: .. runblock:: pycon - >>> from spatialmath import UnitQuaternion + >>> from spatialmath import UnitQuaternion as UQ >>> print(UQ.Rx(0.3).inv()) >>> print(UQ.Rx(0.3).inv() * UQ.Rx(0.3)) >>> print(UQ.Rx([0.3, 0.6]).inv()) @@ -1680,7 +1683,7 @@ def __mul__( >>> from spatialmath import UnitQuaternion as UQ >>> print(UQ.Rx(0.3) * UQ.Rx(0.4)) >>> q = UQ.Rx(0.3) - >>> q *= UQ.Rx(0.4)) + >>> q *= UQ.Rx(0.4) >>> print(q) >>> print(UQ.Rx(0.3) * UQ.Rx([0.4, 0.6])) >>> print(UQ.Rx([0.3, 0.6]) * UQ.Rx(0.3)) diff --git a/spatialmath/twist.py b/spatialmath/twist.py index dcefa840..94a21416 100644 --- a/spatialmath/twist.py +++ b/spatialmath/twist.py @@ -149,10 +149,9 @@ def isunit(self): >>> from spatialmath import Twist3 >>> S = Twist3([1,2,3,4,5,6]) - >>> S.isunit() + >>> S.isunit >>> S = Twist3.UnitRevolute([1,2,3], [4,5,6]) - >>> S.isunit() - + >>> S.isunit """ if len(self) == 1: return smb.isunitvec(self.S) @@ -239,7 +238,7 @@ def __eq__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-argu .. runblock:: pycon - >>> from spatialmath import Twist2 + >>> from spatialmath import Twist3 >>> S1 = Twist3([1,2,3,4,5,6]) >>> S2 = Twist3([1,2,3,4,5,6]) >>> S1 == S2 @@ -258,7 +257,7 @@ def __ne__(left, right): # lgtm[py/not-named-self] pylint: disable=no-self-argu :rtype: bool - ``S1 == S2`` is True if ``S1` is not elementwise equal to ``S2``. + ``S1 == S2`` is True if ``S1`` is not elementwise equal to ``S2``. Example: @@ -495,7 +494,7 @@ def UnitRevolute(cls, a, q, pitch=None): .. runblock:: pycon >>> from spatialmath import Twist3 - >>> Twist3.Revolute([0, 0, 1], [1, 2, 0]) + >>> Twist3.UnitRevolute([0, 0, 1], [1, 2, 0]) """ w = smb.unitvec(smb.getvector(a, 3)) @@ -519,7 +518,7 @@ def UnitPrismatic(cls, a): .. runblock:: pycon >>> from spatialmath import Twist3 - >>> Twist3.Prismatic([0, 0, 1]) + >>> Twist3.UnitPrismatic([0, 0, 1]) """ w = np.r_[0, 0, 0] @@ -822,7 +821,7 @@ def unit(self): .. runblock:: pycon >>> from spatialmath import SE3, Twist3 - >>> T = SE3(1, 2, 0.3) + >>> T = SE3(1, 2, 3) * SE3.Rx(0.3) >>> S = Twist3(T) >>> S.unit() """ @@ -913,7 +912,7 @@ def skewa(self): >>> S = Twist3.Rx(0.3) >>> se = S.skewa() >>> se - >>> smb.trexp(se) + >>> base.trexp(se) """ if len(self) == 1: return smb.skewa(self.S) @@ -1112,7 +1111,7 @@ def __mul__( #. scalar x Twist is handled by ``__rmul__`` #. scalar multiplication is commutative but the result is not a group - operation so the result will be a matrix + operation so the result will be a matrix #. Any other input combinations result in a ValueError. For pose composition the ``left`` and ``right`` operands may be a sequence @@ -1342,7 +1341,7 @@ def isvalid(v, check=True): >>> from spatialmath import Twist2, base >>> import numpy as np >>> Twist2.isvalid([1, 2, 3]) - >>> a = smb.skewa([1, 2, 3]) + >>> a = base.skewa([1, 2, 3]) >>> a >>> Twist2.isvalid(a) >>> Twist2.isvalid(np.random.rand(3,3)) @@ -1373,14 +1372,16 @@ def UnitRevolute(cls, q): :return: 2D prismatic twist :rtype: Twist2 instance - - ``Twist2.Revolute(q)`` is a 2D Twist object representing rotation about the 2D point ``q``. + - ``Twist2.UnitRevolute(q)`` is a 2D Twist object representing rotation about the 2D point ``q``. Example: .. runblock:: pycon >>> from spatialmath import Twist2 - >>> Twist2.Revolute([0, 1]) + >>> Twist2.UnitRevolute([0, 1]) + + :seealso: :meth:`Twist2.UnitPrismatic` """ q = smb.getvector(q, 2) @@ -1397,14 +1398,16 @@ def UnitPrismatic(cls, a): :return: 2D prismatic twist :rtype: Twist2 instance - - ``Twist2.Prismatic(a)`` is a 2D Twist object representing 2D-translation in the direction ``a``. + - ``Twist2.UnitPrismatic(a)`` is a 2D Twist object representing 2D-translation in the direction ``a``. Example: .. runblock:: pycon >>> from spatialmath import Twist2 - >>> Twist2.Prismatic([1, 2]) + >>> Twist2.UnitPrismatic([1, 2]) + + :seealso: :meth:`Twist2.UnitRevolute` """ w = 0 v = smb.unitvec(smb.getvector(a, 2)) @@ -1491,10 +1494,11 @@ def pole(self): .. runblock:: pycon - >>> from spatialmath import SE3, Twist3 + >>> from spatialmath import SE2, Twist2 >>> T = SE2(1, 2, 0.3) >>> S = Twist2(T) >>> S.pole() + """ p = np.cross(np.r_[0, 0, self.w], np.r_[self.v, 0]) / self.theta return p[:2] @@ -1519,7 +1523,7 @@ def SE2(self, theta=1, unit="rad"): .. runblock:: pycon >>> from spatialmath import Twist2 - >>> S = Twist2.Prismatic([1,2]) + >>> S = Twist2.UnitPrismatic([1,2]) >>> S.SE2() :seealso: :func:`Twist3.exp` @@ -1555,7 +1559,7 @@ def skewa(self): >>> S = Twist2([1,2,3]) >>> se = S.skewa() >>> se - >>> smb.trexp2(se) + >>> base.trexp2(se) """ if len(self) == 1: return smb.skewa(self.S) @@ -1617,7 +1621,7 @@ def unit(self): .. runblock:: pycon - >>> from spatialmath import SE3, Twist3 + >>> from spatialmath import SE2, Twist2 >>> T = SE2(1, 2, 0.3) >>> S = Twist2(T) >>> S.unit() @@ -1641,7 +1645,7 @@ def ad(self): .. runblock:: pycon - >>> from spatialmath import SE3, Twist3 + >>> from spatialmath import SE2, Twist2 >>> T = SE2(1, 2, 0.3) >>> S = Twist2(T) >>> S.unit() @@ -1737,7 +1741,7 @@ def __mul__( #. scalar x Twist is handled by ``__rmul__`` #. scalar multiplication is commutative but the result is not a group - operation so the result will be a matrix + operation so the result will be a matrix #. Any other input combinations result in a ValueError. For pose composition the ``left`` and ``right`` operands may be a sequence @@ -1819,7 +1823,7 @@ def __repr__(self): return ( "Twist2([\n" + ",\n".join( - [" [{:.5g}, {:.5g}, {:.5g}}]".format(*list(tw.S)) for tw in self] + [" [{:.5g}, {:.5g}, {:.5g}]".format(*list(tw.S)) for tw in self] ) + "\n])" )