This page was generated from doc/euclidean/end-conditions-natural.ipynb. Interactive online version: Binder badge.

Natural End Conditions§

For the first and last segment, we assume that the inner tangent is known. We try to find the outer tangent by setting the second derivative to \(0\).

We are looking only at the non-uniform case here, it’s easy to get to the uniform case by setting \(\Delta_i = 1\).

natural (a.k.a. “relaxed”?)

[1]:
import sympy as sp
sp.init_printing(order='rev-lex')

utility.py

[2]:
from utility import NamedExpression
[3]:
t = sp.symbols('t')

Begin§

first polynomial segment: \(\boldsymbol{p}_0(t)\), \(t \in [t_0, t_1]\)

[4]:
t0, t1 = sp.symbols('t:2')
[5]:
a0, b0, c0, d0 = sp.symbols('a:dbm0')
[6]:
d0 * t**3 + c0 * t**2 + b0 * t + a0
[6]:
$\displaystyle \boldsymbol{d}_{0} t^{3} + \boldsymbol{c}_{0} t^{2} + \boldsymbol{b}_{0} t + \boldsymbol{a}_{0}$
[7]:
p0 = NamedExpression('pbm0', _.subs(t, (t - t0) / (t1 - t0)))
p0
[7]:
$\displaystyle \boldsymbol{p}_{0} = \frac{\boldsymbol{d}_{0} \left(- t_{0} + t\right)^{3}}{\left(t_{1} - t_{0}\right)^{3}} + \frac{\boldsymbol{c}_{0} \left(- t_{0} + t\right)^{2}}{\left(t_{1} - t_{0}\right)^{2}} + \frac{\boldsymbol{b}_{0} \left(- t_{0} + t\right)}{t_{1} - t_{0}} + \boldsymbol{a}_{0}$

Velocity = Tangent Vector = Derivative:

[8]:
pd0 = p0.diff(t)
pd0
[8]:
$\displaystyle \frac{d}{d t} \boldsymbol{p}_{0} = \frac{3 \boldsymbol{d}_{0} \left(- t_{0} + t\right)^{2}}{\left(t_{1} - t_{0}\right)^{3}} + \frac{\boldsymbol{c}_{0} \left(- 2 t_{0} + 2 t\right)}{\left(t_{1} - t_{0}\right)^{2}} + \frac{\boldsymbol{b}_{0}}{t_{1} - t_{0}}$

similar to notebook about non-uniform Hermite splines

\begin{align} \boldsymbol{x}_0 &= \boldsymbol{p}_0(t_0)\\ \boldsymbol{x}_1 &= \boldsymbol{p}_0(t_1)\\ \boldsymbol{\dot{x}}_0 &= \boldsymbol{p}_0'(t_0)\\ \boldsymbol{\dot{x}}_1 &= \boldsymbol{p}_0'(t_1) \end{align}
[9]:
equations_begin = [
    p0.evaluated_at(t, t0).with_name('xbm0'),
    p0.evaluated_at(t, t1).with_name('xbm1'),
    pd0.evaluated_at(t, t0).with_name('xbmdot0'),
    pd0.evaluated_at(t, t1).with_name('xbmdot1'),
]

only for display purposes, the calculations are still done with \(t_i\)

[10]:
delta_begin = [
    (t0, 0),
    (t1, sp.Symbol('Delta0')),
]
[11]:
for e in equations_begin:
    display(e.subs(delta_begin))
$\displaystyle \boldsymbol{x}_{0} = \boldsymbol{a}_{0}$
$\displaystyle \boldsymbol{x}_{1} = \boldsymbol{d}_{0} + \boldsymbol{c}_{0} + \boldsymbol{b}_{0} + \boldsymbol{a}_{0}$
$\displaystyle \dot{\boldsymbol{x}}_{0} = \frac{\boldsymbol{b}_{0}}{\Delta_{0}}$
$\displaystyle \dot{\boldsymbol{x}}_{1} = \frac{3 \boldsymbol{d}_{0}}{\Delta_{0}} + \frac{2 \boldsymbol{c}_{0}}{\Delta_{0}} + \frac{\boldsymbol{b}_{0}}{\Delta_{0}}$
[12]:
coefficients_begin = sp.solve(equations_begin, [a0, b0, c0, d0])
[13]:
for c, e in coefficients_begin.items():
    display(NamedExpression(c, e.subs(delta_begin)))
$\displaystyle \boldsymbol{a}_{0} = \boldsymbol{x}_{0}$
$\displaystyle \boldsymbol{b}_{0} = \Delta_{0} \dot{\boldsymbol{x}}_{0}$
$\displaystyle \boldsymbol{c}_{0} = 3 \boldsymbol{x}_{1} - 3 \boldsymbol{x}_{0} - \Delta_{0} \dot{\boldsymbol{x}}_{1} - 2 \Delta_{0} \dot{\boldsymbol{x}}_{0}$
$\displaystyle \boldsymbol{d}_{0} = - 2 \boldsymbol{x}_{1} + 2 \boldsymbol{x}_{0} + \Delta_{0} \dot{\boldsymbol{x}}_{1} + \Delta_{0} \dot{\boldsymbol{x}}_{0}$

Acceleration = Second Derivative

[14]:
pdd0 = pd0.diff(t)
pdd0
[14]:
$\displaystyle \frac{d^{2}}{d t^{2}} \boldsymbol{p}_{0} = \frac{3 \boldsymbol{d}_{0} \left(- 2 t_{0} + 2 t\right)}{\left(t_{1} - t_{0}\right)^{3}} + \frac{2 \boldsymbol{c}_{0}}{\left(t_{1} - t_{0}\right)^{2}}$
[15]:
pdd0.evaluated_at(t, t0)
[15]:
$\displaystyle \left.{\frac{d^{2}}{d t^{2}} \boldsymbol{p}_{0}}\right\rvert_{t=t_{0}} = \frac{2 \boldsymbol{c}_{0}}{\left(t_{1} - t_{0}\right)^{2}}$
[16]:
sp.Eq(_.expr, 0).subs(coefficients_begin)
[16]:
$\displaystyle \frac{2 \left(3 \boldsymbol{x}_{1} - 3 \boldsymbol{x}_{0} - t_{1} \dot{\boldsymbol{x}}_{1} - 2 t_{1} \dot{\boldsymbol{x}}_{0} + t_{0} \dot{\boldsymbol{x}}_{1} + 2 t_{0} \dot{\boldsymbol{x}}_{0}\right)}{\left(t_{1} - t_{0}\right)^{2}} = 0$
[17]:
xd0 = NamedExpression.solve(_, 'xbmdot0')
xd0.subs(delta_begin)
[17]:
$\displaystyle \dot{\boldsymbol{x}}_{0} = - \frac{- 3 \boldsymbol{x}_{1} + 3 \boldsymbol{x}_{0} + \Delta_{0} \dot{\boldsymbol{x}}_{1}}{2 \Delta_{0}}$

End§

\(N\) vertices, \(N-1\) polynomial segments

last polynomial: \(\boldsymbol{p}_{N-2}(t)\), \(t \in [t_{N-2}, t_{N-1}]\)

To simplify the notation a bit, let’s assume we have \(N = 10\) vertices, which makes \(\boldsymbol{p}_8\) the last polynomial segment.

[18]:
a8, b8, c8, d8 = sp.symbols('a:dbm8')
[19]:
t8, t9 = sp.symbols('t8:10')
[20]:
d8 * t**3 + c8 * t**2 + b8 * t + a8
[20]:
$\displaystyle \boldsymbol{d}_{8} t^{3} + \boldsymbol{c}_{8} t^{2} + \boldsymbol{b}_{8} t + \boldsymbol{a}_{8}$
[21]:
p8 = NamedExpression('pbm8', _.subs(t, (t - t8) / (t9 - t8)))
p8
[21]:
$\displaystyle \boldsymbol{p}_{8} = \frac{\boldsymbol{d}_{8} \left(- t_{8} + t\right)^{3}}{\left(t_{9} - t_{8}\right)^{3}} + \frac{\boldsymbol{c}_{8} \left(- t_{8} + t\right)^{2}}{\left(t_{9} - t_{8}\right)^{2}} + \frac{\boldsymbol{b}_{8} \left(- t_{8} + t\right)}{t_{9} - t_{8}} + \boldsymbol{a}_{8}$
[22]:
pd8 = p8.diff(t)
pd8
[22]:
$\displaystyle \frac{d}{d t} \boldsymbol{p}_{8} = \frac{3 \boldsymbol{d}_{8} \left(- t_{8} + t\right)^{2}}{\left(t_{9} - t_{8}\right)^{3}} + \frac{\boldsymbol{c}_{8} \left(- 2 t_{8} + 2 t\right)}{\left(t_{9} - t_{8}\right)^{2}} + \frac{\boldsymbol{b}_{8}}{t_{9} - t_{8}}$
\begin{align} \boldsymbol{x}_{N-2} &= \boldsymbol{p}_{N-2}(t_{N-2})\\ \boldsymbol{x}_{N-1} &= \boldsymbol{p}_{N-2}(t_{N-1})\\ \boldsymbol{\dot{x}}_{N-2} &= \boldsymbol{p}_{N-2}'(t_{N-2})\\ \boldsymbol{\dot{x}}_{N-1} &= \boldsymbol{p}_{N-2}'(t_{N-1}) \end{align}
[23]:
equations_end = [
    p8.evaluated_at(t, t8).with_name('xbm8'),
    p8.evaluated_at(t, t9).with_name('xbm9'),
    pd8.evaluated_at(t, t8).with_name('xbmdot8'),
    pd8.evaluated_at(t, t9).with_name('xbmdot9'),
]
[24]:
delta_end = [
    (t8, 0),
    (t9, sp.Symbol('Delta8')),
]
[25]:
for e in equations_end:
    display(e.subs(delta_end))
$\displaystyle \boldsymbol{x}_{8} = \boldsymbol{a}_{8}$
$\displaystyle \boldsymbol{x}_{9} = \boldsymbol{d}_{8} + \boldsymbol{c}_{8} + \boldsymbol{b}_{8} + \boldsymbol{a}_{8}$
$\displaystyle \dot{\boldsymbol{x}}_{8} = \frac{\boldsymbol{b}_{8}}{\Delta_{8}}$
$\displaystyle \dot{\boldsymbol{x}}_{9} = \frac{3 \boldsymbol{d}_{8}}{\Delta_{8}} + \frac{2 \boldsymbol{c}_{8}}{\Delta_{8}} + \frac{\boldsymbol{b}_{8}}{\Delta_{8}}$
[26]:
coefficients_end = sp.solve(equations_end, [a8, b8, c8, d8])
[27]:
for c, e in coefficients_end.items():
    display(NamedExpression(c, e.subs(delta_end)))
$\displaystyle \boldsymbol{a}_{8} = \boldsymbol{x}_{8}$
$\displaystyle \boldsymbol{b}_{8} = \Delta_{8} \dot{\boldsymbol{x}}_{8}$
$\displaystyle \boldsymbol{c}_{8} = 3 \boldsymbol{x}_{9} - 3 \boldsymbol{x}_{8} - \Delta_{8} \dot{\boldsymbol{x}}_{9} - 2 \Delta_{8} \dot{\boldsymbol{x}}_{8}$
$\displaystyle \boldsymbol{d}_{8} = - 2 \boldsymbol{x}_{9} + 2 \boldsymbol{x}_{8} + \Delta_{8} \dot{\boldsymbol{x}}_{9} + \Delta_{8} \dot{\boldsymbol{x}}_{8}$
[28]:
pdd8 = pd8.diff(t)
pdd8
[28]:
$\displaystyle \frac{d^{2}}{d t^{2}} \boldsymbol{p}_{8} = \frac{3 \boldsymbol{d}_{8} \left(- 2 t_{8} + 2 t\right)}{\left(t_{9} - t_{8}\right)^{3}} + \frac{2 \boldsymbol{c}_{8}}{\left(t_{9} - t_{8}\right)^{2}}$

second derivative at the end of the last segment:

[29]:
pdd8.evaluated_at(t, t9)
[29]:
$\displaystyle \left.{\frac{d^{2}}{d t^{2}} \boldsymbol{p}_{8}}\right\rvert_{t=t_{9}} = \frac{3 \boldsymbol{d}_{8} \left(2 t_{9} - 2 t_{8}\right)}{\left(t_{9} - t_{8}\right)^{3}} + \frac{2 \boldsymbol{c}_{8}}{\left(t_{9} - t_{8}\right)^{2}}$
[30]:
sp.Eq(_.expr, 0).subs(coefficients_end)
[30]:
$\displaystyle \frac{2 \left(3 \boldsymbol{x}_{9} - 3 \boldsymbol{x}_{8} - t_{9} \dot{\boldsymbol{x}}_{9} - 2 t_{9} \dot{\boldsymbol{x}}_{8} + t_{8} \dot{\boldsymbol{x}}_{9} + 2 t_{8} \dot{\boldsymbol{x}}_{8}\right)}{\left(t_{9} - t_{8}\right)^{2}} + \frac{3 \left(2 t_{9} - 2 t_{8}\right) \left(- 2 \boldsymbol{x}_{9} + 2 \boldsymbol{x}_{8} + t_{9} \dot{\boldsymbol{x}}_{9} + t_{9} \dot{\boldsymbol{x}}_{8} - t_{8} \dot{\boldsymbol{x}}_{9} - t_{8} \dot{\boldsymbol{x}}_{8}\right)}{\left(t_{9} - t_{8}\right)^{3}} = 0$
[31]:
xd9 = NamedExpression.solve(_, 'xbmdot9')
xd9.subs(delta_end)
[31]:
$\displaystyle \dot{\boldsymbol{x}}_{9} = - \frac{- 3 \boldsymbol{x}_{9} + 3 \boldsymbol{x}_{8} + \Delta_{8} \dot{\boldsymbol{x}}_{8}}{2 \Delta_{8}}$

Luckily, that’s symmetric to the result we got above.

Example§

one-dimensional; 3 time/value pairs are given. The slope for the middle value is given, the begin and end slopes are calculated using the “natural” end conditions as calculated above.

[32]:
values = 2, 2, 2
times = 0, 4, 5
slope = 2
[33]:
sp.plot((
    p0.subs(coefficients_begin).subs_symbols(xd0).expr.subs({
        t0: times[0],
        t1: times[1],
        sp.Symbol('xbm0'): values[0],
        sp.Symbol('xbm1'): values[1],
        sp.Symbol('xbmdot1'): slope,
    }),
    (t, times[0], times[1])
), (
    p8.subs(coefficients_end).subs_symbols(xd9).expr.subs({
        t8: times[1],
        t9: times[2],
        sp.Symbol('xbm8'): values[1],
        sp.Symbol('xbm9'): values[2],
        sp.Symbol('xbmdot8'): slope,
    }),
    (t, times[1], times[2])
), axis_center=(0, values[1]));
../_images/euclidean_end-conditions-natural_48_0.svg
[33]:
<sympy.plotting.plot.Plot at 0x7f635dabdbd0>