Module pyastrobee.trajectories.box_paths
Different ways of characterizing paths between regions of the environment
These are mainly used for timing heuristics for splines right now - for instance, using a simple method of traversing the environment to create an estimate of how long we will spend in each region
View Source
"""Different ways of characterizing paths between regions of the environment
These are mainly used for timing heuristics for splines right now - for instance,
using a simple method of traversing the environment to create an estimate of how
long we will spend in each region
"""
import time
import numpy as np
import numpy.typing as npt
import cvxpy as cp
import pybullet
from pyastrobee.utils.boxes import Box, intersect_boxes, visualize_3D_box
from pyastrobee.utils.debug_visualizer import visualize_path, visualize_points
from pyastrobee.utils.errors import OptimizationError
def min_length_path(
start_pt: npt.ArrayLike, end_pt: npt.ArrayLike, boxes: list[Box]
) -> np.ndarray:
"""Determine the minimum-length path between two points through a sequence of safe boxes
Args:
start_pt (npt.ArrayLike): Starting XYZ position, shape (3,)
end_pt (npt.ArrayLike): Ending XYZ position, shape (3,)
boxes (list[Box]): Sequence of safe boxes to pass through
Returns:
np.ndarray: Path from start to end, shape (n_boxes + 1, 3)
"""
n_boxes = len(boxes)
points = cp.Variable((n_boxes + 1, 3))
pathlength = cp.sum(cp.norm2(cp.diff(points, axis=0), axis=1))
objective = cp.Minimize(pathlength)
constraints = [points[0] == start_pt, points[-1] == end_pt]
for i, box in enumerate(boxes):
lower, upper = box
constraints.append(points[i] >= lower)
constraints.append(points[i] <= upper)
constraints.append(points[i + 1] >= lower)
constraints.append(points[i + 1] <= upper)
prob = cp.Problem(objective, constraints)
prob.solve()
if prob.status != cp.OPTIMAL:
raise OptimizationError(
"Unable to find a solution.\n"
+ "Check that the set of boxes is connected and that the start/end points are contained in the boxes"
)
return points.value
def centerpoint_path(
start_pt: npt.ArrayLike, end_pt: npt.ArrayLike, boxes: list[Box]
) -> np.ndarray:
"""Determine a path between two points, using the center points of boxes and their intersections as waypoints
Args:
start_pt (npt.ArrayLike): Starting XYZ position, shape (3,)
end_pt (npt.ArrayLike): Ending XYZ position, shape (3,)
boxes (list[Box]): Sequence of safe boxes to pass through
Returns:
np.ndarray: Path from start to end, shape (2 * n_boxes + 1, 3)
"""
n_boxes = len(boxes)
# One point per box, one point per box intersection, and two points for start/end
n_points = 2 * n_boxes + 1
points = np.empty((n_points, 3))
points[0] = start_pt
points[-1] = end_pt
for i in range(n_boxes):
points[2 * i + 1] = boxes[i].center
if i < n_boxes - 1: # Not the last box
points[2 * i + 2] = intersect_boxes(boxes[i], boxes[i + 1]).center
return points
def intersection_path(
start_pt: npt.ArrayLike, end_pt: npt.ArrayLike, boxes: list[Box]
) -> np.ndarray:
"""Determine a path between two points, using the intersections between consecutive boxes as waypoints
Args:
start_pt (npt.ArrayLike): Starting XYZ position, shape (3,)
end_pt (npt.ArrayLike): Ending XYZ position, shape (3,)
boxes (list[Box]): Sequence of safe boxes to pass through
Returns:
np.ndarray: Path from start to end, shape (n_boxes + 1, 3)
"""
n_boxes = len(boxes)
n_points = n_boxes + 1
points = np.empty((n_points, 3))
points[0] = start_pt
points[-1] = end_pt
for i in range(n_boxes - 1):
points[i + 1] = intersect_boxes(boxes[i], boxes[i + 1]).center
return points
def _test_path_methods():
start_pt = [0.1, 0.2, 0.3]
end_pt = [1.5, 5, 1.7]
boxes = [
Box((0, 0, 0), (1, 1, 1)),
Box((0.5, 0.5, 0.5), (1.5, 5, 1.5)),
Box((1, 4.5, 1), (2, 5.5, 2)),
]
time_a = time.time()
min_length_solution = min_length_path(start_pt, end_pt, boxes)
time_b = time.time()
centerpoint_solution = centerpoint_path(start_pt, end_pt, boxes)
time_c = time.time()
intersection_solution = intersection_path(start_pt, end_pt, boxes)
time_d = time.time()
print("min length time: ", time_b - time_a)
print("centerpoint time: ", time_c - time_b)
print("intersection time: ", time_d - time_c)
pybullet.connect(pybullet.GUI)
min_length_color = (1, 0, 0)
centerpoint_color = (0, 1, 0)
intersection_color = (0, 0, 1)
visualize_path(min_length_solution, color=min_length_color)
visualize_path(centerpoint_solution, color=centerpoint_color)
visualize_path(intersection_solution, color=intersection_color)
visualize_points(min_length_solution, color=min_length_color)
visualize_points(centerpoint_solution, color=centerpoint_color)
visualize_points(intersection_solution, color=intersection_color)
for box in boxes:
visualize_3D_box(box)
pybullet.addUserDebugText("Min length", [0, 0, -0.2], min_length_color)
pybullet.addUserDebugText("Centerpoint", [0, 0, -0.4], centerpoint_color)
pybullet.addUserDebugText("Intersection", [0, 0, -0.6], intersection_color)
input("Press Enter to close")
if __name__ == "__main__":
_test_path_methods()
Functions
centerpoint_path
def centerpoint_path(
start_pt: 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]]],
end_pt: 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]]],
boxes: list[pyastrobee.utils.boxes.Box]
) -> numpy.ndarray
Determine a path between two points, using the center points of boxes and their intersections as waypoints
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| start_pt | npt.ArrayLike | Starting XYZ position, shape (3,) | None |
| end_pt | npt.ArrayLike | Ending XYZ position, shape (3,) | None |
| boxes | list[Box] | Sequence of safe boxes to pass through | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Path from start to end, shape (2 * n_boxes + 1, 3) |
View Source
def centerpoint_path(
start_pt: npt.ArrayLike, end_pt: npt.ArrayLike, boxes: list[Box]
) -> np.ndarray:
"""Determine a path between two points, using the center points of boxes and their intersections as waypoints
Args:
start_pt (npt.ArrayLike): Starting XYZ position, shape (3,)
end_pt (npt.ArrayLike): Ending XYZ position, shape (3,)
boxes (list[Box]): Sequence of safe boxes to pass through
Returns:
np.ndarray: Path from start to end, shape (2 * n_boxes + 1, 3)
"""
n_boxes = len(boxes)
# One point per box, one point per box intersection, and two points for start/end
n_points = 2 * n_boxes + 1
points = np.empty((n_points, 3))
points[0] = start_pt
points[-1] = end_pt
for i in range(n_boxes):
points[2 * i + 1] = boxes[i].center
if i < n_boxes - 1: # Not the last box
points[2 * i + 2] = intersect_boxes(boxes[i], boxes[i + 1]).center
return points
intersection_path
def intersection_path(
start_pt: 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]]],
end_pt: 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]]],
boxes: list[pyastrobee.utils.boxes.Box]
) -> numpy.ndarray
Determine a path between two points, using the intersections between consecutive boxes as waypoints
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| start_pt | npt.ArrayLike | Starting XYZ position, shape (3,) | None |
| end_pt | npt.ArrayLike | Ending XYZ position, shape (3,) | None |
| boxes | list[Box] | Sequence of safe boxes to pass through | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Path from start to end, shape (n_boxes + 1, 3) |
View Source
def intersection_path(
start_pt: npt.ArrayLike, end_pt: npt.ArrayLike, boxes: list[Box]
) -> np.ndarray:
"""Determine a path between two points, using the intersections between consecutive boxes as waypoints
Args:
start_pt (npt.ArrayLike): Starting XYZ position, shape (3,)
end_pt (npt.ArrayLike): Ending XYZ position, shape (3,)
boxes (list[Box]): Sequence of safe boxes to pass through
Returns:
np.ndarray: Path from start to end, shape (n_boxes + 1, 3)
"""
n_boxes = len(boxes)
n_points = n_boxes + 1
points = np.empty((n_points, 3))
points[0] = start_pt
points[-1] = end_pt
for i in range(n_boxes - 1):
points[i + 1] = intersect_boxes(boxes[i], boxes[i + 1]).center
return points
min_length_path
def min_length_path(
start_pt: 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]]],
end_pt: 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]]],
boxes: list[pyastrobee.utils.boxes.Box]
) -> numpy.ndarray
Determine the minimum-length path between two points through a sequence of safe boxes
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
| start_pt | npt.ArrayLike | Starting XYZ position, shape (3,) | None |
| end_pt | npt.ArrayLike | Ending XYZ position, shape (3,) | None |
| boxes | list[Box] | Sequence of safe boxes to pass through | None |
Returns:
| Type | Description |
|---|---|
| np.ndarray | Path from start to end, shape (n_boxes + 1, 3) |
View Source
def min_length_path(
start_pt: npt.ArrayLike, end_pt: npt.ArrayLike, boxes: list[Box]
) -> np.ndarray:
"""Determine the minimum-length path between two points through a sequence of safe boxes
Args:
start_pt (npt.ArrayLike): Starting XYZ position, shape (3,)
end_pt (npt.ArrayLike): Ending XYZ position, shape (3,)
boxes (list[Box]): Sequence of safe boxes to pass through
Returns:
np.ndarray: Path from start to end, shape (n_boxes + 1, 3)
"""
n_boxes = len(boxes)
points = cp.Variable((n_boxes + 1, 3))
pathlength = cp.sum(cp.norm2(cp.diff(points, axis=0), axis=1))
objective = cp.Minimize(pathlength)
constraints = [points[0] == start_pt, points[-1] == end_pt]
for i, box in enumerate(boxes):
lower, upper = box
constraints.append(points[i] >= lower)
constraints.append(points[i] <= upper)
constraints.append(points[i + 1] >= lower)
constraints.append(points[i + 1] <= upper)
prob = cp.Problem(objective, constraints)
prob.solve()
if prob.status != cp.OPTIMAL:
raise OptimizationError(
"Unable to find a solution.\n"
+ "Check that the set of boxes is connected and that the start/end points are contained in the boxes"
)
return points.value