Привет Хабр! Я работаю RnD-художником в минском центре разработки Wargaming. А в свободное время даю волю своей инженерной фантазии. В этой статье я хочу поделиться своим опытом домашнего марсоходостроения.
Началось все с того, что захотелось почувствовать, каково управлять марсоходом, находящимся за десятки световых минут. Доступа к настоящему роверу, конечно же, нет, поэтому построил свой. Главным условием, которое я был намерен соблюсти – симуляция временного лага в управлении. Усилий потрачено масса, но нужный результат был достигнут.
Не буду подробно останавливаться на своих изысканиях, а сразу опишу финальную конструкцию. Если будет интересно, почему я пришел к этим решениям и комплектующим (или сможете предложить варианты лучше), то милости прошу в комментарии.
Hardware
Перво-наперво была приобретена платформа — радиоуправляемый танк Abrams M1A2, содержимое которого (за исключением двигателей) было беспощадно выпотрошено:
Далее, я приступил к поиску источника питания. К сожалению, Плутоний-238 для самодельного РИТЭГа найти не удалось, поэтому пришлось использовать альтернативный вариант. Достойной заменой стала батарея из 16 аккумуляторов NCR18650B (3400 мА) с контроллером заряда/разряда. Расчетная емкость — 27 А/ч при напряжении 7,2 В:
Мозгом ровера стал Raspberry Pi 2. Шлейфом (удлиненным и армированным скотчем) к нему подключена камера без IR фильтра, с инфракрасной подсветкой:
Подсветка потребляет достаточно много энергии, поэтому включается лишь во время съемки при уровне освещения ниже установленного порога. Также только на время замера включается и оптический дальномер. Включение и отключение происходит при помощи 40-амперных мосфетов (других ключей в запасе не нашлось), управляемых микроконтроллером Atmega328 с минимальной обвязкой. К этому же контроллеру подключен дальномер и фоторезистор. Для последнего я подобрал функцию аппроксимации под промышленный люксметр, взятый на работе (там мы его используем для валидации освещения). Также я использовал бесконтактный датчик температуры MLX90614. Вся эта обвязка была собрана на макетной плате и прикручена к серводвигателям вращения головы. К Raspberry головной модуль подключен всего четырьмя проводами — питание и шина I2C.
Для управления сервоприводами я использовал специализированный I2C-контроллер PCA9685 на 16 ШИМ выходов. Сначала попробовал Arduino, но библиотека для сервоприводов там реализована программно. Из-за этого возникали спонтанные рывки и подергивания во время работы контроллера с I2C. Как-то раз ровер даже начал молотить камерой о стол. После замены Arduino на PCA9685 все движения стали ровными
Для управления двигателями я использовал немного доработанный Dual Channel H-Bridge Motor Shield.
По умолчанию для управления требуется семь линий, две из которых ШИМ. Я выбросил входную логику, подключившись к входам драйверов на прямую. Получилось пять управляющих линий. Но теперь стало необходимо, чтобы четыре из них управлялись через ШИМ. Пытался использовать оставшиеся линии PCA9685, но оказалось, что частота ШИМ на всех каналах должны быть одна, а 50 герц для управления серводвигателями мне никак не подходили. У Raspberry всего один полноценный ШИМ, а связываться с программными не хотелось. В результате я решил использовать дополнительный контроллер, который заодно измеряет напряжение батареи. В итоге получился I2C моторшилд:
Дополнительно я установил энкодеры от мышек с прорезинеными колесами от них же, которые отслеживают скорость и дистанцию при движении. Энкодеры подключены к малинке через RC-фильтры и вызывают прерывания в обработчике при срабатывании. Применение простых фильтров почти убрало дребезг контактов, а оставшиеся случайные срабатывания элементарно отсекались в коде. Скорость меряется по временным промежутка между прерываниями, и усредняется по нескольким замерам.
Для питания ровера используется два понижающих стабилизатора — отдельно для Raspberry и электроники, и отдельно для сервоприводов. Внутренности марсохода выглядят так:
Датчики влажности и давления, а также GPS, я вынес на «броню». GPS подключен к нативному последовательному порту Raspberry. В итоге у меня получился вот такой «зоопарк» I2C-устройств:
Изначально в качестве центра управления трудился Arduino c радиомодулем, подключенным к PC. Но решение было не совсем аутентичным, поэтому я заменил его на еще один Raspberry Pi 2, управляемый по SSH. Заодно это позволило управлять ровером не из дома (если вы понимаете, о чем я). Обмен данными с ровером идет через модули nrf24L01 PA.
Итоговое фото марсохода:
Software
Для работы с железом я собрал и установил библиотеки BCM2835, WiringPi, NRF24 и OpenCV. По поводу последней были большие опасения, но на удивление, все прошло гладко. Хоть на сборку и ушло более трех часов. OpenCV понадобился для исправления сильного искажения камеры (для этого откалибровал камеру), для отрисовки GUI поверх снимков и создания примитивной панорамы:
С библиотеками для I2C-датчиков пришлось повозиться. В итоге я понял, что каждый датчик предпочитает свою частоту шины. Большую часть кода датчиков пришлось переписать под библиотеку BCM2835 — тут частоту шины можно регулировать. Не обошлось без казусов. Оказалось, что магнитомер девятиосевого гироскопа —отдельный девайс со своим адресом. При его программном подключении на общую шину он почему-то вырубал датчик влажности, хотя они и имели разные адреса. Поэтому магнитометр программно подключается к шине только перед замером.
Обновлять данные акселерометра для фильтра MadgwickAHRS нужно с равными промежутками времени, поэтому его код я вынес в отдельный поток. Во избежание конфликтов доступа к шине добавил мьютекс для I2C. Кстати, MadgwickAHRS очень удобная штука — скармливаем ему сырые данные, а на выходе получаем готовый отфильтрованный кватернион поворота ровера в пространстве. Чтобы данные сильно не плыли, необходим откалиброванный гироскоп. Также он позволяет получать крен и тангаж марсохода и осуществлять поворот на заданный угол.
Для реализации настраиваемой задержки радиомодуля я использовал очереди из структур (32 байта пакета и метка времени), а также два отдельных потока. Один непосредственно общается с радиомодулем, а второй занимается управлением данными. Опять же есть мьютексы для защиты от конфликтов. Протокол обмена прост до безобразия — принять/отправить строку или файл. Благодаря этому можно передать роверу скомпилированную программу из центра управления — в итоге пригодилось для правки багов уже во время миссии.
Двигателями управляю посредством ПИД-регуляторов, с учетом данных от энкодеров. На настройку коэффициентов регуляторов потратил несколько дней, снимая десятки и даже сотни графиков и подстраивая параметры. Но в итоге добился плавной и четкой работы.
Были некоторые проблемы с потерей данных на шине I2C между малиной и микроконтроллерами. Решилось использованием контрольных сумм.
Написал парсер для модуля GPS, благо протокол NMEA 0183 не отличается сложностью. Парсер работает в отдельном потоке и посимвольно обрабатывает входящую информацию.
Научил марсоход делать тепловые карты, используя mlx90614 (тот же вид с балкона):
Миссия 1: тест в домашних условиях
Изначально я планировал выехать загород и запустить марсоход в сельской местности. Но из-за неблагоприятных погодных условий (снежный покров в полметра), было решено произвести запуск прямо в квартире.
Итак, ровер был активирован перед уходом на работу. Я удаленно связался с центром управления и успешно установил связь с марсоходом. Задержку сигнала установил на две минуты. Вскоре получил первую панораму (кликабельно):
В целом лог управления ровером выглядел так:
Внезапно обнаружился баг в коде — ровер не ждал окончания команд движения. Пришлось править, собирать и отправлять ему пофикшенную прошивку. И вот ровер отправляется навстречу неизвестности:
Но всплыла очередная проблема — внезапно начала портиться приходящая по радиоканалу информация. Пример (тут отключена проверка на контрольную сумму, иначе файлы не были бы приняты):
Быстрый разбор показал, что виновата шина SPI в центре управления. Снизив ее частоту вдвое, проблему удалось решить. Заодно я снизил частоту шины для ровера и залил ему новую прошивку.
Итак, миссия продолжилась. Первый тепловой скан:
В целом удаленно управлять ровером со значительной задержкой оказалось очень увлекательно. Вот только скорость продвижения невысока. Например, заехав в кладовку, ровер целый час пытался оттуда выбраться. Как потом стало ясно — я не рассчитал расстояние и зацепился головным модулем за шланг пылесоса:
Когда в центре управления отказал радиомодуль, миссию пришлось свернуть, т.к. на уровне ПО решить проблему не удалось (подвели разъемы).
Весна близко! Поэтому планирую в ближайшее время пофиксить все баги и отправить марсоход на улицу.
Если вы можете поделиться полезным опытом или просто хотите задать вопросы — пишите комментарии.
Все исходники тут github.com/DIMOSUS/Rover