2020年1月19日日曜日

ros2arduino利用時に必要なMicroXRCEAgentは、ビルド時と実行時のライブラリの依存関係に注意する必要があった話


背景

ROS2とは、ロボットに関する利用しやすい環境を提供してくれるフレームワークです。
Arduinoとは、電子工作に入門しやすい組み込みボードの名前です。
ros2arduinoとは、Arduinoを利用して動かす組み込み装置の情報をROS2とやり取りするために開発されたライブラリです。
ros2arduinoの対応マイコンとしてESP32が挙がっていたので、今回はESP32が載ったマイコンボードを利用しました。

ros2arduinoを利用するプログラムをESP32に書き込んでROS2と連携しようとしたところ、ros2arduinoを利用して動くESP32からMicro-XRCE-DDSが情報を受け取った際に下記のエラーが発生しました。
(現状のros2arduinoの仕様として、MicroXRCEAgentの起動直前か起動後にESP32をリセットする必要があります。)
MicroXRCEAgent serial --dev /dev/ttyUSB0 -b 115200
Enter 'q' for exit
[1579353536.904875] info     | SerialServerLinux.cpp | init                     | running...             | fd: 3
[1579353543.122428] info     | Root.cpp           | create_client            | create                 | client_key: 0xAABBCCDD, session_id: 0x81
[1579353543.122536] info     | SerialServerBase.cpp | on_create_client         | session established    | client_key: 0xAABBCCDD, address: 0
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
中止 (コアダンプ)

調べたり試したりしたところ複数のエラー回避方法が分かったので、調査内容も含めて備忘録として内容を共有します。

使ったもの

  • ROS2が動くPC
    Ubuntu18.04で動かすPCに、ROS2のdashingをインストールしました。
  • Micro-XRCE-DDS-Agent
    ROS2が動くPCにインストールします。
    このプログラムのビルド環境がエラーに関連していたので、記事本文でビルドと実行の環境を解説します。
  • ESP32が載った開発ボード
    ESP32-WROOM-32が載った開発ボードを利用しました。
    M5Stackとかでも動くと思います。
  • ESP32の開発環境をインストールしたPC
    Arduino IDEPlatformIOをインストールてください。
    今回はROS2を動かすPCにインストールしましたが、ROS2をインストールしてない別のPCを利用しても良いです。
    Arduino IDEを利用する場合はarduino-esp32をインストールしてください。
    PlatformIOを利用する場合はボード情報のインストールは必要ありませんが、aarch64(ARMの64bit)環境を利用する場合は、ESP32のプログラムをビルドするためにコンパイラの置き換えが必要かもしれません。
  • ros2arduino
    ESP32の開発環境にインストールしてください。
    ArduinoIDEならライブラリマネージャーからインストールできます。
    PlatformIOならplatformio.iniのlib_depsにros2arduinoを加えれば利用できる状態になります。
  • ros2arduinoのシリアル通信プログラム
    ESP32の開発環境にros2arduinoをインストールして、シリアル通信のサンプルプログラムであるpublisherをESP32に書き込んでください。
    Arduino IDEなら、ライブラリマネージャーからライブラリをインストールして、「ファイル->スケッチ例->ros2arduino->publisher」で開けます。
    PlatformIOなら、platformio.iniのlib_depsにros2arduinoを追加して、main.cppを下記のように記述するとサンプルプログラムを利用できます。
    #include <Arduino.h>
    #include <WiFi.h>
    #include "../.pio/libdeps/esp32dev/ros2arduino_ID5966/examples/publisher/publisher.ino"

Micro-XRCE-DDSが動かない原因: ライブラリの依存関係がビルド時と異なる

解決方法を把握するために検索したところ、「ROS2で自作ロボットの開発ツールを整える」で原因と解決方法を紹介してくれていました。
Micro-XRCE-DDSが利用を想定しているライブラリのバージョンとROS2のsetup.bash(aptでインストールしたdashingなら /opt/ros/dashing/setup.bash )で読み込まれるライブラリのバージョンが合っていないのが原因でエラーを発生しているようで、実行時にライブラリの参照先を切り替えることでエラーを回避できるようでした。

setup.bashを実行していない環境でのMicro-XRCE-DDSの依存関係はこうなっていました。
ldd `which MicroXRCEAgent`
 linux-vdso.so.1 (0x00007ffc157c6000)
 libmicroxrcedds_agent.so.1.1 => /usr/local/lib/libmicroxrcedds_agent.so.1.1 (0x00007f61cd8c4000)
 libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f61cd6a5000)
 libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f61cd31c000)
 libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f61cd104000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61ccd13000)
 libfastrtps.so.1 => /usr/local/lib/libfastrtps.so.1 (0x00007f61cc5bf000)
 libmicroxrcedds_client.so.1.1 => /usr/local/lib/libmicroxrcedds_client.so.1.1 (0x00007f61cc3a8000)
 libmicrocdr.so.1.1 => /usr/local/lib/libmicrocdr.so.1.1 (0x00007f61cc19c000)
 libfastcdr.so.1 => /usr/local/lib/libfastcdr.so.1 (0x00007f61cbf89000)
 /lib64/ld-linux-x86-64.so.2 (0x00007f61cde7a000)
 libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f61cbbeb000)
 libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f61cb9e7000)
 libssl.so.1.1 => /usr/lib/x86_64-linux-gnu/libssl.so.1.1 (0x00007f61cb75a000)
 libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007f61cb28f000)
 librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f61cb087000)

setup.bash実行後の依存関係は、こうなっていました。
追加されたライブラリには+を、変更されたライブラリには*を記します。
ldd `which MicroXRCEAgent`
 linux-vdso.so.1 (0x00007ffff77de000)
 libmicroxrcedds_agent.so.1.1 => /usr/local/lib/libmicroxrcedds_agent.so.1.1 (0x00007f0fa403d000)
 libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f0fa3e1e000)
 libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f0fa3a95000)
 libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0fa387d000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0fa348c000)
* libfastrtps.so.1 => /opt/ros/dashing/lib/libfastrtps.so.1 (0x00007f0fa2cf6000)
 libmicroxrcedds_client.so.1.1 => /usr/local/lib/libmicroxrcedds_client.so.1.1 (0x00007f0fa2adf000)
 libmicrocdr.so.1.1 => /usr/local/lib/libmicrocdr.so.1.1 (0x00007f0fa28d3000)
* libfastcdr.so.1 => /opt/ros/dashing/lib/libfastcdr.so.1 (0x00007f0fa26c1000)
 /lib64/ld-linux-x86-64.so.2 (0x00007f0fa45f3000)
 libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f0fa2323000)
 libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f0fa211f000)
+ libtinyxml2.so.6 => /usr/lib/x86_64-linux-gnu/libtinyxml2.so.6 (0x00007f0fa1f0b000)
 libssl.so.1.1 => /usr/lib/x86_64-linux-gnu/libssl.so.1.1 (0x00007f0fa1c7e000)
 libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007f0fa17b3000)
 librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f0fa15ab000)

対応方法1: 依存関係を切り替えて実行する

MicroXRCEAgentでエラーを発生させていたのはlibfastrtpsのようだったので、下記のようにLD_PRELOADで参照先を変更しつつコマンドを実行したらエラーを発生させずに動かせました。
LD_PRELOAD="/usr/local/lib/libfastrtps.so.1" MicroXRCEAgent serial --dev /dev/ttyUSB0 -b 115200

対応方法2: ビルド時と実行時の環境に合わす

MicroXRCEをビルドするときの環境と実行時の環境でライブラリの依存関係が異なることがエラーの原因だったので、setup.bashの実行状況を合わせた下記の2つの状態ならLC_PRELOADを使わずともエラー無くMicroXRCEを動かせました。
  • ROS2のsetup.bashを読み込んでMicroXRCEをビルド -> setup.bashを読み込んだターミナルでMicroXRCEを実行
  • ROS2のsetup.bashを読み込まずにMicroXRCEをビルド -> setup.bashを読み込んでいないターミナルでMicroXRCEを実行
ROS2の環境に依存させた方が自分は良いと思うので、setup.bashを読み込んだ状態でMicroXRCEのビルドと実行を行うのが良いと思います。

まとめ

ROS2のsetup.bashの読み込みの有無でライブラリの依存関係が異なってしまうため、組み合わせによってはMicroXRCEAgentが動かなくなることがありました。
LD_PRELOADを使って実行時に依存関係を修正するか、MicroXRCEAgentのビルド時と実行時の環境を統一することでエラーを回避できました。

参考

ROS2で自作ロボットの開発ツールを整える
ROBOTIS-GIT/ros2arduino

0 件のコメント :