Удаленное управление роботом Lego Mindstorms по JMX и IP Video

Основной модуль конструктора Lego Mindstorms EV3 может работать с прошивкой leJOS, позволяющей запускать Java-приложения. Специально для этого Oracle выпустил и поддерживает отдельную версию полноценной Java SE.Нормальная JVM позволила мне использовать встроенный в нее протокол Java Management Extensions (JMX), чтобы реализовать удаленное управление роботом-манипулятором. Для объединения управляющих элементов, показаний датчиков и картинок с установленных на роботе IP-камер используется мнемосхема, сделанная на платформе AggreGate.

6bf60a1ee19b47f586cd8c893f41bce0.JPG

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

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

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

aa60b8b7ab124fcabcae3d99d2a86e73.JPG

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

6def9700c11f4c93a2135bbe4f1a5790.JPG

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

634276cf87b94d97bc4e66206dc74583.JPG

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

778b60f0e544433786d27e9cd194600c.JPG

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

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

ПО самого робота получилось максимально простым. Программы двух компьютеров очень похожи, они запускают 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 props = new HashMap(); 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-устройством 9568996f83644aaa8283012619db44a3.png После этого выбираем активы (т.е. MBean’ы в данном случае), которые будут добавлены в профиль устройства:

Выбор MBean’ов c73555335f6b463dbb82018f36c41c03.png И через несколько секунд смотрим и меняем текущие значения всех опрошенных свойств MBean’ов:

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

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

Периоды опроса e3795e3f92034a4290746a13aca35e8b.png

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

352aef360a194bcf8b165b10122ed90b.png

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

Вид изнутри редактора интерфейсов Вся форма: 57899e5db955430994435e90ae7ac4fe.pngВид с показанными панелями-контейнерами: aca910a5c02f45dfb44ec452d54bc31d.png

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

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

e189551165d14192a8902088f3fd4a24.png

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

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

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

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

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

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

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

[embedded content]

© Habrahabr.ru