🐸

micro-ROSのembeddedRTPS通信を試してみた

2022/12/17に公開

今年もmicro-ROS推しの記事です!

筆者は小さいロボットに組み込めるような技術に関心が強いので、ここ数年マイコンからROS 2の資源へアクセスできるmicro-ROSに興味を持っています。この記事ではイチオシ機能を紹介しようと思います。
マイコンでROS 2という方向性ではmROS 2という実装もあります。動作対象の基板をお持ちの方は是非体験してみて下さい。
micro-ROSとmROS 2、どちらも良さがあるのでどちらも関心を持ってWatch👀しています。

というわけで、この記事を要約するとこのような内容です。

micro-ROSの最新リリース(Humble)

昨年のAdventCalendarへの投稿ではmicro-ROS推しの記事を投稿しましたが、1年経つ間にmicro-ROSに様々な進化がありました。特に先日ROS 2 Humble向けにリリースされた様々な機能はより進化を感じさせる内容ばかりでした。リリース情報から注目の機能を紹介します。

  • QoS設定APIが追加された
  • Actionに対応した
  • 死活監視/セッションタイムアウト機能が追加された
  • embeddedRTPSを利用したAgentレス通信の追加(experimental)
  • ターゲット環境の拡充やアップグレードが行われた
    • ルネサスe2 Studio対応の追加
      • CAN/FD通信経路の追加
    • Microsoft Azure RTOS対応の追加
    • Platform.IO対応の追加
    • TI Tiva Cシリーズ対応の追加
    • STMicro STM32CubeMX/IDE対応の追加
    • Espressif ESP-IDFサポートの更新
    • Arduino IDEサポートの更新
  • パフォーマンスの大幅な向上やbagfix ←アピール強め

新機能を試してみよう!

ここからはリリースの中でも是非確認しておきたいAgentレス通信について動作を確認してみようと思います。

micro-ROSは標準のROS 2ノードと異なり、Agentと呼ばれるノードがマイコン上のmicro-ROSノードをROS 2の世界へつないでくれる実装になっています。

筆者は、Agentが必要でもmicro-ROSの価値は十分あると思うのですが、Agentとの通信設定などmicro-ROS特有の設定やソフトウェアが求められるので、ROS初心者へオススメするには少し難点かな?とも思います。
コミュニティでもAgentが必要なくなるとより使いやすくなると歓迎する声が多いようです。

Agentレス通信の機能追加については投稿があるので詳細はこちらを確認して下さい。
簡単な説明としては、embeddedRTPSというマイコンからROS 2のDDS通信[1]と直接通信が可能になる機能をmicro-ROSの通信レイヤーとして使用できるようになりますということです。

ESP32用のembeddedRTPS対応を体験する

embeddedRTPS対応機能はまだ実験的な機能なのでST Nucleo F746ZGESP32で動くコードが公開されている状況です。
筆者のデスクにはESP32製品がたくさん積み上がっているのでESP32向けのembeddedRTPS対応を試してみようと思います。

積みあがるESP32製品

M5Stackで動くPub/Subの作り方

普通のESP32ボードで確認する方が楽なのですが、せっかくなのでM5Stackの機能も使ったサンプルの作成に挑戦してみます。

サンプルのビルド手順

embeddedRTPSに対応したサンプルはESP32向けmicro-ROS開発環境の
micro_ros_espidf_component含まれています。こちらの利用方法を説明していきましょう。

  1. ESP32マイコンを用意する
    あまり新しすぎる製品には対応していない可能性があるので注意。
    M5Stackでは、Basic, Gray, Fire, StickCあたりが選択可能です。

  2. ROS 2 Humbleの開発環境を構築する
    まずはHumble環境をインストールしましょう。

  3. Docker環境を構築する
    いくつか手段がありそうですが、dockerイメージをbuildして利用するのが最も動かしやすかったです。

  4. 適当な場所にmicro_ros_espidf_componentをcloneする
    ESP32用にmicro-ROSをビルドするためのパッケージを取得しましょう。

    $ git clone https://github.com/micro-ROS/micro_ros_espidf_component.git
    $ cd micro_ros_espidf_component
    micro_ros_espidf_component$ ls
    3rd-party-licenses.txt  NOTICE                    extra_packages
    CHANGELOG.rst           README.md                 libmicroros.mk
    CMakeLists.txt          colcon.meta               network_interfaces
    CONTRIBUTING.md         docker                    package.xml
    Kconfig.projbuild       esp32_toolchain.cmake.in
    LICENSE                 examples
    
  5. docker buildする
    適当な名前でdocker buildします。この記事ではesp-idf-microros:demoというイメージ名にします。

    micro_ros_espidf_component$ cd docker
    micro_ros_espidf_component/docker$ docker build . -t esp-idf-microros:demo
    

    なお、M5Stack向けの環境構築のためには次のように変更を行ってからbuildします。筆者がesp-idf向けにパッチを当てたM5Stackとarduinoのcomponentをイメージに含めるための操作を加えています。

    --- a/docker/install_micro_ros_deps_script.sh
    +++ b/docker/install_micro_ros_deps_script.sh
    @@ -8,3 +8,8 @@ source $IDF_PATH/export.sh
     pip3 install catkin_pkg lark-parser empy colcon-common-extensions importlib-resources
    
     set +u
    +
    +cd /opt/esp/idf/components
    +git clone https://github.com/nomumu/arduino-esp32.git arduino && cd arduino && git checkout 299d0d7
    +cd ../
    +git clone https://github.com/nomumu/M5Stack.git && cd M5Stack && git checkout 9574dd9
    
  6. 通信設定を変更する
    これは必須ではありませんが、この記事では作成するM5Stack向けのサンプル用に次のコマンドでPub/Subの作成可能数を増やします。最大リソースの定義などがcolcon.metaファイルに定義されているので、目を通しておくとエラーの理解などの役に立つと思います。

    micro_ros_espidf_component$ sed -i s/PUBLISHERS=2/PUBLISHERS=3/g colcon.meta
    micro_ros_espidf_component$ sed -i s/SUBSCRIPTIONS=2/SUBSCRIPTIONS=3/g colcon.meta
    
  7. docker runでビルドする
    5までがエラーなく進められていればビルドが可能になります。esp-idfの環境はDockerイメージの中に構築が済んでいるはずです。Dockerコンテナにmicro_ros_espidf_componentをマウントしてmicro-ROSのESP32向けファームウェアをビルドする仕組みです。
    docker runコマンドでビルドを行います。micro-ROSのライブラリなどをpre-buildするステップと、esp-idfの機能でサンプルをビルドするステップの2段構えになっています。
    このdocker runコマンドは実行時のカレントパスがmicro_ros_espidf_componentである前提で動作するので注意しましょう。

    embeddedRTPSのサンプルはexamples/int32_publisher_embeddedrtpsにあります。これをビルドするには次のコマンドを実行します。

    micro_ros_espidf_component$ docker run -it --rm --user espidf --volume="/etc/timezone:/etc/timezone:ro" -v  $(pwd):/micro_ros_espidf_component -v  /dev:/dev --privileged --workdir /micro_ros_espidf_component esp-idf-microros:demo /bin/bash  -c "cd examples/int32_publisher_embeddedrtps; idf.py menuconfig build flash monitor"
    

    このコマンドでは

    • サンプルのコンフィグ(menuconfig)
    • ビルド(build)
    • マイコンへの書き込み(flash)
    • 実行ログのモニタ(monitor)

    を順番に実行します。
    コンフィグメニューが表示されたら、必要な設定を行った後にESCで抜けるとbuildへ進みます。
    menuconfig
    必要最低限の設定として、micro-ROS Settingsの中のWiFi Configurationを手元環境に合わせましょう。[2]
    micro-ROS Settings
    ここではint32_publisher_embeddedrtpsのビルドを選択した場合にmicro-ROS over embeddedRTPS (EXPERIMENTAL)が選択されていることも確認できます。
    なお、設定を変更するとメニューを抜ける際に保存するかを聞かれますので、'Y'を選択すると次回からは同じ設定がロードされます。
    もし初回の入力も省略したい場合は、サンプルのディレクトリにあるsdkconfig.defaultsへ設定を書き込んでしまうのが楽です。

    ところで、リポジトリのREADME.mdでは公式のDockerイメージを利用する説明になっていますが、執筆時点でこのイメージはまだ更新が追い付いていないようで目的には合致しなかったので使用しません。

  8. pre-buildをcleanする方法
    micro-ROSの通信設定を変更したりするとpre-buildからやり直したいことがよくあります。このような時は次のようにするとcleanを実行できます。

    micro_ros_espidf_component$ make -f libmicroros.mk clean
    
  9. マイコンへの書き込み
    6の手順を実行する前にESP32マイコンをUSB接続しておき、ビルドが成功すると次のように書き込みが行われ、シリアルモニタが起動します。

    Successfully created esp32 image.
    Generated /micro_ros_espidf_component/examples/int32_publisher_embeddedrtps/build/int32_publisher.bin
    Executing action: flash
    Serial port /dev/ttyUSB0
    Connecting......
    Detecting chip type... Unsupported detection protocol, switching and trying again...
    Connecting....
    Detecting chip type... ESP32
    Running ninja in directory /micro_ros_espidf_component/examples/int32_publisher_embeddedrtps/build
    Executing "ninja flash"...
    [0/2] Re-checking globbed directories...
    [1/4] Performing build step for 'bootloader'
    ninja: no work to do.
    [1/2] cd /opt/esp/idf/components/esptool_py &...f/components/esptool_py/run_serial_tool.cmak
    esptool.py esp32 -p /dev/ttyUSB0 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x10000 int32_publisher.bin
    esptool.py v3.3.2-dev
    Serial port /dev/ttyUSB0
    Connecting.....
    Chip is ESP32-D0WDQ6-V3 (revision 3)
    Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
    Crystal is 40MHz
    MAC: 08:3a:f2:68:74:d0
    Uploading stub...
    Running stub...
    Stub running...
    Changing baud rate to 460800
    Changed.
    Configuring flash size...
    Flash will be erased from 0x00008000 to 0x00008fff...
    Flash will be erased from 0x00001000 to 0x00007fff...
    Flash will be erased from 0x00010000 to 0x000c0fff...
    Compressed 3072 bytes to 103...
    Writing at 0x00008000... (100 %)
    Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.1 seconds (effective 386.5 kbit/s)...
    Hash of data verified.
    Compressed 26048 bytes to 16120...
    Writing at 0x00001000... (100 %)
    Wrote 26048 bytes (16120 compressed) at 0x00001000 in 0.8 seconds (effective 247.7 kbit/s)...
    Hash of data verified.
    Compressed 724080 bytes to 457679...
    Writing at 0x00010000... (3 %)
    Writing at 0x0001c1ad... (7 %)
    ~~~
    Writing at 0x000b62b7... (96 %)
    Writing at 0x000bbabc... (100 %)
    Wrote 724080 bytes (457679 compressed) at 0x00010000 in 10.9 seconds (effective 530.5 kbit/s)...
    Hash of data verified.
    
    Leaving...
    Hard resetting via RTS pin...
    Executing action: monitor
    Serial port /dev/ttyUSB0
    Connecting....
    Detecting chip type... Unsupported detection protocol, switching and trying again...
    Connecting....
    Detecting chip type... ESP32
    Running idf_monitor in directory /micro_ros_espidf_component/examples/int32_publisher_embeddedrtps
    Executing "/opt/esp/python_env/idf4.3_py3.6_env/bin/python /opt/esp/idf/tools/idf_monitor.py -p /dev/ttyUSB0 -b 115200 --toolchain-prefix xtensa-esp32-elf- /micro_ros_espidf_component/examples/int32_publisher_embeddedrtps/build/int32_publisher.elf -m '/opt/esp/python_env/idf4.3_py3.6_env/bin/python' '/opt/esp/idf/tools/idf.py'"...
    --- idf_monitor on /dev/ttyUSB0 115200 ---
    --- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
    ets Jul 29 2019 12:21:46
    
    rst:0x1 (POWERON_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:2
    load:0x3fff0030,len:7220
    

動作確認

シリアルモニタへPublishing: 1 Publishing: 2...と出力されている状態で、パソコン側から次のようにトピックを確認してみましょう。ESP32のmicro-ROSアプリとパソコンのROS 2が直接Pub/Subを行っていることが確認できるはずです!

$ ros2 topic list
/freertos_int32_publisher
/parameter_events
/rosout
$ ros2 topic echo /freertos_int32_publisher
data: 519
---
data: 520
---
data: 521
---

もしlistに出てこないなどの場合、次のようにros2 daemonを再起動してみると効果があります。

$ ros2 daemon stop
The daemon has been stopped
$ ros2 daemon start
The daemon has been started

どうやらROS 2のdaemon機能との相性がよくないらしく、再接続時などにうまくいかないことがありました。改善を期待したいですね。

M5Stackで動かしてみた

embeddedRTPSがパソコンのROS 2環境と直接通信できることはわかりました。ではマイコン同士の通信はどうでしょう??
ということで、以前作成したM5Stack向けのサンプルを改造して2つのM5Stack(Basic, Fire)とパソコンが同じトピックを見れるか確認してみました!

各ボタンを/btn/a/btn/b/btn/cのトピックへstd_msgs/Boolで投げる簡単な通信を確認します。
M5Stackのサンプル

このように、Agentも不要で、さらにマイコン同士も特別な処理なしにROS 2の基本的な通信でPub/Subができることが確認できました。

まとめ

今回注目したembeddedRTPS機能は、実験的な実装とはいえ基本的な通信が利用可能な状態になっていることが確認できました。マイコン同士の通信にも利用可能ということがわかったので、これまでAgentの扱いが難しくて利用できなかったシーンなどにも適用の幅が広がるのは間違いないでしょう!
不安定な部分や利用方法の難解さなどはまだあるものの、今後に十分期待してよいと思います。

脚注
  1. 互換性記載はeProsimaのFastDDSのみ ↩︎

  2. M5Stackのためにはarduinoが要求するFREERTOS_HZ=1000の設定も必要 ↩︎

Discussion