Shortcuts

Source code for pypose.function.spline

import torch
from . import is_SE3


[docs]def chspline(points, interval=0.1): r""" Cubic Hermite Spline, a piecewise-cubic interpolator matching values and first derivatives. Args: points (:obj:`Tensor`): the sequence of points for interpolation with shape [..., point_num, dim]. interval (:obj:`float`): the unit interval between interpolated points. We assume the interval of adjacent input points is 1. Therefore, if we set ``interval`` as ``0.1``, the interpolated points between the points at :math:`t` and :math:`t+1` will be :math:`[t, t+0.1, ..., t+0.9, t+1]`. Default: ``0.1``. Returns: ``Tensor``: the interpolated points. The interpolated points are evenly distributed between a start point and a end point according to the number of interpolated points. In this function, the interpolated points are also evenly distributed between the start point and end point. Denote the starting point :math:`p_0` with the starting tangent :math:`m_0` and the ending point :math:`p_1` with the ending tagent :math:`m_1`. The interpolated point at :math:`t` can be defined as: .. math:: \begin{aligned} p(t) &= (1-3t^2+2t^3)p_0 + (t-2t^2+t^3)m_0+(3t^2-2t^3)p_1+(-t^2+t^3)m_1\\ &= \begin{bmatrix} p_0, m_0, p_1, m_1 \end{bmatrix} \begin{bmatrix} 1& 0&-3& 2\\ 0& 1&-2& 1\\ 0& 0& 3&-2\\ 0& 0&-1& 1 \end{bmatrix} \begin{bmatrix}1\\ t\\ t^2\\ t^3\end{bmatrix} \end{aligned} Note: The implementation is based on wiki of `Cubic Hermite spline <https://en.wikipedia.org/wiki/Cubic_Hermite_spline>`_. Examples: >>> import torch >>> import pypose as pp >>> points = torch.tensor([[[0., 0., 0.], ... [1., .5, 0.1], ... [0., 1., 0.2], ... [1., 1.5, 0.4], ... [1.5, 0., 0.], ... [2., 1.5, 0.4], ... [2.5, 0., 0.], ... [1.75, 0.75, 0.2], ... [2.25, 0.75, 0.2], ... [3., 1.5, 0.4], ... [3., 0., 0.], ... [4., 0., 0.], ... [4., 1.5, 0.4], ... [5., 1., 0.2], ... [4., 0.75, 0.2], ... [5., 0., 0.]]]) >>> waypoints = pp.chspline(points, 0.1) .. figure:: /_static/img/module/liespline/CsplineR3.png :width: 600 Fig. 1. Result of Cubic Spline Interpolation in 3D space. """ assert points.dim() >= 2, "Dimension of points should be [..., N, C]" assert interval < 1.0, "The interval should be smaller than 1." batch, N = points.shape[:-2], points.shape[-2] dargs = {'device': points.device, 'dtype': points.dtype} intervals = torch.arange(0, 1, interval, **dargs) timeline = torch.arange(0, N, **dargs).unsqueeze(-1) timeline = (timeline + intervals).view(-1)[:-(intervals.shape[0]-1)] x = torch.arange(N, **dargs) idxs = torch.searchsorted(x[...,1:], timeline[..., :]) x = x.expand(batch+(-1,)) xs = timeline.expand(batch+(-1,)) m = (points[..., 1:, :] - points[..., :-1, :]) m /= (x[..., 1:] - x[..., :-1])[..., None] m = torch.cat([m[...,[0],:], (m[..., 1:,:] + m[..., :-1,:]) / 2, m[...,[-1],:]], -2) dx = x[..., idxs + 1] - x[..., idxs] t = (xs - x[..., idxs]) / dx alpha = torch.arange(4, **dargs) tt = t[..., None, :]**alpha[..., None] A = torch.tensor([[1, 0, -3, 2], [0, 1, -2, 1], [0, 0, 3, -2], [0, 0, -1, 1]], **dargs) hh = (A @ tt).mT interpoints = hh[..., :1] * points[..., idxs, :] interpoints += hh[..., 1:2] * m[..., idxs, :] * dx[..., None] interpoints += hh[..., 2:3] * points[..., idxs + 1, :] interpoints += hh[..., 3:4] * m[..., idxs + 1, :] * dx[..., None] return interpoints
[docs]def bspline(data, interval=0.1, extrapolate=False): r''' B-spline interpolation, which currently only supports SE3 LieTensor. Args: data (:obj:`LieTensor`): the input sparse poses with [batch_size, poses_num, dim] shape. interval (:obj:`float`): the unit interval between interpolated poses. We assume the interval of adjacent input poses is 1. Therefore, if we set ``interval`` as ``0.1``, the interpolated poses between the poses at :math:`t` and :math:`t+1` will be at :math:`[t, t+0.1, ..., t+0.9, t+1]`. Default: ``0.1``. extrapolate(``bool``): flag to determine whether the interpolate poses pass the start and end poses. Default: ``False``. Returns: :obj:`LieTensor`: the interpolated SE3 LieTensor. A poses query at :math:`t \in [t_i, t_{i+1})` (i.e. a segment of the spline) only relies on the poses at four steps :math:`\{t_{i-1},t_i,t_{i+1},t_{i+2}\}`. It means that the interpolation between adjacent poses needs four consecutive poses. In this function, the interpolated poses are evenly distributed between a pose query according to the interval parameter. The absolute pose of the spline :math:`T_{s}^w(t)`, where :math:`w` denotes the world and :math:`s` is the spline coordinate frame, can be calculated: .. math:: \begin{aligned} &T^w_s(t) = T^w_{i-1} \prod^{i+2}_{j=i}\delta T_j,\\ &\delta T_j= \exp(\lambda_{j-i}(t)\delta \hat{\xi}_j),\\ &\lambda(t) = MU(t), \end{aligned} :math:`M` is a predefined matrix, shown as follows: .. math:: M = \frac{1}{6}\begin{bmatrix} 5& 3& -3& 1 \\ 1& 3& 3& -2\\ 0& 0& 0& 1 \\ \end{bmatrix} :math:`U(t)` is a vector: .. math:: U(t) = \begin{bmatrix} 1\\ u(t)\\ u(t)^2\\ u(t)^3\\ \end{bmatrix} where :math:`u(t)=(t-t_i)/\Delta t`, :math:`\Delta t = t_{i+1} - t_{i}`. :math:`t_i` is the time point of the :math:`i_{th}` given pose. :math:`t` is the interpolation time point :math:`t \in [t_{i},t_{i+1})`. :math:`\delta \hat{\xi}_j` is the transformation between :math:`\hat{\xi}_{j-1}` and :math:`\hat{\xi}_{j}` .. math:: \begin{aligned} \delta \hat{\xi}_j :&= \log(\exp(\hat{\xi} _w^{j-1})\exp(\hat{\xi}_j^w)) &= \log(T_j^{j-1})\in \mathfrak{se3} \end{aligned} Note: The implementation is based on Eq. (3), (4), (5), and (6) of this paper: David Hug, et al., `HyperSLAM: A Generic and Modular Approach to Sensor Fusion and Simultaneous Localization And Mapping in Continuous-Time <https://ieeexplore.ieee.org/abstract/document/9320417>`_, 2020 International Conference on 3D Vision (3DV), Fukuoka, Japan, 2020. Examples: >>> import torch, pypose as pp >>> a1 = pp.euler2SO3(torch.Tensor([0., 0., 0.])) >>> a2 = pp.euler2SO3(torch.Tensor([torch.pi / 4., torch.pi / 3., torch.pi / 2.])) >>> poses = pp.SE3([[[0., 4., 0., a1[0], a1[1], a1[2], a1[3]], ... [0., 3., 0., a1[0], a1[1], a1[2], a1[3]], ... [0., 2., 0., a1[0], a1[1], a1[2], a1[3]], ... [0., 1., 0., a1[0], a1[1], a1[2], a1[3]], ... [1., 0., 1., a2[0], a2[1], a2[2], a2[3]], ... [2., 0., 1., a2[0], a2[1], a2[2], a2[3]], ... [3., 0., 1., a2[0], a2[1], a2[2], a2[3]], ... [4., 0., 1., a2[0], a2[1], a2[2], a2[3]]]]) >>> wayposes = pp.bspline(poses, 0.1) .. figure:: /_static/img/module/liespline/BsplineSE3.png Fig. 1. Result of B Spline Interpolation in SE3. ''' assert is_SE3(data), "The input poses are not SE3Type." assert data.dim() >= 2, "Dimension of data should be [..., N, C]." assert interval < 1.0, "The interval should be smaller than 1." batch = data.shape[:-2] if extrapolate: data = torch.cat((data[..., :1, :].expand(batch+(2,-1)), data, data[..., -1:, :].expand(batch+(2,-1))), dim=-2) else: assert data.shape[-2]>=4, "Number of poses is less than 4." Bth, N, D = data.shape[:-2], data.shape[-2], data.shape[-1] dargs = {'dtype': data.dtype, 'device': data.device} timeline = torch.arange(0, 1, interval, **dargs) tt = timeline ** torch.arange(4, **dargs).view(-1, 1) B = torch.tensor([[5, 3,-3, 1], [1, 3, 3,-2], [0, 0, 0, 1]], **dargs) / 6 dP = data[..., 0:-3, :].unsqueeze(-2) w = (B @ tt).unsqueeze(-1) index = (torch.arange(0, N-3).unsqueeze(-1) + torch.arange(0, 4)).view(-1) P = data[..., index, :].view(Bth + (N-3,4,1,D)) P = (P[..., :3, :, :].Inv() * P[..., 1:, :, :]).Log() A = (P * w).Exp() Aend = (P[..., -1, :] * ((B.sum(dim=1)).unsqueeze(-1))).Exp() Aend = Aend[..., [0], :] * Aend[..., [1], :] * Aend[..., [2], :] A = A[..., 0, :, :] * A[..., 1, :, :] * A[..., 2, :, :] ps, pend = dP * A,dP[...,-1,:,:]*Aend[...,-1,:,:] poses = torch.cat((ps.view(Bth + (-1, D)), pend), dim=-2) return poses

Docs

Access documentation for PyPose

View Docs

Tutorials

Get started with tutorials and examples

View Tutorials

Get Started

Find resources and how to start using pypose

View Resources