Основы работы с Robotic Operating System 2: скажи миру «Hello, world!»

        Доброй всем фазы вращения Земли!
        Сегодня мы продолжим знакомство с системой ROS, позволяющей легко и непринужденно создавать системы управления роботами. Для тех кто пропустил предыдущее занятие — примерный план работ.
    1. Установка, основные концепции
    2. Создаем свой пакет, знакомимся с сообщениями, простая программа
    3. Сервисы и параметры

        В прошлый раз мы установили, что базовым элементом ROS является пакет. Учебный процесс не может пройти мимо этого занимательного факта, так что достаньте лупу или микроскоп посильнее — будем изучать!

        В данном случае наиболее удобным будет построение своего пакета. Чтобы ничего не испортить, да и поддержать микрофлору ROS в хорошем состоянии, создадим отдельную директорию для всех экспериментов. В моем случае это будет ~/ros.

     . /opt/ros/electric/setup.sh
    cd
    mkdir ros
    cd ./ros
    export ROS_PACKAGE_PATH="~/ros:/opt/ros/electric/stacks"
    

        Последняя строка задает переменную окружения, хранящую пути к стандартному хранилищу пакетов (номер 2) и к пользовательским (в данном случае путь номер 1). Особо пытливый читатель может предположить, что мы создали пользовательский стэк. Увы, это будет ошибочное предположение — нам не хватает файла-манифеста stack.xml. Впрочем, это поправимо, но сейчас совершенно не нужно. А теперь самое время для таинства.

    Заверните, пожалуйста


        Пакеты создаются командой roscreate-pkg и вызывается она следующим образом:
    $ roscreate-pkg tutorial std_msgs rospy roscpp
     

        Первый аргумент — название пакета, затем следует список зависимостей (о них мы поговорим чуть ниже). Сейчас нужно наш пакет зарегистрировать при помощи уже известной по прошлому уроку команды rospack.
    rospack profile
    rospack find tutorial
    /home/crady/ros/tutorial 

        Вроде бы все идет хорошо. Но что будет, если запустить наш пакет на другой машине? А если там другая версия ROS? А если это, а потом сразу в-о-о-о-н то? Отслеживание зависимостей должно помочь со всем этим. ROS выделяет 2 вида связей между пакетами — первой очереди и все остальные. Деление сразу становиться понятным, если посмотреть в манифест нашего модуля:
     cat ./ros/tutorial/manifest.xml 
    

    <package>
      <description brief="tutorial">
    
         tutorial
    
      </description>
      <author>crady</author>
      <license>BSD</license>
      <review status="unreviewed" notes=""/>
      <url>http://ros.org/wiki/tutorial</url>
      <depend package="std_msgs"/>
      <depend package="rospy"/>
      <depend package="roscpp"/>
    
    </package>
    </source>
    

        Здесь в аттрибуте <depend> перечислены зависимости первой очереди. Все, что они требуют уже для себя — «косвенные» требования. Чтобы не лазить каждый раз в манифест, можно снова воспользоваться командой rospack:
    
    $ rospack depends1 tutorial
    std_msgs
    rospy
    roscpp
    
    $ rospack depends tutorial
    rospack
    roslib
    std_msgs
    rosgraph_msgs
    rosbuild
    roslang
    rospy
    cpp_common
    roscpp_traits
    rostime
    roscpp_serialization
    xmlrpcpp
    rosconsole
    roscpp
    

        Осталось совсем немного теории и мы перейдем к самому вкусному — написанию кода.

    Тут-то он мне и говорит...


        Предположим, что наш пакет готов и умеет… умеет… умеет вычислять энергию картинки с использованием MRF. Может быть когда-нибудь расскажу, как это и зачем, не в этом суть. Для того, чтобы в этом была какая-то польза, очевидно, наш исполняемый файл (как Вы помните с прошлого занятия, он называется узлом [node] в терминах ROS), должен на вход что-то получать (например, картинку) и что-то выдавать (допустим, число или даже ряд чисел). Так как у нас много самых разнообразных библиотек, есть смысл стандартизировать формат входных и выходных данных. Сделано это при помощи механизма сообщений (msg). Фактически, мы получаем следующую систему: издатель (Publisher) болтает с подписчиком (Subscriber) на определенную тему (Topic).
        Итак, у нас есть каркас будущего приложения и некоторые теоретические знания. Самое время нарастить на этих нелепых костях немного мяса и посмотреть, как же выглядят программы в ROS.

    And so you code...


        Издателя сообщений мы напишем на замечательном языке Python. Поидее, это должен был бы быть hello-world, но мы пойдем своим путем и изменим текст собщения! Перейдем в папку с нашим пакетом:
    $ cd $(rospack find tutorial)

        Создадим папку для скрипта:
    $ mkdir scripts

         И создадим в ней текстовый файл нехитрого содержания:
    $ vi ./scripts/talker.py

    #!/usr/bin/env python
    
    #Первым делом подгружаем манифест
    #Любой узел всегда начинается с этого
    import roslib
    roslib.load_manifest('tutorial')
    
    #Цепляем нужные нам библиотеки. rospy привязка ROS к Python, 
    #String - обертка над очевидным типом данных для формирования сообщений
    import rospy
    from std_msgs.msg import String
    def talker():
    #Создаем шину под названием chatter для публикации сообщений типа String
        pub = rospy.Publisher('chatter', String)
    #Говорим ROS, как называется наш узел
        rospy.init_node('talker')
    #Пока ROS работает, публикуем строку
        while not rospy.is_shutdown():
            str = "Zooo! %s"%rospy.get_time()
            rospy.loginfo(str)
            pub.publish(String(str))
            rospy.sleep(1.0)
    
    if __name__ == '__main__':
        try:
            talker()
        except rospy.ROSInterruptException: pass
    


        Так как это модуль-болтушка, необходимо указать формат сообщений, которые он публикует:
    $ echo "string str" > ./msg/Str.msg

        Поддерживаются следующие типы данных:
    • int8, int16, int32, int64 (и uint*)
    • float32, float64
    • string
    • time, duration
    • other msg files
    • Массивы переменной и фиксированной длины (array[] и array[C], соответственно)

        Все, что нужно, это просто перечислить нужные поля в текстовом поле, причем 1 строка соответствует одному параметру. «И все?», спросит читатель. Ну, в общем и целом да. Формирование кода msg берет на себя местный make. Кстати, и нам пора им заняться.
     $ vi ./CMakeLists.txt

        Все, что нужно — раскомментировать следующую строчку:
    rosbuild_genmsg()

        Это даст системе понять, что узел хочет поделиться с кем-нибудь информацией и нужно скомпилировать соответствующий код.
        Все, вводим в консоли
    $ make
    $ chmod +x ./scripts/listener.py

    и наслаждаемся. Да, Python здесь тоже требует make, собственно, для сообщений. chmod помечает файл как исполняемый (это важно!).
        Тем временем займемся собеседником на не менее замечательном языке C++. Да, механизм msgs позволяет без проблем и лишних телодвижений общаться совершенно разным узлам.
        Создадим новый пакет:
    $ roscreate-pkg tutorialSubscriber roscpp std_msgs
    $ cd tutorialSubscriber/
    $ vi ./src/listener.cpp

    #include "ros/ros.h"
    #include "std_msgs/String.h"
    
    void chatterCallback(const std_msgs::String::ConstPtr& msg)
      {
        //Печатаем сообщение
        ROS_INFO("There something on the line! [%s]", msg->data.c_str());
      }
    
    int main(int argc, char **argv)
      {
        //Создаем узел listener
        ros::init(argc, argv, "listener");
        ros::NodeHandle n;
        //указываем, что нужно слушать шину chatter, причем очередь сообщений у нас не более 1000, 
        //при получении нового - вызываем метод chatterCallback
        ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
        //spin начинает циклически вызывать наш callback до тех пор, пока ROS не прекратит работу
        ros::spin();
    
        return 0;
      }

        Осталось изменить немного make:
    vi ./CMakeLists.txt

        Нужно привести предпоследнюю строчку к следующему виду:
    rosbuild_add_executable(listener src/listener.cpp)

        Запускаем make и тут, но уже для компиляции исполняемого файла.
        Все, осталось оценить наши труды, открываем аж 4 терминала:
    1. roscore запустит master-процесс
    2. rosrun tutorial talker.py запустит Издателя
    3. rosrun tutorialSubcriber listener запустит подписчика
    4. rxgraph покажет нам схему соединения узлов

        На схеме видно, что текст сообщения будет печататься дважды — на узле listener и на /rosout, аналоге stdout.

    Тем временем...


        А тем временем вечеринка набирает обороты. Люди дошли до кондиции и решили сыграть партейку в бильярд. Но что-то пошло не так…
        
    Поделиться публикацией

    Похожие публикации

    Комментарии 4

      0
      А как же 3-й пункт «Сервисы и параметры»?

      И что делать, если нет какого-нибудь пакета, например, std_msgs или rospy или roscpp.
      У меня так же нет rxgraph. Как их правильно устанавливать?
      Стоит ROS Groovy на Ubuntu (x86) и на Arch (armv7h).
        0
        Простите.
        Пакеты std_msgs, rospy и roscpp были.
        Но нет rxgraph.
          0
          Добрый день. Существенно изменились жизненные обстоятельства, возникли другие задачи, так что сейчас я не могу уделать этому проекту досточного объема времени. Я вам в течение пары дней постараюсь ответить. если хотите — можно связаться через личку/почту.
            0
            Спасибо за ответ.
            Я нашел похожую тему тут.
            Немного разобрался как устанавливать. Бывают ошибки, но, помаленьку справляюсь с ними.

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

      Самое читаемое