blob: 2f138070d27ef8fa7f559d1394f0c558cd986e34 [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
Ravago Jones5127ccc2022-07-31 16:32:45 -070016
Austin Schuhb67a9782016-01-02 22:55:52 -080017def cartesian_to_polar(X_cartesian):
Ravago Jones5127ccc2022-07-31 16:32:45 -070018 """Converts a cartesian coordinate to polar.
Austin Schuhb67a9782016-01-02 22:55:52 -080019
20 Args:
21 X_cartesian, numpy.matrix[3, 1] with x, y, theta as rows.
22
23 Returns:
24 numpy.matrix[3, 1] with e, phi, alpha as rows.
25 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070026 phi = numpy.arctan2(X_cartesian[1, 0], X_cartesian[0, 0])
27 return numpy.matrix([[numpy.hypot(X_cartesian[0, 0], X_cartesian[1, 0])],
28 [phi], [X_cartesian[2, 0] - phi]])
29
Austin Schuhb67a9782016-01-02 22:55:52 -080030
31def polar_to_cartesian(X_polar):
Ravago Jones5127ccc2022-07-31 16:32:45 -070032 """Converts a polar coordinate to cartesian.
Austin Schuhb67a9782016-01-02 22:55:52 -080033
34 Args:
35 X_polar, numpy.matrix[3, 1] with e, phi, alpha as rows.
36
37 Returns:
38 numpy.matrix[3, 1] with x, y, theta as rows.
39 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070040 return numpy.matrix([[X_polar[0, 0] * numpy.cos(X_polar[1, 0])],
41 [X_polar[0, 0] * numpy.sin(X_polar[1, 0])],
42 [X_polar[1, 0] + X_polar[2, 0]]])
43
Austin Schuhb67a9782016-01-02 22:55:52 -080044
45def simulate_dynamics(X_cartesian, U):
Ravago Jones5127ccc2022-07-31 16:32:45 -070046 """Calculates the robot location after 1 timestep.
Austin Schuhb67a9782016-01-02 22:55:52 -080047
48 Args:
49 X_cartesian, numpy.matrix[3, 1] with the starting location in
50 cartesian coordinates.
51 U, numpy.matrix[2, 1] with velocity, angular_velocity as rows.
52
53 Returns:
54 numpy.matrix[3, 1] with the cartesian coordinate.
55 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070056 X_cartesian += numpy.matrix([[U[0, 0] * numpy.cos(X_cartesian[2, 0]) * dt],
57 [U[0, 0] * numpy.sin(X_cartesian[2, 0]) * dt],
58 [U[1, 0] * dt]])
Austin Schuhb67a9782016-01-02 22:55:52 -080059
Ravago Jones5127ccc2022-07-31 16:32:45 -070060 return X_cartesian
61
Austin Schuhb67a9782016-01-02 22:55:52 -080062
63def U_from_array(U_array):
Ravago Jones5127ccc2022-07-31 16:32:45 -070064 """Converts the U array from the optimizer to a bunch of column vectors.
Austin Schuhb67a9782016-01-02 22:55:52 -080065
66 Args:
67 U_array, numpy.array[N] The U coordinates in v, av, v, av, ...
68
69 Returns:
70 numpy.matrix[2, N/2] with [[v, v, v, ...], [av, av, av, ...]]
71 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070072 return numpy.matrix(U_array).reshape((2, -1), order='F')
73
Austin Schuhb67a9782016-01-02 22:55:52 -080074
75def U_to_array(U_matrix):
Ravago Jones5127ccc2022-07-31 16:32:45 -070076 """Converts the U matrix to the U array for the optimizer.
Austin Schuhb67a9782016-01-02 22:55:52 -080077
78 Args:
79 U_matrix, numpy.matrix[2, N/2] with [[v, v, v, ...], [av, av, av, ...]]
80
81 Returns:
82 numpy.array[N] The U coordinates in v, av, v, av, ...
83 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070084 return numpy.array(U_matrix.reshape((1, -1), order='F'))
85
Austin Schuhb67a9782016-01-02 22:55:52 -080086
87def cost(U_array, X_cartesian):
Ravago Jones5127ccc2022-07-31 16:32:45 -070088 """Computes the cost given the inital position and the U array.
Austin Schuhb67a9782016-01-02 22:55:52 -080089
90 Args:
91 U_array: numpy.array[N] The U coordinates.
92 X_cartesian: numpy.matrix[3, 1] The cartesian coordinates of the starting
93 location.
94
95 Returns:
96 double, The quadratic cost of evaluating U.
97 """
Ravago Jones5127ccc2022-07-31 16:32:45 -070098 X_cartesian_mod = X_cartesian.copy()
99 U_matrix = U_from_array(U_array)
100 my_cost = 0
101 Q = numpy.matrix([[0.01, 0, 0], [0, 0.01, 0], [0, 0, 0.01]]) / dt / dt
102 R = numpy.matrix([[0.001, 0], [0, 0.001]]) / dt / dt
103 for U in U_matrix.T:
104 # TODO(austin): Let it go to the point from either side.
105 U = U.T
106 X_cartesian_mod = simulate_dynamics(X_cartesian_mod, U)
107 X_current_polar = cartesian_to_polar(X_cartesian_mod)
108 my_cost += U.T * R * U + X_current_polar.T * Q * X_current_polar
Austin Schuhb67a9782016-01-02 22:55:52 -0800109
Ravago Jones5127ccc2022-07-31 16:32:45 -0700110 return my_cost
111
Austin Schuhb67a9782016-01-02 22:55:52 -0800112
113if __name__ == '__main__':
Ravago Jones5127ccc2022-07-31 16:32:45 -0700114 X_cartesian = numpy.matrix([[-1.0], [-1.0], [0.0]])
115 x_array = []
116 y_array = []
117 theta_array = []
Austin Schuhb67a9782016-01-02 22:55:52 -0800118
Ravago Jones5127ccc2022-07-31 16:32:45 -0700119 e_array = []
120 phi_array = []
121 alpha_array = []
Austin Schuhb67a9782016-01-02 22:55:52 -0800122
Ravago Jones5127ccc2022-07-31 16:32:45 -0700123 cost_array = []
Austin Schuhb67a9782016-01-02 22:55:52 -0800124
Ravago Jones5127ccc2022-07-31 16:32:45 -0700125 time_array = []
126 u0_array = []
127 u1_array = []
Austin Schuhb67a9782016-01-02 22:55:52 -0800128
Ravago Jones5127ccc2022-07-31 16:32:45 -0700129 num_steps = 20
Austin Schuhb67a9782016-01-02 22:55:52 -0800130
Ravago Jones5127ccc2022-07-31 16:32:45 -0700131 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(cost,
136 U_array.copy(),
137 bounds=[(-1, 1),
138 (-7, 7)] *
139 num_steps,
140 args=(X_cartesian, ),
141 iter=500,
142 full_output=True)
143 U_matrix = U_from_array(U_array)
Austin Schuhb67a9782016-01-02 22:55:52 -0800144
Ravago Jones5127ccc2022-07-31 16:32:45 -0700145 # Simulate the dynamics
146 X_cartesian = simulate_dynamics(X_cartesian, U_matrix[:, 0])
Austin Schuhb67a9782016-01-02 22:55:52 -0800147
Ravago Jones5127ccc2022-07-31 16:32:45 -0700148 # Save variables for plotting.
149 cost_array.append(fx[0, 0])
150 u0_array.append(U_matrix[0, 0])
151 u1_array.append(U_matrix[1, 0])
152 x_array.append(X_cartesian[0, 0])
153 y_array.append(X_cartesian[1, 0])
154 theta_array.append(X_cartesian[2, 0])
155 time_array.append(i * dt)
156 X_polar = cartesian_to_polar(X_cartesian)
157 e_array.append(X_polar[0, 0])
158 phi_array.append(X_polar[1, 0])
159 alpha_array.append(X_polar[2, 0])
Austin Schuhb67a9782016-01-02 22:55:52 -0800160
Ravago Jones5127ccc2022-07-31 16:32:45 -0700161 U_array = U_to_array(
162 numpy.hstack((U_matrix[:, 1:], numpy.matrix([[0], [0]]))))
Austin Schuhb67a9782016-01-02 22:55:52 -0800163
Ravago Jones5127ccc2022-07-31 16:32:45 -0700164 if fx < 1e-05:
165 print('Cost is', fx, 'after', i, 'cycles, finishing early')
166 break
Austin Schuhb67a9782016-01-02 22:55:52 -0800167
Ravago Jones5127ccc2022-07-31 16:32:45 -0700168 # Plot
169 pylab.figure('xy')
170 pylab.plot(x_array, y_array, label='trajectory')
Austin Schuhb67a9782016-01-02 22:55:52 -0800171
Ravago Jones5127ccc2022-07-31 16:32:45 -0700172 pylab.figure('time')
173 pylab.plot(time_array, x_array, label='x')
174 pylab.plot(time_array, y_array, label='y')
175 pylab.plot(time_array, theta_array, label='theta')
176 pylab.plot(time_array, e_array, label='e')
177 pylab.plot(time_array, phi_array, label='phi')
178 pylab.plot(time_array, alpha_array, label='alpha')
179 pylab.plot(time_array, cost_array, label='cost')
180 pylab.legend()
Austin Schuhb67a9782016-01-02 22:55:52 -0800181
Ravago Jones5127ccc2022-07-31 16:32:45 -0700182 pylab.figure('u')
183 pylab.plot(time_array, u0_array, label='u0')
184 pylab.plot(time_array, u1_array, label='u1')
185 pylab.legend()
Austin Schuhb67a9782016-01-02 22:55:52 -0800186
Ravago Jones5127ccc2022-07-31 16:32:45 -0700187 pylab.show()