Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fixed
^^^^^

* Fixed RSL-RL play video filenames missing the numeric checkpoint stem used for playback.
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def main(env_cfg: ManagerBasedRLEnvCfg | DirectRLEnvCfg | DirectMARLEnvCfg, agen
log_dir = os.path.dirname(resume_path)

env_cfg.log_dir = log_dir
apply_video_recording(env_cfg, log_dir, args_cli, subdir="play")
apply_video_recording(env_cfg, log_dir, args_cli, subdir="play", checkpoint_path=resume_path)

screen.stage("Creating environment")
env = create_isaaclab_env(
Expand Down
29 changes: 28 additions & 1 deletion source/isaaclab_rl/isaaclab_rl/entrypoints/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,14 @@ def pre_launch_video_config(env_cfg: Any, log_dir: str | None = None, args_cli:
pass


def apply_video_recording(env_cfg: Any, log_dir: str, args_cli: argparse.Namespace, *, subdir: str = "train") -> None:
def apply_video_recording(
env_cfg: Any,
log_dir: str,
args_cli: argparse.Namespace,
*,
subdir: str = "train",
checkpoint_path: str | None = None,
) -> None:
"""Configure internal video recording on the environment config.

Enables recording by ensuring ``env_cfg.video_recorders`` is non-empty, then applies
Expand All @@ -809,6 +816,8 @@ def apply_video_recording(env_cfg: Any, log_dir: str, args_cli: argparse.Namespa
args_cli: Parsed command-line arguments.
subdir: Sub-directory name appended to ``<log_dir>/videos/`` for the fallback output
path. Use ``"train"`` for training runs and ``"play"`` for evaluation.
checkpoint_path: Checkpoint loaded by a play run. When set to a ``model_<N>.pt``
path with a numeric id, the checkpoint stem is appended to play video names.
"""
if not getattr(args_cli, "video", False):
return
Expand Down Expand Up @@ -950,6 +959,8 @@ def apply_video_recording(env_cfg: Any, log_dir: str, args_cli: argparse.Namespa
cfg.video_length = video_length
if video_interval is not None:
cfg.video_interval = video_interval
if subdir == "play" and (label := _checkpoint_video_label(checkpoint_path)) is not None:
cfg.output_filename_prefix = _checkpoint_video_prefix(cfg.output_filename_prefix, label)

print("[INFO] Video recording enabled.")
for cfg in env_cfg.video_recorders:
Expand All @@ -964,6 +975,22 @@ def apply_video_recording(env_cfg: Any, log_dir: str, args_cli: argparse.Namespa
)


def _checkpoint_video_label(checkpoint_path: str | None) -> str | None:
if checkpoint_path is None:
return None

path = Path(checkpoint_path)
if re.fullmatch(r"model_\d+", path.stem) is None or path.suffix != ".pt":
return None
return path.stem


def _checkpoint_video_prefix(prefix: str, label: str) -> str:
if prefix == label or prefix.endswith(f"_{label}"):
return prefix
return f"{prefix}_{label}"


def wrap_record_video(env, log_dir: str, args_cli: argparse.Namespace):
"""No-op stub kept for backwards compatibility.

Expand Down
36 changes: 36 additions & 0 deletions source/isaaclab_rl/test/test_apply_video_recording.py

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we cut down on the tests?

@diegoferigo-rai diegoferigo-rai Aug 28, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cut down in f70e29f, see the top-level comment.

Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,42 @@ def test_apply_video_recording_injects_correct_recorder():
assert rec.video_length == 42 # CLI override applied
assert rec.video_interval == 500 # CLI override applied
assert rec.output_dir == os.path.join("/my/log", "videos", "play")
assert rec.output_filename_prefix == "clip"


@pytest.mark.parametrize(
("existing_prefix", "checkpoint_name", "expected_prefix"),
[
("clip", "model_1200.pt", "clip_model_1200"),
("eval", "model_42.pt", "eval_model_42"),
# Overlapping numeric ids stay distinct tokens instead of substrings.
("clip_model_1200", "model_120.pt", "clip_model_1200_model_120"),
# Non-model or non-numeric stems keep the configured prefix.
("clip", "custom_1200.pt", "clip"),
("clip", "final.pt", "clip"),
],
)
def test_apply_video_recording_labels_play_video_with_checkpoint_stem(existing_prefix, checkpoint_name, expected_prefix):
"""Play videos append only a numeric model checkpoint stem, kept as a distinct token."""
from isaaclab.envs.utils.video_recorder_cfg import VideoRecorderCfg

existing = VideoRecorderCfg()
existing.output_filename_prefix = existing_prefix

env_cfg = _env_cfg()
env_cfg.video_recorders = [existing]
apply_video_recording(env_cfg, "/my/log", _args(), subdir="play", checkpoint_path=f"/my/log/{checkpoint_name}")

assert env_cfg.video_recorders[0].output_filename_prefix == expected_prefix


def test_apply_video_recording_leaves_train_video_prefix_unchanged():
"""Checkpoint labels are only applied to play videos, not training videos."""

env_cfg = _env_cfg()
apply_video_recording(env_cfg, "/my/log", _args(), checkpoint_path="/my/log/model_1200.pt")

assert env_cfg.video_recorders[0].output_filename_prefix == "clip"


def test_apply_video_recording_uses_cfg_defaults_when_cli_not_passed():
Expand Down
Loading