ROS в Docker + Arduino: установка и настройка, подключение Arduino Mega в качестве узла

На текущий момент я работаю в последней версии ROS - Noetic Ninjemys. К сожалению, она официально поддерживает только версию Ubuntu 20.04. Поэтому, столкнувшись с выбором после обновления системы до версии 22.04 LTS: либо собирать ROS 1 из исходников, либо попробовать развернуть ее в контейнере, остановился на последнем. К тому же, было интересно поработать с Docker - как-то раньше не доводилось.

С установкой docker проблем возникнуть не должно. Примеры для docker будут без sudo (инструкция по настройке прав пользователя). Если все работает, то в ответ на запуск тестового контейнера:

docker run hello-world

docker выдаст сообщение о своей успешной установке:

На вики ros есть целый раздел по работе c ROS из docker

Для себя я выбрал официальный образnoetic-robot. Для его установки достаточно обной каманды:

$ docker pull ros:noetic-robot

После скачивания образа, контейнер для работы с ros запускается командой:

docker run -it ros:noetic-robot

В результате чего становится доступна консоль контейнера (буду обозначать ее "ctr:$"), в ней и запускается ros:

ctr:$ roscore

в ответ появится стандартное приветствие, в котором указаны параметры ros:

Исходно, в образе noetic-robot пакеты rosserial_arduino и rosserial, необходимые для работы с arduino не установлены. Их установка в контейнере производится обычным способом с помощью apt (sudo не требуется, т.к. сеанс контейнера запускается с пользователем root):

ctr:$ apt update

ctr:$ apt install ros-noetic-rosserial-arduino

ctr:$ apt install ros-noetic-rosserial

После установки добавится несколько новых пакетов, получить их список можно камандой:

ctr:$ rospack list | grep rosserial

Тут сразу необходимо сделать замечание, что все изменения в контейнере будут доступны только во время текущего сеанса, если выйти и заново запустить контейнер, то установленных ранее пакетов в нем не окажется. Для сохранения изменений необходимо сделать commit в образ. В новом окне терминала нужно проверить ID запущенного контейнера:

docker ps

после чего сделать коммит в текущий образ или создать новый (я буду использовать текущий):

$ docker commit 0059faf98a62 ros:noetic-robot

При следующем запуске пакет rosserial_arduino будет доступен.

Теперь необходимо сделать подготовку Arduino Mega: подключить, определить порт, залить тестовый скетч и проверить связь через последовательный порт - в рамках основной системы.

Порт легко определяется в Arduino IDE - в моем случае, это "/dev/ttyACM0". Для проверки работы через com-port удобно воспользоваться примером SerialEvent из стандартной библиотеки:

Arduino с ним делает эхо, отправляя в ответ принятую строку, при получении символа перехода на новую строку. 

Если при попытке прошить скетч появится ошибка доступа к serial port:

    avrdude: ser_open(): can't open device "/dev/ttyACM0": Permission denied

необходимо добавить текущего пользователя в группу dialout:

$ sudo usermod -a -G dialout ${USER}

и выполнить выход из системы, после повторного входа доступ должен появиться.

Проверить общение с платой после загрузки скрипта можно с помощью одной из терминальных программ (например, serial monitor, встроенный в Arduino IDE, или picocom / minicom). Либо напрямую отправляя строку в порт, для чего понадобится открыть два терминала, один для просмотра полученных данных:

$ cat /dev/ttyACM

второй для отправки строки в порт:

$ echo "HELLO WORLD" >> /dev/ttyACM0


Этот же метод удобно будет использовать для проверки связи с платой arduino из контейнера.

Для того, чтобы устройства, подключенные к ПК, стали доступны из контейнера, необходимо назначить ему привилегии для всех устройств или только для выбранных. В данном случае доступ к только к com-port для связи с arduino выглядит так:

docker run --device=/dev/ttyACM0 -it ros:noetic-robot

После запуска контейнера доступ к com-port "/dev/ttyACM0" становится общим как для основной системы (host), так и для контейнера (container). Это легко проверить, посылая строку в arduino из основной системы и принимая ответ в контейнере и наоборот:

Для того, чтобы передать строку в порт и получить ее в рамках только контейнера во втором терминале необходимо запустить процесс docker с ID исходного контейнера:

docker exec -it <container ID> bash

теперь работать с com-port можно внутри контейнера:

Связь с arduino есть - можно переходить к настройке на ней тестового узла и проверке работы с ros. Для этого необходимо сгенерировать библиотеку ros_lib для Arduino IDE в папке контейнера - например "/home":

ctr:$ cd /home

ctr:$ rosrun rosserial_arduino make_libraries.py .

Скрипт создаст папку ros_lib с библиотекой. Ее содержание должно быть примерно таким:


Далее необходимо скопировать папку с библиотекой "ros_lib" в подпапку "libraries" директории основной системы, указанной в качестве "Sketchbook location" в настройках Arduino IDE. В моем случае это "/home/alex/Dropbox/Projects/Arduino":


Для этого во втором терминале по ID или имени запущенного контейнера (по имени даже удобней, т.к. работает автодополнение) копируем с помощью docker:

$ docker cp <ID>:/home/ros_lib <arduino_libraries_dir>

После запуска Arduino IDE библитека ros_lib должна стать доступна для использования, в том числе, ее примеры появятся в меню Files -> Examples. Для проверки взаимодействия с ros используем пример Blink:

Если по каким-то причинам с помощью меню пример не открывается, его можно также открыть как обычный проект, он находится в папке "./ros_lib/examples/Blink":

Этот пример позволяет управлять встроенным светодиодом arduino с помощью сообщений в топике "toggle_led" типа "std_msgs::Empty". Для того, чтобы включать и выключать светодиод понадобится три консоли контейнера. В первой, с которой ранее работали, запускаем ros:

ctr:$ roscore

Во второй подключаемся к текущему контейнеру:

$ docker exec -it <ID> bash

и настраиваем окружение ros командой:

ctr:$ source ros_entrypoint.sh

Проверить, что во второй консоли есть доступ к основному процессу ros можно с помощью rostopic:

ctr:$ rostopic list

в списке активных топиков должы быть:

/rosout

/rosout_agg

Далее во второй консоли запускаем узел, обеспечивающий взаимодействие с arduino:

ctr:$ rosrun rosserial_python serial_node.py /dev/ttyACM0

Теперь необходимо запустить и настроить третью консоль контейнера (аналогично второй), в которой с помощью ручной публикации сообщений в топик "toggle_led" можно включать и выключать светодиод:

ctr:$ rostopic pub toggle_led std_msgs/Empty --once 


После каждой новой отправки сообщения состояние светодиода будет изменяться - arduino работает в качестве узла ros, при этом сам ros запущен в контейнере - именно такой результат и хотелось получить ☺

В библиотеке ros_lib также есть и другие примеры, позволяющие попробовать arduino в различных применениях, а их описание можно посмотреть в разделе tutorials пакета rosserial_arduino

Всем мира! Let`s go design! 

Комментарии