Merge branch 'master' into qt-gui-update

This commit is contained in:
Artem30801
2020-01-02 19:30:17 +03:00
14 changed files with 175 additions and 72 deletions

7
Drone/README.md Normal file
View File

@@ -0,0 +1,7 @@
# Client of clever-show
Application for remote synchronized control of drones and emergency drone protection module.
Documentation is located here:
* English
* [Russian](../docs/ru/client.md)

View File

@@ -115,7 +115,7 @@ class Client(object):
logger.critical("Caught interrupt, exiting!")
self.selector.close()
def _reconnect(self, timeout=3.0, attempt_limit=5):
def _reconnect(self, timeout=2.0, attempt_limit=3):
logger.info("Trying to connect to {}:{} ...".format(self.server_host, self.server_port))
attempt_count = 0
while not self.connected:
@@ -153,7 +153,7 @@ class Client(object):
self.selector.register(self.client_socket, events, data=self.server_connection)
self.server_connection.connect(self.selector, self.client_socket, (self.server_host, self.server_port))
def broadcast_bind(self, timeout=3.0, attempt_limit=5):
def broadcast_bind(self, timeout=2.0, attempt_limit=3):
broadcast_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
broadcast_client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
broadcast_client.bind(("", self.broadcast_port))

View File

@@ -791,6 +791,7 @@ class Telemetry:
try:
self.calibration_status = get_calibration_status()
self.system_status = get_sys_status()
self.battery = self.get_battery(self.ros_telemetry)
except rospy.ServiceException:
rospy.logdebug("Some service is unavailable")
self.selfcheck = ["WAIT_ROS"]
@@ -798,7 +799,6 @@ class Telemetry:
rospy.logdebug(e)
except rospy.TransportException as e:
rospy.logdebug(e)
self.battery = self.get_battery(self.ros_telemetry)
def update(self):
self.update_telemetry_fast()

View File

@@ -2,7 +2,9 @@
[Русская версия](README_RU.md)
Software for making the drone show controlled by Raspberry Pi and COEX [Clever](https://github.com/CopterExpress/clever) package.
Software for making the drone show controlled by Raspberry Pi, PX4 and COEX [Clever](https://github.com/CopterExpress/clever) package.
Create animation in Blender, convert it to drone paths, set up the drones and run your own show!
[![Build Status](https://travis-ci.org/CopterExpress/clever-show.svg?branch=master)](https://travis-ci.org/CopterExpress/clever-show)
@@ -14,9 +16,9 @@ Software for making the drone show controlled by Raspberry Pi and COEX [Clever](
## This software includes
* [Drone side](https://github.com/CopterExpress/clever-show/tree/master/Drone) with autonomous flight module, animation player module and client application for remote synchronized control of drones
* [Drone side](https://github.com/CopterExpress/clever-show/tree/master/Drone) for remote synchronized control of drones with emergency drone protection module
* [Server side](https://github.com/CopterExpress/clever-show/tree/master/Server) for making the drone show with ability of tuning drones, animation and music
* [Blender 2.8 addon](https://github.com/CopterExpress/clever-show/tree/master/blender-addon) for animation export to drone paths
* [Blender 2.8 addon](https://github.com/CopterExpress/clever-show/tree/master/blender-addon) for exporting animation to drone paths
* [Raspberry Pi image](https://github.com/CopterExpress/clever-show/releases/latest) for quick launch software on the drones
## Documentation

View File

@@ -1,19 +1,28 @@
# clever-show
[English version](README.md)
Програмное обеспечение для запуска шоу дронов под управлением Raspberry Pi с пакетом COEX [Clever](https://github.com/CopterExpress/clever).
Програмное обеспечение для запуска шоу дронов под управлением Raspberry Pi с пакетом COEX [Clever](https://github.com/CopterExpress/clever) и полётного контроллера с прошивкой PX4.
Создайте анимацию в Blender, сконвертируйте её в полётные пути дронов, настройте дроны и запустите своё собственное шоу дронов!
[![Build Status](https://travis-ci.org/CopterExpress/clever-show.svg?branch=master)](https://travis-ci.org/CopterExpress/clever-show)
## Демонстрационное видео
[![Autonomous drone show in a theater](http://img.youtube.com/vi/HdHbZFz7nR0/0.jpg)](http://www.youtube.com/watch?v=HdHbZFz7nR0)
12 дронов выступают в Электротеатре Станиславский, Москва.
## Пакет включает в себя
* [Набор ПО для дрона](https://github.com/CopterExpress/clever-show/tree/master/Drone) с библиотекой для автономного полёта, модулем воспроизведения анимаций и клиентским приложением для удаленного синхронизированного управления дронами
* [Набор ПО для дрона](https://github.com/CopterExpress/clever-show/tree/master/Drone) с клиентским приложением для удаленного синхронизированного управления дронами и модулем экстренной посадки.
* [Серверное приложение](https://github.com/CopterExpress/clever-show/tree/master/Server) для создания шоу и настройки дронов, анимации и музыки
* [Аддон для Blender 2.8](https://github.com/CopterExpress/clever-show/tree/master/blender-addon) для преобразования анимации полёта коптеров, созданной в Blender, в файлы полётов для каждого коптера
* [Образ для Raspberry Pi](https://github.com/CopterExpress/clever-show/releases/latest) для быстрого запуска ПО на коптере
## Документация
Инструкция по запуску ПО находится [здесь](docs/ru/start-tutorial.md).
Подробная документация расположена в папке [docs](https://github.com/CopterExpress/clever-show/tree/master/docs).

7
Server/README.md Normal file
View File

@@ -0,0 +1,7 @@
# Server of clever-show
Application for creating and running drone shows, adjusting drones, animations and music.
Documentation is located here:
* English
* [Russian](../docs/ru/server.md)

View File

@@ -336,7 +336,8 @@ def view_battery(value):
def view_selfcheck(value):
if isinstance(value, list):
if len(value)==1:
return value[0]
if len(value[0]) <= 8:
return value[0]
return "ERROR"
return value

View File

@@ -205,10 +205,6 @@ class Server(messaging.Singleton):
logging.error(f"Unexpected error {e}!")
raise
finally:
broadcast_sock.close()
logging.info("Broadcast sender thread stopped, socked closed!")
def _broadcast_listen(self):
logging.info("Broadcast listener thread started!")
broadcast_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

View File

@@ -21,13 +21,14 @@ from quamash import QEventLoop
from server_gui import Ui_MainWindow
from server import Server, Client, now
import messaging_lib as messaging
import config as cfg
import copter_table_models as table
from copter_table import CopterTableWidget
#from emergency import *
# TODO uncomment
from visual_land_dialog import VisualLandDialog
def multi_glob(*patterns):
@@ -272,7 +273,6 @@ class MainWindow(QtWidgets.QMainWindow):
self.send_to_selected("resume", {"time": server.time_now() + time_gap})
self.ui.pause_button.setText('Pause')
@pyqtSlot()
def land_selected(self):
for copter in self.model.user_selected():
@@ -286,7 +286,6 @@ class MainWindow(QtWidgets.QMainWindow):
def disarm_all(self):
Client.broadcast_message("disarm")
@pyqtSlot()
def test_leds_selected(self):
for copter in self.model.user_selected():
@@ -538,51 +537,9 @@ class MainWindow(QtWidgets.QMainWindow):
self.player.play()
@pyqtSlot()
def emergency(self): # TODO refactor for the sake of god
client_row_min = 0
client_row_max = self.model.rowCount() - 1
result = -1
while (result != 0) and (result != 3) and (result != 4):
# light_green_red(min, max)
client_row_mid = int(math.ceil((client_row_max + client_row_min) / 2.0))
print(client_row_min, client_row_mid, client_row_max)
for row_num in range(client_row_min, client_row_mid):
self.model.data_contents[row_num].client \
.send_message("led_fill", {"green": 255})
for row_num in range(client_row_mid, client_row_max + 1):
self.model.data_contents[row_num].client \
.send_message("led_fill", {"red": 255})
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
result = Dialog.exec()
print("Dialog result: {}".format(result))
if client_row_max != client_row_min:
if result == 1:
for row_num in range(client_row_mid, client_row_max + 1):
self.model.data_contents[row_num].client \
.send_message("led_fill")
client_row_max = client_row_mid - 1
elif result == 2:
for row_num in range(client_row_min, client_row_mid):
self.model.data_contents[row_num].client \
.send_message("led_fill")
client_row_min = client_row_mid
if result == 0:
Client.broadcast_message("led_fill")
elif result == 3:
for row_num in range(client_row_min, client_row_max + 1):
self.model.data_contents[row_num].client \
.send_message("land")
elif result == 4:
for row_num in range(client_row_min, client_row_max + 1):
self.model.data_contents[row_num].client \
.send_message("disarm")
def visual_land(self):
dialog = VisualLandDialog(self.model)
dialog.start()
@messaging.message_callback("telemetry")

View File

@@ -0,0 +1,112 @@
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtGui import QKeySequence
from PyQt5 import QtWidgets
import visual_land
import math
import logging
import sys
from functools import partial
# TODO: previous step and reset
class VisualLandDialog(QtWidgets.QDialog):
def __init__(self, model):
super(VisualLandDialog, self).__init__()
self.ui = visual_land.Ui_Dialog()
self.setupUi()
self.model = model
self.row_min = 0
self.row_max = self.model.rowCount() - 1
self._finished = False
def setupUi(self):
self.ui.setupUi(self)
self.ui.one_button.clicked.connect(partial(self.selection_choice, 1))
self.ui.two_button.clicked.connect(partial(self.selection_choice, 2))
self.ui.land_emergency_button.clicked.connect(partial(self.send_to_selected, "land", None))
self.ui.disarm_emergency_button.clicked.connect(partial(self.send_to_selected, "disarm", None))
self.ui.one_button.setShortcut(QKeySequence("1"))
self.ui.two_button.setShortcut(QKeySequence("2"))
self.ui.land_emergency_button.setShortcut(QKeySequence("L"))
self.ui.disarm_emergency_button.setShortcut(QKeySequence("D"))
@property
def row_mid(self):
return int(math.ceil((self.row_min + self.row_max) / 2.0))
def send_to_row(self, row, message, args=None):
logging.debug(f"Send {message}: {args} to {row}")
self.model.data_contents[row].client.send_message(message, args)
# test[row] = args # for testing
# print(test)
def clear_leds(self, rows):
for row in rows:
self.send_to_row(row, "led_fill")
def start(self):
self.show()
self.send_led_indication()
self.exec()
def send_led_indication(self):
for row in range(self.row_min, self.row_mid):
self.send_to_row(row, "led_fill", {"green": 255})
for row in range(self.row_mid, self.row_max + 1):
self.send_to_row(row, "led_fill", {"red": 255})
@pyqtSlot()
def selection_choice(self, choice):
if self.row_min == self.row_max:
# self.ui.one_button.setDisabled(True) # maybe?
# self.ui.two_button.setDisabled(True)
return
if choice == 1:
to_clear = range(self.row_mid, self.row_max + 1)
self.row_max = self.row_mid - 1
elif choice == 2:
to_clear = range(self.row_min, self.row_mid)
self.row_min = self.row_mid
else:
return
self.clear_leds(to_clear)
self.send_led_indication()
@pyqtSlot()
def send_to_selected(self, message, args=None):
for row in range(self.row_min, self.row_max + 1):
self.send_to_row(row, message, args)
self._finished = True
self.close()
def closeEvent(self, event):
if not self._finished:
self.clear_leds(range(self.row_min, self.row_max + 1))
event.accept()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
app = QtWidgets.QApplication(sys.argv)
import copter_table_models
model = copter_table_models.CopterDataModel()
for i in range(10):
model.add_client(copter_table_models.StatedCopterData())
dialog = VisualLandDialog(model)
test = list(range(10))
dialog.start()

Binary file not shown.

View File

@@ -21,8 +21,8 @@ cd source-dir
sudo docker run --privileged -it --rm -v /dev:/dev -v $(pwd):/mnt goldarte/img-tool:v0.5
```
The image will be located in `images` directory in the clever-show source code directory.
The image will be located in `images` directory in the root of clever-show source code directory.
Article about building custom image is located here:
Full documentation about building custom image is located here:
* English
* [Russian](../docs/ru/image-building.md)

View File

@@ -105,13 +105,13 @@
* Кнопка `Pause/Resume` - ставит на паузу и возобновляет выполнение полётных задач. После каждого нажатия кнопка меняет состояние на обратное.
* Состояние`Pause` - ставит на паузу очередь заданий всех выбранных коптеров: приостанавливается выполнение любого полётного задания. Рекомендуется использовать в чрезвычайных ситуациях для определения неисправного коптера. **Внимание!** Данная команда НЕ прерывает полёт коптера в уже указанную точку (например: элементы взлёта, посадки; следование до начальной точки анимации и т.д.)
* Состояние `Resume` - все выбранные коптеры *синхронизированно* продолжат выполнение своих очередей заданий (например исполнение анимации)
#### Средства перехвата в экстренных ситуациях
* Кнопка `Land selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.**
* Кнопка `Land ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки. **Используйте в экстренных случаях как одно из средств перехвата.**
* Кнопка `Land selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки AUTO.LAND. **Используйте в экстренных случаях как одно из средств перехвата.**
* Кнопка `Land ALL` - ВСЕ коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим посадки AUTO.LAND. **Используйте в экстренных случаях как одно из средств перехвата.**
* Кнопка `Emergency land` - прерывает выполнение полётных заданий *ВСЕХ* подключенных коптеров. Сбрасывает очередь заданий - *действие необратимо*. Выполняет полную остановку и немедленную посадку коптеров. **Используйте в экстренных случаях как одно из средств перехвата.**
* Кнопка `Emergency land` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно переходят в режим экстренной посадки - на все моторы подаётся небольшая мощность, которая уменьшается через определённое время до нуля. **Используйте в экстренных случаях как одно из средств перехвата.**
* Кнопка `Visual land` - открывает диалоговое окно модуля визуальной посадки неисправного коптера. Полное описание находится в [конце статьи](#visual-land).
* Кнопка `Disarm selected` - все выбранные коптеры прекращают выполнение своих полётных заданий, очищают очередь заданий и немедленно отключают моторы. Это может привести к падению и повреждению коптеров.

View File

@@ -1,4 +1,4 @@
# Инструкция по настройке и запуску клиента и сервера
# Быстрый старт
## Список оборудования
@@ -36,7 +36,7 @@ cd ~/clever-show/Drone
sudo ./client_setup.sh <SSID> <password> <copter name> <server ip>
```
* Теперь при запуске серверного приложения настроенные коптеры будут отображаться в виде таблицы. Также можно подключаться к Raspberry Pi на коптере по его имени через `ssh` в указанной при настройке wifi сети, например `ssh pi@clever-1`, пароль `raspberry`.
* Теперь при запуске серверного приложения настроенные коптеры будут отображаться в виде таблицы. Также можно подключаться к Raspberry Pi на коптере по его имени с добавкой .local через `ssh` в указанной при настройке wifi сети, например `ssh pi@clever-1.local`, пароль `raspberry`. Если у вас установлено приложение samba, то к коптеру можно подключаться по его имени, например `ssh pi@clever-1`.
Документация по клиентской части находится [здесь](client.md).
@@ -70,3 +70,15 @@ python3 server_qt.py
```
Документация по серверной части находится [здесь](server.md).
## Подготовка дрона
Для запуска анимации все коптеры должны иметь настроенную систему позиционирования. По-умолчанию в образе настроен полёт по optical flow - на коптере должен быть установлер лазерный дальномер, а камера должна быть наклонена вниз шлейфом назад.
Вы можете настроить один коптер на любую систему позиционирования, которую поддерживает пакет [clever](https://clever.coex.tech/ru/programming.html#positioning), а затем размножить настройки на остальные коптеры с [сервера](server.md#раздел-сервер) с помощью команды `Send launch files`.
## Подготовка анимации
* Создайте анимацию объектов в Blender или воспользуйтесь [примерами](../../blender-addon/examples). Условная единица расстояния в Blender конвертируется в метры. Задержка между кадрами по-умолчанию в [настройках коптера](../../Drone/client_config.ini) равна 0.1 секунды (параметр frame_delay в разделе ANIMATION), будьте внимательны при настройке частоты кадров в анимации Blender. Следите за скоростями коптеров, чтобы они были не слишком большими: аддон выдаст предупреждение, но всё равно сконвертирует анимацию.
* Сконвертируйте анимацию с помощью [аддона для Blender](blender-addon.md).
* Загрузите анимацию с помощью команды `Send animations` на [сервере](server.md#раздел-server).