2022年8月28日日曜日

dockerを通してubuntu22.04でros1のrosserialを動かす


背景

ros1はubuntu20.04向けのnoeticが最後なようでubuntu22.04にはaptでインストール可能なものがありませんが、dockerのros1のコンテナを使えばubuntu22.04でもros1を使えました。
備忘録を兼ねてrosserial-arduinoの動作確認手順を記事にします。

使ったもの


ESP32をrosserial端末として準備

subscriberで名前を変えられ、publisherで「名前+起動ミリ秒」を配信するrosserial端末としてESP32を動かします。

この記事では~/pio/esp32-rosserial-testというディレクトリ(PlatformIOのプロジェクト)を作り、その中に必要なファイルを配置してESP32に書き込みます。
cppファイルは更にその中のsrcディレクトリ内に置くので、srcディレクトリも一緒に作ります。
mkdir -p ~/pio/esp32-rosserial-test/src

コードは古いですがライブラリとして公開されているrosserialを使います。
今回は複雑なことはしないのでライブラリが古くても動きます。
~/pio/esp32-rosserial-test/platformio.ini
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
rosserial_arduino

device_name_subで名前を変えられ、device_hello_pubで「hello by [名前] at [ミリ秒]」を配信するrosserial-arduinoのプログラムです。
~/pio/esp32-rosserial-test/src/main.cpp
#include <Arduino.h>
#include <ros.h>
#include <std_msgs/String.h>

String deviceName = "asdf";
void cbSubDeviceName(const std_msgs::String& ms) { deviceName = ms.data; }

ros::NodeHandle nh;
String nameTopicSubDevice = "device_name_sub";
ros::Subscriber<std_msgs::String> subDeviceName(nameTopicSubDevice.c_str(),
&cbSubDeviceName);
String nameTopicPubDeviceHello = "device_hello_pub";
std_msgs::String msDeviceHello;
ros::Publisher pubDeviceHello(nameTopicPubDeviceHello.c_str(), &msDeviceHello);

void setup() {
nh.getHardware()->setBaud(115200);
nh.initNode();
nh.advertise(pubDeviceHello);
nh.subscribe(subDeviceName);
}

void loop() {
nh.spinOnce();
String strToPub = "hello by " + deviceName + " at " + String(millis());
msDeviceHello.data = strToPub.c_str();
pubDeviceHello.publish(&msDeviceHello);
delay(10);
}

上記の設定とプログラムをESP32に書き込みます。
ESP32開発ボードをPCに接続し、下記のコマンドを実行すると書き込めます。
cd ~/pio/esp32-rosserial-tset
pio run -t upload

USBポートを指定しつつdockerコンテナを起動

下記の設定ファイルを置いたディレクトリでdocker composeを通して起動するとコンテナのros1が起動します。

この記事では~/docker/ros1-testというディレクトリを作り、その中にファイルを置いてdocker composeを実行します。
mkdir -p ~/docker/ros1-test

noeticのコンテナにrosserialをインストールし、コンテナ起動時にroscoreを実行するDockerfileです。
~/docker/ros1-test/Dockerfile
FROM ros:noetic

RUN apt update && apt install -y ros-${ROS_DISTRO}-rosserial

CMD ["roscore"]

先程作成したDockerfileを利用してコンテナを起動し、それのdeviceとしてESP32の開発ボードのポートである/dev/ttyUSB0をコンテナに設定しているdocker-comoseファイルです。
これだけならdocker-compose.ymlを使わなくても良いですが、都度USBポートを指定するのが手間なのと他のコンテナと連携時はdocker-composeが必要になってくるので、あえて使いました。

USBを開くには特権(privileged)設定を有効にするか、deviceとしてusbポートを読み込ませて制限なく扱える状態にする必要があります。

privilegedを有効にして/devディレクトリを渡す書き方はこちらです。
~/docker/ros1-test/docker-compose.yml
version: "3.9"

services:
ros1:
build: .
privileged: true
volume:
- /dev:/dev

privileged権限を指定したポート(下記の場合はttyUSB0)だけに設定する書き方はこちらです。
~/docker/ros1-test/docker-compose.yml
version: "3.9"

services:
ros1:
build: .
devices:
- /dev/ttyUSB0:/dev/ttyUSB0

Dockerfileとdocker-compose.ymlを作成したディレクトリでdocker composeを通してコンテナを起動します。
cd ~/docker/ros1-test
docker compose up

実行後にエラーが出て無ければ起動成功です。


初回起動時はコンテナのダウンロードとコンテナへのrosserialのインストールを行うので時間がかかります。
自分の環境だと5分ほどかかりました。

setup.bashを読み込みつつコマンドを実行

コンテナが起動したらrosserial-pythonを通してUSBポート(/dev/ttyUSB0)を115200bpsで開きます。
docker compose exec bashで立ち上がるコマンド実行画面はrosのsetup.bashが読み込まれていないため、それを呼び出してからrosのコマンドを実行します。
これはdocker compose upを実行したのとは別のターミナルを起動して実行します。
cd ~/docker/ros1-test
docker compose exec ros1 bash -c "source /opt/ros/noetic/setup.bash && rosrun rosserial_python serial_node.py _port:=/dev/ttyUSB0 _baud:=115200"
または
cd ~/docker/ros1-test
docker compose exec ros1 bash
source /opt/ros/noetic/setup.bash
rosrun rosserial_python serial_node.py _port:=/dev/ttyUSB0 _baud:=115200

下記のエラーで失敗する場合は、deviceでポートを指定するかprivilegedを有効にしてください。(先程のdocker-compose.ymlの解説で書き方の例を出しています。)
[ERROR] [1674892372.324435]: Error opening serial: [Errno 1] could not open port /dev/ttyUSB0: [Errno 1] Operation not permitted: '/dev/ttyUSB0'

成功するとdevice_hello_pubとdevice_name_subが認識されます。


別のターミナルでdevoce_hello_pubの内容を確認します。
cd ~/docker/ros1-test
docker compose exec ros1 bash -c "source /opt/ros/noetic/setup.bash && rostopic echo /device_hello_pub"

期待通りに配信されました。
---
data: "hello by asdf at 21197"
---
data: "hello by asdf at 21208"
---
data: "hello by asdf at 21220"
---
data: "hello by asdf at 21230"
---
data: "hello by asdf at 21241"

device_name_subを通して名前をhoiに変えてみます。
cd ~/docker/ros1-test
docker compose exec ros1 bash -c "source /opt/ros/noetic/setup.bash && rostopic pub -1 /device_name_sub std_msgs/String \"hoi\""

device_hello_pubを確認すると期待通りに名前がasdfからhoiに変わりました。
---
data: "hello by hoi at 241659"
---
data: "hello by hoi at 241670"
---
data: "hello by hoi at 241681"
---
data: "hello by hoi at 241692"
---
data: "hello by hoi at 241703"
---
data: "hello by hoi at 241714"

publisherもsubscriberも期待通りに動きました。

終わり

ros1をaptではインストールできないubuntu22.04でdockerを利用してros1のrosserialを動かせました。
ros1からros2へ移行できていないプログラムをubuntu22.04で動作確認できて嬉しいです。

参考


ros - Official Image | Docker
ROS複数Docker通信
ROS Docker Setup
Is ROS already sourced if I do it from a dockerfile?
micro-ROS / micro_ros_arduino
devices | Compose file version 3 reference
[自分用]docker-compose.yamlテンプレ

変更履歴

2023/01/28 privileged: trueを設定するdocker-compose.ymlの例を追加しました。

0 件のコメント :