Module pyastrobee.utils.bag_frame
Tools for determining the (approximate) position/orientation of a volumetric softbody
Pybullet's built-in functions don't work well on softbodies (can't get reliable angular information) so this will help determine the state of the cargo bag
Here, we use the positions of the eight mesh vertices closest to the corners of the bag's main compartment to approximate the delta-x/y/z values, and then create an orthonormal basis from that
See the find_corner_verts script for more info on the corner-vertex identification process
View Source
"""Tools for determining the (approximate) position/orientation of a volumetric softbody
Pybullet's built-in functions don't work well on softbodies (can't get reliable angular information)
so this will help determine the state of the cargo bag
Here, we use the positions of the eight mesh vertices closest to the corners of the bag's main compartment
to approximate the delta-x/y/z values, and then create an orthonormal basis from that
See the find_corner_verts script for more info on the corner-vertex identification process
"""
import time
import numpy as np
import numpy.typing as npt
import pybullet
from pyastrobee.utils.transformations import make_transform_mat
from pyastrobee.utils.debug_visualizer import visualize_frame
from pyastrobee.utils.mesh_utils import get_mesh_data
from pyastrobee.utils.bullet_utils import load_deformable_object
from pyastrobee.config.bag_properties import TOP_HANDLE_BAG_CORNERS
# For indexing into the list of vertices
# Naming these as constants so their usage is clearer in the functions below
RIGHT_BACK_TOP = 0
RIGHT_BACK_BOT = 1
RIGHT_FRONT_TOP = 2
RIGHT_FRONT_BOT = 3
LEFT_BACK_TOP = 4
LEFT_BACK_BOT = 5
LEFT_FRONT_TOP = 6
LEFT_FRONT_BOT = 7
def get_x_axis(mesh: npt.ArrayLike, corners: list[int]) -> np.ndarray:
"""Calculate the x-axis of the bag frame based on averaging the length-wise vectors between corners
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Best-fit axis based on the corner positions, shape (3,)
"""
a = mesh[corners[RIGHT_BACK_TOP]] - mesh[corners[LEFT_BACK_TOP]]
b = mesh[corners[RIGHT_BACK_BOT]] - mesh[corners[LEFT_BACK_BOT]]
c = mesh[corners[RIGHT_FRONT_TOP]] - mesh[corners[LEFT_FRONT_TOP]]
d = mesh[corners[RIGHT_FRONT_BOT]] - mesh[corners[LEFT_FRONT_BOT]]
return np.average([a, b, c, d], axis=0)
def get_y_axis(mesh: npt.ArrayLike, corners: list[int]):
"""Calculate the y-axis of the bag frame based on averaging the width-wise vectors between corners
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Best-fit axis based on the corner positions, shape (3,)
"""
a = mesh[corners[RIGHT_BACK_TOP]] - mesh[corners[RIGHT_FRONT_TOP]]
b = mesh[corners[RIGHT_BACK_BOT]] - mesh[corners[RIGHT_FRONT_BOT]]
c = mesh[corners[LEFT_BACK_TOP]] - mesh[corners[LEFT_FRONT_TOP]]
d = mesh[corners[LEFT_BACK_BOT]] - mesh[corners[LEFT_FRONT_BOT]]
return np.average([a, b, c, d], axis=0)
def get_z_axis(mesh: npt.ArrayLike, corners: list[int]):
"""Calculate the z-axis of the bag frame based on averaging the height-wise vectors between corners
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Best-fit axis based on the corner positions, shape (3,)
"""
a = mesh[corners[RIGHT_BACK_TOP]] - mesh[corners[RIGHT_BACK_BOT]]
b = mesh[corners[RIGHT_FRONT_TOP]] - mesh[corners[RIGHT_FRONT_BOT]]
c = mesh[corners[LEFT_BACK_TOP]] - mesh[corners[LEFT_BACK_BOT]]
d = mesh[corners[LEFT_FRONT_TOP]] - mesh[corners[LEFT_FRONT_BOT]]
return np.average([a, b, c, d], axis=0)
def orthogonalize(
x: npt.ArrayLike, y: npt.ArrayLike, z: npt.ArrayLike
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Turn three linearly independent vectors into an orthogonal/orthonormal basis
We assume that the direction of x is correct, and base the other calculations on this
Args:
x (npt.ArrayLike): Initial x axis, shape (3,)
y (npt.ArrayLike): Initial y axis, shape (3,)
z (npt.ArrayLike): Initial z axis, shape (3,)
Returns:
Tuple[np.ndarray, np.ndarray, np.ndarray]: The new normalized + orthogonal x, y, and z axes
"""
x_new = x / np.linalg.norm(x)
y_new = np.cross(z, x)
y_new = y_new / np.linalg.norm(y_new)
z_new = np.cross(x, y)
z_new = z_new / np.linalg.norm(z_new)
return x_new, y_new, z_new
def get_bag_frame(mesh: npt.ArrayLike, corners: list[int]) -> np.ndarray:
"""Determine the transformation matrix for the cargo bag frame
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Transformation matrix, shape (4, 4)
"""
x = get_x_axis(mesh, corners)
y = get_y_axis(mesh, corners)
z = get_z_axis(mesh, corners)
R = np.column_stack(orthogonalize(x, y, z))
origin = np.average(mesh, axis=0)
return make_transform_mat(R, origin)
if __name__ == "__main__":
pybullet.connect(pybullet.GUI)
pybullet.resetSimulation(pybullet.RESET_USE_DEFORMABLE_WORLD)
filename = "pyastrobee/assets/meshes/bags/top_handle.vtk"
bag_id = load_deformable_object(filename, bending_stiffness=10)
while True:
n_verts, bag_mesh = get_mesh_data(bag_id)
T = get_bag_frame(bag_mesh, TOP_HANDLE_BAG_CORNERS)
visualize_frame(T, lifetime=1)
pybullet.stepSimulation()
time.sleep(1 / 240)
Variables
LEFT_BACK_BOT
LEFT_BACK_TOP
LEFT_FRONT_BOT
LEFT_FRONT_TOP
RIGHT_BACK_BOT
RIGHT_BACK_TOP
RIGHT_FRONT_BOT
RIGHT_FRONT_TOP
TOP_HANDLE_BAG_CORNERS
Functions
get_bag_frame
def get_bag_frame(
mesh: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]],
corners: list[int]
) -> numpy.ndarray
Determine the transformation matrix for the cargo bag frame
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| mesh | npt.ArrayLike | Bag mesh vertex positions, shape (num_verts, 3) | None |
| corners | list[int] | Indices of the 8 vertices closest to the corners of the bag | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Transformation matrix, shape (4, 4) |
View Source
def get_bag_frame(mesh: npt.ArrayLike, corners: list[int]) -> np.ndarray:
"""Determine the transformation matrix for the cargo bag frame
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Transformation matrix, shape (4, 4)
"""
x = get_x_axis(mesh, corners)
y = get_y_axis(mesh, corners)
z = get_z_axis(mesh, corners)
R = np.column_stack(orthogonalize(x, y, z))
origin = np.average(mesh, axis=0)
return make_transform_mat(R, origin)
get_x_axis
def get_x_axis(
mesh: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]],
corners: list[int]
) -> numpy.ndarray
Calculate the x-axis of the bag frame based on averaging the length-wise vectors between corners
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| mesh | npt.ArrayLike | Bag mesh vertex positions, shape (num_verts, 3) | None |
| corners | list[int] | Indices of the 8 vertices closest to the corners of the bag | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Best-fit axis based on the corner positions, shape (3,) |
View Source
def get_x_axis(mesh: npt.ArrayLike, corners: list[int]) -> np.ndarray:
"""Calculate the x-axis of the bag frame based on averaging the length-wise vectors between corners
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Best-fit axis based on the corner positions, shape (3,)
"""
a = mesh[corners[RIGHT_BACK_TOP]] - mesh[corners[LEFT_BACK_TOP]]
b = mesh[corners[RIGHT_BACK_BOT]] - mesh[corners[LEFT_BACK_BOT]]
c = mesh[corners[RIGHT_FRONT_TOP]] - mesh[corners[LEFT_FRONT_TOP]]
d = mesh[corners[RIGHT_FRONT_BOT]] - mesh[corners[LEFT_FRONT_BOT]]
return np.average([a, b, c, d], axis=0)
get_y_axis
def get_y_axis(
mesh: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]],
corners: list[int]
)
Calculate the y-axis of the bag frame based on averaging the width-wise vectors between corners
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| mesh | npt.ArrayLike | Bag mesh vertex positions, shape (num_verts, 3) | None |
| corners | list[int] | Indices of the 8 vertices closest to the corners of the bag | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Best-fit axis based on the corner positions, shape (3,) |
View Source
def get_y_axis(mesh: npt.ArrayLike, corners: list[int]):
"""Calculate the y-axis of the bag frame based on averaging the width-wise vectors between corners
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Best-fit axis based on the corner positions, shape (3,)
"""
a = mesh[corners[RIGHT_BACK_TOP]] - mesh[corners[RIGHT_FRONT_TOP]]
b = mesh[corners[RIGHT_BACK_BOT]] - mesh[corners[RIGHT_FRONT_BOT]]
c = mesh[corners[LEFT_BACK_TOP]] - mesh[corners[LEFT_FRONT_TOP]]
d = mesh[corners[LEFT_BACK_BOT]] - mesh[corners[LEFT_FRONT_BOT]]
return np.average([a, b, c, d], axis=0)
get_z_axis
def get_z_axis(
mesh: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]],
corners: list[int]
)
Calculate the z-axis of the bag frame based on averaging the height-wise vectors between corners
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| mesh | npt.ArrayLike | Bag mesh vertex positions, shape (num_verts, 3) | None |
| corners | list[int] | Indices of the 8 vertices closest to the corners of the bag | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Best-fit axis based on the corner positions, shape (3,) |
View Source
def get_z_axis(mesh: npt.ArrayLike, corners: list[int]):
"""Calculate the z-axis of the bag frame based on averaging the height-wise vectors between corners
Args:
mesh (npt.ArrayLike): Bag mesh vertex positions, shape (num_verts, 3)
corners (list[int]): Indices of the 8 vertices closest to the corners of the bag
Returns:
np.ndarray: Best-fit axis based on the corner positions, shape (3,)
"""
a = mesh[corners[RIGHT_BACK_TOP]] - mesh[corners[RIGHT_BACK_BOT]]
b = mesh[corners[RIGHT_FRONT_TOP]] - mesh[corners[RIGHT_FRONT_BOT]]
c = mesh[corners[LEFT_BACK_TOP]] - mesh[corners[LEFT_BACK_BOT]]
d = mesh[corners[LEFT_FRONT_TOP]] - mesh[corners[LEFT_FRONT_BOT]]
return np.average([a, b, c, d], axis=0)
orthogonalize
def orthogonalize(
x: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]],
y: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]],
z: Union[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]], numpy._typing._nested_sequence._NestedSequence[numpy._typing._array_like._SupportsArray[numpy.dtype[Any]]], bool, int, float, complex, str, bytes, numpy._typing._nested_sequence._NestedSequence[Union[bool, int, float, complex, str, bytes]]]
) -> tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]
Turn three linearly independent vectors into an orthogonal/orthonormal basis
We assume that the direction of x is correct, and base the other calculations on this
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| x | npt.ArrayLike | Initial x axis, shape (3,) | None |
| y | npt.ArrayLike | Initial y axis, shape (3,) | None |
| z | npt.ArrayLike | Initial z axis, shape (3,) | None |
Returns:
| Type | Description |
|---|---|
| Tuple[np.ndarray, np.ndarray, np.ndarray] | The new normalized + orthogonal x, y, and z axes |
View Source
def orthogonalize(
x: npt.ArrayLike, y: npt.ArrayLike, z: npt.ArrayLike
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Turn three linearly independent vectors into an orthogonal/orthonormal basis
We assume that the direction of x is correct, and base the other calculations on this
Args:
x (npt.ArrayLike): Initial x axis, shape (3,)
y (npt.ArrayLike): Initial y axis, shape (3,)
z (npt.ArrayLike): Initial z axis, shape (3,)
Returns:
Tuple[np.ndarray, np.ndarray, np.ndarray]: The new normalized + orthogonal x, y, and z axes
"""
x_new = x / np.linalg.norm(x)
y_new = np.cross(z, x)
y_new = y_new / np.linalg.norm(y_new)
z_new = np.cross(x, y)
z_new = z_new / np.linalg.norm(z_new)
return x_new, y_new, z_new