Закрыть
ROS на практике

ROS на практике

Давайте на практике разберем архитектуру ROS, изучим файловую систему ROS, рассмотрим некоторые концепции, инструменты и примеры взаимодействия с узлами, темами и сервисами ROS, а также посмотрим как все это работает в связке.

Я уже упоминал, что в архитектуре ROS можно выделить два основных концептуальных уровня: уровень файловой системы и уровень вычислительного графа.

Навигация по файловой системе ROS

В ROS есть несколько инструментов командной строки для навигации по файловой системе.

Для получения информации и перехода к пакетам и стекам используются инструменты rospac, rosstack, roscd и rosls.

rospack и rosstack используются для получения информации о пакетах и стеках, путях, зависимостях и т.д.

Например, если вы хотите найти путь к пакету turtlesim, необходимо написать в командной строке:

$ rospack find turtlesim

Вы получите в ответ следующее:

/opt/ros/indigo/share/turtles

То же самое происходит со стеками, установленными в системе. Пример:

$ rosstack find 'имя_стека'

Для получения списка файлов, внутри пакета или стека, используйте

$ rosls turtlesim

После этого вы увидите следующее:

cmake    images    msg     package.xml     srv

Если вы хотите перейти внутрь папки, то вам необходимо использовать roscd следующим образом:

$ roscd turtlesim
$ pwd

В ответ вы получите следующий новый путь:

/opt/ros/indigo/share/turtlesim

 

Создание собственного рабочего пространства

Перед тем как что-либо делать, необходимо создать собственное рабочее пространство. В этом рабочем пространстве у вас будет весь код, который вы создадите.

Чтобы посмотреть, какое рабочее пространство использует ROS, необходимо выполнить следующую команду:

$ echo $ROS_PACKAGE_PATH

Вы увидите что-то типа:

/opt/ros/indigo/share:/opt/ros/indigo/stacks

Папка, которую мы собираемся создать находится в /dev/roboschool/. Для добавления этой папки, используем следующие строки:

$ cd ~
$ mkdir -p dev/roboschool

Как только папка будет создана, нам необходимо добавить этот новый путь в ROS_PACKAGE_PATH. Чтобы это сделать, нам нужно только добавить новую строку в конец файла /baschrc:

$ echo "export ROS_PACKAGE_PATH="~/dev/roboschool:${ROS_PACKAGE_PATH}"" >>
~/.bashrc
$ . ~/.bashrc

Проверяем новое значение переменной окружения:

$ echo $ROS_PACKAGE_PATH

Теперь у нас есть новая папка, созданная и сконфигурированная для работы с ROS.

Создание пакета ROS

Как я уже говорил ранее, вы можете создать пакет вручную. Но для того, чтобы избежать скучной работы, мы используем инструмент командной строки roscreate-pkg.

Мы создадим новый пакет в подготовленной ранее папке, используя следующие строки:

$ cd ~/dev/roboschool
$ roscreate-pkg architect_tutorials std_msgs rospy roscpp

Формат этой команды включает имя пакета и зависимости, которые используются этим пакетом. В нашем случае, это std_msgs, rospy и roscpp. Это показано в следующей командной строке:

roscreate-pkg [package_name] [depend1] [depend2] [depend3]

Зависимости включают:

  • std_msgs — содержит общие типы сообщений, представленные примитивными типами данных и другими базовыми конструкциями сообщений, такими как многомерные массивы.
  • rospy — это клиентская библиотека Python для ROS
  • rospy — это C++ реализация ROS. Она обеспечивает клиентскую библиотеку, подключаемую программистами на C++ для быстроко интерфейса с темами, сервисами и параметрами ROS.

Если все сделано правильно, вы увидите на экране что-то типа этого:

создание пакета ROS

Как мы говорили ранее, вы можете использовать команды rospack, roscd и rosls для получения информации о новом пакете:

  • rospack find architect_tutorials — эта команда помогает нам найти путь
  • rospack depends architect_tutorials — эта команда позволяет посмотреть зависимости
  • rosls architect_tutorials — команда позволяет посмотреть содержимое
  • roscd architect_tutorials — меняет текущий путь

 

Компиляция пакета ROS

Как только ваш пакет создан, необходимо выполнить компиляцию пакета.

$ rosmake architect_tutorials

После выполнения операции, вы увидите что-то вроде:

Компиляция пакета ROS

Если не произойдет каких-то ошибок, пакет будет откомпилирован.

 

Работаем с узлами ROS

Узлы являются исполняемыми программами, и эти исполняемые файлы находятся в директории имя_пакета/bin. Чтобы попрактиковаться и изучить работу с узлами, мы используем пакет под названием turtlesim.

Перед тем, как начать работать, необходимо выполнить:

$ roscore

Результат выглядит примерно так:

Запуск ROS

Установим простенький симулятор если он не был установлен заранее (если при установке ROS вы выбрали вариант в котором устанавливаются все модули и пакеты, то этот шаг можно пропустить):

$ sudo apt-get install ros-indigo-ros-tutorials

Для получения информации об узлах существует инструмент rosnode. Чтобы посмотреть, какие у этой команды есть параметры, открываем новое окно Терминала и выполняем в нем команду:

$ rosnode

Вы увидите список возможных параметров, как показано ниже:

Параметры rosnode

Если вам требуется более подробное описание, как использовать эти параметры, используйте следующий формат:

$ rosnode имя_параметра -h

Теперь, когда roscore запущен, мы собираемся получить информацию о запущенных узлах:

$ rosnode list

Вы увидите, что выполняется только узел /rosout. Это нормально, потому что этот узел выполняется вместе с roscore.

Мы можем получить информацию об узле, используя различные параметры. Попробуйте использовать следующие команды для получения дополнительной информации:

$ rosnode info
$ rosnode ping
$ rosnode machine
$ rosnode kill

Запустим новый узел, используя rosrun:

$ rosrun turtlesim turtlesim_node

Должно открыться новое окно с маленькой черепашкой в центре.

Запуск turtlesim

Если теперь посмотреть на список узлов, то мы увидим в нем узел с именем /turtlesim.

Информацию об узле можно посмотреть, используя rosnode info имя_узла. Вы можете увидеть большое количество информации, которую можно использовать для отладки ваших программ:

$ rosnode info /turtlesim
Node [/turtlesim]
Publications:
 * /turtle1/color_sensor [turtlesim/Color]
 * /rosout [rosgraph_msgs/Log]
 * /turtle1/pose [turtlesim/Pose]

Subscriptions:
 * /turtle1/cmd_vel [unknown type]

Services:
 * /turtle1/teleport_absolute
 * /turtlesim/get_loggers
 * /turtlesim/set_logger_level
 * /reset
 * /spawn
 * /clear
 * /turtle1/set_pen
 * /turtle1/teleport_relative
 * /kill
contacting node http://hp-notebook:60243/ ...
Pid: 7997
Connections:
21
* topic: /rosout
  * to: /rosout
  * direction: outbound
  * transport: TCPROS

Здесь мы можем увидеть публикации (тем), описания (тем) и сервисы (srv), которые есть у узла, а также уникальные имена каждого.

Теперь давайте посмотрим, как взаимодействовать с узлами, используя темы и сервисы.

 

Взаимодействие с темами

Для взаимодействия и получения информации о темах используется инструмент rostopic, имеющий следующие параметры:

  • rostopic bw — Отображает ширину полосы данных, используемую темой
  • rostopic echo — Выводит сообщения на экран
  • rostopic find — Ищет темы по их типу
  • rostopic hz — Отображает скорость публикации тем
  • rostopic info — Выводит информацию об активных темах
  • rostopic list — Список активных тем
  • rostopic pub — Публикует данные в тему
  • rostopic type — Выводит тип темы

Если вы хотите посмотреть больше информаии об этих параметрах, используйте -h:

rostopic bw -h

Используя параметр pub, мы можем публиковать темы, на которые может подписаться любой узел. Нам нужно лишь опубликовать тему с корректным именем.

Используем узел, который необходим для управления. Запустим в новом окне Терминала:

$ rosrun turtlesim turtle_teleop_key

Мы можем двигать черепашку, используя клавиши со стрелками.

Управлените черепашкой в ROS

Как черепашка движется во время выполнения turtle_teleop_key?

Если вы хотите увидеть информацию об узлах /teleop_turtle и /turtlesim, мы можем посмотреть на следующий код, который находится в теме, под названием * /turtle1/ cmd_vel [geometry_msgs/Twist] в разделе Publications первого узла. В разделе Subscriptions второго узла есть * /turtle1/cmd_vel [geometry_msgs/Twist]:

$ rosnode info /teleop_turtle
Node [/teleop_turtle]
Publications:
* /turtle1/cmd_vel [geometry_msgs/Twist]
 ...
$ rosnode info /turtlesim
Node [/turtlesim]
...
Subscriptions:
 * /turtle1/cmd_vel [geometry_msgs/Twist]
...

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

turtlesim_node и узел turtle_teleop обмениваются информацией посредством тем ROS. turtle_teleop_key публикует клавиши перемещения в теме, в то время как turtlesim подписан на ту же самую тему для получения клавиш перемещения. Можно использовать rqt_graph, который показывает выполняющиеся в настоящий момент узлы и темы.

rqt_graph создает динамический граф того, что происходит в системе. rqt_graph является частью пакета rqt. Если rqt_graph еще не установлен в системе, выполните:

$ sudo apt-get install ros-indigo-rqt
$ sudo apt-get install ros-indigo-rqt-common-plugins

В новом окне Теринала выполним следующую команду:

$ rosrun rqt_graph rqt_graph

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

Окно rqt_graph

Если переместить указатель мыши на /turtle1/cmd_vel, то это выделит узлы ROS (здесь — синим и зеленым цветом) и темы (здесь — красным цветом). Как вы можете видеть, /teleop_turtle обменивается информацией с /turtlesim, используя тему /turtle1/cmd_vel.

Узлы rqt_graph

Используя параметр echo, мы можем посмотреть информацию, отправленную узлом. Выполните следующую команду и используйте клавиши со стрелками, чтобы посмотреть что за данные были отправлены:

$ rostopic echo /turtle1/cmd_vel

Вы увидите что-то похожее на следующее:

linear:
  x: -2.0
y: 0.0 z: 0.0
   angular:
  x: 0.0
  y: 0.0
  z: 0.0
---
   linear:
  x: 0.0
  y: 0.0
  z: 0.0
   angular:
  x: 0.0
  y: 0.0
  z: 2.0
---

Вновь вернемся с rqt_graph. В левом верхнем углу нажмите кнопку Обновить для отображения нового узла.

Новый узел rqt_graph

Как видно, показанный здесь красным rostopic echo, также теперь подписан на тему turtle1/cmd_vel.

Вы можете посмотреть список тем, на которые подписаны и которые опубликованы, используя следующую строку кода:

$ rostopic list
/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

Посмотреть тип сообщения, отправленного темой, можно используя следующую команду:

$ rostopic type /turtle1/cmd\_vel
geometry_msgs/Twist

Если вам необходимо посмотреть поля сообщения, выполните:

$ rosmsg show geometry_msgs/Twist
geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
   float64 x
   float64 y
   float64 z

Это полезные инструменты, потому что, используя эту информацию, мы можем публиковать темы, используя команду rostopic pub [topic] [msg_type] [args]

$ rostopic pub -1 /turtle1/cmd_vel geometry_msgs/
Twist -- '[3.0, 0.0, 0.0]' '[0.0, 0.0, 2]'

Вы увидите черепашку, создающую кривую.

Результат публикации темы

Эта команда отправляет единственное сообщение в turtlesim, в котором содержится информация о том, что необходимо двигаться с линейной скоростью равной 3.0 и угловой скоростью равной 2.0.

Разберем эту команду по частям:

  • rostopic pub — публикуем сообщение в данной теме
  • -1 — публиковать только одно сообщение, затем выйти
  • /turtle1/cmd_vel — имя темы, в которую публиковать
  • geometry_msgs/Twist — тип сообщения, который использовался при публи- кации темы
  • — — — двойное тире сообщает анализатору синтаксиса, что ни один из следующих аргументов не является параметром. Это необходимо в тех случаях, когда в аргумент входит дефис, например в отрицательных числах.
  • '[3.0, 0.0, 0.0]' '[0.0, 0.0, 2]' — сообщение geometry_msgs/Twist имеет два вектора: linear и angular, каждый из которых состоит из трех числовых элементов c плавающей точкой. В нашем случае, '[3.0, 0.0, 0.0]' становится линейным значением с x=3, y=0, z=0 и '[0.0, 0.0, 2]' является угловым значением с x=0, y=0, z=2. Эти аргументы имеют YAML- синтаксис.

 

Использование сервисов

Сервисы являются еще одним способом, посредством которого узлы могут обмениваться информацией друг с другом. Сервисы позволяют узлам отправлять запрос и получать ответ.

Для взаимодействия с сервисами используется инструмент rosservice. Параметры для этой команды:

  • rosservice args /service — Отображает аргументы сервиса
  • rosservice call /service — Вызывает сервис с представленными аргументами
  • rosservice find msg-type — Ищет сервисы по их типу
  • rosservice info /service — Выводит информацию о сервисе
  • rosservice list — Список активных сервисов
  • rosservice type /service — Отображает тип сервиса
  • rosservice uri /service — Выводит ROSPRC URI сервиса

Для получения списка доступных сервисов для узла turtlesim, используется следующий код. Если он не заработает, выполните roscore и запустите узел turtlesim.

$ rosservice list

Вы увидите следующее:

/clear
/kill
/reset
/rosout/get_loggers
/rosout/set_logger_level
/rostopic_9126_1412423515462/get_loggers
/rostopic_9126_1412423515462/set_logger_level
/rqt_gui_py_node_8979/get_loggers
/rqt_gui_py_node_8979/set_logger_level
/spawn
/teleop_turtle/get_loggers
/teleop_turtle/set_logger_level
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtle2/set_pen
/turtle2/teleport_absolute
/turtle2/teleport_relative
/turtlesim/get_loggers
/turtlesim/set_logger_level

Рабочий стол при этом выглядит примерно таким образом:

Внешний вид рабочего стола при работе ROS

Для того, чтобы посмотреть тип любого сервиса, например, сервиса /clear

$ rosservice type /clear

Вы увидите:

std_srvs/Empty

Для вызова сервиса используйте rosservice call [service] [args]. Если вы хотите запустить сервис /clear:

$ rosservice call /clear

В окне turtlesim вы увидите, что линии, созданные движением черепашки будут удалены.

Теперь попробуйем другой сервис, например, сервис /spawn. Этот сервис создаст другую черепашку в другом месте, с другой пространственной ориентацией.

$ rosservice type /spawn | rossrv show

Увидим следующее:

float32 x
float32 y
float32 theta
string name
---
string name

Используя эти поля, мы знаем как запустить сервис. Нам необходимы координаты x и y, ориентация theta и имя новой черепашки:

$ rosservice call /spawn 3.0 3.0 0.2 "new_turtle"

Увидим следующий результат:

Создание новой черепашки в ROS

Использование сервера параметров

Сервер параметров используется для хранения данных, доступных любым узлам. В ROS имеется инструмент для управления сервером параметров под названием rosparam. Параметры:

  • rosparam set parameter value — устанавливает параметр
  • rosparam get parameter — получает параметр
  • rosparam load file — загружает параметры из файла
  • rosparam dump file — выгружает параметры в файл
  • rosparam delete parameter — удаляет параметр
  • rosparam list — список имен параметров

Например, мы можем посмотреть параметры на сервере, которые используются всеми узлами:

$ rosparam list

На выходе получим:

/background_b
/background_g
/background_r
/rosdistro
/roslaunch/uris/host_hp_notebook__48988
/rosversion
/run_id

Параметры background принадлежат узлу turtlesim. Эти параметры изменяют цвет окон, который изначально голубой. Если вы хотите считать значение, используйте параметр get:

$ rosparam get /background_b

Для установки нового значения, используйте параметр set:

$ rosparam set /background_b 100

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

$ rosservice call clear

Если нам нужно посмотреть содержимое всего сервера параметров, то для этого используется команда rosparam get /:

$ rosparam get /

Другим важным свойством rosparam является параметр dump. Используя этот параметр, мы можем загружать или сохранять содержимое сервера параметров.

Для сохранения сервера параметров используйте rosparam dump [file_name] [пространство_имен]:

$ rosparam dump save.yaml

Для загрузки файла с новыми данными на сервер параметров используйте rosparam load [file_ name] [пространство_имен]:

$ rosparam load load.yaml пространство_имен

Пример: загрузим сохраненный сервер параметров в новое пространство имен, например copy:

$ rosparam load save.yaml copy
$ rosparam get copy/background_b

 

Создание узлов

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

$ roscd architect_tutorials/src/

Создадим файл example1_a.cpp, который будет посылать данные по имени узла и файл example1_b.cpp, который будет показывать данные в оболочке.

Скопируйте следующий код в файл example1_a.cpp:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include "sstream"
int main(int argc, char **argv)
{
  ros::init(argc, argv, "example1_a");
  ros::NodeHandle n;
  ros::Publisher chatter_pub = n.advertise<std_msgs::String>("message", 1000);
  ros::Rate loop_rate(10);
  while (ros::ok())
  {
    std_msgs::String msg;
    std::stringstream ss;
    ss << " I am the example1_a node ";
    msg.data = ss.str();
    //ROS_INFO("%s", msg.data.c_str());
    chatter_pub.publish(msg);
    ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

Несколько пояснений к коду. В заголовке подключаются библиотеки ros/ros.h, std_msgs/String.h и sstream. Здесь ros/ros.h включает все необходимые файлы для использования узла с ROS и std_msgs/String.h включает заголовок, который обозначает тип сообщения, которое мы собираемся использовать:

#include "ros/ros.h"
#include "std_msgs/String.h"
#include

Инициализируем узел и присваиваем ему имя, которое должно быть уникальным:

ros::init(argc,argv,"example1_a");

Следом идет обработчик нашего процесса:

ros::NodeHandle n;

Устанавливаем издателя и сообщаем мастеру имя и тип темы. Имя message и второй параметр является размером буфера. Если тема быстро публикует данные, буфер будет содержать 1000 сообщений:

ros::Publisher chatter_pub = n.advertise<std_msgs::String>("message", 1000);

Установим частоту отправки данных, в этом случае, равную 10 Гц:

ros::Rate loop_rate(10);

Библиотека ros::ok () останавливает узел, если он принимает нажатие клавиш Ctrl + C или если ROS останавливает все узлы:

while (row::ok())
{

Далее создается переменная для сообщения с корректным типом отправляемых данных:

std_msgs::String msg;
std::stringstream ss;
ss << " I am the example1_a node ";
msg.data = ss.str();

Публикация сообщения:

chatter_pub.publish(msg);

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

ros::spinOnce();

Необходимое время для паузы, чтобы получить частоту 10 Гц:

loop_rate.sleep();

Скопируйте следующий код в файл example_b.cpp:

#include "ros/ros.h"
#include "std_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, "example1_b");
  ros::NodeHandle n;
  ros::Subscriber sub = n.subscribe("message", 1000, chatterCallback);
  ros::spin();
  return 0;
}

Некоторые пояснения по поводу представленного кода. Подключаем заголовки и тип сообщения для использования в теме:

#include "ros/ros.h"
#include "std_msgs/String.h"

Эта функция вызывается каждый раз, когда узел получает сообщение. Это то место, где мы делаем что-то с данными. В этом случае, мы показываем их в оболочке:

void messageCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

Создаем подписчика и «слушаем» тему с именем message. Буфер будет 1000 и функция-обработчик сообщения будет messageCallback:

ros::Subscriber sub = n.subscribe("message", 1000, messageCallback);

Библиотека ros::spin () является циклом, где узел начинает читать тему и затем приходит сообщение, называемое messageCallback. Когда пользователь нажимает Ctrl + C, узел выходит из цикла и цикл завершается:

ros::spin();

 

Компиляция узла

Так как мы используем пакет architect_tutorials, нам нужно отредактировать файл CMakeLists.txt. Для этого вы можете использовать текстовый редактор или использовать инструмент rosed. Он открывает файл в редакторе Vim:

$ rosed architect_tutorials CMakeLists.txt

В конец файла копируем следующие строки кода:

rosbuild_add_executable(example1_a src/example1_a.cpp)
rosbuild_add_executable(example1_b src/example1_b.cpp)

Эти строки создадут два исполняемых файла в папке /bin.

Теперь для построения пакета и компиляции всех узлов используем инструмент rosmake:

$ rosmake architect_tutorials

Если ROS не запущена на вашем компьютере, используйте:

$ roscore

Вы можете проверить запущена ли ROS, используя команду rosnode list:

$ rosnode list

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

$ rosrun architect_tutorials example1_a
$ rosrun architect_tutorials example1_b

Если вы проверите оболочку, где запущен узел example1_b, вы увидите что-то типа:

...
[ INFO] [1355228542.283236850]: I heard: [ I am the example1_a node ]
[ INFO] [1355228542.383221843]: I heard: [ I am the example1_a node ]
[ INFO] [1355228542.483249861]: I heard: [ I am the example1_a node ]
...

Все что происходит, проиллюстрировано ниже на рисунке. Узел example_1 публикует тему message и узел example_2 подписан на тему.

Компиляция узла ROS

Вы можете использовать rosnode и rostopic для отладки и контроля за тем, что делает узел.

Попробуйте следующие команды:

$ rosnode list
$ rosnode info /example1_a
$ rosnode info /example1_b
$ rostopic list
$ rostopic info /message
$ rostopic type /message
$ rostopic bw /message

 

Создание файлов msg и srv

msg и srv являются файлами, содержащие определения типов передаваемых данных и сами данные. ROS использует эти файлы для создания необходимого кода для нас.

В примере из предыдущего раздела «Компиляция узла» мы создали два узла стандартного типа mesaage. Теперь мы создадим пользовательские сообщения при помощи инструментов, имеющихся в ROS.

Во-первых, создадим новую папку msg в нашем пакете architect_tutorials и создадим новый файл architect_msg1.msg, добавив в него следующие строки:

int32 A
int32 B
int32 C

Теперь отредактируем CMakeLists.txt, удалим # из строки # rosbuild_genmsg (), и откомпилируем пакет, используя rosmake:

$ rosmake architect_tutorials

Для проверки все ли нормально, можно использовать команду rosmsg:

$ rosmsg show architect_tutorials/architect_msg1

Если вы увидите то же содержимое что и в файле architect_msg1.msg, то все нормально.

Создадим файл srv. Для этого создадим новую папку с именем srv в папке architect_tutorials и создадим новый файл architect_srv1.srv, добавив следующие строки:

int32 A
int32 B
int32 C
---
int32 sum

Отредактируем CMakeList.txt и удалим # из строки # rosbuild_gensrv () и откомпилируем пакет, используя rosmake architect_tutorials.

Для проверки все ли нормально, используем инструмент rossrv:

$ rossrv show architect_tutorials/architect_srv1

Если вы увидите то же содержимое, что и в файле architect_srv1.srv, то все нормально.

 

Использование новых файлов srv и msg

Теперь разберем как создать сервис и как использовать его в ROS. Наш сервис будет вычислять сумму трех чисел. Нам нужно два узла: клиент и сервер.

В пакете architect_tutorials создадим два новых узла с именами example2_a.cpp и example2_b.cpp. Файлы необходимо поместить в папку src.

В файл example2_a.cpp добавляем следующий код:

#include "ros/ros.h"
#include "chapter2_tutorials/chapter2_srv1.h"

bool add(chapter2_tutorials::chapter2_srv1::Request  &req,
chapter2_tutorials::chapter2_srv1::Response &res)
{
  res.sum = req.A + req.B + req.C;
  ROS_INFO("request: A=%ld, B=%ld C=%ld", (int)req.A, (int)req.B,(int)req.C);
  ROS_INFO("sending back response: [%ld]", (int)res.sum);
  return true;
}
int main(int argc, char **argv)
{
  ros::init(argc, argv, "add_3_ints_server");
  ros::NodeHandle n;
  ros::ServiceServer service = n.advertiseService("add_3_ints", add);
  ROS_INFO("Ready to add 3 ints.");
  ros::spin();
  return 0;
}

Подключаем необходимые заголовки и созданный нами файл srv:

#include "ros/ros.h"
#include "architect_tutorials/architect_srv1.h"

Следующая функция добавляет три переменные и отправляет результат в другой узел:

bool add(architect_tutorials::architect_srv1::Request &req, architect_tutorials::architect_srv1::Response &res)

Далее создается сервис и оповещается ROS:

ros::ServiceServer service = n.advertiseService("add_3_ints", add);

В файл example2_b.cpp добавляем код:

#include "ros/ros.h"
#include "architect_tutorials/architect_srv1.h"
#include
int main(int argc, char **argv)
{
  ros::init(argc, argv, "add_3_ints_client");
  if (argc != 4)
  {
    ROS_INFO("usage: add_3_ints_client A B C ");
    return 1;
  }
  ros::NodeHandle n;
  ros::ServiceClient client = n.serviceClient<architect_tutorials::architect_srv1>("add_3_ints");
  architect_tutorials::architect_srv1 srv;
  srv.request.A = atoll(argv[1]);
  srv.request.B = atoll(argv[2]);
  srv.request.C = atoll(argv[3]);
  if (client.call(srv))
  {
    ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  }
  else
  {
    ROS_ERROR("Failed to call service add_3_ints");
    return 1;
  }
  return 0;
}

Создаем клиента для сервиса с именем add_3_ints:

ros::ServiceClient client = n.serviceClient<architect_tutorials::architect_srv1>("add_3_ints");

Здесь мы создаем экземпляр нашего файла srv и заполняем все значения для отправки. Сообщением имеет три поля:

architect_tutorials::architect_srv1 srv;
srv.request.A = atoll(argv[1]);
srv.request.B = atoll(argv[2]);
srv.request.C = atoll(argv[3]);

Этими строками кода вызывается сервис и отправляются данные. Если вызов прошел успешно, call () вернет true, если же нет — call () вернет false:

if (client.call(srv))

Для компиляции нового в файл CMakeList.txt добавим следующие строки:

rosbuild_add_executable(example2_a src/example2_a.cpp)
rosbuild_add_executable(example2_b src/example2_b.cpp)

Выполняем команду:

$ rosmake architect_tutorials

Для запуска узлов выполняем в двух оболочках следующие строки команд:

$rosrun architect_tutorials example2_a
$rosrun architect_tutorials example2_b 1 2 3

После этого, вы должны увидеть следущее:

Node example2_a
[ INFO] [1355256113.014539262]: Ready to add 3 ints.
[ INFO] [1355256115.792442091]: request: A=1, B=2 C=3
[ INFO] [1355256115.792607196]: sending back response: [6]
Node example2_b
[ INFO] [1355256115.794134975]: Sum: 6

Теперь создадим узлы, используя наш пользовательский файл msg. В качестве примера опять же берем example1_a.cpp, но уже с новым сообщением architect_msg1.msg.

Следующий фрагмент кода представляет содержимое файла example3_a.cpp:

#include "ros/ros.h"
#include "architect_tutorials/architect_msg1.h"
#include
int main(int argc, char **argv)
{
  ros::init(argc, argv, "example1_a");
  ros::NodeHandle n;
  ros::Publisher pub = n.advertise<architect_tutorials::architect_msg1>("message", 1000);
  ros::Rate loop_rate(10);
  while (ros::ok())
  {
    architect_tutorials::architect_msg1 msg;
    msg.A = 1;
    msg.B = 2;
    msg.C = 3;
    pub.publish(msg);
    ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

Ниже приведено содержимое файла example3_b.cpp:

#include "ros/ros.h"
#include "architect_tutorials/architect_msg1.h"
void messageCallback(const architect_tutorials::architect_msg1::ConstPtr& msg)
{
  ROS_INFO("I heard: [%d] [%d] [%d]", msg->A, msg->B, msg->C);
}
int main(int argc, char **argv)
{
  ros::init(argc, argv, "example1_b");
  ros::NodeHandle n;
  ros::Subscriber sub = n.subscribe("message", 1000, messageCallback);
  ros::spin();
  return 0;
}

Если мы запустим оба узла сейчас, то увидим что-то похожее:

...
[ INFO] [1355270835.920368620]: I heard: [1] [2] [3]
[ INFO] [1355270836.020326372]: I heard: [1] [2] [3]
[ INFO] [1355270836.120367449]: I heard: [1] [2] [3]
[ INFO] [1355270836.220266466]: I heard: [1] [2] [3]
...

[add_ratings]

5 thoughts on “ROS на практике

  1. Вы пишите

    >Несколько пояснений к коду. В заголовке подключаются библиотеки ros/ros.h, std_msgs/String.h и sstream.

    Но у вас в коде нет sstream, только пустой include на 3й строчке

    #include "ros/ros.h" #include "std_msgs/String.h" #include

    поэтому ошибка при компилировании получается, надо поправить на

    #include "ros/ros.h" #include "std_msgs/String.h" #include "sstream"

    1. Подобные ошибки еще в example2_a.cpp

      #include "chapter2_tutorials/chapter2_srv1.h" bool add(chapter2_tutorials::chapter2_srv1::Request &req, chapter2_tutorials::chapter2_srv1::Response &res)

      chapter2_srv1.h стоит вместо architect_srv1.h соответственно этот кусок кода из примера

      bool add(chapter2_tutorials::chapter2_srv1::Request &req, chapter2_tutorials::chapter2_srv1::Response &res)

      неверный и надо заменить на

      #include "ros/ros.h" #include "architect_tutorials/architect_srv1.h" bool add(architect_tutorials::architect_srv1::Request &req, architect_tutorials::architect_srv1::Response &res)

      Кстати, компилятор ругается на разрыв из 2х строк в bool add, который надо не забыть убрать после копирования.

      и в example2_b.cpp

      #include "ros/ros.h" #include "architect_tutorials/architect_msg1.h" #include

      3я строчка пустая, ее вообще надо убрать

  2. Лично у меня все получилось, но были следующие проблемы

    1. Я использовал export ROS_PACKAGE_PATH=:${ROS_PACKAGE_PATH}

    вместо вашего echo "export ROS_PACKAGE_PATH=":${ROS_PACKAGE_PATH}"" >> ~/.bashrc который у меня лично не работал

    2. Каждый раз, открывая новый терминал приходилось добавлять

    source /opt/ros/indigo/setup.bash

    export ROS_PACKAGE_PATH=:${ROS_PACKAGE_PATH}

    Хотелось бы настроить так, чтобы не вводить их постоянно.

    3. черепаха двигалась только когда окно терминала с выполненной командой rosrun turtlesim turtle_teleop_key было поверх остальных окон. Наверное, это тоже как-то можно исправить.

    еще у вас 2 раза стоит rospy вместо rospy и roscpp

    rospy — это клиентская библиотека Python для ROS

    rospy — это C++ реализация ROS. Она обеспечивает клиентскую библиотеку, подключаемую программистами на C++ для быстроко интерфейса с темами, сервисами и параметрами ROS.

    1. В окне с командой rosrun tuttlesim turtle_teleop_key работает приложение, которое считывает нажатые клавиши, поэтому оно и должно быть активным — это правильно (в фоновом режиме клавиши не считываются).

Оставить ответ

Ваш email не будет опубликован.Обязательны поля помечены *