File size: 3,279 Bytes
27d1888
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
"""Regenerate dexterity figures from a saved dexterity.json (no GPU)."""
import json
import sys
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

out_dir = Path(sys.argv[1])
d = json.load(open(out_dir / "dexterity.json"))
report, curves = d["report"], d["curves"]
trajs, force_fig = d["trajectories"], d["force_fig"]
names = list(report)
GT = "#878c98"
palette = ["#3c7ec0", "#b54e7f", "#6f66bd", "#c26a1e"]
colors = {n: palette[i % len(palette)] for i, n in enumerate(names)}

fig, axes = plt.subplots(1, 4, figsize=(16, 3.4))
first = names[0]
for k in range(4):
    ax = axes[k]
    ax.plot(np.array(trajs[first]["gt"])[:, k], color=GT, ls="--", lw=2, label="GT")
    for n in names:
        ax.plot(np.array(trajs[n]["pred"])[:, k], color=colors[n], lw=1.8, label=n)
    ax.set_title(f"hand dim {trajs[first]['joints'][k]}", fontsize=10)
    ax.set_xlabel("step (20 Hz)")
axes[0].set_ylabel("normalized q"); axes[0].legend(fontsize=8)
fig.suptitle("Finger-joint open-loop trajectories (most-moving hand dims)")
fig.tight_layout(); fig.savefig(out_dir / "fig_finger_trajectories.png", dpi=150); plt.close(fig)

fig, ax = plt.subplots(figsize=(6, 3.6))
for n in names:
    ax.plot(curves[n], color=colors[n], lw=2, marker="o", ms=3, label=n)
ax.set_xlabel("chunk step"); ax.set_ylabel("hand MSE")
ax.set_title("Per-step hand error growth (mean over anchors)")
ax.legend(); fig.tight_layout()
fig.savefig(out_dir / "fig_per_step_error.png", dpi=150); plt.close(fig)

fig, axes = plt.subplots(1, len(names), figsize=(6.0 * len(names), 3.6), squeeze=False)
slot_x = 16 + np.arange(4) * 1.0
for ax, n in zip(axes[0], names):
    ff = force_fig[n]
    ax.plot(np.arange(16), np.array(ff["gt_history"])[:, 2], color=GT, lw=2,
            label="GT force (5 Hz history)")
    ax.plot(slot_x, np.array(ff["gt_slots"])[:, 2], color=GT, ls="--", marker="o",
            label="GT future slots")
    if ff["vq_recon"] is not None:
        ax.step(np.arange(16), np.array(ff["vq_recon"])[:, 2], color="#3c7ec0",
                where="mid", lw=1.6, label="VQ-discretized view")
    if ff["force_pred"] is not None:
        ax.plot(slot_x, np.array(ff["force_pred"])[:, 2], color=colors[n],
                marker="s", lw=1.8, label="predicted future force")
    ax.axvline(15.5, color="#cccccc", lw=1)
    ax.set_title(f"{n} · finger {ff['finger']} · Fz", fontsize=10)
    ax.set_xlabel("5 Hz frame (past | future)"); ax.legend(fontsize=7)
axes[0][0].set_ylabel("normalized force")
fig.suptitle("Continuous force vs discrete VQ bottleneck")
fig.tight_layout(); fig.savefig(out_dir / "fig_force_continuity.png", dpi=150); plt.close(fig)

metrics = ["mse_hand", "vel_mse_hand", "fine_energy_ratio_hand", "tactile_delta_hand"]
fig, axes = plt.subplots(1, len(metrics), figsize=(3.4 * len(metrics), 3.2))
for ax, metric in zip(axes, metrics):
    vals = [report[n][metric] for n in names]
    ax.bar(range(len(names)), vals, color=[colors[n] for n in names], width=0.55)
    ax.set_xticks(range(len(names)), names, fontsize=7)
    ax.set_title(metric, fontsize=9)
fig.suptitle("Dexterity metrics (hand dims)")
fig.tight_layout(); fig.savefig(out_dir / "fig_metrics.png", dpi=150); plt.close(fig)
print("replotted", out_dir)