Год назад мы перешли с графического программирование нашего робота LEGO Mindstorms EV3 на текстовое и первым языком программирования, который мы освоили, стал EV3 BASIC. Нам очень понравились его простота, отзывчивость к первым, зачастую неумелым попыткам выжать из него что-то большее, то, что мы могли получить на графическом "леговском" EV3-G. За этот год мы создали с использование BASICа целый ряд проектов (NAVIDOZ3R, Music Station, Телеграф, НУ ПОГОДИ, Саймон сказал), которые, как нам кажется, неплохо раскрыли возможности этого языка. Программы, написанные не нем работают существенно быстрее программ на "языке из кубиков". Бейсик не навязывает "хороший стиль" программирования, он дает свободу программисту - в этом его основная фишка.
Однако чем более сложными становились наши проекты с использованием EV3 BASIC, тем все более отчетливо мы ощущали его ограничения:
Хорошей альтернативой и следующей ступенькой в полноценном программировании 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
Очень полезным источником информации для нас стал сайт по 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()
Однако чем более сложными становились наши проекты с использованием 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 оставил только положительные эмоции и дал массу полезной информации, в том числе и для размышления.
В первой части - описание процесса установки и первоначальной настройки 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
# подключаем модуль для работы с 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()
Как долго я это искал!Большое спасибо
ОтветитьУдалитьДоброго времени суток!
ОтветитьУдалитьУ меня вот такая проблема:
Перешел по ссылке "Начинаем работать с EV3 в ev3dev" и за ступорился на 4 шагу. Сделал все как написано, запись образа на карту microSDHC прошла успешна. Вставил карту, включил EV3, подсветка кнопок быстро моргнула оранжевым и в то же время на экране появились большая надпись «ev3dev» и строки с мелким шрифтом. Дальше ни что не моргало, ждал минуты 10, однако меню программы Brickman так и не появилась. Блок ни к чему не был подключен.
P.S. Заметил вышла новая версия Win32DiskImager (1.0). В этой новой версии появились дополнительные опции. Может дело в них?
EV3 достаточно капризен относительно качества карт памяти. Попробуйте другую карту, лучше 4-8 Gb SDHC известного производителя.
УдалитьСпасибо! Действительно капризен к картам. Проверил на трех картах: SD(2GB), SDHC(4GB, class4) (производителей не запомнил) - полет нормальный; а вот карта от Самсунга SDHC (8GB, class10) - та самая злосчастная карта.
ОтветитьУдалитьЗдравствуйте, как так может получиться, что у вас Samsung SDHC (8 gb, 10class) не работает?) У меня, собственно, с этой же карты всё замечательно запускается)))
УдалитьВ той же ссылке в одном из последующих шагов вышла проблема:
ОтветитьУдалитьПри разрешении доступа к Интернету для EV3 в свойствах основного соединения PC пишет: "Ошибка при разрешении общего доступа к подключению к Интернету.
(null)". Подскажите пожалуйста, что делать?
Добрый день. Прочитал документацию по программированию ev3dev и так не нашёл как удалять программы из File Browser. Подскажите пожалуйста.
ОтветитьУдалитьЗдравствуйте. Я прочитал всю документацию по ev3dev, но так и не нашёл как удалять программы с File Browser. Подскажите пожалуйста.
ОтветитьУдалитьОн не имеет такой функциональности - позволяет только запускать и останавливать программы.
УдалитьФайл удалить можно используя консольную команду rm
ссылка на курс Введение в Linux ошибочная
ОтветитьУдалитьВопрос по инструкции Начинаем работать с EV3 в ev3dev. Если запускаю предложенную программу в терминале, то все нормально, если прямо с кирпича, то до конца не договаривает. С чем это связано?
ОтветитьУдалитьМенеджер Brickman слишком рано прерывает процесс. Добавьте задержку в конце программы в 1-2 сек.
УдалитьХорошее дело делаете! Есть вопрос по сторонним датчикам (Hi-Technic, в частности) - есть опыт их использования? Откуда брать синтаксис команд и как интерпретировать их показания?
ОтветитьУдалитьХочется следующую программу для WRO написать на "взрослом языке" :) Заранее спасибо
Со всеми датчиками работа ведется единообразно, главное перевести в нужный режим (о режимах здесь http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-jessie/sensors.html). Чтение с любого датчика - метод value().
УдалитьПрограмма на python/ev3dev никак не защищена от ее копирования из робота, стоящего в карантине, учитывайте это.
Спапсибо за ответ - почему-то сходу не увидел эту информацию в документации EV3Dev. Я правильно понял, что любой сторонний сенсор инициализируется строкой типа:
Удалитьhtsensor = Sensor(in1)
и затем переключается в нужный режим соотв. командой:
htsensor.mode = "RAW"
Про отсутствие защиты от копирования - вы имеете ввиду, что SD-карту может кто-то вытащить или другое? Программу ведь можно записать в модуль непосредственно перед началом соревнований.
И еще в связи с этим возник такой вопрос - посколько программа на Python компилируется внутри блока EV3, то это занимает определенное время (секунды, но все же), которое критично в дисциплинах типа WRO Regular с жесткими временными рамками. Как сделать так, чтобы с блока запускался заранее скомпилированный код? Заранее спасибо.
Еще не проверяли, но похоже для сторонних i2c-датчиков нужно указать кроме порта еще и адрес, через двоеточие (http://ev3dev-lang-python-1.readthedocs.io/en/restruct-docs/sensors.html).
УдалитьПосле карантина программу переписать не дадут. Во время карантина программа в роботе не защищена.
Робота можно запускать по кнопке, поэтому вся инициализация и долгий запуск/загрузка остаются вне времени попытки.
Как вариант борьбы с "ненадежным карантином" - на карантин вставлять одну sd, потом во время калибровки и тестовых проездов менять на нужную.
ОтветитьУдалитьТестовые заезды и калибровка происходят до карантина. После карантина менять карту памяти нельзя, за этим строго следят. Иначе возникнет ситуация когда удаленное НИИ во время карантина напишет новую убойную программу, а участник принесет ее на новой карте памяти.
УдалитьЭто конечно все равно не страхует от удаленного доступа к роботу посредством скрытых беспроводных сетей и прочих махинаций.
Ок, понятно. Хотя мы пока не сталкивались с такими серьезными ограничениями на региональных и городских этапах WRO в Казахстане...
ОтветитьУдалитьВот рабочий код (выводит на экран номер цвета) для датчика HT Color Sensor - может будет полезен:
from ev3dev.ev3 import *
from time import *
import ev3dev.fonts as fonts
LS1 = Sensor('in1')
LS1.mode = 'COLOR'
lcd = Screen()
btn = Button()
while True:
if btn.any():
exit()
else:
lcd.clear()
color = LS1.value(0)
text = str(color)
lcd.draw.text((3, 20), "Color Number:", font=fonts.load('helvB24'))
lcd.draw.text((80, 50), text, font=fonts.load('helvB24'))
lcd.update()
sleep(0.3)
sleep(1)
Отлично, спасибо!
УдалитьЕще такой вопрос - можно ли отключать моторы/сенсоры программным путем - чтобы при нарушении контакта (напр. провод отошел) программа не вылетала сразу же, а продолжала работать?
ОтветитьУдалитьПрограмма не вылетит, если в критических местах обрабатывать исключения. Погуглите конструкция try - except.
УдалитьОк, спасибо за наводку - почитал, в целом разобрался. Однако неясен синтаксис ошибки после except.
ОтветитьУдалитьКонкретная ситуация: в процессе выполнения программы отошел провод датчика цвета, однако есть смысл продолжить маршрут, предположив искомое значение цвета (считаем, что моторы в порядке).
try:
Удалитьa = S.value()
except Exception:
# действия в случае ошибки, например
# a = 0 принудительно
спасибо!
УдалитьEV3 BASIC, понравился?! Python или LeJOS - вот это я понимаю.
ОтветитьУдалитьБоюсь, вы как раз не понимаете некоторых нюансов использования разных средств программирования применительно к EV3.
УдалитьЗдравствуйте, есть просьба к заданию №3. Попробуйте запустить робота по замкнутой кривой, а не по кругу. У меня догадка, что он все равно правильно измерит длину кривой и остановится на месте старта.
ОтветитьУдалитьЗаранее спасибо.
Здравствуйте!
ОтветитьУдалитьМожете подсказать как устанавливать библиотеки на ev3dev?
Если речь о модулях Python, на примере numpy:
Удалитьsudo apt-get install python3-pip
sudo pip3 install numpy