blob: 2f8c83665ce8c34ef8124f7a7fb40265aea8747f [file] [log] [blame]
Austin Schuh35d19872018-11-30 15:50:47 +11001#!/usr/bin/python
2
3from __future__ import print_function
4
5import numpy
6import sys
7from matplotlib import pylab
8import glog
9import gflags
10
11"""This file is my playground for implementing spline following."""
12
13FLAGS = gflags.FLAGS
14
15
16def spline(alpha, control_points):
17 """Computes a Bezier curve.
18
19 Args:
20 alpha: scalar or list of spline parameters to calculate the curve at.
21 control_points: n x 4 matrix of control points. n[:, 0] is the
22 starting point, and n[:, 3] is the ending point.
23
24 Returns:
25 n x m matrix of spline points. n is the dimension of the control
26 points, and m is the number of points in 'alpha'.
27 """
28 if numpy.isscalar(alpha):
29 alpha = [alpha]
30 alpha_matrix = [[(1.0 - a)**3.0, 3.0 * (1.0 - a)**2.0 * a,
31 3.0 * (1.0 - a) * a**2.0, a**3.0] for a in alpha]
32
33 return control_points * numpy.matrix(alpha_matrix).T
34
35
36def dspline(alpha, control_points):
37 """Computes the derivitive of a Bezier curve wrt alpha.
38
39 Args:
40 alpha: scalar or list of spline parameters to calculate the curve at.
41 control_points: n x 4 matrix of control points. n[:, 0] is the
42 starting point, and n[:, 3] is the ending point.
43
44 Returns:
45 n x m matrix of spline point derivatives. n is the dimension of the
46 control points, and m is the number of points in 'alpha'.
47 """
48 if numpy.isscalar(alpha):
49 alpha = [alpha]
50 dalpha_matrix = [[
51 -3.0 * (1.0 - a)**2.0, 3.0 * (1.0 - a)**2.0 + -2.0 * 3.0 *
52 (1.0 - a) * a, -3.0 * a**2.0 + 2.0 * 3.0 * (1.0 - a) * a, 3.0 * a**2.0
53 ] for a in alpha]
54
55 return control_points * numpy.matrix(dalpha_matrix).T
56
57
58def ddspline(alpha, control_points):
59 """Computes the second derivitive of a Bezier curve wrt alpha.
60
61 Args:
62 alpha: scalar or list of spline parameters to calculate the curve at.
63 control_points: n x 4 matrix of control points. n[:, 0] is the
64 starting point, and n[:, 3] is the ending point.
65
66 Returns:
67 n x m matrix of spline point second derivatives. n is the dimension of
68 the control points, and m is the number of points in 'alpha'.
69 """
70 if numpy.isscalar(alpha):
71 alpha = [alpha]
72 ddalpha_matrix = [[
73 2.0 * 3.0 * (1.0 - a),
74 -2.0 * 3.0 * (1.0 - a) + -2.0 * 3.0 * (1.0 - a) + 2.0 * 3.0 * a,
75 -2.0 * 3.0 * a + 2.0 * 3.0 * (1.0 - a) - 2.0 * 3.0 * a,
76 2.0 * 3.0 * a
77 ] for a in alpha]
78
79 return control_points * numpy.matrix(ddalpha_matrix).T
80
81
82def dddspline(alpha, control_points):
83 """Computes the third derivitive of a Bezier curve wrt alpha.
84
85 Args:
86 alpha: scalar or list of spline parameters to calculate the curve at.
87 control_points: n x 4 matrix of control points. n[:, 0] is the
88 starting point, and n[:, 3] is the ending point.
89
90 Returns:
91 n x m matrix of spline point second derivatives. n is the dimension of
92 the control points, and m is the number of points in 'alpha'.
93 """
94 if numpy.isscalar(alpha):
95 alpha = [alpha]
96 ddalpha_matrix = [[
97 -2.0 * 3.0,
98 2.0 * 3.0 + 2.0 * 3.0 + 2.0 * 3.0,
99 -2.0 * 3.0 - 2.0 * 3.0 - 2.0 * 3.0,
100 2.0 * 3.0
101 ] for a in alpha]
102
103 return control_points * numpy.matrix(ddalpha_matrix).T
104
105
106def spline_theta(alpha, control_points, dspline_points=None):
107 """Computes the heading of a robot following a Bezier curve at alpha.
108
109 Args:
110 alpha: scalar or list of spline parameters to calculate the heading at.
111 control_points: n x 4 matrix of control points. n[:, 0] is the
112 starting point, and n[:, 3] is the ending point.
113
114 Returns:
115 m array of spline point headings. m is the number of points in 'alpha'.
116 """
117 if dspline_points is None:
118 dspline_points = dspline(alphas, control_points)
119
120 return numpy.arctan2(
121 numpy.array(dspline_points)[1, :], numpy.array(dspline_points)[0, :])
122
123
124def dspline_theta(alphas,
125 control_points,
126 dspline_points=None,
127 ddspline_points=None):
128 """Computes the derivitive of the heading at alpha.
129
130 This is the derivitive of spline_theta wrt alpha.
131
132 Args:
133 alpha: scalar or list of spline parameters to calculate the derivative
134 of the heading at.
135 control_points: n x 4 matrix of control points. n[:, 0] is the
136 starting point, and n[:, 3] is the ending point.
137
138 Returns:
139 m array of spline point heading derivatives. m is the number of points
140 in 'alpha'.
141 """
142 if dspline_points is None:
143 dspline_points = dspline(alphas, control_points)
144
145 if ddspline_points is None:
146 ddspline_points = ddspline(alphas, control_points)
147
148 dx = numpy.array(dspline_points)[0, :]
149 dy = numpy.array(dspline_points)[1, :]
150
151 ddx = numpy.array(ddspline_points)[0, :]
152 ddy = numpy.array(ddspline_points)[1, :]
153
154 return 1.0 / (dx**2.0 + dy**2.0) * (dx * ddy - dy * ddx)
155
156
157def ddspline_theta(alphas,
158 control_points,
159 dspline_points=None,
160 ddspline_points=None,
161 dddspline_points=None):
162 """Computes the second derivitive of the heading at alpha.
163
164 This is the second derivitive of spline_theta wrt alpha.
165
166 Args:
167 alpha: scalar or list of spline parameters to calculate the second
168 derivative of the heading at.
169 control_points: n x 4 matrix of control points. n[:, 0] is the
170 starting point, and n[:, 3] is the ending point.
171
172 Returns:
173 m array of spline point heading second derivatives. m is the number of
174 points in 'alpha'.
175 """
176 if dspline_points is None:
177 dspline_points = dspline(alphas, control_points)
178
179 if ddspline_points is None:
180 ddspline_points = ddspline(alphas, control_points)
181
182 if dddspline_points is None:
183 dddspline_points = dddspline(alphas, control_points)
184
185 dddspline_points = dddspline(alphas, control_points)
186
187 dx = numpy.array(dspline_points)[0, :]
188 dy = numpy.array(dspline_points)[1, :]
189
190 ddx = numpy.array(ddspline_points)[0, :]
191 ddy = numpy.array(ddspline_points)[1, :]
192
193 dddx = numpy.array(dddspline_points)[0, :]
194 dddy = numpy.array(dddspline_points)[1, :]
195
196 return -1.0 / ((dx**2.0 + dy**2.0)**2.0) * (dx * ddy - dy * ddx) * 2.0 * (
197 dy * ddy + dx * ddx) + 1.0 / (dx**2.0 + dy**2.0) * (dx * dddy - dy *
198 dddx)
199
200
201def main(argv):
202 # Build up the control point matrix
203 start = numpy.matrix([[0.0, 0.0]]).T
204 c1 = numpy.matrix([[0.5, 0.0]]).T
205 c2 = numpy.matrix([[0.5, 1.0]]).T
206 end = numpy.matrix([[1.0, 1.0]]).T
207 control_points = numpy.hstack((start, c1, c2, end))
208
209 # The alphas to plot
210 alphas = numpy.linspace(0.0, 1.0, 1000)
211
212 # Compute x, y and the 3 derivatives
213 spline_points = spline(alphas, control_points)
214 dspline_points = dspline(alphas, control_points)
215 ddspline_points = ddspline(alphas, control_points)
216 dddspline_points = dddspline(alphas, control_points)
217
218 # Compute theta and the two derivatives
219 theta = spline_theta(alphas, control_points, dspline_points=dspline_points)
220 dtheta = dspline_theta(alphas, control_points, dspline_points=dspline_points)
221 ddtheta = ddspline_theta(
222 alphas,
223 control_points,
224 dspline_points=dspline_points,
225 dddspline_points=dddspline_points)
226
227 # Plot the control points and the spline.
228 pylab.figure()
229 pylab.plot(
230 numpy.array(control_points)[0, :],
231 numpy.array(control_points)[1, :],
232 '-o',
233 label='control')
234 pylab.plot(
235 numpy.array(spline_points)[0, :],
236 numpy.array(spline_points)[1, :],
237 label='spline')
238 pylab.legend()
239
240 # For grins, confirm that the double integral of the acceleration (with
241 # respect to the spline parameter) matches the position. This lets us
242 # confirm that the derivatives are consistent.
243 xint_plot = numpy.matrix(numpy.zeros((2, len(alphas))))
244 dxint_plot = xint_plot.copy()
245 xint = spline_points[:, 0].copy()
246 dxint = dspline_points[:, 0].copy()
247 xint_plot[:, 0] = xint
248 dxint_plot[:, 0] = dxint
249 for i in range(len(alphas) - 1):
250 xint += (alphas[i + 1] - alphas[i]) * dxint
251 dxint += (alphas[i + 1] - alphas[i]) * ddspline_points[:, i]
252 xint_plot[:, i + 1] = xint
253 dxint_plot[:, i + 1] = dxint
254
255 # Integrate up the spline velocity and heading to confirm that given a
256 # velocity (as a function of the spline parameter) and angle, we will move
257 # from the starting point to the ending point.
258 thetaint_plot = numpy.zeros((len(alphas),))
259 thetaint = theta[0]
260 dthetaint_plot = numpy.zeros((len(alphas),))
261 dthetaint = dtheta[0]
262 thetaint_plot[0] = thetaint
263 dthetaint_plot[0] = dthetaint
264
265 txint_plot = numpy.matrix(numpy.zeros((2, len(alphas))))
266 txint = spline_points[:, 0].copy()
267 txint_plot[:, 0] = txint
268 for i in range(len(alphas) - 1):
269 dalpha = alphas[i + 1] - alphas[i]
270 txint += dalpha * numpy.linalg.norm(
271 dspline_points[:, i]) * numpy.matrix(
272 [[numpy.cos(theta[i])], [numpy.sin(theta[i])]])
273 txint_plot[:, i + 1] = txint
274 thetaint += dalpha * dtheta[i]
275 dthetaint += dalpha * ddtheta[i]
276 thetaint_plot[i + 1] = thetaint
277 dthetaint_plot[i + 1] = dthetaint
278
279
280 # Now plot x, dx/dalpha, ddx/ddalpha, dddx/dddalpha, and integrals thereof
281 # to perform consistency checks.
282 pylab.figure()
283 pylab.plot(alphas, numpy.array(spline_points)[0, :], label='x')
284 pylab.plot(alphas, numpy.array(xint_plot)[0, :], label='ix')
285 pylab.plot(alphas, numpy.array(dspline_points)[0, :], label='dx')
286 pylab.plot(alphas, numpy.array(dxint_plot)[0, :], label='idx')
287 pylab.plot(alphas, numpy.array(txint_plot)[0, :], label='tix')
288 pylab.plot(alphas, numpy.array(ddspline_points)[0, :], label='ddx')
289 pylab.plot(alphas, numpy.array(dddspline_points)[0, :], label='dddx')
290 pylab.legend()
291
292 # Now do the same for y.
293 pylab.figure()
294 pylab.plot(alphas, numpy.array(spline_points)[1, :], label='y')
295 pylab.plot(alphas, numpy.array(xint_plot)[1, :], label='iy')
296 pylab.plot(alphas, numpy.array(dspline_points)[1, :], label='dy')
297 pylab.plot(alphas, numpy.array(dxint_plot)[1, :], label='idy')
298 pylab.plot(alphas, numpy.array(txint_plot)[1, :], label='tiy')
299 pylab.plot(alphas, numpy.array(ddspline_points)[1, :], label='ddy')
300 pylab.plot(alphas, numpy.array(dddspline_points)[1, :], label='dddy')
301 pylab.legend()
302
303 # And for theta.
304 pylab.figure()
305 pylab.plot(alphas, theta, label='theta')
306 pylab.plot(alphas, dtheta, label='dtheta')
307 pylab.plot(alphas, ddtheta, label='ddtheta')
308 pylab.plot(alphas, thetaint_plot, label='thetai')
309 pylab.plot(alphas, dthetaint_plot, label='dthetai')
310
311 # TODO(austin): Start creating a velocity plan now that we have all the
312 # derivitives of our spline.
313
314 pylab.legend()
315 pylab.show()
316
317
318if __name__ == '__main__':
319 argv = FLAGS(sys.argv)
320 sys.exit(main(argv))