Основной модуль конструктора Lego Mindstorms EV3 может работать с прошивкой leJOS, позволяющей запускать Java-приложения. Специально для этого Oracle выпустил и поддерживает отдельную версию полноценной Java SE.

Нормальная JVM позволила мне использовать встроенный в нее протокол Java Management Extensions (JMX), чтобы реализовать удаленное управление роботом-манипулятором. Для объединения управляющих элементов, показаний датчиков и картинок с установленных на роботе IP-камер используется мнемосхема, сделанная на платформе AggreGate.



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

Оба компьютера подключены к IP-сети помещения через Wi-Fi адаптеры NETGEAR WNA1100. Робот управляется восемью двигателями Mindstorms — из них 4 «большие» и 4 «маленькие». Также установлены инфракрасный и ультразвуковой датчики для автоматической остановки у препятствия при движении задним ходом, два датчика прикосновения для остановки поворота манипулятора из-за препятствия, и гироскопический датчик, облегчающий ориентировку оператора при помощи визуализации положения плеча.

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



В самом манипуляторе два двигателя отвечают за подъем и опускание «плеча» и «предплечья». Еще три двигателя занимаются подъемом/опусканием кисти, ее поворотом на 360 градусов и сжиманием/разжиманием «пальцев».



Самым сложным механическим узлом является «кисть». Из-за необходимости выноса трех тяжелых двигателей в район «локтя» конструкция получилась достаточно хитрой.



В целом все выглядит так (коробок спичек был с трудом найден для масштаба):



Для передачи картинки установлены две камеры:

  • Обычный Android-смартфон с установленным приложением IP Webcam для общего обзора (на снимке HTC One)
  • Автономная Wi-Fi микро-камера AI-Ball, установленная прямо на «кисти» манипулятора и помогающая хватать предметы сложной формы




Программирование EV3


ПО самого робота получилось максимально простым. Программы двух компьютеров очень похожи, они запускают JMX сервер, регистрируют MBean'ы, соответствующие двигателям и датчикам, и засыпают в ожидании операций по JMX.

Код главных классов ПО руки-манипулятора
public class Arm
{
  
  public static void main(String[] args)
  {
    try
    {
      EV3Helper.printOnLCD("Starting...");
      
      EV3Helper.startJMXServer("192.168.1.8", 9000);
      
      MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
      
      EV3LargeRegulatedMotor motor = new EV3LargeRegulatedMotor(BrickFinder.getDefault().getPort("A"));
      LargeMotorMXBean m = new LargeMotorController(motor);
      ObjectName n = new ObjectName("robot:name=MotorA");
      mbs.registerMBean(m, n);
 
      // Registering other motors here
     
      EV3TouchSensor touchSensor = new EV3TouchSensor(SensorPort.S1);
      TouchSensorMXBean tos = new TouchSensorController(touchSensor);
      n = new ObjectName("robot:name=Sensor1");
      mbs.registerMBean(tos, n);
      
      // Registering other sensors here
      
      EV3Helper.printOnLCD("Running");
      
      Sound.beepSequenceUp();
      
      Thread.sleep(Integer.MAX_VALUE);
    }
    catch (Throwable e)
    {
      e.printStackTrace();
    }
  }
  
}

public class EV3Helper
{
  
  static void startJMXServer(String address, int port)
  {
    MBeanServer server = ManagementFactory.getPlatformMBeanServer();
    
    try
    {
      java.rmi.registry.LocateRegistry.createRegistry(port);
      
      JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + address + ":" + String.valueOf(port) + "/server");
      
      Map<String, Object> props = new HashMap<String, Object>();
      props.put("com.sun.management.jmxremote.authenticate", "false");
      props.put("com.sun.management.jmxremote.ssl", "false");
      
      JMXConnectorServer connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, props, server);
      connectorServer.start();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }
  
  static void printOnLCD(String s)
  {
    LCD.clear();
    LCD.drawString(s, 0, 4);
  }
  
}





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

Пример кода интерфейса
public interface LargeMotorMXBean
{
  public abstract void forward();
  
  public abstract boolean suspendRegulation();
  
  public abstract int getTachoCount();
  
  public abstract float getPosition();
  
  public abstract void flt();
  
  public abstract void flt(boolean immediateReturn);
  
  public abstract void stop(boolean immediateReturn);
  
  public abstract boolean isMoving();
  
  public abstract void waitComplete();
  
  public abstract void rotateTo(int limitAngle, boolean immediateReturn);
  
  public abstract void setAcceleration(int acceleration);
  
  public abstract int getAcceleration();
  
  public abstract int getLimitAngle();
  
  public abstract void resetTachoCount();
  
  public abstract void rotate(int angle, boolean immediateReturn);
  
  public abstract void rotate(int angle);
  
  public abstract void rotateTo(int limitAngle);
  
  public abstract boolean isStalled();
  
  public abstract void setStallThreshold(int error, int time);
  
  public abstract int getRotationSpeed();
  
  public abstract float getMaxSpeed();
  
  public abstract void backward();
  
  public abstract void stop();
  
  public abstract int getSpeed();
  
  public abstract void setSpeed(int speed);
}




Пример кода реализации MBean'а
public class LargeMotorController implements LargeMotorMXBean
{
  final EV3LargeRegulatedMotor motor;
  
  public LargeMotorController(EV3LargeRegulatedMotor motor)
  {
    this.motor = motor;
  }
  
  @Override
  public void forward()
  {
    motor.forward();
  }
  
  @Override
  public boolean suspendRegulation()
  {
    return motor.suspendRegulation();
  }
  
  @Override
  public int getTachoCount()
  {
    return motor.getTachoCount();
  }
  
  @Override
  public float getPosition()
  {
    return motor.getPosition();
  }
  
  @Override
  public void flt()
  {
    motor.flt();
  }
  
  @Override
  public void flt(boolean immediateReturn)
  {
    motor.flt(immediateReturn);
  }

  // Similar delegating methods skipped
}





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

Подключение к серверу


Непосредственное управление роботом осуществляет сервер IoT-платформы AggreGate. Установленная бесплатная версия продукта AggreGate Network Manager включает драйвер протокола JMX и позволяет подключить до десяти JMX-хостов. Нам понадобится подключить два — по одному на каждый кирпичик EV3.

Прежде всего, нужно создать аккаунт JMX устройства, указав в настройках URL, заданный при запуске JMX сервера:

Свойства соединения с JMX-устройством



После этого выбираем активы (т.е. MBean'ы в данном случае), которые будут добавлены в профиль устройства:

Выбор MBean'ов



И через несколько секунд смотрим и меняем текущие значения всех опрошенных свойств MBean'ов:

Снимок устройства



Можно также потестировать различные операции вызывая вручную методы MBean'ов, например forward() и stop().

Список операций



Далее настраиваем периоды опроса для датчиков. Высокая частота опроса (100 раз в секунду) используется, так как управляющий сервер находится в локальной сети вместе с роботом и именно сервер принимает решения об остановке вращения при упоре в препятствие и т.п. Решение, безусловно, не промышленное, но в хорошо работающей Wi-Fi сети в рамках одной квартиры показало себя вполне адекватным.

Периоды опроса



Интерфейс оператора


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



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

Вид изнутри редактора интерфейсов
Вся форма:


Вид с показанными панелями-контейнерами:



Теперь, как говорят АСУТПшники, осталось «оживить мнемосхему». Для этого применяются так называемые привязки связывающие свойства и методы графических компонентов интерфейса со свойствами и методами серверных объектов. Так как компьютеры EV3 уже подключены к серверу, серверными объектами могут быть и MBean'ы нашего робота.

Весь интерфейс оператора содержит около 120 привязок, большая часть из которых однотипна:



Половина однотипных привязок реализует управление при помощи кликов на кнопки, расположенные на мнемосхеме. Это красиво, удобно для тестирования, но совершенно непригодно для реального передвижения робота и перемещения грузов. Активаторами привязок из этой группы являются события mousePressed и mouseReleased различных кнопок.

Вторая половина привязок позволяет управлять роботом с клавиатуры, предварительно нажав на кнопку Keyboard Control. Эти привязки реагируют на события keyPressed и keyReleased, а в условии каждой привязки прописано, на какой именно код кнопки нужно реагировать.

Все управляющие привязки вызывают методы forward(), backward() и stop() различных MBean'ов. Поскольку доставка событий происходит асинхронно, важно, чтобы вызовы функций forward()/backward() и последующие вызовы stop() не перепутались. Для этого привязки, вызывающие методы одного MBean'а, добавлены в одну очередь (Queue).

Две отдельные группы привязок выставляют начальные скорости и ускорения двигателей (сейчас это реализовано на стороне сервера при помощи модели, поэтому эти привязки отключены) и меняют скорости/ускорения при перемещении ползунков Speed и Acceleration.

Привязки скорости и ускорения двигателей



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

Остальные привязки



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

Привязки модели управления



Закончив настройку виджета и модели можно запускать виджет, активировать клавиатурное управление и развлекаться:

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


  1. TimReset
    22.05.2015 18:10

    Я слышал о leJOS когда занимался HaikuVM ( haiku-vm.sourceforge.net ) — «JVM» для микроконтроллеров (в том числе и Arduino). Я даже порт под Gradle пилю, чтобы можно было нормально проекты запускать github.com/TimReset/HaikuVMGradle. Но не думал, что для Lego Oracle сделали свою JVM!
    А что за железо в Lego, что позволяет запускать JVM? Я так понимаю, там чистая JVM или код преобразуется в C и компилируется под платформу? Хотя если было бы так, то не работал бы JMX.



    1. akceptor
      22.05.2015 18:24

      Сейчас тоже играю с лего и как раз установил LeJOS, работает не очень быстро, но вполне сносно.
      Очень удобно что ставится все на карту памяти и прямо оттуда грузится. Ставится фактически Linux (есть ssh доступ и т.п.), сама JVM поддерживает даже Java 8


      1. TimReset
        22.05.2015 18:35

        А Вы на чём её запускаете? EV3, NXT или RCX? Нужно ли было работать при жёстких таймингах? У меня, например, с HaikuVM есть проблема — хочу определённый сигнал послать, с определённым интервалом — порядка нескольких микросекунд — и это нельзя сделать из Java, т.к. там нельзя оперировать такими задержками из за большого overhead'а вносимого Java.


    1. akceptor
      22.05.2015 19:39
      +1

      Не знаю почему но не могу ответить на комментарий с вопросом, отвечу здесь.
      Запускаю на EV3, с таймингами пока не сталкивался т.как только начал играться и дальше штатного железа через штатные же стредства (да и то с проблемами столкнулся).
      Но я сомневаюсь что LeJOS поддерживает тайминги в микросекундах, да и что-то серьезнее радиоуправляемого робота на Mindstorms делать не стоит.


  1. TimsTims
    22.05.2015 20:07

    Круть! Нет больше слов! Детальки сверкают, так и хочется потрогать, пощупать, собрать еще что-нибудь к нему)