🤖

令和最新版 ROS Noetic on Docker のすすめ

2024/11/07に公開

導入

こんにちは、CA技研でエンジニアをしているHiraiKyoです。

CA技研ではロボット開発フレームワーク「Robot Operating System (ROS)」を使用して製品開発しております。
弊社ではROS2への移行は検討段階で、現在も主にROS Noeticを使用しています。
この理由は、ROSのように様々な機器とのインテグレーションを前提としていると、どうしても機器側のROS2対応が行われるまでは移行しづらいためです。

しかし、ROS NoeticのEOLも目前に迫っています。
ROSは特定のUbuntuバージョンに対応してリリースされており、最新版のROS NoeticはUbuntu 20に対応しています。
そのUbuntu 20が2025年5月にサポート終了となります。(あと半年)

そこで弊社では、ROSをすべてDockerコンテナ内で実行する事で、メンテナビリティ向上に取り組みました。

もうちょっと掘り下げた経緯

Docker導入の経緯は、元々は以下の2点でした。

  • ROS Kinetic用SDKを使用した機器が既に存在していた。
    • その機器を使用しないシステムではROS Noetic, その機器を使用するシステムではROS Kineticを使用するという状況
    • ROS KineticはUbuntu 16なため、Node.jsのバージョンを上げられず苦労した
    • Ubuntu 16がEOLを迎えているため、カーネルレベルのエラー発生が増えていた
  • Intel第12世代以降のCPUはLinux Kernel 6を要求するため、Ubuntu 24へのアップデートが必要だった
    • ROS1最新版のROS NoeticですらUbuntu 20であり、最新ハードへの対応に迫られていた。

懸念材料には以下がありました。

  • Docker導入による計算速度低下
    • 点群処理や画像処理を行うため、高い演算能力が求められる
    • 仮想化により計算速度が低下するのであれば、ROS NoeticをUbuntu 24環境下でビルドする方向もあった
  • 仮想化で既存システムが本当に動作するのか
    • 既存システムのシリアル通信やネットワーク周りがDocker対応できるか

結果として、これらは全てDocker導入により解決できました。

レポジトリ

https://github.com/CA-Giken/docker-noetic-capc

ROS on Docker 環境構築

前提

  • Ubuntu 24

注意

当記事では、スタンドアローン環境での利用を想定しており、セキュリティ面は考慮していません。

ROS Noetic on Docker 基本方針

  • Docker
    • ホストネットワークを利用する
      • スタンドアローン環境での利用なので
    • GUIはXserverを利用する
      • VNCはrvizの画面がリフレッシュされない現象に悩まされたので利用しない
  • ROSパッケージ開発
    • serviceは利用せず、topicの送受のみを行う。
      • RPCが利用できないため
    • 独自メッセージ型を使用せず、基本型のみを利用する
      • RPCが利用できないため

ディレクトリ構成

.
├── docker-noetic-capc
│   ├── kinetic
│   │   ├── Dockerfile
│   │   └── entrypoint.sh
│   ├── noetic
│   │   ├── Dockerfile
│   │   └── entrypoint.sh
│   └── docker-compose.yml
├── container1(今回はkineticと命名)
│   └── catkin_ws
│       └── src
└── container2(今回はnoeticと命名)
    └── catkin_ws
        └── src

Dockerをインストール

# 古いパッケージを削除
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done

# GPGキーを追加
sudo apt-get update
sudo apt-get 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

# Dockerをレポジトリ一覧に追加
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") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

# インストール
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# インストール確認テスト
sudo docker run hello-world

ROSをインストールするDockerfileを作成

今回はKinetic, Noeticを共存させるため、それぞれのDockerfileを作成します。
機器ごとにDockerfileを作成してもよいですし、その際にNoeticのコンテナが2つになってもいいです。

なお、異なるROSディストリビューションの共存については、ROS Cross-Distribution Communication対応表をご覧ください。
ROS Cross-Distribution Communication - General

noetic/Dockerfile
FROM ubuntu:focal-20240427

WORKDIR /root/

ENV DEBIAN_FRONTEND=noninteractive

# Upgrade Packages
RUN apt-get update -q \
    && apt-get upgrade -y \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Utility packages
RUN apt-get update -q \
    && apt-get install -y software-properties-common \
    && add-apt-repository universe && apt-get update -q \
    && apt-get install -y git build-essential wget curl vim sudo lsb-release locales \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install X11 requirements
RUN apt-get update -q && apt-get install -y \
    iputils-ping telnet x11-apps \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install Python
RUN apt-get update -q && apt-get install -y \
    python3.8 python3-pip python3-dev python3-tk python-is-python3 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install ROS
ENV ROS_DISTRO noetic
RUN sh -c 'echo "deb http://packages.ros.org/ros/ubuntu focal main" > /etc/apt/sources.list.d/ros-latest.list'
RUN curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
RUN apt-get update -q \
    && apt-get install -y ros-noetic-desktop-full \
    && apt-get install -y python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential python3-catkin-tools \
    && rosdep init \
    && rosdep update \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Setting for Japanese
RUN apt-get update -q && apt-get install -y \
    tzdata locales fonts-noto-cjk fcitx-mozc \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
RUN ln -s -f /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    dpkg-reconfigure tzdata && \
    locale-gen ja_JP.UTF-8

# Set user
ARG UID=1000
RUN useradd -m -u ${UID} ros

RUN sh -c 'echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc'
RUN sh -c 'echo "export ROS_HOSTNAME=localhost" >> ~/.bashrc'
RUN sh -c 'echo "export ROS_MASTER_URI=http://localhost:11311" >> ~/.bashrc'
RUN sh -c 'echo "export PYTHONPATH=/usr/local/lib/python3.8/dist-packages:$PYTHONPATH" >> ~/.bashrc'

# Command copy
COPY ./entrypoint.sh /app/
RUN chmod +x /app/entrypoint.sh
CMD ["/bin/sh", "-c", "./entrypoint.sh"]
kinetic/Dockerfile
FROM ubuntu:16.04

WORKDIR /root/

ENV DEBIAN_FRONTEND=noninteractive

# Upgrade Packages
RUN apt-get update -q \
    && apt-get upgrade -y \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Utility packages
RUN apt-get update -q \
    && apt-get install -y software-properties-common \
    && add-apt-repository universe && apt-get update -q \
    && apt-get install -y git build-essential wget curl vim sudo lsb-release locales \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install X11 requirements
RUN apt-get update -q && apt-get install -y \
    iputils-ping telnet x11-apps \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install Python
RUN add-apt-repository universe && \
    apt-get update -q && apt-get install -y \
    python3-pip python3-dev python3-tk \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Install ROS
ENV ROS_DISTRO kinetic
RUN sh -c 'echo "deb http://packages.ros.org/ros/ubuntu xenial main" > /etc/apt/sources.list.d/ros-latest.list'
RUN curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
RUN apt-get update -q \
    && apt-get install -y ros-kinetic-desktop-full \
    && apt-get install -y python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential python-catkin-tools\
    && rosdep init \
    && rosdep update \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Setting for Japanese
RUN apt-get update -q && apt-get install -y \
    tzdata locales fonts-noto-cjk fcitx-mozc \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*
RUN ln -s -f /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    dpkg-reconfigure tzdata && \
    locale-gen ja_JP.UTF-8

# Set user
ARG UID=1000
RUN useradd -m -u ${UID} ros

RUN sh -c 'echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc'
RUN sh -c 'echo "export ROS_HOSTNAME=localhost" >> ~/.bashrc'
RUN sh -c 'echo "export ROS_MASTER_URI=http://localhost:11311" >> ~/.bashrc'

# Command copy
COPY ./entrypoint.sh /app/
CMD ["/bin/sh", "-c", "./entrypoint.sh"]

Docker compose を作成

Dockerfileを参照して、kinetic用とnoetic用の2つのコンテナを立ち上げます。

kineticコンテナはレガシー対応目的なので、roscoreはnoeticで立ち上げてkineticコンテナが接続しにいく形です。

docker-compose.yml
version: "3.9"

services:
  roskinetic:
    container_name: roskinetic
    hostname: roskinetic
    privileged: true
    build:
      context: ./kinetic
      dockerfile: Dockerfile
      shm_size: 2gb
    shm_size: 2gb
    environment:
      - DISPLAY=display:0
      - QT_X11_NO_MITSHM=1
      - ROS_MASTER_URI=http://localhost:11311
    volumes:
      - type: bind
        source: ~/kinetic/catkin_ws/src
        target: /root/catkin_ws/src
    stdin_open: true
    tty: true
    extra_hosts:
      - "display:host-gateway"
    network_mode: host
  rosnoetic:
    container_name: rosnoetic
    hostname: rosnoetic
    build:
      context: ./noetic
      dockerfile: Dockerfile
    environment:
      - DISPLAY=display:0
    working_dir: /root
    volumes:
      - type: bind
        source: ~/noetic/catkin_ws/src
        target: /root/catkin_ws/src
    stdin_open: true
    tty: true
    ports:
      - 11311:11311
    extra_hosts:
      - "display:host-gateway"
    network_mode: host

ホスト側Xserverの設定

/etc/gdm/custom.confの変更

デフォルトでは外部からの接続(TCP)を受け付けないので変更が必要。

DisallowTCP=false

Linux Mintを利用している場合はLight Display Managerなので、/etc/lightdm/lightdm.confを変更します。

xserver-allow-tcp=true

PCを再起動し、接続確認

telnet localhost 6000

接続されれば外部接続が可能となっています(特に何も表示されない)。

表示テスト

export DISPLAY=localhost:0
xeyes

外部からのアクセス制御を許可

xhost +

使用方法

# Dockerコンテナにマウントするディレクトリを作成
mkdir -p ~/noetic/catkin_ws/src
mkdir -p ~/kinetic/catkin_ws/src

# Xserverの接続許可(PC起動ごとに必要)
xhost +

# コンテナ & roscore立ち上げ
docker compose up
docker compose exec rosnoetic bash -c "cd catkin_ws && catkin build && roscore"

これでKinetic用、Noetic用コンテナがそれぞれ起動します。

ホスト側の~/dist/catkin_ws/srcにROSパッケージを配置した後、コンテナに入ってビルドできます。

# Kineticコンテナで実行する例
docker compose exec roskinetic bash -c "cd catkin_ws && catkin build"

以上を実行すると、コンテナ内にdevel, buildディレクトリが作成されるので、あとは任意のlaunchファイルを実行するだけです。

# Noeticコンテナで実行する例
docker compose exec roskinetic bash -c "roslaunch xxx yyy.launch"

あとがき

現在はセンサ1機、アクチュエータ1機のシステムで使用していますが、今後センサやアクチュエータが増えた際にDockerコンテナを増やしていく形での対応を予定しています。

追記: WSL2対応

WindowsのWSL2環境でGUI表示するにはXserverをUbuntu上ではなくWindows上でホストし、

  1. 上記のホスト側Xserverを行わない
  2. VcXsrvを導入する。https://sourceforge.net/projects/vcxsrv/
    すべて「次へ」連打でOKです。
  3. Windows上でVcXsrv -> XLaunchを実行する
  4. WSLのUbuntuにある~/.bashrcに以下を追加する
export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0.0
export LIBGL_ALWAYS_INDIRECT=1

これでWindows環境からもRvizなどのGUIを見る事ができるようになります。

Discussion