（たぶん）最小コスト・最短時間でROS2にマイコンを繋ぐ話

※2022.04.09更新
[ros2arduinoのFoxy対応が見込めない](https://github.com/ROBOTIS-GIT/ros2arduino/issues/49#issuecomment-664086749)ため，Foxy対応の検証は取り止めとする．
今後は[micro-ROS](https://qiita.com/tags/micro-ros)や[関連記事](https://qiita.com/tags/micro-ros)を参照されたい．

※2020.11.03更新
現時点では，ROS2 Foxy環境下のMicroXRCEAgent（v1.3.0）とros2arduino（v0.2.1）ファームウェアとの間でtopicのsubscribeが途中で停止するなどの挙動が見られる．このバージョンの対応環境は公式にはDashingとなっているため，いったんFoxyに関する記述を訂正線にて取り消す．スクリーンショットやコードブロック内の記述についてはFoxy環境下のものをそのまま残すので，適宜読み替えていただきたい．
後にros2arduinoがFoxy対応となった場合は再度検証して記事を更新する予定である．

# 記事概要
最小コスト・最短時間でROS2へのマイコン接続を体験すべく，安価なマイコンボードであるSeeeduino XIAOを用いて，コードを一切書かずに，マイコンからROS2にtopicをpublishする手順を紹介する．

![seeeduino_xiao_publisher_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574178/f405cc7a-c0f7-795a-47eb-dcc6d341d17a.gif)

# 動機
筆者はROS2のロボット制御への活用に興味を持っている．最近は[ros2arduino][1]をいじっていて，M5StickCやM5Atomなどを使用して[制御系を模擬した系][2]を試したり，[マイコンのタイマ割り込みによる定時処理と，（相対的に定時性の低い）デスクトップROS2によるtopic通信との併合系][3]の構築に取り組んでいる．

ros2arduino公式が通常のPub/Sub動作を確認しているボードのうち，趣味の領域で主に使用されているのはESP32系のボードであろう．先に挙げた筆者の取り組みもESP32系のモジュールを使用している．ros2arduinoのウェブサイトを確認すると，他に[Arduino MKR ZERO][4]というボードもサポートされていることがわかる．これはARM Cortex-M0+コアを搭載していて，俗にSAMD系Arduinoなどと呼ばれている．現時点で国内でも入手しやすく，ros2arduinoを試すのに手を出しやすいボードであると言える．

筆者はESP32系以外のプラットフォームでもros2arduinoを試してみたいと考え，このボードについて仕様を調べ始めた．搭載されているマイコンの型番を確認するとすぐに，[Seeeduino XIAO][5]に搭載されているマイコンと同じであることに気付いた（外観や回路図から，Arduino MKR ZEROはTQFP48パッケージ，Seeeduino XIAOはQFN48パッケージを採用していることがわかる）．加えて，ホストPCとの仮想シリアル通信のために外付けのUSB-シリアル変換チップを搭載せず，マイコンのUSB機能（CDC）を利用しているという点も共通している．

Seeeduino XIAOについては[先達の記事][6]なども参照いただくとして，特筆すべきはその低価格・入手性・接続の手軽さである．このボードを用いれば，最小コスト・最短時間でROS2にマイコンを繋ぐ（とりあえず繋いでPubるだけ）ことができるのではないかと筆者は考えた．

# 目的
Seeeduino XIAO（以下，本ボード）にros2arduinoのpublisherサンプルを焼き込み，ホストPCのROS2（Dashing ~~およびFoxy~~ ）に対してtopicをpublishできることを確認したので，手順を公開する．

# 本論
本記事においてはROS2 ~~Foxy~~ Dashingの環境で説明する ~~が，Dashingの環境でも同じようにpublisherサンプルをコンパイルし，Micro-XRCE-DDS-Agent経由でtopicをsubscribeできることを確認している~~ ．
手順は大まかに3つのステップから構成される．

Step1. ROS2環境やMicro-XRCE-DDS-Agentなどの事前準備
Step2. ArduinoIDEの設定とpublisherサンプルのコンパイル
Step3. publisherサンプルによる動作確認

既にROS2での開発やArduinoIDEでの開発に親しんでいる方ならば，付け加える手順は少しだけであるし，つまづく箇所も特にないだろう．

## 事前準備

### ROS2環境の準備
筆者は下記環境で確認しているが，ROS2のバイナリインストールでも同様に動くと思われる．

||ベースOSバージョン|ROS2バージョン|インストールタイプ|
|---|---|---|---|
|~~環境1~~|~~Ubuntu 20.04(amd64)~~|~~Foxy~~|~~ソースコードからビルド~~|
|環境2|Ubuntu 18.04(amd64)|Dashing|ソースコードからビルド|

### Micro-XRCE-DDS-Agentの準備
[Micro-XRCE-DDSの公式サイト][7]にもインストール手順が記載されているが，AgentとClient間のライブラリバージョン依存性が高いことが知られている．現時点では[ros2arduinoのサイト][1]を確認し，ArduinoIDEにインストールするClientライブラリのバージョンと整合が取れるバージョンをビルドするのが良いだろう．
また，ビルド時や実行時に参照するライブラリのバージョンをROS2本体のものと揃えるために，ビルド時および実行時にROS2の環境変数を読み込むようにするのがベターであることもわかってきている．以下は[ros2arduinoのサイト][1]からの抜粋を改変したビルド手順である．

```shell
$ git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git
$ cd Micro-XRCE-DDS-Agent && git checkout v1.3.0
$ mkdir build && cd build
$ source ros2_foxy/install/setup.bash # ROS2の環境変数を読み込む．これはFoxyをソースコードからビルドした場合の例．
$ cmake ..
$ make
$ sudo make install
$ sudo ldconfig /usr/local/lib/
```

## ArduinoIDEの設定とサンプルスケッチ（publisherサンプル）のコンパイル
ArduinoIDEそのものはUbuntuのソフトウェアセンターなどから容易にインストールできるので，設定方法について述べる．今回は下記バージョンにて検証している．

||バージョン|
|---|---|
|ArduinoIDE|v1.8.13|

本記事においては，Seeed社の提供するコンパイラやライブラリ（以下，まとめてボード定義と呼ぶ）を使用せず，マイコンをArduino MKR ZEROとして取り扱う．

### ボード定義の追加
ボードマネージャから"MKR"と検索して，"Arduino SAMD Boards (32-bits ARM Cortex-M0+)"をインストールする．

![board_manager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574178/39957729-3837-ef8b-754a-fd25477dd81d.png)

今回は下記バージョンにて検証している．

||バージョン|
|---|---|
|Arduino SAMD Boards (32-bits ARM Cortex-M0+)|v1.8.8|


### ボード設定
ツール > ボード > Arduino SAMD (32-bits ARM Cortex-M0+) Boards > Arduino MKRZERO
と設定する．

### ros2arduinoのインストール
ライブラリマネージャから"ros2arduino"を検索してインストールする．前述のように，Agentのバージョンとの整合性に気を付けること．

![library_manager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574178/c6517b69-b472-b48c-6314-8770a57a90c2.png)

今回は下記バージョンにて検証している．

||バージョン|
|---|---|
|Micro-XRCE-DDS-Agent|v1.3.0|
|ros2arduino|v0.2.1|

### publisherサンプルのコンパイル
スケッチ例のros2arduinoカテゴリの中からpublisherサンプルを開いてコンパイルする．
**コードを1文字も書く必要はない．**
本ボードはRAMが32KBであり，ros2arduinoの必要とする最低限のRAMしか積んでいないので，残りRAMにあまり余裕がないことがコンパイル結果からもわかる．publisherサンプルをベースに応用を考える際はこの点に注意する必要がある．

![compile_publisher_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574178/a8997190-8c86-51ad-7b63-ad2ebc4882e4.png)

コンパイルが通ることを確認したら，本ボードをホストPCに接続してコンパイルしたファームウェアを書き込む．

※2020.10.12更新
本ボードを始めとしたマイコンボードなどを初めてホストPCに接続するような場合は，通信やファームウェアの書き込みのために仮想シリアルポート（/dev/ttyACM0など）へのアクセス権を得る必要がある．

```shell
$ sudo usermod -a -G dialout $USER
```

仮想シリアルポートのオープンに失敗していると考えられる場合は，試してみると良い．

## publisherサンプルによる動作確認

### ホストPCへの本ボードの接続
USBケーブル（Type-A - Type-C）で接続すると，標準的なUbuntu環境であれば仮想シリアルポート"/dev/ttyACM0"として認識されるはずである．他にモデムデバイスや似たようなマイコンボードを接続していると，"/dev/ttyACM1"のように数字の部分が異なる場合がある．本記事においては"/dev/ttyACM0"として扱う．
正常なファームウェアが書き込まれていれば，この仮想シリアルポートを通じてMicro-XRCE-DDS-Agentへの接続試行が始まっている．

### Micro-XRCE-DDS-Agentの起動
先の仮想シリアルポートを指定してMicro-XRCE-DDS-Agentを起動する．起動するとすぐにセッションが確立される．

```shell
$ source ros2_foxy/install/setup.bash # ROS2の環境変数を読み込む．これはFoxyをソースコードからビルドした場合の例．
$ MicroXRCEAgent serial --dev /dev/ttyACM0 -b 115200
```

### ros2 topic echoによるtopicのsubscribe
もう1つターミナルを立ち上げ，"ros2 topic echo"コマンドによって確認する．
下記アニメーションのように，およそ1秒間に2回のtopicがsubscribeされればOK．

```shell
$ source ros2_foxy/install/setup.bash # ROS2の環境変数を読み込む．これはFoxyをソースコードからビルドした場合の例．
$ ros2 topic list
$ ros2 topic echo /arduino_chatter
```

![seeeduino_xiao_publisher_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/574178/f405cc7a-c0f7-795a-47eb-dcc6d341d17a.gif)


### USB接続についての注意点
本ボードにおいて陥りやすいトラブルとして，仮想シリアルポートが見えなくなったり，Micro-XRCE-DDS-Agentを起動しようとして仮想シリアルポートをオープンできなかったり，といったトラブルがある．大抵の場合は，ホストPCから仮想シリアルポートをオープンしたままマイコンにリセットをかけたりすると，これらのトラブルが発生しやすい．
マイコンとは別のUSB-シリアル変換チップを搭載している多くのマイコンボードでは，マイコンにリセットをかけてもボード上のUSB-シリアル変換チップに電力（多くの場合USBバスパワー）が供給されている限り，仮想シリアルポートは切断されない．本ボードの場合は，USB-シリアル変換機能がマイコンそのものの機能（CDC）によって実装されているためリセットがかかるたびに仮想シリアルポートが切断→再接続という扱いになり，前述のように仮想シリアルポートをオープンしたままリセットをかけると，再接続の際にデバイスファイルの数字（/dev/ttyACM*）がインクリメントされるのである．
通信周りのトラブルが発生していると思われる際は，いったん仮想シリアルポートを全てクローズし，マイコンをリセットして，dmesgコマンドによりデバイスファイル名を確認するのがトラブル解消の近道である．

# 結論
ros2arduinoのpublisherサンプルを，Arduino MKR ZERO用にコンパイルして本ボードに焼き込むことで，仮想シリアルポート経由でホストPC上のROS2（Dashing ~~およびFoxy~~ ）にtopicをpublishできることを確認した．

# Future Work
ここまでお読みいただいておいて恐縮だが，実はArduino MKR ZEROとしてのボード定義を使用しなくとも，Seeed社が配布するSeeeduino XIAO用のボード定義を使用して，ros2arduinoのpublisherサンプルをコンパイルして動作させることもできている．Arduinoのビルドシステムは筆者が思っていた以上に，デバイス依存コードとデバイス非依存コードをうまく切り分けて実装できていると感じた．
であるならば，ros2arduinoの必要性能要件（RAM : >= 32Kb[^1]）を満たしていて，かつArduinoIDEからシリアルポート（スケッチ内で"Serial"と表現される）が使えるボードならば，もっと広範なボードでpublisherサンプルを動作させられるのではないかと考えた．
現時点では手持ちの2種のボードで，同様の手法を用いて仮想シリアルポート経由でのpublishを確認できている．
1. STM32 nucleo-f446re
2. Wio Terminal

# 参考資料
1.[ros2arduino][1]
2.[ros2arduino Demo on [M5StickC -> Raspberry Pi Zero WH -> M5Atom Lite]][2]
3.[ROS Japan UG #38 オンラインミーティングの録画][3]
4.[Arduino MKR ZERO][4]
5.[Seeeduino XIAO][5]
6.[コインサイズ Arduino互換機 Seeeduino XIAO を使ってみた][6]
7.[Micro-XRCE-DDSのインストール手順][7]
8.[@MAEHARA_Keisukeのポータル記事][8]

[1]:https://github.com/ROBOTIS-GIT/ros2arduino
[2]:https://speakerdeck.com/maeharakeisuke/ros2arduino-demo-on-m5stickc-raspberry-pi-zero-wh-m5atom-lite
[3]:https://www.youtube.com/watch?v=SFs4jSWL-D8&feature=emb_logo
[4]:https://store.arduino.cc/usa/arduino-mkrzero
[5]:https://wiki.seeedstudio.com/Seeeduino-XIAO/
[6]:https://qiita.com/nanase/items/0fed598975c49b1d707e
[7]:https://micro-xrce-dds.docs.eprosima.com/en/latest/installation.html
[8]:https://qiita.com/MAEHARA_Keisuke/items/31faa68cf5c51831acc6

[^1]: 公式サイトでの単位表記が微妙だが，kb（キロビット）と解釈すると小さすぎるので，32KB（キロバイト）と読むのが妥当か