アプトポッド組み込みエンジニアの久保田です。
近年、ロボットやモビリティを動作させるためのプラットフォームとしてROSを採用することが多くなってきています。 通常、ROSは特定のUbuntuバージョンに対応したディストリビューションとしてリリースされていますので、ROSのディストリビューションを変更したい場合は、ホストOSも変更する必要があります。この制約を回避する方法として、Dockerというコンテナ技術を使う方法があります。Dockerを使えば、ホストOSを変更せず様々なROSディストリビューションを動かすことができるようになります。
また、Dockerの利用はホストOSのアップデートが難しいプラットフォームでROSを利用する際にも効果的です。例えば、当社で販売している車載向けエッジコンピュータEDGEPLANT T1はNVIDIA Jetson TX2を搭載していますが、JetsonシリーズはホストOSのアップデートが難しいため、使用可能なROSのディストリビューションを自由に選択することができません。Dockerコンテナを利用することにより様々なROSディストリビューションを動作させることができます。
連載ROS Tips第2回目となる今回は、ROS開発に便利なDockerコンテナの活用テクニックを紹介します。
なお、連載の予定として、以下のようなコンテンツを予定しております。(連載内容は、変更・追加の可能性があります。ご了承ください)
- そもそもROSとは?
- ROS開発におけるDocker活用テクニック(本記事)
- ROSのDockerコンテナからNVIDIA GPUを利用するには
- 大量伝送シナリオにおけるROS/DDSのチューニング
本連載の投稿済みの記事はこちらからご覧いただけます。
- Dockerとは
- ROS Dockerコンテナイメージ
- DockerコンテナでROSを実行する
- Docker活用テクニック① コンテナ間ネットワークの設定
- Docker活用テクニック② Docker Composeで複数コンテナを起動する
- Docker活用テクニック③ コンテナでGUIを利用する
- Docker活用テクニック④ コンテナ内からWebカメラの映像を取得する
- まとめ
- 当社プロダクトのご案内
Dockerとは
Dockerは、アプリケーションとその動作に必要なすべてのファイルを「コンテナ」というパッケージにまとめる技術です。このコンテナは「仮想化」を利用して、異なる環境でも一貫した動作を実現します。つまり、Dockerは「どこでも同じ動きをするアプリケーション」を作るためのツールであり、その背後には仮想化技術が活用されています。
ROSをDockerで利用する際のメリットとデメリットは次の通りです。
メリット:
- 環境の統一: Dockerを使用すると、ROSの環境をコンテナ内に構築できるため、開発者間での環境の差異を気にすることなく、一貫した環境で作業することができます。
- 移植性: Dockerコンテナは、異なるマシンやクラウド環境でも動作します。これにより、ROSのアプリケーションを簡単に移植やデプロイが可能となります。
- バージョン管理: 異なるROSのバージョンを同時に実行することが容易になります。これは、特定のバージョンに依存するプロジェクトを管理する際に便利です。
- 隔離: Dockerコンテナはホストシステムから隔離されているため、システムの設定や依存関係を変更してもホストシステムに影響を与えません。
- セキュリティ: コンテナの隔離性により、潜在的なセキュリティリスクからホストシステムを保護することができます。
デメリット:
- オーバーヘッド: Dockerを使用すると、一部のリソースオーバーヘッドが発生する可能性があります。特に、高いリアルタイム性を要求するアプリケーションには影響が出ることがあります。
- ハードウェアアクセス: Dockerコンテナはホストのハードウェアに直接アクセスするのが難しい場合があります。これは、特定のロボットハードウェアやセンサーとの連携に制約をもたらすことがあります。
- 学習コスト: Docker自体の学習や設定が必要です。ROSのみならず、Dockerの知識も必要となります。
- ストレージサイズ: コンテナイメージのサイズが大きくなることがあり、ストレージの使用量が増加する可能性があります。
ROS Dockerコンテナイメージ
ROSが動作するDockerコンテナイメージは、Open Source Robotics Foundation (OSRF) が公式イメージを提供しています。イメージはROSディストリビューションの配布方法に準じて、core・base・desktopのイメージとして配布されています。
- core: ROSメッセージをpub/subできる最低限のパッケージ
- base: core + ロボットを動かすための最低限のパッケージ
- desktop: base + GUIを含むパッケージ
詳細については以下のリンク先の情報からご確認いただけます
公式イメージは以下のサイトにて公開されています
イメージ提供サイト: Docker Containers for ROS
ros - Official Image | Docker Hub
最新の安定版(LTS)ディストリビューションのイメージ一覧
公式イメージとして、各ROSディストリビューションのイメージが公開されています。ここでは、代表的な最新安定版 (LTS) のイメージを紹介します。
ROSディストリビューション | イメージ名 | イメージに付けられたタグ |
---|---|---|
ROS Noetic | ros-core | ros:noetic-ros-core |
ros-base | ros:noetic | |
ros-desktop | osrf/ros:noetic-desktop | |
ROS 2 Humble | ros-core | ros:humble-ros-core |
ros-base | ros:humble | |
ros-desktop | osrf/ros:humble-desktop |
(desktopは amd64のみ, arm64の場合、ros-baseイメージにて apt install ros-<distribution name>-desktop を実行することにより同等のイメージとなります )
DockerコンテナでROSを実行する
提供されているDockerコンテナイメージをダウンロードすることにより、簡単にROSを試すことができます。ここでは、簡単なStringのpublish/subscribeを試してみます。
docker/Tutorials/Docker - ROS Wiki
ROS
Dockerコンテナイメージをダウンロード
pullコマンドにて、イメージをダウンロードします。
$ docker pull ros:noetic
ROSマスターを起動
runコマンドにて、ROSマスターとなるコンテナ (コンテナ名: rosmaster) を起動します
$ docker run -it --rm --name=rosmaster \ --net=host \ ros:noetic \ roscore ... logging to /root/.ros/log/7f4ee758-512e-11ee-862e-00155d66fab1/roslaunch-localhost-1.log Checking log directory for disk usage. This may take a while. Press Ctrl-C to interrupt Done checking log file disk usage. Usage is <1GB. started roslaunch server http://localhost:40991/ ros_comm version 1.16.0 SUMMARY ======== PARAMETERS * /rosdistro: noetic * /rosversion: 1.16.0 NODES auto-starting new master process[master]: started with pid [32] ROS_MASTER_URI=http://localhost:11311/ setting /run_id to 7f4ee758-512e-11ee-862e-00155d66fab1 process[rosout-1]: started with pid [42] started core service [/rosout]
ROSメッセージをpublish
runコマンドにて、ROSメッセージをpublishするコンテナ (コンテナ名: rospub) を起動します
$ docker run -it --rm --name rospub \ --net=host \ ros:noetic \ rostopic pub -r 1 /my_topic std_msgs/String '{data: "hello"}'
ROSメッセージをsubscribe
runコマンドにて、ROSメッセージをsubscribeするコンテナ (コンテナ名: rossub) を起動します
$ docker run -it --rm --name rossub \ --net=host \ ros:noetic \ rostopic echo /my_topic data: "hello" --- data: "hello" --- data: "hello" ---
ROS 2
Dockerコンテナイメージをダウンロード
pullコマンドにて、イメージをダウンロードします。
$ docker pull ros:humble
ROSメッセージをpublish
runコマンドにて、ROSメッセージをpublishするコンテナ (コンテナ名: rospub) を起動します
$ docker run -it --rm --name rospub \ --net=host \ --ipc=host \ ros:humble \ ros2 topic pub /my_topic std_msgs/msg/String '{data: "hello"}' publisher: beginning loop publishing #1: std_msgs.msg.String(data='hello') publishing #2: std_msgs.msg.String(data='hello') publishing #3: std_msgs.msg.String(data='hello')
ROSメッセージをsubscribe
runコマンドにて、ROSメッセージをsubscribeするコンテナ (コンテナ名: rossub) を起動します
$ docker run -it --rm --name rossub \ --net=host \ --ipc=host \ ros:humble \ ros2 topic echo /my_topic data: hello --- data: hello --- data: hello ---
Docker活用テクニック① コンテナ間ネットワークの設定
Dockerコンテナはデフォルトでブリッジネットワークを使用します。これはホストOSとは独立したプライベートなネットワーク空間です。コンテナ間では、コンテナの動作している筐体に合わせてネットワーク設定します。
ホスト内通信 (同一筐体内通信)
ROS
ROSマスター用のコンテナを立てて、アプリ側からはブリッジネットワーク内通信によってROSマスターに接続します。各コンテナはコンテナ名で名前解決できますのでROSマスターの指定にはコンテナ名を使用できます。環境変数にて、ROSホスト名(ROS_HOSTNAME)とROSマスターURI(ROS_MASTER_URI)を指定します。
ブリッジネットワーク作成 (ネットワーク名: rosnet)
$ docker network create rosnet
ROSマスターコンテナ (コンテナ名: rosmaster)
コンテナ名がネットワークホスト名となります。ブリッジネットワーク内のROSマスターURIは http://rosmaster:11311 です。
$ docker run -it --rm \ --net=rosnet \ --name rosmaster \ ros:noetic \ roscore
ROSアプリケーションコンテナ (コンテナ名: rosapp)
コンテナ名がネットワークホスト名となります。ROSホスト名(ROS_HOSTNAME)とコンテナ名を一致させてください。
$ docker run -it --rm \ --net=rosnet \ --env ROS_HOSTNAME=rosapp \ --env ROS_MASTER_URI=http://rosmaster:11311 \ --name rosapp \ ros:noetic \ rostopic pub -r 1 /my_topic std_msgs/String '{data: "hello"}'
ROS 2
ROS 2通信には、マスターは不要となっています。ホスト内通信ではプロセス間通信を利用するため、IPCの設定 (--ipc=host) をします。
1.1. Leveraging Fast DDS SHM in Docker deployments — Fast DDS 2.13.0 documentation
$ docker run -it --rm --name rospub \ --net=host \ --ipc=host \ ros:humble \ ros2 topic pub /my_topic std_msgs/msg/String '{data: "hello"}'
ホスト間通信 (別筐体間通信)
ROS
ホストのネットワークインターフェースを共有するように設定 ( --net=host ) します。
ROSマスターコンテナ (コンテナ名: rosmaster)
ROSマスターが動作している筐体のIPアドレスまたはホスト名を使用します。ここでは、ROSマスターが動作している筐体のIPアドレスを 192.168.0.1 とします。ROSマスターURIは http://192.168.0.1:11311 です。
$ docker run -it --rm \ --net=host \ --name rosmaster \ ros:noetic \ roscore
ROSアプリケーションコンテナ (コンテナ名: rosapp)
ROSホスト名(ROS_HOSTNAME)にROSアプリケーションが動作している筐体のIPアドレスを設定します。ここでは ROSアプリケーションが動作している筐体のIPアドレスを 192.168.0.2 とします。
$ docker run -it --rm \ --net=host \ --env ROS_HOSTNAME=192.168.0.2 \ --env ROS_MASTER_URI=http://192.168.0.1:11311 \ --name rosapp \ ros:noetic \ rostopic pub -r 1 /my_topic std_msgs/String '{data: "hello"}'
ROS 2
UDP通信の設定をします。コンテナ外との通信となるため、ホストのネットワークインターフェースを共有するように設定 ( --net=host ) します。
ROS 2は通信ミドルウェアに DDS(Data Distribution Service)を採用しており、UDP通信をするためにはDDSヘの設定が必要です。ここでは、DDSが Fast DDS の場合の設定を紹介します。Fast DDSの設定は、設定ファイルfastrtps-profile.xmlを作成し、環境変数 (FASTRTPS_DEFAULT_PROFILES_FILE) で指定します。
6.2. UDP Transport — Fast DDS 2.13.0 documentation
設定ファイル (fastrtps-profile.xml)
<?xml version="1.0" encoding="UTF-8" ?> <profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles" > <transport_descriptors> <transport_descriptor> <transport_id>CustomUdpTransport</transport_id> <type>UDPv4</type> </transport_descriptor> </transport_descriptors> <participant profile_name="participant_profile" is_default_profile="true"> <rtps> <userTransports> <transport_id>CustomUdpTransport</transport_id> </userTransports> <useBuiltinTransports>false</useBuiltinTransports> </rtps> </participant> </profiles>
設定ファイル反映 & コンテナ起動
$ docker run -it --rm --name rospub \ --net=host \ -e FASTRTPS_DEFAULT_PROFILES_FILE=/tmp/fastrtps-profile.xml \ -v ./fastrtps-profile.xml:/tmp/fastrtps-profile.xml \ ros:humble \ ros2 topic pub /my_topic std_msgs/msg/String '{data: "hello"}'
Docker活用テクニック② Docker Composeで複数コンテナを起動する
Docker Composeとは
Docker Composeは、複数のDockerコンテナを定義し、連携させて実行するためのツールです。設定ファイル (docker-compose.yml) に、使用するコンテナの設定やネットワーク、ボリュームの情報などを記述することで、一つのコマンドで複数のコンテナを同時に起動・停止することができます。特に、複数のサービスが連携するアプリケーションの開発やテストにおいて、環境の構築や管理を簡単にするために利用されます。
ここでは、Dockerコンテナ実行で紹介したコマンドをDocker Compose で実行してみます。
docker/Tutorials/Compose - ROS Wiki
ROS
設定ファイル (docker-compose.yml)
version: '3' networks: rosnet: driver: bridge services: ros-master: container_name: rosmaster image: ros:noetic command: stdbuf -o L roscore networks: - rosnet restart: always ros-pub: container_name: rospub image: ros:noetic depends_on: - ros-master networks: - rosnet restart: always environment: - ROS_HOSTNAME=rospub - ROS_MASTER_URI=http://rosmaster:11311 command: rostopic pub -r 1 /my_topic std_msgs/String "hello" ros-sub: container_name: rossub image: ros:noetic depends_on: - ros-master networks: - rosnet restart: always environment: - ROS_HOSTNAME=rossub - ROS_MASTER_URI=http://rosmaster:11311 command: rostopic echo /my_topic
Dockerコンテナ起動
$ docker-compose -f ./docker-compose.yml up [+] Building 0.0s (0/0) [+] Running 4/4 ✔ Network tmp_rosnet Created 0.1s ✔ Container rosmaster Created 0.1s ✔ Container rossub Created 0.1s ✔ Container rospub Created 0.1s Attaching to rosmaster, rospub, rossub rossub | data: "hello" rossub | --- rossub | data: "hello" rossub | --- rossub | data: "hello" rossub | ---
(※) 停止はCtrl-C
Dockerコンテナ削除
$ docker-compose -f ./docker-compose.yml down [+] Running 4/4 ✔ Container rossub Removed 0.0s ✔ Container rospub Removed 0.0s ✔ Container rosmaster Removed 0.0s ✔ Network tmp_rosnet Removed
ROS 2
設定ファイル (docker-compose.yml)
version: '3' services: ros-pub: container_name: rospub image: ros:humble network_mode: "host" ipc: host command: > ros2 topic pub /my_topic std_msgs/msg/String '{data: "hello"}' ros-sub: container_name: rossub image: ros:humble network_mode: "host" ipc: host depends_on: - ros-pub command: ros2 topic echo /my_topic
Dockerコンテナ起動
$ docker compose -f docker-compose.yml up [+] Building 0.0s (0/0) [+] Running 2/2 ✔ Container rospub Created 0.1s ✔ Container rossub Created 0.0s Attaching to rospub, rossub rospub | publisher: beginning loop rospub | publishing #1: std_msgs.msg.String(data='hello') rospub | rossub | data: hello rossub | --- rospub | publishing #2: std_msgs.msg.String(data='hello') rospub | rossub | data: hello rossub | ---
(※) 停止はCtrl-C
Dockerコンテナ削除
$ docker compose -f docker-compose.yml down [+] Running 2/0 ✔ Container rossub Removed 0.0s ✔ Container rospub Removed
Docker活用テクニック③ コンテナでGUIを利用する
Dockerコンテナ内でGUIアプリケーションを利用するには、ホストでX Serverが必要となります。X ServerはLinuxデスクトップアプリケーションで利用されているWindowシステムです。
事前準備
ホストで X Server が動作していることを確認してください。多くの Linux ディストリビューションでは、デフォルトで X Server がインストールされています。 X Server のアクセス権をコンテナからの接続を許可するように設定します。
$ xhost +local:
GUIアプリケーション起動
Dockerコンテナを起動します。コンテナでGUIが利用できるように起動オプション (-e DISPLAY -e QT_X11_NO_MITSHM=1 -v /tmp/.X11-unix:/tmp/.X11-unix) を付加してください。
ここでは、ROS 2のための3D可視化ツールである rviz2 を起動します。
$ docker run -it --rm --name guitest \ -e DISPLAY \ -e QT_X11_NO_MITSHM=1 \ -v /tmp/.X11-unix:/tmp/.X11-unix \ osrf/ros:humble-desktop \ rviz2
起動後、ホスト上にrviz2が表示されます。
Docker活用テクニック④ コンテナ内からWebカメラの映像を取得する
ROSでWebカメラ画像を取得するにはV4L2(Video4Linux version 2)を利用します。V4L2は、Linuxカーネル内のAPIであり、ビデオキャプチャと出力デバイスのサポートを提供します。
事前準備
WebカメラがV4L2でサポートされているかを確認してください。UVC(USB Video Class)規格のWebカメラは対応しています。
Webカメラ画像取得ノード
ROS
ノード提供サイト: ROS Camera driver for GStreamer-based video streams - branch: master
GitHub - ros-drivers/gscam: ROS Camera driver for GStreamer-based video streams.
ROS 2
ノード提供サイト: ROS Camera driver for GStreamer-based video streams - branch: ros2
GitHub - ros-drivers/gscam at ros2
Webカメラ画像取得
ここでは、ROS 2 で取得してみます。
Dockerコンテナを起動します。コンテナでWebカメラデバイスが利用できるように起動オプション (--privileged --device=/dev/video0:/dev/video0) を付加してください。
$ docker run -it --rm --name cameratest \ --net=host \ --privileged \ --device=/dev/video0:/dev/video0 \ osrf/ros:humble-desktop \ /bin/bash
起動したDockerコンテナにログイン後、Webカメラ画像取得ノードをビルドします。ビルドが完了後、Webカメラ画像取得ノードを起動します。起動設定ファイルは次の通りです。
Webカメラ画像取得ノード設定ファイル (v4l.launch.xml)
<launch> <!-- This launchfile should bring up a node that broadcasts a ros image transport on /webcam/image_raw --> <arg name="DEVICE" default="/dev/video0"/> <!-- The GStreamer framerate needs to be an integral fraction --> <arg name="FPS" default="10/1"/> <arg name="PUBLISH_FRAME" default="false"/> <node namespace="v4l" name="gscam_driver_v4l" pkg="gscam" exec="gscam_node" output="screen"> <param name="camera_name" value="default"/> <param name="camera_info_url" value="package://gscam/examples/uncalibrated_parameters.ini"/> <param name="gscam_config" value="v4l2src device=$(var DEVICE) ! video/x-raw,framerate=$(var FPS) ! videoconvert"/> <param name="frame_id" value="/v4l_frame"/> <param name="sync_sink" value="true"/> </node> <node if="$(var PUBLISH_FRAME)" name="v4l_transform" pkg="tf2_ros" exec="static_transform_publisher" args="1 2 3 0 -3.141 0 /world /v4l_frame"/> </launch>
Webカメラ画像取得ノード起動後、トピック名が /v4l/camera/image_raw のROSメッセージが送信されます。
# ros2 launch ./v4l.launch.xml [INFO] [launch]: All log files can be found below /root/.ros/log/2023-09-13-07-37-40-455157-feb5b5ceb79d-2335 [INFO] [launch]: Default logging verbosity is set to INFO [INFO] [gscam_node-1]: process started with pid [2336] [gscam_node-1] [INFO] [1694590661.146771024] [v4l.gscam_driver_v4l]: Using gstreamer config from rosparam: "v4l2src device=/dev/video0 ! video/x-raw,framerate=10/1 ! videoconvert" [gscam_node-1] [INFO] [1694590661.147929610] [v4l.gscam_driver_v4l]: camera calibration URL: package://gscam/examples/uncalibrated_parameters.ini [gscam_node-1] [INFO] [1694590661.148746118] [v4l.gscam_driver_v4l]: Loaded camera calibration from package://gscam/examples/uncalibrated_parameters.ini [gscam_node-1] [INFO] [1694590663.558110687] [v4l.gscam_driver_v4l]: Time offset: 1694587415.451620 [gscam_node-1] [INFO] [1694590663.572234037] [v4l.gscam_driver_v4l]: Publishing stream... [gscam_node-1] [INFO] [1694590663.572646995] [v4l.gscam_driver_v4l]: Started stream.
Webカメラ画像のROSメッセージを確認する場合、「Docker活用テクニック④ コンテナでGUIを利用する」で起動した rviz2 で表示します。
まとめ
今回は、ROS開発に便利なDockerコンテナの活用テクニックを紹介しました。紹介した活用テクニックにより、ホストで使用する場合と同様の扱いができますので、ご活用ください。
冒頭にもご紹介した通り、当社は車載向けエッジコンピューターEDGEPLANT T1を販売しています。
EDGEPLANT T1はNVIDIA® Jetson™ TX2を搭載したエッジコンピュータで、映像のエンコードやデコードなどGPUを使用する処理や、デバイスエッジでのAIモデルの実行用途にお使いいただけます。また、SIMスロット、GPSモジュールなど、IoT端末として必要な様々な機能を備えています。車載機器に求められるEMC規格(Eマーク)、信頼性規格(JASO D014)などにも準拠しており、ROSを使用したロボット開発、自動運転システムの開発にもおすすめです。
EDGEPLANT T1は、当社の販売パートナー様または、Amazon.co.jp からご購入いただけます。詳しくは、製品ページをご覧ください。
AI自動運転での活用例もございます。ご興味がございましたらこちらの記事もご覧ください。
次回以降は
- DockerでROSを利用する際に、GPUを使用する方法についての紹介
- ROS 2を利用する上での大容量データ転送シナリオにおけるチューニングについての紹介
を予定しています。
当社プロダクトのご案内
先日行われた ROSConJP 2023 では、ROSの開発ワークフローを効率化するDXソリューションとして、新たなソリューションを発表いたしました。詳細については、ROSConJP 2023の参加報告記事をご覧ください。
また、ROS開発において、当社プロダクトをどの様にご活用いただけるかは、連載1日目にて当社のVPoPがより詳しく紹介しておりますので、こちらもご覧ください。
さらに、当社では、モビリティ・ロボットのフリート管理や遠隔監視、遠隔制御を実現するための管制制御システム向けのソリューションフレームワーク「intdash CONTROL CENTER」を提供しています。
スマートシティにおける自動運転車、工場や物流倉庫における搬送ロボット、建設現場における建設機械など、モビリティ群の統合遠隔監視・管理、遠隔制御システムな どでお困りのことがあれば、ぜひお声掛けください。
お問合せフォームは こちら です。