Давайте на практике разберем архитектуру 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.
Если все сделано правильно, вы увидите на экране что-то типа этого:
Как мы говорили ранее, вы можете использовать команды rospack, roscd и rosls для получения информации о новом пакете:
- rospack find architect_tutorials — эта команда помогает нам найти путь
- rospack depends architect_tutorials — эта команда позволяет посмотреть зависимости
- rosls architect_tutorials — команда позволяет посмотреть содержимое
- roscd architect_tutorials — меняет текущий путь
Компиляция пакета ROS
Как только ваш пакет создан, необходимо выполнить компиляцию пакета.
$ rosmake architect_tutorials
После выполнения операции, вы увидите что-то вроде:
Если не произойдет каких-то ошибок, пакет будет откомпилирован.
Работаем с узлами ROS
Узлы являются исполняемыми программами, и эти исполняемые файлы находятся в директории имя_пакета/bin. Чтобы попрактиковаться и изучить работу с узлами, мы используем пакет под названием turtlesim.
Перед тем, как начать работать, необходимо выполнить:
$ roscore
Результат выглядит примерно так:
Установим простенький симулятор если он не был установлен заранее (если при установке ROS вы выбрали вариант в котором устанавливаются все модули и пакеты, то этот шаг можно пропустить):
$ sudo apt-get install ros-indigo-ros-tutorials
Для получения информации об узлах существует инструмент rosnode. Чтобы посмотреть, какие у этой команды есть параметры, открываем новое окно Терминала и выполняем в нем команду:
$ rosnode
Вы увидите список возможных параметров, как показано ниже:
Если вам требуется более подробное описание, как использовать эти параметры, используйте следующий формат:
$ rosnode имя_параметра -h
Теперь, когда roscore запущен, мы собираемся получить информацию о запущенных узлах:
$ rosnode list
Вы увидите, что выполняется только узел /rosout. Это нормально, потому что этот узел выполняется вместе с roscore.
Мы можем получить информацию об узле, используя различные параметры. Попробуйте использовать следующие команды для получения дополнительной информации:
$ rosnode info $ rosnode ping $ rosnode machine $ rosnode kill
Запустим новый узел, используя rosrun:
$ rosrun turtlesim turtlesim_node
Должно открыться новое окно с маленькой черепашкой в центре.
Если теперь посмотреть на список узлов, то мы увидим в нем узел с именем /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
Мы можем двигать черепашку, используя клавиши со стрелками.
Как черепашка движется во время выполнения 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
У вас должно появиться похожее окно:
Если переместить указатель мыши на /turtle1/cmd_vel, то это выделит узлы ROS (здесь — синим и зеленым цветом) и темы (здесь — красным цветом). Как вы можете видеть, /teleop_turtle обменивается информацией с /turtlesim, используя тему /turtle1/cmd_vel.
Используя параметр 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. В левом верхнем углу нажмите кнопку Обновить для отображения нового узла.
Как видно, показанный здесь красным 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
Рабочий стол при этом выглядит примерно таким образом:
Для того, чтобы посмотреть тип любого сервиса, например, сервиса /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 имеется инструмент для управления сервером параметров под названием 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 подписан на тему.
Вы можете использовать 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]
Вы пишите
>Несколько пояснений к коду. В заголовке подключаются библиотеки 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"
Подобные ошибки еще в 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я строчка пустая, ее вообще надо убрать
Спасибо большое, ошибки исправил.
Лично у меня все получилось, но были следующие проблемы
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
В окне с командой rosrun tuttlesim turtle_teleop_key работает приложение, которое считывает нажатые клавиши, поэтому оно и должно быть активным — это правильно (в фоновом режиме клавиши не считываются).