Source code for pyrobopath.toolpath.path.smoothing
from typing import List, Sequence
import numpy as np
from pyrobopath.tools.types import ArrayLike3, NDArray
from pyrobopath.tools.linalg import unit_vector3, angle_between
from pyrobopath.toolpath.path import Path, CubicBSplineSegment
from pyrobopath.toolpath.path.transform import Rotation, Transform
def _continuous_cubic_splines(
control_points: List[ArrayLike3],
) -> List[CubicBSplineSegment]:
v = control_points
return [
CubicBSplineSegment(v[i : (i + 4)], Rotation(), Rotation())
for i in range(len(v) - 3)
]
def _entry_length(p0: NDArray, p1: NDArray, p2: NDArray, tol: float) -> float:
return 3 * tol / np.cos(angle_between(p0 - p1, p2 - p1) / 2)
[docs]
def smooth_cubic_bspline(path: List[Transform], tol: float) -> Sequence[Path]:
"""Smooth a piecewise linear path within tolerance `tol` using cubic splines
:param path: a list of SE(3) poses representing piecewise linear paths
:type path: List[SE3]
:param tol: the maximum distance allowed between the generated curves and
the corner points of the path
:type tol: float
:return: a list of G2 continuous B-spline paths
:rtype: Sequence[Path]
"""
# blend first segment
p0 = path[0].t
p1 = path[1].t
p2 = path[2].t
ls_p1 = _entry_length(p0, p1, p2, tol)
if np.linalg.norm(p1 - p0) > ls_p1:
v1 = p0
v3 = p1
v2 = p1 + ls_p1 * unit_vector3(p0 - p1)
v0 = 2 * v1 - v2
v = [v0, v1, v2, v3]
else:
v1 = p0
v2 = p1
v0 = 2 * v1 - v2
v = [v0, v1, v2]
# blend corners
for i in range(1, len(path) - 2):
pk_m1 = path[i - 1].t
pk = path[i].t
pk_p1 = path[i + 1].t
pk_p2 = path[i + 2].t
dist = np.linalg.norm(pk_p1 - pk)
l_pk = _entry_length(pk_m1, pk, pk_p1, tol)
l_pk_p1 = _entry_length(pk, pk_p1, pk_p2, tol)
uk_kp1 = unit_vector3(pk_p1 - pk)
if dist > (l_pk + l_pk_p1):
vi = pk + uk_kp1 * l_pk
vi_p1 = pk_p1 - uk_kp1 * l_pk_p1
vi_p2 = pk_p1
v.extend(([vi, vi_p1, vi_p2]))
elif min(l_pk, l_pk_p1) < dist <= (l_pk + l_pk_p1):
le_pk = l_pk / (l_pk + l_pk_p1) * dist
vi = pk + uk_kp1 * le_pk
vi_p1 = pk_p1
v.extend([vi, vi_p1])
else:
v.append(pk_p1)
# blend last segment
l_p1 = _entry_length(path[-3].t, path[-2].t, path[-1].t, tol)
if np.linalg.norm(path[-1].t - path[-2].t) > l_p1:
v2 = path[-2].t + l_p1 * unit_vector3(path[-1].t - path[-2].t)
v.append(v2)
else:
v2 = path[-2].t
v1 = path[-1].t
v0 = 2 * v1 - v2
v.extend([v1, v0])
return _continuous_cubic_splines(v)