🥧

ロボコンでRaspberry Piを爆速で使えるようにした

2022/12/10に公開

この記事は、あくあたん工房 Advent Calendar 2022の10日目の記事です。

みなさん,Raspberry Piは使っていますか?Picoじゃないほうです.在庫もないほうです.
私の所属するForteFibreを含めたロボコン界隈(?)でも,Raspbeery Piは組み込みやすく高機能な演算資源として利用されるようになっています.

ところで…

Raspberry Piを使う上で地味に面倒な作業が環境構築です.
まだaptですべて片付けばいいですが,例えば独自のPi Hat(拡張基板)を使ったり,Linuxには搭載されていない通信モジュールのドライバを入れるとなると手順がどうしても複雑になります.
実際,私たちがRaspberry Piを使用したNHK学生ロボコンでは,Pi Hatの導入,2つのハードウェアのドライバの導入を行っていました.
さらに,ロボット用のプラットフォームであるROS 2の導入も必要になります.

この作業を手作業ですると,例えばこんなふうになります.

  1. Ubuntuを起動用SDカードに書き込む
  2. SDカードから起動して,ネットワーク接続する(WiFi or Ethernet?)
  3. aptでROSを入れる
  4. 各種ツールでドライバソースコードをダウンロード
  5. dkmsでインストール(時間が掛かる)
  6. Pi Hat用の設定ファイルを複数作成する
  7. いろいろサービス止めてみたりする?
  8. etc, etc....

いっぱいありますね…ここでNHK学生ロボコンでは,操縦者が1名,ロボットが2台ありました.つまりRaspberry Piは最低3台あります.もっと嫌な話をしましょう.SDカード,なんとよく壊れます.
実際,大会までの練習中に壊れたことが何度かありました.

そのことを考えると,こうやって環境構築をしたSDカードをある程度の数用意する必要があります.面倒ですね?

そこで自動化ですよ

面倒なことはPythonのできる…じゃないですが,環境構築を自動化すれば簡単ですよね?幸いRaspberry Piは複数台あるので,同時に環境構築したら面倒事も減るし時間も掛からない,ミスもしないので最高です.

今回の自動化に使う道具は2つです.

それではやっていきましょう.

初回起動しよう

SDカードにUbuntuのイメージを書き込んで初回起動します.このとき,パスワードの設定だったりネットワークの設定だったりをしないといけません.さらにSSHも有効にして鍵を入れて…と面倒ですね.ここを自動化するためにcloud-initがあります.
cloud-initはもともと,大量展開されるサーバ群の初期設定を行うためのソフトウェアです.yaml形式の設定ファイルをうまく与えてやれば,良い感じに初期設定してくれます.
Raspberry PiのUbuntuでは,ブート用ファイルのあるパーティション(SDカード先頭の512MB程度)にこの設定ファイルを配置します.user-confignetwork-configというファイルがルートに見えるはずです.デフォルトである程度の解説が書いてありますが,部ではそこにSSH鍵の取り込みとWiFi設定を追加しました.

例えば,user-configにはこんな内容を入れています.ここでは,GitHubからSSH公開鍵を取ってくる記述と,パスワード再設定を起動時に求めなくする変更が入っています.

chpasswd:
  expire: false
  list:
  - ubuntu:CHANGEME-PASSWORD1111

# Enable password authentication with the SSH daemon
ssh_pwauth: true

## On first boot, use ssh-import-id to give the specific users SSH access to
## the default user
ssh_import_id:
- gh:f0reachARR

これで,SSHですぐアクセスできるRaspberry Piができました.

AnsibleでROS2をインストールしよう

ここからはAnsibleの仕事です.Ansibleは,yaml形式の設定ファイルを使って,リモート/ローカルのマシンにソフトウェアの自動インストールやセットアップを行うツールです.(公式曰く,インフラ自動構築ツール)

Ansibleを使って,まずは(一番簡単なので)ROS2をインストールしましょう.とはいえ,Ansible用のrole(処理のグループ)が有志によって公開されているので,それを使えば簡単です.
https://github.com/rarrais/ansible-role-ros2
これを,git submoduleで取り込み,良い感じにinclude_roleを使って実行すればOKです.

---
- name: Install ROS2
  include_role:
    name: rarrais.ros2
    apply:
      become: true
  vars:
    ros2_user:
        name: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
    ros2_configuration: ros-base
    ros2_packages:
      - joy
      - urg-node
      - fluent-rviz
    ros2_distribution: humble
    install_argcomplete: true
    dev_ws: ros2_ws

この例では,ROS2 humbleをjoy(ゲームパッド用ノード),urg-node(LIDAR用ノード),fluent-rviz(ビジュアライザのC++向けユーティリティ)と共にインストールしています.加えて,~/ros2_wsにワークスペースをセットアップしたり,必要な内容を.bashrcに追記する作業も行ってくれます.

Ansibleでドライバをインストールしよう

ロボットに必要なのはROSだけではなく,他のハードウェアも必要です.そして,ハードウェアといえばデバイスドライバが付いてきます.今回は,GitHubから無線LANアダプターのドライバをダウンロードしてインストールしてみます.
ドライバのインストールはdkmsを使います.これはDynamic Kernel Module Supportと言って,ドライバのソースコードをカーネルに合わせてビルドするという動作を行います.

例として以下のようなroleを書きました.このroleでは,dkmsをはじめとした依存関係をインストールしてから,デバイスドライバ本体をGitで取得して,dkmsでビルドしています.ビルドスクリプトはドライバの配布元が出しているものを参考にしています.

---
- name: Install driver dependencies
  apt:
    name:
      - "linux-headers-{{ansible_kernel}}"
      - dkms
      - rsync
      - flex
      - bison
  become: true
- name: Clone 8812au driver
  git:
    repo: https://github.com/cilynx/rtl88x2bu
    dest: /tmp/8812au
    force: true
  ignore_errors: true
- name: Configure for Raspberry Pi
  shell:
    executable: /bin/bash
    chdir: /tmp/8812au
    cmd: |
      sed -i 's/I386_PC = y/I386_PC = n/' Makefile
      sed -i 's/ARM_RPI = n/ARM_RPI = y/' Makefile

      VER=$(sed -n 's/\PACKAGE_VERSION="\(.*\)"/\1/p' dkms.conf) # バージョンを動的に取得
      rsync -rvhP ./ /usr/src/rtl88x2bu-${VER}
      dkms add -m rtl88x2bu -v ${VER} # dkmsの管理下に入れる

  become: true
- name: Install dkms
  shell:
    executable: /bin/bash
    chdir: /tmp/8812au
    cmd: |
      VER=$(sed -n 's/\PACKAGE_VERSION="\(.*\)"/\1/p' dkms.conf)
    
      dkms build -m rtl88x2bu -v ${VER}
      dkms install -m rtl88x2bu -v ${VER}
  become: true

これで,無線LANアダプターを使えるようになりました.

自動化の結果

環境構築を自動化した結果,SDカード書き込み+cloudinitの設定+コマンド1つでRaspberry Piをほぼ試合で利用できる状態にセットアップできました.所要時間は40分程度で,ほとんどAnsibleの完了待ちでした.
複数のRaspberry Piに並列でAnsibleを実行できるので,複数台に増えても時間は1時間程度に収まります.

これからのこと

配布されているイメージを書き込み,cloudinitして,とやるのはやはり時間が掛かります.本来であればイメージを書き込んだら全て完了していてほしいです.それなら,イメージを自作すればいいのでは?ということで,現在挑戦してみています.

Discussion