DIY. Підводний дрон. Історія одного божевілля
У цьому матеріалі описано проектування, розробка і складання прототипу підводного апарата на базі Raspberry PI і управління ним з Android-смартфона. Стаття може стати в нагоді як новачкам (викладені ази керування електродвигунами, діодами, камерами, гіроскопом), так і досвідченим інженерам (вони можуть поглумитися над виконанням і рішеннями :)). Загалом, якщо ви любитель зробити чогось електронного своїми руками — милості прошу до прочитання.
Я переглянув багато передач по Discovery про винахідників, і одного разу захотілося зробити самому щось цікаве, та щоб у всі тяжкі: електромотори, контролери, управління, камера. Купивши багато корисних (і не дуже) девайсів, я став думати, який саме проект буде прикольно зробити. Прийшов до висновку, що літаючих дронов хоч чимось жуй, що їздять теж, а от з водою якийсь косяк (ха-ха, так я «трохи» помилявся).
Після серфінгу в інтернеті я знайшов досить багато рішень, але більшість з них ще не вийшли в маси і знаходилися в стані прототипів, або коштували некисло дорого (3++до єнотів). Якщо цікаво, то можете глянути пару хороших тут .
Забігаючи наперед, скажу, що у мене вийшов робочий прототип, який здатний плавати на кілька метрів глибини, з усіма плюсами і мінусами. Складно сказати, що в результаті я отримав продукт, яким можна легко керувати і що він зараз готовий зробити щось більше, ніж вгамувати інженерний азарт. Але всі вузли у нього працюють справно, і головне, що мої помилки і напрацювання можуть допомогти комусь зробити щось значуще (навіть якщо тільки для нього самого).
Так як роботи було зроблено дійсно багато, можна розділити за наступними темами:
- У порядку марення (затравочка)
- Вибір компонентів
- Програмування ESC
- Налаштування Raspberry PI сервера
- Управління електродвигунами на Raspberry PI
- Гіроскоп
- Управління світлодіодами на Raspberry PI
- Протокол спілкування клієнт-сервер
- Додаток на Android
- Відео Stream
- Управління з екрану і джойстиком
- Зборка і тестування конструкцій
- Висновки
У порядку марення
Скажу відразу: я любитель, і більшість з того, що я робив, було зроблено методом наукового тику, бо зібрати щось і протестить на-а-набагато веселіше, ніж прорахувати і зрозуміти, що в теорії воно не працює. Варіантів конструкції та ідей було чимало, і були відверто «дурні» реалізації (одна з них буде описана чисто по приколу далі).
Першим і основним завданням було зрозуміти, яким чином можна зробити в побутових умовах герметичний корпус і при цьому передати момент на гребні гвинти.
Як зазвичай, на початку приходять не самі розумні ідеї (а думати двічі — не мій підхід), і я спробував поекспериментувати з передачею моменту магнітами. Вибрав просту конструкцію і зібрав такий ось «високотехнологічний» девайс:
На шестірню було прикручене за два магніту і з'єднані з двигунами (дотримуючи всі полярності магнітів), між шестернями пластина яка імітує стінки корпусу. Якщо у вас прокинулося бажання закрити статтю і виколоти собі очі — це нормальна реакція на побачене, але я вас попереджав на початку :)
Незважаючи на кривизну, після запуску макета можна сміливо прокричати: «І все-таки вона крутиться» ©. Момент передається, деталі обертаються, але за рахунок тяжіння сильно зростає тертя. Наступний мінус, крім десятків інших, тому що якщо стопнути уявний гвинт, то магніти втрачають «контакт» і другий раз вже зчепитися не можуть із-за різниці швидкостей.
Навіть якщо зібрати таку конструкцію, використавши підшипники, нормальні шестерні і магніти, це буде далеко не найкращим рішенням, бо ККД, простота реалізації і просто здоровий глузд кульгають.
У підсумку прийнято рішення не випендрюватися і взяти безколекторний двигун, і при потребі залити лаком обмотку. Винести за корпус (так-так, в саму воду) і просто загерметизувати виведені дроти. Це буде значно простіше, ніж думати конструкцію з маслом і сальниками або ще якийсь складний механізм, для того, щоб утримати воду поза механіки та електроніки.
Отже, перейдемо до більш серйозних речей.
Вибір компонентів
Далі список того, що було використано при створенні апарату.
Материнка
В якості контролера однозначно була обрана «малинка» (Raspberry PI 3B ). Arduino-подібні плати для такої комплексної задачі відразу відпали, так як треба контролювати мінімум 4 двигуна, діоди, гіроскоп, відправляти потік відео з камери і при цьому отримувати і обробляти команди з пристроїв керування.
Малина йде з вбудованим Wi-Fi і Ethernet під RJ-45, що безперечно дозволить провести всі ці операції.
Канал зв'язку
Так, так — вита пара
З передачею даних під водою завжди було туго. Вода служить відмінним екраном, і, отже, про всяку беспроводку можна забути (любителям «а як же підводні човни» — прохання ознайомитися з розміром антени для реалізації і вартістю технологій, складністю і шириною пропускання каналу). Так що іншого виходу не залишалося. Був чекліст, який успішно пройдено:
- Можливість передачі під водою.
- Швидкість такої передачі.
- Універсальність (не треба винаходити велосипед ні на распберри, ні на передавачі).
Є спеціальний кабель для води з нульовою плавучістю і іншими принадами, але купити 100 метрів окремо не можна, а витрачати 500+ баксів на котушку я не захотів.
Передавач «на суші»
Міні-роутер NEXX
Було 3 основних варіанти передачі.
- Спробувати вивести проводом Wi-Fi антену з води на сушу (замість витої пари кабель одножильний), але тут було багато спірних моментів.
- Побудувати міст на двох Raspberry як клієнт-сервер (було б доречно, але дорожче і геморней).
- Взяти готовий міні-роутер і з'єднати кручений парою з малиною. Цей варіант був взятий за основу, так як він самий надійний, швидкий та дешевий. Як показала практика, це непоганий варіант.
Електродвигун
N2830/2212 1000KV
Після випробування 3 різних моторів вибір припав саме на цю модель. Чому? Та все просто — він досить потужний, у нього є друга вісь і можна повісити 2 гвинта. Всі двигуни нормально працюють у воді до першого влучення водоростей або піску. Якщо брати высокооборотистые і менш потужні — вода не їх стихія. Більш дешеві варіанти теж не виправдали надій. Загалом ціна/якість.
Плата управління (ESC)
Afro ESC (30A)
Теж доволі все просто. Її можна запрограмувати на перемикання forward/reverse, і потужності 30 ампер повинно вистачити на випробовувані мотори. Плюс до всього, їх не треба було чекати місяць-другий з Китаю (але, звичайно ж, є одне але:).
Afro ESC USB Програматор
Програматор для наших ESC. За допомогою цього девайса можна залити потрібну прошивку. Власне кажучи, це і є «але». Чекати довелося півтора місяця.
Світлодіод
CREE XHP50
Ця модель здатна випалювати все живе світловим потоком. Така мета і була.
Імпульсний драйвер для світлодіодів
7-30В 3А
Відмінно підходить для наших цілей — тримає два вищевказаних світлодіода, а головне — підключається до малинці і дозволяє плавно регулювати яскравість діодів.
Гіроскоп
MPU-6050
Трехосевой гіроскоп/акселерометр, який буде відправляти дані на Raspberry про розташування в просторі. Сильно вони не знадобилися, але дані були :)
Камера
RPi Camera F
Або будь-яка інша нативна камера Raspberry PI. Головна умова — щоб вона коннект через шлейф, а не була USB. З видеостримингом завжди були проблеми (затримки, кодинг), точніше, це не проблеми, а «треба вбити багато часу, щоб завелося».
Джойстик
iPega PG-9055 — помічник управління, якщо у вас руки мокрі, а вони такими будуть, можна активувати управління даним девайсом.
Батарея
Turnigy 2200mAh 3S 30C
Знову був ряд тестів і вибір. Батареї 18650 не підійшли, бо у контролера струм розряду був 25А, чого не вистачало всій системі на піках (так, кілька батарей померло смертю хоробрих після використання). Можна було спробувати спорудити на 6 батареях і отримати 50А, але це складніше. Кабелем таку потужність постійного(!!) струму передати нереально (пам'ятаємо про дистанцію 100 м). Так що взяв Li-Po, яка чудово впоралася зі своїм завданням і помістилася в корпус.
Та інше
- Гребні гвинти (одна з основних проблем)
- Cable Gland (а як же без них)
- Коробка
- Коробка
- і ще коробки, коннектори, дроту та інше.
Програмування ESC
На жаль, Afro ESC з коробки має тільки один напрямок обертання, тому треба «шити». Сама засідка в тому, що треба чекати лінкер з Китаю, решта деталі. Припустимо, що у вас вже є :)
Порада, яку я зустрів на просторах інтернету, — купити USB хаб , щоб у разі короткого замикання згорів він, а не ваш дорогий ноут. Але кожен вільний вибирати свою долю :)
Качаємо файл прошивки на диск і програму для прошивки KKMulticopter Flashtool .
Тепер важливо не натупить з підключенням проводів, бо наслідки можуть бути сумні для якогось пристрою. Короткий лікнеп по ESC по проводах:
- Пара червоний-чорний товстих кабелів з бананом «татом» на кінці. Силове харчування двигуна. Я подавав 12 вольт і ампер — скільки просило :)
- Червоний — ВХІД плюс.
- Чорний — ВХІД мінус.
- Товсті дроти червоний-чорний-жовтий — живлення на двигун. Поки ми їх не чіпаємо, але надалі будемо з'єднувати на аналогічні «кольору» електродвигуна.
- Тонкі дроти керування та живлення.
- Жовтий — це безпосередньо кабель, по якому передаємо сигнал управління.
- Червоний — вихід 5в (в цілому я його не використовував). Їх є кілька різних типів, якщо захочете — читайте .
- Чорний — мінус.
Підключаємо тонкі дроти до Linker'у: мінус до мінуса (чорні) і сигнал на сигнал (жовті). Тонкий червоний не чіпаємо і не підключаємо . Далі даємо харчування на плату (чорний і червоний входи). Особисто я використовував цей імпульсний блок , але підійде будь-який з схожими характеристиками.
Відкриваємо програму прошивки і вибираємо:
Programmer:Turnigy USB Linker or Afro USB Linker (залежить від того, що у вас є).
Port: власне той USB, куди включили. Благо, вибір там не великий.
Baud rate: 9600.
Controller: atmega 8-based brushless ESC (8kB flash).
І вибираємо вже скачаний файл прошивки. Тиснемо кнопку «Run» і молимося :) Скоро прошивка успішно заллється на ESC, і все буде готово. Весь процес можете подивитися тут:
З 8-ми програмованих мною ESC здох тільки один :)
Налаштування Raspberry PI сервера
Тепер перейдемо до налаштування Raspberry PI.
- Качаємо останній дистрибутив системи тут .
- Викачуємо і встановлюємо Etcher.
- Вставляємо порожню microSD флешку в картрідер (тут вже самі думайте, де їх взяти).
- Відкриваємо Etcher і вибираємо zip або img Raspbian, який завантажили на першому кроці.
- Натискаємо 'Flash!'
- Чекаємо закінчення установки і витягаємо microSD.
- Вставляємо флешку в наш Raspberry.
Для тестів нам буде достатньо «малинки», телефону на базі Android і будь-якого Wi-Fi роутера з доступом до інтернет. Якщо ви любитель консольного спілкування з пристроями, то вам нічого пояснювати й не треба, самі впораєтеся. Для більш лінивих — підключаємо монітор, клавіатуру (HDMI і USB порти працюють на «ура» з коробки), живлення від якогось USB-microUSB і вантажимо систему. Важливо пам'ятати, що на всі двигуни подаємо 12V, а на Raspberry — 5V. Також важливо, якщо вимкнути різко харчування на малину — може побитися операційна система (пов'язано це з пам'яттю самого пристрою). Так що краще подумати про безперебійності.
Підключаємо до мережі Wi-Fi, і в цілому система готова. Я б радив налаштувати ваш роутер і дати static IP адреса для малини, бо підключатися будемо саме по IP.
Якщо ви вже працювали з Linux, то подальші дії вам будуть не дікі. Найпростішим способом створити серверний додаток буде NodeJS. Розібратися, що та як не становить праці, так як в Гуглі ще нікого не банили. Відкриваємо термінал і поїхали:
sudo apt-get update sudo apt-get dist-upgrade sudo apt-get install -y nodejs
І перевірочно запускаємо node -v, щоб точно зрозуміти, що все встановилося.
Можна відразу злити весь проект з репозиторію і запустити (посилання в кінці статті), але хіба ми тут заради цього зібралися? :) Тепер створюємо папку нашого проекту mkdir ~/drone і переходимо в неї cd ~/drone та ініціалізуємо проект: npm init. Установник запитає назва та індекс файл — вкажемо app.js.
Після ініціалізації ставимо express : npm install express. І створюємо файл app.js: touch app.js.
Для тих, хто працює з GUI (монік і клава), раджу поставити Atom . У ньому дуже навіть зручно писати js-код. Для інших — Vim, Nano, MCEdit (самі знаєте).
Далі додаємо код app.js і зберігаємо файл:
'use strict'; const express = require('express'); const net = require('net'); const app = express(); const server = net.createServer((socket) => { socket.on('data', (data) => { var command = data.toString(); console.log(err); }); }).on('error', (err) => { console.log(err); // handle errors here // throw err; }); server.timeout = 0; // grab an arbitrary unused port. server.listen(49655, () => { console.log('opened server on', server.address()); });
І запускаємо з консолі node app.js у папці(!!) drone. Тепер у вас крутиться сервер, який слухає підключення на 49655 порт. Запам'ятаємо: наш локальний IP (ifconfig в консолі) і всі підключення будемо виробляти на IP:49655.
Зараз давайте ускладнювати конструкції :)
Управління електродвигунами з Raspberry PI
Отже, починаємо веселу частину нашої роботи — пишемо управління на Raspberry PI. Йдемо в папку проекту ~/drone і встановлюємо PiGpio бібліотеки:
sudo apt-get update sudo apt-get install pigpio npm install pigpio
Тепер невеликий лікнеп по управлінню ESC. Нам треба передати якусь частоту на керований провід (жовтий тонкий) (див. розділ «Програмування ESC»). Прошивка afro_nfet_besc30_r1 має діапазон 1100-1900, де значення 1500 — стан спокою, 1100 макс реверс, 1900 макс форвард.
Створюємо файлик engines.js і в нього додаємо JS-код:
//Константа стану спокою const SERVO_ZERO = 1500; //Підключаємо бібліотеку 'pigpio' const Gpio = require('pigpio').Gpio; //Визначаємо наші 4 двигуна по пинам расбперри (пиновка буде пояснено трохи нижче) const servo1 = new Gpio(22, {mode: Gpio.OUTPUT}); const servo2 = new Gpio(10, {mode: Gpio.OUTPUT}); const servo3 = new Gpio(9, {mode: Gpio.OUTPUT}); const servo4 = new Gpio(27, {mode: Gpio.OUTPUT}); //Встановлюємо кожній платі стан "спокою" servo1.servoWrite(SERVO_ZERO); servo2.servoWrite(SERVO_ZERO); servo3.servoWrite(SERVO_ZERO); servo4.servoWrite(SERVO_ZERO); //За викликом цього методу пишемо значення в наш ESC module.exports.engines = { leftEsc: (value) => { servo1.servoWrite(value); }, rightEsc: (value) => { servo2.servoWrite(value); }, topLeftEsc: (value) => { servo3.servoWrite(value); }, topRightEsc: (value) => { servo4.servoWrite(value); } }
Щоб використовувати цей код, пишемо в app.js:
const engines = require('./engines.js').engines; engines.leftEsc(*VALUE[1100;1900]*);
Власне кажучи, servo1.servoWrite() — і є магічне управління. Подаємо 1100 — двигун крутиться в реверсному напрямку. Подаємо 1900 — max forward. А ось терморегулятори нашої малини:
Якщо коротко, то ми використовуємо помаранчеві GPIO. Одна пара GPIO 10 — GPIO 22, друга GPIO 9 і GPIO 27 (логічніше було б використовувати 27;22 10;9, але при пайку не врахував, і довелося поміняти). Якщо брати за порядним числами (вказані сірим), то це 13;15 і 19;21 контакти. На кожен з них підключаємо жовті дроти з ESC, а мінус ж об'єднуємо в один GND, наприклад 39 контакт.
Залишилося підключити інше. Коннектим дроти живлення (червоний і чорний) Afro ESC до окремого блоку живлення(12v), підключаємо мотори до жовтий-червоний-чорний товстим проводах і вже можемо гратися. Наприклад, допилити код і по інтервалу подавати різні сигнали на піни. Невелике відео, правда, там управління здійснюється з Android, але це опишу трохи пізніше:
Гіроскоп
Підключаємо:
VCC до піну 1 (3.3 V PWR)
GND до будь-якого мінуса
SCL до піну 5 (GPIO 3)
SDA до піну 3 (GPIO 2)
Підключення не складне, а програмно ще простіше — хороші люди зробили все до нас. На допомогу поспішають бібліотеки i2c-bus іi2c-mpu6050 .
Встановлюємо у проект:
npm install i2c-bus npm install i2c-mpu6050
Створюємо файл gyroscope.js і додаємо:
const i2 = require('i2c-bus'); const MPU6050 = require('i2c-mpu6050'); const address = 0x68; const i2c1 = i2c.openSync(1); const sensor = new MPU6050(i2c1, address); var reading = false; var sensorData = {}; module.exports.gyroscope = { getData : () => { return JSON.stringify(sensorData); }, readRotation : () => { sensor.readRotation(function (err, rot) { if (err) { console.log(e); return; } console.log(rot); }); }, readSensor : () => { if (!reading) { reading = true; } sensor.read(function (err, data) { reading = false; if (err) { console.log(err); } else { sensorData = data; } }); } }
Запускаємо метод readSensor з якоюсь періодичністю і забираємо дані останнього опитування з getData. Там буде багато складових (x,y,z,a) положенні. Розібратися що до чого, чесно, не встиг. Були важливіші завдання з керуванням. Знаю, рішення таке собі, але воно мало суто ознайомчий характер, тому і актуальність результату не дуже важлива.
Raspberry PI Flashlight control
Потихеньку продовжуємо нарощувати функціонал і на цей раз займемося світлодіодами. У нас є два XHP-50 діода і драйвер-контролю (див. розділ «Вибір компонентів» ). Вони дуже добре гріються, тому без радіаторів використовувати їх нікому не рекомендую. На тому ж сайті, благо, вони є. Головне, не наплутати з розміром.
Отже, прикручуємо діоди на радіатор, паяємо мінус одного до плюса іншого (на них є маркування) і припаюємо мінусовій та плюсової провід:
Далі дивимося на наш контролер і теж паяємо.
Живлення — 12V плюс і мінус, можна запитаться тим же, чим і живимо двигуни. Висновки на діод з'єднуємо з раніше припаяними відповідно L - до мінуса, L+ до плюса. Аналоговий контакт (А) в даній реалізації не потрібен, залишаємо порожнім.
Тепер нас цікавить цифровий вхід (D) і земля (G). Коннектим цифру (D) на GPIO 11 і землю на багатостраждальний GND (будь-який, можна один). Тепер повертаємося до нашої Raspberry PI, створюємо файлик light.js і додаємо наступне:
//Наша знайома бібліотека const Gpio = require('pigpio').Gpio; //Створюємо Gpio для нашого піна const light = new Gpio(11, {mode: Gpio.OUTPUT}); // Значення можна ставити від 0 до 255 (з доків по драйвер світлодіода) const LIGHT_MIN = 0; const LIGHT_MAX = 255; //Потрібно буде для обчислень const LIGHT_DIAPASONE = LIGHT_MAX - LIGHT_MIN; //Вимикаємо світлодіод спочатку (система стартує з включеними на максимум світлодіодами) light.pwmWrite(LIGHT_MIN); //Експортуємо методи роботи module.exports.light = { on: () => { //Передаємо на драйвер значення 255 light.pwmWrite(LIGHT_MAX); }, off: () => { //Передаємо на драйвер значення 0 light.pwmWrite(LIGHT_MIN); }, //Тут приймаємо значення у відсотках від 0 до 100 set: (val) => { if(val < 0 || val > 100) return; //Обчислюємо значення виходячи з відсотка val = Math.round(LIGHT_MIN + LIGHT_DIAPASONE/100 * val); console.log("Light:"+val); //Передаємо на драйвер значення light.pwmWrite(val); } }
Запустити можемо з нашого app.js так само, як і engines:
const light = require('./light.js').light; light.on(); light.off(); light.set(50);
Відеодемонстрація готового рішення:
Якщо зрозуміти головну ідею, то все гранично просто: підключив, подав сигнал, дивишся результат. Грубо кажучи, це весь функціонал контролю, який нам потрібен для активізації елементів. Але все це марно без якогось контролера, який і буде включати/регулювати все це, а це говорить про те, що треба побудувати комунікацію між сервером і клієнтом. Значить придумаємо протокол :)
Протокол спілкування
Як ви зрозуміли з первісної налаштування Raspberry PI, спілкування буде вироблено за сокет підключення. І потрібен якийсь набір команд, який буде захардкожен на сервері і клієнті. Так як я сам собі д'артаньян, то вирішив використати свій формат, бо варіанти, які є, не зовсім підходять за критерієм вага/читаність. Будуємо, грунтуючись на базі моделі управління, а це телефон з Android на борту.
Ось приблизно та сама модель (програма буде описана нижче). Екран поділений на дві частини. Ліва відповідає за горизонтальну площину (вперед і назад), права — за вертикальну.
Ставите палець в якійсь області — це вважається точкою відліку і контролю парою двигунів, але при цьому це точка спокою.
Якщо ведете його вертикально вгору — потихеньку підвищується швидкість обертання обох двигунів у прямому напрямку з потужністю від 0 до максимуму (залежить від відстані від точки торкання до поточної).
Якщо рухаємо вниз: обидва двигуна обертаються в реверсному напрямку, зі швидкістю, залежною від віддалення від точки дотику.
Якщо вправо: лівий крутиться в прямому, правий в реверсному.
Тобто за фактом нам потрібно знати кут відхилення від точки дотику (де верх = 90, низ — 90, право 0, ліво 180) і коефіцієнт швидкості — віддаленість пальця від точки дотику (від 0 до 100). Кінцеві команди виглядає так:
C:L;A:45;V:35; C:R;A:0;V:100; C:LIGHT;V:50;
З — будь-яка команда починається з цієї букви (command);
: — роздільник ключ-значення;
; — роздільник пар ключ-значення;
L — каже, що натискання було в лівій частині екрану;
R — відповідно у правій;
LIGHT — раптово, світло :)
A — ANGLE кут відхилення;
V — value значення чогось.
Тобто перша команда каже: «Горизонтальна пара двигунів, напрямок 45 градусів (лівий на максимумі (але максимум — це 35%, виходячи з V), правий варто), зі швидкістю обертання 35% від максимуму».
Друга відповідно: «Вертикальна пара двигунів, напрямок 0 градусів (лівий на максимумі, правий на максимальному реверсі), з максимальною швидкістю обертання». Третя: «Включити світло на 50% яскравості».
Так і шолом з сокету на кожну зміну якогось значення окремі команди блоками ключ-значення. Далі магічним способом перетворюємо це команди на плати управління. Розписувати перетворення цих даних — дуже нудно, тут і так багато тексту. Так що кожен може включити уяву або спробувати розібратися в контролері .
Додаток на Android
Нарешті-то ми «доїхали» до створення контролера для всього цього. В цілому, спілкування складається з «натиснув на щось на екрані — передав значення на сервер». Коротко пройдемося по базовому додатком. Тут немає нічого незвичайного і надскладного, тому весь код викладати не буду. При бажанні зайдете на репозиторій і подивіться. Також хочу зазначити, що всі новомодні патерни MVVM, MVP, VIPER і т. д. пішли лісом, бо мені треба було робоче додаток в мінімальні терміни, а не випендрюватися. Юайка проста, так що робиться максимально просто .
Першим ділом створимо активність з вьюхами відображення стану.
Лаяут буде наступний:
- Шар відображення відео (плеєр инкапсулирован під фрагмент ).
- Шар JoystickView — кастомний вьюха, в якій перевизначений метод dispatchTouchEventі в ньому обробляємо натискання і переміщення пальців по екрану. Вона ж малює UI-лінії від натискання до поточного стану. Обов'язковий аспект — підтримка мультитача, так як управління здійснюється двома пальцями.
- Шар з TextView поточної температури Raspberry PI (якщо щось пішло не так і вона починає грітися — можна припинити використання раніше, ніж вона вимкнеться/згорить). Благо, такого не було.
- Шар відображення даних з гіроскопа. Набір TextView в парах label-значення. Виводить «сирі дані» з гіроскопа. Планувалося їх обробляти, але часу не вистачило для розбору всього і винаходи крутого відображення.
- Шар з SeekBar — управління світлодіодом, простий елемент, при перетягуванні якого змінюється значення від 0 до 100.
Відео Stream
Тут було зроблено досить багато роботи для підбору необхідних способів стрімінг.
HLS відразу відпав, так як затримка була близько 10 секунд. При бажанні можна було добитися 5, але це все одно далеко не те, що треба. В управлінні вкрай необхідно передати відео з мінімальною затримкою. Але передачу організувати не складно, що з боку Raspberry, що відображення Android.
RTMP вже буде важче. Проблема в тому, що не було особливо простого варіанту реалізації на Android (на момент кінця 2017 року). Є платні варіанти, з якими теж повозитися, є багатостраждальний VLC, який треба компилить з C++ і налаштовувати, є вже зібраний VLC як бібліотека, який теж з коробки не хотів програвати. Якщо додати до всього, що це було як хобі і робилося у вечірній післяробочий час, коли голова не дуже свіжа і часу ±2 години, то ці варіанти я відкинув.
Зате знайшов дуже навіть робоче рішення, яке підходило за моїми параметрами. RPi Camera Viewer for Android — рішення, яке отримує Raw H. 264 потік з камери і відображає на TextureView c використанням нативного MediaCodec. Якщо чесно, то я до кінця не розкрутив всю логіку відображення, але зібрав і трохи модифікував під себе для використання. Власне, головним з боку Android виступає DecoderThread . Він коннектітся по IP і порту, декодит потік і віддає в Surface, а в разі обриву або помилки йде рекконект кожні 2 секунди.
З боку Raspberry команда запуску потоку буде наступна:
raspivid -t 0 -w 1280 -h 720 -hf -ih -fps 10 -o - | nc -k -l 2222
Raspivid — команда, яка здійснює «захоплення» відео з підключеної камери (але не USB, а саме підключеної шлейфом). Параметри вибирав методом «наукового» тику. Якість 1280?720, частота кадрів 10fps. Шалено мало, але мінімальна затримка.
І весь потік, який транслюється з камери передається без змін командою Netcat на 2222 порт.
Додаток на Android коннектітся до нього і вже обробляє відео, відображаючи на екрані.
Тиждень вечорів довелося витратити на пошук і тестування таких простих рішень, так як спілкування з Raspberry для мене було новизну, як і стрімінг з Unix-системи.
Управління з екрану і джойстиком
Як вже було сказано раніше, JoystickView передає кут і значення торкання екрана на сокет у вигляді команди типу C:L;A:45;V:35;
Є потік підключення до сокета (з реконнектами, колбеками та іншим), в якому знаходиться чергу команд. Циклом обирає їх і відправляє на сервер.
В допомогу екранному управління допилил управління джойстиком .
Даний девайс коннектітся по Bluetooth і працює як клавіатура. Отже, всі дані обробляються MainActivity методами onKeyDown і onGenericMotionEvent.
Тест управління:
Начебто нічого важливого з боку Android не забув, якщо щось не розкрив — пишіть, поясню, допишу.
Зборка і тестування конструкцій
Тепер ми дісталися до наступного кроку — засунути все як-то у що-то (ги-ги). На етапі проектування і складання мені допомагав один дуже хороший чоловік на ім'я Сергій . Власне, він знайшов коробку, зробив якусь залізну платформу, на яку можна було кріпити двигуни в різних позиціях, і зібрав все це ось такий варіант:
Забіжу вперед і скажу, що варіант з 6-ма двигунами було б продуктивніше, але я не вигрібав проектування чотирьох, і було вирішено тестувати урізану версію. Спочатку ми вирішили протестить конструкцію в цілому і не стали засовувати електроніку в коробку, а просто прикріпили двигуни до платформи, та по двох крученим парам подали на кожен з них сигнал з ESC (який залишався завжди на суші).
Ось такий адок вийшов в результаті:
Забили болт на втрати при передачі (кабелю було 10 метрів) і на основні косяки такого рішення. Тому основна мета-подивитися:
- Чи можна жити з 4-ма двигунами?
- Чи можна керувати з телефону?
- Буде затікати вода в коробку?
Але я упустив один важливий момент, про який Серьога мене попереджав. Після тесту у ванній всі ці контакти треба було гарненько просушити, бо вони — найслабша ланка. А я забив і отримав в результаті комбо з 48 місць(!!!) сполуки, які можуть (а якщо можуть — обов'язково будуть) втрачати контакт.
Тут як на всіх мемасиках очікування-реальність.
Тест у ванній:
Його сильно кренить з-за дротів, але він здатний переміщатися. Ну і трохи перевантажений вантажами, так як коробка порожня — не розрахував. А ось наступний тест «поле» виявився ще гіршим:
Двигуни і контакти трохи піддалися корозії, з-за низької температури кабель втратив пластичність, і вже все відбувалося не так жваво. Та й гребні гвинти були невідповідного розміру.
Але, найголовніше, що був вже зрозумілий шлях доопрацювань і конструкційних змін. Спочатку я намагався залишити харчування поза апарату, але тести показали, що це просто неможливо. Величезні втрати постійного струму говорили про те, що або треба використовувати кабель товщиною з руку, або засунути батареї всередину.
Так як ніхто не був упевнений, що на глибині 10+ метрів ця конструкція не протече і не заллє електроніку — вирішили зробити посилений варіант «коробка в коробці». Замовили в Китаї все необхідне і приступили до подальшої збірці:
У коробці були просвердлені отвори для проводів, які виходили через cable glands (забув, як вони називаються по-іншому).
Додатково була прикручена коробка для діодів і камери. Природно, на гумову прокладку й щільно затиснута гвинтами. Далі була оповита плата сполук, з окремими роз'ємами під кожен ESC, драйвер діода і гіроскоп.
Щоб спростити і без того нагромаджені конструкції, харчування зробили одне (для всіх вузлів) — від LiPO батареї, так як тільки вона здатна видати такий струм розрядки, який покрив би і Малина, і діоди, і двигуни. Але додали плату зменшення напруги з 12В до 5V, яка живила тільки малину. «Працюємо далі» ©
Отримали ось такий внутрішній дата-бокс. 4 виводу мотора, 2 діода і шлейф відео. З ним довелося повозитися найбільше. Правда, в останній момент підключення камера не злетіла (на тестах було все добре) — десь таки переламав шлейф під час нескінченних розбирань і збірок. Так як вже не було сил це все перезбирати ще раз — вирішено було забити, так прикріпити на скотч екшн-камеру в водозащитном кейсі і провести нові тестування.
До речі, були замінені також гребні гвинти на максимально великі з тих, які продаються для моделей, і замінені на більш потужні двигуни. На кожен електромотор, який відповідав за горизонтальну площину, було прикріплено по 2 гвинта, просто тому, що вони були :)
В якості роутера був підключений наш NEXX . Тобто з Raspberry PI виходить вита пара і коннектітся допомогою RJ-45 в маршрутизатор. Він же, у свою чергу, роздає Wi-Fi для Android-телефону. На малині забитий статичну адресу IP, що дозволяє не налаштовувати кожен раз підключення. Правда знову є проблеми з підключенням — якщо немає нормального покриття зв'язку, телефон не може приєднатися нормально по IP. Для цього відключав мобільний інтернет і намагався приєднатися кілька разів. Після підключення відкривав SSH на телефоні і запускав команди запуску node сервера (а після всього — вимикання системи).
Настав час нових тестів і, здавалося б, що може піти не так? Та все :)
По приїзді на київське водосховище були виявлені хвилі нелюдських розмірів (ну це я так себе втішаю).
Та наслідки таких запливів: повністю забиті двигуна всякої плаваючою штукою типу водоростей, піском. Тест був знову провалений.
Третій і самий успішний тест був проведений вже на спокійному озері, що давало чималі переваги у порівнянні з першими двома.
Висновки
Головне, що з цього всього вийшло — великий досвід у побудові таких ось зв'язок і систем. У процесі розробки було використано чимала кількість різних девайсів, і їх управління виявилося набагато простіше, ніж це виглядає здалеку.
Конструкція на чотирьох двигунах не сильно виправдала свої очікування — керувати таким девайсом вкрай важко. В ідеалі треба робити чотири вертикальних двигуна, мінімум два горизонтальних. При цьому стабілізувати всі гіроскопом, бо людина губиться в управлінні, і йому треба допомагати (як це роблять всі літаючі побратими).
Також всі мотори, які є у продажу повітря дуже швидко забиваються піском і іншим, піддаються корозії окремих елементів (що логічно) і навряд чи підійдуть під такі проекти. Конкретно моторів для підводної роботи я не знайшов (точніше, на них може і написано, що waterproof, але це від бризок і глибини до метра).
Після цього проекту будь розумний будинок не здається таким вже складним. Сервер, команди на реле, та й все. Головне, час і бажання. Інше прийде.
А сенс?
А його немає.
Обіцяні посилання:
Для запуску використовуємо файл app.js.
Всім добра.
Опубліковано: 07/12/18 @ 11:00
Розділ Різне
Рекомендуємо:
Автоматизація тестування API, або Чому я вирішив обрати Postman
Наша перемога в Google Premier Partner Awards 2018
Кар'єра в IT: посада Embedded-розробник
Senior у пошуках роботи. Про задачі на технічних співбесідах і теоретичні питання
Финстрип за Листопад 2018, інфо-сайти. 140К+, багато думок