blob: 44f9be6670cadc9231a2254d84143a66df0b112b [file] [log] [blame]
Austin Schuh085eab92020-11-26 13:54:51 -08001#!/usr/bin/python3
Austin Schuh35d19872018-11-30 15:50:47 +11002
3from __future__ import print_function
4
Austin Schuh35d19872018-11-30 15:50:47 +11005from matplotlib import pylab
Austin Schuh35d19872018-11-30 15:50:47 +11006import gflags
Austin Schuh387a6872018-12-01 19:14:33 +11007import glog
8import numpy
9import scipy
10import scipy.integrate
Austin Schuh7b1e9df2018-12-19 18:04:41 +110011import scipy.optimize
Austin Schuh387a6872018-12-01 19:14:33 +110012import sys
Austin Schuh35d19872018-11-30 15:50:47 +110013
Austin Schuh44aa9142018-12-03 21:07:23 +110014from frc971.control_loops.python import polydrivetrain
Austin Schuh8cb98eb2018-12-05 22:06:45 +110015from frc971.control_loops.python import drivetrain
Austin Schuh7b1e9df2018-12-19 18:04:41 +110016from frc971.control_loops.python import controls
17import y2016.control_loops.python.drivetrain
Austin Schuh387a6872018-12-01 19:14:33 +110018"""This file is my playground for implementing spline following.
19
20All splines here are cubic bezier splines. See
21 https://en.wikipedia.org/wiki/B%C3%A9zier_curve for more details.
22"""
Austin Schuh35d19872018-11-30 15:50:47 +110023
24FLAGS = gflags.FLAGS
25
26
Austin Schuh8cb98eb2018-12-05 22:06:45 +110027def RungeKutta(f, y0, t, h, count=1):
28 """4th order RungeKutta integration of dy/dt = f(t, y) starting at X."""
29 y1 = y0
30 dh = h / float(count)
Austin Schuh5ea48472021-02-02 20:46:41 -080031 for x in range(count):
Austin Schuh8cb98eb2018-12-05 22:06:45 +110032 k1 = dh * f(t + dh * x, y1)
33 k2 = dh * f(t + dh * x + dh / 2.0, y1 + k1 / 2.0)
34 k3 = dh * f(t + dh * x + dh / 2.0, y1 + k2 / 2.0)
35 k4 = dh * f(t + dh * x + dh, y1 + k3)
36 y1 += (k1 + 2.0 * k2 + 2.0 * k3 + k4) / 6.0
37 return y1
38
39
Austin Schuh35d19872018-11-30 15:50:47 +110040def spline(alpha, control_points):
Austin Schuh387a6872018-12-01 19:14:33 +110041 """Computes a Cubic Bezier curve.
Austin Schuh35d19872018-11-30 15:50:47 +110042
43 Args:
44 alpha: scalar or list of spline parameters to calculate the curve at.
45 control_points: n x 4 matrix of control points. n[:, 0] is the
46 starting point, and n[:, 3] is the ending point.
47
48 Returns:
49 n x m matrix of spline points. n is the dimension of the control
50 points, and m is the number of points in 'alpha'.
51 """
52 if numpy.isscalar(alpha):
53 alpha = [alpha]
54 alpha_matrix = [[(1.0 - a)**3.0, 3.0 * (1.0 - a)**2.0 * a,
55 3.0 * (1.0 - a) * a**2.0, a**3.0] for a in alpha]
56
57 return control_points * numpy.matrix(alpha_matrix).T
58
59
60def dspline(alpha, control_points):
Austin Schuh387a6872018-12-01 19:14:33 +110061 """Computes the derivative of a Cubic Bezier curve wrt alpha.
Austin Schuh35d19872018-11-30 15:50:47 +110062
63 Args:
64 alpha: scalar or list of spline parameters to calculate the curve at.
65 control_points: n x 4 matrix of control points. n[:, 0] is the
66 starting point, and n[:, 3] is the ending point.
67
68 Returns:
69 n x m matrix of spline point derivatives. n is the dimension of the
70 control points, and m is the number of points in 'alpha'.
71 """
72 if numpy.isscalar(alpha):
73 alpha = [alpha]
74 dalpha_matrix = [[
John Park91e69732019-03-03 13:12:43 -080075 -3.0 * (1.0 - a)**2.0,
76 3.0 * (1.0 - a)**2.0 + -2.0 * 3.0 * (1.0 - a) * a,
77 -3.0 * a**2.0 + 2.0 * 3.0 * (1.0 - a) * a, 3.0 * a**2.0
Austin Schuh35d19872018-11-30 15:50:47 +110078 ] for a in alpha]
79
80 return control_points * numpy.matrix(dalpha_matrix).T
81
82
83def ddspline(alpha, control_points):
Austin Schuh387a6872018-12-01 19:14:33 +110084 """Computes the second derivative of a Cubic Bezier curve wrt alpha.
Austin Schuh35d19872018-11-30 15:50:47 +110085
86 Args:
87 alpha: scalar or list of spline parameters to calculate the curve at.
88 control_points: n x 4 matrix of control points. n[:, 0] is the
89 starting point, and n[:, 3] is the ending point.
90
91 Returns:
92 n x m matrix of spline point second derivatives. n is the dimension of
93 the control points, and m is the number of points in 'alpha'.
94 """
95 if numpy.isscalar(alpha):
96 alpha = [alpha]
97 ddalpha_matrix = [[
98 2.0 * 3.0 * (1.0 - a),
99 -2.0 * 3.0 * (1.0 - a) + -2.0 * 3.0 * (1.0 - a) + 2.0 * 3.0 * a,
John Park91e69732019-03-03 13:12:43 -0800100 -2.0 * 3.0 * a + 2.0 * 3.0 * (1.0 - a) - 2.0 * 3.0 * a, 2.0 * 3.0 * a
Austin Schuh35d19872018-11-30 15:50:47 +1100101 ] for a in alpha]
102
103 return control_points * numpy.matrix(ddalpha_matrix).T
104
105
106def dddspline(alpha, control_points):
Austin Schuh387a6872018-12-01 19:14:33 +1100107 """Computes the third derivative of a Cubic Bezier curve wrt alpha.
Austin Schuh35d19872018-11-30 15:50:47 +1100108
109 Args:
110 alpha: scalar or list of spline parameters to calculate the curve 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 n x m matrix of spline point second derivatives. n is the dimension of
116 the control points, and m is the number of points in 'alpha'.
117 """
118 if numpy.isscalar(alpha):
119 alpha = [alpha]
120 ddalpha_matrix = [[
John Park91e69732019-03-03 13:12:43 -0800121 -2.0 * 3.0, 2.0 * 3.0 + 2.0 * 3.0 + 2.0 * 3.0,
122 -2.0 * 3.0 - 2.0 * 3.0 - 2.0 * 3.0, 2.0 * 3.0
Austin Schuh35d19872018-11-30 15:50:47 +1100123 ] for a in alpha]
124
125 return control_points * numpy.matrix(ddalpha_matrix).T
126
127
128def spline_theta(alpha, control_points, dspline_points=None):
Austin Schuh387a6872018-12-01 19:14:33 +1100129 """Computes the heading of a robot following a Cubic Bezier curve at alpha.
Austin Schuh35d19872018-11-30 15:50:47 +1100130
131 Args:
132 alpha: scalar or list of spline parameters to calculate the heading at.
133 control_points: n x 4 matrix of control points. n[:, 0] is the
134 starting point, and n[:, 3] is the ending point.
135
136 Returns:
137 m array of spline point headings. m is the number of points in 'alpha'.
138 """
139 if dspline_points is None:
Austin Schuh387a6872018-12-01 19:14:33 +1100140 dspline_points = dspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100141
142 return numpy.arctan2(
John Park91e69732019-03-03 13:12:43 -0800143 numpy.array(dspline_points)[1, :],
144 numpy.array(dspline_points)[0, :])
Austin Schuh35d19872018-11-30 15:50:47 +1100145
146
Austin Schuh387a6872018-12-01 19:14:33 +1100147def dspline_theta(alpha,
Austin Schuh35d19872018-11-30 15:50:47 +1100148 control_points,
149 dspline_points=None,
150 ddspline_points=None):
Austin Schuh387a6872018-12-01 19:14:33 +1100151 """Computes the derivative of the heading at alpha.
Austin Schuh35d19872018-11-30 15:50:47 +1100152
Austin Schuh387a6872018-12-01 19:14:33 +1100153 This is the derivative of spline_theta wrt alpha.
Austin Schuh35d19872018-11-30 15:50:47 +1100154
155 Args:
156 alpha: scalar or list of spline parameters to calculate the derivative
157 of the heading at.
158 control_points: n x 4 matrix of control points. n[:, 0] is the
159 starting point, and n[:, 3] is the ending point.
160
161 Returns:
162 m array of spline point heading derivatives. m is the number of points
163 in 'alpha'.
164 """
165 if dspline_points is None:
Austin Schuh387a6872018-12-01 19:14:33 +1100166 dspline_points = dspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100167
168 if ddspline_points is None:
Austin Schuh387a6872018-12-01 19:14:33 +1100169 ddspline_points = ddspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100170
171 dx = numpy.array(dspline_points)[0, :]
172 dy = numpy.array(dspline_points)[1, :]
173
174 ddx = numpy.array(ddspline_points)[0, :]
175 ddy = numpy.array(ddspline_points)[1, :]
176
177 return 1.0 / (dx**2.0 + dy**2.0) * (dx * ddy - dy * ddx)
178
179
Austin Schuh387a6872018-12-01 19:14:33 +1100180def ddspline_theta(alpha,
Austin Schuh35d19872018-11-30 15:50:47 +1100181 control_points,
182 dspline_points=None,
183 ddspline_points=None,
184 dddspline_points=None):
Austin Schuh387a6872018-12-01 19:14:33 +1100185 """Computes the second derivative of the heading at alpha.
Austin Schuh35d19872018-11-30 15:50:47 +1100186
Austin Schuh387a6872018-12-01 19:14:33 +1100187 This is the second derivative of spline_theta wrt alpha.
Austin Schuh35d19872018-11-30 15:50:47 +1100188
189 Args:
190 alpha: scalar or list of spline parameters to calculate the second
191 derivative of the heading at.
192 control_points: n x 4 matrix of control points. n[:, 0] is the
193 starting point, and n[:, 3] is the ending point.
194
195 Returns:
196 m array of spline point heading second derivatives. m is the number of
197 points in 'alpha'.
198 """
199 if dspline_points is None:
Austin Schuh387a6872018-12-01 19:14:33 +1100200 dspline_points = dspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100201
202 if ddspline_points is None:
Austin Schuh387a6872018-12-01 19:14:33 +1100203 ddspline_points = ddspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100204
205 if dddspline_points is None:
Austin Schuh387a6872018-12-01 19:14:33 +1100206 dddspline_points = dddspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100207
Austin Schuh387a6872018-12-01 19:14:33 +1100208 dddspline_points = dddspline(alpha, control_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100209
210 dx = numpy.array(dspline_points)[0, :]
211 dy = numpy.array(dspline_points)[1, :]
212
213 ddx = numpy.array(ddspline_points)[0, :]
214 ddy = numpy.array(ddspline_points)[1, :]
215
216 dddx = numpy.array(dddspline_points)[0, :]
217 dddy = numpy.array(dddspline_points)[1, :]
218
219 return -1.0 / ((dx**2.0 + dy**2.0)**2.0) * (dx * ddy - dy * ddx) * 2.0 * (
Ravago Jones26f7ad02021-02-05 15:45:59 -0800220 dy * ddy + dx * ddx) + 1.0 / (dx**2.0 + dy**2.0) * (
221 dx * dddy - dy * dddx)
Austin Schuh35d19872018-11-30 15:50:47 +1100222
223
Austin Schuh387a6872018-12-01 19:14:33 +1100224class Path(object):
225 """Represents a path to follow."""
Ravago Jones26f7ad02021-02-05 15:45:59 -0800226
Austin Schuh387a6872018-12-01 19:14:33 +1100227 def __init__(self, control_points):
228 """Constructs a path given the control points."""
229 self._control_points = control_points
230
231 def spline_velocity(alpha):
Ravago Jones26f7ad02021-02-05 15:45:59 -0800232 return numpy.linalg.norm(
233 dspline(alpha, self._control_points), axis=0)
Austin Schuh387a6872018-12-01 19:14:33 +1100234
235 self._point_distances = [0.0]
236 num_alpha = 100
237 # Integrate the xy velocity as a function of alpha for each step in the
238 # table to get an alpha -> distance calculation. Gaussian Quadrature
239 # is quite accurate, so we can get away with fewer points here than we
240 # might think.
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100241 for alpha in numpy.linspace(0.0, 1.0, num_alpha)[1:]:
Austin Schuh387a6872018-12-01 19:14:33 +1100242 self._point_distances.append(
John Park91e69732019-03-03 13:12:43 -0800243 scipy.integrate.fixed_quad(spline_velocity, alpha, alpha +
244 1.0 / (num_alpha - 1.0))[0] +
Austin Schuh387a6872018-12-01 19:14:33 +1100245 self._point_distances[-1])
246
247 def distance_to_alpha(self, distance):
248 """Converts distances along the spline to alphas.
249
250 Args:
251 distance: A scalar or array of distances to convert
252
253 Returns:
254 An array of distances, (1 big if the input was a scalar)
255 """
256 if numpy.isscalar(distance):
257 return numpy.array([self._distance_to_alpha_scalar(distance)])
258 else:
John Park91e69732019-03-03 13:12:43 -0800259 return numpy.array(
260 [self._distance_to_alpha_scalar(d) for d in distance])
Austin Schuh387a6872018-12-01 19:14:33 +1100261
262 def _distance_to_alpha_scalar(self, distance):
263 """Helper to compute alpha for a distance for a single scalar."""
264 if distance <= 0.0:
265 return 0.0
266 elif distance >= self.length():
267 return 1.0
Ravago Jones26f7ad02021-02-05 15:45:59 -0800268 after_index = numpy.searchsorted(
269 self._point_distances, distance, side='right')
Austin Schuh387a6872018-12-01 19:14:33 +1100270 before_index = after_index - 1
271
272 # Linearly interpolate alpha from our (sorted) distance table.
273 return (distance - self._point_distances[before_index]) / (
274 self._point_distances[after_index] -
John Park91e69732019-03-03 13:12:43 -0800275 self._point_distances[before_index]) * (
Ravago Jones26f7ad02021-02-05 15:45:59 -0800276 1.0 / (len(self._point_distances) - 1.0)
277 ) + float(before_index) / (len(self._point_distances) - 1.0)
Austin Schuh387a6872018-12-01 19:14:33 +1100278
279 def length(self):
280 """Returns the length of the spline (in meters)"""
281 return self._point_distances[-1]
282
283 # TODO(austin): need a better name...
284 def xy(self, distance):
285 """Returns the xy position as a function of distance."""
286 return spline(self.distance_to_alpha(distance), self._control_points)
287
288 # TODO(austin): need a better name...
289 def dxy(self, distance):
290 """Returns the xy velocity as a function of distance."""
Ravago Jones26f7ad02021-02-05 15:45:59 -0800291 dspline_point = dspline(
292 self.distance_to_alpha(distance), self._control_points)
Austin Schuh387a6872018-12-01 19:14:33 +1100293 return dspline_point / numpy.linalg.norm(dspline_point, axis=0)
294
295 # TODO(austin): need a better name...
296 def ddxy(self, distance):
297 """Returns the xy acceleration as a function of distance."""
298 alpha = self.distance_to_alpha(distance)
299 dspline_points = dspline(alpha, self._control_points)
300 ddspline_points = ddspline(alpha, self._control_points)
301
John Park91e69732019-03-03 13:12:43 -0800302 norm = numpy.linalg.norm(dspline_points, axis=0)**2.0
Austin Schuh387a6872018-12-01 19:14:33 +1100303
304 return ddspline_points / norm - numpy.multiply(
Ravago Jones26f7ad02021-02-05 15:45:59 -0800305 dspline_points, (numpy.array(dspline_points)[0, :] * numpy.array(
306 ddspline_points)[0, :] + numpy.array(dspline_points)[1, :] *
Austin Schuh387a6872018-12-01 19:14:33 +1100307 numpy.array(ddspline_points)[1, :]) / (norm**2.0))
308
309 def theta(self, distance, dspline_points=None):
310 """Returns the heading as a function of distance."""
Ravago Jones26f7ad02021-02-05 15:45:59 -0800311 return spline_theta(
312 self.distance_to_alpha(distance),
313 self._control_points,
314 dspline_points=dspline_points)
Austin Schuh387a6872018-12-01 19:14:33 +1100315
316 def dtheta(self, distance, dspline_points=None, ddspline_points=None):
317 """Returns the angular velocity as a function of distance."""
318 alpha = self.distance_to_alpha(distance)
319 if dspline_points is None:
320 dspline_points = dspline(alpha, self._control_points)
321 if ddspline_points is None:
322 ddspline_points = ddspline(alpha, self._control_points)
323
324 dtheta_points = dspline_theta(alpha, self._control_points,
325 dspline_points, ddspline_points)
326
327 return dtheta_points / numpy.linalg.norm(dspline_points, axis=0)
328
John Park91e69732019-03-03 13:12:43 -0800329 def dtheta_dt(self,
330 distance,
331 velocity,
332 dspline_points=None,
333 ddspline_points=None):
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100334 """Returns the angular velocity as a function of time."""
John Park91e69732019-03-03 13:12:43 -0800335 return self.dtheta(distance, dspline_points,
336 ddspline_points) * velocity
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100337
Austin Schuh387a6872018-12-01 19:14:33 +1100338 def ddtheta(self,
339 distance,
340 dspline_points=None,
341 ddspline_points=None,
342 dddspline_points=None):
343 """Returns the angular acceleration as a function of distance."""
344 alpha = self.distance_to_alpha(distance)
345 if dspline_points is None:
346 dspline_points = dspline(alpha, self._control_points)
347 if ddspline_points is None:
348 ddspline_points = ddspline(alpha, self._control_points)
349 if dddspline_points is None:
350 dddspline_points = dddspline(alpha, self._control_points)
351
352 dtheta_points = dspline_theta(alpha, self._control_points,
353 dspline_points, ddspline_points)
354 ddtheta_points = ddspline_theta(alpha, self._control_points,
355 dspline_points, ddspline_points,
356 dddspline_points)
357
358 # TODO(austin): Factor out the d^alpha/dd^2.
359 return ddtheta_points / numpy.linalg.norm(
360 dspline_points, axis=0)**2.0 - numpy.multiply(
Ravago Jones26f7ad02021-02-05 15:45:59 -0800361 dtheta_points,
362 (numpy.array(dspline_points)[0, :] * numpy.array(
363 ddspline_points)[0, :] + numpy.array(dspline_points)[1, :]
364 * numpy.array(ddspline_points)[1, :]) / (
365 (numpy.array(dspline_points)[0, :]**2.0 +
366 numpy.array(dspline_points)[1, :]**2.0)**2.0))
Austin Schuh387a6872018-12-01 19:14:33 +1100367
368
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100369def integrate_accel_for_distance(f, v, x, dx):
370 # Use a trick from
371 # https://www.johndcook.com/blog/2012/02/21/care-and-treatment-of-singularities/
372 #
373 # We want to calculate:
374 # v0 + (integral of dv/dt = f(x, v) from x to x + dx); noting that dv/dt
375 # is expressed in t, not distance, so we want to do the integral of
376 # dv/dx = f(x, v) / v.
377 #
378 # Because v can be near zero at the start of the integral (but because f is
379 # nonnegative, v will never go to zero), but the integral should still be
380 # valid, we follow the suggestion and instead calculate
381 # v0 + integral((f(x, v) - f(x0, v0)) / v) + integral(f(x0, v0) / v).
382 #
383 # Using a0 = f(x0, v0), we get the second term as
384 # integral((f(x, v) - a0) / v)
385 # where when v is zero we will also be at x0/v0 (because v can only start
386 # at zero, not go to zero).
387 #
388 # The second term, integral(a0 / v) requires an approximation.--in
389 # this case, that dv/dt is constant. Thus, we have
390 # integral(a0 / sqrt(v0^2 + 2*a0*x)) = sqrt(2*a0*dx + v0^2) - sqrt(v0^2)
391 # = sqrt(2 * a0 * dx * v0^2) - v0.
392 #
393 # Because the RungeKutta function returns v0 + the integral, this
394 # gives the statements below.
395
396 a0 = f(x, v)
397
398 def integrablef(t, y):
399 # Since we know that a0 == a(0) and that they are asymptotically the
400 # same at 0, we know that the limit is 0 at 0. This is true because
401 # when starting from a stop, under sane accelerations, we can assume
402 # that we will start with a constant acceleration. So, hard-code it.
403 if numpy.abs(y) < 1e-6:
404 return 0.0
405 return (f(t, y) - a0) / y
406
John Park91e69732019-03-03 13:12:43 -0800407 return (RungeKutta(integrablef, v, x, dx) - v) + numpy.sqrt(2.0 * a0 * dx +
408 v * v)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100409
410
411class Trajectory(object):
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100412 def __init__(self, path, drivetrain, longitudal_accel, lateral_accel,
413 distance_count):
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100414 self._path = path
415 self._drivetrain = drivetrain
John Park91e69732019-03-03 13:12:43 -0800416 self.distances = numpy.linspace(0.0, self._path.length(),
417 distance_count)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100418 self._longitudal_accel = longitudal_accel
419 self._lateral_accel = lateral_accel
420
421 self._B_inverse = numpy.linalg.inv(self._drivetrain.B_continuous)
422
423 def create_plan(self, vmax):
424 vmax = 10.0
425 plan = numpy.array(numpy.zeros((len(self.distances), )))
426 plan.fill(vmax)
427 return plan
428
429 def lateral_velocity_curvature(self, distance):
Ravago Jones26f7ad02021-02-05 15:45:59 -0800430 return numpy.sqrt(
431 self._lateral_accel / numpy.linalg.norm(self._path.ddxy(distance)))
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100432
433 def lateral_accel_pass(self, plan):
434 plan = plan.copy()
435 # TODO(austin): This appears to be doing nothing.
436 for i, distance in enumerate(self.distances):
437 plan[i] = min(plan[i], self.lateral_velocity_curvature(distance))
438 return plan
439
440 def compute_K345(self, current_dtheta, current_ddtheta):
441 # We've now got the equation:
442 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
443 K1 = numpy.matrix(
444 [[-self._drivetrain.robot_radius_l * current_ddtheta],
445 [self._drivetrain.robot_radius_r * current_ddtheta]])
446 K2 = numpy.matrix(
447 [[1.0 - self._drivetrain.robot_radius_l * current_dtheta],
448 [1.0 + self._drivetrain.robot_radius_r * current_dtheta]])
449
450 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
451 K3 = self._B_inverse * K1
452 K4 = -self._B_inverse * self._drivetrain.A_continuous * K2
453 K5 = self._B_inverse * K2
454 return K3, K4, K5
455
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100456 def forward_acceleration(self, x, v):
457 current_ddtheta = self._path.ddtheta(x)[0]
458 current_dtheta = self._path.dtheta(x)[0]
459 # We've now got the equation:
460 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
461 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
462 K3, K4, K5 = self.compute_K345(current_dtheta, current_ddtheta)
463
464 C = K3 * v * v + K4 * v
465 # Note: K345 are not quite constant over the step, but we are going
466 # to assume they are for now.
John Park91e69732019-03-03 13:12:43 -0800467 accelerations = [
468 (12.0 - C[0, 0]) / K5[0, 0], (12.0 - C[1, 0]) / K5[1, 0],
469 (-12.0 - C[0, 0]) / K5[0, 0], (-12.0 - C[1, 0]) / K5[1, 0]
470 ]
Austin Schuhec7f06d2019-01-04 07:47:15 +1100471 maxa = -float('inf')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100472 for a in accelerations:
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100473 U = K5 * a + K3 * v * v + K4 * v
474 if not (numpy.abs(U) > 12.0 + 1e-6).any():
475 maxa = max(maxa, a)
476
477 lateral_accel = v * v * numpy.linalg.norm(self._path.ddxy(x))
478 # Constrain the longitudinal acceleration to keep in a pseudo friction
479 # circle. This will make it so we don't floor it while in a turn and
480 # cause extra wheel slip.
John Park91e69732019-03-03 13:12:43 -0800481 long_accel = numpy.sqrt(1.0 - (
482 lateral_accel / self._lateral_accel)**2.0) * self._longitudal_accel
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100483 return min(long_accel, maxa)
484
485 def forward_pass(self, plan):
486 plan = plan.copy()
487 for i, distance in enumerate(self.distances):
488 if i == len(self.distances) - 1:
489 break
490
491 plan[i + 1] = min(
492 plan[i + 1],
493 integrate_accel_for_distance(
494 self.forward_acceleration, plan[i], self.distances[i],
495 self.distances[i + 1] - self.distances[i]))
496 return plan
497
498 def backward_acceleration(self, x, v):
499 # TODO(austin): Forwards and backwards are quite similar. Can we
500 # factor this out?
501 current_ddtheta = self._path.ddtheta(x)[0]
502 current_dtheta = self._path.dtheta(x)[0]
503 # We've now got the equation:
504 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
505 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
506 K3, K4, K5 = self.compute_K345(current_dtheta, current_ddtheta)
507
508 C = K3 * v * v + K4 * v
509 # Note: K345 are not quite constant over the step, but we are going
510 # to assume they are for now.
John Park91e69732019-03-03 13:12:43 -0800511 accelerations = [
512 (12.0 - C[0, 0]) / K5[0, 0], (12.0 - C[1, 0]) / K5[1, 0],
513 (-12.0 - C[0, 0]) / K5[0, 0], (-12.0 - C[1, 0]) / K5[1, 0]
514 ]
Austin Schuhec7f06d2019-01-04 07:47:15 +1100515 mina = float('inf')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100516 for a in accelerations:
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100517 U = K5 * a + K3 * v * v + K4 * v
518 if not (numpy.abs(U) > 12.0 + 1e-6).any():
519 mina = min(mina, a)
520
521 lateral_accel = v * v * numpy.linalg.norm(self._path.ddxy(x))
522 # Constrain the longitudinal acceleration to keep in a pseudo friction
523 # circle. This will make it so we don't floor it while in a turn and
524 # cause extra wheel slip.
John Park91e69732019-03-03 13:12:43 -0800525 long_accel = -numpy.sqrt(1.0 - (
526 lateral_accel / self._lateral_accel)**2.0) * self._longitudal_accel
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100527 return max(long_accel, mina)
528
529 def backward_pass(self, plan):
530 plan = plan.copy()
531 for i, distance in reversed(list(enumerate(self.distances))):
532 if i == 0:
533 break
534
535 plan[i - 1] = min(
536 plan[i - 1],
537 integrate_accel_for_distance(
538 self.backward_acceleration, plan[i], self.distances[i],
539 self.distances[i - 1] - self.distances[i]))
540 return plan
541
542 # TODO(austin): The plan should probably not be passed in...
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100543 def ff_accel(self, plan, distance):
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100544 if distance < self.distances[1]:
545 after_index = 1
546 before_index = after_index - 1
547 if distance < self.distances[0]:
548 distance = 0.0
549 elif distance > self.distances[-2]:
550 after_index = len(self.distances) - 1
551 before_index = after_index - 1
552 if distance > self.distances[-1]:
553 distance = self.distances[-1]
554 else:
Ravago Jones26f7ad02021-02-05 15:45:59 -0800555 after_index = numpy.searchsorted(
556 self.distances, distance, side='right')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100557 before_index = after_index - 1
558
559 vforwards = integrate_accel_for_distance(
560 self.forward_acceleration, plan[before_index],
561 self.distances[before_index],
562 distance - self.distances[before_index])
563 vbackward = integrate_accel_for_distance(
564 self.backward_acceleration, plan[after_index],
565 self.distances[after_index],
566 distance - self.distances[after_index])
567
568 vcurvature = self.lateral_velocity_curvature(distance)
569
570 if vcurvature < vforwards and vcurvature < vbackward:
571 accel = 0
572 velocity = vcurvature
573 elif vforwards < vbackward:
574 velocity = vforwards
575 accel = self.forward_acceleration(distance, velocity)
576 else:
577 velocity = vbackward
578 accel = self.backward_acceleration(distance, velocity)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100579 return (distance, velocity, accel)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100580
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100581 def ff_voltage(self, plan, distance):
582 _, velocity, accel = self.ff_accel(plan, distance)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100583 current_ddtheta = self._path.ddtheta(distance)[0]
584 current_dtheta = self._path.dtheta(distance)[0]
585 # TODO(austin): Factor these out.
586 # We've now got the equation:
587 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
588 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
589 K3, K4, K5 = self.compute_K345(current_dtheta, current_ddtheta)
590
591 U = K5 * accel + K3 * velocity * velocity + K4 * velocity
592 return U
Austin Schuh387a6872018-12-01 19:14:33 +1100593
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100594 def goal_state(self, distance, velocity):
Ravago Jones26f7ad02021-02-05 15:45:59 -0800595 width = (
596 self._drivetrain.robot_radius_l + self._drivetrain.robot_radius_r)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100597 goal = numpy.matrix(numpy.zeros((5, 1)))
598
599 goal[0:2, :] = self._path.xy(distance)
600 goal[2, :] = self._path.theta(distance)
601
John Park91e69732019-03-03 13:12:43 -0800602 Ter = numpy.linalg.inv(
603 numpy.matrix([[0.5, 0.5], [-1.0 / width, 1.0 / width]]))
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100604 goal[3:5, :] = Ter * numpy.matrix(
605 [[velocity], [self._path.dtheta_dt(distance, velocity)]])
606 return goal
607
608
Austin Schuh35d19872018-11-30 15:50:47 +1100609def main(argv):
610 # Build up the control point matrix
611 start = numpy.matrix([[0.0, 0.0]]).T
612 c1 = numpy.matrix([[0.5, 0.0]]).T
613 c2 = numpy.matrix([[0.5, 1.0]]).T
614 end = numpy.matrix([[1.0, 1.0]]).T
615 control_points = numpy.hstack((start, c1, c2, end))
616
617 # The alphas to plot
618 alphas = numpy.linspace(0.0, 1.0, 1000)
619
620 # Compute x, y and the 3 derivatives
621 spline_points = spline(alphas, control_points)
622 dspline_points = dspline(alphas, control_points)
623 ddspline_points = ddspline(alphas, control_points)
624 dddspline_points = dddspline(alphas, control_points)
625
626 # Compute theta and the two derivatives
627 theta = spline_theta(alphas, control_points, dspline_points=dspline_points)
Ravago Jones26f7ad02021-02-05 15:45:59 -0800628 dtheta = dspline_theta(
629 alphas, control_points, dspline_points=dspline_points)
630 ddtheta = ddspline_theta(
631 alphas,
632 control_points,
633 dspline_points=dspline_points,
634 dddspline_points=dddspline_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100635
636 # Plot the control points and the spline.
637 pylab.figure()
Ravago Jones26f7ad02021-02-05 15:45:59 -0800638 pylab.plot(
639 numpy.array(control_points)[0, :],
640 numpy.array(control_points)[1, :],
641 '-o',
642 label='control')
643 pylab.plot(
644 numpy.array(spline_points)[0, :],
645 numpy.array(spline_points)[1, :],
646 label='spline')
Austin Schuh35d19872018-11-30 15:50:47 +1100647 pylab.legend()
648
649 # For grins, confirm that the double integral of the acceleration (with
650 # respect to the spline parameter) matches the position. This lets us
651 # confirm that the derivatives are consistent.
652 xint_plot = numpy.matrix(numpy.zeros((2, len(alphas))))
653 dxint_plot = xint_plot.copy()
654 xint = spline_points[:, 0].copy()
655 dxint = dspline_points[:, 0].copy()
656 xint_plot[:, 0] = xint
657 dxint_plot[:, 0] = dxint
658 for i in range(len(alphas) - 1):
659 xint += (alphas[i + 1] - alphas[i]) * dxint
660 dxint += (alphas[i + 1] - alphas[i]) * ddspline_points[:, i]
661 xint_plot[:, i + 1] = xint
662 dxint_plot[:, i + 1] = dxint
663
664 # Integrate up the spline velocity and heading to confirm that given a
665 # velocity (as a function of the spline parameter) and angle, we will move
666 # from the starting point to the ending point.
John Park91e69732019-03-03 13:12:43 -0800667 thetaint_plot = numpy.zeros((len(alphas), ))
Austin Schuh35d19872018-11-30 15:50:47 +1100668 thetaint = theta[0]
John Park91e69732019-03-03 13:12:43 -0800669 dthetaint_plot = numpy.zeros((len(alphas), ))
Austin Schuh35d19872018-11-30 15:50:47 +1100670 dthetaint = dtheta[0]
671 thetaint_plot[0] = thetaint
672 dthetaint_plot[0] = dthetaint
673
674 txint_plot = numpy.matrix(numpy.zeros((2, len(alphas))))
675 txint = spline_points[:, 0].copy()
676 txint_plot[:, 0] = txint
677 for i in range(len(alphas) - 1):
678 dalpha = alphas[i + 1] - alphas[i]
679 txint += dalpha * numpy.linalg.norm(
John Park91e69732019-03-03 13:12:43 -0800680 dspline_points[:, i]) * numpy.matrix([[numpy.cos(theta[i])],
681 [numpy.sin(theta[i])]])
Austin Schuh35d19872018-11-30 15:50:47 +1100682 txint_plot[:, i + 1] = txint
683 thetaint += dalpha * dtheta[i]
684 dthetaint += dalpha * ddtheta[i]
685 thetaint_plot[i + 1] = thetaint
686 dthetaint_plot[i + 1] = dthetaint
687
Austin Schuh35d19872018-11-30 15:50:47 +1100688 # Now plot x, dx/dalpha, ddx/ddalpha, dddx/dddalpha, and integrals thereof
689 # to perform consistency checks.
690 pylab.figure()
691 pylab.plot(alphas, numpy.array(spline_points)[0, :], label='x')
692 pylab.plot(alphas, numpy.array(xint_plot)[0, :], label='ix')
693 pylab.plot(alphas, numpy.array(dspline_points)[0, :], label='dx')
694 pylab.plot(alphas, numpy.array(dxint_plot)[0, :], label='idx')
695 pylab.plot(alphas, numpy.array(txint_plot)[0, :], label='tix')
696 pylab.plot(alphas, numpy.array(ddspline_points)[0, :], label='ddx')
697 pylab.plot(alphas, numpy.array(dddspline_points)[0, :], label='dddx')
698 pylab.legend()
699
700 # Now do the same for y.
701 pylab.figure()
702 pylab.plot(alphas, numpy.array(spline_points)[1, :], label='y')
703 pylab.plot(alphas, numpy.array(xint_plot)[1, :], label='iy')
704 pylab.plot(alphas, numpy.array(dspline_points)[1, :], label='dy')
705 pylab.plot(alphas, numpy.array(dxint_plot)[1, :], label='idy')
706 pylab.plot(alphas, numpy.array(txint_plot)[1, :], label='tiy')
707 pylab.plot(alphas, numpy.array(ddspline_points)[1, :], label='ddy')
708 pylab.plot(alphas, numpy.array(dddspline_points)[1, :], label='dddy')
709 pylab.legend()
710
711 # And for theta.
712 pylab.figure()
713 pylab.plot(alphas, theta, label='theta')
714 pylab.plot(alphas, dtheta, label='dtheta')
715 pylab.plot(alphas, ddtheta, label='ddtheta')
716 pylab.plot(alphas, thetaint_plot, label='thetai')
717 pylab.plot(alphas, dthetaint_plot, label='dthetai')
Ravago Jones26f7ad02021-02-05 15:45:59 -0800718 pylab.plot(
719 alphas,
720 numpy.linalg.norm(numpy.array(dspline_points), axis=0),
721 label='velocity')
Austin Schuh387a6872018-12-01 19:14:33 +1100722
723 # Now, repeat as a function of path length as opposed to alpha
724 path = Path(control_points)
725 distance_count = 1000
726 position = path.xy(0.0)
727 velocity = path.dxy(0.0)
728 theta = path.theta(0.0)
729 omega = path.dtheta(0.0)
730
731 iposition_plot = numpy.matrix(numpy.zeros((2, distance_count)))
732 ivelocity_plot = numpy.matrix(numpy.zeros((2, distance_count)))
733 iposition_plot[:, 0] = position.copy()
734 ivelocity_plot[:, 0] = velocity.copy()
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100735 itheta_plot = numpy.zeros((distance_count, ))
736 iomega_plot = numpy.zeros((distance_count, ))
Austin Schuh387a6872018-12-01 19:14:33 +1100737 itheta_plot[0] = theta
738 iomega_plot[0] = omega
739
740 distances = numpy.linspace(0.0, path.length(), distance_count)
741
Austin Schuh5ea48472021-02-02 20:46:41 -0800742 for i in range(len(distances) - 1):
Austin Schuh387a6872018-12-01 19:14:33 +1100743 position += velocity * (distances[i + 1] - distances[i])
744 velocity += path.ddxy(distances[i]) * (distances[i + 1] - distances[i])
745 iposition_plot[:, i + 1] = position
746 ivelocity_plot[:, i + 1] = velocity
747
748 theta += omega * (distances[i + 1] - distances[i])
749 omega += path.ddtheta(distances[i]) * (distances[i + 1] - distances[i])
750 itheta_plot[i + 1] = theta
751 iomega_plot[i + 1] = omega
752
753 pylab.figure()
754 pylab.plot(distances, numpy.array(path.xy(distances))[0, :], label='x')
755 pylab.plot(distances, numpy.array(iposition_plot)[0, :], label='ix')
756 pylab.plot(distances, numpy.array(path.dxy(distances))[0, :], label='dx')
757 pylab.plot(distances, numpy.array(ivelocity_plot)[0, :], label='idx')
758 pylab.plot(distances, numpy.array(path.ddxy(distances))[0, :], label='ddx')
759 pylab.legend()
760
761 pylab.figure()
762 pylab.plot(distances, numpy.array(path.xy(distances))[1, :], label='y')
763 pylab.plot(distances, numpy.array(iposition_plot)[1, :], label='iy')
764 pylab.plot(distances, numpy.array(path.dxy(distances))[1, :], label='dy')
765 pylab.plot(distances, numpy.array(ivelocity_plot)[1, :], label='idy')
766 pylab.plot(distances, numpy.array(path.ddxy(distances))[1, :], label='ddy')
767 pylab.legend()
768
769 pylab.figure()
770 pylab.plot(distances, path.theta(distances), label='theta')
771 pylab.plot(distances, itheta_plot, label='itheta')
772 pylab.plot(distances, path.dtheta(distances), label='omega')
773 pylab.plot(distances, iomega_plot, label='iomega')
774 pylab.plot(distances, path.ddtheta(distances), label='alpha')
775 pylab.legend()
Austin Schuh35d19872018-11-30 15:50:47 +1100776
777 # TODO(austin): Start creating a velocity plan now that we have all the
778 # derivitives of our spline.
779
Austin Schuh44aa9142018-12-03 21:07:23 +1100780 velocity_drivetrain = polydrivetrain.VelocityDrivetrainModel(
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100781 y2016.control_loops.python.drivetrain.kDrivetrain)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100782 position_drivetrain = drivetrain.Drivetrain(
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100783 y2016.control_loops.python.drivetrain.kDrivetrain)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100784
785 longitudal_accel = 3.0
786 lateral_accel = 2.0
787
Ravago Jones26f7ad02021-02-05 15:45:59 -0800788 trajectory = Trajectory(
789 path,
790 drivetrain=velocity_drivetrain,
791 longitudal_accel=longitudal_accel,
792 lateral_accel=lateral_accel,
793 distance_count=500)
Austin Schuh44aa9142018-12-03 21:07:23 +1100794
795 vmax = numpy.inf
796 vmax = 10.0
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100797 lateral_accel_plan = trajectory.lateral_accel_pass(
798 trajectory.create_plan(vmax))
Austin Schuh44aa9142018-12-03 21:07:23 +1100799
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100800 forward_accel_plan = lateral_accel_plan.copy()
801 # Start and end the path stopped.
802 forward_accel_plan[0] = 0.0
803 forward_accel_plan[-1] = 0.0
Austin Schuh44aa9142018-12-03 21:07:23 +1100804
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100805 forward_accel_plan = trajectory.forward_pass(forward_accel_plan)
Austin Schuh44aa9142018-12-03 21:07:23 +1100806
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100807 backward_accel_plan = trajectory.backward_pass(forward_accel_plan)
Austin Schuh44aa9142018-12-03 21:07:23 +1100808
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100809 # And now, calculate the left, right voltage as a function of distance.
Austin Schuh44aa9142018-12-03 21:07:23 +1100810
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100811 # TODO(austin): Factor out the accel and decel functions so we can use them
812 # to calculate voltage as a function of distance.
Austin Schuh44aa9142018-12-03 21:07:23 +1100813
814 pylab.figure()
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100815 pylab.plot(trajectory.distances, lateral_accel_plan, label='accel pass')
816 pylab.plot(trajectory.distances, forward_accel_plan, label='forward pass')
817 pylab.plot(trajectory.distances, backward_accel_plan, label='forward pass')
Austin Schuh44aa9142018-12-03 21:07:23 +1100818 pylab.xlabel("distance along spline (m)")
819 pylab.ylabel("velocity (m/s)")
820 pylab.legend()
821
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100822 dt = 0.005
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100823 # Now, let's integrate up the path centric coordinates to get a distance,
824 # velocity, and acceleration as a function of time to follow the path.
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100825
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100826 length_plan_t = [0.0]
827 length_plan_x = [numpy.matrix(numpy.zeros((2, 1)))]
828 length_plan_v = [0.0]
829 length_plan_a = [trajectory.ff_accel(backward_accel_plan, 0.0)[2]]
830 t = 0.0
831 spline_state = length_plan_x[-1][0:2, :]
832
833 while spline_state[0, 0] < path.length():
834 t += dt
John Park91e69732019-03-03 13:12:43 -0800835
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100836 def along_spline_diffeq(t, x):
837 _, v, a = trajectory.ff_accel(backward_accel_plan, x[0, 0])
838 return numpy.matrix([[x[1, 0]], [a]])
839
John Park91e69732019-03-03 13:12:43 -0800840 spline_state = RungeKutta(along_spline_diffeq, spline_state.copy(), t,
841 dt)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100842
John Park91e69732019-03-03 13:12:43 -0800843 d, v, a = trajectory.ff_accel(backward_accel_plan,
844 length_plan_x[-1][0, 0])
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100845
846 length_plan_v.append(v)
847 length_plan_a.append(a)
848 length_plan_t.append(t)
849 length_plan_x.append(spline_state.copy())
850 spline_state[1, 0] = v
851
852 xva_plan = numpy.matrix(numpy.zeros((3, len(length_plan_t))))
853 u_plan = numpy.matrix(numpy.zeros((2, len(length_plan_t))))
854 state_plan = numpy.matrix(numpy.zeros((5, len(length_plan_t))))
855
856 state = numpy.matrix(numpy.zeros((5, 1)))
857 state[3, 0] = 0.1
858 state[4, 0] = 0.1
859 states = numpy.matrix(numpy.zeros((5, len(length_plan_t))))
860 full_us = numpy.matrix(numpy.zeros((2, len(length_plan_t))))
861 x_es = []
862 y_es = []
863 theta_es = []
864 vel_es = []
865 omega_es = []
866 omega_rs = []
867 omega_cs = []
868
869 width = (velocity_drivetrain.robot_radius_l +
870 velocity_drivetrain.robot_radius_r)
871 Ter = numpy.matrix([[0.5, 0.5], [-1.0 / width, 1.0 / width]])
872
Austin Schuh5ea48472021-02-02 20:46:41 -0800873 for i in range(len(length_plan_t)):
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100874 xva_plan[0, i] = length_plan_x[i][0, 0]
875 xva_plan[1, i] = length_plan_v[i]
876 xva_plan[2, i] = length_plan_a[i]
877
878 xy_r = path.xy(xva_plan[0, i])
879 x_r = xy_r[0, 0]
880 y_r = xy_r[1, 0]
881 theta_r = path.theta(xva_plan[0, i])[0]
882 vel_omega_r = numpy.matrix(
883 [[xva_plan[1, i]],
884 [path.dtheta_dt(xva_plan[0, i], xva_plan[1, i])[0]]])
885 vel_lr = numpy.linalg.inv(Ter) * vel_omega_r
886
John Park91e69732019-03-03 13:12:43 -0800887 state_plan[:, i] = numpy.matrix([[x_r], [y_r], [theta_r],
888 [vel_lr[0, 0]], [vel_lr[1, 0]]])
Ravago Jones26f7ad02021-02-05 15:45:59 -0800889 u_plan[:, i] = trajectory.ff_voltage(backward_accel_plan,
890 xva_plan[0, i])
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100891
892 Q = numpy.matrix(
893 numpy.diag([
894 1.0 / (0.05**2), 1.0 / (0.05**2), 1.0 / (0.2**2), 1.0 / (0.5**2),
895 1.0 / (0.5**2)
896 ]))
897 R = numpy.matrix(numpy.diag([1.0 / (12.0**2), 1.0 / (12.0**2)]))
898 kMinVelocity = 0.1
899
Austin Schuh5ea48472021-02-02 20:46:41 -0800900 for i in range(len(length_plan_t)):
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100901 states[:, i] = state
902
903 theta = state[2, 0]
904 sintheta = numpy.sin(theta)
905 costheta = numpy.cos(theta)
906 linear_velocity = (state[3, 0] + state[4, 0]) / 2.0
907 if abs(linear_velocity) < kMinVelocity / 100.0:
908 linear_velocity = 0.1
909 elif abs(linear_velocity) > kMinVelocity:
910 pass
911 elif linear_velocity > 0:
912 linear_velocity = kMinVelocity
913 elif linear_velocity < 0:
914 linear_velocity = -kMinVelocity
915
916 width = (velocity_drivetrain.robot_radius_l +
John Park91e69732019-03-03 13:12:43 -0800917 velocity_drivetrain.robot_radius_r)
918 A_linearized_continuous = numpy.matrix(
919 [[
920 0.0, 0.0, -sintheta * linear_velocity, 0.5 * costheta,
921 0.5 * costheta
922 ],
923 [
924 0.0, 0.0, costheta * linear_velocity, 0.5 * sintheta,
925 0.5 * sintheta
926 ], [0.0, 0.0, 0.0, -1.0 / width, 1.0 / width],
927 [0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]])
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100928 A_linearized_continuous[3:5, 3:5] = velocity_drivetrain.A_continuous
929 B_linearized_continuous = numpy.matrix(numpy.zeros((5, 2)))
930 B_linearized_continuous[3:5, :] = velocity_drivetrain.B_continuous
931
932 A, B = controls.c2d(A_linearized_continuous, B_linearized_continuous,
933 dt)
934
935 if i >= 0:
936 K = controls.dlqr(A, B, Q, R)
937 print("K", K)
938 print("eig", numpy.linalg.eig(A - B * K)[0])
939 goal_state = trajectory.goal_state(xva_plan[0, i], xva_plan[1, i])
940 state_error = goal_state - state
941
Ravago Jones26f7ad02021-02-05 15:45:59 -0800942 U = (trajectory.ff_voltage(backward_accel_plan, xva_plan[0, i]) +
943 K * (state_error))
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100944
945 def spline_diffeq(U, t, x):
946 velocity = x[3:5, :]
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100947 theta = x[2, 0]
948 linear_velocity = (velocity[0, 0] + velocity[1, 0]) / 2.0
949 angular_velocity = (velocity[1, 0] - velocity[0, 0]) / (
950 velocity_drivetrain.robot_radius_l +
951 velocity_drivetrain.robot_radius_r)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100952 accel = (velocity_drivetrain.A_continuous * velocity +
953 velocity_drivetrain.B_continuous * U)
John Park91e69732019-03-03 13:12:43 -0800954 return numpy.matrix([[numpy.cos(theta) * linear_velocity],
955 [numpy.sin(theta) * linear_velocity],
956 [angular_velocity], [accel[0, 0]],
957 [accel[1, 0]]])
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100958
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100959 full_us[:, i] = U
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100960
John Park91e69732019-03-03 13:12:43 -0800961 state = RungeKutta(lambda t, x: spline_diffeq(U, t, x), state, i * dt,
962 dt)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100963
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100964 pylab.figure()
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100965 pylab.plot(length_plan_t, numpy.array(xva_plan)[0, :], label='x')
966 pylab.plot(length_plan_t, [x[1, 0] for x in length_plan_x], label='v')
967 pylab.plot(length_plan_t, numpy.array(xva_plan)[1, :], label='planv')
968 pylab.plot(length_plan_t, numpy.array(xva_plan)[2, :], label='a')
969
970 pylab.plot(length_plan_t, numpy.array(full_us)[0, :], label='vl')
971 pylab.plot(length_plan_t, numpy.array(full_us)[1, :], label='vr')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100972 pylab.legend()
973
974 pylab.figure()
Ravago Jones26f7ad02021-02-05 15:45:59 -0800975 pylab.plot(
976 numpy.array(states)[0, :], numpy.array(states)[1, :], label='robot')
977 pylab.plot(
978 numpy.array(spline_points)[0, :],
979 numpy.array(spline_points)[1, :],
980 label='spline')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100981 pylab.legend()
982
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100983 def a(_, x):
984 return 2.0
985 return 2.0 + 0.0001 * x
986
987 v = 0.0
Austin Schuh5ea48472021-02-02 20:46:41 -0800988 for _ in range(10):
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100989 dx = 4.0 / 10.0
990 v = integrate_accel_for_distance(a, v, 0.0, dx)
991 print('v', v)
992
Austin Schuh35d19872018-11-30 15:50:47 +1100993 pylab.show()
994
995
996if __name__ == '__main__':
997 argv = FLAGS(sys.argv)
998 sys.exit(main(argv))