Олимпиада НТИ 2019
Работа с MQTT
MQTT – протокол для обмена сообщениями между различными устройствами. Этот протокол используется для отправки команд дрону на Олимпиаде НТИ 2019. Для отправки сообщения оно публикуется в определенный топик; все подписчики этого топика получают это сообщение.
Подписка на топики
В образе Клевера для Олимпиады НТИ 2019 предустановлена библиотека paho-mqtt
для 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.
Отправка сообщений
Для отправки сообщений можно использовать метод publish
клиента:
import paho.mqtt.client as mqtt
# Создание подключения - аналогично предыдущему примеру кода
# ...
client.publish(topic='/xxx', payload='connected')
Данный код опубликует сообщение connected
в топик /xxx
.
Проверка
Для проверки вы можете опубликовать любое сообщение в топик с помощью команды hbmqtt_pub
:
hbmqtt_pub --url mqtt://192.168.1.199:1883 -t /xxx -m 'сообщение'
Где 192.168.1.199
– IP-адрес MQTT-брокера, сообщение
– сообщение для публикации, /xxx
– необходимый топик для публикации.
Чтобы проверить публикацию сообщений от клиента, воспользуйтесь командой hbmqtt_sub
:
hbmqtt_sub --url mqtt://192.168.1.199:1883 -t /xxx
Отправленные в топик /xxx
сообщения будут показаны в терминале.
Работа с Клевером
Для выполнения команд на Клевере:
- подключитесь в Wi-Fi сети NTI;
- подключитесь к вашему Клеверу по SSH по его IP-адресу (подробнее см. подключение по SSH);
После подключения к своему дрону по SSH, смените пароль SSH-доступа, чтобы другие участники не смогли несанкционированно подключаться к нему. Для этого используйте команду
passwd
.
Для редактирования файлов на Клевере вы можете использовать консольные редакторы nano
или vim
. Также вы можете загружать файлы используя PyCharm или WinSCP.
Для автономного полета используйте API модуля simple_offboard.
При использовании русских букв в скрипте на Python 2, добавьте в начало программы следующую строку:
# coding: utf8
Пример программы, выполняющей взлет, полет в точку в системе координат площадки и посадку на 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 метр из командной строки:
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 и примеры кода.
Работа с светодиодной лентой
В используемой версии Клевера LED-лента подключена напрямую к Raspberry Pi. При включении всех светодиодов ленты на полную мощность возможно повреждение цепей питания микрокомпьютера.
Сигнальный провод ленты подключен к GPIO-пину 18.
Про работу с LED-лентой можно прочитать в соответствующей статье.
Работа с LED-лентой через ROS
В образ Клевера для Олимпиады НТИ включена нода ROS, работающая со светодиодной подсветкой. С её помощью можно управлять светодиодами, не запуская свою программу из-под sudo
. По умолчанию эта нода выключена, но её можно включить, если в файле /home/pi/catkin_ws/src/ros_ws281x/launch/clever4.launch
изменить строку
<arg name="enable" default="false"/>
на
<arg name="enable" default="true"/>
и перезапустить службу rosled
:
sudo systemctl restart rosled
Пример работы со светодиодной лентой:
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)