# Олимпиада НТИ 2019 ## Работа с MQTT [MQTT](https://ru.wikipedia.org/wiki/MQTT) – протокол для обмена сообщениями между различными устройствами. Этот протокол используется для отправки команд дрону на Олимпиаде НТИ 2019. Для отправки сообщения оно публикуется в определенный топик; все подписчики этого топика получают это сообщение. ### Подписка на топики В образе Клевера для Олимпиады НТИ 2019 предустановлена библиотека `paho-mqtt` для Python. Пример работы с этой библиотекой описан ниже: ```python import paho.mqtt.client as mqtt # Импортирование библиотеки mqtt # Callback, вызываемый при получении от сервера подтверждения о подключении def on_connect(client, userdata, flags, rc): print("Connected with result code "+str(rc)) # Если подписываться на топик в on_connect, то при обрыве соединения # и повторном подключении произойдёт автоматическое переподписание client.subscribe("/xxx") # Callback, вызываемый при появлении сообщения в одном из топиков, на который # подписан клиент def on_message(client, userdata, msg): # В объекте msg хранится топик, в который пришло сообщение (в поле topic) # и само сообщение (в поле payload) print(msg.topic, str(msg.payload)) # Инициализация клиента MQTT client = mqtt.Client() # Здесь указываются callback'и, вызываемые при подключении и получении сообщения client.on_connect = on_connect client.on_message = on_message # Подключение к MQTT-брокеру. Первый параметр - имя или адрес брокера, второй - порт # (по умолчанию 1883), третий - максимальное время между сообщениями в секундах # (по умолчанию 60). client.connect('192.168.1.199', 1883, 60) # Метод loop_start создаёт поток, в котором будет производиться опрос сервера и # вызов callback'ов. client.loop_start() # Далее продолжается ваша программа ``` Более подробная документация доступна на [странице библиотеки в PyPI](https://pypi.org/project/paho-mqtt/). ### Отправка сообщений Для отправки сообщений можно использовать метод `publish` клиента: ```python import paho.mqtt.client as mqtt # Создание подключения - аналогично предыдущему примеру кода # ... client.publish(topic='/xxx', payload='connected') ``` Данный код опубликует сообщение `connected` в топик `/xxx`. ### Проверка Для проверки вы можете опубликовать любое сообщение в топик с помощью команды `hbmqtt_pub`: ```bash hbmqtt_pub --url mqtt://192.168.1.199:1883 -t /xxx -m 'сообщение' ``` Где `192.168.1.199` – IP-адрес MQTT-брокера, `сообщение` – сообщение для публикации, `/xxx` – необходимый топик для публикации. Чтобы проверить публикацию сообщений от клиента, воспользуйтесь командой `hbmqtt_sub`: ```bash hbmqtt_sub --url mqtt://192.168.1.199:1883 -t /xxx ``` Отправленные в топик `/xxx` сообщения будут показаны в терминале. ## Работа с Клевером Для выполнения команд на Клевере: * подключитесь в Wi-Fi сети NTI; * подключитесь к вашему Клеверу по SSH по его IP-адресу (подробнее см. [подключение по SSH](ssh.md)); > **Caution** После подключения к своему дрону по SSH, смените пароль SSH-доступа, чтобы другие участники не смогли несанкционированно подключаться к нему. Для этого [используйте](https://www.raspberrypi-spy.co.uk/2012/10/how-to-change-raspberry-pi-password/) команду `passwd`. Для редактирования файлов на Клевере вы можете использовать консольные редакторы `nano` или `vim`. Также вы можете загружать файлы используя PyCharm или WinSCP. Для автономного полета используйте API модуля [simple_offboard](simple_offboard.md). > **Hint** При использовании русских букв в скрипте на Python 2, добавьте в начало программы следующую строку: > ```python > # coding: utf8 > ``` Пример программы, выполняющей взлет, полет в точку в системе координат площадки и посадку на Python: ```python # coding: utf8 import rospy from clever import srv from std_srvs.srv import Trigger rospy.init_node('flight') get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry) navigate = rospy.ServiceProxy('navigate', srv.Navigate) land = rospy.ServiceProxy('land', Trigger) # Взлет на 1 метр со скоростью 1 метр в секунду navigate(x=0, y=0, z=1, speed=1, frame_id='body', auto_arm=True) # Ждем 5 секунд rospy.sleep(5) # Полет на координаты x=3, y=2, z=1 площадки с углом по рысканью 3.14 радиан со скоростью 0.5 метров в секунду navigate(x=3, y=2, z=1, yaw=3.14, speed=0.5, frame_id='aruco_map') # Ждем 5 секунд rospy.sleep(5) # Посадка land() ``` Пример взлета на высоту 1 метр из командной строки: ```bash rosservice call /navigate "{x: 0.0, y: 0.0, z: 2, yaw: 0.0, yaw_rate: 0.0, speed: 0.5, frame_id: 'body', auto_arm: true}" ``` Для более подробной информации и описания других команд смотрите [API simple_offboard](simple_offboard.md) и [примеры кода](snippets.md). ### Работа с светодиодной лентой В используемой версии Клевера LED-лента подключена напрямую к Raspberry Pi. При включении всех светодиодов ленты на полную мощность возможно повреждение цепей питания микрокомпьютера. Сигнальный провод ленты подключен к GPIO-пину 18. Про работу с LED-лентой можно прочитать [в соответствующей статье](leds.md). ### Работа с LED-лентой через ROS В образ Клевера для Олимпиады НТИ включена нода ROS, работающая со светодиодной подсветкой. С её помощью можно управлять светодиодами, не запуская свою программу из-под `sudo`. По умолчанию эта нода выключена, но её можно включить, если в файле `/home/pi/catkin_ws/src/ros_ws281x/launch/clever4.launch` изменить строку ```xml ``` на ```xml ``` и перезапустить службу `rosled`: ```bash sudo systemctl restart rosled ``` Пример работы со светодиодной лентой: ```python import rospy # Загружаем из ноды LED-ленты описание сервиса SetLeds... from ros_ws281x.srv import SetLeds # ...и сообщений LEDState и LEDStateArray. Сообщение LEDState # содержит номер светодиода и его цвет, LEDStateArray - массив # сообщений LEDState from ros_ws281x.msg import LEDState, LEDStateArray # Для задания цвета используется стандартное сообщение ColorRGBA from std_msgs.msg import ColorRGBA # Количество светодиодов в ленте NUM_LEDS = 60 # Прокси к сервису установки состояния светодиодов set_leds = rospy.ServiceProxy("/led/set_leds", SetLeds, persistent=True) # Вспомогательная функция заполнения всей ленты указанным цветом. # red, green, blue - интенсивность красного, зелёного, синего цвета # (задаётся числом от 0 до 255) def fill_strip(red, green, blue): # Создаём сообщение для setLeds led_msg = LEDStateArray() led_msg.leds = [] # Для каждого светодиода указываем его новое состояние for i in range(NUM_LEDS): led = LEDState(i, ColorRGBA(red, green, blue, 0)) # Записываем состояние светодиода в сообщение led_msg.leds.append(led) # Вызываем сервис. После этого вся лента должна стать указанного цвета set_leds(led_msg) # Заполняем ленту разными цветами fill_strip(255, 0, 0) rospy.sleep(2.0) fill_strip(0, 255, 0) rospy.sleep(2.0) fill_strip(0, 0, 255) rospy.sleep(2.0) # Выключение ленты fill_strip(0, 0, 0) ```