录制视频#
本指南介绍如何在运行过程中,从可视化器或场景传感器录制 mp4 视频片段。
Shadow Hand 立方体转向任务的录像,录制 Rerun 可视化器的串流视图,包含四个串流环境和四种真值数据
每个 VideoRecorderCfg 条目独立运行:不同数据源按照各自的录制周期写入不同文件,同时运行的录制器数量没有限制。
快速开始#
在 env_cfg.video_recorders 中添加 VideoRecorderCfg :
from isaaclab.envs.utils.video_recorder_cfg import VideoRecorderCfg
env_cfg.video_recorders = [
VideoRecorderCfg(source="viz:kit", output_dir="videos/")
]
source 也接受 ImageViewCfg:使用 VideoRecorderCfg(source=view),可与窗口共享图像选择和帧。传感器与透视视图示例见 在可视化器中显示相机流视图。
也可在命令行传入 --video [SOURCE],无需修改环境配置即可录制。--video 添加一个录制器;若要同时录制多个来源,在 env_cfg.video_recorders 中为每个来源列出一个 VideoRecorderCfg。此列表优先于 --video 的来源设置,但 --video_length 和 --video_interval 仍应用于所有录制器:
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --video
--video 从指定来源录制,--viz 仍决定哪些可视化器打开窗口。所有命令行形式见 --video 来源,VideoRecorderCfg 选项见下方 来源 和 片段控制。从不同于交互视图的角度录制,见 从独立相机视角录制。
示例#
这三个示例均使用 Shadow Hand 立方体重定向任务 Isaac-Reorient-Cube-Shadow-Camera-Direct ,该任务内置平铺相机传感器。示例 1 和 2 分别演示一种录制来源,示例 3 则组合全部四种来源。
run_video_recording.py 的代码
1# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
2# All rights reserved.
3#
4# SPDX-License-Identifier: BSD-3-Clause
5
6"""Tutorial: recording video from visualizers and scene sensors.
7
8This script demonstrates three progressively richer recording configurations,
9all using the Shadow Hand cube-reorientation task
10(``Isaac-Reorient-Cube-Shadow-Camera-Direct``).
11
12Example 1 — Kit viewport (simplest)
13 One clip from the Kit interactive viewport, showing 4 parallel environments.
14
15 .. code-block:: bash
16
17 uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
18 --example 1 --num_envs 4 --viz kit
19
20Example 2 — scene sensor only, headless
21 One clip captured directly from the scene's tiled-camera sensor.
22 No visualizer window opens; the sensor renders offline.
23
24 .. code-block:: bash
25
26 uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
27 --example 2 --num_envs 16
28
29Example 3 — Kit viewport + Kit tiled grid + Newton viewport + scene sensor
30 Four independent clip streams recorded simultaneously.
31
32 .. code-block:: bash
33
34 uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
35 --example 3 --num_envs 4 --viz kit,newton_gl
36
37Clips are written to ``videos/recording_tutorial/example_<N>/`` in the working directory.
38Examples 1 and 2 each demonstrate one recording source; Example 3 combines all of them.
39``--viz`` selects the visualizers to record from; the configs below only set their camera and panels.
40"""
41
42from __future__ import annotations
43
44import argparse
45import contextlib
46import os
47import sys
48
49import gymnasium as gym
50import torch
51
52from isaaclab.visualizers import WindowCfg
53
54import isaaclab_tasks # noqa: F401
55
56with contextlib.suppress(ImportError):
57 import isaaclab_tasks_experimental # noqa: F401
58
59from isaaclab.app import add_launcher_args, launch_simulation
60from isaaclab.envs.utils.video_recorder_cfg import VideoRecorderCfg
61
62from isaaclab_tasks.utils import resolve_task_config, setup_preset_cli
63
64# ---------------------------------------------------------------------------
65# Constants
66# ---------------------------------------------------------------------------
67
68_VIDEO_LENGTH = 100 # env steps per clip
69_NUM_STEPS = 115 # slightly more than _VIDEO_LENGTH so the clip flushes cleanly
70
71_TASK_SHADOW = "Isaac-Reorient-Cube-Shadow-Camera-Direct"
72
73# Kit viewport camera: positioned to show a 2×2 grid of Shadow Hand environments.
74# env_spacing=1.0 with 4 envs → envs centered at ±0.5 in x and y.
75# Cube spawns at ~(0, -0.39, 0.6) per env; wrist cylinder is the landmark at the top.
76_SHADOW_EYE = (0.0, -2.2, 1.8)
77_SHADOW_LOOKAT = (0.0, -0.1, 0.4)
78_SHADOW_ENV_SPACING = 1.0
79
80# Skip the first few steps so the RTX renderer has warmed up before recording starts.
81_KIT_STEP_OFFSET = 5
82
83
84def _output_dir(example: int) -> str:
85 return os.path.join("videos", "recording_tutorial", f"example_{example}")
86
87
88def _shadow_env_cfg(num_envs: int, env_spacing: float = _SHADOW_ENV_SPACING):
89 """Build a base Shadow Hand camera env cfg shared by all examples."""
90 env_cfg, _ = resolve_task_config(_TASK_SHADOW, "", overrides=(*sys.argv[1:], "env.scene.tiled_camera=rgb"))
91 env_cfg.scene.tiled_camera.height = 256
92 env_cfg.scene.tiled_camera.width = 256
93 env_cfg.scene.num_envs = num_envs
94 env_cfg.scene.env_spacing = env_spacing
95 return env_cfg
96
97
98# ---------------------------------------------------------------------------
99# Per-example environment config builders
100# ---------------------------------------------------------------------------
101
102
103def _build_env_cfg_example_1(num_envs: int):
104 """Shadow Hand + Kit viewport: one clip from the interactive viewport."""
105 from isaaclab_visualizers.kit import KitVisualizerCfg
106
107 env_cfg = _shadow_env_cfg(num_envs)
108 env_cfg.sim.visualizer_cfgs = [KitVisualizerCfg(eye=_SHADOW_EYE, lookat=_SHADOW_LOOKAT)]
109
110 out = _output_dir(1)
111 env_cfg.video_recorders = [
112 VideoRecorderCfg(
113 source="viz:kit",
114 output_dir=out,
115 output_filename_prefix="kit_viewport",
116 video_length=_VIDEO_LENGTH,
117 fps=30,
118 step_offset=_KIT_STEP_OFFSET,
119 ),
120 ]
121 return env_cfg, _TASK_SHADOW
122
123
124def _build_env_cfg_example_2(num_envs: int):
125 """Shadow Hand + headless: scene tiled-camera sensor clip only."""
126 env_cfg = _shadow_env_cfg(num_envs, env_spacing=2.0)
127
128 out = _output_dir(2)
129 env_cfg.video_recorders = [
130 VideoRecorderCfg(
131 source="sensor:tiled_camera",
132 output_dir=out,
133 output_filename_prefix="sensor",
134 video_length=_VIDEO_LENGTH,
135 fps=30,
136 ),
137 ]
138 return env_cfg, _TASK_SHADOW
139
140
141def _build_env_cfg_example_3(num_envs: int):
142 """Shadow Hand + Kit viewport + Kit tiled grid + Newton viewport + sensor: four simultaneous streams.
143
144 Note: ``source='viz:newton_gl'`` captures the full Newton GL window. When
145 ``streaming_view=True`` is set on :class:`~isaaclab_visualizers.newton.NewtonGLVisualizerCfg`,
146 the GL window displays the per-environment camera panel, so this effectively records
147 a Newton streaming view without a separate ``render_tiled_rgb_array()`` call.
148 """
149 from isaaclab_visualizers.kit import KitVisualizerCfg
150 from isaaclab_visualizers.newton import NewtonGLVisualizerCfg
151
152 env_cfg = _shadow_env_cfg(num_envs)
153 kit_cfg = KitVisualizerCfg(
154 eye=_SHADOW_EYE,
155 lookat=_SHADOW_LOOKAT,
156 streaming_view=True,
157 streaming_envs=min(num_envs, 16),
158 # No streaming_sensor_prim_path: selects the existing
159 # tiled_camera sensor automatically, so the streaming panel shows the same
160 # RTX-rendered views as source="sensor:tiled_camera".
161 )
162 newton_cfg = NewtonGLVisualizerCfg(
163 eye=_SHADOW_EYE,
164 lookat=_SHADOW_LOOKAT,
165 window=WindowCfg(size=(1280, 720)),
166 focal_length=25.0,
167 )
168 env_cfg.sim.visualizer_cfgs = [kit_cfg, newton_cfg]
169
170 out = _output_dir(3)
171 env_cfg.video_recorders = [
172 VideoRecorderCfg(
173 source="viz:kit",
174 output_dir=out,
175 output_filename_prefix="kit_viewport",
176 video_length=_VIDEO_LENGTH,
177 fps=30,
178 step_offset=_KIT_STEP_OFFSET,
179 ),
180 VideoRecorderCfg(
181 source="viz:kit:streaming_view",
182 output_dir=out,
183 output_filename_prefix="tiled_kit_viewport",
184 video_length=_VIDEO_LENGTH,
185 fps=30,
186 step_offset=_KIT_STEP_OFFSET,
187 ),
188 VideoRecorderCfg(
189 source="viz:newton_gl",
190 output_dir=out,
191 output_filename_prefix="newton_viewport",
192 video_length=_VIDEO_LENGTH,
193 fps=30,
194 ),
195 VideoRecorderCfg(
196 source="sensor:tiled_camera",
197 output_dir=out,
198 output_filename_prefix="sensor",
199 video_length=_VIDEO_LENGTH,
200 fps=30,
201 ),
202 ]
203 return env_cfg, _TASK_SHADOW
204
205
206_BUILDERS = {
207 1: _build_env_cfg_example_1,
208 2: _build_env_cfg_example_2,
209 3: _build_env_cfg_example_3,
210}
211
212# ---------------------------------------------------------------------------
213# Argument parsing
214# ---------------------------------------------------------------------------
215parser = argparse.ArgumentParser(description="Video recording tutorial for Isaac Lab environments.")
216parser.add_argument(
217 "--example", type=int, default=1, choices=[1, 2, 3], help="Which recording example to run (1, 2, or 3)."
218)
219parser.add_argument("--num_envs", type=int, default=None, help="Number of environments to simulate.")
220add_launcher_args(parser)
221args_cli, hydra_args = setup_preset_cli(parser)
222sys.argv = [sys.argv[0]] + hydra_args
223
224
225def main():
226 """Run the selected video recording example."""
227 defaults = {1: 4, 2: 16, 3: 4}
228 num_envs = args_cli.num_envs if args_cli.num_envs is not None else defaults[args_cli.example]
229 env_cfg, task = _BUILDERS[args_cli.example](num_envs)
230 env_cfg.sim.device = args_cli.device if args_cli.device is not None else env_cfg.sim.device
231
232 # Examples 1 and 3 record from the Kit viewport via omni.replicator, which requires
233 # camera rendering support. Force it here for visualizer-only recording.
234 if args_cli.example in (1, 3):
235 args_cli.enable_cameras = True
236
237 with launch_simulation(env_cfg, args_cli):
238 env = gym.make(task, cfg=env_cfg)
239
240 out = _output_dir(args_cli.example)
241 print(f"[INFO]: Running Example {args_cli.example} — clips → {out}/")
242 print(f"[INFO]: Gym observation space: {env.observation_space}")
243 print(f"[INFO]: Gym action space: {env.action_space}")
244
245 print("[INFO]: Setup complete.")
246 env.reset()
247 for _ in range(_NUM_STEPS):
248 with torch.inference_mode():
249 actions = 2 * torch.rand(env.action_space.shape, device=env.unwrapped.device) - 1
250 env.step(actions)
251
252 env.close()
253 print(f"[INFO]: Done. Clips written to {out}/")
254
255
256if __name__ == "__main__":
257 main()
示例 1:Kit 视口#
uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
--example 1 --num_envs 4 --viz kit
录制 Kit 交互视口(RTX 渲染器)
显示 4 个并行环境
将一个视频片段写入
videos/recording_tutorial/example_1/kit_viewport_0000.mp4
Kit 可视化器
示例 2:场景传感器,无界面运行#
uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
--example 2 --num_envs 16
场景传感器
不打开可视化器窗口;直接从
tiled_camera传感器读取帧将一个视频片段写入
videos/recording_tutorial/example_2/sensor_0000.mp4source="sensor:tiled_camera"中的tiled_camera是该相机在env.scene.sensors中注册时使用的键传感器的
data_types必须包含待录制通道;默认通道为rgb,也支持depth、segmentation和normals,见 数据源
示例 3:同时录制所有来源#
uv run python scripts/tutorials/07_visualizers/run_video_recording.py \
--example 3 --num_envs 4 --viz kit,newton_gl
将四个独立的视频片段写入 videos/recording_tutorial/example_3/ :
kit_viewport_0000.mp4:Kit 交互视口(RTX 渲染器)tiled_kit_viewport_0000.mp4:Kit 平铺相机网格(各环境的视图)newton_viewport_0000.mp4:Newton GL 查看器帧缓冲sensor_0000.mp4:场景平铺相机传感器(离线渲染)
Newton GL 可视化器
Kit 可视化器平铺串流
常用配置#
以下命令行和配置示例不属于教程脚本,可按实际需要使用。
在强化学习训练中录制#
训练时不打开窗口,按固定间隔录制短片段:
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --video \
--video_length 200 --video_interval 2000
未设置 --viz 时,--video 从无窗口 Newton GL 可视化器录制。以下命令每 2,000 步录制一段 200 步的视频。除非录制器设置了 output_dir,train 和 play 入口将视频写入本次运行日志目录下的 videos/;自行构建录制器的脚本写入 output_dir,默认是 videos。添加 --viz kit,可在窗口中观察运行并录制相同视图。
从场景传感器录制深度或分割结果#
在传感器数据源后追加通道名称,即可录制 RGB 以外的数据。每个录制器写入独立文件,因此一次运行可以同时录制同一相机的多个通道:
env_cfg.video_recorders = [
VideoRecorderCfg(source="sensor:tiled_camera:rgb", output_filename_prefix="rgb"),
VideoRecorderCfg(source="sensor:tiled_camera:depth", output_filename_prefix="depth"),
]
深度使用 turbo 色图,映射范围为 depth_colormap_min 到 depth_colormap_max。segmentation 和 normals 同样可以为每个录制器设置独立文件名前缀。
使用 Viser 或 Rerun 查看视频流时录制#
Viser 和 Rerun 向浏览器或查看器发送视频流,但不提供帧捕获,无法直接录制。可以同时运行一个支持帧捕获的无界面可视化器:
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz viser --video viz:newton_gl
也可以设置 source="sensor:<name>",直接从场景相机录制,无需可视化器。
从独立相机视角录制#
录制角度应在可视化器上配置,不在录制器上配置。如需在交互式 Kit 查看器之外,从不同角度的无窗口 Newton 可视化器录制,请使用 --viz kit 并配置两个可视化器;录制器会以无窗口方式添加 Newton 可视化器:
from isaaclab_visualizers.kit import KitVisualizerCfg
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg
env_cfg.sim.visualizer_cfgs = [
KitVisualizerCfg(eye=(4.0, 4.0, 2.0)),
NewtonGLVisualizerCfg(eye=(12.0, 0.0, 6.0)),
]
env_cfg.video_recorders = [
VideoRecorderCfg(source="viz:newton_gl", output_dir="videos/"),
]
也可以在场景中使用 CameraCfg 传感器,并通过 source="sensor:<name>" 录制。这样可以完全控制录制视角,无需第二个交互式可视化器。
数据源#
source 字符串用于选择采集对象:
来源字符串 |
采集对象 |
|---|---|
|
|
|
Kit 可视化器视口 |
|
Kit 相机流面板,需要设置 |
|
Newton GL 可视化器视口 |
|
Newton OVRTX 路径追踪视口 |
|
Newton GL 相机流面板,需要设置 |
|
|
|
RGB 通道 |
|
深度,采用 turbo 色图,范围为 |
|
分割,使用颜色区分 |
|
表面法线,使用颜色表示 |
相机角度、分辨率及其他可视化设置应在对应的可视化器配置中设置,不在录制器中设置。--viz 未选中的 viz:<type> 可视化器仅为录制而无窗口运行,使用 sim.visualizer_cfgs 中对应配置的设置。
visualizer 可作为 viz 前缀的完整形式,例如 "visualizer:kit" 等同于 "viz:kit"。已弃用的 newton 类型("viz:newton")仍可使用,但会发出警告;请改用 newton_gl。这里也接受不带前缀的可视化器类型,例如 VideoRecorderCfg(source="newton_gl") 等同于 VideoRecorderCfg(source="viz:newton_gl")。
备注
可通过 "viz:newton_rtx" 录制 Newton RTX 查看器的帧缓冲区,但不支持录制其流式视图。
片段控制#
字段 |
默认值 |
含义 |
|---|---|---|
|
|
每个片段的环境步数 |
|
|
|
|
|
输出帧率;设为 |
|
|
输出文件目录(按需创建) |
|
|
文件名前缀;输出文件名为 |
|
|
删除较早的片段;设为 |
运行开始时录制一个片段:
VideoRecorderCfg(source="viz:kit", video_length=500, video_interval=0)
每隔 1,000 个环境步录制一个片段:
VideoRecorderCfg(source="viz:kit", video_length=200, video_interval=1000)
磁盘上仅保留最新片段:
VideoRecorderCfg(source="viz:kit", video_length=200, video_interval=1000,
keep_last_n_clips=1)
限制#
source="viz:newton_gl:streaming_view"和source="viz:kit:streaming_view"要求在对应可视化器配置中设置streaming_view=True;否则构造录制器时会抛出RuntimeError。使用
source="sensor:<name>"时,场景配置中必须存在该名称的传感器,其data_types必须包含待录制通道,默认通道为"rgb"。
可视化器 |
|
说明 |
|---|---|---|
|
✓ |
启动 Isaac Sim,并在启动时为录制启用相机渲染。 |
|
✓ |
无窗口时使用 pyglet 的 EGL 后端;这是 |
|
✓ |
启动 OVRTX 运行时;捕获帧时,将路径追踪帧缓冲区从 GPU 回读到 CPU。 |
|
✗ |
远程流式工具,无本地帧捕获 API。 |
|
✗ |
浏览器流式工具,无本地帧捕获 API。 |
使用 Rerun 或 Viser 查看视频流时的录制方法,见 使用 Viser 或 Rerun 查看视频流时录制。
另请参阅#
可视化:配置交互式可视化器并录制其画面
在可视化器中显示相机流视图:配置平铺相机面板
在训练期间采集传感器帧 :将逐帧传感器输出保存为图像