EPS32-S3でmicro-ROS導入(SSHしたラズパイのPlatformIOでビルド)

raspberry-pi

ラズパイ(Raspberry pi)とESP32-S3でmicro-ROS通信ができる環境の構築をする方法をまとめます。

スポンサーリンク

システム構成

micro-ROSの通信をするために使っていたシステム構成です。

  • Windows 11 のVisual Studio Code(VSCode)でラズパイにRemote SSH
  • ラズパイでmicro-ROS Agentのビルド
  • ラズパイでPlatformIOをインストールし、ESP32-S3のmicro-ROSプログラムをビルド、書き込み
  • ESP32-S3のmicro-rosがメッセージをパブリッシュ
  • Raspberry pi 4のmicro-ROS Agentがメッセージを受け取り、ROS2のトピックにパブリッシュ
  • VSCodeのターミナルでROS2のトピックを確認

ESP32-S3のプログラムはRaspberry pi上でビルドし書き込みを行っていますので、ラズパイからUSBケーブルを抜き差しするなどを行う必要はありません。例えば、ラズパイとESP32-S3がロボットに組み込まれた場合、PCから遠隔でプログラムの変更を行えます。わざわざUSBケーブルをマイコンボードに挿さなくてよいです。

それでは環境構築を行っていきます。

Raspberry piにROS2 Humbleを導入

Raspberry pi 4にインストールしたUbuntu 22.04にROS2 Humbleをインストールする必要があります。特に導入は躓かなかったので、ここではひとまず記載をしませんが、ROS2 Humble公式のチュートリアルに従い導入すればよいと思います。

PlatformIOでESP32-S3プログラムをクロスコンパイルするときの注意点ですが、クロスコンパイルを実行するターミナルではラズパイ用のROS2のsetupコマンドを実行しないようにしましょう。ラズパイ用のstd_msgsなどと紐づいてしまい(ビルド環境汚染)、アーキテクチャが異なるためリンクエラーなどで正常にビルドできなくなります。

ROS2の起動を毎回するのが面倒ということで、ターミナル起動時に自動で読み込まれるbashrcにsource /opt/ros/humble/setup.bash(他、自作したROS2パッケージのsetup.bashなど)を記載しているとエラーが起きます。

ラズパイでのmicro-ROS Agentのビルド

ラズパイのUbuntu22.04でmicro-ROS アプリケーションのビルドシステムを作っていきます。まずは、WindowsにインストールしたVisual Studio Codeをでラズパイにremote sshをします。Remote SSHは拡張機能です。

micro-ROSアプリケーションのビルドは、micro-ROS公式のチュートリアルの通りに進めれば導入することができます。microros_wsディレクトリ内にアプリケーションが構築されていきます。

firmwareのビルドにはラズパイ4Bの環境では40分程度かかりましたので気長に待ちましょう。firmwareをビルドするときにいくつかエラーが出る場合がありました。筆者の場合は、ROSのリポジトリキーが古くなってしまっていて、依存関係のパッケージ更新がうまくいかなかったのが原因です。

この場合は、ROSリポジトリキーの更新を行うことで解決しました。

# 1. ROSの新しいGPGキーを取得して、システムに登録します
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
sudo apt update

micro-ROSはROS2とはメッセージの方法が異なるため、micro-ROSとROS2では直接会話することができません。そこで、micro-ROS Agentという通訳者を間に立てることでメッセージのやり取りをします。

micro-ROSのアプリでパブリッシュされたメッセージは、micro-ROS Agentが通訳して、ROS2のトピックを発行します。これでROS2で接続されたノードと通信します。また、この発行されたROS2のトピックは、micro-ROS Agentによって逆翻訳され、micro-rosのアプリにも届きます。

通信をするときは、このAgentを立てる(別ターミナルで実行)のを忘れないようにしましょう。

# microros_ws上で実行
cd microros_ws/
source /opt/ros/humble/setup.bash
source install/local_setup.bash
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888

チュートリアルのテストを実行することで、ROS2エコシステムとmicro-ROSアプリが正常に通信出来ているかを確認できます。

ESP32-S3のmicro-ROSプログラムをplatformIOでビルド・書込

micro-ROSは複数のRTOSに対応しており、それぞれ公式導入チュートリアルが準備されています。ここで使用するESP32-S3マイコンはFreeRTOSが対象となります。

今回はボードの開発に便利なPlatformIOでmicro-ROSのプログラムをクロスコンパイルし、ESP32-S3マイコンに書き込む方法を取りたいので、上記のチュートリアルではなく、Githubに公開されているPlatformIOで扱えるmicro-ROSのライブラリを参照します。ここに記載された内容を実行することでESP32-S3にmicro-ROSを導入することができます。

まずはラズパイにPlatformIOを導入して、プロジェクトを作ります。

# platformIOの導入とVSCodeのplatformIOのextensionで必要となるプログラムのインストール

sudo apt update 
sudo apt install -y git python3-pip
sudo apt install python3-venv
pip install -U platformio

# platformIOのプロジェクトとするディレクトリの作成
# このディレクトリはmicroros_wsの外に作ります。中に作るとROS2のcolconの管理に影響を与えます。
mkdir esp32_microros_firmware
cd esp32_microros_firmware

今回使用しているESP32-S3のボードはSwitch Scienceから販売されているESPr® Developer S3 Type-C (USBシリアル変換ICなし)というボードです。

PlatformIOで用意されているボードのプロファイルにはありません。しかし、同じの無線通信モジュールESP32-S3-WROOM-1を搭載しているESP32-S3-DevKitC-1を使うことで書き込みが行えます。

# ボードの検索
pio boards | grep -i "ESP"

例:
esp32s3box                           ESP32S3  240MHz       16MB     320KB   Espressif ESP32-S3-Box
esp32-s3-devkitc-1                   ESP32S3  240MHz       8MB      320KB   Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)
esp32-s3-devkitm-1                   ESP32S3  240MHz       8MB      320KB   Espressif ESP32-S3-DevKitM-1
esp32s3usbotg                        ESP32S3  240MHz       8MB      320KB   Espressif ESP32-S3-USB-OTG

次に、準備したディレクトリにplatformIOのプロジェクトを作ります。

# platformIOのプロジェクトを作成
pio project init --board esp32-s3-devkitc-1

このコマンドで作成されたplatformio.iniを編集します。

# platformio.iniに追記

[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino

; 以下の行を追記
monitor_speed = 115200

; micro-ROSのライブラリと設定を追加
lib_deps =
    https://github.com/micro-ROS/micro_ros_platformio
board_microros_distro = humble
board_microros_transport = serial

build_flags =
    -DARDUINO_USB_CDC_ON_BOOT=1
    -D PIO_FRAMEWORK_ARDUINO_ENABLE_USB_CDC

次にsrcディレクトリの中にmain.cppを作成します。ESP32-S3マイコンからmicro-rosでメッセージを発信するプログラムです。内蔵されているLEDに対応しているIOピンが設定に使ったesp32-s3-devkitc-1とは異なるためLED_BUILTIN_BOARDという定義を書いています。

//src/main.cpp

#include <Arduino.h>
#include <micro_ros_platformio.h>

#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>

#include <std_msgs/msg/int32.h>

#define LED_BUILTIN_BOARD 6

#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}

rcl_publisher_t publisher;
std_msgs__msg__Int32 msg;
rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
rcl_timer_t timer;

void error_loop() {
  while (1) {
    digitalWrite(LED_BUILTIN_BOARD, !digitalRead(LED_BUILTIN_BOARD));
    delay(100);
  }
}

void timer_callback(rcl_timer_t *timer, int64_t last_call_time) {
  RCLC_UNUSED(last_call_time);
  if (timer != NULL) {
    RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
    msg.data++;
  }
}

void setup() {
  // USB-CDCが利用可能になるのを待つ (この行は残す)
  Serial.begin(115200);
  while (!Serial) {
    delay(10);
  }

  set_microros_serial_transports(Serial);

  pinMode(LED_BUILTIN_BOARD, OUTPUT);
  digitalWrite(LED_BUILTIN_BOARD, HIGH);
  delay(2000);

  allocator = rcl_get_default_allocator();

  RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
  RCCHECK(rclc_node_init_default(&node, "micro_ros_platformio_node", "", &support));
  RCCHECK(rclc_publisher_init_default(
    &publisher,
    &node,
    ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32),
    "micro_ros_publisher"));

  RCCHECK(rclc_timer_init_default(
    &timer,
    &support,
    RCL_MS_TO_NS(1000),
    timer_callback));

  RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
  RCCHECK(rclc_executor_add_timer(&executor, &timer));

  msg.data = 0;
}

void loop() {
  delay(100);
  rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100));
}

platformioのmicro-ROSの依存関係を処理するために、必要なライブラリをインストールします。

sudo apt update
sudo apt install -y cmake build-essential

続いて、ここで注意ですが、通常のターミナルで使っているbashではなく、PlatformIO Core CLIで次を実行します。PlatformIO Core CLIはVSCodeのGUI上だと左下にコマンドがあります。クリックすると新しいターミナルが立ち上がります。

見た目は普段使っているbashと同じですが、右上にPlatformIO CLI – user名が表示されています。

このターミナルでmicro-ROSのビルドを行います。ここで実行しないとネイティブなpythonに紐づいてモジュールがインストールされてしまい、うまくいきませんでした。また、pio runでビルドを行いますが、platformIOの仮想環境の中にcatkin_pkgがない場合、インストールが必要です。

# platformIO CORE CLI上でビルドのみを実行
# 実行する場所はplatformIOのプロジェクトのディレクトリである必要があります。
cd esp32_microros_firmware
pio run

# ビルドがうまくいかないときで、
# ターミナルメッセージ内にModuleNotFoundError: No module named 'catkin_pkg'がある場合は次を実行
pip install -U catkin_pkg colcon-common-extensions vcstool lxml

これをラズパイで実行すると、初回はmicro-ROSのfirmwareのビルドがあるため、約20分程度かかります。うまくビルドがされば、次回からはビルドされたものが使われるため、main.cppのプログラムを変更し、書き込む程度ではあまり時間はかかりません。

実行後、結果がSUCCESSしたら、実際にESP32-S3のボードに書き込みをします。今回使用するESPr® Developer S3 Type-C (USBシリアル変換ICなし)はプッシュSWが2個ついています。初回はFLASHを押しながらRESETを押すことで書き込みが可能になります。

# ビルドがうまくいったらESP32-S3のボードに書き込み
# コマンドとしてはビルド後、ターゲットに書き込み。
pio run --target upload

2回目以降は不要なこともあるようですが、同様の手順を取った方がFAILEDしませんので、基本的にはやった方がいいでしょう。

無事に書き込みができたら、ラズパイ、ESP32-S3共に環境の構築、プログラムの実装は完了です。

micro-ROSとROS2での通信の確認方法

構築した環境でうまく通信を行えるかをテストします。

まず、通常のターミナルを立ち上げ、micro-ROS Agentを立ち上げます。この時、シリアル通信をやり取りするポート等を指定します。USBで接続したポートはttyACM0、ttyACM1などで表現されていることが多いです。どれかわからない場合は、USBを接続しているとき、接続していないときを比較すればわかります。

# ===== ターミナル1 ======
# ポートの確認
ls /dev/tty*

# micro-ROS Agentの実行
cd microros_ws/
source /opt/ros/humble/setup.bash
source install/local_setup.bash
ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyACM0 -b 115200 -v6

次に、ESP32-S3のボードのRESETボタンを押して、RESETします。シリアル通信がmicro-ROS Agentと行えれば、ボードに実装されたLED(青色)が点灯します。うまくいかない場合は、点滅します。

通信がうまくいっていれば、micro-ROS AgentはESP32-S3からメッセージを受け取り、ROS2のトピックをパブリッシュしています。別のターミナルを立ち上げ、ROS2コマンドで確認してみましょう。

# ===== ターミナル2 ======
# ros2コマンドの初期設定
source /opt/ros/humble/setup.bash

# トピックが発行されているかを確認(/micro_ros_publisherがあるはず)
ros2 topic list

# ESP32-S3で作ったノードがあるかを確認(/micro_ros_platformio_nodeがあるはず)
ros2 node list

# ESP32-S3からデータが発行されている内容を確認(1秒ごとにカウントアップするデータ)
ros2 topic echo /micro_ros_publisher

最終的に、1秒ごとにカウントアップするデータが見れれば成功です。

まとめ

Windows 11からVisual Studio Codeを使って、Remote SSHをしたRaspberry pi上で、ROS2とmicro-ROSの実行、ESP32-S3マイコンへの書き込みとROS2, micro-ROS間での通信を実行できるようになりました。

これでRaspberry piなど負荷の高い処理を行えるデバイスと、リアルタイム制御を行えるESP32-S3マイコンを組み合わせたアプリを開発できます。

例えば、ロボットを作る際に、タイヤのモータ制御やセンサ値の取得などはESP32-S3マイコンに任せ、カメラによる画像処理はRaspberry piで実行し、情報をROS2, micro-ROSのエコシステムでやり取りするなどです。応用範囲が広がりそうな環境が構築できたと思います。

その他の注意点やヒント

ターミナルを立ち上げるときにbashrcを読み込まない方法

ROS2を実行する際、初めにsetup.bashを実行します。この手続きが面倒というところで、bashrcに書き込み、ターミナルを立ち上げたらすぐにROS2コマンドを実行できるようにしている場合があります。前述したように、setup.bashのコマンドを実行していると、クロスコンパイルでエラーが発生するため、ROS2を実行していない環境を用意する必要があります。

bashrcの記載を削除するのもよいですが、一時的にbashrcなどを読み込まない方法があります。

bash --norc

WindowsのWSLを行う(PlatformIO向けmicro-ROSのビルド、ESP32-S3への書き込み)

ラズパイでESP32-S3向けのmicro-ROSやプログラムをビルドしていますが、ラズパイの処理性能はホストPCであるWindowsのIntelマイコンなどと比べ、かなり低いです。そのため、コンパイルするのに時間がかかってしまいます。

ラズパイからESP32-S3マイコンを毎回外して書き込み、外して再接続するなどの手間はありますが、プログラムが重い場合、より性能の高い方で短時間でビルドしたいという考えはあると思います。

また、ラズパイとの接続をせずにESP32-S3内で完結する機能を作りたいときなど、逆にラズパイの開発が面倒になる場面もあり得ます。

そこで、PlatformIOのmicro-ROSライブラリをWindowsのVSCode上で実行しようと考えますが、次のようなエラーが出てしまいます。

Build dev micro-ROS environment failed: 
 '.' is not recognized as an internal or external command,
operable program or batch file.

Linuxでの実行を前提としているためのエラーです。VSCodeのデフォルトターミナルをgit bashに設定しても、同じくエラーがでるため、簡単な変更では難しいように感じました。

そこでWSL2でUbuntu22.04を準備します。WSL2の導入はネットに多数載っていますので検索してみてください。設定後、VSCodeのWSL拡張機能でWSLに接続し、同じくPlatformIOの設定を行っていくことで実現できます。

WSLは接続したUSBを何もせずに認識することは出来ないため、WindowsのPowerShellからUSBの接続設定を行います。

まず、WindowsのPowerShellを管理者権限で実行します。usbipdをインストールしていない場合は行います。

# PowerShellで実行。インストーラが起動。
winget install --interactive dorssel.usbipd-win

次に認識されているUSBを確認します。接続しているESP32-S3のBUILDの番号を調べます。DEVICEの説明でわからない場合は、USBを接続した場合と接続していない場合で実行して差分を確認しましょう。

usbipd list

BUILDの番号を確認出来たら、WSLにUSBを接続させます。

# usbipd bind --busid <BUILDの番号>
usbipd bind --busid 1-1

usbipd attach --wsl --busid 1-1

以上で、WSLに接続がされました。次にWSLのUbuntu側でUSBが接続できるかを確認します。

# WSLのターミナルで確認
ls /dev/ttyACM*

接続されていたら、platformIOでESP32-S3へプログラムの書き込みを行います。

pio run --target upload

ESP32-S3がシリアルを受け取れずにエラーになってしまう場合があります。この時は前述しているようにESP32-S3のボードでFLASHとRESETをしましょう。RESETを行うとUSBの認識が外れてしまうため、再度PowerShellでattachを行う必要があります。その後、ESP32-S3への書き込みを実行してください。

以上でより性能の高い環境でビルドを実行できるようになります。複数台に同じようなプログラムを書きこむ必要があるときなどにも役立ちそうです。

micro-ROSのビルドがうまくいかなかったとき.pioディレクトリを削除

PlatformIOのmicro-ROSのビルドが何かの原因でうまくいかなかった場合、より安全に再実行するために、.pioのディレクトリの中身を削除してから行うことが推奨されるようです。

ライブラリを再構築する場合や、ROS2のディストリビューションを変更するときなど、古い設定ファイルを削除する目的で実行します。

# PlatformIOが提供する削除コマンド
# コンパイルされたオブジェクトファイル等を削除し、次のビルドを完全に新規に行う
# ダウンロードされたライブラリ(.pio/libdeps)やPlatformIOが内部で使うPython仮想環境(.pio/penv)は削除されない
pio run --target clean

# micro-ROS固有のライブラリや設定ファイルを削除する
pio run --target clean_microros
# .pioのフォルダごと削除する方法。
# ビルド成果物だけでなく、ダウンロードされたすべてのライブラリやPython仮想環境など、
# PlatformIOがプロジェクト用に作成したすべての一時ファイルを削除
cd esp32_microros_firmware
rm -rf .pio
タイトルとURLをコピーしました