はじめに
TERAKATA Roboticsの連載第2回です。
前回はハードウェア構成についてお話ししましたが、今回はその上で動く「ソフトウェア開発環境」に焦点を当てます。
私たちのチームは6人構成。
- AさんのPC: Ubuntu 20.04 (ROS 1の名残で…)
- BさんのPC: Ubuntu 24.04 (ネイティブ)
- CさんのPC: Ubuntu 22.04 (ネイティブ)
こんなバラバラな環境で、「AさんのPCではビルド通るけど、BさんのPCでは動かない」なんてことになったら目も当てられません。
特に、中之島やつくばといった現地の実験時間は極めて貴重です。現地で「ライブラリのバージョンが合わない!」と騒いでいる時間は1秒もありません。
そこで私たちは、Dockerを徹底活用して「地球上のどこでも、誰のPCでも、同じコマンドで同じ環境が立ち上がる」状態を作りました。
1. 開発用Dockerイメージの設計
私たちの Dockerfile は、開発の利便性と実機パフォーマンスの両立を目指して設計されています。
Base Image
基本となるイメージには、公式のROS 2イメージを使用しています。
FROM ros:humble-ros-base
ここに、チーム全員が必要とする共通ツール(tmux, vim, htop, gitなど)や、プロジェクトで使用するROS 2パッケージ(Cartographer, Nav2, pcl_rosなど)を apt-get でインストールして固めています。
2. 構成のモジュール化とGPU設定
私たちのロボットは多数のセンサー(LiDAR, Camera, IMU, GPS...)を扱います。これらを1つの巨大な docker-compose.yml に書くと管理が大変です。
そこで、機能ごとに docker-compose ファイルを分割し、共通設定を docker-compose.common.yml に集約する構成に変更しました。
分割された構成
メインの docker-compose.yml は、各機能の定義ファイルを読み込むだけです。
services:
robot_core:
extends:
file: ./docker-compose.robot_core.yml
service: robot_core
motor_controller:
extends:
file: ./docker-compose.motor_controller.yml
service: motor_controller
共通設定とGPU (docker-compose.common.yml)
GPUパススルー設定やボリュームマウントなど、全コンテナで共通する設定は common サービスとして定義しています。
開発用PC(GeForce搭載機)やJetsonを使う場合に備え、nvidia-container-toolkit を導入してコンテナ内からGPUが見えるように設定済みです。
services:
common:
network_mode: host
ipc: host
volumes:
- ../../ros2_ws:/home/tr-user/ros2_ws
environment:
- NVIDIA_VISIBLE_DEVICES=all
- NVIDIA_DRIVER_CAPABILITIES=all
これにより、新しいセンサーを追加する際も「commonを継承」するだけで済みます。
3. ネットワーク設定のキモ:Network Host Mode
ROS 2の通信ミドルウェア(DDS)は、デフォルトでマルチキャスト(UDP)を使用します。
Dockerのデフォルトであるブリッジネットワーク(Bridge Network)を使うと、コンテナ内外の通信のためにポート転送設定が大量に必要になり、非常に面倒です。特に複数台のPC(ロボットと監視用PC)で通信する場合、DDSの設定が地獄と化します。
そこで私たちは、開発フェーズでは割り切って Host Network Mode を使用しています。
network_mode: host
ipc: host # 共有メモリ通信(Zero Copy)のためにも重要
これだけで、コンテナはホストPCのネットワークインターフェースを直接共有します。
- ロボット実機(Docker) <--> 監視用PC(Docker)
- ロボット実機(Docker) <--> 監視用PC(ネイティブUbuntu)
どの組み合わせでも、まるでローカルホストにいるかのように ros2 topic list が互いに見えます。
「通信が繋がらない」というトラブルの9割はこれで解決しました。
4. パーミッション問題への対処 (Static UID/GID)
Dockerコンテナ内で生成されたファイル(rosbagなど)が root 権限になってしまい、ホスト側で編集できない問題は「あるある」です。
以前は entrypoint.sh で動的にユーザーを作成していましたが、現在はDockerfile内でホスト側のユーザーIDと一致するユーザーを作成しておくというシンプルな運用に倒しています。
Dockerfile でのユーザー作成
Ubuntuのデフォルトユーザーは大抵 UID=1000 なので、Dockerイメージ内でも UID=1000 のユーザー(tr-user)を作成し、そのユーザーでプロセスを実行するようにしています。
# 一般ユーザーの作成
ARG USERNAME=tr-user
ARG USER_UID=1000
ARG USER_GID=$USER_UID
RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m -s /bin/bash $USERNAME \
&& usermod -aG dialout,video,audio,input $USERNAME
# ... (中略) ...
USER $USERNAME
これにより、複雑なスクリプトを排除しつつ、開発者の9割(UID=1000の人々)にとって快適なパーミッション環境を実現しています。
5. まとめ
- DockerでOSごと環境を配布し、依存関係地獄から解放される。
- Host NetworkでROS 2通信のトラブルを回避する。
- UID Mappingでファイル権限のイライラを解消する。
この「再現性最強」の環境があったからこそ、私たちは現地に行ってすぐに実験走行を開始できました。
「環境構築」は地味な作業ですが、ここをサボらないことが、開発スピードを最大化する秘訣だと確信しています。
次回は、いよいよ技術の核心へ。
つくばで500mを走り切った、CartographerとNav2による自律走行システムのチューニングについて深掘りします。
Nav2のパラメータ設定、知りたくないですか?全部見せます!
リンク
最新の活動状況や、今回紹介した技術のソースコードは以下で公開しています。
興味を持っていただけた方は、ぜひフォロー&スターをお願いします!
- Website: https://sites.google.com/view/terakata-robotics
- X (Twitter): https://x.com/terakata_robot
- Instagram: https://www.instagram.com/terakata_robotics/
- GitHub: https://github.com/TERAKATA-Robotics/terabo-navigation