#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# File : frame_utils_xyzw.py
# Author : Jiayuan Mao
# Email : maojiayuan@gmail.com
# Date : 02/18/2020
#
# This file is part of Project Concepts.
# Distributed under terms of the MIT license.
"""Utilities for frame transformations in the XYZW convention. It focuses on transformation matrices and quaternions used in robotics."""
import numpy as np
from typing import Tuple, TYPE_CHECKING
from concepts.math.rotationlib_xyzw import quat_mul, quat_conjugate, rotate_vector, mat2quat, quat2mat, axisangle2quat
from concepts.utils.typing_utils import Vec3f, Vec4f
if TYPE_CHECKING:
from concepts.simulator.pybullet.client import BulletClient
__all__ = [
'calc_transformation_matrix_from_plane_equation',
'mat2posquat', 'posquat2mat',
'compose_transformation', 'inverse_transformation', 'get_transform_a_to_b',
'frame_mul', 'frame_inv',
'solve_tool_from_ee', 'solve_ee_from_tool',
'calc_ee_rotation_mat_from_directions', 'calc_ee_quat_from_directions',
]
[docs]
def mat2posquat(mat: np.ndarray) -> Tuple[Vec3f, Vec4f]:
"""Convert a 4x4 transformation matrix to position and quaternion.
The quaternion is represented as a 4-element array in the order of (x, y, z, w), which follows the convention of pybullet.
"""
mat = np.asarray(mat, dtype=np.float64)
pos = mat[:3, 3]
quat = mat2quat(mat[:3, :3])
return pos, quat
[docs]
def posquat2mat(pos: Vec3f, quat: Vec4f) -> np.ndarray:
"""Convert position and quaternion to a 4x4 transformation matrix.
The quaternion is represented as a 4-element array in the order of (x, y, z, w), which follows the convention of pybullet.
"""
pos = np.asarray(pos, dtype=np.float64)
quat = np.asarray(quat, dtype=np.float64)
mat = np.eye(4)
mat[:3, :3] = quat2mat(quat)
mat[:3, 3] = pos
return mat
[docs]
def frame_mul(pos_a: Vec3f, quat_a: Vec4f, a_to_b: Tuple[Vec3f, Vec4f]) -> Tuple[np.ndarray, np.ndarray]:
"""Multiply a frame with a transformation.
The frame is represented as a tuple of position and quaternion.
The transformation is represented as a tuple of position and quaternion.
The quaternion is represented as a 4-element array in the order of (x, y, z, w), which follows the convention of pybullet.
"""
pos_a = np.asarray(pos_a, dtype=np.float64)
quat_a = np.asarray(quat_a, dtype=np.float64)
transform_pos = np.asarray(a_to_b[0], dtype=np.float64)
transform_quat = np.asarray(a_to_b[1], dtype=np.float64)
pos_b = pos_a + rotate_vector(transform_pos, quat_a)
quat_b = quat_mul(quat_a, transform_quat)
return pos_b, quat_b
[docs]
def frame_inv(pos_b: Vec3f, quat_b: Vec4f, a_to_b: Tuple[Vec3f, Vec4f]) -> Tuple[np.ndarray, np.ndarray]:
"""Inverse a frame with a transformation.
The frame is represented as a tuple of position and quaternion.
The transformation is represented as a tuple of position and quaternion.
The quaternion is represented as a 4-element array in the order of (x, y, z, w), which follows the convention of pybullet.
"""
pos_b = np.asarray(pos_b, dtype=np.float64)
quat_b = np.asarray(quat_b, dtype=np.float64)
transform_pos = np.asarray(a_to_b[0], dtype=np.float64)
transform_quat = np.asarray(a_to_b[1], dtype=np.float64)
inv_transform_quat = quat_conjugate(transform_quat)
quat_a = quat_mul(quat_b, inv_transform_quat)
pos_a = pos_b - rotate_vector(transform_pos, quat_a)
return pos_a, quat_a
[docs]
def calc_ee_rotation_mat_from_directions(u: Vec3f, v: Vec3f) -> np.ndarray:
"""Compute the rotation matrix from two directions (the "down" direction for the end effector and the "forward" direction for the end effector).
Args:
u: The "down" direction for the end effector.
v: The "forward" direction for the end effector.
"""
u = np.asarray(u)
u = u / np.linalg.norm(u)
v = np.asarray(v)
v = v - np.dot(u, v) * u
v = v / np.linalg.norm(v)
w = np.cross(u, v)
return np.array([u, v, w]).T
[docs]
def calc_ee_quat_from_directions(u: Vec3f, v: Vec3f, default_quat: Vec4f = (0, 1, 0, 0)) -> np.ndarray:
"""Compute the quaternion from two directions (the "down" direction for the end effector and the "forward" direction for the end effector).
Args:
u: the "down" direction for the end effector.
v: the "forward" direction for the end effector.
default_quat: the default quaternion to be multiplied. This is the quaternion that represents the rotation for the default end effector orientation,
facing downwards and the forward direction is along the x-axis.
"""
mat = calc_ee_rotation_mat_from_directions(u, v)
mat_reference = calc_ee_rotation_mat_from_directions((0, 0, -1), (1, 0, 0))
quat = mat2quat(np.dot(mat, np.linalg.inv(mat_reference)))
return quat_mul(quat, default_quat)