Trained in 7 seconds on an M2 MacBook Air on 5,000 simulated pointing
episodes over virtual Chamonix terrain (MuJoCo).
Input (3): normalised (x_m, y_m, z_m) — event in arm frame [0,1]
→ Linear(3, 64) + SiLU
→ Linear(64, 64) + SiLU
→ Linear(64, 3)
Output (3): (θ0, θ1, θ2) — joint angles in radians
1import torch, numpy as np
2import torch.nn as nn
3from huggingface_hub import hf_hub_download
4
5class LookAtNet(nn.Module):
6 def __init__(self):
7 super().__init__()
8 self.net = nn.Sequential(
9 nn.Linear(3, 64), nn.SiLU(),
10 nn.Linear(64, 64), nn.SiLU(),
11 nn.Linear(64, 3),
12 )
13 def forward(self, x): return self.net(x)
14
15ckpt = torch.load(hf_hub_download("Ethgar/skyfull-lookat-chamonix-v1", "lookat_chamonix.pt"), weights_only=False)
16model = LookAtNet()
17model.load_state_dict(ckpt['state_dict'])
18model.eval()
19norm = ckpt['norm']
20
21# Event at (x_m=0.20, y_m=0.05, z_m=0.08) in arm frame
22xyz = np.array([0.20, 0.05, 0.08], dtype=np.float32)
23x_n = (xyz - norm['x_min']) / (norm['x_max'] - norm['x_min']).clip(1e-6)
24with torch.no_grad():
25 y_n = model(torch.from_numpy(x_n).unsqueeze(0)).numpy()[0]
26theta = y_n * norm['y_std'] + norm['y_mean']
27print(f"θ0={np.degrees(theta[0]):.1f}° θ1={np.degrees(theta[1]):.1f}°")