ひつじTips

技術系いろいろつまみ食います。

WSL2とDockerでWindowsにROS2環境を作る+vscodeで開発できるようにする+VNCなしでGUI(rqt, rviz2)を使う

Windows10環境で,ROS2のパッケージ開発をDocker上で行うための手順をまとめます.

頻繁にROSを使わないので直接環境にROSを入れるのは抵抗感があり...... でもROS2を使ってみたい!ということで,ローカルにROS2環境を入れずに開発を行う,dockerでの開発フローを整えてみました.

具体的には,以下のようにして実現します.

  • ROS2の開発用のDockerイメージを作成
  • 開発用Dockerコンテナに,ホスト(Win)上にあるROS nodeのソースコードをマウント
  • vscodeで開発用Dockerコンテナ内のROS nodeのソースコードを編集
    • WinにROS環境が入っていなくてもコンテナ内には環境があるので,IntelliSenseなどは有効です
  • 開発用Dockerコンテナ内でビルド・動作確認
  • 開発したROS nodeの実行に必要最低限の環境が入ったリリース用Dockerイメージの作成

また,一応 rqt, rviz2 のGUIも開発用コンテナで出せるようにしています(あると開発中は便利なので).

GUIありROSのdocker環境の構築については,以下の記事などVNCでやってる人は見つけられたのですが,開発用のエディタまでVNC上で動かすことになるならちょっと微妙だなぁと...

ROSではない開発と同様に,ホスト上で動くvscodeを使ったリモート開発でやりたかった,というモチベーションがありました.

memoteki.net

qiita.com

前提

WSL2, Docker, vscode での一般的な開発環境の準備,具体的には以下の2点がなされている状態を前提とします.

  • WSL2/DockerのインストールとWSL2上でDockerが使える
  • vscodeのインストールとWSL上での開発ができる

両方とも以下に詳しい説明がありますし,ググればいろいろ情報は出ると思います.

docs.microsoft.com

また,DockerやROS2の基本的な使い方の説明は省きます(このへんある程度わかっている人でないと,ROS2をdockerで動かしたいという人はいないと思うので...)

本エントリで使用しているソースはこちらにあります.適宜記事内でも参照してます.

github.com

開発用ディレクトリの作成

とりあえず普通にROS2パッケージを作成するときのように,<ワークスペース名>/srcディレクトリをWSL上に作ります(ここではワークスペース名をwsとします).

また,ワークスペース以下にdockerディレクトリも作っておきます.

ws
├── docker
└── src

ROS2開発用Dockerイメージの準備

ROS2実行用イメージを用意し,そこに開発に必要なパッケージを加えて開発用イメージを作ります.

ROS2実行用イメージの用意

ちょっと何故かはよくわからないですが,DockerHubにはROSの公式っぽいレジストリが2つあります.

両方とも,Dockerfileは同じレポジトリのものを使ってるみたいですが,前者にはcoreとbaseがあり,後者はdesktop版しかありません.

まぁとは言え2つのレジストリなんて気にせずにdocker pullで入れられます.今回は,GUI(rqt, rviz2)も使うのでdesctop版を入れます.

$ docker pull osrf/ros:foxy-desktop

ROS2パッケージ開発用イメージの作成

osrf/ros:foxy-desktopをベースに,開発に必要なものを入れた開発用イメージを作成していきます.

Dockerfile等の細かい説明はここではしません.とりあえず,すでにあるものを使って進めますので,ここにある各ファイル(Dockerfile.devDockerfile.relbuild_image.shentrypoint.shrun_dev-container.sh)をros2-study/docker以下に配置してください.

.envファイルのDOCKER_IMG変数が,これから作成するDockerイメージの名前になるので適宜変更してください

ここでws/docker/build_image.shを実行すると,$DOCKER_IMG:dev_<ワークスペース名>という名前で開発用イメージを作成してくれます(本エントリの例だと,mu777/ros:dev_wsという名前になります)

$ cd ws
$ bash docker/build_image.sh

Dockerfile.devでは,osrf/ros:foxy-desktopをベースに開発に必要なライブラリをインストールしています.未検証ですが,sudogosubuild-essentiallibpython3-devpython3-pippython3-colcon-common-extensionspython3-rosdepがあれば最低限OKかなと思います.

が,他にも必要そうなものは適当に入れています.bash-completionがあると,補完が効いて便利なので入れています.

ちょっとROSのテストまわりはわかっていないので本記事には入れてません.テストに必要なパッケージ等も別途入れる必要があるかもしれません.適宜必要なものを入れるよう,修正ください.

ROS2開発用コンテナ上での開発

上までの手順で作成した開発用イメージのコンテナ上で,ROS2パッケージの開発を行います.

開発用コンテナの起動

何はともあれ,開発用コンテナを起動させましょう~

docker run実行時に開発のためのもろもろの設定が必要なため,起動処理をスクリプトrun_dev-container.sh)にまとめております.それを実行してみてください.

<ワークスペース名>_devという名前のコンテナが起動すればOKです!

$ cd ws
$ bash docker/run_dev-container.sh
$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
0e9c19de69d5        mu777/ros2:dev_ws           "/entrypoint.sh /bin…"   14 seconds ago      Up 12 seconds                           ws-dev 

run_dev-container.shでは,主にユーザ周りの設定やGUIを使う設定などを行っています.

ユーザ周りの設定に関しては,以下の記事を大変参考にさせていただきました🙏

qiita.com

vscodeでの開発設定

ほぼまんま以下の記事の内容を使っています(ありがたい...🙏🙏)

zenn.dev

ワークスペース以下に.vscodeフォルダを作成し,そこに上記事内のc_cpp_properties.json, settings.json, tasks.jsonを作成します..gitignoreも記事通り設定します.

この時点のディレクトリ構成は,以下のようになっているはずです.

ws
├── .gitignore
├── .vscode
│   ├── c_cpp_properties.json
│   ├── settings.json
│   └── tasks.json
├── docker
│   ├── .env
│   ├── Dockerfile.dev
│   ├── Dockerfile.rel
│   ├── build_image.sh
│   ├── entrypoint.sh
│   └── run_dev-container.sh
└── src


tasks.jsonだけ,rosdepを毎回実施するために以下のように書き換えました.

{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "ROS Build Setup",
      "type": "shell",
      "command": "source /opt/ros/$ROS_DISTRO/setup.bash; rosdep update; rosdep install -i --from-path ${workspaceRoot}/src --rosdistro $ROS_DISTRO -y;",
      "options": {
        "cwd": "${workspaceRoot}"
      }
    },
    {
      "label": "ROS Build",
      "type": "shell",
      "command": "colcon build",
      "options": {
        "cwd": "${workspaceRoot}"
      }
    },
    {
      "label": "Build",
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "dependsOrder": "sequence",
      "dependsOn": [
        "ROS Build Setup",
        "ROS Build"
      ],
      "problemMatcher": []
    }
  ]
}

開発用コンテナに接続されたvscodeを開く

やっとvscodeの出番が来ました!

Windowsで普通にvscodeを起動させ,vscodeのリモートエクスプローラから「Containers」を選ぶと,起動中の開発用コンテナ(ws-dev)がリストに出ていると思うので,それにAttachしてください.

vscodeのリモートエクスプローラ

vscodeのウインドウが開くと思うので,「Open Folder」からワークスペースのパスを選択し,開いてください(ワークスペースrun_dev-container.shでマウントされています)

作成したワークスペースを開く

これで,このvscode上で普通に開発ができます!!

コンテナ上でのROSパッケージの開発とビルド

普通にコンテナ上でいつものようにROSパッケージを開発すればよいだけなのですが,動作確認も兼ねて,以下のチュートリアルを普通にやってみます

docs.ros.org

vscodeのターミナルがコンテナ内になっているので,そこでコマンドを実行していきます(普通にターミナルからdocker execで開発用コンテナ内に入ってコマンド実行しても大丈夫です)

まずは,ros2 pkg createでパッケージを作成して,

$ cd ws/src
$ ros2 pkg create --build-type ament_cmake cpp_pubsub

パッケージの作成

チュートリアルに書いているwgetコマンドで,ソースをダウンロードします.

$ cd ws/src/cpp_pubsub/src/
$ wget -O publisher_member_function.cpp https://raw.githubusercontent.com/ros2/examples/foxy/rclcpp/topics/minimal_publisher/member_function.cpp
$ wget -O subscriber_member_function.cpp https://raw.githubusercontent.com/ros2/examples/foxy/rclcpp/topics/minimal_subscriber/member_function.cpp

ソースのダウンロード

package.xmlへ依存パッケージを記載して,

+  <depend>rclcpp</depend>
+  <depend>std_msgs</depend>

package.xmlへの記述

CMakeLists.txtを編集します.

+ find_package(rclcpp REQUIRED)
+ find_package(std_msgs REQUIRED)

+ add_executable(talker src/publisher_member_function.cpp)
+ ament_target_dependencies(talker rclcpp std_msgs)
+ add_executable(listener src/subscriber_member_function.cpp)
+ ament_target_dependencies(listener rclcpp std_msgs)

+ install(TARGETS
+   talker
+   listener
+   DESTINATION lib/${PROJECT_NAME})

CMakeLists.txtへ追記

チュートリアルに従ってビルドコマンドを実行してみると,ビルドできました🎉🎉

$ rosdep install -i --from-path src --rosdistro foxy -y
$ colcon build --packages-select cpp_pubsub

ビルド実行

ビルドコマンドはtasks.jsonに設定済みなので,vscodeからもビルドを実行できます.

ビルド実行 from vscode

ターミナルを2つ出して,talkerとlisternerをそれぞれ実行すると,ちゃんと実行されていることがわかります!👏

$ source install/setup.bash
$ ros2 run cpp_pubsub talker
$ source install/setup.bash
$ ros2 run cpp_pubsub listener

ros2 run実行

GUI(rqt, rviz2)の使用

本エントリで採用した方法は,Windowsホスト側にXサーバを立てる方式です.

諸事情により自分の環境がまだWindows10なので採用できませんでしたが,Windows11ではWSLでGUIアプリを動かす方法としてWSLgというものがあります.今後はそちらのほうがよいかもしれません.

docs.microsoft.com

以下で簡単に,GUI(rqt, rviz2)をXで使う方法をまとめます.

まず,Windows側でXサーバを立てます.よくあるのはVcXsrvとかXmingでしょうか. 自分の環境ではもともと使っていたMobaXtermに入っているXサーバを使っています.

mobaxterm.mobatek.net

設定は以下のような感じです.

MobaXTermのXサーバ設定

クライアント側(コンテナ側)では,DISPLAY変数を設定しておきます.

と言っても,run_dev-container.shで以下のように設定済みのはずです.

  HOST_IP=`ipconfig.exe | grep IPv4 | grep -v 172 | cut -d: -f2 | awk '{ print $1}' | sed 's/\r//g'`
  DISPLAY=${HOST_IP}:0.0

ので,コンテナ内で確認してみてください.

$ echo $DISPLAY 
192.168.0.6:0.0

IPアドレスの部分はWindows側のものを指定する必要があります.

もし設定がうまくできていなければ,コマンドプロンプトipconfigコマンドを実行するなどしてIPアドレスを調べ,コンテナ内のDISPLAY変数を設定してください

※ このコンテナを実行しっぱなしでwifiをつなぎ直したりすると,コンテナ内のDISPLAY変数はそのままだけど,IPアドレスが変わってしまって,Xサーバと接続できなくなることがあります その場合もDISPLAY変数を再設定してください

この状態でコンテナ内でrqt, rviz2を実行すると,ホストのWindows側にrqt, rviz2が起動します.

vscodeのターミナルからrqtを実行したときの様子

リリース用Dockerイメージの作成と実行

ROS2パッケージ開発を進めていくと,リリース用の必要最低限の環境が入ったdockerイメージを作成したくなってくると思います.

以下では簡単にその方法をまとめます.

リリース用イメージの作成

ROSのDockerHubのページの「Creating a Dockerfile to build ROS packages」を参考にリリース用イメージを作っていきます.

registry.hub.docker.com

が,これもスクリプト化しているので,WSL上でdocker/build_image.shrelという引数で実行すれば作ってくれます.

$ cd ws
$ bash docker/build_image.sh rel

$DOCKER_IMG:rel_<ワークスペース名>という名前でイメージができたらOKです(本エントリの例だと,mu777/ros2:rel_wsという名前になります)

以下に少し少し細かい話を書いておきます.

Dockerのコンテキストですが,リリース用イメージは開発対象のパッケージを含むため,ワークスペース全体としています.一方,開発用イメージは,開発対象のパッケージには依存しないので,コンテキストはdockerフォルダだけにするようにしています.

vcs周りは正直使い方がよくわかっていないので,rosinstallとかには対応できていません.これだけだと依存関係がうまく構築できないかもです(だれか教えて欲しい..)

リリース用イメージの実行

リリース用イメージが作成できたら実行するだけです.

WSLのターミナルを2つ開いて片方でtalker,もう片方でlistenerを実行します

$ docker run --rm -it mu777/ros2:rel_ws ros2 run cpp_pubsub talker
$ docker run --rm -it mu777/ros2:rel_ws ros2 run cpp_pubsub listener

すると,期待通り実行され,ノード間で通信できていることがわかります!🎉🎉

リリース用イメージを使ってROSパッケージを実行する

おわりに

ちょっとROS2がまだよくわかってないですが,ROS1を使っていたけどしばらく離れていた自分には,roscoreも実行せずに勝手に各ノードがpub/subできてるのは感動的ですね~

いろいろ遊べるとよいなと思います💪💪