Views
No views yet
FetchPickAndPlace-v4 (Gymnasium-Robotics / MuJoCo), trained by
imitation learning from a SAC+HER expert. This repo accompanies a controlled comparison
of Behavior Cloning (BC), DAgger, and RL — see the full write-up, figures, video,
and training code on GitHub:
fetch-imitation-learning.hhmm1122/fetch-pickandplace-sac-her.Honest scope: an engineering + analysis project, not novel research. These weights are an MLP+MSE imitation of a tanh-squashed SAC policy; they reach the imitation policy-class ceiling (~0.8-0.9 success), not the expert's 1.0. The value is the clean, reproducible BC-vs-DAgger-vs-RL comparison and the quantified distribution-shift analysis.
| File | Method | Demo budget | Online expert queries | This ckpt | 3-seed mean |
|---|---|---|---|---|---|
bc_d50_s0.pt | Behavior Cloning | 50 demos | - | 0.35 | 0.31 +/- 0.05 |
bc_d200_s0.pt | Behavior Cloning | 200 demos | - | 0.72 | 0.81 +/- 0.06 |
dagger_d25_s0.pt | DAgger | 25 demos init | 4,000 | 0.82 | 0.79 +/- 0.05 |
dagger_d50_s0.pt | DAgger | 50 demos init | 4,000 | 0.92 | 0.89 +/- 0.02 |
.pt files contain a state dict plus the input-normalization statistics. This snippet is
self-contained (only needs torch, numpy, gymnasium, gymnasium-robotics):1import numpy as np, torch, torch.nn as nn
2import gymnasium as gym, gymnasium_robotics
3
4class MLPPolicy(nn.Module):
5 def __init__(self, hidden):
6 super().__init__()
7 self.net = nn.Sequential(
8 nn.Linear(28, hidden), nn.ReLU(),
9 nn.Linear(hidden, hidden), nn.ReLU(),
10 nn.Linear(hidden, 4),
11 )
12 def forward(self, x):
13 return self.net(x)
14
15def load_policy(path):
16 ckpt = torch.load(path, map_location="cpu", weights_only=False)
17 net = MLPPolicy(ckpt["hidden"]); net.load_state_dict(ckpt["state_dict"]); net.eval()
18 mean = np.asarray(ckpt["obs_mean"], np.float32)
19 std = np.asarray(ckpt["obs_std"], np.float32)
20 def act(obs):
21 x = np.concatenate([obs["observation"], obs["desired_goal"]]).astype(np.float32)
22 x = (x - mean) / std
23 with torch.no_grad():
24 a = net(torch.as_tensor(x).unsqueeze(0)).squeeze(0).numpy()
25 return np.clip(a, -1.0, 1.0).astype(np.float32)
26 return act
27
28gym.register_envs(gymnasium_robotics)
29env = gym.make("FetchPickAndPlace-v4", max_episode_steps=50)
30policy = load_policy("dagger_d50_s0.pt")
31obs, info = env.reset(seed=0)
32success = 0.0
33for _ in range(50):
34 obs, _r, term, trunc, info = env.step(policy(obs))
35 success = float(info["is_success"])
36 if term or trunc:
37 break
38print("success:", success)observation (25) concatenated with desired_goal (3); achieved_goal
is dropped (redundant). Actions are 4-d [dx, dy, dz, gripper] clipped to [-1, 1].FetchPickAndPlace-v4, max_episode_steps=50, sparse reward.info["is_success"] on the final step. 100 eval episodes, seeds 0-99.