ROS2 環境の構築 with LXC

# 1. はじめに

この記事は Ubuntu 22.04 までしかサポートしていない ROS2 humble 環境を lxc で構築する手順メモです。
昔 ROS2 dashing を構築した際のローカルメモ(下書き)よりもより良い記事があったため参考にさせていただきました。

https://qiita.com/usagr10asu1/items/a720f9e664e5948c4e31

構成は下記の通り。

| 項目 |  |
|---|---|
| 機種名 | HP Pavilion Aero 13-bg |
| CPU | AMD Ryzen 7 8840U |
| GPU | Radeon 780M |
| RAM | 16 GB |
| SWAP | 16 GB |
| Host OS | Kubuntu 24.04 |
| Host OS 側ユーザー名 | user |
| Guest OS | Ubuntu 22.04 |
| Guest OS 側ユーザー名 | ubuntu (デフォルト＆変更可) |
| ROS2 | humble |

# 2. 作業の流れ

3\. lxd / lxc の準備
　3.1\. lxd / lxc のインストール
　3.2\. lxc の設定
　3.3\. GUI / Sound の共有
　3.4\. ディレクトリの共有(任意)
　3.5\. lxc 内で docker を動かす設定(任意)
　3.6\. シリアル通信用のデバイスファイル(ttyACM0)を共有する(任意)
　3.7\. インスタンスの自動起動を off にする(任意)
　3.8\. 設定の確認
4\. コンテナ内環境構築
　4.1\. 基本パッケージ(日本語含む)のインストール
　4.2\. ssh の有効化
　4.3\. IP アドレスの固定
5\. ROS2 Humble インストール
6\. MoveIt2 インストール

# 3. lxd / lxc の準備

コンテナ名を適宜決めて、一時的なシェル変数に設定

```bash:Host 側
LXCNAME=ros2-humble
```

## 3.1. lxd / lxc のインストール

今は lxd は snap 側で管理されているため、snap によるインストール＆初期設定。
lxd init での設定は、基本的にすべて default でも問題なし(好みで変更)

```bash: Host 側
sudo snap install lxd
sudo lxd init
```

コンテナの作成(初回はダウンロードで少し時間がかかります)

```bash: Host 側
sudo lxc launch ubuntu:22.04 $LXCNAME
```

## 3.2. lxc の設定

Host 側の UID/GID と共通化
(現在のアカウント以外と共通化する場合は適宜変更)

```bash: Host 側
sudo lxc config set $LXCNAME raw.idmap "both $(id -u) $(id -g)"
```

## 3.3. GUI / Audio の共有

GUI, Audio の作業ディレクトリを Host 側と bind して共有

```bash: Host 側
sudo lxc config device add $LXCNAME audio disk source=/run/user/1000/pulse/native path=/tmp/.pulse-native
sudo lxc config device add $LXCNAME xorg disk source=/tmp/.X11-unix/X0 path=/tmp/.X11-unix/X0
```

環境変数の設定と video グループの GID を gpu に割り当てて共有

```bash: Host 側
sudo lxc config set $LXCNAME environment.DISPLAY :0
sudo lxc config device add $LXCNAME gpu gpu gid=`getent group video | cut -d: -f3`
```

## 3.4. ディレクトリの共有(任意)

その他、Host 側とディレクトリを共有したい場合は適宜下記コマンドで追加
(e.g. Host 側 /home/user/Downloads, Guest 側 /home/ubuntu/Downloads を共有する場合)

```bash: Host 側
sudo lxc config device add $LXCNAME share disk source=${HOME}/Downloads path=/home/ubuntu/Downloads
```

## 3.5. lxc 内で docker を動かす設定(任意)

lxc 内で docker を動かしたい場合は下記のコマンドでネストを有効化

```
sudo lxc config set $LXCNAME security.nesting true
```

## 3.6. シリアル通信用のデバイスファイル(ttyACM0)を共有する(任意)

```
sudo lxc config device add $LXCNAME ttyACM0 unix-char path=/dev/ttyACM0
```

## 3.7. インスタンスの自動起動を off にする(任意)

host 起動時に、毎回インスタンスを起動させたくない場合は以下のコマンドで抑制
(起動させたい場合は `sudo lxc start $LXCNAME` による手動)

```
sudo lxc config set $LXCNAME boot.autostart=false
```

## 3.8. 設定の確認

以下で設定を確認できます。
both 1000 1000, audio, xorg, gpu など追加されていることが確認できます。

```bash: Host 側
sudo lxc config show $LXCNAME
```

# 4. コンテナ内環境構築

コンテナ内で作業するため、bash を実行
(ログアウトするまでroot権限なので注意)

```bash: Host 側
sudo lxc exec $LXCNAME /bin/bash
```

## 4.1. 基本パッケージ(日本語含む)のインストール

まずは最新の環境にまで upgrade

```bash: Guest 側
apt update && apt full-upgrade -y
```

基本的なパッケージのインストール

```bash: Guest 側
apt install -y x11-apps pulseaudio dbus-x11 language-pack-ja fonts-noto-cjk-extra task-japanese-gnome-desktop language-pack-gnome-ja-base language-pack-gnome-ja gnome-user-docs-ja gnome-control-center fonts-noto-color-emoji manpages-ja fcitx5-mozc ibus-mozc
```

サウンドの設定

```bash: Guest 側
sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf
```

日本語設定/ロケール設定

```bash: Guest 側
update-locale LANG=ja_JP.UTF-8
timedatectl set-timezone Asia/Tokyo
localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
localectl set-x11-keymap jp jp106
source /etc/default/locale
dbus-uuidgen > /var/lib/dbus/machine-id
```

ubuntu ユーザーに切り替えて、ユーザー個別の設定

```bash: Guest 側
su - ubuntu
im-config -n fcitx5
source /etc/default/locale
```

下記コマンドで ~/.profile に追記

```bash: Guest 側
cat << 'EOF' >> ~/.profile

export PULSE_SERVER=unix:/tmp/.pulse-native

export LANG=ja_JP.UTF8
export GTK_IM_MODULE=fcitx5
export QT_IM_MODULE=fcitx5
export XMODIFIERS=@im=fcitx5
export DefaultIMModule=fcitx5
if [ \$SHLVL = 1 ] ; then
  (fcitx5 > /dev/null 2>&1 &)
  xset -r 49  > /dev/null 2>&1
fi
EOF
```

ここまでの作業を確実に反映させるためコンテナの再起動
(Host 側に戻ります)

```bash: Guest 側
sudo reboot
```

## 4.2. ssh の有効化

ホストからゲストに ssh でログインするための鍵を作成
(すべて未指定の改行のみでもOKですが、気になる方は適宜指定を)

```bash:Host 側
ssh-keygen -f ~/.ssh/${LXCNAME}
```

ここで作成された公開鍵(ros2-humble.pub)の中身を後で使うので、画面上に表示やメモ帳などにコピーしておく(下記は cat で画面上に出力しているだけです)

```bash: 出力例
cat ~/.ssh/${LXCNAME}.pub
ssh-ed25519 ABCDEFG...(略) user@host
```

Guest 側に移行

```bash:Host 側
sudo lxc exec $LXCNAME /bin/bash
```

Guest 側に ssh をインストール

```bash:Guest 側
apt install ssh
```

鍵による認証を有効にするため `/etc/ssh/sshd_config` ファイル内の `PubkeyAuthentication` のコメントアウトを解除

```bash: Guest 側
nano /etc/ssh/sshd_config
```
`#PubkeyAuthentication yes`
　　　　　　↓
`PubkeyAuthentication yes`


ユーザーを `ubuntu` に切り替えた後、先程作成した公開鍵を `~/.ssh/authorized_keys` に保存する。
(先述の `ssh-ed25519 ABCDEFG...(略) user@host` をそのまま `~/.ssh/authorized_keys` に書き込みでOK)

```bash:Guest 側
su - ubuntu
nano ~/.ssh/authorized_keys
```

```bash:Guest 側
cat ~/.ssh/authorized_keys
ssh-ed25519 ABCDEFG...(略) user@host
```

権限の設定

```bash:Geust 側
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```


## 4.3. IP アドレスの固定

事前に lxdbr0 のネットワークアドレスを把握しておく。

```bash: Host 側
ip a |grep lxdbr0 | grep inet
```

手元では出力結果が 10.169.78.1/24 でした。

```bash:出力結果
    inet 10.169.78.1/24 scope global lxdbr0
```

下記コマンドで IP アドレスの固定化を行う。
IP は先程確認したネットワークアドレス内で決める(今回は `10.169.78.100` とした)。
その後再起動。

```bash: Host 側
sudo lxc network attach lxdbr0 ${LXCNAME} eth0 eth0
sudo lxc config device set ${LXCNAME} eth0 ipv4.address 10.169.78.100
sudo lxc restart ${LXCNAME}
```

コンテナにアクセスしやすいように、固定化したIPアドレスを登録させておく

```bash: Host 側
echo "10.169.78.100 ${LXCNAME}" | sudo tee -a /etc/hosts > /dev/null
```

Host 側のユーザーで、ssh で Guest 側にアクセスする際の設定を行う。
~/.ssh/config をエディタで開き、下記を追記
(-X オプションをデフォルトで有効にするため `ForwardX11 yes` を指定)

```: Host 側 ~/.ssh/config の設定
Host ros2-humble
    HostName ros2-humble
    ForwardX11 yes
    User ubuntu
    IdentityFile ~/.ssh/ros2-humble
```

ssh 接続テスト

```bash: Host 側
ssh ros2-humble
```

正常に接続を確認できたなら、以降の作業はすべてこの ssh 越しで作業を行います。

# 5. ROS2 Humble インストール

以降の手順は公式との差異はありません。

https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debs.html

universe リポジトリを有効化

```
sudo apt install software-properties-common
sudo add-apt-repository universe
```

GPG key の取得

```
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
```

ROS2 humble のリポジトリを追加

```
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
```

リポジトリの反映と必要パッケージのインストール

```
sudo apt update -y && sudo apt upgrade -y
sudo apt install -y ros-humble-desktop ros-dev-tools
```

サンプルテスト
下記コマンドで受信待ち

```
source /opt/ros/humble/setup.bash
ros2 run demo_nodes_py listener
```

もう一つのコンソールを立ち上げ、ssh ros2-humble にログインし、
下記コマンドで listener を起動し、受信待ちの listener へ通知が届けばOK

```
source /opt/ros/humble/setup.bash
ros2 run demo_nodes_cpp talker
```

# 6. MoveIt2 インストール

こちらも同様に ssh ros2-humble でログインして作業を行います。
手順も下記公式の通りです。

Getting Started に沿って進める

https://moveit.picknik.ai/main/doc/tutorials/getting_started/getting_started.html

## 6.1. 必要なパッケージのインストール

```
sudo apt install python3-rosdep
```

初期設定

```
sudo rosdep init
rosdep update
sudo apt update
sudo apt dist-upgrade
```

colcon と mixin をインストール

```
sudo apt install -y python3-colcon-common-extensions python3-colcon-mixin
colcon mixin add default https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml
colcon mixin update default
```

vcstool をインストール

```
sudo apt install -y python3-vcstool
```

## 6.2 ビルド

ソースコードの配置

```
mkdir -p ~/ws_moveit/src
cd ~/ws_moveit/src
git clone -b humble https://github.com/moveit/moveit2_tutorials
```

vcs を使って moveit2_tutorials のソースコード取得

```
vcs import --recursive < moveit2_tutorials/moveit2_tutorials.repos
```

バイナリをインストールしていた場合、削除

```
sudo apt remove ros-$ROS_DISTRO-moveit*
```

依存関係の解消

```
sudo apt update -y && rosdep install -r --from-paths . --ignore-src --rosdistro $ROS_DISTRO -y
```

ビルド

```
cd ~/ws_moveit
colcon build --mixin release
```

## 6.3. デモを起動

```
source install/setup.bash
ros2 launch moveit2_tutorials demo.launch.py rviz_config:=panda_moveit_config_demo_empty.rviz
```

![rviz.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38092/f88c656d-0b36-67e3-754d-a8d35ccaa117.png)

# 7. まとめ

まだ環境を構築したばかりで、lxc ならではのトラブルは未検証です。
これから tutorial を含めて色々触っていく中で、解消できた項目などは随時反映させていきたいとおもいます。