blob: 0c229c1a44c70c0444e479e38e39fd107865fd3d [file] [log] [blame]
Austin Schuhb67a9782016-01-02 22:55:52 -08001#!/usr/bin/python3
2
3import numpy
4import scipy.optimize
5from matplotlib import pylab
6
7dt = 0.05
8
9# This module computes a NMPC which solves the non-holonomic point
10# stabilization problem for a mobile robot. The algorithm is currently
11# too slow to run on a robot in real-time, but is fast enough to use offline
12# and to use to compare different parameters for NMPCs.
13#
14# Inital algorithm from http://www.ece.ufrgs.br/~fetter/icma05_608.pdf
15
16def cartesian_to_polar(X_cartesian):
17 """Converts a cartesian coordinate to polar.
18
19 Args:
20 X_cartesian, numpy.matrix[3, 1] with x, y, theta as rows.
21
22 Returns:
23 numpy.matrix[3, 1] with e, phi, alpha as rows.
24 """
25 phi = numpy.arctan2(X_cartesian[1, 0], X_cartesian[0, 0])
26 return numpy.matrix([[numpy.hypot(X_cartesian[0, 0], X_cartesian[1, 0])],
27 [phi],
28 [X_cartesian[2, 0] - phi]])
29
30def polar_to_cartesian(X_polar):
31 """Converts a polar coordinate to cartesian.
32
33 Args:
34 X_polar, numpy.matrix[3, 1] with e, phi, alpha as rows.
35
36 Returns:
37 numpy.matrix[3, 1] with x, y, theta as rows.
38 """
39 return numpy.matrix([[X_polar[0, 0] * numpy.cos(X_polar[1, 0])],
40 [X_polar[0, 0] * numpy.sin(X_polar[1, 0])],
41 [X_polar[1, 0] + X_polar[2, 0]]])
42
43def simulate_dynamics(X_cartesian, U):
44 """Calculates the robot location after 1 timestep.
45
46 Args:
47 X_cartesian, numpy.matrix[3, 1] with the starting location in
48 cartesian coordinates.
49 U, numpy.matrix[2, 1] with velocity, angular_velocity as rows.
50
51 Returns:
52 numpy.matrix[3, 1] with the cartesian coordinate.
53 """
54 X_cartesian += numpy.matrix(
55 [[U[0, 0] * numpy.cos(X_cartesian[2, 0]) * dt],
56 [U[0, 0] * numpy.sin(X_cartesian[2, 0]) * dt],
57 [U[1, 0] * dt]])
58
59 return X_cartesian
60
61def U_from_array(U_array):
62 """Converts the U array from the optimizer to a bunch of column vectors.
63
64 Args:
65 U_array, numpy.array[N] The U coordinates in v, av, v, av, ...
66
67 Returns:
68 numpy.matrix[2, N/2] with [[v, v, v, ...], [av, av, av, ...]]
69 """
70 return numpy.matrix(U_array).reshape((2, -1), order='F')
71
72def U_to_array(U_matrix):
73 """Converts the U matrix to the U array for the optimizer.
74
75 Args:
76 U_matrix, numpy.matrix[2, N/2] with [[v, v, v, ...], [av, av, av, ...]]
77
78 Returns:
79 numpy.array[N] The U coordinates in v, av, v, av, ...
80 """
81 return numpy.array(U_matrix.reshape((1, -1), order='F'))
82
83def cost(U_array, X_cartesian):
84 """Computes the cost given the inital position and the U array.
85
86 Args:
87 U_array: numpy.array[N] The U coordinates.
88 X_cartesian: numpy.matrix[3, 1] The cartesian coordinates of the starting
89 location.
90
91 Returns:
92 double, The quadratic cost of evaluating U.
93 """
94 X_cartesian_mod = X_cartesian.copy()
95 U_matrix = U_from_array(U_array)
96 my_cost = 0
97 Q = numpy.matrix([[0.01, 0, 0],
98 [0, 0.01, 0],
99 [0, 0, 0.01]]) / dt / dt
100 R = numpy.matrix([[0.001, 0],
101 [0, 0.001]]) / dt / dt
102 for U in U_matrix.T:
103 # TODO(austin): Let it go to the point from either side.
104 U = U.T
105 X_cartesian_mod = simulate_dynamics(X_cartesian_mod, U)
106 X_current_polar = cartesian_to_polar(X_cartesian_mod)
107 my_cost += U.T * R * U + X_current_polar.T * Q * X_current_polar
108
109 return my_cost
110
111if __name__ == '__main__':
112 X_cartesian = numpy.matrix([[-1.0],
113 [-1.0],
114 [0.0]])
115 x_array = []
116 y_array = []
117 theta_array = []
118
119 e_array = []
120 phi_array = []
121 alpha_array = []
122
123 cost_array = []
124
125 time_array = []
126 u0_array = []
127 u1_array = []
128
129 num_steps = 20
130
131 U_array = numpy.zeros((num_steps * 2))
132 for i in range(400):
133 print('Iteration', i)
134 # Solve the NMPC
135 U_array, fx, _, _, _ = scipy.optimize.fmin_slsqp(
136 cost, U_array.copy(), bounds = [(-1, 1), (-7, 7)] * num_steps,
137 args=(X_cartesian,), iter=500, full_output=True)
138 U_matrix = U_from_array(U_array)
139
140 # Simulate the dynamics
141 X_cartesian = simulate_dynamics(X_cartesian, U_matrix[:, 0])
142
143 # Save variables for plotting.
144 cost_array.append(fx[0, 0])
145 u0_array.append(U_matrix[0, 0])
146 u1_array.append(U_matrix[1, 0])
147 x_array.append(X_cartesian[0, 0])
148 y_array.append(X_cartesian[1, 0])
149 theta_array.append(X_cartesian[2, 0])
150 time_array.append(i * dt)
151 X_polar = cartesian_to_polar(X_cartesian)
152 e_array.append(X_polar[0, 0])
153 phi_array.append(X_polar[1, 0])
154 alpha_array.append(X_polar[2, 0])
155
156 U_array = U_to_array(numpy.hstack((U_matrix[:, 1:], numpy.matrix([[0], [0]]))))
157
158 if fx < 1e-05:
159 print('Cost is', fx, 'after', i, 'cycles, finishing early')
160 break
161
162 # Plot
163 pylab.figure('xy')
164 pylab.plot(x_array, y_array, label = 'trajectory')
165
166 pylab.figure('time')
167 pylab.plot(time_array, x_array, label='x')
168 pylab.plot(time_array, y_array, label='y')
169 pylab.plot(time_array, theta_array, label = 'theta')
170 pylab.plot(time_array, e_array, label='e')
171 pylab.plot(time_array, phi_array, label='phi')
172 pylab.plot(time_array, alpha_array, label='alpha')
173 pylab.plot(time_array, cost_array, label='cost')
174 pylab.legend()
175
176 pylab.figure('u')
177 pylab.plot(time_array, u0_array, label='u0')
178 pylab.plot(time_array, u1_array, label='u1')
179 pylab.legend()
180
181 pylab.show()