dreamzero / scripts /eval /replot_dexterity.py
zhicao's picture
Scheduled Commit
27d1888 verified
Raw History Blame Contribute Delete
3.28 kB
"""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)