Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development branch, release 3.9.0 #305

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
12 changes: 11 additions & 1 deletion doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Logarithm of Rotation
~check_skew_symmetric_matrix

~cross_product_matrix
~rot_log_from_compact_axis_angle


Quaternion
Expand Down Expand Up @@ -178,6 +179,7 @@ Modified Rodrigues Parameters
~mrp_double

~concatenate_mrp
~mrp_prod_vector

~mrp_from_axis_angle
~mrp_from_quaternion
Expand Down Expand Up @@ -295,7 +297,6 @@ Transformation Matrix
~vectors_to_points
~vector_to_direction
~vectors_to_directions
~scale_transform
~adjoint_from_transform

~plot_transform
Expand Down Expand Up @@ -439,6 +440,15 @@ Jacobians
~left_jacobian_SE3_inv
~left_jacobian_SE3_inv_series

Deprecated Functions
--------------------

.. autosummary::
:toctree: _apidoc/
:template: function.rst

~scale_transform


:mod:`pytransform3d.batch_rotations`
====================================
Expand Down
5 changes: 5 additions & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ pytransform3d offers...
* a matplotlib-like interface to Open3D's visualizer to display
geometries and transformations

The design philosophy of pytransform3d is to not hide anything; there is no
magic: no operator overloading, no classes to store data (rotations or
transformations) other than NumPy arrays, and no layers of abstraction.
It is as transparent to the user as possible and its interface is mainly
functional.

--------
Citation
Expand Down
2 changes: 1 addition & 1 deletion doc/source/user_guide/uncertainty.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ A typical visual representation of Gaussian distributions in 3D coordinates
are equiprobable ellipsoids (obtained with
:func:`~pytransform3d.uncertainty.to_ellipsoid`). This is equivalent to showing
the :math:`k\sigma, k \in \mathbb{R}` intervals of a 1D Gaussian distribution.
However, for transformations are also interactions between rotation and
However, for transformations there are also interactions between rotation and
translation components so that an ellipsoid is not an appropriate
representation to visualize the distribution of transformations in 3D. We have
to project a 6D hyper-ellipsoid to 3D (for which we can use
Expand Down
7 changes: 6 additions & 1 deletion pytransform3d/rotations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
euler_from_quaternion,
quaternion_from_angle,
cross_product_matrix,
rot_log_from_compact_axis_angle,
mrp_from_quaternion,
quaternion_from_mrp,
mrp_from_axis_angle,
Expand All @@ -93,7 +94,9 @@
quaternion_double, quaternion_integrate, quaternion_gradient,
concatenate_quaternions, q_conj, q_prod_vector, quaternion_diff,
quaternion_dist, quaternion_from_euler)
from ._mrp import mrp_near_singularity, norm_mrp, mrp_double, concatenate_mrp
from ._mrp import (
mrp_near_singularity, norm_mrp, mrp_double, concatenate_mrp,
mrp_prod_vector)
from ._slerp import (slerp_weights, pick_closest_quaternion, quaternion_slerp,
axis_angle_slerp, rotor_slerp)
from ._testing import (
Expand Down Expand Up @@ -223,7 +226,9 @@
"norm_mrp",
"mrp_double",
"concatenate_mrp",
"mrp_prod_vector",
"cross_product_matrix",
"rot_log_from_compact_axis_angle",
"mrp_from_quaternion",
"quaternion_from_mrp",
"mrp_from_axis_angle",
Expand Down
19 changes: 13 additions & 6 deletions pytransform3d/rotations/_conversions.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,27 @@ def cross_product_matrix(v):
-v_2 & v_1 & 0
\end{array}\right).

The function can also be used to compute the logarithm of rotation from
a compact axis-angle representation.

Parameters
----------
v : array-like, shape (3,)
3d vector

Returns
-------
V : array-like, shape (3, 3)
V : array, shape (3, 3)
Cross-product matrix
"""
return np.array([[0.0, -v[2], v[1]],
[v[2], 0.0, -v[0]],
[-v[1], v[0], 0.0]])


rot_log_from_compact_axis_angle = cross_product_matrix


def matrix_from_two_vectors(a, b):
"""Compute rotation matrix from two vectors.

Expand Down Expand Up @@ -1964,12 +1970,13 @@ def axis_angle_from_two_directions(a, b):
def compact_axis_angle(a):
r"""Compute 3-dimensional axis-angle from a 4-dimensional one.

In a 3-dimensional axis-angle, the 4th dimension (the rotation) is
represented by the norm of the rotation axis vector, which means we
transform :math:`\left( \boldsymbol{\hat{e}}, \theta \right)` to
:math:`\theta \boldsymbol{\hat{e}}`.
In the 3-dimensional axis-angle representation, the 4th dimension (the
rotation) is represented by the norm of the rotation axis vector, which
means we map :math:`\left( \hat{\boldsymbol{\omega}}, \theta \right)` to
:math:`\boldsymbol{\omega} = \theta \hat{\boldsymbol{\omega}}`.

We usually assume active rotations.
This representation is also called rotation vector or exponential
coordinates of rotation.

Parameters
----------
Expand Down
3 changes: 3 additions & 0 deletions pytransform3d/rotations/_conversions.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import numpy.typing as npt
def cross_product_matrix(v: npt.ArrayLike) -> np.ndarray: ...


def rot_log_from_compact_axis_angle(v: npt.ArrayLike) -> np.ndarray: ...


def matrix_from_two_vectors(a: npt.ArrayLike, b: npt.ArrayLike) -> np.ndarray: ...


Expand Down
41 changes: 39 additions & 2 deletions pytransform3d/rotations/_mrp.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def mrp_double(mrp):
\boldsymbol{\psi}` represent the same rotation and correspond to two
antipodal unit quaternions [1]_.

No rotation is a special case, in which no second representation exists.
Only the zero vector represents no rotation.

Parameters
----------
mrp : array-like, shape (3,)
Expand All @@ -78,7 +81,10 @@ def mrp_double(mrp):
http://malcolmdshuster.com/Pub_1993h_J_Repsurv_scan.pdf
"""
mrp = check_mrp(mrp)
return mrp / -np.dot(mrp, mrp)
norm = np.dot(mrp, mrp)
if norm == 0.0:
return mrp
return mrp / -norm


def concatenate_mrp(mrp1, mrp2):
Expand All @@ -90,7 +96,8 @@ def concatenate_mrp(mrp1, mrp2):
apply the result to v.

The solution for concatenation of two rotations
:math:`\boldsymbol{\psi}_1,\boldsymbol{\psi}_2` is given by Shuster [1]_:
:math:`\boldsymbol{\psi}_1,\boldsymbol{\psi}_2` is given by Shuster [1]_
(Equation 257):

.. math::

Expand Down Expand Up @@ -131,3 +138,33 @@ def concatenate_mrp(mrp1, mrp2):
return (
(1.0 - norm1_sq) * mrp2 + (1.0 - norm2_sq) * mrp1 - 2.0 * cross_product
) / (1.0 + norm2_sq * norm1_sq - 2.0 * scalar_product)


def mrp_prod_vector(mrp, v):
r"""Apply rotation represented by MRPs to a vector.

To apply the rotation defined by modified Rodrigues parameters
:math:`\boldsymbol{\psi} \in \mathbb{R}^3` to a vector
:math:`\boldsymbol{v} \in \mathbb{R}^3`, we left-concatenate the original
MRPs and then right-concatenate its inverted (negative) version.

Parameters
----------
mrp : array-like, shape (3,)
Modified Rodrigues parameters.

v : array-like, shape (3,)
3d vector

Returns
-------
w : array, shape (3,)
3d vector

See Also
--------
concatenate_mrp : Concatenates MRPs.
q_prod_vector : The same operation with a quaternion.
"""
mrp = check_mrp(mrp)
return concatenate_mrp(concatenate_mrp(mrp, v), -mrp)
3 changes: 3 additions & 0 deletions pytransform3d/rotations/_mrp.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ def mrp_double(mrp: npt.ArrayLike) -> np.ndarray: ...


def concatenate_mrp(mrp1: npt.ArrayLike, mrp2: npt.ArrayLike) -> np.ndarray: ...


def mrp_prod_vector(mrp: npt.ArrayLike, v: npt.ArrayLike) -> np.ndarray: ...
4 changes: 2 additions & 2 deletions pytransform3d/rotations/_quaternions.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def quaternion_gradient(Q, dt=1.0):

Returns
-------
A : array-like, shape (n_steps, 3)
A : array, shape (n_steps, 3)
Angular velocities in a compact axis-angle representation. Each angular
velocity represents the rotational offset after one unit of time.
Angular velocities are given in global frame and will be
Expand Down Expand Up @@ -272,7 +272,7 @@ def quaternion_diff(q1, q2):

.. math::

\omega = 2 \log (q_1 * \overline{q_2})
(\hat{\boldsymbol{\omega}}, \theta) = \log (q_1 \overline{q_2})

Parameters
----------
Expand Down
15 changes: 15 additions & 0 deletions pytransform3d/test/test_rotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2437,6 +2437,21 @@ def test_mrp_double():
pr.assert_quaternion_equal(q, q_double)
assert not np.allclose(q, q_double)

assert_array_almost_equal(np.zeros(3), pr.mrp_double(np.zeros(3)))


def test_mrp_prod_vector():
rng = np.random.default_rng(2183)
v = pr.random_vector(rng, 3)
assert_array_almost_equal(v, pr.mrp_prod_vector([0, 0, 0], v))

for _ in range(5):
mrp = pr.random_vector(rng, 3)
q = pr.quaternion_from_mrp(mrp)
v_mrp = pr.mrp_prod_vector(mrp, v)
v_q = pr.q_prod_vector(q, v)
assert_array_almost_equal(v_mrp, v_q)


def test_assert_euler_almost_equal():
pr.assert_euler_equal(
Expand Down
Loading