blob: b428c9a68384846de43354683736f123f1dc47cc [file] [log] [blame]
Austin Schuh35d19872018-11-30 15:50:47 +11001#!/usr/bin/python
2
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)
31 for x in xrange(count):
32 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 * (
John Park91e69732019-03-03 13:12:43 -0800220 dy * ddy + dx * ddx) + 1.0 / (dx**2.0 + dy**2.0) * (dx * dddy -
221 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."""
226 def __init__(self, control_points):
227 """Constructs a path given the control points."""
228 self._control_points = control_points
229
230 def spline_velocity(alpha):
John Park91e69732019-03-03 13:12:43 -0800231 return numpy.linalg.norm(dspline(alpha, self._control_points),
232 axis=0)
Austin Schuh387a6872018-12-01 19:14:33 +1100233
234 self._point_distances = [0.0]
235 num_alpha = 100
236 # Integrate the xy velocity as a function of alpha for each step in the
237 # table to get an alpha -> distance calculation. Gaussian Quadrature
238 # is quite accurate, so we can get away with fewer points here than we
239 # might think.
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100240 for alpha in numpy.linspace(0.0, 1.0, num_alpha)[1:]:
Austin Schuh387a6872018-12-01 19:14:33 +1100241 self._point_distances.append(
John Park91e69732019-03-03 13:12:43 -0800242 scipy.integrate.fixed_quad(spline_velocity, alpha, alpha +
243 1.0 / (num_alpha - 1.0))[0] +
Austin Schuh387a6872018-12-01 19:14:33 +1100244 self._point_distances[-1])
245
246 def distance_to_alpha(self, distance):
247 """Converts distances along the spline to alphas.
248
249 Args:
250 distance: A scalar or array of distances to convert
251
252 Returns:
253 An array of distances, (1 big if the input was a scalar)
254 """
255 if numpy.isscalar(distance):
256 return numpy.array([self._distance_to_alpha_scalar(distance)])
257 else:
John Park91e69732019-03-03 13:12:43 -0800258 return numpy.array(
259 [self._distance_to_alpha_scalar(d) for d in distance])
Austin Schuh387a6872018-12-01 19:14:33 +1100260
261 def _distance_to_alpha_scalar(self, distance):
262 """Helper to compute alpha for a distance for a single scalar."""
263 if distance <= 0.0:
264 return 0.0
265 elif distance >= self.length():
266 return 1.0
John Park91e69732019-03-03 13:12:43 -0800267 after_index = numpy.searchsorted(self._point_distances,
268 distance,
269 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]) * (
276 1.0 /
277 (len(self._point_distances) - 1.0)) + float(before_index) / (
Austin Schuh387a6872018-12-01 19:14:33 +1100278 len(self._point_distances) - 1.0)
279
280 def length(self):
281 """Returns the length of the spline (in meters)"""
282 return self._point_distances[-1]
283
284 # TODO(austin): need a better name...
285 def xy(self, distance):
286 """Returns the xy position as a function of distance."""
287 return spline(self.distance_to_alpha(distance), self._control_points)
288
289 # TODO(austin): need a better name...
290 def dxy(self, distance):
291 """Returns the xy velocity as a function of distance."""
John Park91e69732019-03-03 13:12:43 -0800292 dspline_point = dspline(self.distance_to_alpha(distance),
293 self._control_points)
Austin Schuh387a6872018-12-01 19:14:33 +1100294 return dspline_point / numpy.linalg.norm(dspline_point, axis=0)
295
296 # TODO(austin): need a better name...
297 def ddxy(self, distance):
298 """Returns the xy acceleration as a function of distance."""
299 alpha = self.distance_to_alpha(distance)
300 dspline_points = dspline(alpha, self._control_points)
301 ddspline_points = ddspline(alpha, self._control_points)
302
John Park91e69732019-03-03 13:12:43 -0800303 norm = numpy.linalg.norm(dspline_points, axis=0)**2.0
Austin Schuh387a6872018-12-01 19:14:33 +1100304
305 return ddspline_points / norm - numpy.multiply(
306 dspline_points, (numpy.array(dspline_points)[0, :] *
307 numpy.array(ddspline_points)[0, :] +
308 numpy.array(dspline_points)[1, :] *
309 numpy.array(ddspline_points)[1, :]) / (norm**2.0))
310
311 def theta(self, distance, dspline_points=None):
312 """Returns the heading as a function of distance."""
John Park91e69732019-03-03 13:12:43 -0800313 return spline_theta(self.distance_to_alpha(distance),
314 self._control_points,
315 dspline_points=dspline_points)
Austin Schuh387a6872018-12-01 19:14:33 +1100316
317 def dtheta(self, distance, dspline_points=None, ddspline_points=None):
318 """Returns the angular velocity as a function of distance."""
319 alpha = self.distance_to_alpha(distance)
320 if dspline_points is None:
321 dspline_points = dspline(alpha, self._control_points)
322 if ddspline_points is None:
323 ddspline_points = ddspline(alpha, self._control_points)
324
325 dtheta_points = dspline_theta(alpha, self._control_points,
326 dspline_points, ddspline_points)
327
328 return dtheta_points / numpy.linalg.norm(dspline_points, axis=0)
329
John Park91e69732019-03-03 13:12:43 -0800330 def dtheta_dt(self,
331 distance,
332 velocity,
333 dspline_points=None,
334 ddspline_points=None):
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100335 """Returns the angular velocity as a function of time."""
John Park91e69732019-03-03 13:12:43 -0800336 return self.dtheta(distance, dspline_points,
337 ddspline_points) * velocity
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100338
Austin Schuh387a6872018-12-01 19:14:33 +1100339 def ddtheta(self,
340 distance,
341 dspline_points=None,
342 ddspline_points=None,
343 dddspline_points=None):
344 """Returns the angular acceleration as a function of distance."""
345 alpha = self.distance_to_alpha(distance)
346 if dspline_points is None:
347 dspline_points = dspline(alpha, self._control_points)
348 if ddspline_points is None:
349 ddspline_points = ddspline(alpha, self._control_points)
350 if dddspline_points is None:
351 dddspline_points = dddspline(alpha, self._control_points)
352
353 dtheta_points = dspline_theta(alpha, self._control_points,
354 dspline_points, ddspline_points)
355 ddtheta_points = ddspline_theta(alpha, self._control_points,
356 dspline_points, ddspline_points,
357 dddspline_points)
358
359 # TODO(austin): Factor out the d^alpha/dd^2.
360 return ddtheta_points / numpy.linalg.norm(
361 dspline_points, axis=0)**2.0 - numpy.multiply(
362 dtheta_points, (numpy.array(dspline_points)[0, :] *
363 numpy.array(ddspline_points)[0, :] +
364 numpy.array(dspline_points)[1, :] *
365 numpy.array(ddspline_points)[1, :]) /
366 ((numpy.array(dspline_points)[0, :]**2.0 +
367 numpy.array(dspline_points)[1, :]**2.0)**2.0))
368
369
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100370def integrate_accel_for_distance(f, v, x, dx):
371 # Use a trick from
372 # https://www.johndcook.com/blog/2012/02/21/care-and-treatment-of-singularities/
373 #
374 # We want to calculate:
375 # v0 + (integral of dv/dt = f(x, v) from x to x + dx); noting that dv/dt
376 # is expressed in t, not distance, so we want to do the integral of
377 # dv/dx = f(x, v) / v.
378 #
379 # Because v can be near zero at the start of the integral (but because f is
380 # nonnegative, v will never go to zero), but the integral should still be
381 # valid, we follow the suggestion and instead calculate
382 # v0 + integral((f(x, v) - f(x0, v0)) / v) + integral(f(x0, v0) / v).
383 #
384 # Using a0 = f(x0, v0), we get the second term as
385 # integral((f(x, v) - a0) / v)
386 # where when v is zero we will also be at x0/v0 (because v can only start
387 # at zero, not go to zero).
388 #
389 # The second term, integral(a0 / v) requires an approximation.--in
390 # this case, that dv/dt is constant. Thus, we have
391 # integral(a0 / sqrt(v0^2 + 2*a0*x)) = sqrt(2*a0*dx + v0^2) - sqrt(v0^2)
392 # = sqrt(2 * a0 * dx * v0^2) - v0.
393 #
394 # Because the RungeKutta function returns v0 + the integral, this
395 # gives the statements below.
396
397 a0 = f(x, v)
398
399 def integrablef(t, y):
400 # Since we know that a0 == a(0) and that they are asymptotically the
401 # same at 0, we know that the limit is 0 at 0. This is true because
402 # when starting from a stop, under sane accelerations, we can assume
403 # that we will start with a constant acceleration. So, hard-code it.
404 if numpy.abs(y) < 1e-6:
405 return 0.0
406 return (f(t, y) - a0) / y
407
John Park91e69732019-03-03 13:12:43 -0800408 return (RungeKutta(integrablef, v, x, dx) - v) + numpy.sqrt(2.0 * a0 * dx +
409 v * v)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100410
411
412class Trajectory(object):
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100413 def __init__(self, path, drivetrain, longitudal_accel, lateral_accel,
414 distance_count):
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100415 self._path = path
416 self._drivetrain = drivetrain
John Park91e69732019-03-03 13:12:43 -0800417 self.distances = numpy.linspace(0.0, self._path.length(),
418 distance_count)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100419 self._longitudal_accel = longitudal_accel
420 self._lateral_accel = lateral_accel
421
422 self._B_inverse = numpy.linalg.inv(self._drivetrain.B_continuous)
423
424 def create_plan(self, vmax):
425 vmax = 10.0
426 plan = numpy.array(numpy.zeros((len(self.distances), )))
427 plan.fill(vmax)
428 return plan
429
430 def lateral_velocity_curvature(self, distance):
431 return numpy.sqrt(self._lateral_accel /
432 numpy.linalg.norm(self._path.ddxy(distance)))
433
434 def lateral_accel_pass(self, plan):
435 plan = plan.copy()
436 # TODO(austin): This appears to be doing nothing.
437 for i, distance in enumerate(self.distances):
438 plan[i] = min(plan[i], self.lateral_velocity_curvature(distance))
439 return plan
440
441 def compute_K345(self, current_dtheta, current_ddtheta):
442 # We've now got the equation:
443 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
444 K1 = numpy.matrix(
445 [[-self._drivetrain.robot_radius_l * current_ddtheta],
446 [self._drivetrain.robot_radius_r * current_ddtheta]])
447 K2 = numpy.matrix(
448 [[1.0 - self._drivetrain.robot_radius_l * current_dtheta],
449 [1.0 + self._drivetrain.robot_radius_r * current_dtheta]])
450
451 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
452 K3 = self._B_inverse * K1
453 K4 = -self._B_inverse * self._drivetrain.A_continuous * K2
454 K5 = self._B_inverse * K2
455 return K3, K4, K5
456
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100457 def forward_acceleration(self, x, v):
458 current_ddtheta = self._path.ddtheta(x)[0]
459 current_dtheta = self._path.dtheta(x)[0]
460 # We've now got the equation:
461 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
462 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
463 K3, K4, K5 = self.compute_K345(current_dtheta, current_ddtheta)
464
465 C = K3 * v * v + K4 * v
466 # Note: K345 are not quite constant over the step, but we are going
467 # to assume they are for now.
John Park91e69732019-03-03 13:12:43 -0800468 accelerations = [
469 (12.0 - C[0, 0]) / K5[0, 0], (12.0 - C[1, 0]) / K5[1, 0],
470 (-12.0 - C[0, 0]) / K5[0, 0], (-12.0 - C[1, 0]) / K5[1, 0]
471 ]
Austin Schuhec7f06d2019-01-04 07:47:15 +1100472 maxa = -float('inf')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100473 for a in accelerations:
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100474 U = K5 * a + K3 * v * v + K4 * v
475 if not (numpy.abs(U) > 12.0 + 1e-6).any():
476 maxa = max(maxa, a)
477
478 lateral_accel = v * v * numpy.linalg.norm(self._path.ddxy(x))
479 # Constrain the longitudinal acceleration to keep in a pseudo friction
480 # circle. This will make it so we don't floor it while in a turn and
481 # cause extra wheel slip.
John Park91e69732019-03-03 13:12:43 -0800482 long_accel = numpy.sqrt(1.0 - (
483 lateral_accel / self._lateral_accel)**2.0) * self._longitudal_accel
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100484 return min(long_accel, maxa)
485
486 def forward_pass(self, plan):
487 plan = plan.copy()
488 for i, distance in enumerate(self.distances):
489 if i == len(self.distances) - 1:
490 break
491
492 plan[i + 1] = min(
493 plan[i + 1],
494 integrate_accel_for_distance(
495 self.forward_acceleration, plan[i], self.distances[i],
496 self.distances[i + 1] - self.distances[i]))
497 return plan
498
499 def backward_acceleration(self, x, v):
500 # TODO(austin): Forwards and backwards are quite similar. Can we
501 # factor this out?
502 current_ddtheta = self._path.ddtheta(x)[0]
503 current_dtheta = self._path.dtheta(x)[0]
504 # We've now got the equation:
505 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
506 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
507 K3, K4, K5 = self.compute_K345(current_dtheta, current_ddtheta)
508
509 C = K3 * v * v + K4 * v
510 # Note: K345 are not quite constant over the step, but we are going
511 # to assume they are for now.
John Park91e69732019-03-03 13:12:43 -0800512 accelerations = [
513 (12.0 - C[0, 0]) / K5[0, 0], (12.0 - C[1, 0]) / K5[1, 0],
514 (-12.0 - C[0, 0]) / K5[0, 0], (-12.0 - C[1, 0]) / K5[1, 0]
515 ]
Austin Schuhec7f06d2019-01-04 07:47:15 +1100516 mina = float('inf')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100517 for a in accelerations:
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100518 U = K5 * a + K3 * v * v + K4 * v
519 if not (numpy.abs(U) > 12.0 + 1e-6).any():
520 mina = min(mina, a)
521
522 lateral_accel = v * v * numpy.linalg.norm(self._path.ddxy(x))
523 # Constrain the longitudinal acceleration to keep in a pseudo friction
524 # circle. This will make it so we don't floor it while in a turn and
525 # cause extra wheel slip.
John Park91e69732019-03-03 13:12:43 -0800526 long_accel = -numpy.sqrt(1.0 - (
527 lateral_accel / self._lateral_accel)**2.0) * self._longitudal_accel
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100528 return max(long_accel, mina)
529
530 def backward_pass(self, plan):
531 plan = plan.copy()
532 for i, distance in reversed(list(enumerate(self.distances))):
533 if i == 0:
534 break
535
536 plan[i - 1] = min(
537 plan[i - 1],
538 integrate_accel_for_distance(
539 self.backward_acceleration, plan[i], self.distances[i],
540 self.distances[i - 1] - self.distances[i]))
541 return plan
542
543 # TODO(austin): The plan should probably not be passed in...
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100544 def ff_accel(self, plan, distance):
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100545 if distance < self.distances[1]:
546 after_index = 1
547 before_index = after_index - 1
548 if distance < self.distances[0]:
549 distance = 0.0
550 elif distance > self.distances[-2]:
551 after_index = len(self.distances) - 1
552 before_index = after_index - 1
553 if distance > self.distances[-1]:
554 distance = self.distances[-1]
555 else:
John Park91e69732019-03-03 13:12:43 -0800556 after_index = numpy.searchsorted(self.distances,
557 distance,
558 side='right')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100559 before_index = after_index - 1
560
561 vforwards = integrate_accel_for_distance(
562 self.forward_acceleration, plan[before_index],
563 self.distances[before_index],
564 distance - self.distances[before_index])
565 vbackward = integrate_accel_for_distance(
566 self.backward_acceleration, plan[after_index],
567 self.distances[after_index],
568 distance - self.distances[after_index])
569
570 vcurvature = self.lateral_velocity_curvature(distance)
571
572 if vcurvature < vforwards and vcurvature < vbackward:
573 accel = 0
574 velocity = vcurvature
575 elif vforwards < vbackward:
576 velocity = vforwards
577 accel = self.forward_acceleration(distance, velocity)
578 else:
579 velocity = vbackward
580 accel = self.backward_acceleration(distance, velocity)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100581 return (distance, velocity, accel)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100582
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100583 def ff_voltage(self, plan, distance):
584 _, velocity, accel = self.ff_accel(plan, distance)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100585 current_ddtheta = self._path.ddtheta(distance)[0]
586 current_dtheta = self._path.dtheta(distance)[0]
587 # TODO(austin): Factor these out.
588 # We've now got the equation:
589 # K2 * d^2x/dt^2 + K1 (dx/dt)^2 = A * K2 * dx/dt + B * U
590 # Now, rephrase it as K5 a + K3 v^2 + K4 v = U
591 K3, K4, K5 = self.compute_K345(current_dtheta, current_ddtheta)
592
593 U = K5 * accel + K3 * velocity * velocity + K4 * velocity
594 return U
Austin Schuh387a6872018-12-01 19:14:33 +1100595
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100596 def goal_state(self, distance, velocity):
John Park91e69732019-03-03 13:12:43 -0800597 width = (self._drivetrain.robot_radius_l +
598 self._drivetrain.robot_radius_r)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100599 goal = numpy.matrix(numpy.zeros((5, 1)))
600
601 goal[0:2, :] = self._path.xy(distance)
602 goal[2, :] = self._path.theta(distance)
603
John Park91e69732019-03-03 13:12:43 -0800604 Ter = numpy.linalg.inv(
605 numpy.matrix([[0.5, 0.5], [-1.0 / width, 1.0 / width]]))
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100606 goal[3:5, :] = Ter * numpy.matrix(
607 [[velocity], [self._path.dtheta_dt(distance, velocity)]])
608 return goal
609
610
Austin Schuh35d19872018-11-30 15:50:47 +1100611def main(argv):
612 # Build up the control point matrix
613 start = numpy.matrix([[0.0, 0.0]]).T
614 c1 = numpy.matrix([[0.5, 0.0]]).T
615 c2 = numpy.matrix([[0.5, 1.0]]).T
616 end = numpy.matrix([[1.0, 1.0]]).T
617 control_points = numpy.hstack((start, c1, c2, end))
618
619 # The alphas to plot
620 alphas = numpy.linspace(0.0, 1.0, 1000)
621
622 # Compute x, y and the 3 derivatives
623 spline_points = spline(alphas, control_points)
624 dspline_points = dspline(alphas, control_points)
625 ddspline_points = ddspline(alphas, control_points)
626 dddspline_points = dddspline(alphas, control_points)
627
628 # Compute theta and the two derivatives
629 theta = spline_theta(alphas, control_points, dspline_points=dspline_points)
John Park91e69732019-03-03 13:12:43 -0800630 dtheta = dspline_theta(alphas,
631 control_points,
632 dspline_points=dspline_points)
633 ddtheta = ddspline_theta(alphas,
634 control_points,
635 dspline_points=dspline_points,
636 dddspline_points=dddspline_points)
Austin Schuh35d19872018-11-30 15:50:47 +1100637
638 # Plot the control points and the spline.
639 pylab.figure()
John Park91e69732019-03-03 13:12:43 -0800640 pylab.plot(numpy.array(control_points)[0, :],
641 numpy.array(control_points)[1, :],
642 '-o',
643 label='control')
644 pylab.plot(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')
John Park91e69732019-03-03 13:12:43 -0800718 pylab.plot(alphas,
719 numpy.linalg.norm(numpy.array(dspline_points), axis=0),
720 label='velocity')
Austin Schuh387a6872018-12-01 19:14:33 +1100721
722 # Now, repeat as a function of path length as opposed to alpha
723 path = Path(control_points)
724 distance_count = 1000
725 position = path.xy(0.0)
726 velocity = path.dxy(0.0)
727 theta = path.theta(0.0)
728 omega = path.dtheta(0.0)
729
730 iposition_plot = numpy.matrix(numpy.zeros((2, distance_count)))
731 ivelocity_plot = numpy.matrix(numpy.zeros((2, distance_count)))
732 iposition_plot[:, 0] = position.copy()
733 ivelocity_plot[:, 0] = velocity.copy()
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100734 itheta_plot = numpy.zeros((distance_count, ))
735 iomega_plot = numpy.zeros((distance_count, ))
Austin Schuh387a6872018-12-01 19:14:33 +1100736 itheta_plot[0] = theta
737 iomega_plot[0] = omega
738
739 distances = numpy.linspace(0.0, path.length(), distance_count)
740
741 for i in xrange(len(distances) - 1):
742 position += velocity * (distances[i + 1] - distances[i])
743 velocity += path.ddxy(distances[i]) * (distances[i + 1] - distances[i])
744 iposition_plot[:, i + 1] = position
745 ivelocity_plot[:, i + 1] = velocity
746
747 theta += omega * (distances[i + 1] - distances[i])
748 omega += path.ddtheta(distances[i]) * (distances[i + 1] - distances[i])
749 itheta_plot[i + 1] = theta
750 iomega_plot[i + 1] = omega
751
752 pylab.figure()
753 pylab.plot(distances, numpy.array(path.xy(distances))[0, :], label='x')
754 pylab.plot(distances, numpy.array(iposition_plot)[0, :], label='ix')
755 pylab.plot(distances, numpy.array(path.dxy(distances))[0, :], label='dx')
756 pylab.plot(distances, numpy.array(ivelocity_plot)[0, :], label='idx')
757 pylab.plot(distances, numpy.array(path.ddxy(distances))[0, :], label='ddx')
758 pylab.legend()
759
760 pylab.figure()
761 pylab.plot(distances, numpy.array(path.xy(distances))[1, :], label='y')
762 pylab.plot(distances, numpy.array(iposition_plot)[1, :], label='iy')
763 pylab.plot(distances, numpy.array(path.dxy(distances))[1, :], label='dy')
764 pylab.plot(distances, numpy.array(ivelocity_plot)[1, :], label='idy')
765 pylab.plot(distances, numpy.array(path.ddxy(distances))[1, :], label='ddy')
766 pylab.legend()
767
768 pylab.figure()
769 pylab.plot(distances, path.theta(distances), label='theta')
770 pylab.plot(distances, itheta_plot, label='itheta')
771 pylab.plot(distances, path.dtheta(distances), label='omega')
772 pylab.plot(distances, iomega_plot, label='iomega')
773 pylab.plot(distances, path.ddtheta(distances), label='alpha')
774 pylab.legend()
Austin Schuh35d19872018-11-30 15:50:47 +1100775
776 # TODO(austin): Start creating a velocity plan now that we have all the
777 # derivitives of our spline.
778
Austin Schuh44aa9142018-12-03 21:07:23 +1100779 velocity_drivetrain = polydrivetrain.VelocityDrivetrainModel(
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100780 y2016.control_loops.python.drivetrain.kDrivetrain)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100781 position_drivetrain = drivetrain.Drivetrain(
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100782 y2016.control_loops.python.drivetrain.kDrivetrain)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100783
784 longitudal_accel = 3.0
785 lateral_accel = 2.0
786
John Park91e69732019-03-03 13:12:43 -0800787 trajectory = Trajectory(path,
788 drivetrain=velocity_drivetrain,
789 longitudal_accel=longitudal_accel,
790 lateral_accel=lateral_accel,
791 distance_count=500)
Austin Schuh44aa9142018-12-03 21:07:23 +1100792
793 vmax = numpy.inf
794 vmax = 10.0
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100795 lateral_accel_plan = trajectory.lateral_accel_pass(
796 trajectory.create_plan(vmax))
Austin Schuh44aa9142018-12-03 21:07:23 +1100797
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100798 forward_accel_plan = lateral_accel_plan.copy()
799 # Start and end the path stopped.
800 forward_accel_plan[0] = 0.0
801 forward_accel_plan[-1] = 0.0
Austin Schuh44aa9142018-12-03 21:07:23 +1100802
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100803 forward_accel_plan = trajectory.forward_pass(forward_accel_plan)
Austin Schuh44aa9142018-12-03 21:07:23 +1100804
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100805 backward_accel_plan = trajectory.backward_pass(forward_accel_plan)
Austin Schuh44aa9142018-12-03 21:07:23 +1100806
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100807 # And now, calculate the left, right voltage as a function of distance.
Austin Schuh44aa9142018-12-03 21:07:23 +1100808
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100809 # TODO(austin): Factor out the accel and decel functions so we can use them
810 # to calculate voltage as a function of distance.
Austin Schuh44aa9142018-12-03 21:07:23 +1100811
812 pylab.figure()
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100813 pylab.plot(trajectory.distances, lateral_accel_plan, label='accel pass')
814 pylab.plot(trajectory.distances, forward_accel_plan, label='forward pass')
815 pylab.plot(trajectory.distances, backward_accel_plan, label='forward pass')
Austin Schuh44aa9142018-12-03 21:07:23 +1100816 pylab.xlabel("distance along spline (m)")
817 pylab.ylabel("velocity (m/s)")
818 pylab.legend()
819
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100820 dt = 0.005
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100821 # Now, let's integrate up the path centric coordinates to get a distance,
822 # velocity, and acceleration as a function of time to follow the path.
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100823
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100824 length_plan_t = [0.0]
825 length_plan_x = [numpy.matrix(numpy.zeros((2, 1)))]
826 length_plan_v = [0.0]
827 length_plan_a = [trajectory.ff_accel(backward_accel_plan, 0.0)[2]]
828 t = 0.0
829 spline_state = length_plan_x[-1][0:2, :]
830
831 while spline_state[0, 0] < path.length():
832 t += dt
John Park91e69732019-03-03 13:12:43 -0800833
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100834 def along_spline_diffeq(t, x):
835 _, v, a = trajectory.ff_accel(backward_accel_plan, x[0, 0])
836 return numpy.matrix([[x[1, 0]], [a]])
837
John Park91e69732019-03-03 13:12:43 -0800838 spline_state = RungeKutta(along_spline_diffeq, spline_state.copy(), t,
839 dt)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100840
John Park91e69732019-03-03 13:12:43 -0800841 d, v, a = trajectory.ff_accel(backward_accel_plan,
842 length_plan_x[-1][0, 0])
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100843
844 length_plan_v.append(v)
845 length_plan_a.append(a)
846 length_plan_t.append(t)
847 length_plan_x.append(spline_state.copy())
848 spline_state[1, 0] = v
849
850 xva_plan = numpy.matrix(numpy.zeros((3, len(length_plan_t))))
851 u_plan = numpy.matrix(numpy.zeros((2, len(length_plan_t))))
852 state_plan = numpy.matrix(numpy.zeros((5, len(length_plan_t))))
853
854 state = numpy.matrix(numpy.zeros((5, 1)))
855 state[3, 0] = 0.1
856 state[4, 0] = 0.1
857 states = numpy.matrix(numpy.zeros((5, len(length_plan_t))))
858 full_us = numpy.matrix(numpy.zeros((2, len(length_plan_t))))
859 x_es = []
860 y_es = []
861 theta_es = []
862 vel_es = []
863 omega_es = []
864 omega_rs = []
865 omega_cs = []
866
867 width = (velocity_drivetrain.robot_radius_l +
868 velocity_drivetrain.robot_radius_r)
869 Ter = numpy.matrix([[0.5, 0.5], [-1.0 / width, 1.0 / width]])
870
871 for i in xrange(len(length_plan_t)):
872 xva_plan[0, i] = length_plan_x[i][0, 0]
873 xva_plan[1, i] = length_plan_v[i]
874 xva_plan[2, i] = length_plan_a[i]
875
876 xy_r = path.xy(xva_plan[0, i])
877 x_r = xy_r[0, 0]
878 y_r = xy_r[1, 0]
879 theta_r = path.theta(xva_plan[0, i])[0]
880 vel_omega_r = numpy.matrix(
881 [[xva_plan[1, i]],
882 [path.dtheta_dt(xva_plan[0, i], xva_plan[1, i])[0]]])
883 vel_lr = numpy.linalg.inv(Ter) * vel_omega_r
884
John Park91e69732019-03-03 13:12:43 -0800885 state_plan[:, i] = numpy.matrix([[x_r], [y_r], [theta_r],
886 [vel_lr[0, 0]], [vel_lr[1, 0]]])
887 u_plan[:, i] = trajectory.ff_voltage(backward_accel_plan, xva_plan[0,
888 i])
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100889
890 Q = numpy.matrix(
891 numpy.diag([
892 1.0 / (0.05**2), 1.0 / (0.05**2), 1.0 / (0.2**2), 1.0 / (0.5**2),
893 1.0 / (0.5**2)
894 ]))
895 R = numpy.matrix(numpy.diag([1.0 / (12.0**2), 1.0 / (12.0**2)]))
896 kMinVelocity = 0.1
897
898 for i in xrange(len(length_plan_t)):
899 states[:, i] = state
900
901 theta = state[2, 0]
902 sintheta = numpy.sin(theta)
903 costheta = numpy.cos(theta)
904 linear_velocity = (state[3, 0] + state[4, 0]) / 2.0
905 if abs(linear_velocity) < kMinVelocity / 100.0:
906 linear_velocity = 0.1
907 elif abs(linear_velocity) > kMinVelocity:
908 pass
909 elif linear_velocity > 0:
910 linear_velocity = kMinVelocity
911 elif linear_velocity < 0:
912 linear_velocity = -kMinVelocity
913
914 width = (velocity_drivetrain.robot_radius_l +
John Park91e69732019-03-03 13:12:43 -0800915 velocity_drivetrain.robot_radius_r)
916 A_linearized_continuous = numpy.matrix(
917 [[
918 0.0, 0.0, -sintheta * linear_velocity, 0.5 * costheta,
919 0.5 * costheta
920 ],
921 [
922 0.0, 0.0, costheta * linear_velocity, 0.5 * sintheta,
923 0.5 * sintheta
924 ], [0.0, 0.0, 0.0, -1.0 / width, 1.0 / width],
925 [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 +1100926 A_linearized_continuous[3:5, 3:5] = velocity_drivetrain.A_continuous
927 B_linearized_continuous = numpy.matrix(numpy.zeros((5, 2)))
928 B_linearized_continuous[3:5, :] = velocity_drivetrain.B_continuous
929
930 A, B = controls.c2d(A_linearized_continuous, B_linearized_continuous,
931 dt)
932
933 if i >= 0:
934 K = controls.dlqr(A, B, Q, R)
935 print("K", K)
936 print("eig", numpy.linalg.eig(A - B * K)[0])
937 goal_state = trajectory.goal_state(xva_plan[0, i], xva_plan[1, i])
938 state_error = goal_state - state
939
940 U = (trajectory.ff_voltage(backward_accel_plan, xva_plan[0, i]) + K *
941 (state_error))
942
943 def spline_diffeq(U, t, x):
944 velocity = x[3:5, :]
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100945 theta = x[2, 0]
946 linear_velocity = (velocity[0, 0] + velocity[1, 0]) / 2.0
947 angular_velocity = (velocity[1, 0] - velocity[0, 0]) / (
948 velocity_drivetrain.robot_radius_l +
949 velocity_drivetrain.robot_radius_r)
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100950 accel = (velocity_drivetrain.A_continuous * velocity +
951 velocity_drivetrain.B_continuous * U)
John Park91e69732019-03-03 13:12:43 -0800952 return numpy.matrix([[numpy.cos(theta) * linear_velocity],
953 [numpy.sin(theta) * linear_velocity],
954 [angular_velocity], [accel[0, 0]],
955 [accel[1, 0]]])
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100956
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100957 full_us[:, i] = U
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100958
John Park91e69732019-03-03 13:12:43 -0800959 state = RungeKutta(lambda t, x: spline_diffeq(U, t, x), state, i * dt,
960 dt)
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100961
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100962 pylab.figure()
Austin Schuh7b1e9df2018-12-19 18:04:41 +1100963 pylab.plot(length_plan_t, numpy.array(xva_plan)[0, :], label='x')
964 pylab.plot(length_plan_t, [x[1, 0] for x in length_plan_x], label='v')
965 pylab.plot(length_plan_t, numpy.array(xva_plan)[1, :], label='planv')
966 pylab.plot(length_plan_t, numpy.array(xva_plan)[2, :], label='a')
967
968 pylab.plot(length_plan_t, numpy.array(full_us)[0, :], label='vl')
969 pylab.plot(length_plan_t, numpy.array(full_us)[1, :], label='vr')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100970 pylab.legend()
971
972 pylab.figure()
John Park91e69732019-03-03 13:12:43 -0800973 pylab.plot(numpy.array(states)[0, :],
974 numpy.array(states)[1, :],
975 label='robot')
976 pylab.plot(numpy.array(spline_points)[0, :],
977 numpy.array(spline_points)[1, :],
978 label='spline')
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100979 pylab.legend()
980
Austin Schuh8cb98eb2018-12-05 22:06:45 +1100981 def a(_, x):
982 return 2.0
983 return 2.0 + 0.0001 * x
984
985 v = 0.0
986 for _ in xrange(10):
987 dx = 4.0 / 10.0
988 v = integrate_accel_for_distance(a, v, 0.0, dx)
989 print('v', v)
990
Austin Schuh35d19872018-11-30 15:50:47 +1100991 pylab.show()
992
993
994if __name__ == '__main__':
995 argv = FLAGS(sys.argv)
996 sys.exit(main(argv))