Isaac SimをDockerでインストールする

# 環境について 
Dockerでisaac simをインストールします。 
nvidia公式で配布されている`nvcr.io/nvidia/isaac-sim:4.5.0`というイメージがありますが、以下2点が理由で別のベースイメージを使用します。  
- Isaac Sim WebRTC Streaming ClientというVNCのようなかたちで操作しなければならない
- cudaバージョン等を自分で指定したものを使いたい

色々なインストール方法があるのですが、[isaac lab](https://isaac-sim.github.io/IsaacLab/main/source/setup/installation/pip_installation.html)としてはpip installの方法が紹介されているので、このやり方をベースとします。  
Pythonのパッケージ管理はuvで、venv仮想環境上にisaac simをインストールします。  
isaac simのインストール自体にはROSやcudaは必要ないはずですが、isaac labやpytorch等を用いたライブラリを後々使用したいので入れています。  

- OS: Ubuntu 22.04
- GPU: Geforce RTX 3080 (10GB)
- GPU driver: 570.133.07
- Dockerベースイメージ: `nvidia/cuda:12.4.1-cudnn-devel-ubuntu22.04`
- isaac sim: 4.5.0
- ROS: ROS2 humble

# 先にやっておくこと
## GPUのdriverインストール
GPUのdriverをインストールします。  
apt経由でもいいのですが、個人的にあまり上手くいったためしがないので.runファイル経由でインストールしました。  
[driver手動検索のページ](https://www.nvidia.com/ja-jp/drivers/)
```bash
sudo sh NVIDIA-Linux-x86_64-570.133.07.run
```
だいたいよしなにYesを選択しておけば問題ないのですが、途中でUEFIのセキュアブートの箇所で引っ掛かります。  
私の環境はWindows11とのデュアルブートとなっております。  
Windows11は10からの移行時にUEFIを要求されてました。  
そのため、Windows11とのデュアルブートをしている方は確実にこれが要求されるかと思います。  
以下ページを参考にさせていただき、公開鍵を作ります。  
参考: [UEFI のセキュアブート機にNVIDIAのドライバを入れる話](https://qiita.com/arc279/items/99f08b549c95881007b9)

そして、rebootする前に以下を実行してパスワードを設定します。
`********`の箇所はtabで候補が出ると思います。
```bash
sudo mokutil --import /usr/share/nvidia/nvidia-modsign-crt-********.der
```
その後再起動するのですが、いつものubuntuの起動画面の前に1枚別の画面が挟まります。  
一番上は普通に起動する選択肢ですが、私の場合は2つ目にenrollする選択肢が出たのでそちらを選びます。  
そして、先ほど入力したパスワードをいれます。

この時点で、`nvidia-smi`が通ると思います。


<!-- ## cuda関係のインストール
色々と方法がありますが、私はnetwork版でインストールしました。  
[cuda12.4.1のnetwork版](https://developer.nvidia.com/cuda-12-4-1-download-archive?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_network)
```bash
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb 
sudo apt update
sudo apt install cuda-toolkit-12-4
```
この時点で、`nvcc -V`が通ると思います。  
(もしかしたら、改めて`source ~/.bashrc`をする必要があるかもしれません) -->


## Docker関係のインストール
まずは[公式ドキュメント](https://docs.docker.com/engine/install/ubuntu/)の通り、dockerをインストールします。  
以下はそのコピペです。
```bash
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt remove $pkg; done

sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

さらに、[nvidia-container-toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)をインストールします。以下はそのコピペです。  
```bash
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
  && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo sed -i -e '/experimental/ s/^#//g' /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
		
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
```

# Docker環境をつくる
## Dockerイメージをビルドする
Docker composeを使用します。

ディレクトリ構成は好みですが、私は以下のようなかたちで作っていることが多いです。
```
.
├── docker
|   ├── cpu
|   |   └── Dockerfile
|   ├── gpu
|   |   └── Dockerfile
|   ├── compose.yaml
|   ├── entrypoint.sh
|   └── .env
└── src
```

まずは、Docker内でローカルユーザをつくるために.envファイルをつくります。  
以下のようにして現在ホスト側でログインしているユーザの情報をそのまま使います。手打ちしてもよいです。
```bash
echo UID=$(id -u) > .env
echo GID=$(id -g) >> .env
echo USERNAME=$(whoami) >> .env
```

.envはこうなります。
```bash:.env
UID=1000
GID=1000
USERNAME=<usernameを書く>
```

次にcompose.yamlファイルを作成します。  
docker-compose.yaml時代の書き方とかが混ざっていたり、port等は必要がないものがある気がしますが、気にしない方向でいきます。  
```yaml:compose.yaml
services:
  gpu:
    build:
      context: ../
      dockerfile: docker/gpu/Dockerfile
      args:
        - UID=${UID}
        - GID=${GID}
        - USERNAME=${USERNAME}
    env_file:
      - .env
    image: <イメージ名を入力する>
    command: /bin/bash
    ports:
      - 8000:8000
    working_dir: /home/${USERNAME}
    tty: true
    volumes:
      - /tmp/.X11-unix:/tmp/.X11-unix
      - /home/${USERNAME}/.Xauthority:/root/.Xauthority
    runtime: nvidia
    environment:
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: all
      DISPLAY: $DISPLAY
      QT_X11_NO_MITSHM: 1
    privileged: true
    shm_size: '16gb'
    network_mode: "host"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
```
恐らくポイントとしてあげられるのが以下項目で、これがないとDockerコンテナ内のnvidia-smiでGPUを見つけられていても、vulkaninfoで見つけられない場合があります。
```yaml:compose.yamlの一部
    runtime: nvidia
    environment:
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: all
```
ないと下記のようなエラーが出てきます。
```bash
[Error] [gpu.foundation.plugin] No device could be created. Some known system issues:
- The driver is not installed properly and requires a clean re-install.
- Your GPUs do not support RayTracing: DXR or Vulkan ray_tracing, or hardware is excluded due to performance.
- The driver cannot enumerate any GPU: driver, display, TCC mode or a docker issue. For Vulkan, test it with Vulkaninfo tool from Vulkan SDK, instead of nvidia-smi.
- For Ubuntu, it requires server-xorg-core 1.20.7+ and a display to work without --no-window.
- For Linux dockers, the setup is not complete. Install the latest driver, xServer and NVIDIA container runtime.
```

Dockerfileはこんな感じです。  
[isaac sim](https://docs.isaacsim.omniverse.nvidia.com/latest/installation/install_python.html)側のインストールだとなぜかエラーが出たので、[isaac lab](https://isaac-sim.github.io/IsaacLab/main/source/setup/installation/pip_installation.html)側のインストールの方法を参考にしています。  
(恐らく、pytorchをあらかじめインストールしておく必要があったのだと思います)

ちょこちょことUSERを切り替えて、isaac sim使用時をローカルユーザとなるようにしています。  
一応isaacsim実行時に引数を指定することでrootでも実行できるようなのですが、ローカルユーザ使用が前提となっているようなのでこのようにしています。  
rootのままuv pip install ~~~してしまうと、rootユーザでコンテナに入らないと実行できなくなります。

```docker:Dockerfile
ARG BASE_IMAGE=nvidia/cuda:12.4.1-cudnn-devel-ubuntu22.04
FROM $BASE_IMAGE

ARG UID
ARG GID
ARG USERNAME

USER root
SHELL ["/bin/bash", "-c"]

ENV TZ=Asia/Tokyo

RUN export DEBIAN_FRONTEND=noninactive \
    && apt-get update && apt-get install -y --no-install-recommends \
        tzdata \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

RUN apt-get update && apt-get install -y --no-install-recommends \
        apt-transport-https \
        build-essential \
        bzip2 \
        ca-certificates \
        curl \
        ffmpeg \
        g++ \
        git \
        gnupg \
        graphviz \
        locales \
        libgl1-mesa-dev \
        libusb-1.0-0-dev \
        lsb-release \
        net-tools \
        openssh-client \
        software-properties-common \
        sudo \
        unzip \
        vim \
        wget \
        xorg-dev \
        zip \
        emacs \
        python3-dev \
        python3-setuptools \
        python3-pip \
        python3-venv \
        vulkan-tools \
        usbutils \
        x11-apps \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

RUN groupadd -g ${GID} ${USERNAME} && useradd -u ${UID} -g ${GID} -s /bin/bash -m ${USERNAME}
WORKDIR /home/$USERNAME/ws

# ROS2
ENV ROS_DISTRO=humble
RUN locale-gen ja_JP ja_JP.UTF-8 \
    && update-locale LC_ALL=ja_JP.UTF-8 LANG=ja_JP.UTF-8
ENV LANG=ja_JP.UTF-8
RUN add-apt-repository universe \
    && curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg \
    && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null \
    && apt-get update && apt-get install -y \
        ros-$ROS_DISTRO-desktop \
        ros-dev-tools \
        ros-$ROS_DISTRO-xacro \
        ros-$ROS_DISTRO-joint-state-publisher \
        ros-$ROS_DISTRO-joint-state-publisher-gui \
        ros-$ROS_DISTRO-moveit \
        python3-rosdep \
        python3-colcon-common-extensions \
        python3-colcon-mixin \
        python3-vcstool \
        python3-argcomplete \
        # for isaacsim bridge
        ros-$ROS_DISTRO-vision-msgs \
        ros-$ROS_DISTRO-ackermann-msgs \
        python3-rosinstall \
        python3-rosinstall-generator \
        python3-wstool \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && rosdep init \
    && rosdep update

# uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

WORKDIR /home/$USERNAME/

USER $USERNAME
RUN mkdir isaacsim \
    && cd isaacsim \
    && uv venv --python 3.10 \
    && source .venv/bin/activate \
    && uv pip install --upgrade pip \
    && uv pip install torch==2.5.1 torchvision==0.20.1 --index-url https://download.pytorch.org/whl/cu124 \
    && uv pip install 'isaacsim[all,extscache]==4.5.0' --extra-index-url https://pypi.nvidia.com

USER root
RUN source /opt/ros/$ROS_DISTRO/setup.bash --extend \
    && cd ws \
    && git clone https://github.com/isaac-sim/IsaacSim-ros_workspaces.git \
    && cd IsaacSim-ros_workspaces/humble_ws \
    && rosdep update && apt-get update \
    && rosdep install -i --from-path src --rosdistro humble -y \
    && colcon build

# entrypoint
COPY docker/entrypoint.sh ./ws/entrypoint.sh
RUN chmod +x ./ws/entrypoint.sh

USER $USERNAME

ENTRYPOINT ["./ws/entrypoint.sh"]
```

enrtypoint.shはこんな感じです。  
現状はROSのためだけにあります。
```bash:entrypoint.sh
#!/bin/bash
set -e

source /opt/ros/$ROS_DISTRO/setup.bash --extend
source /home/$USERNAME/ws/IsaacSim-ros_workspaces/humble_ws/install/setup.bash --extend

exec "$@"
```

ビルドするときはdockerのディレクトリに移動してビルドします。
```bash
$ cd docker/
sudo docker compose build gpu
```

## Dockerコンテナをrunする
実行するときは以下のようなかたちです。

```bash
sudo docker compose run --rm -u <usernameを書く> gpu
```

コンテナ内で以下をするとisaac simが実行できると思います。  
実行後、GUIはすぐに立ち上がるのですが、諸々ロードするのに結構時間がかかり、GUI画面から応答待ちのダイアログが表示されるので待ちます。
```bash
cd isaacsim
source .venv/bin/activate
isaacsim
```

# ROS2チュートリアルを実行してみる
基本的には[公式のTurtlebotのチュートリアル](https://docs.isaacsim.omniverse.nvidia.com/latest/ros2_tutorials/tutorial_ros2_turtlebot.html#isaac-sim-app-tutorial-ros2-turtlebot)に沿って実行します。

この前提となる[ROS and ROS 2 Installation](https://docs.isaacsim.omniverse.nvidia.com/latest/installation/install_ros.html#isaac-sim-app-install-ros)については、既にROS2:humbleと関連パッケージはDockerfile内でもう入っており、恐らくROS2 BridgeはONになっているはずなのでスキップします。  
(Cyclone DDSを使う場合は戻ってくる必要があります)

コンテナをrunして、
```bash
sudo docker compose run --rm -u <usernameを書く> gpu
```
まず適当な場所に移動して、turtlebot3のrepositoryをcloneしておきます。
```bash
git clone -b humble-devel https://github.com/ROBOTIS-GIT/turtlebot3.git turtlebot3
```
そしてisaacsimを立ち上げます。
```bash
cd isaacsim
source .venv/bin/activate
isaacsim
```

ここからチュートリアルに沿っていきます。  
isaacsimのGUI画面の、下のIsaac Sim AssetsタブからEnvironments->SIMPLE_ROOM->simple_roomを選択してロードします。  
滑り台みたいなテーブル?が置いてあるシンプルな部屋がロードされたと思います。

File->Importから、先ほど別ターミナルでcloneしたTurtlebot3をインポートします。  
場所は`(cloneした場所)/turtlebot3/turtlebot3_description/urdf/turtlebot3_burger.urdf`です。  
インポートするときに、Linksの部分だけチュートリアルの画像と違うので、Static BaseからMoveable Baseに変更にしておきます。

インポートしたTurtlebot3はテーブル?の上に乗ってしまうので、右サイドバーのturtlebot3_burgerをクリックして表示される矢印をドラッグし、適当に床面の近くに移動させます。  
後に使うPlayボタンを押すと勝手に下に落ちるので、ちょっと床から浮かせておくくらいでいいそうです。

右サイドバーのturtlebot3_burger->joints->wheel_left_jointとwheel_right_jointをクリックし、その下に表示される諸々のPropertyの中からDumpingとStiffnessを探します。  
そして両方ともDampingを10000000.0、Stiffnessを0.0としておきます。　　
![isaac_install_right_area.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4006547/93a1db35-8fda-411d-b65e-008e5e0eae5d.png)


そして、次の[チュートリアルページ](https://docs.isaacsim.omniverse.nvidia.com/latest/ros2_tutorials/tutorial_ros2_drive_turtlebot.html)を見ながらWindow->Graph Editors->Action Graphからグラフを作ります。今回はコツコツ写経をしました。  
その中のMake Arrayはデフォルトは1入力ですが、右のPropertyからArray Sizeを手入力で変えるかプラスボタンを押して2入力とします。

グラフができた後は、各ノードをクリックして右サイドバーに表示されるPropertyに対して、以下を変更します。
- ROS2 Subscribe Twist Node: topic nameに`/cmd_vel`を入力します
- Differential Controllerのノードで以下のようにPropertyを変更します
  + Max Angular Speed: 1.0
  + Max Linear Speed: 0.22
  + Wheel Distance: 0.16
  + Wheel Radius: 0.025
- Articulation Controller: targetPrimでturtlebot3_burger->base_footprintを選択する
![isaac_install_target_prim.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4006547/d90a79c7-8c5c-42c1-be58-c021e76622ac.png)
- Constant Tokenのそれぞれのノードで以下のようにPropertyを変更します
  + Value: wheel_left_joint 
  + Value: wheel_right_joint

上記が終われば、左側にある三角の再生マークのplayボタンをクリックすると動作可能の状態になります。  
何らか失敗していると鬼のようなエラーと警告の嵐になるかと思いますので、直しましょう。


別ターミナルを立ち上げて、同じコンテナにexecで入ります。
```bash
sudo docker compose exec -u <usernameを書く> gpu bash
```
entrypoint.shを読み込んでないので中身に相当するものを読み込みます。  
(`source entrypoint.sh`でもよいです)
```bash
source /opt/ros/$ROS_DISTRO/setup.bash --extend
source /home/$USERNAME/ws/IsaacSim-ros_workspaces/humble_ws/install/setup.bash --extend
```
別ターミナルから以下を実行すると、壁にぶつかるまでTurtlebot3が直進します。
```bash
ros2 topic pub /cmd_vel geometry_msgs/Twist "{'linear': {'x': 0.2, 'y': 0.0, 'z': 0.0}, 'angular': {'x': 0.0, 'y': 0.0, 'z': 0.0}}"
```
壁にぶつかってもタイヤは動き続けます。
止めるには以下を実行します。
```bash
ros2 topic pub /cmd_vel geometry_msgs/Twist "{'linear': {'x': 0.0, 'y': 0.0, 'z': 0.0}, 'angular': {'x': 0.0, 'y': 0.0, 'z': 0.0}}"
```