dropbear-locomotion / scripts /plot_release_metrics.py
cudabenchmarktest's picture
Release Dropbear locomotion v0.1.0 (model_8745)
de3e3f2 verified
Raw
History Blame Contribute Delete
3.85 kB
#!/usr/bin/env python3
"""Render a compact release report from one RSL-RL TensorBoard event file."""
from __future__ import annotations
import argparse
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
from tensorboard.backend.event_processing.event_accumulator import (
EventAccumulator,
)
TAGS = {
"reward": "Train/mean_reward",
"episode_length": "Train/mean_episode_length",
"velocity_error": "Metrics/base_velocity/error_vel_xy",
"bad_orientation": "Episode_Termination/bad_orientation",
"timeout": "Episode_Termination/time_out",
"value_loss": "Loss/value",
"surrogate_loss": "Loss/surrogate",
}
def load_series(
accumulator: EventAccumulator,
tag: str,
) -> tuple[np.ndarray, np.ndarray]:
events = accumulator.Scalars(tag)
steps = np.asarray([event.step for event in events])
values = np.asarray([event.value for event in events])
return steps, values
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("event_file", type=Path)
parser.add_argument("output", type=Path)
parser.add_argument("--title", default="Dropbear Stage 46")
args = parser.parse_args()
accumulator = EventAccumulator(
str(args.event_file),
size_guidance={"scalars": 0},
)
accumulator.Reload()
available = set(accumulator.Tags()["scalars"])
missing = [tag for tag in TAGS.values() if tag not in available]
if missing:
raise RuntimeError(f"Missing TensorBoard tags: {missing}")
data = {
name: load_series(accumulator, tag)
for name, tag in TAGS.items()
}
plt.style.use("dark_background")
fig, axes = plt.subplots(2, 2, figsize=(13, 8), sharex=True)
fig.patch.set_facecolor("#07111f")
for axis in axes.flat:
axis.set_facecolor("#0c1727")
axis.grid(color="#334155", alpha=0.28)
axis.spines[["top", "right"]].set_visible(False)
axes[0, 0].plot(*data["reward"], color="#5eead4", linewidth=2)
axes[0, 0].set_title("Mean episode reward")
axes[0, 0].set_ylabel("reward")
axes[0, 1].plot(
*data["episode_length"],
color="#60a5fa",
linewidth=2,
label="episode length",
)
axes[0, 1].axhline(
1000,
color="#a7f3d0",
linestyle=":",
linewidth=1,
label="full horizon",
)
axes[0, 1].set_title("Survival recovery")
axes[0, 1].set_ylabel("steps")
axes[0, 1].legend(frameon=False, fontsize=8)
axes[1, 0].plot(
*data["velocity_error"],
color="#fbbf24",
linewidth=2,
)
axes[1, 0].set_title("Planar velocity tracking error")
axes[1, 0].set_ylabel("m/s")
axes[1, 0].set_xlabel("checkpoint iteration")
axes[1, 1].plot(
*data["bad_orientation"],
color="#fb7185",
linewidth=2,
label="bad orientation",
)
axes[1, 1].plot(
*data["timeout"],
color="#4ade80",
linewidth=2,
label="time-out",
)
axes[1, 1].set_title("Episode completion")
axes[1, 1].set_ylabel("fraction")
axes[1, 1].set_xlabel("checkpoint iteration")
axes[1, 1].legend(frameon=False, fontsize=8)
start = int(data["reward"][0][0])
end = int(data["reward"][0][-1])
fig.suptitle(
f"{args.title} · iterations {start}{end}",
x=0.065,
ha="left",
fontsize=17,
fontweight="bold",
)
fig.text(
0.985,
0.015,
"RSL-RL PPO continuation · 512 parallel environments",
ha="right",
color="#94a3b8",
fontsize=8,
)
fig.tight_layout(rect=(0.04, 0.04, 0.99, 0.93))
args.output.parent.mkdir(parents=True, exist_ok=True)
fig.savefig(args.output, dpi=180, facecolor=fig.get_facecolor())
if __name__ == "__main__":
main()