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

948 add support to create coordinate systems from homogeneous transformation matrices #949

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## 0.6.9 (unreleased)

### Changes

- added support for homogeneous transformation matrices \[{pull}`949`\]:
- added `create_cs_from_homogeneous_transformation` to `CoordinateSystemManager`
- added `from_homogeneous_transformation` to `LocalCoordinateSystem`
- added `as_homogeneous_matrix` to `LocalCoordinateSystem`

### Fixes

- rename (fix typo) argument to `lcs_child_in_parent` in `CoordinateSystemManager.add_cs` \[{pull}`936`\].
Expand Down
16 changes: 16 additions & 0 deletions weldx/tests/transformations/test_cs_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,10 @@ def test_coordinate_system_manager_create_coordinate_system():
orientations = np.matmul(rot_mat_x, rot_mat_y)
coords = Q_([[1, 0, 0], [-1, 0, 2], [3, 5, 7], [-4, -5, -6]], "mm")

transformation_matrix = np.resize(np.identity(4), (4, 4, 4))
transformation_matrix[:, :3, :3] = orientations
transformation_matrix[:, :3, 3] = coords.m

csm = tf.CoordinateSystemManager("root")
lcs_default = tf.LocalCoordinateSystem()

Expand Down Expand Up @@ -2790,6 +2794,18 @@ def test_coordinate_system_manager_create_coordinate_system():
time=time,
)

# from homogeneous transformation ---------------------
csm.create_cs_from_homogeneous_transformation(
"lcs_homogeneous_default", "root", transformation_matrix, coords.u, time
)
check_coordinate_system(
csm.get_cs("lcs_homogeneous_default"),
orientations,
coords,
True,
time=time,
)


def test_coordinate_system_manager_transform_data():
"""Test the coordinate system managers transform_data function."""
Expand Down
48 changes: 47 additions & 1 deletion weldx/transformations/cs_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
from weldx.exceptions import WeldxDeprecationWarning, WeldxException
from weldx.geometry import SpatialData
from weldx.time import Time, types_time_like, types_timestamp_like
from weldx.types import UnitLike
from weldx.util import check_matplotlib_available, dataclass_nested_eq

from .local_cs import LocalCoordinateSystem
from .types import types_coordinates, types_orientation
from .types import types_coordinates, types_homogeneous, types_orientation

# only import heavy-weight packages on type checking
if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -830,6 +831,51 @@ def create_cs_from_axis_vectors(
coordinate_system_name, reference_system_name, lcs, lcs_child_in_parent
)

def create_cs_from_homogeneous_transformation(
self,
coordinate_system_name: str,
reference_system_name: str,
transformation_matrix: types_homogeneous,
translation_unit: UnitLike,
time: types_time_like = None,
time_ref: types_timestamp_like = None,
lcs_child_in_parent: bool = True,
):
"""Create a coordinate system from a homogeneous transformation matrix and add
it to the coordinate system manager.

This function uses the `LocalCoordinateSystem.from_homogeneous_transformation`
method of the `LocalCoordinateSystem` class.

Parameters
----------
coordinate_system_name :
Name of the new coordinate system.
reference_system_name :
Name of the parent system. This must have been already added.
transformation_matrix :
Describes the homogeneous transformation matrix that includes the rotation
and the translation (coordinates).
translation_unit :
Unit describing the value of the translation. Necessary, because the
homogeneous transformation matrix is unitless.
time :
Time data for time dependent coordinate systems.
time_ref :
Reference time for time dependent coordinate systems
lcs_child_in_parent :
If set to `True`, the passed `LocalCoordinateSystem` instance describes
the new system orientation towards is parent. If `False`, it describes
how the parent system is positioned in its new child system.

"""
lcs = LocalCoordinateSystem.from_homogeneous_transformation(
transformation_matrix, translation_unit, time, time_ref
)
self.add_cs(
coordinate_system_name, reference_system_name, lcs, lcs_child_in_parent
)

def delete_cs(self, coordinate_system_name: str, delete_children: bool = False):
"""Delete a coordinate system from the coordinate system manager.

Expand Down
84 changes: 83 additions & 1 deletion weldx/transformations/local_cs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@
from weldx.core import TimeSeries
from weldx.exceptions import WeldxException
from weldx.time import Time, TimeDependent, types_time_like, types_timestamp_like
from weldx.transformations.types import types_coordinates, types_orientation
from weldx.transformations.types import (
types_coordinates,
types_homogeneous,
types_orientation,
)
from weldx.transformations.util import normalize
from weldx.types import UnitLike

__all__ = ("LocalCoordinateSystem",)

Expand Down Expand Up @@ -540,6 +545,45 @@ def from_axis_vectors(
t_axes = (1, 0) if mat.ndim == 2 else (1, 2, 0)
return cls(mat.transpose(t_axes), coordinates, time, time_ref)

@classmethod
def from_homogeneous_transformation(
cls,
transformation_matrix: types_homogeneous,
translation_unit: UnitLike,
time: types_time_like = None,
time_ref: types_timestamp_like = None,
) -> LocalCoordinateSystem:
"""Construct a local coordinate system from a homogeneous transformation matrix.

Parameters
----------
transformation_matrix :
Describes the homogeneous transformation matrix that includes the rotation
and the translation (coordinates).
translation_unit :
Unit describing the value of the translation. Necessary, because the
homogeneous transformation matrix is unitless.
time :
Time data for time dependent coordinate systems (Default value = None)
time_ref :
Optional reference timestamp if ``time`` is a time delta.

Returns
-------
LocalCoordinateSystem
Local coordinate system

"""
if isinstance(transformation_matrix, xr.DataArray):
transformation_matrix = np.array(transformation_matrix.data)
if transformation_matrix.ndim == 3:
orientation = transformation_matrix[:, :3, :3]
coordinates = Q_(transformation_matrix[:, :3, 3], translation_unit)
else:
orientation = transformation_matrix[:3, :3]
coordinates = Q_(transformation_matrix[:3, 3], translation_unit)
return cls(orientation, coordinates=coordinates, time=time, time_ref=time_ref)

@property
def orientation(self) -> xr.DataArray:
"""Get the coordinate systems orientation matrix.
Expand Down Expand Up @@ -690,6 +734,44 @@ def as_rotation(self) -> Rot: # pragma: no cover
"""
return Rot.from_matrix(self.orientation.values)

def as_homogeneous_matrix(self, translation_unit: UnitLike) -> np.ndarray:
"""Get a homogeneous transformation matrix from the coordinate system
orientation.

Parameters
----------
translation_unit : UnitLike
Unit the translation part of the homogeneous transformation matrix
should represent.

Returns
-------
numpy.ndarray
Numpy array representing the homogeneous transformation matrix.

"""

if self.is_time_dependent:
time_dim = self.time.shape[0]
else:
time_dim = 1

rotation = np.resize(self.orientation.data, (time_dim, 3, 3))
coordinates = self.coordinates
if not isinstance(coordinates, TimeSeries):
translation = np.resize(
coordinates.data.to(translation_unit).m, (time_dim, 3)
)
homogeneous_matrix = np.resize(np.identity(4), (time_dim, 4, 4))
homogeneous_matrix[:, :3, :3] = rotation
homogeneous_matrix[:, :3, 3] = translation

return np.squeeze(homogeneous_matrix)
else:
raise NotImplementedError(
"Cannot convert LCS with `TimeSeries` coordinates to homogeneous matrix"
)

def _interp_time_orientation(self, time: Time) -> xr.DataArray:
"""Interpolate the orientation in time."""
if "time" not in self.orientation.dims: # don't interpolate static
Expand Down
3 changes: 3 additions & 0 deletions weldx/transformations/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

from typing import Union

import numpy as np
import numpy.typing as npt
import pint
import xarray as xr
from scipy.spatial.transform import Rotation

types_coordinates = Union[xr.DataArray, npt.ArrayLike, pint.Quantity]
types_orientation = Union[xr.DataArray, npt.ArrayLike, Rotation]
types_homogeneous = Union[xr.DataArray, np.ndarray]


__all__ = [
"types_coordinates",
"types_orientation",
"types_homogeneous",
]
Loading