Skip to content

Commit 5cdbd07

Browse files
committed
- add extra_qref_fn in reference generator
1 parent b69ddad commit 5cdbd07

3 files changed

Lines changed: 78 additions & 16 deletions

File tree

mpx/config/config_spot_arm.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,30 @@ def dynamics(model, mjx_model, contact_id, body_id):
111111
# Torque bounds used by the MPC cost / clipping.
112112
max_torque = 500
113113
min_torque = -500
114-
solver_mode = "primal_dual" # Solver mode for the optimization problem
114+
solver_mode = "primal_dual" # Solver mode for the optimization problem
115+
116+
extra_qref_data = {
117+
"amp": jnp.array([2.0, 0.5, 0.4, 0.6, 0.7, 0.8, 0.5]),
118+
"freq": jnp.array([0.2, 1.0, 1.2, 0.8, 0.9, 1.0, 0.5]),
119+
"joint_index": jnp.arange(7, dtype=jnp.int32),
120+
}
121+
122+
def extra_qref_fn(q_ref, current_time, data):
123+
if data is None:
124+
return q_ref
125+
126+
arm_amp_ref = data["amp"]
127+
arm_freq_ref = data["freq"]
128+
arm_joint_index = data["joint_index"]
129+
130+
def arm_fn(t, carry):
131+
q_ref = carry
132+
#
133+
time_n = t * dt + current_time
134+
arm_pos = arm_amp_ref * jnp.sin(2 * jnp.pi * arm_freq_ref * time_n) + q0[arm_joint_index]
135+
136+
q_ref = q_ref.at[t,arm_joint_index].set(arm_pos)
137+
return (q_ref)
138+
init_carry = q_ref
139+
q_ref = jax.lax.fori_loop(0, N+1, arm_fn, init_carry)
140+
return q_ref

mpx/utils/mpc_utils.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ def terrain_orientation(liftoff_pos,Ryaw):
3535

3636
return jnp.roll(quat,1)
3737

38-
@partial(jax.jit, static_argnums=(0,1,2,3,4,5))
39-
def reference_generator(use_terrain_estimator,N,dt,n_joints,n_contact,mass,foot0,q0,t_timer, x, foot, input, duty_factor, step_freq,step_height,liftoff,contact,clearence_speed):
38+
@partial(jax.jit, static_argnums=(0,1,2,3,4,5), static_argnames=("extra_qref_fn",))
39+
def reference_generator(use_terrain_estimator,N,dt,n_joints,n_contact,mass,foot0,q0,t_timer, x, foot, input, duty_factor, step_freq,step_height,liftoff,contact,clearence_speed, current_time, extra_qref_fn=None, extra_qref_data=None):
4040
p = x[:3]
4141
quat = x[3:7]
4242
# q = x[7:7+n_joints]
@@ -141,6 +141,25 @@ def cubic_splineZ(current_foot, foothold, step_height,val):
141141
liftoff = liftoff.at[1::3].set(liftoff_y)
142142
liftoff = liftoff.at[2::3].set(liftoff_z)
143143

144+
# ## Reference for the arm
145+
# nq_arm = 7
146+
# arm_amp_ref = jnp.array([2.0, 0.5, 0.4, 0.6, 0.7, 0.8, 0.5])
147+
# arm_freq_ref = jnp.array([0.2, 1.0, 1.2, 0.8, 0.9, 1.0, 0.5])
148+
# larm_index = 0
149+
# def arm_fn(t, carry):
150+
# q_ref = carry
151+
# #
152+
# time_n = t * dt + current_time
153+
# arm_pos = arm_amp_ref * jnp.sin(2 * jnp.pi * arm_freq_ref * time_n) + q0[larm_index:larm_index+nq_arm]
154+
155+
# q_ref = q_ref.at[t,larm_index:larm_index+nq_arm].set(arm_pos)
156+
# return (q_ref)
157+
# init_carry = q_ref
158+
# q_ref = jax.lax.fori_loop(0, N+1, arm_fn, init_carry)
159+
160+
if extra_qref_fn is not None:
161+
q_ref = extra_qref_fn(q_ref, current_time, extra_qref_data)
162+
144163
return jnp.concatenate([p_ref, quat_ref, q_ref, dp_ref, omega_ref, foot_ref, contact_sequence,grf_ref], axis=1),jnp.concatenate([contact_sequence], axis=1), liftoff
145164

146165
@partial(jax.jit, static_argnums=(0,1,2,3))

mpx/utils/mpc_wrapper.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class MPCData(PyTreeNode):
1515
"""Carry state for the pure functional MPC API."""
1616

1717
dt: float
18+
time: jnp.ndarray
1819
duty_factor: float
1920
step_freq: float
2021
step_height: float
@@ -24,6 +25,7 @@ class MPCData(PyTreeNode):
2425
U0: jnp.ndarray
2526
V0: jnp.ndarray
2627
W: jnp.ndarray
28+
extra_qref_data: object = None
2729

2830

2931
mpx_data = MPCData
@@ -165,19 +167,27 @@ def __init__(self, config, limited_memory=False):
165167

166168
reference_generator = getattr(config, "reference_generator", mpc_utils.reference_generator)
167169
clearance_speed = getattr(config, "clearance_speed", getattr(config, "clearence_speed", 0.2))
168-
self._ref_gen = jax.jit(
169-
partial(
170-
reference_generator,
171-
config.use_terrain_estimation,
172-
config.N,
173-
config.dt,
174-
config.n_joints,
175-
config.n_contact,
176-
robot_mass,
177-
foot0=config.p_legs0,
178-
q0=config.q0,
179-
clearence_speed=clearance_speed,
180-
)
170+
171+
ref_gen_kwargs = {
172+
"foot0": config.p_legs0,
173+
"q0": config.q0,
174+
"clearence_speed": clearance_speed,
175+
}
176+
if hasattr(config, "extra_qref_fn"):
177+
ref_gen_kwargs["extra_qref_fn"] = config.extra_qref_fn
178+
179+
# `reference_generator` is already jitted in mpc_utils. Keep a single
180+
# binding layer here to avoid argument-position ambiguities when an
181+
# extra q-ref callback is provided from config.
182+
self._ref_gen = partial(
183+
reference_generator,
184+
config.use_terrain_estimation,
185+
config.N,
186+
config.dt,
187+
config.n_joints,
188+
config.n_contact,
189+
robot_mass,
190+
**ref_gen_kwargs,
181191
)
182192
self._timer_run = jax.jit(mpc_utils.timer_run)
183193
self._update_warm_start = partial(
@@ -193,6 +203,7 @@ def make_data(self):
193203

194204
return MPCData(
195205
dt=self.config.dt,
206+
time=jnp.asarray(0.0, dtype=jnp.float32),
196207
duty_factor=self.config.duty_factor,
197208
step_freq=self.config.step_freq,
198209
step_height=self.config.step_height,
@@ -202,6 +213,7 @@ def make_data(self):
202213
U0=self.initial_U0,
203214
V0=self.initial_V0,
204215
W=self.config.W,
216+
extra_qref_data=getattr(self.config, "extra_qref_data", None),
205217
)
206218

207219
def control_output(self, x0, X, U, reference, parameter):
@@ -226,6 +238,8 @@ def _run_impl(self, data, x0, input, contact):
226238
input=input,
227239
liftoff=data.liftoff,
228240
contact=contact,
241+
current_time=data.time,
242+
extra_qref_data=data.extra_qref_data,
229243
)
230244

231245
# Reference generation and solver execution stay on the pure JAX path.
@@ -255,7 +269,9 @@ def _run_impl(self, data, x0, input, contact):
255269
V,
256270
)
257271

272+
new_time = data.time + jnp.asarray(1 / self.mpc_frequency, dtype=data.time.dtype)
258273
data = data.replace(
274+
time=new_time,
259275
X0=X0,
260276
U0=U0,
261277
V0=V0,
@@ -286,6 +302,7 @@ def reset(self, data, qpos, qvel, foot):
286302
U0=self.initial_U0,
287303
X0=jnp.tile(initial_state, (self.config.N + 1, 1)),
288304
V0=self.initial_V0,
305+
time=jnp.asarray(0.0, dtype=jnp.float32),
289306
contact_time=self.config.timer_t,
290307
liftoff=jnp.ravel(foot),
291308
)

0 commit comments

Comments
 (0)