ROS (Robot Operating System)

ROS (Robot Operating System, операционная система для роботов) — это структура программной системы (фреймворк), предоставляющая функционал для распределенной работы по программированию роботов. ROS (под названием Switchyard) была первоначально разработана в 2007 году в Лаборатории искусственного интеллекта Стэнфордского университета.

При разработке робота обычно приходится реализовывать свою архитектуру, свой протокол обмена сообщениями, драйвер пульта управления, логику навигации и пр. И даже если имеется возможность использовать для этих задач различные готовые библиотеки, то все равно остается серьезная проблема — объединить их для робота в единую систему. Разработчики ROS позиционируют свою систему именно как операционную — для программ взаимодействия и управления роботом ROS играет роль операционной системы, предоставляя программам управления свои интерфейсы, библиотеки и готовые приложения. ROS работает под уже готовой OC (Ubuntu Linux), в которой реализует свой дополнительный слой абстракции — конкретно для управления роботами. ROS обеспечивает стандартные службы операционной системы, такие как аппаратную абстракцию, низкоуровневый контроль устройств, реализацию часто используемых функций, передачу сообщений между процессами и управление пакетами.

ROS развивается в двух направлениях: в качестве уже описанной здесь операционной системы и в виде поддерживаемых пользователями пакетов (ros-pkg), организованных в наборы (стеки), реализующие различные функции робототехники. Так, ROS содержит вспомогательные библиотеки и приложения для роботов: преобразование систем координат, утилиты для визуализации данных и распознавания объектов, стек навигации и многое другое. Реализованы для ROS и драйверы, позволяющие единым образом работать со многими устройствами: джойстиками, устройствами GPS, камерами, лазерными дальномерами и пр.

ROS основан на архитектуре графов, где обработка данных происходит в узлах, которые могут получать и передавать между собой сообщения (структурированные данные). Комбинируя готовые узлы ROS и, по необходимости, дописывая собственные, можно существенно сократить время разработки и позволить себе сконцентрироваться только на тех задачах, которые действительно нужно решить.

На данный момент под управлением ROS работает уже много роботов. Вот неполный список: PR2, TurtleBot, PR1, HERB, STAIR I и II, Nao, Husky A200, iRobot Create, Lego Mindstorms NXT.

ROS выпускается в соответствии с условиями лицензии BSD и c открытым исходным кодом. Она бесплатна для использования как в исследовательских, так и в коммерческих целях. Пакеты из ros-pkg распространяются на условиях различных открытых лицензий.

 

Дистрибутивы ROS

 

Самым первым дистрибутивом ROS был Box Turtle — релиз от 2 марта 2010 года.

C Turtle (релиз от 2 августа 2010 года) — вторая версия дистрибутива ROS, содержащая обновление библиотек, входящих в ROS Box Turtle.

Diamondback (релиз от 2 марта 2011 года) — третий дистрибутив ROS. Содержит новые стеки, включая поддержку сенсора Kinect, стабильный релиз библиотеки Point Cloud Library. Diamondback стал меньше, проще и более конфигурируем, чем ROS C Turtle.

Electric Emys (релиз от 30 августа 2011 года) — четвертый дистрибутив ROS. Он содержит стабильные версии библиотек arm_navigation и PCL, а также расширяет поддержку новых платформ, таких как Android и Arduino.

ROS Fuerte Turtle (релиз от 23 апреля 2012 года) — стал пятым дистрибутивом ROS, в нем произведены значительные улучшения, облегчающие интегрирование с другими системами программного обеспечения.

 

Установка ROS

 

Шаги по установке ROS Fuerte Turtle под Ubuntu Linux расписаны на официальном сайте системы http://www.ros.org/wiki/fuerte/Installation/Ubuntu. Рассмотрим ее установку на компьютер с операционной системой Ubuntu 12.04 (Precise).

Добавляем адрес сервера ROS, чтобы менеджер пакетов знал откуда брать пакеты ROS:

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu precise main" >

/etc/apt/sources.list.d/ros-latest.list'

Получаем ключ:

wget http://packages.ros.org/ros.key -O - | sudo apt-key add -

Обновляем список пакетов — тем самым сервер ROS.org будет проиндексирован:

sudo apt-get update

 

Отдаем команду установки ROS Fuerte (рекомендованная конфигурация Desktop- Full):

sudo apt-get install ros-fuerte-desktop-full

 

Разработчики ROS стремятся интегрировать в систему лучшие открытые робототехнические библиотеки, сохраняя при этом модульность системы, чтобы пользователь мог установить только те модули, которые ему действительно необходимы. Некоторые библиотеки вынесены из ROS и устанавливаются в ОС стандартным образом, что позволяет использовать эти библиотеки и без ROS. Установим необходимый нам пакет rosserial:

sudo apt-get install ros-fuerte-ros-comm

Отдельно необходимо установить и пакеты rosinstall и rosdep:

sudo apt-get install python-rosinstall python-rosdep

В начале новой сессии bash необходимо прописать установку переменных окружения ROS:

echo "source /opt/ros/fuerte/setup.bash" >> ~/.bashrc . ~/.bashrc

На этом установка закончена.

 

Узлы и темы в ROS

 

Узел — это исполняемый файл пакета ROS. Узлы ROS используют клиентские библиотеки ROS для связи с другими узлами. Клиентские библиотеки ROS позволяют реализовывать узлы ROS на различных языках программирования, например:

 Rospy — клиентская библиотека для Python;

 Roscpp — клиентская библиотека для С++;

 Rosjava — клиентская библиотека для Java.

Узлы могут публиковать сообщения по теме (publisher), а также подписаться на тему для приема сообщений (subscriber). Сообщения — тип данных, используемых для публикации или подписки на тему. Типы сообщений описываются в файлах сообщений msg — простых текстовых файлах с полем типа и полем имени в строке. Типы полей, которые можно использовать:

 

 int8;

 int16;

 int32;

 int64;

 float32;

 float64;

 string;

 time;

 duration;

 other msg files;

 variable-length array[];

 fixed-length array[C].

 

Файлы msg используются для генерации исходного кода для сообщений на разных языках. Файлы msg хранятся в подкаталоге msg каталога пакета.

 

Узлы могут также предоставлять или использовать службы (Service). Службы позволяют узлам послать запрос и получить ответ.

Файлы служб srv — такие же простые текстовые файлы, как и файлы msg, но они состоят из двух частей: запроса и ответа. Эти две части, разделяются линией: - - - . Пример файла srv:

int64 A int64 B

---

int64 Sum

В этом примере, A и В — это запрос, а Sum — это ответ. Файлы srv хранятся в подкаталоге srv каталога пакета.

Пакет rosserial

Библиотека rosserial устанавливает соединение точка-точка (point-to-point connection) через последовательный порт с недорогими контроллерами (типа Arduino) так, что вы можете посылать сообщения ROS туда и обратно.

Библиотека rosserial состоит из общего P2P-протокола, библиотеки для работы с Arduino и узлов для ПК.

Библиотека для работы с Arduino находится в папке проекта serial, в каталоге serial_arduino/libraries. Копируем папку ros_lib в библиотечный каталог libraries Arduino IDE.

 Подключение библиотеки ros_lib
 Подключение библиотеки ros_lib

 

 

Подготовка сообщения (publisher) на Arduino

 

Создадим скетч на Arduino, демонстрирующий создание узла ROS, публикующего сообщения в тему. Соединяем Arduino с компьютером, на котором запущена ROS по последовательному порту (в рассматриваемом случае это порт /dev/ttyUSB0). К контроллеру подключен датчик температуры DS18B20. Будем отправлять в ROS значения температуры с датчика, используя библиотеки OneWire.h и ros_lib.

В каждую программу ROS Arduino необходимо включить заголовочный файл ros.h и файлы заголовков для всех типов сообщений, которые мы будем использовать, — в нашем случае это std_msgs/Float32.h:

#include <ros.h>

#include <std_msgs/Float32.h>

Далее нам необходимо создать экземпляр объекта узла serial_node, что позволит нашей программе выступать в качестве подписчика (subscriber), либо публиковать сообщения (publisher):

ros::NodeHandle nh;

Создаем экземпляр publisher для нашего узла serial_node, публикующий сообщения типа std_msgs::Float32 в тему temperature:

std_msgs::Float32 float32_msg;

ros::Publisher chatter("temperature", &float32_msg);

В подпрограмме setup() необходимо инициализировать узел и объявить о роли узла chatter в качестве publisher:

nh.initNode(); nh.advertise(chatter);

В цикле loop() после считывания данных с датчика температуры публикуем сообщение в тему и вызываем ros::spinOnce(), где обрабатываются все функции обратного вызова соединения.

chatter.publish( &float32_msg ); nh.spinOnce();

Код данного скетча представлен в примере

 

#include <OneWire.h>

OneWire ds(10); // линия 1-Wire будет на pin 10

#include <ros.h>

#include <std_msgs/Float32.h>

 

ros::NodeHandle nh; std_msgs::Float32 float32_msg;

ros::Publisher chatter("temperature", &float32_msg);

void setup(void)

{

nh.initNode(); nh.advertise(chatter);

}

void loop(void)

{

byte i;

byte present = 0; byte data[12]; byte addr[8];

if ( !ds.search(addr)) { ds.reset_search(); return;

}

ds.reset(); ds.select(addr);

ds.write(0x44,1); // запускаем конвертацию delay(1000); // скорее всего достаточно 750 ms present = ds.reset();

ds.select(addr);

ds.write(0xBE); // считываем ОЗУ датчика

for ( i = 0; i < 9; i++) { // обрабатываем 9 байтов data[i] = ds.read();

}

Serial.print(" CRC=");

Serial.print( OneWire::crc8( data, 8), HEX); Serial.println();

// высчитываем температуру int HighByte, LowByte, Temp; float Tempf1,Tempf2; LowByte = data[0];

HighByte = data[1];

Temp = (HighByte << 8) + LowByte; Tempf1=Temp/16; Tempf2=(Temp%16)*100/16; float32_msg.data=Tempf1+Tempf2/100;

// публикуем сообщение chatter.publish( &float32_msg ); nh.spinOnce();

}

 

Теперь проверим работу данного скетча. Первое, что необходимо запустить, — это команда roscore. Команда rosnode отображает информацию об узлах ROS, которые работают в настоящий момент. Команда rosnode list выдает список этих активных узлов. В терминале увидим:

/rosout

 

Соответственно, есть только один работающий узел: rosout. Этот узел работает всегда, т. к. он собирает и логирует отладочные сообщения узлов.

Команда rosrun позволяет назначить имя пакета, чтобы непосредственно запустить его узел:

$ rosrun [package_name] [node_name]

Запускаем узел serial_node.py пакета rosserial_python, который соединяет нашу Arduino с остальной частью ROS. Необходимо выставить используемый последовательный порт:

rosrun rosserial_python serial_node.py /dev/ttyUSB0

В терминале набираем:

$ rosnode list

И смотрим список активных узлов:

/rosout

/serial_node

Утилита rxgraph, являющаяся частью пакета rxtools, позволяет визуально показать узлы и темы, запущенные в настоящий момент. Набираем в терминале:

$ rxgraph

 

Утилита rxgraph демонстрирует cписок активных узлов и тем
Утилита rxgraph демонстрирует cписок активных узлов и тем

 

 

Утилита rostopic позволяет получить информацию о темах ROS. Команда rostopic echo показывает данные, опубликованные в теме. Набираем в терминале:

$ rostopic echo /temperature

и видим постоянно поступающие с Arduino данные датчика температуры

Публикация сообщений из Arduino в тему temperature
Публикация сообщений из Arduino в тему temperature

 

Создание подписки (subscriber) на Arduino

 

Теперь рассмотрим пример использования Arduino в качестве узла subscriber для приема сообщений. В этом примере мы будем включать/выключать светодиод, подключенный к выводу 13 Arduino, получая сообщения из ROS.

Включаем заголовочный файл ros.h и файлы заголовков для всех типов сообщений, которые мы будем использовать, — в нашем случае это std_msgs/Empty.h (для пустых сообщений):

#include <ros.h>

#include <std_msgs/Empty.h>

Далее необходимо создать экземпляр объекта узла serial_node, что позволит нашей программе выступать в качестве подписчика (subscriber), либо публиковать сообщения (publisher):

ros::NodeHandle nh;

Создаем экземпляр subscriber для нашего узла, публикующий пустые сообщения типа std_msgs::Float32 в тему toggle_led:

ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );

 

Создаем для нашего узла функцию обратного вызова messageCb. Эта функция должна постоянно получать сообщение в качестве аргумента. Для нашей функции обратного вызова messageCb назначим тип сообщения std_msgs::Empty. По получении сообщения функция инвертирует значение сигнала на выводе 13 Arduino, при этом включая/выключая светодиод.

void messageCb( const std_msgs::Empty& toggle_msg){ digitalWrite(13, HIGH-digitalRead(13)); // blink the led

}

В подпрограмме setup() необходимо инициализировать узел и объявить о роли узла в качестве подписчика на сообщения:

 

nh.initNode(); nh.subscribe(sub);

 

И наконец, в цикле loop() вызываем ros::spinOnce(), где обрабатываются все функции обратного вызова соединения:

nh.spinOnce();

Код данного скетча представлен в примере.

 

#include <ros.h>

#include <std_msgs/Empty.h> ros::NodeHandle nh;

void messageCb( const std_msgs::Empty& toggle_msg){ digitalWrite(13, HIGH-digitalRead(13)); // blink the led

}

ros::Subscriber<std_msgs::Empty> sub("toggle_led", &messageCb );

void setup()

{

pinMode(13, OUTPUT); nh.initNode(); nh.subscribe(sub);

}

void loop()

{

nh.spinOnce(); delay(1);

}

Запускаем узел serial_node.py пакета rosserial_python, который соединяет нашу Arduino с остальной частью ROS. Необходимо выставить используемый последовательный порт (здесь использована другая плата Arduino, подключенная к порту ttyACM0):

rosrun rosserial_python serial_node.py /dev/ttyACM0

 

Переходим на компьютер с запущенной ROS и проверяем список активных узлов:

$ rosnode list

Смотрим результат:

/rosout

/serial_node

Наш узел запущен как подписчик на сообщения по теме toggle_led, но никаких сообщений он пока не получил. Связь по темам осуществляется путем отправки сообщений ROS между узлами. Для общения издателя и абонента издатель (publisher) и абонент (subscriber) должны отправлять и получать сообщения одинакового типа. Это означает, что тип темы определяется типом сообщений, которые в ней публикуются. Тип сообщения, отправляемого в тему, может быть определен с помощью команды rostopic type:

$ rostopic type toggle_led

Результат:

std_msgs/Empty

Теперь используем rostopic с сообщениями — rostopic pub публикует данные в тему:

rostopic pub [topic] [msg_type] [args]

Отправим единичное сообщение:

rostopic pub toggle_led std_msgs/Empty --once

Светодиод должен изменить значение на противоположное.

Для отправки сообщения в цикле (-r) с определенной частотой введем команду:

rostopic pub toggle_led std_msgs/Empty -r 1

Эта команда публикует сообщение с частотой 1 Гц в тему toggle_led. Светодиод будет мигать с частотой 2 раза в секунду.

Связь через ROS двух плат Arduino

 

Теперь создадим пример передачи сообщений через ROS между двумя платами Arduino. На одной плате Arduino, соединенной с ROS, находится датчик температуры DS18B20. Подключим к ROS по другому последовательному порту вторую плату Arduino, к которой подключен дисплей WH0802. На него мы и будем выводить показания температуры с датчика, расположенного на первой плате Arduino. Скетч для публикации показаний температуры у нас уже есть. Скетч для получения сообщений с показаниями температуры, публикуемыми в тему temperature, представлен в примере

 

// подключить библиотеку LiquidCrystal

#include <LiquidCrystal.h>

// создание экземпляра объекта LiquidCrystal LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// rosserial

#include <ros.h>

#include <std_msgs/Empty.h>

#include <std_msgs/Float32.h>

ros::NodeHandle nh;

void messageCb( const std_msgs::Float32& toggle_msg){ digitalWrite(13, HIGH-digitalRead(13)); // blink the led lcd.setCursor(0, 0);

lcd.print("Temp="); lcd.setCursor(0, 1); lcd.print(toggle_msg.data);}

ros::Subscriber<std_msgs::Float32> sub("temperature", &messageCb );

void setup() { lcd.begin(8, 2); pinMode(13, OUTPUT); nh.initNode(); nh.subscribe(sub);

}

void loop() { nh.spinOnce(); delay(1000);

}

Теперь посмотрим, как реализовать передачу в ROS. Запустить два узла serial_node с одним именем не получится. Одна из особенностей ROS состоит в том, что вы можете переназначить имена (Names) узлов из командной строки:

$ rosrun rosserial_python serial_node.py /dev/ttyUSB0 name:=serial1

$ rosrun rosserial_python serial_node.py /dev/ttyACM0 name:=serial2

Теперь посмотрим список активных узлов командой rosnode_list:

/rosout

/serial1

/serial2

А командой rxgraph посмотрим узлы и темы. При этом показания температуры отображаются на дисплее WH0802.

 

Утилита rxgraph — cписок активных узлов и тем
Утилита rxgraph — cписок активных узлов и тем

 

Вверх

 

Основные версии плат Arduino

Due — плата на базе 32-битного ARM микропроцессора Cortex-M3 ARM SAM3U4E;

Leonardo — плата на микроконтроллере ATmega32U4;

Uno — самая популярная версия базовой платформы Arduino;

Duemilanove — плата на микроконтроллере ATmega168 или ATmega328;

Diecimila — версия базовой платформы Arduino USB;

Nano — компактная платформа, используемая как макет. Nano подключается к компьютеру при помощи кабеля USB Mini-B;

Mega ADK — версия платы Mega 2560 с поддержкой интерфейса USB-host для связи с телефонами на Android и другими устройствами с интерфейсом USB;

Mega2560 — плата на базе микроконтроллера ATmega2560 с использованием чипа ATMega8U2 для последовательного соединения по USB-порту;

Mega — версия серии Mega на базе микроконтроллера ATmega1280;

Arduino BT — платформа с модулем Bluetooth для беспроводной связи и программирования;

LilyPad — платформа, разработанная для переноски, может зашиваться в ткань;

Fio — платформа разработана для беспроводных применений. Fio содержит разъем для радио XBee, разъем для батареи LiPo и встроенную схему подзарядки;

Mini — самая маленькая платформа Arduino;

Pro — платформа, разработанная для опытных пользователей, может являться частью большего проекта;

Pro Mini — как и платформа Pro, разработана для опытных пользователей, которым требуется низкая цена, меньшие размеры и дополнительная функциональность.