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

Merged
merged 74 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
624ff64
Design philosophy
AlexanderFabisch Oct 28, 2024
be61146
Fix double cover function of MRP
AlexanderFabisch Oct 29, 2024
201a361
Add mrp_prod_vector
AlexanderFabisch Oct 29, 2024
2e20650
Fix grammar in docs
AlexanderFabisch Oct 29, 2024
665ddcb
Update docstring
AlexanderFabisch Oct 31, 2024
ac9f8de
Define rot_log_from_compact_axis_angle
AlexanderFabisch Oct 31, 2024
e444bac
Correct docstring
AlexanderFabisch Oct 31, 2024
8121f71
Deprecate scale_transform
AlexanderFabisch Oct 31, 2024
c94dda9
Correct formula for quaternion_diff
AlexanderFabisch Oct 31, 2024
6e22683
Correct formula
AlexanderFabisch Oct 31, 2024
acade5b
Fix docstring
AlexanderFabisch Oct 31, 2024
7d1128c
Add alias check_rot_log
AlexanderFabisch Nov 2, 2024
87efcb1
added a trajetorie mode for Temporal
JonasHablitzelAVLD Oct 17, 2024
c0cc942
added sclerp for arrays/trajectories
JonasHablitzel Oct 22, 2024
5d070b3
added Unittest and docstrings to support multiple timestampf for Temp…
JonasHablitzel Nov 4, 2024
5be78c0
Test concat_dynamic
AlexanderFabisch Nov 7, 2024
b8580ee
Test edge cases of dual_quaternions_sclerp
AlexanderFabisch Nov 7, 2024
0d37ac9
added sclerp for arrays/trajectories
JonasHablitzel Oct 22, 2024
1d00e41
added Unittest and docstrings to support multiple timestampf for Temp…
JonasHablitzel Nov 4, 2024
4ee17a9
Fixed pep8 issues
JonasHablitzel Nov 12, 2024
656a4d0
restructured function in trajectories/batch_rotations
JonasHablitzel Nov 12, 2024
a69fe7b
Formatted pep8 in docstring
JonasHablitzel Nov 12, 2024
e20673f
fixed typo
JonasHablitzel Nov 12, 2024
9caeeff
Rearrganed function in docs
JonasHablitzel Nov 17, 2024
3a2dbd4
added test for adding/removing transform in Temporal to have full cov…
JonasHablitzel Nov 17, 2024
a7da8fe
Add missing spaces
AlexanderFabisch Nov 19, 2024
7f78639
PEP8
AlexanderFabisch Nov 19, 2024
9b0feb0
Move batch orientation operations
AlexanderFabisch Nov 19, 2024
43066fa
Test norm_axis_angles
AlexanderFabisch Nov 19, 2024
433642c
Test 1D and 3D cases
AlexanderFabisch Nov 19, 2024
6177be0
Test axis_angles_from_quaternions
AlexanderFabisch Nov 19, 2024
d1044ef
Rearrange test
AlexanderFabisch Nov 19, 2024
885d4ec
PEP8
AlexanderFabisch Nov 19, 2024
b3a3d89
Remove duplicate test
AlexanderFabisch Nov 19, 2024
33d3858
Test batch_dq_q_conj
AlexanderFabisch Nov 20, 2024
9cb60c1
Test nd sclerp
AlexanderFabisch Nov 20, 2024
077d101
Remove duplicate test
AlexanderFabisch Nov 20, 2024
0b9094a
Simplify conversion logic
AlexanderFabisch Nov 20, 2024
403ad89
Less computations in vectorized branch
AlexanderFabisch Nov 20, 2024
41d6c8f
Refactor conversion
AlexanderFabisch Nov 20, 2024
1bf0878
Reformat code
AlexanderFabisch Nov 19, 2024
b95e128
Test special case
AlexanderFabisch Nov 19, 2024
bf53c3f
Test 3D case
AlexanderFabisch Nov 19, 2024
b100794
Add more n-d tests
AlexanderFabisch Nov 19, 2024
d7c6f10
PEP8
AlexanderFabisch Nov 20, 2024
0aa89a4
Test and fix error in conversion
AlexanderFabisch Nov 20, 2024
5fab670
PEP8
AlexanderFabisch Nov 20, 2024
895eb43
dual_quaternions_from_screw_parameters nd tests
AlexanderFabisch Nov 21, 2024
17004a8
Full branch coverage of TemporalTransformManager
AlexanderFabisch Nov 21, 2024
7483405
Handle edge cases properly
AlexanderFabisch Nov 21, 2024
dd24fa2
Introduce time clipping flag
AlexanderFabisch Nov 22, 2024
86b3f78
Proper array names
AlexanderFabisch Nov 22, 2024
fe9662c
Simplify logic
AlexanderFabisch Nov 22, 2024
bf736ae
Test concat_many_to_many
AlexanderFabisch Nov 22, 2024
3f8617a
Update pytransform3d/test/test_transform_manager.py
AlexanderFabisch Nov 23, 2024
357b54e
Remove duplicate array definition
AlexanderFabisch Nov 23, 2024
c1078f6
More readable and consistent names
AlexanderFabisch Nov 23, 2024
651a910
Simplify unit test
AlexanderFabisch Nov 23, 2024
08d80f5
Current time could also be float
AlexanderFabisch Nov 23, 2024
8e0edc0
Update docstring
AlexanderFabisch Nov 23, 2024
a1280be
More readable variable names
AlexanderFabisch Nov 24, 2024
0fd3cbb
Update pytransform3d/test/test_batch_rotations.py
AlexanderFabisch Nov 24, 2024
206de59
Update pytransform3d/test/test_batch_rotations.py
AlexanderFabisch Nov 24, 2024
54dff24
Rename variable
AlexanderFabisch Nov 24, 2024
9434ae0
Update pytransform3d/test/test_batch_rotations.py
AlexanderFabisch Nov 24, 2024
9f10b31
PEP8
AlexanderFabisch Nov 24, 2024
fa79eec
Don't normalize samples in test of norm_axis_angle
AlexanderFabisch Nov 24, 2024
3b775db
Remove magic numbers
AlexanderFabisch Nov 24, 2024
49606f0
Remove magic numbers
AlexanderFabisch Nov 24, 2024
ada7798
Remove magic numbers
AlexanderFabisch Nov 24, 2024
0497f7a
Remove magic numbers
AlexanderFabisch Nov 24, 2024
457ce90
Explain checks
AlexanderFabisch Nov 24, 2024
72dcd91
Add ORCID to CITATION.cff
AlexanderFabisch Nov 26, 2024
8671ab9
Version 3.9.0
AlexanderFabisch Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ preferred-citation:
authors:
- family-names: Fabisch
given-names: Alexander
orcid: https://orcid.org/0000-0003-2824-7956
title: "pytransform3d: 3D Transformations for Python"
doi: 10.21105/joss.01159
journal: Journal of Open Source Software
start: 1159
issue: 33
volume: 4
month: 1
year: 2019
year: 2019
29 changes: 27 additions & 2 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,10 @@ Logarithm of Rotation
:toctree: _apidoc/
:template: function.rst

~check_rot_log
~check_skew_symmetric_matrix

~rot_log_from_compact_axis_angle
~cross_product_matrix


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

~concatenate_mrp
~mrp_prod_vector

~mrp_from_axis_angle
~mrp_from_quaternion
Expand Down Expand Up @@ -295,7 +298,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 +441,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 Expand Up @@ -468,7 +479,9 @@ Axis-Angle Representation
:toctree: _apidoc/
:template: function.rst

~norm_axis_angles
~axis_angles_from_matrices
~axis_angles_from_quaternions

~cross_product_matrices

Expand Down Expand Up @@ -516,6 +529,8 @@ Transformation Matrices
~invert_transforms
~concat_one_to_many
~concat_many_to_one
~concat_many_to_many
~concat_dynamic

~transforms_from_pqs
~transforms_from_exponential_coordinates
Expand All @@ -533,6 +548,13 @@ Positions and Quaternions
~pqs_from_transforms
~pqs_from_dual_quaternions

Screw Parameters
----------------
.. autosummary::
:toctree: _apidoc/
:template: function.rst

~screw_parameters_from_dual_quaternions

Exponential Coordinates
-----------------------
Expand All @@ -553,10 +575,13 @@ Dual Quaternions
:template: function.rst

~batch_dq_conj
~batch_dq_q_conj
~batch_concatenate_dual_quaternions
~batch_dq_prod_vector

~dual_quaternions_from_pqs
~dual_quaternions_power
~dual_quaternions_sclerp
~dual_quaternions_from_screw_parameters


:mod:`pytransform3d.uncertainty`
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
2 changes: 1 addition & 1 deletion examples/plots/plot_interpolation_for_transform_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def create_sinusoidal_movement(
tm.add_transform("A", "world", transform_WA)
tm.add_transform("B", "world", transform_WB)

query_time = 4.9 # [s]
query_time = 4.9 # [s] or an array of times np.array([4.9, 5.2])
A2B_at_query_time = tm.get_transform_at_time("A", "B", query_time)

# transform the origin of A in A (x=0, y=0, z=0) to B
Expand Down
2 changes: 1 addition & 1 deletion pytransform3d/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""3D transformations for Python."""


__version__ = "3.8.0"
__version__ = "3.9.0"
87 changes: 86 additions & 1 deletion pytransform3d/batch_rotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"""
import numpy as np
from .rotations import (
angle_between_vectors, slerp_weights, pick_closest_quaternion)
angle_between_vectors, slerp_weights, pick_closest_quaternion, norm_angle)


def norm_vectors(V, out=None):
Expand Down Expand Up @@ -38,6 +38,52 @@ def norm_vectors(V, out=None):
return out


def norm_axis_angles(a):
"""Normalize axis-angle representation.

Parameters
----------
a : array-like, shape (..., 4)
Axis of rotation and rotation angle: (x, y, z, angle)

Returns
-------
a : array, shape (..., 4)
Axis of rotation and rotation angle: (x, y, z, angle). The length
of the axis vector is 1 and the angle is in [0, pi). No rotation
is represented by [1, 0, 0, 0].
"""
a = np.asarray(a)

# Handle the case of only one axis-angle instance
only_one = a.ndim == 1
a = np.atleast_2d(a)

angles = a[..., 3]
norm = np.linalg.norm(a[..., :3], axis=-1)

no_rot_mask = (angles == 0.0) | (norm == 0.0)
rot_mask = ~no_rot_mask

res = np.empty_like(a)
res[no_rot_mask, :] = np.array([1.0, 0.0, 0.0, 0.0])
res[rot_mask, :3] = (
a[rot_mask, :3] / norm[rot_mask, np.newaxis]
)

angle_normalized = norm_angle(angles)

negative_angle_mask = angle_normalized < 0.0
res[negative_angle_mask, :3] *= -1.0
angle_normalized[negative_angle_mask] *= -1.0

res[rot_mask, 3] = angle_normalized[rot_mask]

if only_one:
res = res[0]
return res


def angles_between_vectors(A, B):
"""Compute angle between two vectors.

Expand Down Expand Up @@ -338,6 +384,45 @@ def axis_angles_from_matrices(Rs, traces=None, out=None):
return out


def axis_angles_from_quaternions(qs):
"""Compute axis-angle from quaternion.

This operation is called logarithmic map.

We usually assume active rotations.

Parameters
----------
qs : array-like, shape (..., 4)
Unit quaternion to represent rotation: (w, x, y, z)

Returns
-------
as : array, shape (..., 4)
Axis of rotation and rotation angle: (x, y, z, angle). The angle is
constrained to [0, pi) so that the mapping is unique.
"""
quaternion_vector_part = qs[..., 1:]
qvec_norm = np.linalg.norm(quaternion_vector_part, axis=-1)

# Vectorized branches bases on norm of the vector part
small_p_norm_mask = qvec_norm < np.finfo(float).eps
non_zero_mask = ~small_p_norm_mask

axes = (quaternion_vector_part[non_zero_mask]
/ qvec_norm[non_zero_mask, np.newaxis])

w_clamped = np.clip(qs[non_zero_mask, 0], -1.0, 1.0)
angles = 2.0 * np.arccos(w_clamped)

result = np.empty_like(qs)
result[non_zero_mask] = norm_axis_angles(
np.concatenate((axes, angles[..., np.newaxis]), axis=-1))
result[small_p_norm_mask] = np.array([1.0, 0.0, 0.0, 0.0])

return result


def cross_product_matrices(V):
"""Generate the cross-product matrices of vectors.

Expand Down
6 changes: 6 additions & 0 deletions pytransform3d/batch_rotations.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ def norm_vectors(
out: Union[npt.ArrayLike, None] = ...) -> np.ndarray: ...


def norm_axis_angles(a: npt.ArrayLike) -> np.ndarray: ...


def angles_between_vectors(A: npt.ArrayLike, B: npt.ArrayLike) -> np.ndarray: ...


Expand Down Expand Up @@ -38,6 +41,9 @@ def axis_angles_from_matrices(
out: Union[np.ndarray, None] = ...) -> np.ndarray: ...


def axis_angles_from_quaternions(qs: npt.ArrayLike) -> np.ndarray: ...


def cross_product_matrices(V: npt.ArrayLike) -> np.ndarray: ...


Expand Down
10 changes: 8 additions & 2 deletions pytransform3d/rotations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
matrix_requires_renormalization, norm_matrix,
quaternion_requires_renormalization,
plane_basis_from_normal,
check_skew_symmetric_matrix, check_matrix, check_quaternion,
check_skew_symmetric_matrix, check_rot_log, check_matrix, check_quaternion,
check_quaternions, check_axis_angle, check_compact_axis_angle,
check_rotor, check_mrp)
from ._random import (
Expand Down 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 @@ -137,6 +140,7 @@
"random_compact_axis_angle",
"random_quaternion",
"check_skew_symmetric_matrix",
"check_rot_log",
"check_matrix",
"check_quaternion",
"check_quaternions",
Expand Down Expand Up @@ -223,7 +227,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
Loading