Всем привет! Проектирование, печать и сборка нового корпуса наконец-то завершились. Также завершился запуск новой платы управления на базе STM32F373 и FW успешно перенесено на новый МК. Все ближе подходит релиз версии 1.00 с базовым функционалом. Теперь можно рассказать о том, что еще ни разу не упоминалось в цикле — прикладное ПО для управления и используемые протоколы для коммуникации. Как всегда, будет много картинок, видео и список граблей, на которые я успел наступить с прыжка.

Этапы разработки:

Часть 1 — проектирование
Часть 2 — сборка
Часть 3 — кинематика
Часть 4 — математика траекторий и последовательности
Часть 5 — электроника
Часть 6 — переход на 3D печать
Часть 7 — новый корпус, прикладное ПО и протоколы общения

Небольшой обзор новых изменений


Были куплены еще 8 штук DS3218MG (с запасом) и удалось полностью перейти на новые приводы (в COXA были MG996R). Это решило проблему с синхронизацией скорости поворота COXA и скоростей FEMUR и TIBIA, так как приводы были разные.

После долгих 10 дней печати на 3D принтере, в свет вышел новый пластиковый корпус. Визуально он получился больше предыдущего, но физически размеры остались прежними. Сборка корпуса заняла 5 дней с учетом печати новых деталей для замены деталей с косяками, ну куда же без них.

Вес корпуса без электроники и приводов составляет ~1.5кг. Вес приводов ~1.2кг. Электроника весит ~90г, АКБ весит ~148г. Суммарный вес гексапода составляет около 3кг.

Фото корпуса во время различных этапов его сборки






Как вы могли заметить, внутри стоит активное охлаждение в виде 2-х вентиляторов 35х35mm. Я оставил их для своего спокойствия. Перегрева ни разу еще не было и не ожидается, так как гексапод столько не проработает, АКБ сядет раньше :) При длительной работе (более 30 минут) от внешнего блока питания наблюдается ощутимый нагрев силовой платы. Термометр в виде левой руки показал температуру около 70 градусов. Также отсутствует теплоотвод в виде медных полигонов на другой стороне платы, это обусловлено её домашним производством. В ближайшее время закажу производство в Китае с учетом этого недостатка, да и вообще многих других.

Спереди имеются 4 RGB светодиода для индикации состояния гексапода. К примеру, при низком заряде АКБ светодиоды начнут часто мигать красным цветом, попутно сводя с ума пьезопищалкой. При потере соединения светодиоды будут уже мигать синим, но без пищалки и более редко. Если все ОК — горят зеленым. Ну все кейсы описывать не буду :) Ниже видео с процессом инициализации гексапода для наглядности процесса.


И видео с тестом при потере соединения:


Китайцы наконец-то произвели и прислали новую плату управления, и минимальный набор компонентов был напаян в тот же день. На плате был найден всего 1 некритичный косяк — перепутаны выводы тактовой кнопки RESET (там их 4), что решается откусыванием 2-х выводов по диагонали.


Я ожидал больше косяков, но рад что их нет (даже RX и TX не перепутал). Фото платы под спойлером, также добавил пару фоток старого бутерброда для сравнения.

Плату заказывал на PCBWay, само производство заняло 24 часа, как и обещали. Ехало дольше обычного — 19 дней доставкой ePacket (обычно с этой доставкой доходит за 12-14 дней). Стоимость суммарно составила $12 с производством и доставкой 5 плат (минимальное количество для заказа), что по сравнению с ценами в нашей стране очень дешево.

На данный момент я не стал переводить коммуникацию на Bluetooth, поэтому гексапод по-прежнему управляется через WIFI соединение (возможность подключения Bluetooth к плате управления уже заложено).

Фото платы управления




Фото с описанием разъемов

Для отладки выведен SWD интерфейс на контакты для пайки проводов (на фото провода уже напаяны). К плате подключаются следующие блоки: концевики с конечностей, шлейф на плату питания для передачи сигнала приводам, пищалка со встроенным генератором, передние RGB светодиоды, питание, балансировочный разъем с АКБ для контроля заряда ячеек.

Прошивка платы


Все МК от ST, которые я встречал, поддерживали программирование через USART и STM32F373 не исключение. Это позволяет программировать МК без использования программатора или отладчика, которые не очень дешевые. В данном случае можно использовать любой USB-UART преобразователь за 150 рублей с Ali и программу «STM32 Flash loader demonstrator».

Для прошивки платы нужно выполнить манипуляцию с BOOT джампером и кнопкой RESET (надеть и нажать). Далее необходимо подключить USB-UART преобразователь к разъему SERVICE (RX, TX, GND) и воспользоваться программой «STM32 Flash loader demonstrator» (как ей пользоваться можно найти в интернете).

Программное обеспечение


Перейдем к тому, чего я еще не описывал в процессе разработки — приложение для взаимодействия пользователя с гексаподом (AIWM Control).

С ноутбуком ходить по улице не очень удобно, поэтому я решил сделать ПО под Android (телефон сейчас у всех в кармане есть). Программа написана на C++ с Qt\QML, что позволило еще и сохранить кроссплатформенность приложения, так что под Windows получить exe-шник не проблема.

Интерфейс программы не богат функционалом и сделан максимально простым. Программа имеет 3 возможных экрана: подключения, терминала и управления. Внутри это реализовано при помощи QML SwipeView. Из любого экрана можно вернуться на экран подключения при помощи кнопки возврата на телефоне. При нажатии кнопки возврата на экране подключения приложение завершает работу.

Экран подключения. Ну тут все просто — всего 2 кнопки. Отсюда можно перейти на экран управления и на экран терминала. Нажали — перешли. Внутри это реализовано установкой соответствующего индекса в SwipeView, остальное сделает Qt. При входе на экран запускается отдельный поток для обработки коммуникации (мы же не любим, когда интерфейс тормозит) и при выходе с экрана он убивается.

Скриншоты

Экран управления. Тут интереснее. На этом экране отображаются заряд батареи и статус модулей гексапода. При ошибке одного из модулей загорается красным соответствующий ему прямоугольник. Так же имеется индикация ошибок для понимания, что же именно произошло с модулем. К примеру, если было неправильно сконфигурировано ядро передвижения, то загорится прямоугольник «Movement engine» и «Configuration error».

Пример состояния блоков при нормальной работе гексапода. В данном случае ошибок нет и все модули подсвечены зеленым.


Я думаю стоит кратко описать показанные здесь модули и ошибки. Начнем с ошибок.

  • FATAL_ERROR — фатальная ошибка системы. Выставляется в случае обнаружения ошибки, при которой работа невозможна или опасна. К примеру, было обнаружено, что параметр внешней функции имеет невалидное значение. Так как эта функция вызывается другим модулем, то прошивка явно содержит какую-то неверную логику. В этом случае FW запускает аварийный цикл, в котором работает только индикация светодиодами и коммуникация;
  • CONFIGURATION_ERROR — ошибка конфигурации. Выставляется в случае обнаружения неверного параметра в конфигурации (к примеру, длина TIBIA = 0xFFFF мм). Работа в данном случае опасна и одновременно с этой ошибкой выставляется FATAL_ERROR со всем вытекающими;
  • MEMORY_ERROR — ошибка памяти. Выставляется в случае отваливания EEPROM, либо сбоя на шине I2C. Работа невозможна, так как нельзя загрузить конфигурацию. Одновременно с этой ошибкой выставляется FATAL_ERROR со всем вытекающими;
  • VOLTAGE_ERROR — ошибка напряжения. Выставляется в случае обнаружения низкого суммарного напряжения АКБ, либо одной из его ячеек. В этой случае гексапод просто садится на брюхо и пищит, мигая красными светодиодами;
  • SYNC_ERROR — ошибка синхронизации. Выставляется в случае обнаружения пропуска цикла вычислений. Синхронизация в гексаподе завязана на частоту PWM сервоприводов (160 Гц) и все расчеты должны проводиться с такой же частотой. Рассинхронизация возможна в результате выполнения долгой операции (запись большого блока в EEPROM), либо мы просто не успеваем считать;
  • MATH_ERROR — ошибка математики. Выставляется в случае обнаружения деления на 0, сбоя FPU и прочих радостей. В случае установки ошибки отключается модуль, в котором она была обнаружена;
  • CONNECTION_LOST — соединение потеряно. Выставляется в случае обнаружения потери соединения. По идеи, этот блок никогда не должен загореться красным, так как в случае обнаружения потери соединения (а это означает, что от мастера нет пакетов) гексапод перестает слать пакеты с текущим состоянием. Ошибка нужна для внутреннего использования, в случае её установки гексапод садиться на брюхо и мигает синими светодиодами (видео в начале статьи);

Модули:

  • CONFIGURATOR — отвечает за работу с EEPROM. Предоставляет интерфейс для чтения и записи данных;
  • SERVO_DRIVER — драйвер сервоприводов. Занимается расчетом ширины импульса и организует синхронную передачу значения драйверу PWM;
  • LIMBS_DRIVER — драйвер конечностей. Занимается расчетами траекторий движения конечности и решает задачу обратной кинематики;
  • MOVEMENT_ENGINE — ядро передвижения. Предоставляет интерфейс для переключения между последовательностями и организует передачу координат драйверу конечностей;
  • SYSTEM_MONITOR — модуль слежения за состоянием системы. Предоставляет интерфейс для установки\сброса ошибок, выполняет аналоговые измерения и расчет соответствующих величин (напряжений);

Есть различные элементы управления для посылки соответствующих команд гексаподу для движения. Мечи тут не просто так — он может ткнуть в кого-нибудь, либо постучать в дверь :) Кстати, из-за довольно большого веса и мощных приводов тыкает он довольно ощутимо.

Скриншот

Экран терминала. Сервисный экран, предназначенный для конфигурации и отладки гексапода. Экран очень простой в плане функционала и выполняет 2 функции: принять и послать. Всё. Для коммуникации используется CLI и о нем поговорим чуть ниже.

Скриншот

Кишки. Внутри все устроено достаточно просто. Есть 2 потока: главный и поток коммуникации. В главном потоке обрабатываются GUI и ядро приложения, в потоке коммуникации обрабатывается (внезапно) коммуникация. Общение между потоками осуществляется при помощи сигналов и слотов.

Коммуникация осуществляется при помощи QUdpSocket, а на более верхнем уровне используется SWLP (Simple WireLess Protocol) протокол. Можно не пытаться гуглить описание протокола — это мое детище и о нем поговорим чуть позже.

Передача пакетов осуществляется по событию таймера. При обработке события таймера поток коммуникации дергает главный поток (генерирует сигнал), который отдает ему данные для передачи. Далее данные запаковываются в SWLP фрейм и передаются по UDP протоколу через WIFI соединение.

При приеме пакета обрабатывается сигнал от сокета о том, что датаграмма пришла. Далее идет ряд проверок, извлекаются данные и генерируется сигнал главному потоку «Данные пришли — забирай». Все просто. Главный поток, после этого, начинает обновлять интерфейс в соответствии с принятыми данными.

Протокол коммуникации — Simple Wireless Protocol (SWLP)


Это основной протокол коммуникации с гексаподом в нормальном режиме работы. В качества транспортного протокола используется UDP/IP. На момент написания статьи формат фрейма (ADU) и полезной нагрузки (PDU) имеют следующий вид:


  • Start marker — маркер начала пакета. Имеет фиксированное значение 0xAAAA. Нужен для дополнительного контроля целостности пакета;
  • Packet number — номер пакета. Протокол UDP не гарантирует очередность пакетов и это поле необходимо для отбрасывания старых пакетов. Значение увеличивается гексаподом при каждом принятии пакета;
  • PDU (Payload Data Unit) — содержит полезные данные (внезапно). Бывает двух типов COMMAND и STATUS;
  • CRC16 — нужен для контроля целостности пакета. Полином 0xA001, алгоритм расчета стащил из спецификации ModBus;

Большая часть PDU зарезервирована для дополнительных данных, которые могут понадобиться в будущем. Поля в PDU думаю нет смысла описывать, там названия говорят сами за себя. Можно прокомментировать только то, что напряжение передается в mV.

Больше интересен сам процесс коммуникации. Протокол использует модель «Master — Slave». Master (в данном случае приложение на Android) постоянно посылает пакеты c «PDU COMMAND» минимум 2 раза в секунду, slave (гексапод) на каждый пакет от мастера должен сформировать и отправить пакет с «PDU STATUS». При этом гексапод не может запросить данные у мастера, да и запрашивать то нечего.

Почему я не использовал какой-нибудь из существующих протоколов? А зачем? Большинство из них имеют переменную длину или избыточные заголовки. Я больше люблю ситуации когда точно известно сколько байт я должен принять, мне так спокойнее. Возможно это неправильно и неэффективно, но это работает просто и достаточно надежно.

В прошивке прием фрейма (ADU) основан на одной из очень полезной функции USART — RTO (Receiver Timeout). Эта штука позволяет установить значение времени «тишины» на линии RX микроконтроллера и как только тишина длиться дольше установленного времени, генерируется прерывание. Это позволяет использовать DMA для приема данных без обработки каждого байта в прерывании и не нужно искать начало пакета в потоке данных.

Исходя из выше сказанного, пауза между пакетами должна быть больше времени передачи 30 битов. Значение 30 выбрано не просто так, это время передачи 3.5 символа по USARTу (с небольшим запасом) и это правило взято из спецификации ModBus протокола.

Отрывок из спецификации ModBus


Протокол отладки — CLI


Для использования протокола нужно в приложении на экране подключения нажать на кнопку «Терминал». По умолчанию активным протоколом коммуникации является SWPL. Для использования CLI необходимо послать «cli cli cli» для переключения протокола. Приложение это делает автоматически при переходе на экран терминала.

После этого мы можем использовать команды CLI для управления гексаподом. Команды имеют следующий формат: <имя модуля> <команда> <аргументы>.

Примеры:
— «system status» запрашивает текущую информацию о системе;
— «servo calibration 1500» отключает все сервоприводы от драйвера и устанавливает ширину импульса 1500us для коррекции углов;
— «config write 0200 0102AABB» записывает данные 0102AABB в EEPROM по адресу 0200;

Пример установки обратного направления для 1 и 3 привода


Полный список команд (вывод help)

Такого списка команд более чем достаточно, чтобы потрогать различные места в прошивке в процессе работы. По мере расширения функционала этот список соответственно будет расти.

Грабли


Ниже я приведу список эпичных ошибок в процессе разработки, начиная с момента печати корпуса:

  • На плате управления я предусмотрел защиту от переполюсовки в виде диода. Компоненты для платы пришли частями и этот самый диод как раз шел в другой партии. У меня были другие диоды в наличии, но я решил впаять перемычку, руководствуясь «Не ну я же разработал эту плату и знаю где + и -». Раз так на 10й я все-таки перепутал VDD и GND. Все обошлось только убитыми стабилизаторами на 3V3 и 5V соответственно, МК остался цел (дорогой негодяй);
  • В драйвере сервоприводов была предусмотрена функция ограничения угла поворота привода и соответственно при настройке я её не включил. Действительно, зачем? В итоге я не заметил как TIBIA уперлась в FEMUR (положение 0 находится близко к границе касания), привод в этом время упорно пытался довернуть TIBIA в нужную позицию. Через минут 15 начало веять теплом от конечности, потрогав её я обнаружил что чёт она горячая слишком. Привод был мертв. Полностью. И DC мотор и оба транзистора. Хорошо, что купил на 2 штуки больше;

Вроде ничего не забыл эпичного.

Я не стал перечислять подобные моменты:

Нужно было вытащить провод от антенны из заглушки. Для этого нужно снять крышку открутив 8 болтов. Что из этого вышло:

  1. во время снятия крышки открутилась одна из пластиковых стоек, в результате этого нужно было снять конечность с оси COXA (теперь это можно сделать быстро);
  2. снял, закрутил стойку, поставил конечность на место;
  3. поставил крышку на место и закрутил 8 болтов обратно;

Что я забыл? Правильно — провод антенны вытащить, погнали обратно разбирать. В общем было весело :) Хорошо, что к этому моменту я обзавелся электрической отверткой и процесс вращения болтов стал приносить удовольствие.


Видео


В процессе разработки я решил сделать видео по сборке корпуса. К сожалению, удалось сделать видео только для сборки конечностей. К тому моменту, когда пришла эта идея, туловище было уже собрано на 80% и разбирать его не очень хотелось. Может быть в другой раз.


Погуляем в новом корпусе (без звука):