Skip to content

Commit 561182d

Browse files
committed
working example obstacle avoidance
1 parent 39b554c commit 561182d

3 files changed

Lines changed: 64 additions & 60 deletions

File tree

README.md

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -84,36 +84,6 @@ python mpx/examples/mjx_quad.py
8484

8585
Use the keyboard's arrows to control the robot.
8686

87-
## Diffusion-MPPI-guided FDDP
88-
89-
MPX includes a receding-horizon manipulation solver that anneals a diagonal
90-
Gaussian in control-knot space, then refines its mean with FDDP. The local
91-
solver adds a temporary Gaussian precision to `l_u` and `l_uu`, decays that
92-
guidance each iteration, and reports physical task cost separately from prior
93-
cost. Original FDDP and primal-dual modes remain available.
94-
95-
```bash
96-
# Interactive Push-T with live samples, mean, FDDP prediction, and history
97-
pixi run python -m mpx.examples.push_t_guided_mpc --viewer --verbose
98-
99-
# Deterministic Push-T benchmark and plots
100-
pixi run python -m mpx.examples.push_t_guided_mpc \
101-
--headless --seeds 0 1 2 3 4 --plot
102-
103-
# Interactive full-MJX AgileX Piper box push
104-
pixi run python -m mpx.examples.agilex_box_push_guided_mpc --viewer --verbose
105-
106-
# Nominal and mismatched-plant Piper benchmarks
107-
pixi run python -m mpx.examples.agilex_box_push_guided_mpc \
108-
--headless --seeds 0 1 2 3 4 --condition nominal --plot
109-
pixi run python -m mpx.examples.agilex_box_push_guided_mpc \
110-
--headless --condition heavy
111-
```
112-
113-
Both examples accept `--mode fddp`, `--mode mppi`, `--mode mppi_fddp`, or
114-
`--mode guided`. See [`docs/diffusion_mppi_fddp.md`](docs/diffusion_mppi_fddp.md)
115-
for equations, ablations, video commands, diagnostics, model attribution, and
116-
measured limitations.
11787

11888
> **Note:**
11989
The first time running the script it can take more than a minute to JIT the solver

mpx/config/config_piper.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@
2727
merit_penalty = jnp.array(1e-6,dtype=jnp.float32)
2828

2929

30-
q0 = jnp.array([0, 0, -0., -0., 0, 0])
31-
q0_init = q0
32-
3330

3431
n_joints = 6
3532
n_contact = len(contact_frame)
@@ -43,12 +40,12 @@
4340
qacc_slice = slice(0, nv)
4441
tau_slice = slice(nv, nv + n_joints)
4542

46-
Qq = jnp.diag(jnp.ones(n_joints)) * 0.01
43+
Qq = jnp.diag(jnp.ones(n_joints)) * 0.1
4744
Qdq = jnp.diag(jnp.ones(n_joints)) * 0.2
48-
Qacc = jnp.diag(jnp.ones(nv)) * 0.001
45+
Qacc = jnp.diag(jnp.ones(nv)) * 0.01
4946
Qtau = jnp.diag(jnp.ones(n_joints)) * 0.0001
50-
Qee = jnp.diag(jnp.ones(3)) * 100.0
51-
Qee_final = jnp.diag(jnp.ones(3)) * 500.0
47+
Qee = jnp.diag(jnp.ones(3)) * 200.0
48+
Qee_final = jnp.diag(jnp.ones(3)) * 1000.0
5249
W = {
5350
"Qq": Qq,
5451
"Qdq": Qdq,
@@ -58,8 +55,9 @@
5855
"Qee_final": Qee_final,
5956
}
6057

61-
initial_state = jnp.concatenate([q0, jnp.zeros(nv)])
6258

59+
obstacle_center = jnp.array([0.3, 0.0, 0.6])
60+
obstacle_radius = 0.2
6361
max_torque = jnp.array([32.0, 32.0, 32.0, 8.0, 8.0, 8.0])
6462
min_torque = -max_torque
6563
_MODEL = mujoco.MjModel.from_xml_path(model_path)
@@ -68,6 +66,9 @@
6866
raise ValueError("All controlled Piper joints must define position limits")
6967
joint_position_min = jnp.asarray(_MODEL.jnt_range[:n_joints, 0])
7068
joint_position_max = jnp.asarray(_MODEL.jnt_range[:n_joints, 1])
69+
q0 = joint_position_min + 0.5 * (joint_position_max - joint_position_min)
70+
q0_init = q0
71+
initial_state = jnp.concatenate([q0, jnp.zeros(nv)])
7172
barrier_parameter = jnp.array(1.0, dtype=jnp.float32)
7273
multiplier_regularization = jnp.array(1e-8, dtype=jnp.float32)
7374
_INITIAL_DATA = mujoco.MjData(_MODEL)
@@ -135,13 +136,27 @@ def inequality(x, u, t, parameter):
135136
"""Joint position bounds, feasible when values are <= 0."""
136137
del u, t, parameter
137138
qpos, _ = _state_parts(x)
139+
data = mjx.make_data(_MJX_MODEL)
140+
data = data.replace(qpos=qpos)
141+
data = smooth.kinematics(_MJX_MODEL, data)
142+
end_effector_position = data.geom_xpos[_CONTACT_ID[0]]
143+
## obstacle avoidance constraints - sphere centered at (0.3, 0.0, 0.4) with radius 0.2
144+
distance_to_obstacle = jnp.linalg.norm(end_effector_position - obstacle_center)
145+
obstacle_constraint = jnp.array([obstacle_radius - distance_to_obstacle])
138146
return jnp.concatenate(
139147
[
140148
joint_position_min - qpos,
141149
qpos - joint_position_max,
150+
obstacle_constraint
142151
]
143152
)
144153

154+
def pseudo_huber(error, weight, delta):
155+
squared_error = error.T @ weight @ error
156+
return delta**2 * (
157+
jnp.sqrt(1.0 + squared_error / delta**2) - 1.0
158+
)
159+
145160
def cost(W, reference, x, u, t):
146161
qpos, qvel = _state_parts(x)
147162
q = qpos
@@ -159,14 +174,15 @@ def cost(W, reference, x, u, t):
159174

160175
end_effector_position = data.geom_xpos[_CONTACT_ID[0]]
161176
end_effector_error = end_effector_position - p_ref
177+
ee_tracking_cost = pseudo_huber(end_effector_error, W["Qee"], delta=0.1)
162178
joint_error = q - q_ref
163179

164180
stage_cost = (
165181
joint_error.T @ W["Qq"] @ joint_error
166182
+ (dq - dq_ref).T @ W["Qdq"] @ (dq - dq_ref)
167183
+ acc.T @ W["Qacc"] @ acc
168184
+ tau.T @ W["Qtau"] @ tau
169-
+ end_effector_error.T @ W["Qee"] @ end_effector_error
185+
+ ee_tracking_cost
170186
)
171187
terminal_cost = (
172188
joint_error.T @ W["Qq"] @ joint_error

mpx/examples/agile_x.py

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010

1111
import jax
12+
13+
jax.config.update("jax_enable_x64", True)
14+
1215
import jax.numpy as jnp
1316
import mujoco
1417
import mujoco.viewer
@@ -36,18 +39,29 @@ def solve_mpc(mpc_data, qpos, qvel, command):
3639

3740
return solve_mpc
3841

39-
def sample_reachable_position(rng, model, data, end_effector_id, qpos):
40-
joint_range = model.jnt_range[: config.n_joints]
41-
sample_qpos = rng.uniform(
42-
low=joint_range[:, 0],
43-
high=joint_range[:, 1],
44-
)
45-
data.qpos[:] = sample_qpos
46-
data.qvel[:] = 0.0
47-
mujoco.mj_forward(model, data)
48-
if data.geom_xpos[end_effector_id][2] < 0.4:
49-
return sample_reachable_position(rng, model, data, end_effector_id, qpos)
50-
return data.geom_xpos[end_effector_id].copy()
42+
# def sample_reachable_position(rng, model, data, end_effector_id, qpos):
43+
# joint_range = model.jnt_range[: config.n_joints]
44+
# sample_qpos = rng.uniform(
45+
# low=joint_range[:, 0],
46+
# high=joint_range[:, 1],
47+
# )
48+
# data.qpos[:] = sample_qpos
49+
# data.qvel[:] = 0.0
50+
# mujoco.mj_forward(model, data)
51+
# if data.geom_xpos[end_effector_id][2] < 0.4:
52+
# return sample_reachable_position(rng, model, data, end_effector_id, qpos)
53+
# return data.geom_xpos[end_effector_id].copy()
54+
def sample_position(indx,obstacle_center,obstacle_radius):
55+
if indx == 0:
56+
return np.array([0,0,obstacle_radius+0.01]) + obstacle_center
57+
elif indx == 1:
58+
return np.array([0.0,obstacle_radius+0.01,0.0]) + obstacle_center
59+
elif indx == 2:
60+
return np.array([0.0,0.0,-obstacle_radius-0.01]) + obstacle_center
61+
elif indx == 3:
62+
return np.array([0.0,-obstacle_radius-0.01,0.0]) + obstacle_center
63+
else:
64+
raise ValueError("Invalid index for sample_position")
5165

5266
def main():
5367
model = mujoco.MjModel.from_xml_path(dir_path + "/../data/piper_l/scene_flat.xml")
@@ -65,9 +79,8 @@ def main():
6579
end_effector_id = mujoco.mj_name2id(
6680
model, mujoco.mjtObj.mjOBJ_GEOM, config.contact_frame[0]
6781
)
68-
desired_position = sample_reachable_position(
69-
rng, model, target_data, end_effector_id, config.q0
70-
)
82+
des_pos_idx = 0
83+
desired_position = sample_position(des_pos_idx, config.obstacle_center, config.obstacle_radius)
7184

7285
data.qpos = np.asarray(config.q0)
7386
mujoco.mj_forward(model, data)
@@ -86,7 +99,7 @@ def main():
8699
q = jnp.zeros(config.n_joints)
87100
dq = jnp.zeros(config.n_joints)
88101
def step_controller():
89-
nonlocal counter, tau, mpc_data, desired_position, target_start_counter, q, dq
102+
nonlocal counter, tau, mpc_data, desired_position, target_start_counter, q, dq, des_pos_idx
90103

91104
qpos = data.qpos.copy()
92105
qvel = data.qvel.copy()
@@ -107,9 +120,8 @@ def step_controller():
107120
counter += 1
108121
timed_out = counter - target_start_counter >= max_steps_per_target
109122
if timed_out:
110-
desired_position = sample_reachable_position(
111-
rng, model, target_data, end_effector_id, data.qpos
112-
)
123+
des_pos_idx = (des_pos_idx + 1) % 4
124+
desired_position = sample_position(des_pos_idx, config.obstacle_center, config.obstacle_radius)
113125
target_start_counter = counter
114126
mpc_data = reset_mpc(
115127
mpc.make_data(), data.qpos.copy(), data.qvel.copy()
@@ -120,13 +132,19 @@ def step_controller():
120132
model,
121133
data
122134
) as viewer:
123-
viewer.sync()
135+
124136
desired_position_geom_id = sim_utils.render_sphere(
125137
viewer,
126138
desired_position,
127139
diameter=0.05,
128140
color=np.array([0.0, 0.0, 1.0, 0.5]))
129-
141+
obstacle_geom_id = sim_utils.render_sphere(
142+
viewer,
143+
config.obstacle_center,
144+
diameter=config.obstacle_radius*2,
145+
color=np.array([1.0, 0.0, 0.0, 0.5])
146+
)
147+
viewer.sync()
130148
while viewer.is_running():
131149
tic = timer()
132150
step_controller()

0 commit comments

Comments
 (0)