Показаны сообщения с ярлыком EV3. Показать все сообщения
Показаны сообщения с ярлыком EV3. Показать все сообщения

воскресенье, 7 июля 2019 г.

Sudoku Hunter

В последнее время наш блог молчит и может показаться, что «Карандаш и Самоделкин» все, сдулись. На самом деле работа кипела, а отсутствие публикуемых материалов было связано с тем, что проекты раз за разом становятся все сложнее и требуют все больше времени на реализацию. Последние наши разработки были так или иначе связаны с нейросетями, поэтому логично было за это время подтянуть скиллы по этой теме, что и было сделано – был пройден ряд онлайн курсов по и даже пара оффлайновых, из которых особенно понравилась «Зимняя школа машинного обучения» (Machine Learning Winter School).


Проект, работа над которым шла больше 4 месяцев, мы назвали Sudoku Hunter. Изначально была идея собрать робота по мотивам NXTSudokuSolver, однако постепенно она трансформировалось в нечто иное, на наш взгляд не менее интересное.
Итак, Sudoku Hunter – это пистолет с EV3, веб-камерой и Raspberry Pi на борту, из которого можно «стрелять» по математическим головоломкам судоку, получая на экране EV3-блока их решение. В алгоритмической части используются технологии машинного зрения и машинного обучения, веб-технологии для создания отладочной видеоконсоли.


Если кто не помнит, судоку - головоломка на бумаге, игровое поле которой представляет собой квадрат размером 9×9, разделённый на меньшие квадраты со стороной в 3 клетки. Таким образом всё игровое поле состоит из 81 клетки. В них уже в начале игры стоят некоторые числа (от 1 до 9), называемые подсказками. От игрока требуется заполнить свободные клетки цифрами от 1 до 9 так, чтобы в каждой строке, в каждом столбце и в каждом малом квадрате 3×3 каждая цифра встречалась бы только один раз.


Конструкция нашего робота не особенно замысловата, поэтому инструкцию по сборке мы решили не делать – вы легко соберете что-то подобное просто глядя на фото. Мы использовали следующие компоненты:
  • Блок Lego Mindstroms EV3 и строительные детали из этого набора
  • Raspberry Pi
  • Веб-камера Sony Playstation Eye
  • Wi-fi адаптер для связи EV3 с RPi
  • Powerbank для питания RPi

Программ, которые необходимы для работы робота - несколько, все они написаны на Python:
  • Программа для EV3 - отвечает за вывод информации на экран блока, связь с Raspberry Pi, передачу на RPi состояния датчика-кнопки
  • Программа для Raspberry Pi - отвечает за обработку кадров с камеры, распознаванием цифр с использованием обученной нейросети, последующим решением распознанного судоку, связь с EV3, вывод вспомогательной и отладочной информации на веб-консоль,
  • Программа для Raspberry Pi, предназначенная для создания дата сета для обучения нейронной сети.
  • Программа для ПК, используется для создания и обучения нейросети на основе подготовленного дата сета

Основная программа для Raspberry Pi работает по следующему алгоритму: 
  • Подключение библиотеки машинного зрения OpenCV
  • Подключение библиотек машинного обучения keras(tensorflow), sklearn
Процесс 1:
  • Взятие кадра с камеры
  • Преобразование изображения из BGR в HSV 
  • Наложение маски для фокусировки на значимых элементах изображения
  • Запись результирующего кадра в объект в памяти
  • Соединение с EV3 с помощью протокола socket 
  • Основной цикл:
  • Чтение кадра из объекта в памяти
  • Поиск в изображении с наложенной маской объектов, напоминающих квадрат 
  • Передача данных о предполагаемом расположении квадрата на EV3, для вывода на экран блока
  • Приём данных с EV3 о состоянии кнопки 
  • Если кнопка нажата:
Процесс 3:
  • Запуск веб-сервера (используется модуль Flask)
  • Соединение с Raspberry Pi с помощью протокола socket
  • Получение данных о предполагаемом расположении квадрата 
  • Подключение библиотеки машинного зрения OpenCV
  • Подключение библиотек машинного обучения keras(tensorflow), sklearn
Процесс 1:
  • Взятие кадра с камеры
  • Преобразование изображения из BGR в HSV 
  • Наложение маски для фокусировки на значимых элементах изображения
  • Запись результирующего кадра в объект в памяти
  • Соединение с EV3 с помощью протокола socket
  • Чтение кадра из объекта в памяти
  • Поиск в изображении с наложенной маской объектов, напоминающих квадрат 
  • Передача данных о предполагаемом расположении квадрата на EV3, для вывода на экран блока
  • Приём данных с EV3 о состоянии кнопки 
  • Если кнопка нажата:
  • Запуск веб-сервера (используется модуль Flask)
  • Вывод информации на веб-сервер - видеоконсоль для удобства отладки
  • Подключение библиотек машинного обучения 
  • Загрузка дата сета изображений 
  • Разбиение дата сета на учебные и тренировочные данные
  • Создание нейронной сети с заданными параметрами 
  • Обучение нейронной сети
  • Сохранение модели 

Процесс 2:
    • Изображение обрабатывается и судоку «нарезается» на 81 квадрат в каждом из которых одна клетка головоломки 
    • Каждый из фрагментов проверяется на наличие в нём цифры и если цифра обнаружена, то клетка подаётся на распознавание нейронной сети 
    • Распознанная цифра заноситься в массив. Когда массив заполнен всеми цифрами, он подаётся в алгоритм, решающий судоку 
    • Массив содержащий решённую головоломку передаётся на EV3 для вывода на экран блока 
  • Вывод информации на веб-сервер - видеоконсоль для удобства отладки

Программа на EV3 работает по следующему алгоритму:
  • Вывод контура квадрата на экран для визуального контроля (размер квадрата, угол разворота, степень его трапециевидности)
  • Передача состояния датчика-кнопки на Rpi
  • Если от RPi получен массив данных с решенной головоломкой - вывод решённого судоку на экран блока

Программа создания дата сета для обучения нейронной сети на Raspberry Pi имеет следующий алгоритм:
Процесс 2: 
    • Изображение обрабатывается и судоку «нарезается» на 81 квадрат в каждом из которых одна клетка головоломки 
    • Каждый квадрат сохраняется в папку, формируя дата сет
Процесс 3:

Программа на ПК, предназначенная для создания и обучения нейросети на основе подготовленного дата сета работает так: 
Стандартный алгоритм обучения нейронной сети мы усовершенствовали путём запуска функции обучения не на заданное количество эпох, а по одной эпохе, с проверкой результата на проверочной выборке после каждой. Если результат ухудшается несколько эпох подряд — прекращаем обучения с ошибкой. Если результаты на тестовой выборке выше 95% - прекращаем обучения, проверяем модель на отдельной выборке (которую она ещё не видела ни при обучении, ни при проверках после каждой эпохи).
Исходные коды всех программ проекта мы выложили здесь: https://github.com/darkavengersmr/Sudoku-Hunter
Демонстрация работы робота:

воскресенье, 17 сентября 2017 г.

НейроКачели

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


Наверное все мы в детстве любили качаться на качелях. Как вы думаете, если ребенка впервые посадить на качели и не показать ему как на них раскачиваться (и не давать соответственно подсмотреть как это делают другие), получится у него их раскачать, сохранять и контролировать скорость раскачивания? В его мозге нет устойчивых взаимосвязей и образов того как ему это сделать. Он начнет пробовать совершать произвольные действия, если качели начнут раскачиваться в мозг пойдет визуальная и вестибулярная обратная связь и по тем действиям, которые он только что предпринимал, начнут формироваться новые нейронные цепочки, связанные с "раскачать качели". Если какие то действия наоборот начнут тормозить раскачавшиеся качели, например ребенок отклонился не в ту сторону - он "запомнит" что так делать не нужно, соответствующие связи будут ослаблены или созданы новые.
Похожим образом работает и искусственная нейросеть, которую мы создадим в нашем сегодняшнем проекте.
Для начала соберем конструкцию. Качели мы построили на базе LEGO Mindstorms EV3, использовав несколько дополнительных деталей из ресурсных наборов. Инструкцию по сборке наиболее сложной части конструкции -  качающихся человечков - в формате LEGO Digital Designer вы можете скачать по ссылке. Остальную часть качелей каждый без труда сможет достроить из тех деталей, которые будут у него под рукой.


Человечки из больших EV3-моторов способны наклоняться, смещая центр тяжести качели. Качели закреплены на раме свободно, поэтому важно чтобы они были как можно более симметричными. Для робота потребуется гироскоп, на основе его показаний будет происходить самообучение нейронной сети.

Посмотрим на качели сбоку. изначально человечки "не знают", в какую сторону им нужно отклоняться (влево или вправо) чтобы качели начали раскачиваться. Хороший способ объяснять вероятности их наклона в ту или иную сторону  используя терминологию спичечных коробков и камушков черного и белого цвета. Пусть у нас есть два коробка, это два нейрона нашей нейронной сети. в каждом из них в случае необученной сети, лежит по 1 камушка каждого цвета. Таким образом  вытащить камушек белого и черного цвета случайным образом можно с вероятностью 50%, соответственно не обученная сеть с равной вероятностью будет отклонять человечков влево и вправо в любой ситуации.
Первый коробок соответствует состоянию когда качели качаются влево, второй - вправо. Если "вытащили" белый камень, человечкам нужно отклониться влево, если черный - вправо. Камень после того как мы его вытащили помещается на место, где он и был.

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

Алгоритм таков:
1) Смотрим куда качаются качели (по данным гироскопа) - влево или вправо и выбираем коробок - первый или второй. Следует заметить что мы не рассматриваем ситуацию когда качели имеют нулевую скорость, например при старте, развивая алгоритм дальше нужно добавить третью коробку с камушками, но пока обойдемся без нее.
2) Вытаскиваем наугад камень из выбранной коробки. Смотрим его цвет и кладем обратно. если камень белый - даем человечкам команду отклониться влево, если черный - вправо.
3) Оцениваем скорость качелей. Если она выросла - кладем в коробку, из которой мы брали камень еще один камень такого же цвета. Если скорость упала - убираем из коробки камень то го цвета, который был вытащен. Следует заметить что последний камень каждого цвета лучше не убирать, пусть остается.
4) Переходим к п.1

В процессе обучения возможна интересная ситуация - неподвижные качели можно раскачать даже неправильными движениями, но только до какого-то предела, если попытки предпринимать и дальше, то они наоборот замедлят раскачивание. В реальной работе робота ситуация действительно проявляется довольно часто, робот раскачивается до какого-то предела (небольшого), но дальше процесс не идет. Дальнейшие попытки раскачать качели приводят к их полной остановке и далее процесс обучение начинается снова, уже в "верном" направлении. И заметьте, это все без вмешательства человека - вполне разумная машина!


Программировать качели мы будем на Python.  Давайте посмотрим код:

# импортируем модуль для работы с железом EV3
from ev3dev.ev3 import *
# импортируем модуль математики
from math import *
# импортируем модуль для работы со временем
from time import *
# импортируем модуль генератора случайных чисел
from random import random
# импортируем модуль многопоточности
import threading

# начальная скорость моторов ("человечков")
speedB = 0
speedC = 0

# текущая средняя скорость движения качелей
moving_average = 0
# предыдущая средняя скорость движения качелей
moving_average_old = 0

# Позиция человечков -100..100
pos = 0

# направление движения качелей
state_gyro = 0

# описание нейрона сети
class brain():
    def __init__(self):
        self.Black = 1
        self.White = 1

# создаем пару нейронов
BrainLeft = brain()
BrainRight = brain()

# объект для отслеживания кнопок на блоке
btn = Button()

# объекты для моторов
B = LargeMotor('outB')
C = LargeMotor('outC')

# объект для работы с гироскопом
gyro = GyroSensor("in4")

# устраняем дрейф гироскопа и ставим его в режим измерения скорости
gyro.mode = "GYRO-RATE"
gyro.mode = "GYRO-ANG"
gyro.mode = "GYRO-RATE"

# человечков - в исходную позицию sleep(1)
B.run_forever(speed_sp=80)
C.run_forever(speed_sp=80)
sleep(3)

B.stop(stop_action="brake")
C.stop(stop_action="brake")
sleep(1)

B.reset()
C.reset()

B.run_to_rel_pos(position_sp=-120, speed_sp=80)
C.run_to_rel_pos(position_sp=-120, speed_sp=80)
while any(C.state): sleep(0.1)

B.stop(stop_action="brake")
C.stop(stop_action="brake")

sleep(1)
B.reset()
C.reset()

# готов к работе!
Sound.speak('Ready').wait()

stop = False

# П-регулятор для управления человечками
def reg():
    while not stop:
        speedB = ((-1*pos)-B.position)*8
        speedC = (pos-C.position)*8

        if(speedB > 900): speedB = 900
        if(speedB < -900): speedB = -900
        if(speedC > 900): speedC = 900
        if(speedC < -900): speedC = -900

        B.run_forever(speed_sp=speedB)
        C.run_forever(speed_sp=speedC)

        sleep(0.01)

# Запускаем регулятор в параллельном потоке
t = threading.Thread(target=reg)
t.daemon = True
t.start()

# Максимальная достигнутая скорость
max_speed = 0

while not stop:

    # вылет по кнопке "назад" на блоке     
    stop = btn.backspace
    
    # скорость по показаниям гироскопа
    state_gyro_speed = gyro.value()
        
    if(state_gyro_speed != 0): 
        # текущее направление движения качелей
        state_gyro = state_gyro_speed/abs(state_gyro_speed)
        if(state_gyro > 0):
            # если влево, и вытащили черный камень
            if(random() <= (BrainLeft.Black/(BrainLeft.Black + BrainLeft.White))): pos = -100
            # если белый 
            else: pos = 100
        else:
            # если вправо, и вытащили черный камень
            if(random() <= (BrainRight.Black/(BrainRight.Black + BrainRight.White))): pos = -100
            # если белый
            else: pos = 100
            
    moving_average_old = moving_average
       
    # ждем перед оценкой
    sleep(0.25)

    moving_average = abs(state_gyro_speed)*0.1 + moving_average*0.9

    if(state_gyro > 0):
        # если скорость не упала при движении влево
        if(moving_average >= moving_average_old): 
            if(pos<0): BrainLeft.Black += 1
            else: BrainLeft.White += 1
        else:
            # если скорость упала при движении влево
            if(pos<0 and BrainLeft.Black > 1): BrainLeft.Black -= 1
            elif(pos>0 and BrainLeft.White > 1): BrainLeft.White -= 1

    elif(state_gyro < 0):
        # если скорость не упала при движении вправо
        if(moving_average >= moving_average_old): 
            if(pos<0): BrainRight.Black += 1
            else: BrainRight.White += 1
        else:
        # если скорость упала при движении вправо
            if(pos<0 and BrainRight.Black > 1): BrainRight.Black -= 1
            elif(pos>0 and BrainRight.White > 1): BrainRight.White -= 1
    # выводим состояние нейронной сети     print("[[ " + str(BrainLeft.Black) + ", " + str(BrainLeft.White) + "][ " + str(BrainRight.Black) + ", " + str(BrainRight.White) + "]]",moving_average)

    # если скорость выросла на 25, фиксируем новый рекорд    
    if moving_average > max_speed + 25:
        print("[[ " + str(BrainLeft.Black) + ", " + str(BrainLeft.White) + "][ " + str(BrainRight.Black) + ", " + str(BrainRight.White) + "]]",moving_average)
        max_speed = moving_average
        # если скорость выросла до 200 - обучение завершено
        if max_speed < 200:
            Sound.speak('New Record')
        else:
            Sound.speak('Learning Complete')

Sound.beep().wait() 
B.stop(stop_action="brake")
C.stop(stop_action="brake")

# завершение работы
Sound.speak('Stop').wait()


print("[[ " + str(BrainLeft.Black) + ", " + str(BrainLeft.White) + "][ " + str(BrainRight.Black) + ", " + str(BrainRight.White) + "]]")

суббота, 7 января 2017 г.

Начинаем программировать EV3 на Python

Год назад мы перешли с графического программирование нашего робота LEGO Mindstorms EV3 на текстовое и первым языком программирования, который мы освоили, стал EV3 BASIC. Нам очень понравились его простота, отзывчивость к первым, зачастую неумелым попыткам выжать из него что-то большее, то, что мы могли получить на графическом "леговском" EV3-G. За этот год мы создали с использование BASICа целый ряд проектов (NAVIDOZ3R, Music Station, Телеграф, НУ ПОГОДИ, Саймон сказал), которые, как нам кажется, неплохо раскрыли возможности этого языка. Программы, написанные не нем работают существенно быстрее программ на "языке из кубиков". Бейсик не навязывает "хороший стиль" программирования, он дает свободу программисту - в этом его основная фишка.


Однако чем более сложными становились наши проекты с использованием EV3 BASIC, тем все более отчетливо мы ощущали его ограничения:

  • все переменные - глобальные
  • так же как и в EV3-G нет возможности работать с многомерными массивами без использования костылей
  • нельзя создать функцию с передачей в нее параметров и возвратом из нее результата
  • проблемы с поддержкой датчиков сторонних производителей

Хорошей альтернативой и следующей ступенькой в полноценном программировании EV3 традиционно считается ROBOTC, однако это достаточно дорогая ступенька, данная среда разработки далеко не бесплатна. Кроме этого ROBOC требует перепрошивки блока EV3 на свою прошивку, это не для всех приемлемо.
В мире opensource существует несколько свободных альтернатив для программирования платформы EV3 с использованием "взрослых" языков. Это LeJOS и язык Java, Monobrick с языком С# и ev3dev с целым рядом языков - Python, C, C++, JavaScript. Go.


Мы решили начать с ev3dev и языка Python, так как ходили слухи что язык этот имеет достаточно низкий порог вхождения подобно Бейсику, при этом не имея присущих ему недостатков. Еще на летних каникулах мы проштудировали замечательную детскую книгу по Python под названием "Hello World! Занимательное программирование" (Картер Сэнд, Уоррен Сэнд, 2016). На примере создания простейших игр книга действительно занимательно погружает читателя в основы языка Python.


Начав разбираться с ev3dev нам показалось, что информации из книги для погружения в Python не достаточно и в поисках ее дополнительных источников мы набрели на крутые онлайн-курсы на портале Stepic.org



В процессе прохождения первого курса, в попытках решения учебных задач с Python на EV3 пришло понимание того, что базовые навыки работы в ОС Linux пришлись бы юным робототехникам как нельзя кстати. Курс на том же Stepic под названием Введение в Linux оставил только положительные эмоции и дал массу полезной информации, в том числе и для размышления. 


Очень полезным источником информации для нас стал сайт по EV3 Python. Его автор, Nigel Ward, уже помогал нам год назад в адаптации EV3 BASIC для русскоязычных робототехников. Его материалы мы взяли за основу и в этот раз. Мы подготовили русскоязычное руководство по Python для EV3, которое состоит из двух частей:


В первой части - описание процесса установки и первоначальной настройки ev3dev. Во второй - справочник по "командам" и примеры программ на Python для платформы EV3.

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


Исходные кода всех программ, а также инструкцию к роботу, способному их решить вы можете скачать по ссылке.



1. Робот, поворачиваясь вокруг своей оси сканирует пространство, после чего выводит на экран "карту-радар" в виде лепестковой диаграммы.


# подключаем модуль поддержки оборудования EV3
from ev3dev.ev3 import *
# из модуля time будем использовать задержку - sleep
from time import sleep
# кроме это нам понадобится модуль математики
import math

# Это скорость разворота робота, в градусах/сек
speed = 50
# переменная для хранения значений с датчика-гироскопа
GyVal = 0

# создаем объект Gyro связанный с датчиком-гироскопа на 1 порту
Gyro = GyroSensor("in1")
# и объект IR, связанный с ИК-датчиком на порту 4
IR = InfraredSensor("in4")

# нам понадобится пара моторов, порты A и D
mA = LargeMotor('outA')
mD = LargeMotor('outD')

# устанавливаем гироскоп в режим измерения угла
Gyro.mode = "GYRO-ANG"

# создаем объект lcd для работы с экраном блока EV3
lcd = Screen()
# рисуем в буфере пару кругов (это скелет карты)
lcd.draw.ellipse(( 84, 59,  94, 69))
lcd.draw.ellipse(( 25, 0,  153, 128))
# выводим инфу из экранного буфера на экран
lcd.update()

# создаем список (массив) для хранения карты 
g = [0 for i in range(360)]

# запускаем робота на вращение вокруг своей оси
mA.run_forever(speed_sp=speed)
mD.run_forever(speed_sp=-1*speed)

# во время вращения заполняем массив g данными
# с ИК-датчика с привязкой у направлению (гироскоп)
while Gyro.value() < 360:
    GyVal =  abs(Gyro.value())    
    g[GyVal] = IR.value()*0.7

# после полного круга останавливаемся
mA.stop(stop_action="hold")
mD.stop(stop_action="hold")  

# выводим карту из массива в экранный буфер
# в виде лепестковой диаграммы
for i in range(90):
    lcd.draw.line(( 89, 64,  89+g[i]*math.sin(math.radians(i)), 64-g[i]*math.cos(math.radians(i))))
    lcd.draw.line(( 89, 64,  89+g[i+90]*math.cos(math.radians(i)), 64+g[i+90]*math.sin(math.radians(i))))
    lcd.draw.line(( 89, 64,  89-g[i+180]*math.sin(math.radians(i)), 64+g[i+180]*math.cos(math.radians(i))))
    lcd.draw.line(( 89, 64,  89-g[i+270]*math.cos(math.radians(i)), 64-g[i+270]*math.sin(math.radians(i))))

# выводим информацию из буфера на экран
lcd.update()
# издаем какой-то звук
Sound.beep()

# и замираем на 10 секунд чтобы успеть посмотреть карту
sleep(10)

2. Плавное изменение цветов и яркости подсветки по нажатию кнопок на блоке, например вверх-вниз изменяют яркость, влево-вправо изменяет цвет, нажатие в центр запускает автоматические переливы. Голосом озвучивается изменения режимов.

# подключаем модуль поддержки оборудования EV3
from ev3dev.ev3 import *
# из модуля time будем использовать задержку - sleep
from time import sleep

# объект для работы с кнопками на блоке  btn = Button()

tr = 1

# список цветов подсветки color = [Leds.RED, Leds.GREEN, Leds.AMBER, Leds.ORANGE, Leds.YELLOW]
cs = 0

# выключаем подсветку Leds.all_off()

# текущая яркость подсветки
on = 127

# цикла - пока не нажата кнопка назад while not btn.backspace:
    # если нажата кнопка влево
    if btn.check_buttons(buttons=['left']):
        cs=cs-1
        if cs<0:
            cs=4
        # проговариваем ее название
        # и изменяем цвет с выбранной яркостью         Sound.speak("Button left")
        Leds.set_color(Leds.LEFT, color[cs], on/255)
        Leds.set_color(Leds.RIGHT, color[cs], on/255)
        sleep(0.5)   

    # тоже самое для кнопки вправо     
elif btn.check_buttons(buttons=['right']):
        cs=cs+1
        if cs>4:
            cs=0

        Sound.speak("Button right")
        Leds.set_color(Leds.LEFT, color[cs], on/255)
        Leds.set_color(Leds.RIGHT, color[cs], on/255)
        sleep(0.5)

    # если нажата кнопка вверх
    if btn.check_buttons(buttons=['up']):
        # увеличиваем яркость подсветки
        on=on+10
        if on>240:
            Sound.speak("Maximum")
            on=255

        Leds.set_color(Leds.LEFT, color[cs], on/255)
        Leds.set_color(Leds.RIGHT, color[cs], on/255)
        sleep(0.1)

    # если нажата кнопка вниз
    elif btn.check_buttons(buttons=['down']):
        # уменьшаем яркость
        on=on-10
        if on<10:
            Sound.speak("Minimum")
            sleep(2)
            on=0
        
        Leds.set_color(Leds.LEFT, color[cs], on/255)
        Leds.set_color(Leds.RIGHT, color[cs], on/255)
        sleep(0.1)

    # при нажатии центральной кнопки     
if btn.check_buttons(buttons=['enter']):
        Sound.speak("Random color")
        tr = 1
        # цикл пока не нажата кнопка назад
        while not btn.backspace or tr!=0:
            Leds.set_color(Leds.RIGHT, Leds.RED,0)
            Leds.set_color(Leds.LEFT, Leds.GREEN,0)
            
            # "переливы" цветов и яркости подсветки
            for i in range(0,255,5):
                Leds.set_color(Leds.LEFT, Leds.GREEN,i/255)
                if btn.check_buttons(buttons=['backspace']):
                    tr = 0            
            if tr == 0:
                break                

            for i in range(255,0,-5):
                Leds.set_color(Leds.RIGHT, Leds.ORANGE,(255-i)/255) 
                Leds.set_color(Leds.LEFT, Leds.GREEN,i/255) 
                
                if btn.check_buttons(buttons=['backspace']) or tr == 0:
                    break              
        
            for i in range(0,255,5):
                Leds.set_color(Leds.LEFT, Leds.GREEN,i/255)
                if btn.check_buttons(buttons=['backspace']):
                    tr = 0
            if tr == 0:
                break
            Leds.set_color(Leds.RIGHT, Leds.ORANGE,1)

            for i in range(255,0,-5):
                Leds.set_color(Leds.RIGHT, Leds.ORANGE,i/255)
                Leds.set_color(Leds.LEFT, Leds.GREEN,i/255)

                if btn.check_buttons(buttons=['backspace']): 
                    break  
            for i in range(0,255,5):
                Leds.set_color(Leds.LEFT, Leds.YELLOW,i/255)
                if btn.check_buttons(buttons=['backspace']):
                    tr = 0
            if tr == 0:
                break

            for i in range(255,0,-5):
                Leds.set_color(Leds.RIGHT, Leds.RED,(255-i)/255)
                Leds.set_color(Leds.LEFT, Leds.YELLOW,i/255)

                if btn.check_buttons(buttons=['backspace']) or tr == 0:
                    break

            for i in range(0,255,5):
                Leds.set_color(Leds.LEFT, Leds.YELLOW,i/255)
                if btn.check_buttons(buttons=['backspace']):
                    tr = 0
            if tr == 0:
                break
            Leds.set_color(Leds.RIGHT, Leds.RED,1)

            for i in range(255,0,-5):
                Leds.set_color(Leds.RIGHT, Leds.RED,i/255)
                Leds.set_color(Leds.LEFT, Leds.YELLOW,i/255)

                if btn.check_buttons(buttons=['backspace']):
                    break

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

# подключаем модуль для работы с EV3
from ev3dev.ev3 import *
# модуль для работы со временем
from time import *
# также нам потребуется pi из модуля математики
from math import pi

# объект для работы с кнопками на блоке
btn = Button()

# пара объектов для работы с моторами
mA = LargeMotor('outA')
mD = LargeMotor('outD')

# сбрасываем состояние энкодеров
# при старте программы они автоматически не сбрасываются!
mA.reset()
mD.reset()

# пара объектов для работы с датчиками освещенности
# у нас датчики от NXT 1.0, если будете использовать
# датчики цвета EV3, используйте класс ColorSensor
CS1 = LightSensor('in2')
CS2 = LightSensor('in3')

# режим работы датчиков - отраженный свет
# для EV3-датчиков цвета используйте 'COL-REFLECT' CS1.mode='REFLECT'
CS2.mode='REFLECT'

# средняя скорость движения по линии, градусов на энкодерах в сек speed = 800
# начальные сведения о радиусе круга - он неизвестен
r = 0

# параметры работы ПД-регулятора
u = 0
e = 0
es = 0
Pk = 1.1
Dk = 2

# создаем функцию для ограничения скорости моторов
# диапазоном -900..900 град/сек
def motor(speed):
    if speed>900:
        speed = 900
    elif speed<-900:
        speed = -900
    return speed

# цикл пока не нажата кнопка "назад"
while not btn.backspace:
    # рассчитываем ошибку для ПД-регулятора
    e = CS1.value() - CS2.value()
    # рассчитываем управляющее воздействие регулятора
    u = e*Pk + (e-es)*Dk
    # сохраняем ошибку для следующей итерации
    es = e

    # подаем управляющее воздействие на моторы
    # используя функцию, ограничивающую скорость  
    mA.run_forever(speed_sp=motor(speed+u))
    mD.run_forever(speed_sp=motor(speed-u))

    # рассчитываем радиус круга     
    if mD.position != mA.position:
        r = (61*(mA.position + mD.position))/(abs(mD.position-mA.position))
    # выводим на консоль текущий радиус круга
    print(r)   
    # небольшая задержка для работы регулятора
    sleep(0.01)
    # если проехали полный круг - вылет из цикла
    if (mA.position*(pi*56/360)+mD.position*(pi*56/360))/2 > pi*(r*2) and time()>3:
        break

# останавливаем моторы методом торможения
mA.stop(stop_action="hold")
mD.stop(stop_action="hold")

# произносим рассчитанный в процессе движения диаметр круга
Sound.speak('Stoped').wait()
Sound.speak(round(r*2)).wait()

4. Робот, имеющий в своей конструкции гироскоп, должен описать круг вокруг кегли и, вернувшись на место старта, остановиться.

# подключаем модуль для работы с EV3
from ev3dev.ev3 import *
# из модуля для работы со временем нам понадобится sleep
from time import sleep
# подключаем модуль математики
from math import *

# переменные для хранение данных с энкодеров
ma = 0
md = 0

# объект для работы с кнопками на блоке
btn = Button()
# средняя скорость робота, град/сек
speed = 200

# объекты для работы с моторами mA = LargeMotor('outA')
mD = LargeMotor('outD')

# объекты для работы с датчиками - инфракрасным и гироскопом IR = InfraredSensor('in4')
Gyro = GyroSensor("in1")

# устанавливаем режим работы гироскопа (измерение угла) Gyro.mode = 'GYRO-ANG'

# создаем функцию для ограничения скорости моторов
# диапазоном -900..900 град/сек
def motor(speed):
    if speed>900:
        speed = 900
    elif speed<-900:
        speed = -900
    return speed

# переменные для хранения расстояния и направления на ближайшую банку
MIN = 100
MINGRAD = 0

# в массиве g будем хранить данным с гироскопа
g = [0 for i in range(360)]

# вращаемся в поисках банки
mA.run_forever(speed_sp=speed)
mD.run_forever(speed_sp=-1*speed)

# находим ближайшую банку
while Gyro.value() < 359:
    GyVal =  Gyro.value()
    g[GyVal] = IR.value()*0.7

    if g[GyVal] < MIN:
        MIN = g[GyVal]
        MINGRAD = GyVal    

# останавливаемся
mA.stop(stop_action="hold")
mD.stop(stop_action="hold")

Sound.beep()

# расчет угла для поворота к банке боком
MINGRAD = MINGRAD + 90

# разворачиваемся к банке левым бортом
while abs(Gyro.value()-MINGRAD) > 1:
    speedL = -1*(Gyro.value()-MINGRAD)*5
    speedR = (Gyro.value()-MINGRAD)*5
    mA.run_forever(speed_sp=motor(speedL))
    mD.run_forever(speed_sp=motor(speedR))

# останавливаемся
mA.stop(stop_action="hold")
mD.stop(stop_action="hold")

sleep(2)

# сбрасываем гироскоп Gyro.mode = 'GYRO-RATE'
Gyro.mode = 'GYRO-ANG'

sleep(1)

grad = 0
# расчет длины стороны 360-ти угольника
i = 2*(MIN*6+70)*tan(radians(0.5))

# сброс энкодеров
mA.reset()
mD.reset()

# снижаем скорость до 50 граду/сек
speed = 50

# цикл - пока не проехали полный круг
# или не нажата кнопка
while grad > -360 and not btn.backspace:
    ob = 56*pi/360
    # если проехали 1 сторону 360-ти угольника
    if ((mA.position-ma)*ob+(mD.position-md)*ob)/2 > i:
        # поворачиваемся на 1 градус
        grad = grad-1
        ma = mA.position
        md = mD.position
    
    # пропорциональный регулятор от grad     
    speedL = speed-(Gyro.value()-grad)*16
    speedR = speed+(Gyro.value()-grad)*16

    # перед подачей на моторы ограничиваем скорость ф. motor()     
    mA.run_forever(speed_sp=motor(speedL))
    mD.run_forever(speed_sp=motor(speedR))

# останавливаемся после того как проехали круг
mA.stop(stop_action="hold")
mD.stop(stop_action="hold") 

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

# подключаем модуль для работы с EV3
from ev3dev.ev3 import *
# нам потребуются модули математики и времени
from math import *
import time

# объект для работы с кнопками на блоке
btn = Button()

# средняя скорость движения робота, град/сек
speed = 300

# объекты для работы с моторами
mA = LargeMotor('outA')
mD = LargeMotor('outD')

# объекты для работы с датчиками
LS1 = LightSensor('in2')
LS2 = LightSensor('in3')
# режим работы датчиков освещенности - отраженный свет
LS1.mode = 'REFLECT'
LS2.mode = 'REFLECT'

# объект для работы с гироскопом
Gyro = GyroSensor("in1")
# режим работы гироскопа - измерение угла
Gyro.mode = "GYRO-ANG"

# функция для ограничения скорости мотора (-900..900)
def motor(speed):    
    if speed>900:   
        speed = 900                             
    elif speed<-900:      
        speed = -900 
    return speed

# сброс энкодеров
mA.reset()
mD.reset()

# параметры ПД-регулятора
e = 0
es = 0
u = 0
Pk = 3
Dk = 6

# диаметр круга роботу неизвестен
D = 0

# пока не нажата кнопка назад
while not btn.backspace:
    e = LS1.value()/10 - LS2.value()/10
    u = e*Pk + (e-es)*Dk    
    es = e
      
    mA.run_forever(speed_sp=motor(speed+u))
    mD.run_forever(speed_sp=motor(speed-u))

    if mA.position != mD.position:
        D = (61*(mA.position + mD.position))/(abs(mA.position-mD.position))
    print(D)

    if (mA.position*(56*pi/360)+mD.position*(56*pi/360))/2 > pi*(D*2) and time.time() > 3:
        break

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

mA.stop(stop_action="hold")
mD.stop(stop_action="hold")
Sound.beep()

# сброс гироскопа
grad = 0
ma = 0
mb = 0
time.sleep(1)
Gyro.mode = 'GYRO-RATE'
Gyro.mode = 'GYRO-ANG'

speedL = 0
speedR = 0

ma = mA.position
mb = mD.position

tmp = D
speed = 50

# аналогично третьей задаче движемся по правильному
# 360-ти угольнику, но длину сторону уменьшаем 
# после каждой стороны, делая "круг" меньше
# как только диаметр вписанной окружности станет
# менее 1 см - останавливаемся

while not btn.backspace:
    ob = 56*pi/360
    if ((mA.position-ma)*ob+(mD.position-mb)*ob)/2 > (2*D)*tan(radians(0.5)):

        if mA.position > mD.position:
            grad = grad+1             
        else:
            grad = grad-1
        
        D = D-tmp/720
        ma = mA.position
        mb = mD.position

    speedL = speed-(Gyro.value()-grad)*10
    speedR = speed+(Gyro.value()-grad)*10

    mA.run_forever(speed_sp=motor(speedL))
    mD.run_forever(speed_sp=motor(speedR))

    print(D)
    if D<0.5:
        break

mA.stop(stop_action="hold")
mD.stop(stop_action="hold")
Sound.beep()

Самое популярное