vscode+Remote-ContainersでROS2開発環境を構築してみた

# はじめに
2020年のGWは外出自粛なので、なかなか手が出なかったROS2に手を付けることにしました。

そこでどうせならとdocker使ってみることにしたのですが、簡単なpackageを作ろうとするとContainer内でのROS2のソース編集が面倒だったので、vscodeのRemote-ContainersというExtensionを使ってみました。

これは**DockerのContainer内をホストPCのvscodeでそのまま編集できる拡張機能**になります。(vscodeはこういう機能強いですね。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/874358db-ce73-fbe0-a31e-fba3fc1c5909.png)

Remote-Containersを使うとvscodeのterminalでros2のコマンド実行と、ソース編集は普段のvscodeをそのまま使ってでき、Container内であることをあまり意識しないで開発出来そうです。（ただ後述しますが、まだまだうまく使いこなせていない状況です。）

環境構築ばっかりして全然ROS2開発的なことは進んでないのですが(汗)、折角なのでその使用方法について備忘録的にまとめてみました。

# 環境
以下のVersionを使用しています。

- Ubuntu : 18.04
    - arch : x86_64
- Docker : 18.09.7
- vscode : 1.44.2
- (vscode extension)remote container : 0.112.0

# Install
Docker, vscodeのInstall方法は割愛します。
Remote-ContainersはvscodeのEXTENSIONSを選択、"remote containers"と入力してInstallしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/f5a09a02-5de3-b71e-5b26-5959a1accd13.png)

# Remote-Containersの設定
Remote-Containersの使い方はこちらの記事を参考にしました。

[VS CodeでDocker開発コンテナを便利に使おう](https://qiita.com/Yuki_Oshima/items/d3b52c553387685460b0)

.devcontainerというディレクトリ以下に
- Dockerfile
- devcontainer.json
の2つのファイルを用意します。

## Dockerfile
これは通常のDockerfileと同じフォーマットです。

一部をmicrosoftのサンプルをベースに変更を加えました。

参考：[vscode-dev-containers/containers/ubuntu-18.04-git/.devcontainer/](https://github.com/microsoft/vscode-dev-containers/tree/v0.112.0/containers/ubuntu-18.04-git/.devcontainer)

変更箇所は↓こちらです。

```Dockerfile
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Set to false to skip installing zsh and Oh My ZSH!
ARG INSTALL_ZSH="true"

# Location and expected SHA for common setup script - SHA generated on release
ARG COMMON_SCRIPT_SOURCE="https://raw.githubusercontent.com/microsoft/vscode-dev-containers/v0.112.0/script-library/common-debian.sh"
ARG COMMON_SCRIPT_SHA="28e3d552a08e0d82935ad7335837f354809bec9856a3e0c2855f17bfe3a19523"


# Avoid warnings by switching to noninteractive
ENV DEBIAN_FRONTEND=noninteractive

# Configure apt and install packages
RUN apt-get update \
    && apt-get -y install --no-install-recommends apt-utils dialog wget ca-certificates 2>&1 \
    #
    # Verify git, common tools / libs installed, add/modify non-root user, optionally install zsh
    && wget -q -O /tmp/common-setup.sh $COMMON_SCRIPT_SOURCE \
    && if [ "$COMMON_SCRIPT_SHA" != "dev-mode" ]; then echo "$COMMON_SCRIPT_SHA /tmp/common-setup.sh" | sha256sum -c - ; fi \
    && /bin/bash /tmp/common-setup.sh "$INSTALL_ZSH" "$USERNAME" "$USER_UID" "$USER_GID" \
    && rm /tmp/common-setup.sh \
    #
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*
```


FROMで設定するbase imageはROS2のeloquentのイメージとしています。
https://hub.docker.com/r/osrf/ros/

```Dockerfile
FROM osrf/ros:eloquent-desktop
```

このFROMの指定先を環境に合わせて変更する必要はあります。(例えばarm64v8は別）

公式のros2のimageは入っていない機能もあったりするらしいので、FROMはrosのimageにするのをやめて、Docker build時にインストールコマンドでROS2をインストールするような形としても使えると思われます。

参考：[Docker上でGUIのROS1/ROS2を一瞬でセットアップする方法](https://qiita.com/karaage0703/items/957bdc7b4dabfc6639da)

## devcontainer.json
こちらはcppのサンプルをベースにしています。

```json:devcontainer.json
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.112.0/containers/cpp
{
	"name": "ros2_docker_ws",
	"dockerFile": "Dockerfile",
	"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined", "--net=host"],

	// Set *default* container specific settings.json values on container create.
	"settings": { 
		"terminal.integrated.shell.linux": "/bin/bash"
	},

	// Add the IDs of extensions you want installed when the container is created.
	"extensions": [
		"ms-vscode.cpptools"
	],

	// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
	"remoteUser": "vscode",

    "workspaceFolder": "/home/vscode/ros2_vscode_ws/",
    "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/home/vscode/ros2_vscode_ws/"
}
```

"runArgs"でdocker run時のオプションが追加できるのですが、なぜか"-e"でENVの追加は反映されませんでした。
Container内でのマウント先は"workspaceFolder","workspaceMount"で設定し、私の場合は`/home/vscode/ros2_vscode_ws/`としています。(追記：最初に記事を書いたときは/home直下に配置していましたが気持ちわるいので変更しました。）

```json:devcontainer.json
   "workspaceFolder": "/home/vscode/ros2_vscode_ws/",
    "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/home/vscode/ros2_vscode_ws/"
```

私の設定ファイル例はこちらにあります。こちらのrepoをそのままcloneしてもらっても試すことは可能です。
[ros2_vscode_ws](https://github.com/Katsushun89/ros2_vscode_ws)

# Open Container
設定ファイルは用意できたので、vscodeを操作してDocker buildし、Containerを立ち上げます。

まずRemote-Containersをインストール済みであれば、vscodeの左下にこの緑色のアイコンがあるのでそれをクリックして、


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/ac9b57cf-643d-de28-d684-471b0d2734eb.png)

Open Folder in Container...を選択し、`.devcontainer`が配置されたディレクトリを選択します。
[ros2_vscode_ws](https://github.com/Katsushun89/ros2_vscode_ws)をcloneしている場合は、そのままros2_vscode_wsディレクトリを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/309559b8-a653-6d9d-a0e5-75473fd9a814.png)

そうするとDocker buildが始まります。
進捗状況は右下のダイアログの`Starting with Dev container`をクリックするとvscodeのterminalに表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/6f0c7db8-9375-9c26-ead2-c7f1137831fa.png)

何かエラーがあるとこちらで確認し、Dockerfileやdevcontainer.jsonを修正して再度Openします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/84239362-bcb7-5783-88b5-1d930350f918.png)

Build中はvscodeの左下が`Opeining Remote..`の表示になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/626e833c-9a49-f241-39f0-c582f3fcd413.png)

Docker build完了して正常にrunされると`Dev Container`という表示に切り替わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/9b3a52dd-2dc6-9820-f6c4-119ed8e92504.png)

ここまでくるとDocker runは出来た状態です。

# ROS2 pub/sub確認

＋アイコンでterminalを追加するとコンテナ内で操作が行えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/681bf050-bb2a-c255-45d0-3d269d569714.png)

vscodeのterminal側と別で立ち上げたPC側のターミナルでdocker runしてpub/sub通信出来るか確認します。

vscode側Terminal

```terminal
export ROS_DOMAIN_ID=1
source /opt/ros/eloquent/setup.bash
ros2 run demo_nodes_cpp listener
```

PC側Terminal

```terminal
$ docker run -it --rm -e ROS_DOMAIN_ID=1 osrf/ros:eloquent-desktop ros2 run demo_nodes_cpp talker
```

vscode側のterminalでlisnterを立ち上げており、そこで受信したtopicが表示されたので無事通信出来ました。

```terminal
# ros2 run demo_nodes_cpp listener
[INFO] [listener]: I heard: [Hello World: 93]
[INFO] [listener]: I heard: [Hello World: 94]
[INFO] [listener]: I heard: [Hello World: 95]
[INFO] [listener]: I heard: [Hello World: 96]
[INFO] [listener]: I heard: [Hello World: 97]
[INFO] [listener]: I heard: [Hello World: 98]
[INFO] [listener]: I heard: [Hello World: 99]
[INFO] [listener]: I heard: [Hello World: 100]
```

# Sample pkgのCreate
Smaple的なpkgをcreateしました。

```terminal
$ cd ~/ros2_vscode_ws/src
$ ros2 pkg create --build-type ament_cmake --node-name hello_world ros2_training
```

そうすると、vscodeのEXPLORER側にファイルが追加されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/302503/a94ab43a-7cbe-ae7b-4dab-cce40cdd4eb9.png)

cppファイルの追加や編集もvscode上で出来ますので普段通りにできました。



# さいごに
vscodeのRemote-Containersを使って普段のvscodeを使ってContainer内でのROS2開発ができる環境を作ってみました。簡単な開発やPCの環境に影響を与えずにROS2を試すのには便利そうです。

ただ、いくつか課題というか、まだどうやっていくか決めかねている点や確認出来ていない点があります。

### vscodeのworkspaceで指定するディレクトリをどうするか
今回はcolcon build実行する`~/ros2_ws`相当をworkspaceとしていますが、本来開発するのは`~/ros2_ws/src`以下に配置されるros pkg単位になるので、本来はその単位でマウントした方が良いか、現状のままでsubmodule的に扱うのが良いか、よくわかっていないです。

公式の情報をみるともう少し見解出せるかもしれないです。
[Developing inside a Container](https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference)

### HWリソースのアクセスは未確認
通常のDockerと同じように使用出来ていると思いますが、HWリソース周りが動作するかは未確認です。

### ネットワーク上の他マシンとの通信
ros2でdocker runして他マシンと通信する場合はoptionで`--net=host`が追加必要ですが、
Docker runのargが恐らくうまく反映されていないので今のままだと他マシンと通信できないかもしれないです。

ただDocker image自体にはvscode経由ではなくてもアクセスできるので、PC上のterminalから個別のdocker runして、その時に`--net=host`を追加するという回避策は出来そうです。

```terminal
$ docker images
REPOSITORY                                                TAG                 IMAGE ID            CREATED             SIZE
vsc-ros2_vscode_ws-da058d6a6689661782fb9fdd8ebdc750-uid   latest              8128a9b74c56        3 hours ago         3.69GB
vsc-ros2_vscode_ws-da058d6a6689661782fb9fdd8ebdc750       latest              be56c327cf1d        3 hours ago         3.69GB
osrf/ros                                                  eloquent-desktop    8133230e6f08        3 months ago        2.6GB

```

---

余り詳しくないですが触ってみた的な記事なので、もっと良い手段があれば教えてください。


# 参考情報

## ROS2 Docker通信
[ROS2 Dashing Docker通信](https://qiita.com/tomoyafujita/items/4978bb88b19d5fee5305)
[ROS複数Docker通信](https://qiita.com/tomoyafujita/items/07937a25bc48aa076056)

## Remote-Containers
[VS CodeでDocker開発コンテナを便利に使おう](https://qiita.com/Yuki_Oshima/items/d3b52c553387685460b0)
[(公式)Developing inside a Container](https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference)


## Docker関連
正直Docker周りはあまり詳しくなかったので、この辺りをみてDockerfileの内容など調べました。

[いまさらだけどDockerに入門したので分かりやすくまとめてみた](https://qiita.com/gold-kou/items/44860fbda1a34a001fc1)
[Dockerfile のベストプラクティス](http://docs.docker.jp/engine/articles/dockerfile_best-practice.html)