У Вас есть минутка? Не могли бы Вы покосить мой газон?
Краткая история разработки на коленке робота-газонокосилки. Управлять ей можно с любой точки земли через интернет. Мечтали почувствовать себя оператором марсохода или лунохода? Всего лишь нужно зайти на сайт mowmylawn.ru и Вы сможете управлять газонокосилкой у меня во дворе!
Предыстория
Вся история с газонокосилками началась летом 2015, мой первый пост на GT как раз был о моем опыте реализации робокосилки из того, что было в гараже.
После этого представители компании Robomow предложили использовать их платформу для дальнейшей разработки. Это очень удобно, потому что все вопросы по железу и механике требуют особых навыков и занимают очень много времени.
Я очень хотел организовать конкурс роботов-газонокосилок. Как оказалось дело это нелегкое. Команд было зарегистрировано более 15, но в итоге только трое участников показали ход работ.
Электроника: Две платы. Raspberrry Pi — в качестве управляющей системы верхнего уровня. В ее задачи входит планирование маршрута, построение траекторий, и тд. Nucleo stm32 — в качестве исполнительной системы нижнего уровня. В ее задачи входит навигация, опрос датчиков, управление моторами итд.
Навигация: Система комплексирования данных RTK, одометров и ИНС. Это система собственной разработки.
Датчики: Навигационные приемники GPS/GLONASS. Энкодеры моторов для одометрии. Инерциальная система навигации. Ультразвуковые датчики.
К сожалению должен сообщить что наша команда, скорее всего, не сможет принять участие в конкурсе. Дело в том что во время первых полевых испытаний в конструкции нашего робота были выявлены определенные недочеты, на исправление которых уйдет некоторое время. Работу над проектом мы приостанавливать не собираемся, но на фоне грядущей сессии, представить даже минимальную версию робота к намеченному сроку — вряд ли сможем(что уж говорить про использование машинного обучения, которое должно было стать нашем сильным преимуществом).
Все усилия по проведению конкурса в Сколково оказались зря. В конце концов оказалось, что свободных газонов для конкурса по робототехнике в Сколково нет. Все возможные спонсоры не хотят связываться с новым конкурсом с неясными перспективами. На письма в муниципальные организации я даже не получил ответ. В итоге у нас была туманная перспектива организовать конкурс для 4 участников из разных городов. Хотя конкурс получался международный, все-таки целесообразней было его отменить, как это не жаль.
Робокосилка 2016. ROS & FUN
Я осваиваю ROS и пишу лаунчер для робокосилки, использую Kinect и SLAM, только на визуальной одометрии робот строит карту и прокладывает маршрут. Kinect в солнечную погоду работает плохо. С ROS Вы за один вечер сделаете «Hello World!», а дальше тьма. Я так и не нашел нормального руководства для новичка как сделать робота, а не просто писать в топики. Кто-нибудь задумывался, почему на карте сообщества ROS нет ВООБЩЕ ни одной точки в РФ? Я открыл группу ВК.
В свободное время прикрутил к тягам своей «рабочей лошадки» два сервопривода, ультразвуковой сенсор и Arduino. Простой тест объезда березы и остановки пройден! А дальше бездна, эту штуку опасно просто так пускать по своему участку! Ее даже не получится пинать как роботов сами знаете откуда.
Оказывается роботы — вещи достаточно скучные в понимании большинства людей, особенно сервисные роботы. Да, знаю о великой дружбе роботов-пылесосов и котиков. Знаю о супер творениях от Boston Dynamics, Darpa и российском боевом роботе-аватаре, это все больше похоже на роботов, чем коробочка, в которой некий алгорим взаимодействует с реальным миром.
В понимании моей дочки роботы — это как минимум трансформеры, а не та еруда, на которую я трачу время. Я принял тяжелое решение и «временно» сделал из робокосилки игрушку на bluetooth управлении.
Управление работает так же как и у снегоуборщика. Оказалось это весело! Особенно для папы.
Дальше — больше! Меня заинтересовала идея реализации управления не на bluetooth, а через интернет, с телеметрией. Скучно, пресно и идея избитая!
А что если дать возможность любому пользователю интернета управлять моей газонокосилкой? Знаете такие идеи, которые потом трудно выкинуть из головы? Это как раз была такой…Сhallenge accepted!
Краудкосилка
Железо
В волшебную коробочку аккуратно добавлены Raspberry pi, USB хаб, wifi-адаптер, веб-камера.
Из интересных моментов по железу. У меня не оказалось драйвера с нужными характеристиками для двигателей. Обычно в качестве драйвера используют H-мост на полевых транзисторов или (хардкор) на реле. Я выбираю более жесткий вариант, потому что именно реле были в наличии.
Обычная схема подключения подразумевает 4 ключа на каждый двигатель, т.е. 8 на 2 ходовых двигателя.
Учитывая, что можно использовать так же и нормально-открытое состояние реле, а так же тот факт, что нет необходимости приводить двигатели в движение по отдельности можно обойтись всего лишь 5 реле для двух ходовых двигателей.
Кроме самого робота установлена на доме камера, которая с частотой 3 кадра/сек. загружает на ftp-сервер в интернете обзорное фото участка для лучшей ориентации. Но впоследствии просто заменил на решение от ivideon.
Программа
Как и прежде Arduino получает по serial-порту сообщения в один символ, которые обозначают необходимое действие. Так же для тестирования и отладки, полученные от raspberry коды отправляются по bluetooth, можно подключить телефон в режиме терминала и получать данные с сервера еще и на телефон.
int m1=2; int m1b=3; int m2=4; int m2b=5; int mk=6; int pis=7; char a,b; void setup() { Serial.begin(9600); Serial1.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for Leonardo only } while (!Serial1) { ; // wait for serial port to connect. Needed for Leonardo only } Serial.println("Start"); pinMode(statpin, OUTPUT); pinMode(m1, OUTPUT); pinMode(m1b, OUTPUT); pinMode(m2, OUTPUT); pinMode(m2b, OUTPUT); pinMode(mk, OUTPUT); pinMode(pis, OUTPUT); analogWrite(pis, 1000); delay(100); analogWrite(pis, 700); delay(200); analogWrite(pis, 300); delay(300); analogWrite(pis, 1000); delay(100); analogWrite(pis, 100); digitalWrite(pis, HIGH); digitalWrite(statpin, LOW); digitalWrite(m1, LOW); digitalWrite(m1b, LOW); digitalWrite(m2, LOW); digitalWrite(m2b, LOW); digitalWrite(mk, LOW); digitalWrite(pis, HIGH); } void loop() // run over and over { if (Serial.available()){ a=Serial.read(); Serial1.println(a); if(a=='B'){ digitalWrite(m1, HIGH); digitalWrite(m1b, LOW); digitalWrite(m2, HIGH); digitalWrite(m2b, LOW); } if(a=='F'){ digitalWrite(m1, HIGH); digitalWrite(m1b, HIGH); digitalWrite(m2, HIGH); digitalWrite(m2b, HIGH); } if(a=='R'){ digitalWrite(m1, HIGH); digitalWrite(m1b, LOW); digitalWrite(m2, HIGH); digitalWrite(m2b, HIGH); } if(a=='L'){ digitalWrite(m1, HIGH); digitalWrite(m1b, HIGH); digitalWrite(m2, HIGH); digitalWrite(m2b, LOW); } if(a=='S'){ digitalWrite(m1, LOW); digitalWrite(m1b, LOW); digitalWrite(m2, LOW); digitalWrite(m2b, LOW); } if(a=='W'){ digitalWrite(mk, HIGH); } if(a=='w'){ digitalWrite(mk, LOW); } if(a=='V'){ digitalWrite(pis, LOW); } if(a=='v'){ digitalWrite(pis, 700); } }else{ } }
На raspberry работают два python скрипта. Один из скриптов с помощью opencv захватывает видео с веб-камеры, установленной на ровере и загружает ее по ftp на сервер. Так же, с какой-то долью вероятности, фото вместе с рандомным сообщением из списка загружается в twitter-аккаунт.
import numpy as np import sys import pygame import pygame.camera from pygame.locals import * from twython import Twython from random import random import ftplib pygame.init() pygame.camera.init() cam = pygame.camera.Camera("/dev/video0",(550,400)) cam.start() CONSUMER_KEY = '-------MBZDT1PwibFeIcSp' CONSUMER_SECRET = '-----------------1ZGHgBRz6aEr4YhUVuO84CuEV' ACCESS_KEY = '-----------------4MCjSkny9Y6rJj5I32ulXctISQF' ACCESS_SECRET = '------------------------cDadRY3He5Kv6CXVuqy2Dh' api = Twython(CONSUMER_KEY,CONSUMER_SECRET,ACCESS_KEY,ACCESS_SECRET) api.verify_credentials() host = "-.--.---.196" ftp_user = "ftp_user" ftp_password = "ftp_password" con = ftplib.FTP(host, ftp_user, ftp_password) con.cwd("/mowmylawn.ru/webcam") a={0:'Всем привет! Какой чудесный день!',1:'Опять трудовые будни. Скорее бы выходной!',2:'Верблюд может не пить две недели...А у меня бензин заканчивается',3:'Ну почему опять я должен заниматься газоном?',4:'Улыбаемся, снимает скрытая камера!',5:'Работа не волк, а вот я могу в лес убежать :)) !',6:'Врум, врум...стригу газон!',7:'Не плачь, на Марсе тоже жизни нет.',8:'Эйнштейн был прав: выходные - понятие относительное',9:'Самое сложное — не знать, правильно ли ты сделал...',10:'Меня одну волнует этот вопрос: Когда у меня отпуск?',11:'Начинаю обработку территории',11:'Кто-куда, а я работать',12:'Судьба - это то, что мы получаем в результате наших решений и поступков.',13:'— У тебя не все дома! — Конечно, я же на работе!',14:'Хозяин, батарейки на исходе! Сжалься...:(((!',15:'Хочешь меня сделать? robogazon.ru',16:'Всё возможно, пока не сделан выбор!'} def twit(): image = cam.get_image() pygame.image.save(image,'webcam.jpg') photo = open('webcam.jpg','rb') b = random() * (len(a)-1) b = int(round(b,0)) #api.upload_media(media=photo) #api.update_status(status=a[b]) api.update_status_with_media(media=photo, status=a[b]) def ftpimg(): image = cam.get_image() pygame.image.save(image,'webcam.jpg') photo = open('webcam.jpg','rb') send = con.storbinary("STOR "+ 'webcam.jpg', photo) while 1 : ftpimg() b = random() * (500) b = int(round(b,0)) if b==107: twit() con.close
Второй скрипт по http получает на сервере текущую команду для действия и отправляет эту команду по serial на arduino.
- «S» — стоп
- «F» — вперед
- «B» — назад
- «L» — влево
- «R» — вправо
- «W» — включить двигатели кошения
- «w» — выключить двигатели кошения
- «V» — включить сигнал
- «v» — выключить сигнал
import serial,time import urllib3 http = urllib3.PoolManager() ser = serial.Serial("/dev/ttyUSB0",9600) ser.writelines("S"); olddata=0 countolddata=0 while 1 : r = http.request('GET', 'http://mowmylawn.ru/1.php') if r.data!=olddata: olddata=r.data countolddata=0 else: countolddata+=1 if countolddata>20: ser.writelines("S") else: ser.writelines(r.data) ser.close() con.close
Веб-сервис
На bootstrap накидал страничку. На странице два .jpg файла, которые обновляются по мере загрузки. Факторов, влияющих на задержки в управлении и телеметрии много, это и Ваша скорость соединения, и канал на сервере, и канал у меня дома.
Максимальная частота обновления, которую удалось добиться мне:
- камера на ровере 4 Гц;
- камера на доме 3 Гц.
База данных Mysql состоит из 2 таблиц, в первой хранится одна пара ключ/значение, это команда для робота. Вторая таблица — users.
Когда Вы встаете в очередь на сайте — отправляется ajax get запрос на добавление пользователя, в базу заносится запись с отметкой timestamp, вашим ip и сгенерированным ключом для управления.
Управлять косилкой одновременно может только один человек (кроме меня) — это пользователь с самым маленьким timestamp. Когда приходит Ваша очередь и Вы начинаете управлять косилкой — в базу заносить timestamp начала управления, каждому отведено на управление 60 сек…
Каждый раз когда Вы наводите на кнопки управления отправляется ajax get запрос с командой и Вашим ключом на управление, при этом проверяется разница между текущим временем и временем, когда Вы начали «игру», если разница больше 60 сек, для Вас игра заканчивается, Ваша запись удаляется из базы и Вы опять можете встать в очередь, «игра» переходит к следующему игроку.
Промо видео
Мой первый пост на reddit. Попробуйте управление mowmylawn.ru. В случае большой очереди или хабраэффекта — прошу понять и простить.
P.S.: На забывайте, что Вы можете принять участие в совместном проекте по разработке фитнес-трекер для ударных видов спорта KickBrick. В команде ждут Вас!
Источник