はじめに (ROS2って、DDSって、Rclexってなに?)
ROS2(Robot Operating System)はロボット開発支援フレームワークです。ROS2環境ではプログラムコードはC++やPythonで書かれることが主に想定されているようですが、これをElixirでも開発したい!ということでElixirのROS2クライアントライブラリ(=RCL)、Rclexが開発されています。
ROS2は通信レイヤにDDS(Data Distribution Service)という通信プロトコル/ライブラリ/パッケージが採用されていて、開発言語を問わず実装にDDSを用いていれば、ノード間で簡単に通信することが可能です(C++で書かれたROS2ノードとPythonで書かれたROS2ノード間で簡単に通信可能)。このRclexを用いればElixirで書いたコードで他のROS2ノードと通信することが可能です。
この記事はRclexを試してみましたので、そのノウハウを紹介するものです。
(将来的に、Elixir+DDS(ROS2なし)で相互通信できるようになる/することを想定しています。なおこの記事(Rclex)はElixir+ROS2(within DDS)環境です。)
DDSについては(ROS2観点ですが)こちらの記事が参考になります。
https://ros.youtalk.jp/2017/05/28/dds.html
なお本記事は、2021年5月時点の内容です。
環境とバージョン
Rclexは環境を選びます。以下の環境で動かしました。
- Ubuntu 18.04.5 LTS (amd64)
- ROS2 Dashing Diademata - Patch Release 8
- Elixir 1.11.2
- Erlang/OTP 24
当初Macでトライしましたが、ROS2は入るのですがRclexがビルドできず断念しました。
Ubuntuは、Ubuntu 20.04.2.0 LTS (Focal Fossa)ではROS2 Dashingがインストールできません。ROS2の新しいリリース(Galactic Geochelone)あたりは入るのですが、今度はROS2 GGではRclexがビルドできません。そのため18.04が必要です。
そういう訳でROS2はDashingでなければなりません。前述のように現状のRclexはDashingのライブラリ構造に依存しているためです。(コードをすごく頑張って直せば対応できるはずですが...。)
ErlangとElixirはUbuntu環境でパッケージインストールしました。特に問題ありませんでした。
環境構築、インストール
Ubuntu 18.04.5環境の構築
私はUbuntuをintel Mac(10.15 Catalina)上にVirtualBoxをインストールしその上で動かしました。ROS2に不慣れなのでGUIツール(rviz2など)も使うかなと思ったためですが、今回のようにDDSで通信を試すだけであればGUIは不要です。たぶんクラウド上のVMでも大丈夫だと思います。
VirtualBoxのインストール、またUbuntuのインストールは割愛します。調べればすぐに見つかるでしょう。
OS本体のインストールが完了したら、VirtualBoxのGuest Additionsもインストールしておきましょう。Guest AddtionsはROS2/Rclexトライアル的には不要ですが、入れたほうが便利です。
% sudo apt-get update
% sudo apt-get install virtualbox-guest-dkms
このあたりを参考にしました。
https://www.linuxmania.jp/virtualbox_02.html
ROS2 Dashingのインストール
Ubuntu環境ができたら、ROS2 Dashingをインストールしていきます。ROS2はバイナリパッケージインストールと自前構築の2つの方法がありますが、ここではソースコードから自前で構築しました。後述のRclexのビルドのために、includeファイルとライブラリの場所を把握しておきたかったためです。(バイナリパッケージでインストールしてもうまくできるかもしれませんが、未試用です。)
基本的な手順はROS2のドキュメント通りです。簡単に解説していきます。
https://docs.ros.org/en/dashing/Installation/Ubuntu-Development-Setup.html
(なおROS2のドキュメントは、バージョンが異なっても全般的には非常に似ていますので注意が必要です。)
以下の手順では、ROS2のフルセットをインストールしていますが、多分、いらない物がたくさんあるはずなので、ROS2に詳しい方は不要なもののインストールは省略できるはずです。RclexのビルドにはROS2の開発環境があればよいはず。
ロケールをセット
% sudo apt update && sudo apt install locales
% sudo locale-gen en_US en_US.UTF-8
% sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
% export LANG=en_US.UTF-8
ちなみに、この設定をしたのに、あとの方でコケました。
ROS2レポジトリを追加
% sudo apt update && sudo apt install curl gnupg2 lsb-release
% sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
% echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
この手順に失敗すると、次の手順のパッケージインストールの際に謎のエラーが出るので、間違えないように注意しましょう。
環境構築に必要なパッケージをインストール
% sudo apt update && sudo apt install -y \
build-essential \
cmake \
git \
python3-colcon-common-extensions \
python3-pip \
python-rosdep \
python3-vcstool \
wget
% python3 -m pip install -U \
argcomplete \
flake8 \
flake8-blind-except \
flake8-builtins \
flake8-class-newline \
flake8-comprehensions \
flake8-deprecated \
flake8-docstrings \
flake8-import-order \
flake8-quotes \
pytest-repeat \
pytest-rerunfailures \
pytest \
pytest-cov \
pytest-runner \
setuptools
% sudo apt install --no-install-recommends -y \
libasio-dev \
libtinyxml2-dev
% sudo apt install --no-install-recommends -y \
libcunit1-dev
ROS2 Dashing本体コードをダウンロード
% mkdir -p ~/ros2_dashing/src
% cd ~/ros2_dashing
% wget https://raw.githubusercontent.com/ros2/ros2/dashing/ros2.repos
% vcs import src < ros2.repos
続いて、rosdepコマンドで依存関係パッケージをインストールしていきます。
% sudo rosdep init
% rosdep update
% rosdep install --from-paths src --ignore-src --rosdistro dashing -y --skip-keys "console_bridge fastcdr fastrtps libopensplice67 libopensplice69 rti-connext-dds-5.3.1 urdfdom_headers"
ドキュメントではこの後、独自のDDS環境をインストールしたい場合には入れよ、と書いてあるのですが、ここではそれはしません。
コンパイル
いよいよROS2本体をビルドしていきます。
% cd ~/ros2_dashing/
% colcon build --symlinck-install
非常に時間がかかります。Corei7/32GBのMac、VirtualBox 4GBメモリ環境で、1時間半くらいかかりました。
環境変数をセット
ビルドが終わった時点で、ROS2環境が ~/ros2_dashing/install配下にできています。ROS2環境を使う(動かす)ためには環境変数(PATH等)をセットする必要があり、それをするためのスクリプトが用意されています。ターミナル実行時に都度環境変数をセットするか(下記)、.bashrc等でシェル起動時に自動で読み込ませるかします。
% . ~/ros2_dashing/install/setup.bash
bash以外のシェル用のスクリプトもあります。(sh, zah, powershell)
簡単にサンプルを動かしてみる
ROS2の環境構築ができたところで、ドキドキしながらサンプルを動かしてみます。
ターミナルを2つ起動し、双方でsetup.bashを実行させてros2コマンドが動くようにしておきます(下記には含んでいます)。その状態で、
% . ~/ros2_dashing/install/local_setup.bash
% ros2 run demo_nodes_cpp talker
% . ~/ros2_dashing/install/local_setup.bash
% ros2 run demo_nodes_py listener
とすると、ターミナル1側でpublishした文字列が、ターミナル2側のsubscriber側で受信できています。やりました!
実際の実行時の画面はこんな感じです。
なお、実行時にエラーが出る可能性があります。% export LANG=en_US.UTF-8 を再度設定する必要がありました。
(新しいターミナルを起動して、そちらではLANGが設定されていなかったからなのかもしれません。ログがなく詳細不明です。)
Elixir環境の構築
ROS2環境ができたので、ようやくRclexですが、前提となるElixirを入れます。
Elixirのドキュメントに従います。
https://elixir-lang.jp/install.html
ddstest2という作業ディレクトリを掘ってやりました。
% mkdir ddstest2
% cd ddstest2
% wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb
% sudo apt-get update
% sudo apt-get install esl-erlang
% sudo apt-get install elixir
その他、Rclexのビルドに必要なパッケージを入れておきます。
% sudo apt-get install build-essential
% sudo apt-get install erlang-dev
Rclexをインストールする
ここからがようやく本番です!
Rclexはライブラリであり、単体で使用するものではないので、ここでは同じ開発者から提供されているサンプルコードを使います。Rclex本体はサンプルコードの中で依存パッケージとして指定されており、サンプルコードをビルドするときに自動的に、インストール・ビルドされます。
% git clone https://github.com/rclex/rclex_samples.git
% cd rclex_samples
今回はただサンプルコードをそのまま使います。Elixirの流儀に従って作業します。
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$ mix deps.get
Resolving Hex dependencies...
Dependency resolution completed:
Unchanged:
elixir_make 0.6.2
rclex 0.3.1
* Getting rclex (Hex package)
* Getting elixir_make (Hex package)
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$
すかさずmixコマンドを実行してみると、
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$ mix
==> elixir_make
Compiling 1 file (.ex)
Generated elixir_make app
==> rclex
mkdir -p /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/priv
mkdir -p /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj
gcc -c -I/usr/lib/erlang/usr/include -I/opt/ros/dashing/include -g -O2 -Wall -Wextra -Wno-unused-parameter -pedantic -fPIC -o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/total_nif.o src/total_nif.c
gcc -c -I/usr/lib/erlang/usr/include -I/opt/ros/dashing/include -g -O2 -Wall -Wextra -Wno-unused-parameter -pedantic -fPIC -o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o src/init_nif.c
src/init_nif.c:10:10: fatal error: rcl/rcl.h: No such file or directory
#include "rcl/rcl.h"
^~~~~~~~~~~
compilation terminated.
Makefile:43: recipe for target '/home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o' failed
make: *** [/home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o] Error 1
could not compile dependency :rclex, "mix compile" failed. You can recompile this dependency with "mix deps.compile rclex", update it with "mix deps.update rclex" or clean it with "mix deps.clean rclex"
==> rclex_samples
** (Mix) Could not compile with "make" (exit status: 2).
You need to have gcc and make installed. If you are using
Ubuntu or any other Debian-based system, install the packages
"build-essential". Also install "erlang-dev" package if not
included in your Erlang/OTP version. If you're on Fedora, run
"dnf group install 'Development Tools'".
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$
コンパイルエラーです。これは、RclexをコンパイルするのにROS2環境側のインクルードファイルが見つからないのが原因です。Rclexが期待するROS2環境のディレクトリ構成と↑で作業したディレクトリ構成が異なっているためで、ここでは、RclexのMakefileの該当部分を修正してしまいます。
# set directory for ROSDISTRO
ROSDIR = /opt/ros/dashing
CC = gcc
LD = ld
RM = rm
PREFIX = $(MIX_APP_PATH)/priv
BUILD = $(MIX_APP_PATH)/obj
NIF = $(PREFIX)/rclex.so
CFLAGS ?= -g -O2 -Wall -Wextra -Wno-unused-parameter -pedantic -fPIC
LDFLAGS ?= -g -shared
# Set Erlang-specific compile and linker flags
ERL_CFLAGS ?= -I$(ERL_EI_INCLUDE_DIR)
ERL_LDFLAGS ?= -L$(ERL_EI_LIBDIR)
# for ROS libs
ROS_CFLAGS ?= -I$(ROSDIR)/include
ROS_LDFLAGS ?= -L$(ROSDIR)/lib
ROS_LDFLAGS += -lrcl -lrmw -lrcutils \
-lrosidl_generator_c -lrosidl_typesupport_c \
-lrosidl_typesupport_introspection_c \
-lstd_msgs__rosidl_generator_c -lstd_msgs__rosidl_typesupport_c \
-lfastcdr -lfastrtps -lrmw_fastrtps_cpp
# if you want to use OpenSplice DDS
# ROS_LDFLAGS += -lrmw_opensplice_cpp -lrosidl_typesupport_opensplice_cpp
SRC ?= src/total_nif.c src/init_nif.c src/node_nif.c src/publisher_nif.c src/subscription_nif.c src/wait_nif.c
SRC += src/msg_int16_nif.c src/msg_string_nif.c
HEADERS =$(wildcard src/*.h)
OBJ = $(SRC:src/%.c=$(BUILD)/%.o)
all: install
install: $(PREFIX) $(BUILD) $(NIF)
$(OBJ): $(HEADERS) Makefile
$(BUILD)/%.o: src/%.c
$(CC) -c $(ERL_CFLAGS) $(ROS_CFLAGS) $(CFLAGS) -o $@ $<
$(NIF): $(OBJ)
$(CC) -o $@ $(ERL_LDFLAGS) $(LDFLAGS) $^ $(ROS_LDFLAGS)
$(PREFIX):
mkdir -p $@
$(BUILD):
mkdir -p $@
clean:
$(RM) $(NIF) $(BUILD)/*.o
.PHONY: all clean install
冒頭部分のROSDIRを修正します。
# set directory for ROSDISTRO
# ROSDIR = /opt/ros/dashing
ROSDIR = /home/kikuzo/ros2_dashing/install
CC = gcc
LD = ld
RM = rm
PREFIX = $(MIX_APP_PATH)/priv
BUILD = $(MIX_APP_PATH)/obj
(..省略..)
できたら、rclexをビルドします。↑のエラーメッセージ内にありましたが、rclex本体を作るためには
% mix deps.compile rclex
とやります。
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$ mix deps.compile rclex
==> rclex
mkdir -p /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/priv
mkdir -p /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj
gcc -c -I/usr/lib/erlang/usr/include -I/home/kikuzo/ros2_dashing/install/include -g -O2 -Wall -Wextra -Wno-unused-parameter -pedantic -fPIC -o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/total_nif.o src/total_nif.c
gcc -c -I/usr/lib/erlang/usr/include -I/home/kikuzo/ros2_dashing/install/include -g -O2 -Wall -Wextra -Wno-unused-parameter -pedantic -fPIC -o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o src/init_nif.c
src/init_nif.c:10:10: fatal error: rcl/rcl.h: No such file or directory
#include "rcl/rcl.h"
^~~~~~~~~~~
compilation terminated.
Makefile:45: recipe for target '/home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o' failed
make: *** [/home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o] Error 1
could not compile dependency :rclex, "mix compile" failed. You can recompile this dependency with "mix deps.compile rclex", update it with "mix deps.update rclex" or clean it with "mix deps.clean rclex"
==> rclex_samples
** (Mix) Could not compile with "make" (exit status: 2).
You need to have gcc and make installed. If you are using
Ubuntu or any other Debian-based system, install the packages
"build-essential". Also install "erlang-dev" package if not
included in your Erlang/OTP version. If you're on Fedora, run
"dnf group install 'Development Tools'".
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$
再びエラー。依然としてファイルがないと言われます。
本来はMakefileを直したほうが良いのですが、どうやら前提とするディレクトリ構成が異なっているようで修正が大きそうなので、ROS2環境側でシンボリックリンクを張って回避することにします。(このあたり、Rclex開発時はどうだったのかちょっと謎です。ROS2 Dashingのパッチレベルが上ってディレクトリ構成が変わったのかな?)
% cd ~/ros2_dashing/install
% mkdir include
% cd include
% ln -s ../rcl/include/rcl rcl
(同様に、rcutils, rmw, rosidl_generator_c, rosidl_typesupport_interface, std_msgsについても市リンクを張ります)
さて今度こそ。
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$ mix deps.compile rclex
==> rclex
(..省略..)
gcc -o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/priv/rclex.so -L/usr/lib/erlang/usr/lib -g -shared /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/total_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/node_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/publisher_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/subscription_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/wait_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/msg_int16_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/msg_string_nif.o -L/home/kikuzo/ros2_dashing/install/lib -lrcl -lrmw -lrcutils -lrosidl_generator_c -lrosidl_typesupport_c -lrosidl_typesupport_introspection_c -lstd_msgs__rosidl_generator_c -lstd_msgs__rosidl_typesupport_c -lfastcdr -lfastrtps -lrmw_fastrtps_cpp
/usr/bin/ld: cannot find -lrcl
collect2: error: ld returned 1 exit status
Makefile:48: recipe for target '/home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/priv/rclex.so' failed
make: *** [/home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/priv/rclex.so] Error 1
could not compile dependency :rclex, "mix compile" failed. You can recompile this dependency with "mix deps.compile rclex", update it with "mix deps.update rclex" or clean it with "mix deps.clean rclex"
==> rclex_samples
** (Mix) Could not compile with "make" (exit status: 2).
You need to have gcc and make installed. If you are using
Ubuntu or any other Debian-based system, install the packages
"build-essential". Also install "erlang-dev" package if not
included in your Erlang/OTP version. If you're on Fedora, run
"dnf group install 'Development Tools'".
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$
コンパイルは通りましたが、こんどはライブラリがないようです。上と同様にシンボリックリンクを貼ってしのぐことにします。
% cd ~/ros2_dashing/install
% mkdir lib
% cd lib
% ln -s ../rcl/lib/librcl.so librcl.so
(
libfastcdr.so librosidl_generator_c.so
libfastrtps.so librosidl_typesupport_c.so
librcl.so librosidl_typesupport_introspection_c.so
librcutils.so libstd_msgs__rosidl_generator_c.so
librmw_fastrtps_cpp.so libstd_msgs__rosidlgenerator_c.so
librmw.so libstd_msgs__rosidl_typesupport_c.so
について同様にします。
)
ちょっと見にくいですがこのようになります。
これでコンパイルが通ります。やりました!
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$ mix deps.compile rclex
==> rclex
(..省略..)
gcc -o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/priv/rclex.so -L/usr/lib/erlang/usr/lib -g -shared /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/total_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/init_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/node_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/publisher_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/subscription_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/wait_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/msg_int16_nif.o /home/kikuzo/ddstest2/rclex_samples/_build/dev/lib/rclex/obj/msg_string_nif.o -L/home/kikuzo/ros2_dashing/install/lib -lrcl -lrmw -lrcutils -lrosidl_generator_c -lrosidl_typesupport_c -lrosidl_typesupport_introspection_c -lstd_msgs__rosidl_generator_c -lstd_msgs__rosidl_typesupport_c -lfastcdr -lfastrtps -lrmw_fastrtps_cpp
Compiling 6 files (.ex)
warning: redefining @doc attribute previously set at line 435.
Please remove the duplicate docs. If instead you want to override a previously defined @doc, attach the @doc attribute to a function head:
@doc """
new docs
"""
def initialize_msgs(...)
lib/rclex.ex:445: Rclex.initialize_msgs/2
Generated rclex app
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$
動かしてみる
rclex_samplesにある、simple_pubを使ってみましょう。
ターミナルを2つ起動し、一つでrclex_samples/simple_pubを動かして送信しし、もう一つはROS2のコマンドでsubscriberを動かして受信してみます。
kikuzo@kikuzo-VirtualBox:~/ddstest2/rclex_samples$ iex -S mix
Erlang/OTP 24 [erts-12.0] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-threads:1] [jit]
Interactive Elixir (1.11.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> RclexSamples.SimplePub.
callback/1 pub_main/1
iex(1)> RclexSamples.SimplePub.pub_main 1
finished rcl_init
publish message:hello,world
publish ok
実際の様子。
受信側はこうですね。
なお、iexコマンドの起動に先立って、ros2環境のセットアップ(setup.bashの実行)をしておかないと実行時にエラーが出ます。
まとめ
Rclexを動かしてみました。DDSでメッセージを送受信するだけですが、とりあえず動いていることは確認できました。
みなさんも、いろいろ試してみて、様子を教えて下さい。