Skip to content

Commit 2e0ac5c

Browse files
author
Shunichi09
authored
Add nmpc cgmres (#13)
* Add NMPC * Update README * Add cgmres * Update readme * Add twowheeled of nmpccgmres * Fix bug
1 parent 2bd2598 commit 2e0ac5c

20 files changed

+556
-158
lines changed

PythonLinearNonlinearControl/common/utils.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,32 @@ def func_x(self, x_1, x_2, u):
116116
k3[:, i] = dt * func(state + k2, u)
117117

118118
return state + (k0 + 2. * k1 + 2. * k2 + k3) / 6.
119+
120+
121+
def line_search(grad, sol, compute_eval_val,
122+
init_alpha=0.001, max_iter=100, update_ratio=1.):
123+
""" line search
124+
Args:
125+
grad (numpy.ndarray): gradient
126+
sol (numpy.ndarray): sol
127+
compute_eval_val (numpy.ndarray): function to compute evaluation value
128+
129+
Returns:
130+
alpha (float): result of line search
131+
"""
132+
assert grad.shape == sol.shape
133+
base_val = np.inf
134+
alpha = init_alpha
135+
original_sol = sol.copy()
136+
137+
for _ in range(max_iter):
138+
updated_sol = original_sol - alpha * grad
139+
eval_val = compute_eval_val(updated_sol)
140+
141+
if eval_val < base_val:
142+
alpha += init_alpha * update_ratio
143+
base_val = eval_val
144+
else:
145+
break
146+
147+
return alpha

PythonLinearNonlinearControl/configs/cartpole.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def terminal_state_cost_fn(terminal_x, terminal_g_x):
148148
* CartPoleConfigModule.TERMINAL_WEIGHT
149149

150150
@staticmethod
151-
def gradient_cost_fn_with_state(x, g_x, terminal=False):
151+
def gradient_cost_fn_state(x, g_x, terminal=False):
152152
""" gradient of costs with respect to the state
153153
154154
Args:
@@ -177,7 +177,7 @@ def gradient_cost_fn_with_state(x, g_x, terminal=False):
177177
return cost_dx * CartPoleConfigModule.TERMINAL_WEIGHT
178178

179179
@staticmethod
180-
def gradient_cost_fn_with_input(x, u):
180+
def gradient_cost_fn_input(x, u):
181181
""" gradient of costs with respect to the input
182182
183183
Args:
@@ -189,7 +189,7 @@ def gradient_cost_fn_with_input(x, u):
189189
return 2. * u * np.diag(CartPoleConfigModule.R)
190190

191191
@staticmethod
192-
def hessian_cost_fn_with_state(x, g_x, terminal=False):
192+
def hessian_cost_fn_state(x, g_x, terminal=False):
193193
""" hessian costs with respect to the state
194194
195195
Args:
@@ -227,7 +227,7 @@ def hessian_cost_fn_with_state(x, g_x, terminal=False):
227227
return hessian[np.newaxis, :, :] * CartPoleConfigModule.TERMINAL_WEIGHT
228228

229229
@staticmethod
230-
def hessian_cost_fn_with_input(x, u):
230+
def hessian_cost_fn_input(x, u):
231231
""" hessian costs with respect to the input
232232
233233
Args:
@@ -242,7 +242,7 @@ def hessian_cost_fn_with_input(x, u):
242242
return np.tile(2.*CartPoleConfigModule.R, (pred_len, 1, 1))
243243

244244
@staticmethod
245-
def hessian_cost_fn_with_input_state(x, u):
245+
def hessian_cost_fn_input_state(x, u):
246246
""" hessian costs with respect to the state and input
247247
248248
Args:

PythonLinearNonlinearControl/configs/first_order_lag.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def terminal_state_cost_fn(terminal_x, terminal_g_x):
115115
* np.diag(FirstOrderLagConfigModule.Sf)
116116

117117
@staticmethod
118-
def gradient_cost_fn_with_state(x, g_x, terminal=False):
118+
def gradient_cost_fn_state(x, g_x, terminal=False):
119119
""" gradient of costs with respect to the state
120120
121121
Args:
@@ -133,7 +133,7 @@ def gradient_cost_fn_with_state(x, g_x, terminal=False):
133133
* np.diag(FirstOrderLagConfigModule.Sf))[np.newaxis, :]
134134

135135
@staticmethod
136-
def gradient_cost_fn_with_input(x, u):
136+
def gradient_cost_fn_input(x, u):
137137
""" gradient of costs with respect to the input
138138
139139
Args:
@@ -146,7 +146,7 @@ def gradient_cost_fn_with_input(x, u):
146146
return 2. * u * np.diag(FirstOrderLagConfigModule.R)
147147

148148
@staticmethod
149-
def hessian_cost_fn_with_state(x, g_x, terminal=False):
149+
def hessian_cost_fn_state(x, g_x, terminal=False):
150150
""" hessian costs with respect to the state
151151
152152
Args:
@@ -165,7 +165,7 @@ def hessian_cost_fn_with_state(x, g_x, terminal=False):
165165
return np.tile(2.*FirstOrderLagConfigModule.Sf, (1, 1, 1))
166166

167167
@staticmethod
168-
def hessian_cost_fn_with_input(x, u):
168+
def hessian_cost_fn_input(x, u):
169169
""" hessian costs with respect to the input
170170
171171
Args:
@@ -181,7 +181,7 @@ def hessian_cost_fn_with_input(x, u):
181181
return np.tile(2.*FirstOrderLagConfigModule.R, (pred_len, 1, 1))
182182

183183
@staticmethod
184-
def hessian_cost_fn_with_input_state(x, u):
184+
def hessian_cost_fn_input_state(x, u):
185185
""" hessian costs with respect to the state and input
186186
187187
Args:

PythonLinearNonlinearControl/configs/make_configs.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .first_order_lag import FirstOrderLagConfigModule
2-
from .two_wheeled import TwoWheeledConfigModule
2+
from .two_wheeled import TwoWheeledConfigModule, TwoWheeledExtendConfigModule
33
from .cartpole import CartPoleConfigModule
4-
from .nonlinear_sample_system import NonlinearSampleSystemConfigModule
4+
from .nonlinear_sample_system import NonlinearSampleSystemConfigModule, NonlinearSampleSystemExtendConfigModule
55

66

77
def make_config(args):
@@ -12,8 +12,12 @@ def make_config(args):
1212
if args.env == "FirstOrderLag":
1313
return FirstOrderLagConfigModule()
1414
elif args.env == "TwoWheeledConst" or args.env == "TwoWheeledTrack":
15+
if args.controller_type == "NMPCCGMRES":
16+
return TwoWheeledExtendConfigModule()
1517
return TwoWheeledConfigModule()
1618
elif args.env == "CartPole":
1719
return CartPoleConfigModule()
1820
elif args.env == "NonlinearSample":
21+
if args.controller_type == "NMPCCGMRES":
22+
return NonlinearSampleSystemExtendConfigModule()
1923
return NonlinearSampleSystemConfigModule()

PythonLinearNonlinearControl/configs/nonlinear_sample_system.py

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,11 @@ def __init__(self):
6262
"threshold": 1e-6,
6363
},
6464
"NMPC": {
65-
"threshold": 1e-5,
66-
"max_iters": 1000,
67-
"learning_rate": 0.1
68-
},
69-
"NMPC-CGMRES": {
70-
"threshold": 1e-3
71-
},
72-
"NMPC-Newton": {
73-
"threshold": 1e-3,
74-
"max_iteration": 500,
75-
"learning_rate": 1e-3
76-
},
65+
"threshold": 0.01,
66+
"max_iters": 5000,
67+
"learning_rate": 0.01,
68+
"optimizer_mode": "conjugate"
69+
}
7770
}
7871

7972
@staticmethod
@@ -133,7 +126,7 @@ def terminal_state_cost_fn(terminal_x, terminal_g_x):
133126
return 0.5 * (terminal_x[0]**2) + 0.5 * (terminal_x[1]**2)
134127

135128
@staticmethod
136-
def gradient_cost_fn_with_state(x, g_x, terminal=False):
129+
def gradient_cost_fn_state(x, g_x, terminal=False):
137130
""" gradient of costs with respect to the state
138131
139132
Args:
@@ -157,7 +150,7 @@ def gradient_cost_fn_with_state(x, g_x, terminal=False):
157150
return cost_dx
158151

159152
@staticmethod
160-
def gradient_cost_fn_with_input(x, u):
153+
def gradient_cost_fn_input(x, u):
161154
""" gradient of costs with respect to the input
162155
163156
Args:
@@ -169,7 +162,7 @@ def gradient_cost_fn_with_input(x, u):
169162
return 2. * u * np.diag(NonlinearSampleSystemConfigModule.R)
170163

171164
@staticmethod
172-
def hessian_cost_fn_with_state(x, g_x, terminal=False):
165+
def hessian_cost_fn_state(x, g_x, terminal=False):
173166
""" hessian costs with respect to the state
174167
175168
Args:
@@ -197,7 +190,7 @@ def hessian_cost_fn_with_state(x, g_x, terminal=False):
197190
return hessian[np.newaxis, :, :]
198191

199192
@staticmethod
200-
def hessian_cost_fn_with_input(x, u):
193+
def hessian_cost_fn_input(x, u):
201194
""" hessian costs with respect to the input
202195
203196
Args:
@@ -212,7 +205,7 @@ def hessian_cost_fn_with_input(x, u):
212205
return np.tile(NonlinearSampleSystemConfigModule.R, (pred_len, 1, 1))
213206

214207
@staticmethod
215-
def hessian_cost_fn_with_input_state(x, u):
208+
def hessian_cost_fn_input_state(x, u):
216209
""" hessian costs with respect to the state and input
217210
218211
Args:
@@ -294,3 +287,67 @@ def gradient_hamiltonian_state(x, lam, u, g_x):
294287

295288
else:
296289
raise NotImplementedError
290+
291+
292+
class NonlinearSampleSystemExtendConfigModule(NonlinearSampleSystemConfigModule):
293+
def __init__(self):
294+
super().__init__()
295+
self.opt_config = {
296+
"NMPCCGMRES": {
297+
"threshold": 1e-3,
298+
"zeta": 100.,
299+
"delta": 0.01,
300+
"alpha": 0.5,
301+
"tf": 1.,
302+
"constraint": True
303+
},
304+
"NMPCNewton": {
305+
"threshold": 1e-3,
306+
"max_iteration": 500,
307+
"learning_rate": 1e-3
308+
}
309+
}
310+
311+
@staticmethod
312+
def gradient_hamiltonian_input_with_constraint(x, lam, u, g_x, dummy_u, raw):
313+
"""
314+
315+
Args:
316+
x (numpy.ndarray): shape(pred_len+1, state_size)
317+
lam (numpy.ndarray): shape(pred_len, state_size)
318+
u (numpy.ndarray): shape(pred_len, input_size)
319+
g_xs (numpy.ndarray): shape(pred_len, state_size)
320+
dummy_u (numpy.ndarray): shape(pred_len, input_size)
321+
raw (numpy.ndarray): shape(pred_len, input_size), Lagrangian for constraints
322+
323+
Returns:
324+
F (numpy.ndarray), shape(pred_len, 3)
325+
"""
326+
if len(x.shape) == 1:
327+
vanilla_F = np.zeros(1)
328+
extend_F = np.zeros(1) # 1 is the same as input size
329+
extend_C = np.zeros(1)
330+
331+
vanilla_F[0] = u[0] + lam[1] + 2. * raw[0] * u[0]
332+
extend_F[0] = -0.01 + 2. * raw[0] * dummy_u[0]
333+
extend_C[0] = u[0]**2 + dummy_u[0]**2 - \
334+
NonlinearSampleSystemConfigModule.INPUT_LOWER_BOUND**2
335+
336+
F = np.concatenate([vanilla_F, extend_F, extend_C])
337+
338+
elif len(x.shape) == 2:
339+
pred_len, _ = u.shape
340+
vanilla_F = np.zeros((pred_len, 1))
341+
extend_F = np.zeros((pred_len, 1)) # 1 is the same as input size
342+
extend_C = np.zeros((pred_len, 1))
343+
344+
for i in range(pred_len):
345+
vanilla_F[i, 0] = \
346+
u[i, 0] + lam[i, 1] + 2. * raw[i, 0] * u[i, 0]
347+
extend_F[i, 0] = -0.01 + 2. * raw[i, 0] * dummy_u[i, 0]
348+
extend_C[i, 0] = u[i, 0]**2 + dummy_u[i, 0]**2 - \
349+
NonlinearSampleSystemConfigModule.INPUT_LOWER_BOUND**2
350+
351+
F = np.concatenate([vanilla_F, extend_F, extend_C], axis=1)
352+
353+
return F

0 commit comments

Comments
 (0)