99from mujoco .mjx ._src import smooth
1010
1111from mpx .jax_ocp_solvers .jax_ocp_solvers import optimizers
12- from mpx .utils .objectives import penalty
1312
1413
1514dir_path = os .path .dirname (os .path .realpath (__file__ ))
2221N = 50
2322mpc_frequency = 50.0
2423solver_mode = "equality"
25- equality_num_alpha = 16
24+ equality_num_alpha = 8
2625
2726regularization = jnp .array (1e-6 ,dtype = jnp .float32 )
2827merit_penalty = jnp .array (1e-6 ,dtype = jnp .float32 )
3938n = nq + nv
4039m = nv + n_joints
4140equality_dim = nv
41+ inequality_dim = 2 * n_joints
4242
4343qacc_slice = slice (0 , nv )
4444tau_slice = slice (nv , nv + n_joints )
6868 raise ValueError ("All controlled Piper joints must define position limits" )
6969joint_position_min = jnp .asarray (_MODEL .jnt_range [:n_joints , 0 ])
7070joint_position_max = jnp .asarray (_MODEL .jnt_range [:n_joints , 1 ])
71- joint_limit_barrier_weight = 0.01
72- joint_limit_barrier_relaxation = 0.05
71+ barrier_parameter = jnp . array ( 1.0 , dtype = jnp . float32 )
72+ multiplier_regularization = jnp . array ( 1e-8 , dtype = jnp . float32 )
7373_INITIAL_DATA = mujoco .MjData (_MODEL )
7474_INITIAL_DATA .qpos [:] = q0
7575mujoco .mj_forward (_MODEL , _INITIAL_DATA )
@@ -130,6 +130,18 @@ def _equilibrium_control(qpos, qvel):
130130 tau = data .qfrc_bias - data .qfrc_passive
131131 return jnp .concatenate ([qacc , tau ])
132132
133+
134+ def inequality (x , u , t , parameter ):
135+ """Joint position bounds, feasible when values are <= 0."""
136+ del u , t , parameter
137+ qpos , _ = _state_parts (x )
138+ return jnp .concatenate (
139+ [
140+ joint_position_min - qpos ,
141+ qpos - joint_position_max ,
142+ ]
143+ )
144+
133145def cost (W , reference , x , u , t ):
134146 qpos , qvel = _state_parts (x )
135147 q = qpos
@@ -148,32 +160,18 @@ def cost(W, reference, x, u, t):
148160 end_effector_position = data .geom_xpos [_CONTACT_ID [0 ]]
149161 end_effector_error = end_effector_position - p_ref
150162 joint_error = q - q_ref
151- joint_limit_cost = jnp .sum (
152- penalty (
153- q - joint_position_min ,
154- alpha = joint_limit_barrier_weight ,
155- sigma = joint_limit_barrier_relaxation ,
156- )
157- + penalty (
158- joint_position_max - q ,
159- alpha = joint_limit_barrier_weight ,
160- sigma = joint_limit_barrier_relaxation ,
161- )
162- )
163163
164164 stage_cost = (
165165 joint_error .T @ W ["Qq" ] @ joint_error
166166 + (dq - dq_ref ).T @ W ["Qdq" ] @ (dq - dq_ref )
167167 + acc .T @ W ["Qacc" ] @ acc
168168 + tau .T @ W ["Qtau" ] @ tau
169169 + end_effector_error .T @ W ["Qee" ] @ end_effector_error
170- + joint_limit_cost
171170 )
172171 terminal_cost = (
173172 joint_error .T @ W ["Qq" ] @ joint_error
174173 + (dq - dq_ref ).T @ W ["Qdq" ] @ (dq - dq_ref )
175174 + end_effector_error .T @ W ["Qee_final" ] @ end_effector_error
176- + joint_limit_cost
177175 )
178176 return jnp .where (t == N , 0.5 * terminal_cost , 0.5 * stage_cost )
179177
@@ -184,13 +182,18 @@ class InverseDynamicsMPCData(PyTreeNode):
184182 U0 : jnp .ndarray
185183 V0 : jnp .ndarray
186184 Veq0 : jnp .ndarray
185+ Vineq0 : jnp .ndarray
186+ slack0 : jnp .ndarray
187187 W : jnp .ndarray
188188 regularization : jnp .ndarray
189189 merit_penalty : jnp .ndarray
190+ barrier_parameter : jnp .ndarray
190191
191192
192193@partial (jax .jit , static_argnums = (0 , 1 ))
193- def _update_warm_start (horizon , shift , u_ref , x0 , X_prev , U_prev , X , U , V , Veq ):
194+ def _update_warm_start (
195+ horizon , shift , u_ref , x0 , X_prev , U_prev , X , U , V , Veq , Vineq , slack
196+ ):
194197 q_slice = slice (0 , n_joints )
195198 dq_slice = slice (nq , nq + n_joints )
196199 u_fallback_idx = 1 if horizon > 1 else 0
@@ -205,6 +208,8 @@ def safe_update():
205208 shift_trajectory (X ),
206209 shift_trajectory (V ),
207210 shift_trajectory (Veq ),
211+ shift_trajectory (Vineq ),
212+ shift_trajectory (slack ),
208213 X [1 , q_slice ],
209214 X [1 , dq_slice ],
210215 )
@@ -215,6 +220,8 @@ def unsafe_update():
215220 jnp .tile (x0 , (horizon + 1 , 1 )),
216221 jnp .zeros_like (X_prev ),
217222 jnp .zeros ((horizon , equality_dim ), dtype = X_prev .dtype ),
223+ shift_trajectory (Vineq ),
224+ shift_trajectory (slack ),
218225 X_prev [1 , q_slice ],
219226 X_prev [1 , dq_slice ],
220227 )
@@ -239,15 +246,24 @@ def __init__(self, config, limited_memory=False):
239246 self .initial_U0 = jnp .tile (config .u_ref , (config .N , 1 ))
240247 self .initial_V0 = jnp .zeros ((config .N + 1 , config .n ))
241248 self .initial_Veq0 = jnp .zeros ((config .N , config .equality_dim ))
249+ initial_g = jax .vmap (config .inequality )(
250+ self .initial_X0 [:- 1 ],
251+ self .initial_U0 ,
252+ jnp .arange (config .N ),
253+ jnp .zeros ((config .N , 1 )),
254+ )
255+ self .initial_slack0 = jnp .maximum (- initial_g , 1e-2 )
256+ self .initial_Vineq0 = config .barrier_parameter / self .initial_slack0
242257
243258 self .dynamics = config .dynamics
244259 solver = partial (
245- optimizers .mpc_equality ,
260+ optimizers .ip_mpc ,
246261 config .cost ,
247262 self .dynamics ,
248263 None ,
249264 limited_memory ,
250265 equality = config .equality ,
266+ inequality = config .inequality ,
251267 num_alpha = config .equality_num_alpha ,
252268 )
253269
@@ -260,7 +276,10 @@ def solve(
260276 U0 ,
261277 V0 ,
262278 Veq0 ,
279+ Vineq0 ,
280+ slack0 ,
263281 regularization ,
282+ current_barrier_parameter ,
264283 ):
265284 return solver (
266285 reference ,
@@ -271,7 +290,11 @@ def solve(
271290 U0 ,
272291 V0 ,
273292 Veq_in = Veq0 ,
293+ Vineq_in = Vineq0 ,
294+ slack_in = slack0 ,
274295 regularization = regularization ,
296+ barrier_parameter = current_barrier_parameter ,
297+ multiplier_regularization = config .multiplier_regularization ,
275298 )
276299
277300 self ._solve = solve
@@ -289,9 +312,12 @@ def make_data(self):
289312 U0 = self .initial_U0 ,
290313 V0 = self .initial_V0 ,
291314 Veq0 = self .initial_Veq0 ,
315+ Vineq0 = self .initial_Vineq0 ,
316+ slack0 = self .initial_slack0 ,
292317 W = W ,
293318 regularization = regularization ,
294319 merit_penalty = merit_penalty ,
320+ barrier_parameter = barrier_parameter ,
295321 )
296322
297323 def _reference (self , x , command ):
@@ -311,7 +337,18 @@ def run(self, data, x, command, contact=None):
311337 del contact
312338 reference = self ._reference (x , command )
313339 parameter = jnp .zeros ((N + 1 , 1 ), dtype = x .dtype )
314- X , U , V , Veq , regularization , alpha_best , any_accepted = self ._solve (
340+ (
341+ X ,
342+ U ,
343+ V ,
344+ Veq ,
345+ Vineq ,
346+ slack ,
347+ regularization ,
348+ next_barrier_parameter ,
349+ alpha_best ,
350+ any_accepted ,
351+ ) = self ._solve (
315352 reference ,
316353 parameter ,
317354 data .W ,
@@ -320,7 +357,10 @@ def run(self, data, x, command, contact=None):
320357 data .U0 ,
321358 data .V0 ,
322359 data .Veq0 ,
360+ data .Vineq0 ,
361+ data .slack0 ,
323362 data .regularization ,
363+ data .barrier_parameter ,
324364 )
325365 valid_solution = jnp .isfinite (U ).all ()
326366 tau = jax .lax .cond (
@@ -329,22 +369,27 @@ def run(self, data, x, command, contact=None):
329369 lambda _ : self .control_output (x , data .X0 , data .U0 , reference , parameter ),
330370 operand = None ,
331371 )
332- U0 , X0 , V0 , Veq0 , q , dq = self ._update_warm_start (
372+ U0 , X0 , V0 , Veq0 , Vineq0 , slack0 , q , dq = self ._update_warm_start (
333373 x ,
334374 data .X0 ,
335375 data .U0 ,
336376 X ,
337377 U ,
338378 V ,
339379 Veq ,
380+ Vineq ,
381+ slack ,
340382 )
341383 # jax.debug.print("====================" )
342384 return data .replace (
343385 X0 = X0 ,
344386 U0 = U0 ,
345387 V0 = V0 ,
346388 Veq0 = Veq0 ,
389+ Vineq0 = Vineq0 ,
390+ slack0 = slack0 ,
347391 regularization = regularization ,
392+ barrier_parameter = jnp .maximum (next_barrier_parameter , 1e-8 ),
348393 ), tau , q , dq , alpha_best , any_accepted
349394
350395 def reset (self , data , qpos , qvel , foot = None ):
@@ -356,13 +401,21 @@ def reset(self, data, qpos, qvel, foot=None):
356401 nominal_control = _equilibrium_control (
357402 x [self .qpos_slice ], x [self .qvel_slice ]
358403 )
404+ X0 = jnp .tile (x , (N + 1 , 1 ))
405+ U0 = jnp .tile (nominal_control , (N , 1 ))
406+ parameters = jnp .zeros ((N , 1 ), dtype = x .dtype )
407+ g = jax .vmap (inequality )(X0 [:- 1 ], U0 , jnp .arange (N ), parameters )
408+ slack0 = jnp .maximum (- g , 1e-2 )
359409 return data .replace (
360- X0 = jnp . tile ( x , ( N + 1 , 1 )) ,
361- U0 = jnp . tile ( nominal_control , ( N , 1 )) ,
410+ X0 = X0 ,
411+ U0 = U0 ,
362412 V0 = self .initial_V0 ,
363413 Veq0 = self .initial_Veq0 ,
414+ Vineq0 = barrier_parameter / slack0 ,
415+ slack0 = slack0 ,
364416 regularization = regularization ,
365417 merit_penalty = merit_penalty ,
418+ barrier_parameter = barrier_parameter ,
366419 )
367420
368421
0 commit comments