Рейтинг@Mail.ru

Программируем робота LEGO Mindstorms EV3 на Java

Автор: Alex. Опубликовано в Копилка . просмотров: 55022

Рейтинг:  5 / 5

Звезда активнаЗвезда активнаЗвезда активнаЗвезда активнаЗвезда активна
 

С помощью чего только не программируются роботы LEGO Mindstorms EV3. Поддерживается большое количество языков программирования на любой вкус. Но сегодня я хочу рассказать вам, о самом, на мой взгляд, удобном и богатом, в плане возможностей, способе программирования роботов LEGO Mindstorms EV3. Удобный, потому что разрабатывать можно в среде разработки Eclipse на языке Java, а богатый, потому, что виртуальная машина Java для EV3, которая называется leJOS, поддерживает не только стандартные возможности EV3, но даёт и гораздо больше.

Программируем робота LEGO Mindstorms EV3 на Java с использованием leJOS

Итак, давайте для начала, чтобы вам было интереснее, я перечислю основные плюсы виртуальной Java-машины leJOS (страничка проекта находится здесь) по сравнению с официальной средой разработки LEGO, основанной на графическом языке программирования LabVIEW:

    1. Во-первых, конечно же, с leJOS вы получаете возможность использовать объектно-ориентированный язык Java, со всеми вкусностями, в том числе массивы (многомерные массивы тоже), рекурсию, синхронизацию, исключения, большинство Java-классов из библиотек java.lang, java.util, java.io и т.д.
    2. С помощью специального плагина вы будете разрабатывать программы в продвинутой среде разработки Eclipse с возможностью полноценной отладки кода.
    3. Вы сможете создавать приложения, работающие не только в модуле EV3, но и приложения для смартфонов, планшетов и компьютеров для удалённого взаимодействия с роботом. В том числе вы сможете создавать приложения для модулей EV3, которые смогут удалённо взаимодействовать с другими модулями EV3 и NXT.
    4. С веб-камеры, подключенной к модулю EV3, вы сможете захватывать изображения или видео и отображать их на дисплее или удалённо передавать их на компьютер или мобильное устройство. Правда видео будет с очень низкой частотой кадров из-за низкой производительности модуля EV3. Но для работы с картинкой мощности EV3 достаточно. Камеру можно использовать, например, вместо датчика цвета.
    5. Также вы сможете воспользоваться утилитами для постройки карт помещения и передвижения по ним с огибанием препятствий (EV3 Map Command и EV3 MCL Command) и удобным центром управления с просмотром в реальном времени, что отображается на дисплее EV3 (EV3 Control Center).
    6. Здесь поддерживаются некоторые датчики NXT, RCX, HiTechnic, Mindsensors, Dexter и Cruizcore Gyro. Из моторов поддерживаются моторы NXT, RCX, PF и Tetrix. Также поддерживаются подключенные к USB-порту GPS-приёмники (с компасом и без него), подробнее см. пакет lejos.hardware.gps.
    7. В будущей версии (после 0.9.0-beta) намечен выход классов Chassis (шасси), позволяющих с лёгкостью управлять колёсным роботом любой конструкции, в том числе с Omni-колёсами или с рулевым управлением, просто указывая направление движения робота. Вы лишь задаёте диаметры колёс, расстояния между ними и т.п., а все расчёты (как повернуть колёса и с какой скоростью) и управление моторами берут на себя классы Chassis.

После того как вам стало интересно, наметим план, по которому мы будем устанавливать и настраивать нужное ПО. Сразу скажу, что всё описанное здесь относится к ПК под управлением Windows, хотя все перечисленные программы можно установить и использовать на ОС Mac OS, Mac OS X и Linux. Первые четыре шага, которые вам нужно сделать, уже описаны в статье «Программирование робота Lego Mindstorms EV3 с помощью Scratch 2.0», поэтому здесь я лишь привожу ссылки на эту статью. После того как SD-карта с виртуальной машиной leJOS EV3 будет готова и модуль EV3 будет подключен к компьютеру, возвращайтесь обратно в эту статью. Итак, вот план действий:

    1. Подготовка SD-карты.
    2. Установка компонентов leJOS на компьютер.
    3. Создание SD-карты leJOS EV3.
    4. Подключение модуля EV3 к компьютеру.
    5. Установка и первый запуск Eclipse.
    6. Установка плагина leJOS EV3 в Eclipse.
    7. Утилиты leJOS.

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

    1. Разработка с leJOS EV3 – создание проекта, запуск и отладка программы.
    2. Управление моторами в leJOS EV3.
    3. Чтение показаний датчиков в leJOS EV3.
    4. Использование веб-камеры в leJOS EV3 для захвата изображения.
    5. Удалённый доступ к EV3.

Установка и первый запуск Eclipse

Если у вас установлен Eclipse, то вы можете пропустить этот пункт или просто обновить его. Установка сводится к загрузке нужного архива и распаковки его на вашем компьютере, например, в корень диска C: (после распаковки у вас появится папка «C:\eclipse»). Рекомендуется использовать дистрибутив для Java-разработчиков (Eclipse IDE for Java EE Developers).

Для запуска Eclipse запустите файл eclipse.exe, который находится в папке eclipse. В процессе запуска Eclipse спросит место расположения рабочей папки (workspace). Можете оставить папку по умолчанию или поменять её. Я выбрал папку «C:\workspace».

После того как Eclipse запустится нам нужно выбрать тот же JDK, который был выбран при установке leJOS на компьютер. Для этого выберите пункт меню «Window -> Preferences» и в диалоге настроек выберите «Java -> Installed JREs», затем нажмите на кнопку «Search...» и выберите папку с JDK, у меня это папка «C:\Program Files\Java\jdk1.7.0_25». После поиска в списке «Installed JREs» появится найденная виртуальная машина, поставьте галку рядом с ней и нажмите «OK».

Добавление установленой Java-машины в Eclipse

Установка плагина leJOS EV3 в Eclipse

Перед тем как начинать программировать нам нужно установить плагин leJOS EV3 в Eclipse. Чтобы это сделать удостоверьтесь, что компьютер подключен к Интернету, затем вызовите пункт меню «Help -> Install New Software...», в открывшемся диалоге нажмите на кнопку «Add...» и в следующем диалоге вбейте строку «leJOS EV3» в поле «Name» и ссылку «http://lejos.sourceforge.net/tools/eclipse/plugin/ev3» в поле «URL» и нажмите на кнопку «OK». После этого вы увидите плагин в списке. Отметьте его галочкой и нажмите на кнопку «Next >».

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

Установка плагина leJOS EV3 в Eclipse

На следующем шаге нажмите «Next >».

Установка плагина leJOS EV3 в Eclipse (шаг 2)

Если запустить установку вышеописанным способом не удалось, вы можете это сделать с помощью магазина Eclipse. Для этого щёлкните по меню «Help -> Eclipse Marketplace...», в поднявшемся диалоге найдите «leJOS EV3» и нажмите кнопку «Install» напротив плагина «leJOS EV3 Plug-In».

Установка плагина leJOS через Eclipse Marketplace

На следующем шаге вы должны принять лицензионное соглашение, поставив галочку «I accept the terms of the license agreement», и нажать на кнопку «Finish».

Соглашение с лицензией при установке плагина leJOS EV3 в Eclipse

После этого при установке плагина вы получите предупреждение, что устанавливаемое ПО не имеет подписи. Нажмите «OK».

Предупреждение, что плагин leJOS EV3 для Eclipse не имеет подписи

После того как процесс установки будет завершён, вы увидите сообщение, что Eclipse нужно перезапустить. Нажмите «Yes», чтобы Eclipse перезапустился.

Предложение перезапустить Eclipse после установки плагина

После перезапуска вызовите пункт меню «Window -> Preferences», в поднявшемся окне настроек щёлкните в списке слева по «leJOS EV3» и проверьте настройки плагина. В поле «EV3_HOME» должен быть указан путь к папке, в которой установлено «leJOS EV3» (тот же путь, который был выбран при установке компонентов leJOS на компьютерleJOS на компьютер). Галка «Run Tools in separate JVM» должна быть установлена, а галка «Use ssh and scp» - нет. Здесь же, если у вас один модуль EV3, можно поставить галочку «Connect to named brick» и в поле «Name» указать IP-адрес этого модуля (где взять адрес, читайте ниже). После проверки и настройки, нажмите «OK».

Настройка плагина leJOS EV3 в Eclipse

IP-адрес можно посмотреть на дисплее модуля EV3. При подключении модуля EV3 к компьютеру через Bluetooth используйте IP-адрес, который вы видите непосредственно под надписью EV3. А при подключении через WiFi (с помощью WiFi-адаптера) используйте второй IP-адрес (на второй картинке – 192.168.0.9).

Меню leJOS EV3 Меню leJOS EV3 с подключенным WiFi-адаптером

Утилиты leJOS

После установки leJOS на Windows-компьютер с помощью дистрибутива вы можете обнаружить в папке bin внутри папки установки (у меня это папка «C:\Program Files\leJOS EV3\bin») .bat файлы для запуска следующих утилит:

        • EV3Console – консоль, которая подключается к порту 8001 модуля EV3 и отображает System.out и System.err;
        • EV3Control – по сути это центр управления вашим EV3: позволяет обнаружить все EV3 в локальной сети, подключиться к выбранному и видеть и управлять меню EV3 удалённо, загружать файлы, запускать программы, управлять моторами, считывать показания с датчиков, просматривать сообщения System.out и System.err (как в EV3Console) и т.п.;
        • EV3MapCommand – позволяет загружать карту помещения (в SVG-формате) и автоматически управлять передвижением робота используя загруженную карту и установленные путевые точки (для управления прокладывается путь с обходом препятствий);
        • EV3MCLCommand – утилита аналогична EV3MapCommand, но местоположение робота определяется автоматически по датчику расстояния с помощью локализации Монте-Карло (Monte Carlo Localization, MCL). Программа отображает набор частиц, и вы можете видеть, как ваш робот определяет своё положение;
        • EV3Image - конвертирует изображения в формат, с которым может работать leJOS EV3;
        • EV3ScpUpload – загружает исполняемые JAR-файлы программ в EV3 и может выполнять их (эта утилита используется плагином leJOS EV3, который устанавливается в Eclipse);
        • EV3SDCard – утилита для создания SD-карты leJOS EV3.

Кстати, утилиты EV3Control и EV3SDCard доступны в Eclipse в виде кнопок и пунктов меню, см. меню leJOS EV3.

Подробнее описывать утилиты я здесь не буду. Вы вполне сможете разобраться с ними сами.

Разработка с leJOS EV3 – создание проекта, запуск и отладка программы

Любая разработка начинается с создания проекта leJOS EV3. Чтобы сделать новый проект, щёлкните по пункту меню «File -> New -> Project...». В поднявшемся диалоговом окне «New Project» выберите «leJOS EV3 -> leJOS EV3 Project» и нажмите «Next >».

Создание проекта leJOS EV3 в Eclipse

На следующем шаге введите название проекта в поле «Project name». Проверьте, чтобы в области JRE была выбрана JavaSE-1.7 (если, конечно, вы не собираетесь использовать Java 8). Нажмите «Finish».

Свойства проекта leJOS EV3 в Eclipse

После этого Eclipse может предложить вам переключиться для отображения проекта с помощью перспективы «Java». Согласитесь, нажав на «Yes».

Перспективы в Eclipse определяют, какие панельки будут видны и, в каком месте на главном окне они находятся. Например, панелька «Variables», отображающая значения переменных, нужна только при отладке, поэтому в перспективе «Java» она прячется, т.к. при написании кода она не нужна, а в перспективе «Debug» она появляется.

Предложение поменять перспективу в Eclipse

Если после этого у вас продолжает отображаться окно приветствия (см. картинку снизу) просто закройте закладку «Welcome» щёлкнув на крестик.

Закрываем окно Welcome в Eclipse

Перед вами должен появиться пустой проект. Вы можете открыть папку проекта на панели «Package Explorer» и папку «LeJOS EV3 EV3 Runtime», в которой вы увидите подключенные библиотеки для работы с EV3.

Пустой проект leJOS EV3 в Eclipse

Теперь создадим Java-пакет. Для этого щёлкните по папке «src» правой кнопкой мышки и выберите пункт контекстного меню «New -> Package». В поднявшемся диалоге задайте название пакета и щёлкните «Finish». Я задам «ru.proghouse.ev3.project».

Создание Java-пакета в Eclipse

Теперь создадим главный класс. Для этого щёлкните по нашему новому пакету «ru.proghouse.ev3.project» правой кнопкой мышки и выберите пункт меню «New -> Class». В поднявшемся диалоге укажите имя класса в поле «Name», какое вам больше нравится, например, «HelloWorld», и установите галочку «public static void main (String[] args)», чтобы при создании класса была создана заглушка для метода «main». Нажмите «Finish».

Создание главного класса проекта в Eclipse

После того как класс будет создан он сразу откроется на отдельной закладке.

Новый класс после создания в Eclipse

Теперь давайте напишем простой код, который будет выводить на дисплей модуля EV3 строку «Hello, world!». Для этого поменяйте код класса «HelloWorld» следующим образом:

package ru.proghouse.ev3.project;
 
import lejos.hardware.lcd.LCD;
import lejos.utility.Delay;
 
public class HelloWorld
{
 
   public static void main(String[] args)
   {
      //Рисуем текст на дисплее.
      LCD.drawString("Hello, world!", 0, 0);
      //Ждём 5000 миллисекунд (5 сек.).
      Delay.msDelay(5000);
   }
 
}

Теперь выполним программу. Убедитесь, что модуль EV3 подключен к компьютеру (см. «Подключение модуля EV3 к компьютеру»), затем щёлкните по нашему классу «HelloWorld» в панели «Package Explorer» правой кнопкой мышки и выберите пункт меню «Run As -> leJOS EV3 Program». Через некоторое время вы увидите надпись «Hello, world!» на дисплее EV3, которая будет высвечиваться на нём 5 секунд. После этого программа завершится, и вы снова увидите меню leJOS. В консоли Eclipse вы увидите следующие строки:

Binary path is C:\workspace\EV3-project\HelloWorld.jar
Main type name is ru.proghouse.ev3.project.HelloWorld
Bin directory is C:\workspace\EV3-project\bin
Jar file has been created successfully
Using the EV3 menu for upload and to execute program
Uploading to 10.0.1.1 ...
Program has been uploaded
Running program ...
leJOS EV3 plugin launch complete

Теперь давайте разберёмся, что произошло. После того как вы выбрали пункт меню «Run As -> leJOS EV3 Program», программа скомпилировалась (в результате получился файл «C:\workspace\EV3-project\HelloWorld.jar»), загрузилась на модуль EV3 и выполнилась. Кстати, теперь прямо в модуле EV3 вы можете найти свою программу в папке Programms.

Чтобы запустить выполнение программы из-под отладчика, щёлкните по нашему классу правой кнопкой мышки и выберите пункт меню «Debug As -> leJOS EV3 program». Если вы поставили точки останова, и выполнение программы остановилось на одной из них, то вы можете увидеть сообщение, что нужно переключиться на перспективу для отладки. Согласитесь, нажав «Yes».

Точки останова проставляются и снимаются в редакторе двойным кликом слева от нужной строчки кода.

Вообще, чтобы больше не видеть этих сообщений вы можете поставить галочку «Remember my decision» (запомнить мой выбор) и нажать «Yes».

Запрос на переключение перспективы Debug в Eclipse

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

Отладчик Eclipse в точке останова

Чтобы продолжить выполнение программы нажмите F8, чтобы шагнуть на следующую строку, нажмите F6, чтобы шагнуть внутрь, нажмите F5, а чтобы остановить программу – Ctrl + F2. Давайте шагнём внутрь. Нажмите F5. При этом откроется файл «LCD.class» и мы попадём в функцию «drawString». Можете дальше походить внутри исходников leJOS, нажимая F5 и F6. Так, можно походить почти по всем исходникам leJOS, благо они доступны для просмотра. Нажмите на кнопку F8, чтобы программа отработала до конца.

Чтобы после отладки обратно переключиться в перспективу «Java» щёлкните сверху справа по соответствующей кнопке.

Кнопки для изменения перспективы в Eclipse

Как видите, всё просто. Теперь попробуем что-нибудь посложнее.

Управление моторами в leJOS EV3

leJOS EV3 поддерживает не только моторы EV3, но и моторы NXT, RCX, PF и Tetrix. Моторы EV3 и NXT могут быть подключены обычным проводом, а для подключения остальных моторов требуются специальные провода и адаптеры.

У всех перечисленных моторов разные возможности. В моторы EV3 и NXT встроены энкодеры сообщающие модулю EV3, как вращается мотор. Такие моторы называются регулируемыми. Регулируемые моторы самостоятельно контролируют скорость вращения и могут повернуться на заданный угол.

Нерегулируемыми моторами (без энкодера), такими как PF и RCX, можно управлять только самым простым способом: задать напряжение и направление вращения и через некоторое время остановить их. Для некоторых проектов это может быть полезным. leJOS позволяет работать с моторами EV3 и NXT как с нерегулируемыми.

PF моторы можно сделать регулируемыми, если подключить их через контроллер Mindsensors GlideWheel-M.

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

        • EV3LargeRegulatedMotor - для большого мотора EV3;
        • EV3MediumRegulatedMotor - для среднего мотора EV3;
        • NXTRegulatedMotor - для мотора NXT;
        • MindsensorsGlideWheelMRegulatedMotor - для мотора PF подключенного через контроллер Mindsensors GlideWheel-M;
        • TetrixRegultedMotor - для мотора Tetrix.

Порты обозначаются следующим образом: MotorPort.A, MotorPort.B, MotorPort.C и MotorPort.D.

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

RegulatedMotor m = new EV3LargeRegulatedMotor(MotorPort.A);

Затем, чтобы повернуть мотор на 360 градусов, нужно сделать следующее:

m.rotate(360);

С регулируемыми моторами вы можете использовать следующие функции (полное описание всех функций можно найти здесь):

        • forward() – двигаться вперёд до получения команды стоп (или её эквивалента);
        • backwards() – двигаться назад до получения команды стоп (или её эквивалента);
        • stop() – остановить мотор;
        • flt() – остановить мотор без торможения;
        • setSpeed(int speed) – установить скорость;
        • rotate(int angle) – повернуть на угол (угол может быть положительным или отрицательным в градусах);
        • rotateTo(int angle) – повернуть мотор до угла (в градусах).

Для синхронизации двух моторов нужно использовать функции synchronizeWithstartSynchronization и endSynchronization. Ниже пример кода для синхронного поворота двух двигателей на 360 градусов:

RegulatedMotor mB = new EV3LargeRegulatedMotor(BrickFinder.getDefault().getPort("B"));
RegulatedMotor mC = new EV3LargeRegulatedMotor(BrickFinder.getDefault().getPort("C"));
mB.synchronizeWith(new RegulatedMotor[] {mC});
mB.startSynchronization();
mB.rotate(360, true);
mC.rotate(360, true);
mB.endSynchronization();
mB.waitComplete();
mC.waitComplete();

А вот пример синхронного запуска и остановки двух двигателей:

RegulatedMotor mB = new EV3LargeRegulatedMotor(BrickFinder.getDefault().getPort("B"));
RegulatedMotor mC = new EV3LargeRegulatedMotor(BrickFinder.getDefault().getPort("C"));
mB.synchronizeWith(new RegulatedMotor[] {mC});
mB.startSynchronization();
mB.forward();
mC.forward();
mB.endSynchronization();
Delay.msDelay(5000);
mB.startSynchronization();
mB.stop(true);
mC.stop(true);
mB.endSynchronization();

Если вы хотите использовать нерегулируемые моторы, то при использовании мотора с энкодером вам нужно использовать классы реализующие интерфейс EncoderMotor, а, если мотор без энкодера, то классы реализующие интерфейс DCMotor. Вот эти классы:

        • UnregulatedMotor – для мотора EV3 или NXT;
        • TetrixEncoderMotor – для мотора Tetrix;
        • RCXMotor – для мотора RCX;
        • PFMateMotor – для моторов PF.

Вот пример управления мотором EV3 или NXT как нерегулируемым:

EncoderMotor em = new UnregulatedMotor(MotorPort.C);
em.setPower(100);
em.forward();
Delay.msDelay(1000);
em.stop();

А, если вы подключите с помощью специального кабеля мотор RCX , то управлять вы им сможете вот так:

DCMotor dcm = new RCXMotor(MotorPort.D);
dcm.setPower(50);
dcm.forward();
Delay.msDelay(1000);
dcm.stop();

Чтение показаний датчиков в leJOS EV3

leJOS учитывает, что датчик (Sensor) может измерять одно или несколько показаний. Например, датчик цвета может определять цвет или интенсивность окружающего света. В leJOS каждый тип измерения называется режимом (Mode), а снятие показания с датчика называется замером (Sample). Каждый датчик поддерживает как минимум один режим, но чаще всего их больше.

Программируя в leJOS важно понимать, что для проведения измерения необходимо использовать комбинацию датчика и режима. Сам по себе датчик (без режима) не может проводить измерение.

Все классы датчиков реализуют интерфейс SensorModes. Этот интерфейс определяет метод получения списка поддерживаемых режимов: ArrayList<String> getAvailableModes(). Также здесь есть методы для получения режима по индексу или по имени: SampleProvider getMode(int index) и SampleProvider getMode(string modeName).

Классы датчиков именуются по строгим правилам, что позволит вам быстро находить нужные классы. Первая часть имени – это EV3 или NXT, соответственно для EV3 и NXT датчиков. Для датчиков сторонних производителей, сначала идёт название производителя. Вторая часть имени – это полное название датчика, используемое производителем. Ещё в имени может присутствовать третья часть – это буква V и следующий за ней номер версии (это нужно, чтобы различать аппаратные версии датчиков). Например, класс для ультразвукового датчика расстояния EV3 называется EV3UltrasonicSensor, а класс для датчика угла HiTechnic называется HiTechnicAngleSensor. Все классы датчиков можно найти в пакете lejos.hardware.sensor (внутри ev3classes.jar).

Режимы в leJOS тоже реализованы как самостоятельные классы, которые реализуют интерфейс SampleProvider. Если датчик поддерживает несколько режимов, то вы можете использовать и смешивать их в одной программе. При этом вам не нужно переключаться между режимами, вы просто должны получать замеры от разных объектов SampleProvider.

Замер состоит из одного или нескольких значений полученных в один момент времени. Например, датчик цвета в режиме измерения интенсивности света возвращает одно значение, а в режиме RGB (определения цвета) – три значения. Не важно, сколько значений содержится в одном замере, они всегда вернутся как массив значений. Все классы реализующие интерфейс SampleProvider имеют следующий метод для получения замера: void fetchSample( float[] sample, int offset). Для того, чтобы узнать размер массива есть метод: int getSampleSize(). Полученный размер не меняется со временем.

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

    1. Получение порта;
    2. Инициализация датчика;
    3. Получение поставщика замеров (SampleProvider);
    4. Создание массива для получения замеров;
    5. Получение замеров.

Шаги 1 – 4 нужно сделать только один раз в программе. Шаг 5 вы будете повторять каждый раз, когда вам нужно делать измерение.

Вот пример работы с датчиком расстояния:

//Получаем порт.
Port port = LocalEV3.get().getPort("S2");
//Инициализируем датчик.
SensorModes sensor = new EV3UltrasonicSensor(port);
//Получаем поставщика замеров для измерения расстояния.
SampleProvider distance= sensor.getMode("Distance");
//Создаём массив для получения замеров.
//Размер массива запрашиваем у поставщика замеров.
float[] sample = new float[distance.sampleSize()];
//Получаем замеры (в данном случае расстояние в метрах).
while(true)
 distance.fetchSample(sample, 0);

Теперь поговорим о единицах измерения. В leJOS датчики используют международную систему единиц (SI). Т.е. расстояние измеряется в метрах, ускорение – в м/с² и т.д. Углы всегда измеряются в градусах.

В leJOS используется декартовая система координат. Ось X направлена в ту же сторону, в которую вилка втыкается в датчик. Это направление совпадает с тем, куда повёрнут ультразвуковой датчик LEGO. Ось Y направлена влево от оси X, а ось Z – вверх. Углы подчиняются правилу правой руки. Это значит, что поворот робота по часовой стрелке будет измерено датчиками, как положительный угол поворота. Для датчиков поддерживающих несколько осей, таких как некоторые гироскопы и акселерометры, порядок осей в возвращаемых замерах всегда будет следующим: X, Y, Z.

Декартова система координат в leJOS

Ещё в leJOS есть фильтры, которые используются для изменения замеров или для изменения потока замеров. Фильтры берут замер у поставщика, изменяют и затем отдают уже изменённый замер. По сути, фильтры тоже являются поставщиками замеров. В leJOS уже есть некоторое количество готовых фильтров, которые вы можете найти в пакете lejos.robotics.filter. Пример снизу показывает, как использовать фильтр для получения среднего значения пяти последних замеров от ультразвукового датчика расстояния.

Код примера показывает, что конструктор фильтра принимает в качестве первого параметра экземпляр SampleProvider. После этого вместо получения замеров от датчика мы будем получать замеры от фильтра.

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

//Получаем порт.
Port port = LocalEV3.get().getPort("S2");
//Инициализируем датчик.
SensorModes sensor = new EV3UltrasonicSensor(port);
//Получаем поставщика замеров для измерения расстояния.
SampleProvider distance= sensor.getMode("Distance");
//Накладываем фильтр на датчик, который будет возвращать
//среднее значение пяти последних замеров.
SampleProvider average = new MeanFilter(distance, 5);
//Создаём массив для получения замеров.
//Размер массива запрашиваем у фильтра.
float[] sample = new float[average.sampleSize()];
//Получаем замер.
average.fetchSample(sample, 0);

Использование веб-камеры в leJOS EV3 для захвата изображения

Очень интересная возможность, которая есть в leJOS EV3 – это поддержка веб-камер. Веб-камера подключается к USB-порту модуля EV3 (сбоку рядом со слотом SD-карты). Я не нашёл списка поддерживаемых веб-камер, но моя старенькая камера Logitech работает без проблем.

Робот LEGO Mindstorms EV3 с веб-камерой работающий на leJOS

Для работы с камерой нужно сделать несколько шагов:

    1. Получение экземпляра объекта Video;
    2. Подготовка камеры к работе (вызов функции open);
    3. Создание массива байтов для хранения кадра (вызов функции createFrame);
    4. Получение кадра (вызов функции grabFrame).

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

Ниже приведена простая демонстрационная программа (пример взят отсюда), которая отображает на экране модуля EV3 видео, полученное с веб-камеры. Поскольку дисплей EV3 монохромный, программа преобразует полученное изображение в чёрно-белое. Преобразование очень простое: точки темнее некоторого порога преобразуются в чёрные, остальные – в белые. Порог хранится в переменной threshold. Для изменения порога вы можете нажимать кнопки вверх и вниз на модуле EV3. Или можете нажать на центральную кнопку, чтобы использовать среднюю яркость в качестве порога. Оценить качество видео, которое будет отображаться на дисплее вашего EV3, вы можете по картинке снизу.

Селфи с веб-камеры на экране EV3

Для работы с изображением здесь используется формат YUYV. Этот формат отделяет информацию о цвете от яркости, с которой в таком случае проще работать, используя пороговые значения. На данный момент поддерживается только этот формат изображений.

import java.io.IOException;
import lejos.hardware.video.Video;
import lejos.hardware.video.YUYVImage;
import lejos.hardware.BrickFinder;
import lejos.hardware.Button;
import lejos.hardware.lcd.GraphicsLCD;
 
 public class CameraDemo {
     public static void main(String[] args) {
         try {
             Video wc = BrickFinder.getDefault().getVideo();
             wc.open(160,120);
             byte[] frame = wc.createFrame();
             YUYVImage img = new YUYVImage(frame, wc.getWidth(), wc.getHeight());
             GraphicsLCD g = BrickFinder.getDefault().getGraphicsLCD();
             int threshold = 128;
             boolean auto = true;
             while (!Button.ESCAPE.isDown()) {
                 wc.grabFrame(frame);
                 if (auto)
                     threshold = img.getMeanY();
                 img.display(g, 0, 0, threshold);
                 if (Button.UP.isDown()) {
                     threshold++;
                     if (threshold > 255)
                         threshold = 255;
                     auto = false;
                 }
                 if (Button.DOWN.isDown()) {
                     threshold--;
                     if (threshold < 0)
                         threshold = 0;
                     auto = false;
                 }
                 if (Button.ENTER.isDown()) {
                     auto = true;
                 }
             }
             wc.close();
             g.clear();
         } catch (IOException ioe) {
             ioe.printStackTrace();
             System.out.println("Driver exception: " + ioe.getMessage());
         }
     }
 }

Теперь посмотрим ещё один пример (пример взят отсюда) в котором EV3 транслирует потоковое видео. Транслируемое видео вы можете посмотреть, открыв в браузере URL с IP-адресом вашего EV3, например, при подключении через Bluetooth это будет http://10.0.1.1:8080/. Однако браузер Chrome не даст вам открыть эту ссылку напрямую, вам нужно будет встроить видео в HTML-документ. Вы можете создать файл с расширением .html со следующим содержимым:

<html>
<body>
<img src="http://10.0.1.1:8080"/>
</body>
</html>

И открыть этот файл в браузере.

Итак, вот код программы:

package mypackage;
 
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
 
import javax.imageio.ImageIO;
 
import lejos.hardware.BrickFinder;
import lejos.hardware.Button;
import lejos.hardware.ev3.EV3;
import lejos.hardware.video.Video;
 
public class HttpStream {
 
    private static int WIDTH = 160;
    private static int HEIGHT = 120;
    private static int NUM_PIXELS = WIDTH * HEIGHT;
    private static int FRAME_SIZE = NUM_PIXELS * 2;
 
    public static void main(String[] args) throws IOException {
        EV3 ev3 = (EV3) BrickFinder.getLocal();
        Video video = ev3.getVideo();
        video.open(WIDTH, HEIGHT);
        byte[] frame = video.createFrame();    
        BufferedImage img = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
        ServerSocket ss = new ServerSocket(8080);
        Socket sock = ss.accept();
        String boundary = "Thats it folks!";
        writeHeader(sock.getOutputStream(), boundary);
        while (Button.ESCAPE.isUp()) {
            video.grabFrame(frame);
            for(int i=0;i<FRAME_SIZE;i+=4) {
                int y1 = frame[i] &amp; 0xFF;
                int y2 = frame[i+2] &amp; 0xFF;
                int u = frame[i+1] &amp; 0xFF;
                int v = frame[i+3] &amp; 0xFF;
                int rgb1 = convertYUVtoARGB(y1,u,v);
                int rgb2 = convertYUVtoARGB(y2,u,v);
                img.setRGB((i % (WIDTH * 2)) / 2, i / (WIDTH * 2), rgb1);
                img.setRGB((i % (WIDTH * 2)) / 2 + 1, i / (WIDTH * 2), rgb2);
            }
            writeJpg(sock.getOutputStream(), img, boundary);
        }    
        video.close();
        sock.close();
        ss.close();
    }
 
    private static void writeHeader(OutputStream stream, String boundary) throws IOException {
        stream.write(("HTTP/1.0 200 OK\r\n" +
                "Connection: close\r\n" +
                "Max-Age: 0\r\n" +
                "Expires: 0\r\n" +
                "Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n" +
                "Pragma: no-cache\r\n" + 
                "Content-Type: multipart/x-mixed-replace; " +
                "boundary=" + boundary + "\r\n" +
                "\r\n" +
                "--" + boundary + "\r\n").getBytes());
    }
 
    private static void writeJpg(OutputStream stream, BufferedImage img, String boundary) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(img, "jpg", baos);
        byte[] imageBytes = baos.toByteArray();
        stream.write(("Content-type: image/jpeg\r\n" +
                "Content-Length: " + imageBytes.length + "\r\n" +
                "\r\n").getBytes());
        stream.write(imageBytes);
        stream.write(("\r\n--" + boundary + "\r\n").getBytes());
    }
 
    private static int convertYUVtoARGB(int y, int u, int v) {
        int c = y - 16;
        int d = u - 128;
        int e = v - 128;
        int r = (298*c+409*e+128)/256;
        int g = (298*c-100*d-208*e+128)/256;
        int b = (298*c+516*d+128)/256;
        r = r>255? 255 : r<0 ? 0 : r;
        g = g>255? 255 : g<0 ? 0 : g;
        b = b>255? 255 : b<0 ? 0 : b;
        return 0xff000000 | (r<<16) | (g<<8) | b;
    }
}

А вот более полезный пример, в котором веб-камера используется для определения цвета (пример взят отсюда). Чтобы пример работал, вам нужно записать моно (стерео работать не будет!) звуки для чёрного, белого, красного и зелёного цветов, например, с помощью программы Audacity, и экспортировать их в файлы black.wav, white.wav, red.wav, green.wav в формате WAV 16-bit PCM, а затем загрузить их в модуль EV3 с помощью центра управления (EV3 Control Center) в папку с программами.

package mypackage;
 
import java.io.File;
import java.io.IOException;
 
import lejos.hardware.BrickFinder;
import lejos.hardware.Button;
import lejos.hardware.Sound;
import lejos.hardware.ev3.EV3;
import lejos.hardware.video.Video;
 
public class ColorDetector {
    private static final int WIDTH = 160;
    private static final int HEIGHT = 120;
    private static final int NUM_PIXELS = WIDTH * HEIGHT;
    private static final File BLACK = new File("black.wav");
    private static final File WHITE = new File("white.wav");
    private static final File RED = new File("red.wav");
    private static final File GREEN = new File("green.wav");
 
    private static long lastPlay = 0;
 
    public static void main(String[] args) throws IOException  {
 
        EV3 ev3 = (EV3) BrickFinder.getLocal();
        Video video = ev3.getVideo();
        video.open(WIDTH, HEIGHT);
        byte[] frame = video.createFrame();
 
        while(Button.ESCAPE.isUp()) {
            video.grabFrame(frame);
            int tr = 0, tg = 0, tb = 0;
 
            for(int i=0;i<frame.length;i+=4) {
                int y1 = frame[i] &amp; 0xFF;
                int y2 = frame[i+2] &amp; 0xFF;
                int u = frame[i+1] &amp; 0xFF;
                int v = frame[i+3] &amp; 0xFF;
                int rgb1 = convertYUVtoARGB(y1,u,v);
                int rgb2 = convertYUVtoARGB(y2,u,v);
 
                tr += ((rgb1 >> 16) &amp; 0xff) + ((rgb2 >> 16) &amp; 0xff);
                tg += ((rgb1 >> 8) &amp; 0xff) + ((rgb2 >> 8) &amp; 0xff);
                tb += (rgb1 &amp; 0xff) + (rgb2 &amp; 0xff);
            }
 
            float ar = tr/NUM_PIXELS, ag = tg/NUM_PIXELS, ab = tb/NUM_PIXELS;
            System.out.println((int) ar + " , " + (int) ag + " , " + (int) ab);
            if (ar < 10 &amp;&amp; ag < 10 &amp;&amp; ab < 10) play(BLACK);
            else if (ar > 250 &amp;&amp; ag > 250 &amp;&amp; ab > 250) play(WHITE);
            else if ( ar > 1.8*ag &amp;&amp; ar > 2*ab) play(RED);
            else if ( ag > 1.8*ar &amp;&amp; ag > ab) play(GREEN);
 
        }
        video.close();
    }
 
    private static void play(File file) {
        long now = System.currentTimeMillis();
 
        if (now - lastPlay > 2000) {
            System.out.println("Playing " + file.getName());
            Sound.playSample(file);
            lastPlay = now;
        }
    }
 
    private static int convertYUVtoARGB(int y, int u, int v) {
        int c = y - 16;
        int d = u - 128;
        int e = v - 128;
        int r = (298*c+409*e+128)/256;
        int g = (298*c-100*d-208*e+128)/256;
        int b = (298*c+516*d+128)/256;
        r = r>255? 255 : r<0 ? 0 : r;
        g = g>255? 255 : g<0 ? 0 : g;
        b = b>255? 255 : b<0 ? 0 : b;
        return 0xff000000 | (r<<16) | (g<<8) | b;
    }
}

Удалённый доступ к EV3

Используя Java RMI, вы можете получить удалённый доступ к оборудованию или меню EV3 с компьютера или другого EV3. На самом деле всё это делается прозрачно: вы можете выполнить программу, написанную для модуля EV3, прямо на компьютере, щёлкнув в Eclipse правой кнопкой мышки по проекту и выбрав пункт меню «Run As -> Java Application». leJOS при таком запуске автоматически найдёт в сети доступный модуль EV3, подключится к нему и выполнит программу удалённо. Есть только одно единственное ограничение: программа не должна обращаться к кнопкам модуля EV3.

Рассмотренный выше способ будет работать, если у вас в локальной сети доступен только один модуль EV3 или NXT. Кроме того, вы не сможете контролировать, какой IP-адрес нужно использовать, если EV3 подключен через Bluetooth и через WiFi одновременно.

Подключиться к нужному EV3 по IP-адресу можно так:

//Подключаемся к EV3.
RemoteEV3 ev3 = new RemoteEV3("10.0.1.1");
//Получаем доступ к аудио системе.
Audio sound = ev3.getAudio();
//Пищим.
sound.systemSound(0);

Работать с другим оборудованием EV3 можно с помощью аналогичных методов и интерфейсов, таких как Power, Port, TextLCD и GraphicsLCD. Чтобы переделать теперь эту программу для запуска на EV3, нужно поменять только первую строчку:

//Получаем доступ к локальному EV3.
Brick ev3 = BrickFinder.getLocal();
//Получаем доступ к аудио системе.
Audio sound = ev3.getAudio();
//Пищим.
sound.systemSound(0);

Вообще в классе BrickFinder есть и другие методы для поиска всех доступных EV3. Например, статический метод getDefault() позволяет получить доступ к локальному EV3, если программа выполняется на EV3, или ищет первый доступный EV3 по сети, если программа выполняется на компьютере. Функция discover() возвращает список всех доступных EV3. Вот так можно пропищать на всех доступных EV3:

BrickInfo[] bricks = BrickFinder.discover();
for(BrickInfo info: bricks)
{
   Brick brick = new RemoteEV3(info.getIPAddress());
   brick.getAudio().systemSound(0);
}

Удалённый доступ к датчикам осуществляется двумя способами. Вот способ 1:

RemoteEV3 ev3 = new RemoteEV3("10.0.1.1");
Port p = ev3.getPort("S1");
EV3IRSensor ir = new EV3IRSensor(p);
SampleProvider sp = ir.getDistanceMode();

Используя этот способ, класс EV3IRSensor создаётся и работает на компьютере, и от датчика к классу пересылаются низкоуровневые i2c, uart и аналоговые данные. Это может приводить к задержкам при работе с некоторыми датчиками.

А вот способ 2:

RemoteEV3 ev3 = new RemoteEV3("10.0.1.1");
RMISampleProvider sp = 
   ev3.createSampleProvider("S1", "lejos.hardware.sensor.EV3IRSensor", "distance");

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

Удалённый доступ к моторам тоже можно делать двумя способами. Вот способ 1:

RemoteEV3 ev3 = new RemoteEV3("10.0.1.1");
Port p = ev3.getPort("A");
NXTMotor m = new NXTMotor(p);

Этот способ работает приемлемо для простых моторов с использованием классов NXTMotor или RCXMotor, но совсем не работает с регулируемыми моторами. Это происходит из-за того, что регулирующий поток, который вынужден очень часто считывать показания с тахометра, создаётся на компьютере, и задержка, возникающая при передаче показаний через HTTP-соединение, слишком высока, что делает регулирование неэффективным.

Для регулируемых моторов нужно использовать способ 2:

RemoteEV3 ev3 = new RemoteEV3("10.0.1.1");
RMIRegulatedMotor m = ev3.createRegulatedMotor("A");

Впечатление, полученное от программирования с leJOS

Степень поддержки оборудования в leJOS впечатляет. Здесь поддерживаются не только моторы и датчики LEGO Mindstorms EV3 и NXT, но и оборудование других линеек LEGO и других производителей. Чего стоит только поддержка веб-камеры! Программные возможности тоже впечатляют: объектный язык программирования, удобные классы и интерфейсы leJOS, удобство и мощь Eclipse делают своё дело. В общем leJOS можно рекомендовать Java-разработчикам для реализации простых и серьёзных проектов с использованием конструктора LEGO Mindstorms EV3 и NXT. К сожалению, одной статьи не хватит, чтобы полностью описать leJOS, но основные моменты, которые послужат вам трамплином для старта при работе с leJOS, вы можете найти выше.

Tags: leJOS Учебники по использованию программ Учебники по программированию LEGO Mindstorms Education EV3 LEGO

Комментарии   

Redy
0 #71 Redy 06.06.2017 13:23
Цитирую Redy:
Цитирую Alex:
Цитирую Redy:
Спасибо большое!!!
И ещё вопрос как через USB соединить два блока ev3?

Я это не пробовал, но это возможно. Вот здесь написано что можно создавать PAN-сети через USB и Bluetooth: lejosnews.wordpress.com/.../...

А вот здесь ссылки на все статьи по этой теме: lejosnews.wordpress.com/.../...

Хорошо, спасибо, буду завтра читать всё!!

Да, там написано что возможно (lejosnews.wordpress.com/.../pan-performance), но не написано как, и что для этого надо сделать, и что после подключения пользоваться датчиками и моторами уже на втором блоке
Цитировать
Alex
0 #72 Alex 06.06.2017 14:44
Цитирую Redy:
Да, там написано что возможно (lejosnews.wordpress.com/.../pan-performance), но не написано как, и что для этого надо сделать, и что после подключения пользоваться датчиками и моторами уже на втором блоке

Вот здесь написано как настроить PAN-сеть: lejosnews.wordpress.com/.../pan-configuration
Вот здесь все статьи на эту тему, в том числе и примеры программ: lejosnews.wordpress.com/tag/pan/
Вот пример управления светодиодами на четырёх EV3: lejosnews.wordpress.com/.../...
Цитировать
nsmihalkov
+1 #73 nsmihalkov 18.08.2017 13:52
Вот здесь есть инструкция (en) по настройке окружения для работы в Intellij IDEA (rapidpm.org/.../...). Сам попробовал, всё заработало с первого раза. Там описано через подключение по WiFi, это нужно учитывать. В теории, можно и другими способами (Bluetooth, USB), но я не пробовал.
Цитировать

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