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')
[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]:
[7]:
p0 = NamedExpression('pbm0', _.subs(t, (t - t0) / (t1 - t0)))
p0
[7]:
Velocity = Tangent Vector = Derivative:
[8]:
pd0 = p0.diff(t)
pd0
[8]:
similar to notebook about non-uniform Hermite splines
[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))
[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)))
Acceleration = Second Derivative
[14]:
pdd0 = pd0.diff(t)
pdd0
[14]:
[15]:
pdd0.evaluated_at(t, t0)
[15]:
[16]:
sp.Eq(_.expr, 0).subs(coefficients_begin)
[16]:
[17]:
xd0 = NamedExpression.solve(_, 'xbmdot0')
xd0.subs(delta_begin)
[17]:
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]:
[21]:
p8 = NamedExpression('pbm8', _.subs(t, (t - t8) / (t9 - t8)))
p8
[21]:
[22]:
pd8 = p8.diff(t)
pd8
[22]:
[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))
[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)))
[28]:
pdd8 = pd8.diff(t)
pdd8
[28]:
second derivative at the end of the last segment:
[29]:
pdd8.evaluated_at(t, t9)
[29]:
[30]:
sp.Eq(_.expr, 0).subs(coefficients_end)
[30]:
[31]:
xd9 = NamedExpression.solve(_, 'xbmdot9')
xd9.subs(delta_end)
[31]:
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]));
[33]:
<sympy.plotting.plot.Plot at 0x7f635dabdbd0>