Turtle — killer turtles — игра в ROS


Начало весны, пора приниматься за недоделанные проекты.
Пока жду детали и вспоминаю ROS.
В качестве разминки (надеюсь после разминки будут более интересные проекты) — небольшая игра в ROS с использованием пакета turtlesim.

ROS — (Robot Operating System) Операционная система для роботов — это фреймворк для программирования роботов, предоставляющий функционал для распределённой работы. Хорошие мануалы (с которых я начинал изучение ROS) на русском языке на этом сайте — здесь
Последняя (восьмая) версия ROS — indigo.
Сначала установил ее на компьютер с операционной системой Ubuntu 14.04. (Инструкция по установке и настройке)
Пакет Turtlesim используют в примерах по изучению ROS. Запуск пакета

rosrun turtlesim turtlesim_node

Появляется окно, в котором расположена черепашка turtle, управлять которой можно отправкой сообщений в темы или сервисы. Есть также возможность управления движением черепашки с помощью клавиатуры.
На самом деле возможности пакета шире, можно создавать любое количество экземпляров черепашки, управлять их перемещением, устанавливать цвет и размер следа черепашки, цвет поля, отслеживать текущие значения координат и текущих скоростей черепашки.
Посмотреть описание пакета turtlesim можно здесь
Итак, игра на ROS c использованием пакета Turtlesim. Движением черепашки будем управлять с помощью геймпада Defender Game Racer X7.

Defender Game Racer X7 имеет 12 кнопок (включая D-Pad и 2 аналоговых джойстика), а также кнопки Turbo, Clear и Home. Устройство поддерживает вибрационную обратную связь, работающую при помощи 2-х вибромоторов. Подключение к компьютеру производится через интерфейс USB. Джойстик может работать в двух режимах, один из которых HID-устройство, другой – контроллер XBOX360 . Переключение производится с помощью кнопки Mode.
Проверяем, опознается ли данный геймпад системой. Набираем в терминале команду

ls /dev/input

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

Джойстик устройства называются по jsX, у меня было js0. Удостоверимся, что джойстик работает. Выполним в терминале команду

sudo jstest /dev/input/js0

И смотрим изменение показаний кнопок и джойстиков

4.1. ROS пакет joy.
ROS пакет joy – драйвер джойстика уже установлен в системе. По умолчанию пакет joy использует устройство /dev/input/js0. Если ваш джойстик имеет другое название (например /dev/input/js1), необходимо перед запуском пакета установить параметр joy_node/dev на сервере параметров:

rosparam set joy_node/dev "/dev/input/js1"

Для запуска

rosrun joy joy_node

При запуске пакет выдает сообщение о подключении джойстика

Пакет создает узел ros node_joy, который публикует в тему joy сообщения типа sensor_msgs/Joy

Посмотреть выдачу сообщений узлом joy_node в тему joy можно по команде
rostopic echo joy
И смотрим сообщения в тему joy при нажатии кнопок и передвижении джойстиков нашего геймпада

Наша задача создать узел, который будет слушателем (subscriber) сообщений из темы joy. Этот узел будет обрабатывать эти сообщения и посылать сообщения в темы и сервисы пакета turtlesim для управления черепахами.

СОЗДАНИЕ ИГРЫ

Суть игры следующая – на поле через некоторое время в случайных позициях появляются черепахи. Задача игрока – управляя с помощью джойстика своей черепахой turtle1 наезжать на этих черепах (при наезде черепаха исчезает).

Создаем в рабочем пространстве catkin новый пакет game_turtlesim.

cd ~/catkin_ws/src
catkin_create_pkg game_turtlesim std_msgs rospy joy turtlesim

При этом сразу указываем зависимости пакета. После создания пакета В директории ~/catkin_ws/src появится новая директория с файлами нового, только что созданного пакета game_turtlesim.
Теперь необходимо отредактировать файлы package.xml и CmakeLists.txt.
В файле package.xml мы заполняем номер версии, данные об авторе, сведения о лицензии. Зависимости пакета указаны в блоках и . В блоке указаны зависимости при сборке пакета, а в блоках – зависимости при выполнении пакета.
В данном проекте мы не собираемся использовать сообщения пользовательского типа, поэтому других изменений в файле package.xml и CmakeLists.txt мы делать не будем.
Необходимо реализовать
— управление с помощью джойстика движением черепахи turtle1;
— определение текущего местонахождения нашей движущейся черепахи;
— создание на поле черепах через определенное время;
— проверку столкновения нашей движущейся черепахи с черепахами на поле (в случае наезда – удаление черепахи).

Управление с помощью джойстика движением черепахи turtle1

Скрипт на языке python, управляющий движением черепахи turtle1 с помощью джойстиков геймпада Defender Game Racer X7. Узел должен являться слушателем (subscriber) темы joy и преобразовывать получаемые данные и формировать запросы в сервис /turtle1/teleport_relative для управления движением черепахи turtle1. Т.к. узел joy_node при перемещениях джойстиков публикует сообщения в тему joy очень часто, сервис /turtle1/teleport_relative не будет успевать обрабатывать эти сообщения и они будут ставиться в очередь, что приведет к сильному замедлению реакции черепахи turtle1 на перемещения джойстиков геймпада. Поэтому я поступил следующим образом. Subscriber будет только обновлять последние значения показаний джойстиков и сохранять их в параметре example_1/joy_tek на сервере параметров, а другой скрипт будет раз в 0.1 сек получать значения параметра example_1/joy_tek и отправлять запросы в сервис /turtle1/teleport_relative. Это приведет к потере реакции на сообщения, публикуемые в тему joy, но задержки не будет.
Скрипт turtlesim_joy.py
Создаем файл

roscd game_turtlesim
mkdir nodes
cd nodes
touch turtlesim_joy.py
chmod +x turtlesim_joy.py

И сам скрипт

#!/usr/bin/env python
#-*-coding:utf-8 -*-

import roslib; roslib.load_manifest('game_turtlesim')
import rospy
import time

from sensor_msgs.msg import Joy
import std_srvs.srv
from turtlesim.srv import SetPen
from turtlesim.srv import TeleportAbsolute


def controller(data):
    # запись данных с джойстиков
    rospy.loginfo(str(data.axes[0])+" "+str(data.axes[3]))
    joy_tek=rospy.get_param("game_turtlesim/joy_tek")
    joy_tek[0]=data.axes[0]
    joy_tek[1]=data.axes[3]
    rospy.set_param("game_turtlesim/joy_tek",joy_tek)
def listener():
     rospy.init_node('sub_turtle1_joy')
     # установка параметров
     rospy.set_param("game_turtlesim/start",1)
     rospy.set_param("game_turtlesim/joy_tek",[0.0, 0.0])
     # установить цвет фона
     rospy.set_param("background_r",255)
     rospy.set_param("background_g",255)
     rospy.set_param("background_b",0)
     serv1=rospy.ServiceProxy('/clear',std_srvs.srv.Empty)
     res1=serv1();
     # установить цвет карандаша для turtle1
     serv1=rospy.ServiceProxy('/turtle1/set_pen',SetPen)
     res1=serv1(255,255,0,10,0);
     # черепаху в начальную позицию
     serv1=rospy.ServiceProxy('turtle1/teleport_absolute',TeleportAbsolute)
     res1=serv1(0.0,0.0,0.89);
     # пауза - отдышаться перед игрой
     rospy.sleep(1.0)

     sub = rospy.Subscriber("joy",Joy,controller)
     rospy.spin()

if __name__ == '__main__':
   listener()

Отклонения влево/вправо левого джойстика (поворот влево/вправо черепахи) и отклонения вперед/назад правого джойстика (движение черепахи вперед/назад). Одним джойстиком осуществлять управление неудобно.

Движение черепахи реализуется в основном скрипте turtle1_go.py
Вот код, реализующий движение turtle1

       if rospy.get_param("game_turtlesim/start")==1:
          joy_tek=rospy.get_param("game_turtlesim/joy_tek")
          speed=joy_tek[1]/K_SPEED
          k=1
          if joy_tek[1]<0:
            k=-1
          angle=math.asin(joy_tek[0]*k)/K_ANGLE
          res1=serv1(speed,angle);
       rospy.sleep(0.1)

Определение текущего местонахождения нашей движущейся черепахи

Узел turtlesim_node публикует в тему turtleX/pose сообщения типа turtlesim/Pose о текущей позиции и скоростях передвижения черепахи. Дополнительный скрипт turtle1_pose.py, создающий узел sub_turtle1_pose, который будет являться слушателем (subscriber) сообщений из темы turtle1/pose. Значения x и y мы будем сохранять в параметре game_turtlesim/turtle1_pose на сервере параметров. Значения этого параметра мы будет использовать в основном скрипте turtle1_go.py. Содержимое скрипта turtle1_pose.py

#!/usr/bin/env python
#-*-coding:utf-8 -*-

import roslib; roslib.load_manifest('game_turtlesim')
import rospy
import time
from turtlesim.msg import Pose

def controller(data):
    # запись данных положения turtle1
    rospy.set_param("game_turtlesim/turtle1_pose",[data.x,data.y])
    rospy.loginfo("x="+str(data.x)+" y="+str(data.y))

def listener():
     rospy.init_node('sub_turtle1_pose')
     # начальная установка параметров
     rospy.set_param("game_turtlesim/turtle1_pose",[0.0, 0.0])
     # объявление subcriber
     sub = rospy.Subscriber("turtle1/pose",Pose,controller)
     rospy.spin()

if __name__ == '__main__':
   listener()

Cоздание на поле черепах через определенное время

Код создания на поле черепах через определенное время. Добавление черепах на поле в происходит при отправке запроса к сервису spawn. Формат сообщения сервису spawn – turtlesim/Spawn:
float32 x
float32 y
float32 theta
string name # Optional. A unique name will be created and returned if this is empty
---
string name
Т.е. нам необходимо отправить значения координат на поле, угол поворота (в радианах) и имя. Список черепах (1 – есть, 0 – нет) и их координаты на поле, а также текущее значение количества черепах будем сохранять в параметрах game_turtlesim/list_turtles, game_turtlesim/coord_turtles, game_turtlesim/count_turtles на сервере параметров rosparam. Параметр game_turtlesim/time считает время выполнения скрипта, чтобы мы могли отслеживать задержку перед появлением следующей черепахи. Код данного фрагмента

# создание черепах turtle00-turtle010
count_turtles=rospy.get_param("game_turtlesim/count_turtles")
count=rospy.get_param("game_turtlesim/count")
if rospy.get_param("game_turtlesim/start")==1 and count_turtles<10 and
           time.time()-rospy.get_param("game_turtlesim/time")>(T_SPEED-max(2,count*0.03)):
   count_turtles=count_turtles+1
   list_turtles=rospy.get_param("game_turtlesim/list_turtles")
   coord_turtles=rospy.get_param("game_turtlesim/coord_turtles")
   ind=list_turtles.index(0,0,10)
   prv1=False
   while prv1==False:
     x=random.randrange(5,110,5)
     y=random.randrange(5,110,5)
     theta=0.1*random.randint(1,10)
     tek_turtle="turtle0"+str(ind)
     res2=serv2(0.1*x,0.1*y,theta,tek_turtle);
     rospy.loginfo("create turtle="+tek_turtle)
     list_turtles[ind]=1
     coord_turtles[ind]=[x,y]
     rospy.loginfo(list_turtles)
     rospy.set_param("game_turtlesim/list_turtles",list_turtles)
     rospy.set_param("game_turtlesim/coord_turtles",coord_turtles)
     rospy.set_param("game_turtlesim/time",time.time())
     rospy.set_param("game_turtlesim/count_turtles",count_turtles)
     prv1=True

Проверка столкновения нашей движущейся черепахи с черепахами на поле

Проверку столкновения нашей движущейся черепахи с черепахами на поле с помощью перебора координат всех черепах на поле (параметр game_turtlesim/coord_turtles) с текущим положением нашей движущейся черепахи turtle1 (параметр game_turtlesim/turtle1_pose) с погрешностью 3 пиксела. В случае наезда удаление черепахи (запрос сервису kill) и внесение значения нулевых координат [0.0, 0.0] для удаленной черепахи в массиве game_turtlesim/coord_turtles и 0 в массиве game_turtlesim/list_turtles. При уничтожении черепахи инкрементируем счетчик игры (параметр game_turtlesim/count на сервере параметров rosparam). Код данного фрагмента

# проверка столкновения turtle1 с turtle00-turtle010
count=rospy.get_param("game_turtlesim/count")
list_turtles=rospy.get_param("game_turtlesim/list_turtles")
coord_turtles=rospy.get_param("game_turtlesim/coord_turtles")
xy=rospy.get_param("game_turtlesim/turtle1_pose")
count_turtles=rospy.get_param("game_turtlesim/count_turtles")
x1=int(xy[0]*10)
y1=int(xy[1]*10)
i=0
for xy in coord_turtles:
   if abs(xy[0]-x1)<4 and abs(xy[1]-(110-y1))<4 and list_turtles[i]==1:
      turtle_del="turtle0"+str(i)
      rospy.loginfo(str(xy[0])+" "+str(xy[1])+" "+str(x1)+" "+str(y1)+" "+turtle_del)
      res3=serv3(turtle_del);
      list_turtles[i]=0
      coord_turtles[i]=[0,0]
      count_turtles=count_turtles-1
      rospy.set_param("game_turtlesim/count",count+1)
      rospy.set_param("game_turtlesim/count_turtles",count_turtles)
      rospy.set_param("game_turtlesim/list_turtles",list_turtles)
      rospy.set_param("game_turtlesim/coord_turtles",coord_turtles)
   i=i+1

И запуск

Командный файл game_turtlesim.launch


  
  
  
  
  
  
  
  
  
  
  

В первом терминале

roscore

Во втором терминале

roslaunch game_turtlesim game_turtlesim.launch

Проект данного примера можно скачать с github (https://github.com/victoruni/game_turtlesim.git)


Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение