Удаленное управление роботом. Управление роботом может происходить разными способами
Независимо от размера и числа людей, живущих в доме, жилое пространство быстро заполняется большим количеством вещей, которые надо где-то хранить. Решение, казалось бы, простое - приобрести дополнительные предметы мебели для хранения скарба, да только и они сами по себе загромождают помещение. Складные полки Clapperboard от Elsa призвана решить проблему громоздкой мебели, скрадывающей полезное пространство.
Идея проста: когда вам не нужно хранить что-либо на полках, они компактно складываются, экономя место. В закрытом виде плоские секции прилегают почти вплотную к стене комнаты и выглядят словно деревянная панель, дверь или какое-то современное произведение искусства. Но когда вам понадобится положить вещи, вы просто откидываете плоский элемент на себя и получаете полку (две, три или пять). Очень удобно для предметов, которые вы часто берете со своего места и используете, например, свежая периодика, книги, бумажник, телефон или ключи. Вещи забрали - полку закрыли, и вот место в помещении зря не загромождено. Среднюю полку можно использовать как столик для ноутбука, место для учебы, рисования, шитья и другой работы. Нижние полки в детской - для игрушек. Пока дети играются с ними, полки закрыты, освобождая место для активных действий. В любом жилом помещении офисе, общественных заведениях складные полки Clapperboard будут очень полезны.
Производитель предлагает складывающиеся полки из шпона дуба, шпона ореха и смешанного материала, а также в двух размерах S (ширина 250 х глубина 60/160 х 1900 мм) и L (ширина 360 х глубина 60/260 х 1900 мм).
Еще варианты складных полок с фотопечатью от Riveli Shelving:
Сборка передатчика и его программа.
Я сделал модуль для компьютера из FTDI Basic Breakout 5/3.3V от DFrobot, довольно распространённого микроконтролера ATMEGA 328P-PU с загрузчиком Arduino и радиомодуля на основе микросхемы nRF24L01. По-сути это просто Arduino Uno с радиомодулем. Что есть, то есть. У радиомодуля есть особенность, которую я не сразу заметил: входное напряжение должно быть в диапазоне от 3 до 3.6 вольт (хотя подача на него 5 вольт его не убьёт, но работать не будет), верхняя граница логической единицы составляет 5В. Это означает то, что для подключения радиомодуля к меге не нужен преобразователь уровней между 3.3В и 5В, а вот стабилизатор на 3.3В установить нужно. У FTDI есть встроенный стабилизатор, от него я и подпитал радиомодуль.Так выглядит сам модуль (внутри и в сборке) :
Программа состоит из инициализации, стартового сообщения и обработки команд из программы управления. Так было в моём случае. Основные команды библиотеки Mirf:
#include
#include
#include
#include
#include
Эти библиотеки нужны для работы радиомодуля
Mirf.csnPin = 4 - задаёт номер пина, отвечающего за «разрешение общаться» радиомодуля и МК
Mirf.cePin = 6 - задаёт номер пина, отвечающего за режим работы радиомодуля (приёмник/передатчик)
Mirf.spi = &MirfHardwareSpi - настраивает линию SPI
Mirf.init() - инициализирует радиомодуль
Mirf.payload = 1 - размер в байтах одного сообщения (поумолчанию 16, максимум 32)
Mirf.channel = 19 - задаёт канал (0 - 127, по умолчанию 0)
Mirf.config() - задаёт параметры передачи
Mirf.setTADDR((byte *)«serv1») - переводит радиомодуль в режим передатчика
Mirf.setRADDR((byte *)«serv1») - переводит радиомодуль в режим приёмника
Mirf.send(data) - отправляет массив типа byte
Mirf.dataReady() - сообщает об окончании обработки принятых данных
Mirf.getData(data) - записать принятые данные в массив data
Прилагаю код программы передатчика.
Программа передатчика
#include
#include
#include
#include
#include
Char active;
byte data;
Void setup()
{
Serial.begin(19200);
Mirf.csnPin = 4;
Mirf.cePin = 6;
Mirf.init();
Mirf.payload = 1;
Mirf.channel = 19;
Mirf.config();
Mirf.setTADDR((byte *)«serv1»);
//сигнальное сообщение о начале работы
data=7;
Mirf.send(data);
delay(200);
}
Void loop()
{
if (Serial.available()) //Если данные готовы к считыванию
{
active=Serial.read(); // Запись данных в переменную
}
If (active=="2")
{
data=2;
}
If (active=="3")
{
data=3;
}
If (active=="4")
{
data=4;
}
If (active=="5")
{
data=5;
}
If (active=="6")
{
data=6;
}
Mirf.send(data); //Отсылаем данные
while(Mirf.isSending()); // Ждём пока данные отсылаются
}
Программа управления.
Есть одна интересная штука - Processing. Синтаксис такой же как в Arduino, только вместо void loop() там расположился void draw(). Но она становилась ещё более интересной в моей ситуации с библиотекой processing Serial, которая позволяет работать с сериал-портом. Прочитав уроки на сайте Spurkfun`а, я поигрался с миганием светодиода на подключенной к компьютеру ардуинке по клику мышки. После этого я написал программу управления роботом с клавиатуры. Прилагаю код управления с помощью стрелок. В нём, в принципе, ничего необычного нет.
Программа управления машинкой
import processing.serial.*;
import cc.arduino.*;
Serial myPort;
PFont f=createFont(«LetterGothicStd-32.vlw», 24);
Void setup()
{
size(360, 160);
stroke(255);
background(0);
textFont(f);
String portName = «XXXX»; // Сюда нужно написать имя вашего порта
myPort = new Serial(this, portName, 19200);
}
Void draw() {
if (keyPressed == false)
{
clear();
myPort.write("6");
println(«6»);
}
}
Void keyPressed()
{
// 10 - enter
// 32 - probel
// 37/38/39/40 - keys
clear();
Fill(255);
textAlign(CENTER);
//text(keyCode, 180, 80);
Switch(keyCode)
{
case 37:
text(«Edem vlevo», 180, 80);
myPort.write("1");
break;
Case 38:
text(«Edem pryamo», 180, 80);
myPort.write("2");
break;
Case 39:
text(«Edem vpravo», 180, 80);
myPort.write("3");
break;
Case 40:
text(«Edem nazad», 180, 80);
myPort.write("4");
break;
Default:
text(«Takoy kommandi net», 180, 80);
myPort.write("6");
break;
}
}
Программа приёмника.
Инициализация этой программы отличается от инициализации программы передатчика буквально одной строчкой. Ключевая команда в бесконечном цикле Mirf.getData(data). Дальше полученная команда сравнивается с числами, которым соответствуют какие-либо действия робота. Ну а дальше робот действует точно по командам. Прилагаю код программы приёмника машинки.
Программ машинки
#include
#include
#include
#include
#include
Void setup()
{
Serial.begin(9600);
PinMode(13, OUTPUT); //LED
Mirf.csnPin = 10;
Mirf.cePin = 9;
Mirf.spi = &MirfHardwareSpi;
Mirf.init();
Mirf.payload = 1;
Mirf.channel = 19;
Mirf.config();
Mirf.setRADDR((byte *)«serv1»);
}
Void loop()
{
byte data;
If(!Mirf.isSending() && Mirf.dataReady())
{
Mirf.getData(data);
Serial.println(data);
}
Switch (data)
{
case 1:
motors(-100, 100); // поворачиваем влево
break;
Case 2:
motors(100, 100); // едем прямо
break;
Case 3:
motors(100, -100); // поворачиваем вправо
break;
Case 4:
motors(-100, -100); // едем назад
break;
Default:
motors(0, 0); // стоим
break;
}
Delay(50);
}
Заключение.
Что из этого всего вышло:
Этого робота я сделал для «Клаустрофобии» . Они проводят квесты в реальности в разных городах, и как раз для одного из таких квестов организаторам понадобился радиоуправляемый робот-сапер. Мне понравилось. Это, конечно, ущербно, т.к. на фоне управления с помощью встроенных в ноутбук средств связи, но зато своё, сделанное весьма быстро и без особых проблем. Надеюсь эта статья поможет сделать нечто подобное, а, может, даже сложнее. Тут уж кому что захочется.
Теги: Добавить метки
Основной модуль конструктора 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
Для каждого типа датчика и мотора создан интерфейс 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.
В прошлом сообщении блога я упомянул о том, что широкодоступный Wii Control - джойстик с обратной связью для приставки Nintendo Wii, может быть использован для дистанционного управления манипуляторами робота. Сейчас я хочу продолжить эту тему, и привести краткий обзор способов дистанционного управления...
Существует, вообще говоря, два широкоиспользуемых и повсеместно признанных способа дистанционного управления автономными и полуавтономными устройствами:
- Управление с помощью инфракрасных сигналов пульта ДУ (то же самое, что переключение каналов телевизора)
- Управление по радио
Первый способ, в отношении управления роботом, реализуется с помощью простенькой схемки, которую даже я, не любитель паяльника, сумел спаять за полчаса - и программы WinLIRC, являющейся по сути Windows-драйвером для такой модели управления (подробности - на моем сайте, в разделе Датчики робота).
Управление по радио - широкоиспользуемая практика, модель такого управления вы можете выдрать из любой радиоуправляемой игрушки, или же найти в каком-либо журнале для радиолюбителей.
В последнее время получают все большее распространение и другие способы беспроводного управления. Конечно же, речь идет о технологиях Bluetooth и Wi-Fi, которые в настоящее время используются практически повсеместно в компьютерах, КПК, коммуникаторах, мобильных телефонах...
Модель управления роботом при использовании технологий Wi-Fi и Bluetooth в основном такая: непосредственно на роботе закрепляются мобильный телефон или КПК, которые, через определенную самопайную схему, могут отсылать роботу управляющие сигналы, и забирать показания датчиков. Основная «мозговая» деятельность - осуществляется на основном, стационарном, компьютере (иногда - даже с помощью распределенной сети серверов). Такой подход позволяет порой в несколько раз уменьшить вес робота и потребляемую им мощность.
Кстати, известен случай, когда на одной из мировых презентаций робота, тот в один прекрасный момент замер на месте - на несколько минут. Это случилось как раз из-за перегрузок Wi-Fi сети здания, где проходила презентация.
Еще один способ управления роботом - визуальный. В простейшем варианте - робот просто движется на свет. Кстати, вариацией этого способа можно считать задачу движения по линии. Но, конечно, такое визуальное управление не слишком функционально и не слишком интерактивно. Более сложные варианты включают в себя использование веб-камеры, закрепляемой на роботе, и анализ изображения, приходящего с камеры. Например, именно таким способом роботов учат распознавать человеческую мимику. Для реализации управления с помощью веб-камеры удобно использовать программное обеспечение RoboRealm, о котором я уже .
Управление звуком - достаточно стандартная функция, для ее реализации можно использовать обычную ОС Windows Vista.
Кстати, в настоящее время существуют также сенсоры, реализующие искусственное обоняние (читайте - на английском - о применении искусственного обоняния в космосе), давно созданы материалы, позволяющие реализовать чувствительную кожу (даже клавиатура для моего старенького Palm m505 сделана из однородного материала, чувствительного к прикосновениям), ну и вкус роботы также могут чувствовать...
В заключение: дистанционное управление требуется практически для любого робота, сколь бы автономным он не был. Поэтому, при проектировании собственного робота, подойдите к этому вопросу серьезно, выберите наиболее доступный вариант, и ориентируйтесь на него - чтобы потом не пришлось все начинать сначала...