Настойка Duckietown Gym и первый запуск.

Настойка Duckietown Gym и первый запуск.

Эмулятор робота duckietown gym — среда тестирования робота. Настойка Duckietown Gym — это ключевой момент перед обучением робота.

Одна из проблем мобильной робототехники — это корректное поведение устройства в дорожной среде. Робот при движении в пространстве опирается на показания датчиков и камер. Для этих целей применяют знания по компьютерному зрению и машинному обучению. Данный подход требует много времени на обучения робота и поиск оптимального алгоритма.

Для решения такой проблемы сотрудники MIT разработали платформу Duckietown. Здесь мы познакомимся с эмулятором робота Duckietown gym, который можно дословно перевести как «Уткоград».

Статья разбита на несколько шагов для удобства работы:

  • настройка duckietown gym.
  • знакомимся и анализируем базовый код по управлению роботом в эмуляторе.
  • учимся управлять роботом.
  • изучаем что такое карты и как они создаются.
  • создаём собственную карту местности.

Настройка

Перейдём к настройке эмулятора робота duckietown gym

Для начала, необходимо перейти в корневой католог gym-duckietown.
Для этого нам необходимо открыть Терминал, через сочетание CTRL + ALT + T (или открыть через меню).
Затем, с помощью команды cd перейти в нужный каталог. Пример ниже.

cd ~/Documents/gym-duckietown/

Анализ базового кода

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

Задание №1 “Первый запуск”

Используя базовое приложение с пользовательским интерфейсом познакомимся с уткоградом. Для этого запустим программу manual_control.py .

python3 ./manual_control.py --env-name Duckietown-udem1-v0

После этого откроется окно эмулятора, где вы сможете осуществить ручное управление вашим виртуальным роботом.

Виртуальное окружение

Теперь открываем код manual_control.py и начинаем его припарировать:

#!/usr/bin/env python
# manual

"""
This script allows you to manually control the simulator or Duckiebot
using the keyboard arrows.
"""
from PIL import Image
import argparse
import sys

import gym
import numpy as np
import pyglet
from pyglet.window import key

from gym_duckietown.envs import DuckietownEnv

# from experiments.utils import save_img

parser = argparse.ArgumentParser()

parser.add_argument("--env-name", default=None)
parser.add_argument("--map-name", default="udem1")
parser.add_argument("--distortion", default=False, action="store_true")
parser.add_argument("--camera_rand", default=False, action="store_true")
parser.add_argument("--draw-curve", action="store_true", help="draw the lane following curve")
parser.add_argument("--draw-bbox", action="store_true", help="draw collision detection bounding boxes")
parser.add_argument("--domain-rand", action="store_true", help="enable domain randomization")
parser.add_argument("--dynamics_rand", action="store_true", help="enable dynamics randomization")
parser.add_argument("--frame-skip", default=1, type=int, help="number of frames to skip")
parser.add_argument("--seed", default=1, type=int, help="seed")
args = parser.parse_args()

if args.env_name and args.env_name.find("Duckietown") != -1:
    env = DuckietownEnv(
        seed=args.seed,
        map_name=args.map_name,
        draw_curve=args.draw_curve,
        draw_bbox=args.draw_bbox,
        domain_rand=args.domain_rand,
        frame_skip=args.frame_skip,
        distortion=args.distortion,
        camera_rand=args.camera_rand,
        dynamics_rand=args.dynamics_rand,
    )
else:
    env = gym.make(args.env_name)

env.reset()
env.render()


Нас интересует 21 строка кода. Видим, что все начинается с парсера, который считывает аргументы, которые мы записываем после вызываемой программы. Эти аргументы передаются в объект env типа DuckietownEnv — виртуальное окружение тренировочного зала.

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

#!/usr/bin/env python
# manual

"""
This script allows you to manually control the simulator or Duckiebot
using the keyboard arrows.
"""
from PIL import Image
import argparse
import sys

import gym
import numpy as np
import pyglet
from pyglet.window import key

from gym_duckietown.envs import DuckietownEnv

# from experiments.utils import save_img

env = DuckietownEnv(
    seed=args.seed,
    map_name=args.map_name,
    draw_curve=args.draw_curve,
    draw_bbox=args.draw_bbox,
    domain_rand=args.domain_rand,
    frame_skip=args.frame_skip,
    distortion=args.distortion,
    camera_rand=args.camera_rand,
    dynamics_rand=args.dynamics_rand,
    )
env.reset()
env.render()


Теперь запустим и проверим работоспособность кода.

python3 ./manual_control.py

Пройдемся по аргументам создаваемого пространства уткограда (DuckietownEnv ) :

Базовые:

  • seed — сид, зерно (и т.п. названия) — целое число которое, задает начальное положение (псевдослучайным образом — т.е. каждое число будет давать один и тот же результат, но заранее неизвестно, если ранее не запускалось с таким числом), в случае когда не задается, начальное положение выбирается случайным образом (int)
  • map_name — наименование, открываемой карты (name.yaml)
  • draw_curve — происходит отрисовка линии следования (True/False)
  • draw_bbox — происходит отрисовка границ, пересечение которых вызывает столкновение (True/False) // камера переходит в режим: вид сверху
Режим карты: вид сверху
Рис 1. Режим: вид сверху

domain_rand — разрешение областной рандомизации на карте  (True/False)

dynamics_rand — если включен, добавляет динамическую тряску (True/False)

Положение:

  • param_user_tile_start— устанавливает начальный тайл, в котором появляется дакибот ((int,int))

// только не выбирайте тайлы, в которых бота не должно быть, т.к. тогда программа уходит во внутренний спор

  • accept_start_angle_deg— устанавливает угол допустимый в полосе движения для стартовой позиции  (float), по умолчанию = 60

Настройка камеры: (по базовым установкам камера имеет разрешение 640 на 480)

  • camera_rand — если включен, добавляется рандомная часть ошибок калибровки камеры (True/False)
  • frame_rate — частота кадров (int), по умолчанию = 30 кадрам в секунду
  • frame_skip — пропускаемые кадры (int)
  • distortion — искривление изображения, если правде, то искривляет аппроксимацией рыбий глаз (True/False)
Эффект отображения "Рыбий глаз"
Рис. 2 Эффект «Рыбий глаз»
  • camera_width — ширина камеры  (int)
  • camera_height —  высота камеры  (int)

сравнение базовой настройки  640 x 480 с 400 x 300

качество разрешения картинки эмулятора duckietown gym
Рис 3. Качество разрешение картинки эмулятора.

Со знакомством и настройкой окружения закончили.

Смотрим внутрь

Возвращаясь к основному коду, видим, что далее по тексту у нас происходит сброс (reset()) для введенного окружения (env), в котором происходит запуск с первичными настройками, после чего начинается “жизнь” в виртуальном окружении.

import gym
import numpy as np
import pyglet
from pyglet.window import key

from gym_duckietown.envs import DuckietownEnv

# from experiments.utils import save_img

env = DuckietownEnv(
    seed=args.seed,
    map_name=args.map_name,
    draw_curve=args.draw_curve,
    draw_bbox=args.draw_bbox,
    domain_rand=args.domain_rand,
    frame_skip=args.frame_skip,
    distortion=args.distortion,
    camera_rand=args.camera_rand,
    dynamics_rand=args.dynamics_rand,
    )
env.reset()
env.render()

Для наблюдения того, что происходит в “виртуальности” начинается рендеринг (render( mode)) возможны несколько вариантов отображения:

  • “top_down” — вид на всю карту сверху вниз
Режим отображения всей карты сверху
Рис 4. Отображение всей карты

“human” — отображение “для человека”, вид через камеру на дакиботе (базовое значение)

Вид обзора с камеры с пояснениями
Рис 5. Фронтальный обзор камеры
  • “free_cam” — аналогично “human”, но без дополнительной информации о положении и параметрах движении
вид обзора с камеры без пояснений
Рис 6. Облегчённый вариант отображения с камеры

Управление дакиботом

Снова возвращаемся к тексту программы  manual_control.py по тексту программы у нас идет функция, вызываемая при нажатии клавиш on_key_press

@env.unwrapped.window.event
def on_key_press(symbol, modifiers):
    """
    This handler processes keyboard commands that
    control the simulation
    """

    if symbol == key.BACKSPACE or symbol == key.SLASH:
        print("RESET")
        env.reset()
        env.render()
    elif symbol == key.PAGEUP:
        env.unwrapped.cam_angle[0] = 0
    elif symbol == key.ESCAPE:
        env.close()
        sys.exit(0)

    # Take a screenshot
    # UNCOMMENT IF NEEDED - Skimage dependency
    # elif symbol == key.RETURN:
    #     print('saving screenshot')
    #     img = env.render('rgb_array')
    #     save_img('screenshot.png', img)


# Register a keyboard handler
key_handler = key.KeyStateHandler()
env.unwrapped.window.push_handlers(key_handler)

Дает возможность сделать перезагрузку (reset()) карты, установить угол камеры, закрыть приложение и сделать скрин (если раскомментировать участок кода)

Полезно, но не интересно и зачастую не используется, можно упростить. Удаляем и идем дальше…, а дальше у нас участок, отвечающий за управление дакиботом (update):

def update(dt):
    """
    This function is called at every frame to handle
    movement/stepping and redrawing
    """
    wheel_distance = 0.102
    min_rad = 0.08

    action = np.array([0.0, 0.0])

    if key_handler[key.UP]:
        action += np.array([0.44, 0.0])
    if key_handler[key.DOWN]:
        action -= np.array([0.44, 0])
    if key_handler[key.LEFT]:
        action += np.array([0, 1])
    if key_handler[key.RIGHT]:
        action -= np.array([0, 1])
    if key_handler[key.SPACE]:
        action = np.array([0, 0])

    v1 = action[0]
    v2 = action[1]
    # Limit radius of curvature
    if v1 == 0 or abs(v2 / v1) > (min_rad + wheel_distance / 2.0) / (min_rad - wheel_distance / 2.0):
        # adjust velocities evenly such that condition is fulfilled
        delta_v = (v2 - v1) / 2 - wheel_distance / (4 * min_rad) * (v1 + v2)
        v1 += delta_v
        v2 -= delta_v

    action[0] = v1
    action[1] = v2

    # Speed boost
    if key_handler[key.LSHIFT]:
        action *= 1.5

Который вызывается каждый раз по таймеру:


pyglet.clock.schedule_interval(update, 1.0 / env.unwrapped.frame_rate)

Сами нажатия клавиш обрабатываются при помощи строк:

# Register a keyboard handler
key_handler = key.KeyStateHandler()
env.unwrapped.window.push_handlers(key_handler)

Мы реализуем вызов обновления каждого кадра через цикл типа:

While True:
    update(0)

Задание №2 “Переделываем вызов обновления”

Добавим в функцию обновления (update) строчку, отвечающую за закрытие приложения:

def update(dt):
    """
    This function is called at every frame to handle
    movement/stepping and redrawing
    """
    wheel_distance = 0.102
    min_rad = 0.08

    action = np.array([0.0, 0.0])

    if key_handler[key.UP]:
        action += np.array([0.44, 0.0])
    if key_handler[key.DOWN]:
        action -= np.array([0.44, 0])
    if key_handler[key.LEFT]:
        action += np.array([0, 1])
    if key_handler[key.RIGHT]:
        action -= np.array([0, 1])
    if key_handler[key.SPACE]:
        action = np.array([0, 0])
    if key_handler[key.ESCAPE]:
        env.close()
        sys.exit(0)

Теперь можно закрыть окно нажатием клавиши ESCAPE. Проверяем работу программы .

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

трёхколёсный робот
Рис 7. Дакиробот с дифференциальным приводом

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

obs, reward, done, info = env.step(action)

Далее, данные обрабатываются и отправляются на рендер кадра.

obs, reward, done, info = env.step(action)
    print("step_count = %s, reward=%.3f" % (env.unwrapped.step_count, reward))

    if key_handler[key.RETURN]:

        im = Image.fromarray(obs)

        im.save("screen.png")

    if done:
        print("done!")
        env.reset()
        env.render()

    env.render()

После объявления функции, добавляем цикл:

While True:
    update(0)

В итоге у нас есть функция в таком виде.

def update(dt):
    """
    This function is called at every frame to handle
    movement/stepping and redrawing
    """
    wheel_distance = 0.102
    min_rad = 0.08

    action = np.array([0.0, 0.0])

    if key_handler[key.UP]:
        action += np.array([0.44, 0.0])
    if key_handler[key.DOWN]:
        action -= np.array([0.44, 0])
    if key_handler[key.LEFT]:
        action += np.array([0, 1])
    if key_handler[key.RIGHT]:
        action -= np.array([0, 1])
    if key_handler[key.SPACE]:
        action = np.array([0, 0])
    if key_handler[key.ESCAPE]:
        env.close()
        sys.exit(0)
        
    v1 = action[0]
    v2 = action[1]
    # Limit radius of curvature
    if v1 == 0 or abs(v2 / v1) > (min_rad + wheel_distance / 2.0) / (min_rad - wheel_distance / 2.0):
        # adjust velocities evenly such that condition is fulfilled
        delta_v = (v2 - v1) / 2 - wheel_distance / (4 * min_rad) * (v1 + v2)
        v1 += delta_v
        v2 -= delta_v

    action[0] = v1
    action[1] = v2
    obs, reward, done, info = env.step(action)
    print("step_count = %s, reward=%.3f" % (env.unwrapped.step_count, reward))

    env.render(mode)
while True:
    update(0) 

Запускаем, проверяем работоспособность нашего кода.

В итоге любая программа будет в общем виде иметь вид:

  1. импорты нужных библиотек
  2. создание среды, ее сброс и рендер
  3. создание функции для управления дакиботом
  4. цикл с функцией управления дакиботом

Карта

Для тренировки нашего робота используют разнообразные карты дорог. Существует несколько карт, встроенных в эмулятор уткограда. Для этого достаточно перейти в папку, где расположены карты:

DEBUG:gym-duckietown:loading map file "/home/art/.local/lib/python3.8/site-packages/duckietown_world/data/gbl/maps/udem1.yaml"

Ниже представлены варианты карт в данном каталоге.

файлы карт для duckietown gym
Рис 8. Карты

Пропишим новое имя карты в конструкторе среды для её загрузки.

загрузка карты duckietown gym
Рис 9. Загрузка новой карты

В папке содержаться карты:

  • 4way
  • 4way_bordered
  • calibration_map_ext
  • calibration_map_int
  • ETH_intersection_map
  • ETH_large_loop
  • ETH_small_loop_1
  • ETH_small_loop_2
  • ETH_small_loop_2_bordered
  • ETH_small_loop_3
  • ETHZ_autolab_fast_track
  • ETHZ_autolab_technical_track
  • ETHZ_autolab_technical_track_bordered
  • ETHZ_loop
  • ETHZ_loop_bordered
  • loop_dyn_duckiebots
  • loop_empty
  • loop_obstacles
  • loop_pedestrains
  • Montreal_loop
  • regress_4way_adam

все имеют формат yaml — Yet Another Markup Language,

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

title_size:0.585

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

Структура карты
Рис 10. Координатная сетка расположение тайлов карты.

Все тайлы задаются после ключевого слова tiles как набор массивов, где идет перечисление типов тайлов через запятую, выделяя строку, заключая тайлы в квадратные скобки:

titles:
- [floor, floor, floor, floor, floor, floor, floor, floor]
- [floor, floor, straight/W, floor, straight/W, straight/W, left/Ncurve, floor]

Доступны следующий тайлы:

  • empty
  • straight
  • curve_left
  •  curve_right
  • 3way_left
  • 3way_right
  • 4way
  • asphalt
  • grass
  • floor

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

структура одного тайла
Рис 11. Схема тайла

На карту можно установить объекты. Например такой:

objects:
    -kind: tree
        pos:[2.5, 4.5]
        rotate: 180
        height: 0.25

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

  • barrier cone
  • duckie
  • duckiebot
  • tree
  • house
  • truck
  • bus building
  • sign_stop
  • sign_T_intersect
  • sign_yield

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

start_pose[[0.4, 0, 0.4], 0]

Где первые три числа списка обозначают положение в соответствии с изначально приведенной координатной сеткой (в условии, что в установке среды начальный тайл установлен как (0,0)), а четвёртое число — угол поворота в радианах.

Напишем код окружения для карты.

title:
-[curve_left/W , floor, floor , floor, curve_left/N]
-[straight/S , floor , straight/N , floor , straight/N]
-[3way_left/S , floor , 4way , floor , 3way_left/N]
-[straight/S, floor , straight/S , asphalt , straight/N]
-[curve_left/S , floor , 3way_left/E , floor , curve_left/E]

start_pose[[0.4, 0, 0.4], 1]

objects:
    trafficlight:
        kind: trafficlight
        pos: [0.18,-0.18]
        rotate: 135
        height: 0.4
        optional: true

title_size: 0.585

Результатом станет такая карта:

карта duckietown gym
Рис 12. Создание своей карты.

Итог

Мы познакомились со средой Duckietown Gym и основными функциями эмулятора. Научились создавать карты и управлять виртуальным роботом.

Вопросы.

  • опишите архитектуру программы.
  • какие параметры управления в симуляции присутствуют?
  • как строится карта?

Курсы в которых изучается материал

1) Онлайн курс: автономный транспорт, компьютерное зрение.

Сохраните или поделитесь
One comment on “Настойка Duckietown Gym и первый запуск.

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

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