Skip to content

Commit 014a312

Browse files
author
Shunichi09
authored
Merge pull request Shunichi09#6 from Shunichi09/develop
Add: TwoWheeledTrack Env
2 parents 1e768f7 + dd8880c commit 014a312

23 files changed

+731
-42
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
11
import numpy as np
2+
3+
def rotate_pos(pos, angle):
4+
""" Transformation the coordinate in the angle
5+
6+
Args:
7+
pos (numpy.ndarray): local state, shape(data_size, 2)
8+
angle (float): rotate angle, in radians
9+
Returns:
10+
rotated_pos (numpy.ndarray): shape(data_size, 2)
11+
"""
12+
rot_mat = np.array([[np.cos(angle), -np.sin(angle)],
13+
[np.sin(angle), np.cos(angle)]])
14+
15+
return np.dot(pos, rot_mat.T)
16+
17+
def fit_angle_in_range(angles, min_angle=-np.pi, max_angle=np.pi):
18+
""" Check angle range and correct the range
19+
20+
Args:
21+
angle (numpy.ndarray): in radians
22+
min_angle (float): maximum of range in radians, default -pi
23+
max_angle (float): minimum of range in radians, default pi
24+
Returns:
25+
fitted_angle (numpy.ndarray): range angle in radians
26+
"""
27+
if max_angle < min_angle:
28+
raise ValueError("max angle must be greater than min angle")
29+
if (max_angle - min_angle) < 2.0 * np.pi:
30+
raise ValueError("difference between max_angle \
31+
and min_angle must be greater than 2.0 * pi")
32+
33+
output = np.array(angles)
34+
output_shape = output.shape
35+
36+
output = output.flatten()
37+
output -= min_angle
38+
output %= 2 * np.pi
39+
output += 2 * np.pi
40+
output %= 2 * np.pi
41+
output += min_angle
42+
43+
output = np.minimum(max_angle, np.maximum(min_angle, output))
44+
return output.reshape(output_shape)

PythonLinearNonlinearControl/configs/cartpole.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
class CartPoleConfigModule():
44
# parameters
55
ENV_NAME = "CartPole-v0"
6+
PLANNER_TYPE = "Const"
67
TYPE = "Nonlinear"
78
TASK_HORIZON = 500
89
PRED_LEN = 50
910
STATE_SIZE = 4
1011
INPUT_SIZE = 1
1112
DT = 0.02
1213
# cost parameters
13-
R = np.diag([1.]) # 0.01 is worked for MPPI and CEM and MPPIWilliams
14+
R = np.diag([0.01]) # 0.01 is worked for MPPI and CEM and MPPIWilliams
1415
# 1. is worked for iLQR
15-
Terminal_Weight = 1.
16+
TERMINAL_WEIGHT = 1.
1617
Q = None
1718
Sf = None
1819
# bounds
@@ -23,6 +24,7 @@ class CartPoleConfigModule():
2324
MC = 1.
2425
L = 0.5
2526
G = 9.81
27+
CART_SIZE = (0.15, 0.1)
2628

2729
def __init__(self):
2830
"""
@@ -76,6 +78,7 @@ def __init__(self):
7678
@staticmethod
7779
def input_cost_fn(u):
7880
""" input cost functions
81+
7982
Args:
8083
u (numpy.ndarray): input, shape(pred_len, input_size)
8184
or shape(pop_size, pred_len, input_size)
@@ -88,6 +91,7 @@ def input_cost_fn(u):
8891
@staticmethod
8992
def state_cost_fn(x, g_x):
9093
""" state cost function
94+
9195
Args:
9296
x (numpy.ndarray): state, shape(pred_len, state_size)
9397
or shape(pop_size, pred_len, state_size)
@@ -118,6 +122,7 @@ def state_cost_fn(x, g_x):
118122
@staticmethod
119123
def terminal_state_cost_fn(terminal_x, terminal_g_x):
120124
"""
125+
121126
Args:
122127
terminal_x (numpy.ndarray): terminal state,
123128
shape(state_size, ) or shape(pop_size, state_size)
@@ -133,13 +138,13 @@ def terminal_state_cost_fn(terminal_x, terminal_g_x):
133138
+ 12. * ((np.cos(terminal_x[:, 2]) + 1.)**2) \
134139
+ 0.1 * (terminal_x[:, 1]**2) \
135140
+ 0.1 * (terminal_x[:, 3]**2))[:, np.newaxis] \
136-
* CartPoleConfigModule.Terminal_Weight
141+
* CartPoleConfigModule.TERMINAL_WEIGHT
137142

138143
return (6. * (terminal_x[0]**2) \
139144
+ 12. * ((np.cos(terminal_x[2]) + 1.)**2) \
140145
+ 0.1 * (terminal_x[1]**2) \
141146
+ 0.1 * (terminal_x[3]**2)) \
142-
* CartPoleConfigModule.Terminal_Weight
147+
* CartPoleConfigModule.TERMINAL_WEIGHT
143148

144149
@staticmethod
145150
def gradient_cost_fn_with_state(x, g_x, terminal=False):
@@ -168,7 +173,7 @@ def gradient_cost_fn_with_state(x, g_x, terminal=False):
168173
cost_dx3 = 0.2 * x[3]
169174
cost_dx = np.array([[cost_dx0, cost_dx1, cost_dx2, cost_dx3]])
170175

171-
return cost_dx * CartPoleConfigModule.Terminal_Weight
176+
return cost_dx * CartPoleConfigModule.TERMINAL_WEIGHT
172177

173178
@staticmethod
174179
def gradient_cost_fn_with_input(x, u):
@@ -177,7 +182,6 @@ def gradient_cost_fn_with_input(x, u):
177182
Args:
178183
x (numpy.ndarray): state, shape(pred_len, state_size)
179184
u (numpy.ndarray): goal state, shape(pred_len, input_size)
180-
181185
Returns:
182186
l_u (numpy.ndarray): gradient of cost, shape(pred_len, input_size)
183187
"""
@@ -190,7 +194,6 @@ def hessian_cost_fn_with_state(x, g_x, terminal=False):
190194
Args:
191195
x (numpy.ndarray): state, shape(pred_len, state_size)
192196
g_x (numpy.ndarray): goal state, shape(pred_len, state_size)
193-
194197
Returns:
195198
l_xx (numpy.ndarray): gradient of cost,
196199
shape(pred_len, state_size, state_size) or
@@ -220,7 +223,7 @@ def hessian_cost_fn_with_state(x, g_x, terminal=False):
220223
* -np.cos(x[2])
221224
hessian[3, 3] = 0.2
222225

223-
return hessian[np.newaxis, :, :] * CartPoleConfigModule.Terminal_Weight
226+
return hessian[np.newaxis, :, :] * CartPoleConfigModule.TERMINAL_WEIGHT
224227

225228
@staticmethod
226229
def hessian_cost_fn_with_input(x, u):
@@ -229,7 +232,6 @@ def hessian_cost_fn_with_input(x, u):
229232
Args:
230233
x (numpy.ndarray): state, shape(pred_len, state_size)
231234
u (numpy.ndarray): goal state, shape(pred_len, input_size)
232-
233235
Returns:
234236
l_uu (numpy.ndarray): gradient of cost,
235237
shape(pred_len, input_size, input_size)
@@ -245,7 +247,6 @@ def hessian_cost_fn_with_input_state(x, u):
245247
Args:
246248
x (numpy.ndarray): state, shape(pred_len, state_size)
247249
u (numpy.ndarray): goal state, shape(pred_len, input_size)
248-
249250
Returns:
250251
l_ux (numpy.ndarray): gradient of cost ,
251252
shape(pred_len, input_size, state_size)

PythonLinearNonlinearControl/configs/make_configs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ def make_config(args):
99
"""
1010
if args.env == "FirstOrderLag":
1111
return FirstOrderLagConfigModule()
12-
elif args.env == "TwoWheeledConst" or args.env == "TwoWheeled":
12+
elif args.env == "TwoWheeledConst" or args.env == "TwoWheeledTrack":
1313
return TwoWheeledConfigModule()
1414
elif args.env == "CartPole":
1515
return CartPoleConfigModule()

PythonLinearNonlinearControl/configs/two_wheeled.py

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
11
import numpy as np
2+
from matplotlib.axes import Axes
3+
4+
from ..plotters.plot_objs import square_with_angle, square
5+
from ..common.utils import fit_angle_in_range
26

37
class TwoWheeledConfigModule():
48
# parameters
59
ENV_NAME = "TwoWheeled-v0"
610
TYPE = "Nonlinear"
11+
N_AHEAD = 1
712
TASK_HORIZON = 1000
813
PRED_LEN = 20
914
STATE_SIZE = 3
1015
INPUT_SIZE = 2
1116
DT = 0.01
1217
# cost parameters
18+
# for Const goal
19+
"""
1320
R = np.diag([0.1, 0.1])
1421
Q = np.diag([1., 1., 0.01])
1522
Sf = np.diag([5., 5., 1.])
23+
"""
24+
# for track goal
25+
R = np.diag([0.01, 0.01])
26+
Q = np.diag([2.5, 2.5, 0.01])
27+
Sf = np.diag([2.5, 2.5, 0.01])
28+
1629
# bounds
17-
INPUT_LOWER_BOUND = np.array([-1.5, 3.14])
30+
INPUT_LOWER_BOUND = np.array([-1.5, -3.14])
1831
INPUT_UPPER_BOUND = np.array([1.5, 3.14])
32+
# parameters
33+
CAR_SIZE = 0.2
34+
WHEELE_SIZE = (0.075, 0.015)
1935

2036
def __init__(self):
2137
"""
@@ -78,6 +94,27 @@ def input_cost_fn(u):
7894
"""
7995
return (u**2) * np.diag(TwoWheeledConfigModule.R)
8096

97+
@staticmethod
98+
def fit_diff_in_range(diff_x):
99+
""" fit difference state in range(angle)
100+
101+
Args:
102+
diff_x (numpy.ndarray):
103+
shape(pop_size, pred_len, state_size) or
104+
shape(pred_len, state_size) or
105+
shape(state_size, )
106+
Returns:
107+
fitted_diff_x (numpy.ndarray): same shape as diff_x
108+
"""
109+
if len(diff_x.shape) == 3:
110+
diff_x[:, :, -1] = fit_angle_in_range(diff_x[:, :, -1])
111+
elif len(diff_x.shape) == 2:
112+
diff_x[:, -1] = fit_angle_in_range(diff_x[:, -1])
113+
elif len(diff_x.shape) == 1:
114+
diff_x[-1] = fit_angle_in_range(diff_x[-1])
115+
116+
return diff_x
117+
81118
@staticmethod
82119
def state_cost_fn(x, g_x):
83120
""" state cost function
@@ -90,7 +127,8 @@ def state_cost_fn(x, g_x):
90127
cost (numpy.ndarray): cost of state, shape(pred_len, state_size) or
91128
shape(pop_size, pred_len, state_size)
92129
"""
93-
return ((x - g_x)**2) * np.diag(TwoWheeledConfigModule.Q)
130+
diff = TwoWheeledConfigModule.fit_diff_in_range(x - g_x)
131+
return ((diff)**2) * np.diag(TwoWheeledConfigModule.Q)
94132

95133
@staticmethod
96134
def terminal_state_cost_fn(terminal_x, terminal_g_x):
@@ -104,8 +142,10 @@ def terminal_state_cost_fn(terminal_x, terminal_g_x):
104142
cost (numpy.ndarray): cost of state, shape(pred_len, ) or
105143
shape(pop_size, pred_len)
106144
"""
107-
return ((terminal_x - terminal_g_x)**2) \
108-
* np.diag(TwoWheeledConfigModule.Sf)
145+
terminal_diff = TwoWheeledConfigModule.fit_diff_in_range(terminal_x \
146+
- terminal_g_x)
147+
148+
return ((terminal_diff)**2) * np.diag(TwoWheeledConfigModule.Sf)
109149

110150
@staticmethod
111151
def gradient_cost_fn_with_state(x, g_x, terminal=False):
@@ -119,10 +159,12 @@ def gradient_cost_fn_with_state(x, g_x, terminal=False):
119159
l_x (numpy.ndarray): gradient of cost, shape(pred_len, state_size)
120160
or shape(1, state_size)
121161
"""
162+
diff = TwoWheeledConfigModule.fit_diff_in_range(x - g_x)
163+
122164
if not terminal:
123-
return 2. * (x - g_x) * np.diag(TwoWheeledConfigModule.Q)
165+
return 2. * (diff) * np.diag(TwoWheeledConfigModule.Q)
124166

125-
return (2. * (x - g_x) \
167+
return (2. * (diff) \
126168
* np.diag(TwoWheeledConfigModule.Sf))[np.newaxis, :]
127169

128170
@staticmethod

PythonLinearNonlinearControl/controllers/make_controllers.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ def make_controller(args, config, model):
2121
elif args.controller_type == "iLQR":
2222
return iLQR(config, model)
2323
elif args.controller_type == "DDP":
24-
return DDP(config, model)
24+
return DDP(config, model)
25+
26+
raise ValueError("No controller: {}".format(args.controller_type))

PythonLinearNonlinearControl/controllers/mpc.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
from logging import getLogger
22

33
import numpy as np
4-
from cvxopt import matrix, solvers
4+
from scipy.optimize import minimize
5+
from scipy.optimize import LinearConstraint
56

67
from .controller import Controller
78
from ..envs.cost import calc_cost
@@ -61,6 +62,7 @@ def __init__(self, config, model):
6162
self.F = None
6263
self.f = None
6364
self.setup()
65+
self.prev_sol = np.zeros(self.input_size*self.pred_len)
6466

6567
# history
6668
self.history_u = [np.zeros(self.input_size)]
@@ -183,19 +185,38 @@ def obtain_sol(self, curr_x, g_xs):
183185

184186
ub = np.array(b).flatten()
185187

188+
# using cvxopt
189+
def optimized_func(dt_us):
190+
return (np.dot(dt_us, np.dot(H, dt_us.reshape(-1, 1))) \
191+
- np.dot(G.T, dt_us.reshape(-1, 1)))[0]
192+
193+
# constraint
194+
lb = np.array([-np.inf for _ in range(len(ub))]) # one side cons
195+
cons = LinearConstraint(A, lb, ub)
196+
# solve
197+
opt_sol = minimize(optimized_func, self.prev_sol.flatten(),\
198+
constraints=[cons])
199+
opt_dt_us = opt_sol.x
200+
201+
""" using cvxopt ver,
202+
if you want to solve more quick please use cvxopt instead of scipy
203+
186204
# make cvxpy problem formulation
187205
P = 2*matrix(H)
188206
q = matrix(-1 * G)
189207
A = matrix(A)
190208
b = matrix(ub)
191209
192210
# solve the problem
193-
opt_result = solvers.qp(P, q, G=A, h=b)
194-
opt_dt_us = np.array(list(opt_result['x']))
211+
opt_sol = solvers.qp(P, q, G=A, h=b)
212+
opt_dt_us = np.array(list(opt_sol['x']))
213+
"""
214+
195215
# to dt form
196216
opt_dt_u_seq = np.cumsum(opt_dt_us.reshape(self.pred_len,\
197217
self.input_size),
198218
axis=0)
219+
self.prev_sol = opt_dt_u_seq.copy()
199220

200221
opt_u_seq = opt_dt_u_seq + self.history_u[-1]
201222

0 commit comments

Comments
 (0)