Введение в программирование в Операционной Системе для Роботов (ROS)
• Мы рассмотрим несколько примеров узлов C ++, обменивающихся данными в рамках ROS
• Повторим понятия узлов, тем и сообщений ROS.
• Более детально рассмотрим структуру хранилища rosbuild, а также создание и построение простого программного пакета, используя rosmak.
Общие сведения – краткое описание ROS
• ROS это пакет промежуточного программного обеспечения для программирования роботов, который содержит равноправные узлы.
• Мы используем ROS, потому что он позволяет облегчить абстракцию аппаратных средств и повторное использование кода.
• В ROS, все основные функции разбиваются на ряд блоков, которые обмениваются данными с помощью сообщений.
• Каждый блок называется узлом и, как правило, работает как отдельный процесс.
• Установление связи между узлами осуществляется ROS Мастером.
ROS Узлы
Анод представляет собой процесс, который выполняет некоторые вычисления. Обычно мы стараемся разделить всю функциональность программного обеспечения на различные модули – каждый работает при помощи одного или нескольких узлов. Узлы объединены в график и передают друг другу информацию с помощью разделов потоковой передачи данных, услуги «Удаленный вызов процедур» (Remote Procedure Call,RPC), а также сервера параметров. Эти узлы предназначены для работы в мелкоструктурном масштабе; система управления роботом обычно включает множество узлов.
ROS разделы
Разделами называют шины, через которые узлы обмениваются сообщениями. Разделы имеют анонимную семантику публикаций/подписок. Это значит, что для узла безразлично, какой узел опубликовал данные, которые он получает, или какой узел подписан на данные, публикуемые им. По одному разделу может быть несколько издателей и подписчиков. Нетрудно иметь несколько подписчиков. Каждый раздел очень типизирован ROS сообщениями, которые он передает. Передача выполняется с помощью TCP или UDP.
ROS сообщения
Узлы общаются друг с другом с помощью публикации сообщения к определенному разделу.
Сообщение – простая структура данных, содержащая введенные поля. Вы можете взглянуть на некоторые основные типы здесь:
std_msgs/Bool
std_msgs/lnt32
std_msgs/String
std_msgs/Empty
В данной статье мы будем рассматривать создание своих собственных сообщений. Сообщения могут также содержать специальное поле заголовка с именем, которое задает временную отметку и систему отсчета.
Получение кода образца
Эти учебные пособия основаны на пособиях для начинающих пользователей ROS. • Все сегодняшние учебные пособия доступны здесь: http://farnsworth.csres.utexas.edu/tutorials/
Используйте следующие команды для установки архива в вашей рабочей среде:
• roscd
• wget http://farnsworth.csres.utexas.edu/tutorials/intro_to_ros.tar.gz
• tar xvzf intro_to_ros.tar.gz
• rosws set intro_to_ros
• Crestart terminal>
• rosmake intro to ros
talker.cpp (intro_to_ros)
#include "ros/ros.h"
#include Mstd_msgs/String.h"
#include
int main(int argc, char **argv) {
ros::init(argc, argv, "talker"); ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(1) ;
int count = 0;
while (ros::ok()) {
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
listener.cpp (intro_to_ros)
#include "ros/ros.h"
#include Mstd_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr msg) {
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv) {
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub =
n.subscribe<std_msgs::String>("chatter", 1000, chatterCallback)
ros::spin() ;
return 0;
talker.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include
ros/ros.h это заголовок для удобства, включающий в себя большинство составляющих, необходимых для запуска системы ROS
std_msgs/string.h это тип сообщения, на которое нам нужно будет отреагировать в этом примере: вы должны будете включить другой заголовок, если вы хотите использовать другой тип сообщения
sstream несет ответственность за некоторые работы со строками в C++
talker.cpp
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::init отвечает за сбор ROS конкретной информации от аргументов, переданных в командной строке, он также присваивает имя нашего узла, Помните, что имена узлов должны быть уникальными в работающей системе. Мы видим пример такого аргумента в следующем примере.
Создание объекта ros::NodeHandle выполняет много заданий, это инициализирует узел, что делает возможной связь с другими узлами ROS и мастером в ROS инфраструктуре, позволяет взаимодействовать с узлом, который связан с этим процессом
talker.cpp
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(1);
NodeHandle::advertise несет ответственность за вызов XML/RPC к Мастеру ROS, который объявляет std_msgs:: строку, в разделе под названием "щебетание"
loop_rate используется для поддержания частоты публикации с частотой 1 Гц (т.е. 1 сообщение в секунду).
talker.cpp
int count = 0;
while (ros::ok()) {
count используется для отслеживания количества передаваемых сообщений. Его значение прикрепляется к строке сообщения, которое он публикует
ros::ok() гарантирует, что все по-прежнему в порядке в рамках ROS. Если что-то не так, то будет показано значение false, программа завершится. Примеры ситуаций, когда будет показано значение false:
Вы выполняете в программе операцию Ctrl+c (это расценивается как сбор секретной информации с помощью сигналов).
Вы открываете другой узел с тем же именем
Вы прописываете команду ros::shutdown () где-то в коде
talker.cpp
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
Эти 4 строки выполняют некоторые манипуляции со строками, чтобы установить счетчик внутри сообщения строки. Причина, по которой мы делаем это таким образом, – это то, что С ++ не имеет хорошего эквивалента функции toString()
msg.data является std::string
Примечание: я обычно использую boost::lexical_cast() вместо функции toString(). Iexical_cast() в значительной степени лучше выполняет для вас функцию, описанную выше (Изучите эту функцию, если вы заинтересовались)
talker.cpp
ROS_INFO("%s", msg.data.c_str());
chatter_pub. publish (msg) ;
ROS_INFO является макросом, который публикует информационное сообщение в экосистеме ROS. По умолчанию сообщения ROS_INFO также публикуются на экране.
Есть средства отладки в ROS, которые могут читать эти сообщения. Вы можете изменить данные о том, какой уровень сообщений должен быть опубликован
ros::Publisher::publish() отправляет сообщение всем подписчикам
talker.cpp
ros::spinOnce() ;
loop_rate.sleep() ;
++count;
ros::spinOnce() аналогична главной функции в инфраструктуре ROS.
Когда вы подписаны на один или несколько разделов, функции обратного вызова для получения сообщений по этим разделам не запрашиваются сразу. Вместо этого они помещаются в очередь, которая обрабатывается, когда вы делаете запрос ros::spinOnce()
Что произойдет, если мы удалим вызов spinOnce()?
ros::Rate.sleep() помогает поддерживать определенную частоту публикации, подсчет увеличивается, чтобы отслеживать сообщения
listener.cpp - в обратном порядке!
int main(int argc, char **argv) {
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub =
n.subscribe<std_msgs::String>("chatter", 1000, chatterCallback) ;
ros::spin();
return 0;
}
ros::NodeHandle::subscribe делает XML / RPC вызов мастеру ROS, это выполняет подписку на тему щебетание.
1000 – это размер очереди. В случае, если мы не в состоянии обрабатывать сообщения достаточно быстро. Это полезно только в случае нерегулярных периодов обработки сообщений. Почему?
Третий аргумент – это функция обратного вызова для определения того, что мы получаем сообщение
ros::spin() представляет собой удобную функцию, которая делает цикл вокруг ros:: spinOnce() во время проверки ros::ok()
listener.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr msg) {
ROS_INFO("I heard: [%s]M, msg->data.c_str());
}
те же заголовки, что и раньше
chatterCallback() является функцией, благодаря которой мы определяем, когда мы получаем сообщение на тему, которая есть в подписках, это имеет хорошо напечатанный аргумент.
Выполнение кода
Вы должны будете выполнить следующие шаги, чтобы заставить этот пример работать. После того, как вы загрузили наш код, постройте пример пакета rosmake intro_to_ros.
В отдельных терминальных окнах, выполните следующие программы:
• roscore
• rosrun intro_to_ros talker
• rosrun intro_to_ros listener
Пример 2 - Добавление узла передачи информации
Неоднократно в системе ROS вы будете иметь большое количество узлов, которые последовательно обрабатывают данные. Например, узел обнаружения сгустка определяет расположение сгустка для каждого изображения с камеры, которое он получает. Чтобы продемонстрировать это, мы изменим наш предыдущий пример следующим образом: представление узла передачи информации, который прослушивает сообщения на тему щебетание и направляет их на тему щебетание2. (Я не мог придумать более забавного название для этой темы). В командной строке происходит перераспределение, в результате которого слушатель подписывается на щебетание2 вместо щебетания.
messenger.cpp (intro_to_ros)
#include "ros/ros.h"
#include Mstd_msgs/String.h"
ros::Publisher chatter_pub;
std_msgs::String my_msg;
void chatterCallback( const std_msgs::String::ConstPtr msg) {
ROS_INFO("I heard: [%s]", msg->data.c_str());
my_msg.data = msg->data + ". Dont kill the messenger!";
chatter_pub.publish(my_msg);
}
int main(int argc, char **argv) {
ros::init(argc, argv, "messenger");
ros::NodeHandle n;
ros::Subscriber sub =
n.subscribe<std_msgs::String>("chatter", 1000, chatterCallback)
chatter_pub = n.advertise<std_msgs::String>("chatter2", 1000);
ros::spin();
return 0;
}
Выполнение кода
Вы должны будете выполнить следующие шаги, чтобы заставить этот пример работать.
В отдельных терминальных окнах, выполните следующие программы:
roscore
rosrun intro_to_ros talker
rosrun intro_to_ros listener chatter:=chatter2
rosrun intro_to_ros messenger
Repository (Хранилище): Содержит весь код из определенной группы разработки (У нас есть 3 хранилища из utexas)
Stack (Стек): Группирует весь код по конкретной теме/устройству
Packages (Пакеты): Отдельные модули, которые обеспечивают различные услуги
Nodes (Узлы): Исполняемые файлы, которые существуют в каждой модели (Вы это уже видели)
Инструменты командной строки – rospack
rospack – это программа командной строки, которая используется для поиска пакетов среди "совокупности деревьев" кода в типичном распределении ROS, рассчитывает зависимости, корректирует сборочные файлы, и вообще содействует миру и гармонии в распределении ROS. Некоторые примеры:
• rospack find intro_to_ros
• rospack list grep ros
• rospack depends intro_to_ros
Инструменты командной строки – rosstack
rosstack – это инструмент командной строки для получения информации о ROS стеках, доступных в файловой системе. Он реализует широкий спектр команд, начиная от местоположения ROS стеков в файловой системе до перечисления доступных стеков и расчета дерева зависимостей стеков. Некоторые примеры:
• rosstack contains intro_to_ros
• rosstack list-names grep examples
• rosstack depends art_examples.
Инструменты командной строки – roscd
roscd является частью rosbash комплекта. Это позволяет изменять каталог (т.е., cd) непосредственно в пакет или стек по имени вместо того, чтобы узнавать путь файловой системы. Некоторые примеры:
• rosed art_examples
• rosed intro_to_ros
• rosed intro_to_ros/src
rosbuild
rosbuild содержит сценарии для управления основанной на CMake системой сборки для ROS. 3 файла используются для построения вашего пакета ROS
CMakeLists.txt - стандартный CMake файл построения, но допускает ROS макросы.
manifest.xml – указывает ваши зависимости, а также предоставляет компиляторные и связующие флажки.
Makefile -1 простая строка, которая вызывает CMake. Вы никогда не должны изменять ее.
CMakeLists.txt
CMakeLists.txt является эквивалентом Makefile. Он используется cmake для построения кода.
Давайте взглянем на файл CMakeLists.txt для нашего intro_to_ros пакета. Есть ряд хороших примеров CMakeLists.txt: http://www.ros.ora/wiki/rosbuild/CMakeLists/ExamDles
Мы быстро увидим некоторые из параметров и функции, которые могут быть использованы в CMakeLists.txt
rosbuild флажки
ROS_BUILD_TYPE: Устанавливает тип сборки. Опции (по умолчанию: RelWithDeblnfo):
• Debug : w/ debug символы, w/o оптимизация
• Release : w/o debug символы, w/ оптимизация
• RelWithDeblnfo : w/ debug символы, w/ оптимизация
• Rel With Asserts : w/o debug символы, w / оптимизация, w/ утверждения (т.е. w/o -DNDEBUG). Новый в ros 1.1.
• MinSizeRel: w/o debug символы, w/оптимизация, очищенные двоичные файлы
• ROS_BUILD_STATIC_EXES: Только статические исполняемые файлы для построения (например, для копирования на другой машине)? true or false; по умолчанию: false
• ROS_BUILD_SHARED_LIBS: Build shared libs? true or false; по умолчанию: true
• ROS_BUILD_STATIC_LIBS: Build static libs? true or false; по умолчанию: false
• ROS_COMPILE_FLAGS: Компиляторные флажки по умолчанию для всех исходных файлов; по умолчанию: W -Wall -Wno-unused-parameter -fno-strict-aliasing"
• ROS_LINK_FLAGS: Связывающие флажки по умолчанию для всех исполняемых файлов и библиотек; по умолчанию:""
CMakeLists.txt (contd)
Основные ROS макросы, которые вы будете завершать с помощью:
• rosbuild_add_library
• создает библиотеку из данного файла C ++
• размещаетбиблиотеку по умолчанию в папке lib
• rosbuild_add_executable
• создает исполняемый файл из данного файла C ++ – должен иметь основной
• исполняемые файлы помещаются в папку bin
• target_link_libraries
• соединяет исполняемый файл в вашем пакете с библиотекой внутри одного и того же пакета
• не требуется для библиотек в других пакетах
• требуется для внешних библиотек.
manifest.xml
manifest.xml предоставляет информацию о зависимостях от системы rosbuild – intro_to_ros manifest.xml. Обеспечивает некоторую базовую документацию для пакета. Это хорошо для опубликованных пакетов. Например manifest.xml в ROS пакете “velodyne common” используется для автоматизации генерации раздела 1 на вики-странице.
• Обеспечивает системные зависимости пакета
• Обеспечивает другие зависимости пакета ROS
• компилятор экспорта и флажок компоновщика
Они используются, когда некоторый другой пакет ROS зависит от вашего пакета.
manifest.xml (contd)
• флаги компилятора
-Kpath to include directory>
• флаги компоновщика
-L
-Kiibrary name> (многократно для нескольких библиотек)
—wi, —rpath, $ {prefix} /lib (путь к динамически подключенным библиотекам)
Таким образом, заданный velodyne_common имеет эти строки. Он имеет библиотеку (velodyne) и систему зависимости (pcap):
-Wl,-rpath,${prefix}/lib -lvelodyne -lpcap"/>
Что такое rosmake?
rosmake является зависимым известным инструментом сборки для ROS пакетов и стеков. Некоторые общие случаи использования:
• rosmake – будет строить пакеты ROS вместе с ROS зависимостями
• rosmake – будет строить все пакеты в этом стеке
• rosmake --pre-clean – запускает функцию make clean && make на всех пакетах в дереве зависимостей
• rosmake --rosdep-install – устанавливает все необходимые зависимости системы
• Запуск: rosmake –help – увидеть все варианты
rosmake по сравнению с make
Чтобы сконструировать пакет, вы также можете перейти в этот каталог пакетов и напечатать make
• rosed intro_to_ros
• make
• make только построит пакет (т.е. не зависимости)
• make быстрее, чем rosmake
• полное дерево зависимостей не проверено
Я обычно использую rosmake при построении пакета впервые, или если мне что-то непонятно на счет зависимостях. После этого я использую make
Инструменты командной строки – roscreate-pkg
roscreate-pkg создает новый пакет в вашем текущем каталоге. Для этого, конечно, вы будете создавать только новые пакеты в каталоге spr12 внутри учебно-экспериментальной среды. Это автоматически генерирует стандартные файлы внутри пакета: CMakeLists.txt, Makefile, manifest.xml и mainpage.dox (не волнуйтесь о последнем). Пример:
• rosed spr12
• roscreate-pkg piyush_khandelwal_p2
Как написать пакет intro_to_ros
Создайте пакет
• rosed art_examples
• roscreate-pkg intro_to_ros
Внутри пакета создайте папку для содержания исходных файлов
• rosed intro_to_ros OR cd intro_to_ros
• mkdir src
Внутри каталога src напишите три файла:
• rosed intro_to_ros/src OR cd src
• gedit talker.cpp
• gedit messenger.cpp
• gedit listener.cpp
Как написать пакет intro_to_ros
Преобразуйте эти 3 файла в исполняемые файлы; Обновите CMakeLists.txt
• rosed intro_to_ros OR cd../
• gedit CMakeLists. txt
Используйте макрос rosbuild_add_executable, чтобы создать исполняемые файлы для этих трех файлов.
Запустите make; вы получите сообщение ошибки о том, что ros.h не был найден.
Обновите manifest.xml для добавления roscpp зависимости.
• gedit manifest.xml
Запустите make и продолжайте редактирование кода, чтобы решить вопросы компиляции и выполнения программы.
Обзор (продолжение)
С этим материалом, вы должны:
• Уметь создавать новые пакеты ROS
• Написать базовый код ROS, и уметь обновлять CMakeLists.txt и manifest.xml на основе вашего кода
• (Дополнительная возможность) Уметь писать библиотеки через систему построения ROS, которая будет использоваться вашим кодом и другими пакетами
• Использовать некоторые основные инструменты командной строки для перемещения по экосистеме ROS и отображать основную информацию о стеках и пакетах.
Подумайте о том, какие шаги удобны для вас. Обсудите с нами в рабочее время.