使用 Docker 运行示例

使用 Docker 运行示例#

从 Isaac Lab 仓库的根目录开始, docker 目录包含所有与 Docker 相关的文件。这些文件包括三个文件( Dockerfiledocker-compose.yaml.env ),这些文件由 Docker 使用,以及我们用来与它们交互的附加脚本, container.sh

在本教程中,我们将学习如何使用 Isaac Lab Docker 容器进行开发。有关 Docker 设置的详细说明,包括安装和获取对 Isaac Sim 图像的访问权限,请参考 Docker 指南 。有关 Docker 的一般说明,请参阅 官方文档

构建容器#

从 Isaac Lab 仓库的根目录构建 Isaac Lab 容器,我们将运行以下命令:

python docker/container.py start

终端将首先拉取基础 IsaacSim 镜像,在其上构建 Isaac Lab 镜像的附加层,然后运行 Isaac Lab 容器。首次构建可能需要几分钟,但后续运行时间会更短,因为 Docker 的缓存可防止重复工作。如果在终端上运行命令 docker container ls ,输出将列出当前系统上运行的容器。如果一切设置正确,将会出现一个名为 NAME isaac-lab-base 的容器,类似于下面的内容:

CONTAINER ID   IMAGE               COMMAND   CREATED           STATUS         PORTS     NAMES
483d1d5e2def   isaac-lab-base      "bash"    30 seconds ago   Up 30 seconds             isaac-lab-base

容器启动后,我们可以从终端进入其中。

python docker/container.py enter

进入 Isaac Lab 容器后,我们以超级用户 root 的身份在终端中。这个环境包含一个 Isaac Lab 仓库的副本,但也可以访问 Isaac Sim 的目录和库。我们可以使用一些很方便的别名在这个环境中运行实验,这些别名已经放入了超级用户 root.bashrc 中。例如,我们可以通过输入别名 isaaclab 来任意地从任何地方使用 isaaclab.sh 脚本。

除了在容器中,我们还将 IsaacLab/source 目录从主机机器进行了 绑定挂载 。这意味着,如果我们在主机机器上的编辑器中修改了该目录下的文件,不需要重新构建 Docker 镜像,容器中的更改会立即反映出来。

现在,我们将在容器中运行一个示例脚本,以演示如何从 Isaac Lab Docker 容器中提取输出。

代码#

该教程对应的是 IsaacLab/source/standalone/tutorials/00_sim 目录中的 log_time.py 脚本。

log_time.py 的代码
 1# Copyright (c) 2022-2024, The Isaac Lab Project Developers.
 2# All rights reserved.
 3#
 4# SPDX-License-Identifier: BSD-3-Clause
 5
 6"""
 7This script demonstrates how to generate log outputs while the simulation plays.
 8It accompanies the tutorial on docker usage.
 9
10.. code-block:: bash
11
12    # Usage
13    ./isaaclab.sh -p source/standalone/tutorials/00_sim/log_time.py
14
15"""
16
17"""Launch Isaac Sim Simulator first."""
18
19
20import argparse
21import os
22
23from omni.isaac.lab.app import AppLauncher
24
25# create argparser
26parser = argparse.ArgumentParser(description="Tutorial on creating logs from within the docker container.")
27# append AppLauncher cli args
28AppLauncher.add_app_launcher_args(parser)
29# parse the arguments
30args_cli = parser.parse_args()
31# launch omniverse app
32app_launcher = AppLauncher(args_cli)
33simulation_app = app_launcher.app
34
35"""Rest everything follows."""
36
37from omni.isaac.lab.sim import SimulationCfg, SimulationContext
38
39
40def main():
41    """Main function."""
42    # Specify that the logs must be in logs/docker_tutorial
43    log_dir_path = os.path.join("logs")
44    if not os.path.isdir(log_dir_path):
45        os.mkdir(log_dir_path)
46    # In the container, the absolute path will be
47    # /workspace/isaaclab/logs/docker_tutorial, because
48    # all python execution is done through /workspace/isaaclab/isaaclab.sh
49    # and the calling process' path will be /workspace/isaaclab
50    log_dir_path = os.path.abspath(os.path.join(log_dir_path, "docker_tutorial"))
51    if not os.path.isdir(log_dir_path):
52        os.mkdir(log_dir_path)
53    print(f"[INFO] Logging experiment to directory: {log_dir_path}")
54
55    # Initialize the simulation context
56    sim_cfg = SimulationCfg(dt=0.01)
57    sim = SimulationContext(sim_cfg)
58    # Set main camera
59    sim.set_camera_view([2.5, 2.5, 2.5], [0.0, 0.0, 0.0])
60
61    # Play the simulator
62    sim.reset()
63    # Now we are ready!
64    print("[INFO]: Setup complete...")
65
66    # Prepare to count sim_time
67    sim_dt = sim.get_physics_dt()
68    sim_time = 0.0
69
70    # Open logging file
71    with open(os.path.join(log_dir_path, "log.txt"), "w") as log_file:
72        # Simulate physics
73        while simulation_app.is_running():
74            log_file.write(f"{sim_time}" + "\n")
75            # perform step
76            sim.step()
77            sim_time += sim_dt
78
79
80if __name__ == "__main__":
81    # run the main function
82    main()
83    # close sim app
84    simulation_app.close()

代码说明#

Isaac Lab Docker 容器具有几个 数据卷 ,以便在主机计算机和容器之间实现持久存储。其中一个数据卷是 /workspace/isaaclab/logs 目录。 log_time.py 脚本指定该目录作为应该写入 log.txt 的位置:

    # Specify that the logs must be in logs/docker_tutorial
    log_dir_path = os.path.join("logs")
    if not os.path.isdir(log_dir_path):
        os.mkdir(log_dir_path)
    # In the container, the absolute path will be
    # /workspace/isaaclab/logs/docker_tutorial, because
    # all python execution is done through /workspace/isaaclab/isaaclab.sh
    # and the calling process' path will be /workspace/isaaclab
    log_dir_path = os.path.abspath(os.path.join(log_dir_path, "docker_tutorial"))
    if not os.path.isdir(log_dir_path):
        os.mkdir(log_dir_path)
    print(f"[INFO] Logging experiment to directory: {log_dir_path}")

正如注释中所述, os.path.abspath() 将在前面添加 /workspace/isaaclab ,因为在 Docker 容器中,所有的 Python 执行都是通过 /workspace/isaaclab/isaaclab.sh 进行的。结果将是一个名为 log.txt 的文件,在每个模拟步骤上, sim_time 将被写入一行:

    # Prepare to count sim_time
    sim_dt = sim.get_physics_dt()
    sim_time = 0.0

    # Open logging file
    with open(os.path.join(log_dir_path, "log.txt"), "w") as log_file:
        # Simulate physics
        while simulation_app.is_running():
            log_file.write(f"{sim_time}" + "\n")
            # perform step
            sim.step()
            sim_time += sim_dt

执行脚本#

我们将执行脚本以生成一个日志,在我们的执行中添加 --headless 标志以防止 GUI:

isaaclab -p source/standalone/tutorials/00_sim/log_time.py --headless

现在, log.txt 将在 /workspace/isaaclab/logs/docker_tutorial 中生成。如果我们在容器中输入 exit 以退出,我们将返回到主机终端环境中的 IsaacLab/docker 中。然后,我们可以输入以下命令,从 Docker 容器中检索日志并将它们放在主机上:

./container.py copy

我们将看到一个终端输出报告,报告了我们从容器中检索出的输出物。如果我们转到 /isaaclab/docker/artifacts/logs/docker_tutorial ,我们将看到上面由脚本生成的 log.txt 文件的副本。

artifacts 下的每个目录对应于容器中与 数据卷 映射的 Docker 目录, container.sh copy 命令将它们从这些 `数据卷`_ 中复制到这些目录中。

我们可以再次运行 container.sh enter 来返回 Isaac Lab Docker 终端环境,但是我们已经检索到了日志并希望去检查它们。我们可以使用以下命令停止 Isaac Lab Docker 容器:

./container.py stop

这将关闭 Docker Isaac Lab 容器。镜像将保留并保持可用供进一步使用, 数据卷 的内容也将保留。如果我们希望释放镜像占用的磁盘空间,(~20.1GB),并且不介意下次运行 ./container.sh start 时重复构建过程,我们可以输入以下命令来删除 isaac-lab-base 镜像:

docker image rm isaac-lab-base

随后运行 docker image ls 将会显示标记为 isaac-lab-base 的镜像已经不存在。如果我们希望释放更多空间,我们可以对底层 NVIDIA 容器重复该过程。如果需要更强大的从 Docker 释放资源的方法,请查阅 `docker prune`_命令的文档。