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

В нашем хакспейсе — создать робота не такая большая проблема. Но не у всех и не всегда есть возможность экспериментировать с реальным «железом» — поэтому интересно было попробовать решить задачу — в виртуальной среде, и после уже оживить «железо».

Так и возникла идея серии статей, про решение простейшей задачи ориентации робота в пространстве — от виртуальной симуляции, до воплощения в реальном мобильном роботе:

Часть 1. Настройка виртуальной среды, интеграция с python и OpenCV для распознавания образов из виртуального мира.
Часть 2. Создание виртуального мобильного робота, алгоритм автономного перемещения (поиск объекта)
Часть 3. Создание реального робота, перенос логики на него.

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

Мозгом робота будет микрокомпьютер RaspberryPi2 — на котором без проблем работает и python, и OpenCV. Таким образом необходимо состыковать систему виртуальной робототехники V-REP — с Python и OpenCV. Вот про это и будет первая часть — данная публикация.

Видео, что получилось (поиск зеленого объекта)

На верхнем окне — прямое изображение с видео-камеры в 3д виртуальном мире, на нижнем окне — результат выполнения python скрипта, что получает изображение передаёт её OpenCV и рисует маркер, вокруг найденного объекта.

Нарисуем архитектуру нашего мобильного робота.

Архитектура мобильного робота
Реальный робот, будет выглядит примерно так (ещё камеру добавим)


Архитектурно:


Как мы видим — «мозг» робота получает изображение, распознаёт (посредством OpenCV) и передаёт управляющие команды на колеса. И заменив изображение с камеры на 3д изображение, а управление колесами — на управление виртуальными колесами 3д робота в виртуальном мире — мы получим стенд для отработки логики.

Получаем, такую архитектуру для виртуального стенда:


В нашей сегодняшней первой части — нам необходимо решить задачу связки V-REP с внешним python скриптом, который выполняет распознавание образов с помощью OpenCV, и выводит маркер вокруг найденного объекта.

Получается такая архитектура:


Установка V-REP

Про бесплатную систему робо-моделирования V-REP — было несколько русскоязычных публикаций:

Поэтому установку и первые шаги — можно посмотреть в них.

Сцена

Считаем что V-REP установлен и работает. Нам необходимо создать сцену.

На сцене будет камера, что просматривает область, на которой есть разные 3д объекты, это изображение необходимо транслировать внешнему python скрипту, который должен вызывать OpenCV для распознавания, и формирования маркера, и возврат данного изображения обратно в V-REP — для контроля.

К счастью среди примеров который поставляются с V-REP был подобный пример, правда он использовал ROS (что для текущих задач не требовался):


На базе этого демо появилась следующая сцена:
  • подвижный штатив, что вращается
  • на этом штативе закреплена камера v0 (vision sensor)
  • размещён v1 сенсор, данные на который передаются из внешней системы

Сцену scene.ttt можно скачать отсюда.

Python API

Считаем, что Python у вас установлен (тестировалось на 2.7.6)

При запуске V-REP автоматически загружается плагин обеспечивающий связку Python API. Для работы с API через python скрипт необходимо наличие трёх файлов в папке:
  • remoteApi.so (или remoteApi.dll)
  • vrep.py
  • vrepConst.py

Их можно скопировать из V-REP каталога (programming/remoteApiBindings/python/python, programming/remoteApiBindings/lib/lib/). После этого можно импортировать модуль V-REP:
import vrep

Простейший скрипт, что подключается к API, выглядит так:
import vrep

vrep.simxFinish(-1) # just in case, close all opened connections

clientID = vrep.simxStart('127.0.0.1', 19997, True, True, 5000, 5)

if clientID!=-1:
  print 'Connected to remote API server'

  while (vrep.simxGetConnectionId(clientID) != -1):
    print "some work"
else:
  print "Failed to connect to remote API Server"
  vrep.simxFinish(clientID)


На нашей сцене есть два объекта — v0 и v1 — это Visual Sensor — с первого мы считываем картинку, на второй мы должны записывать результат. Поэтому мы должны получить эти объекты в контексте нашего python скрипта, делается это с помощью API команды vrep.simxGetObjectHandle

  res, v0 = vrep.simxGetObjectHandle(clientID, 'v0', vrep.simx_opmode_oneshot_wait)
  res, v1 = vrep.simxGetObjectHandle(clientID, 'v1', vrep.simx_opmode_oneshot_wait)

Обращаем внимание, что все функции доступные через API — так же доступны и через внутренние скрипты (Lua), единственное отличие что в названии вместо simx — sim, то есть в нашем случае вызов функции на Lua будет «simGetObjectHandle».

Для получения из Vision Sensor картинки и записи — есть две функции: vrep.simxGetVisionSensorImage и vrep.simxSetVisionSensorImage соответственно.

И в python коде это будет выглядеть так (где v0 и v1 — соответствующие объекты):
    # получение изображения из сенсора v0
    err, resolution, image = vrep.simxGetVisionSensorImage(clientID, v0, 0, vrep.simx_opmode_buffer)
    # запись изображения в сенсор v1
    vrep.simxSetVisionSensorImage(clientID, v1, image, 0, vrep.simx_opmode_oneshot)

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


Ретрансляция изображения

Таким образом сейчас мы можем сделать python скрипт, что через V-REP API берёт изображение с видео-сенсора и ретранслирует его:
simple_image_retranslate.py
# simple_image_retranslate.py

import vrep
import time

vrep.simxFinish(-1)

clientID = vrep.simxStart('127.0.0.1', 19997, True, True, 5000, 5)

if clientID!=-1:
  print 'Connected to remote API server'

  # get vision sensor objects
  res, v0 = vrep.simxGetObjectHandle(clientID, 'v0', vrep.simx_opmode_oneshot_wait)
  res, v1 = vrep.simxGetObjectHandle(clientID, 'v1', vrep.simx_opmode_oneshot_wait)


  err, resolution, image = vrep.simxGetVisionSensorImage(clientID, v0, 0, vrep.simx_opmode_streaming)
  time.sleep(1)

  while (vrep.simxGetConnectionId(clientID) != -1):
    err, resolution, image = vrep.simxGetVisionSensorImage(clientID, v0, 0, vrep.simx_opmode_buffer)
    if err == vrep.simx_return_ok:
      vrep.simxSetVisionSensorImage(clientID, v1, image, 0, vrep.simx_opmode_oneshot)
    elif err == vrep.simx_return_novalue_flag:
      print "no image yet"
    else:
      print err
else:
  print "Failed to connect to remote API Server"
  vrep.simxFinish(clientID)

Для проверки мы должны стартовать сцену (кнопка «Play» вверх), и после этого в командной строке запустить файл simple_image_retranslate.py

И вот результат (v1 — отображает изображение из python скрипта):


Ок, базовая заготовка есть, теперь необходимо подключить распознавание образов (компьютерное зрение)

Компьютерное зрение, OpenCV

Думаю все слышали про открытую систему компьютерного зрения, OpenCV, которую разработал
Gary Bradski

Gary Bradski посетил стенд нашего хакспейса на иннопроме 2014

Во первых OpenCV надо установить, установка opencv2 на Linux Mint (Ubuntu) выглядела достаточно просто:
sudo apt-get install libopencv-dev python-opencv

После этого появилась возможность подключать библиотеку в python коде:
import cv2

Теперь необходимо реализовать простейшую функцию распознавания. В качестве базы был взять этот пример, поиска зелёного мяча на картинке.

Появилась функция def track_green_object(image), что возвращает координаты зелёного объекта, если найден.

И так же надо отметить маркером вокруг найденного объекта, для этого мы воспользуемся базовой OpenCV функцией для рисования прямоугольника: cv2.rectangle.

И наш фрагмент кода, что:
  1. получает изображение (из v0)
  2. находит объект (что-то зелёное)
  3. добавляет маркер (желтый прямоугольник)
  4. возвращает изображение (в v1)

выглядит так:
    # get image from vision sensor 'v0'
    err, resolution, image = vrep.simxGetVisionSensorImage(clientID, v0, 0, vrep.simx_opmode_buffer)
    if err == vrep.simx_return_ok:
      image_byte_array = array.array('b', image)
      image_buffer = I.frombuffer("RGB", (resolution[0],resolution[1]), image_byte_array, "raw", "RGB", 0, 1)
      img2 = numpy.asarray(image_buffer)

      # try to find something green
      ret = track_green_object(img2)

      # overlay rectangle marker if something is found by OpenCV
      if ret:
        cv2.rectangle(img2,(ret[0]-15,ret[1]-15), (ret[0]+15,ret[1]+15), (0xff,0xf4,0x0d), 1)

      # return image to sensor 'v1'
      img2 = img2.ravel()
      vrep.simxSetVisionSensorImage(clientID, v1, img2, 0, vrep.simx_opmode_oneshot)


Результат

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


Исходный код проекта можно найти на github.

В следующей части мы будем создавать робота в V-REP, и программировать его для поиска зелёного мяча.

Комментарии (1)


  1. San_tit
    09.04.2016 21:46

    Проблема с такой конфигурацией будет с FPS обработки изображения с камеры (по крайней мере с raspicam).
    Очень буду рад, если у вас получится решение с 25-30+ FPS с минимальными задержками на реальном железе.