Создаем робота в домашних условиях
Время на прочтение
8 мин
Количество просмотров 198K
Наверняка, насмотревшись фильмов про роботов, тебе не раз хотелось построить своего боевого товарища, но ты не знал с чего начать. Конечно, у тебя не получится построить двуногого терминатора, но мы и не стремимся к этому. Собрать простого робота может любой, кто умеет правильно держать паяльник в руках и для этого не нужно глубоких знаний, хотя они и не помешают. Любительское роботостроение мало чем отличается от схемотехники, только гораздо интереснее, потому что тут так же затронуты такие области, как механика и программирование. Все компоненты легкодоступны и стоят не так уж и дорого. Так что прогресс не стоит на месте, и мы будем его использовать в свою пользу.
Введение
Итак. Что же такое робот? В большинстве случаев это автоматическое устройство, которое реагирует на какие-либо действия окружающей среды. Роботы могут управляться человеком или выполнять заранее запрограммированные действия. Обычно на роботе располагают разнообразные датчики (расстояния, угла поворота, ускорения), видеокамеры, манипуляторы. Электронная часть робота состоит из микроконтроллера (МК) – микросхема, в которую заключён процессор, тактовый генератор, различная периферия, оперативная и постоянная память. В мире существует огромное количество разнообразных микроконтроллеров для разных областей применения и на их основе можно собирать мощных роботов. Для любительских построек широкое применение нашли микроконтроллеры AVR. Они, на сегодняшний день, самые доступные и в интернете можно найти много примеров на основе этих МК. Чтобы работать с микроконтроллерами тебе нужно уметь программировать на ассемблере или на Cи и иметь начальные знания в цифровой и аналоговой электронике. В нашем проекте мы будем использовать Cи. Программирование для МК мало чем отличается от программирования на компьютере, синтаксис языка такой же, большинство функций практически ничем не отличаются, а новые довольно легко освоить и ими удобно пользоваться.
Что нам нужно
Для начала наш робот будет уметь просто объезжать препятствия, то есть повторять нормальное поведение большинства животных в природе. Всё что нам потребуется для постройки такого робота можно будет найти в радиотехнических магазинах. Решим, как наш робот будет передвигаться. Самым удачным я считаю гусеницы, которые применяются в танках, это наиболее удобное решение, потому что гусеницы имеют большую проходимость, чем колёса машины и ими удобнее управлять (для поворота достаточно вращать гусеницы в разные стороны). Поэтому тебе понадобится любой игрушечный танк, у которого гусеницы вращаются независимо друг от друга, такой можно купить в любом магазине игрушек по разумной цене. От этого танка тебе понадобится только платформа с гусеницами и моторы с редукторами, остальное ты можешь смело открутить и выкинуть. Так же нам потребуется микроконтроллер, мой выбор пал на ATmega16 – у него достаточно портов для подключения датчиков и периферии и вообще он довольно удобный. Ещё тебе потребуется закупить немного радиодеталей, паяльник, мультиметр.
Делаем плату с МК
Схема робота
В нашем случае микроконтроллер будет выполнять функции мозга, но начнём мы не с него, а с питания мозга робота. Правильное питание – залог здоровья, поэтому мы начнём с того, как правильно кормить нашего робота, потому что на этом обычно ошибаются начинающие роботостроители. А для того, чтобы наш робот работал нормально нужно использовать стабилизатор напряжения. Я предпочитаю микросхему L7805 – она предназначена, чтобы на выходе выдавать стабильное напряжение 5В, которое и нужно нашему микроконтроллеру. Но из-за того, что падение напряжения на этой микросхеме составляет порядка 2,5В к нему нужно подавать минимум 7,5В. Вместе с этим стабилизатором используются электролитические конденсаторы, чтобы сгладить пульсации напряжения и в цепь обязательно включают диод, для защиты от переполюсовки.
Теперь мы можем заняться нашим микроконтроллером. Корпус у МК — DIP (так удобнее паять) и имеет сорок выводов. На борту имеется АЦП, ШИМ, USART и много другого, что мы пока использовать не будем. Рассмотрим несколько важных узлов. Вывод RESET (9-ая нога МК) подтянут резистором R1 к «плюсу» источника питания – это нужно делать обязательно! Иначе твой МК может непреднамеренно сбрасываться или, проще говоря – глючить. Так же желательной мерой, но не обязательной является подключение RESET’а через керамический конденсатор C1 к «земле». На схеме ты так же можешь увидеть электролит на 1000 мкФ, он спасает от провалов напряжения при работе двигателей, что тоже благоприятно скажется на работе микроконтроллера. Кварцевый резонатор X1 и конденсаторы C2, C3 нужно располагать как можно ближе к выводам XTAL1 и XTAL2.
О том, как прошивать МК, я рассказывать не буду, так как об этом можно прочитать в интернете. Писать программу мы будем на Cи, в качестве среды программирования я выбрал CodeVisionAVR. Это довольно удобная среда и полезна новичкам, потому что имеет встроенный мастер создания кода.
Плата моего робота
Управление двигателями
Не менее важным компонентом в нашем роботе является драйвер двигателей, который облегчает нам задачу в управлении им. Никогда и ни в коем случае нельзя подключать двигатели напрямую к МК! Вообще мощными нагрузками нельзя управлять с микроконтроллера напрямую, иначе он сгорит. Пользуйтесь ключевыми транзисторами. Для нашего случая есть специальная микросхема – L293D. В подобных несложных проектах всегда старайтесь использовать именно эту микросхему с индексом «D», так как она имеет встроенные диоды для защиты от перегрузок. Этой микросхемой очень легко управлять и её просто достать в радиотехнических магазинах. Она выпускается в двух корпусах DIP и SOIC. Мы будем использовать в корпусе DIP из-за удобства монтажа на плате. L293D имеет раздельное питание двигателей и логики. Поэтому саму микросхему мы будем питать от стабилизатора (вход VSS), а двигатели напрямую от аккумуляторов (вход VS). L293D выдерживает нагрузку 600 мА на каждый канал, а этих каналов у неё два, то есть к одной микросхеме можно подключить два двигателя. Но, чтобы перестраховаться, мы объединим каналы, и тогда потребуется по одной микре на каждый двигатель. Отсюда следует, что L293D сможет выдержать 1.2 А. Чтобы этого добиться нужно объединить ноги микры, как показано на схеме. Микросхема работает следующим образом: когда на IN1 и IN2 подаётся логический «0», а на IN3 и IN4 логическая единица, то двигатель вращается в одну сторону, а если инвертировать сигналы – подать логический ноль, тогда двигатель начнёт вращаться в другую сторону. Выводы EN1 и EN2 отвечают за включение каждого канала. Их мы соединяем и подключаем к «плюсу» питания от стабилизатора. Так как микросхема греется во время работы, а установка радиаторов проблематична на этот тип корпуса, то отвод тепла обеспечивается ногами GND — их лучше распаивать на широкой контактной площадке. Вот и всё, что на первое время тебе нужно знать о драйверах двигателей.
Датчики препятствий
Чтобы наш робот мог ориентироваться и не врезался во всё, мы установим на него два инфракрасных датчика. Самый простейший датчик состоит из ик-диода, который излучает в инфракрасном спектре и фототранзистор, который будет принимать сигнал с ик-диода. Принцип такой: когда перед датчиком нет преграды, то ик-лучи не попадают на фототранзистор и он не открывается. Если перед датчиком препятствие, тогда лучи от него отражаются и попадают на транзистор – он открывается и начинает течь ток. Недостаток таких датчиков в том, что они могут по-разному реагировать на различные поверхности и не защищены от помех — от посторонних сигналов других устройств датчик, случайно, может сработать. От помех может защитить модулирование сигнала, но пока мы этим заморачиватся не будем. Для начала, и этого хватит.
Первый вариант датчиков моего робота
Прошивка робота
Чтобы оживить робота, для него нужно написать прошивку, то есть программу, которая бы снимала показания с датчиков и управляла двигателями. Моя программа наиболее проста, она не содержит сложных конструкций и всем будет понятна. Следующие две строки подключают заголовочные файлы для нашего микроконтроллера и команды для формирования задержек:
#include <mega16.h>
#include <delay.h>
Следующие строки условные, потому что значения PORTC зависят от того, как ты подключил драйвер двигателей к своему микроконтроллеру:
PORTC.0 = 1;
PORTC.1 = 0;
PORTC.2 = 1;
PORTC.3 = 0;
Значение 0xFF означает, что на выходе будет лог. «1», а 0x00 – лог. «0».
Следующей конструкцией мы проверяем, есть ли перед роботом препятствие и с какой оно стороны:
if (!(PINB & (1<<PINB.0)))
{
...
}
Если на фототранзистор попадает свет от ик-диода, то на ноге микроконтроллера устанавливается лог. «0» и робот начинает движение назад, чтобы отъехать от препятствия, потом разворачивается, чтобы снова не столкнуться с преградой и затем опять едет вперёд. Так как у нас два датчика, то мы проверяем наличие преграды два раза – справа и слева и потому можем узнать с какой стороны препятствие. Команда «delay_ms(1000)» указывает на то, что пройдёт одна секунда, прежде чем начнёт выполняться следующая команда.
Заключение
Я рассмотрел большинство аспектов, которые помогут тебе собрать твоего первого робота. Но на этом робототехника не заканчивается. Если ты соберёшь этого робота, то у тебя появится куча возможностей для его расширения. Можно усовершенствовать алгоритм робота, как например, что делать, если препятствие не с какой-то стороны, а прямо перед роботом. Так же не помешает установить энкодер – простое устройство, которое поможет точно располагать и знать расположение твоего робота в пространстве. Для наглядности возможна установка цветного или монохромного дисплея, который может показывать полезную информацию – уровень заряда аккумулятора, расстояние до препятствия, различную отладочную информацию. Не помешает и усовершенствование датчиков – установка TSOP (это ик-приёмники, которые воспринимают сигнал только определённой частоты) вместо обычных фототранзисторов. Помимо инфракрасных датчиков существуют ультразвуковые, стоят подороже, и тоже не лишены недостатков, но в последнее время набирают популярность у роботостроителей. Для того, чтобы робот мог реагировать на звук, было бы неплохо установить микрофоны с усилителем. Но по-настоящему интересным, я считаю, установка камеры и программирование на её основе машинного зрения. Есть набор специальных библиотек OpenCV, с помощью которых можно запрограммировать распознавание лиц, движения по цветным маякам и много всего интересного. Всё зависит только от твоей фантазии и умений.
Список компонентов:
- ATmega16 в корпусе DIP-40>
- L7805 в корпусе TO-220
- L293D в корпусе DIP-16 х2 шт.
- резисторы мощностью 0,25 Вт номиналами: 10 кОм х1 шт., 220 Ом х4 шт.
- конденсаторы керамические: 0.1 мкФ, 1 мкФ, 22 пФ
- конденсаторы электролитические: 1000 мкФ х 16 В, 220 мкФ х 16В х2 шт.
- диод 1N4001 или 1N4004
- кварцевый резонатор на 16 МГц
- ИК-диоды: подойдут любые в количестве двух штук.
- фототранзисторы, тоже любые, но реагирующие только на длину волны ик-лучей
Код прошивки:
/*****************************************************
Прошивка для робота
Тип МК : ATmega16
Тактовая частота : 16,000000 MHz
Если у тебя частота кварца другая, то это нужно указать в настройках среды:
Project -> Configure -> Закладка "C Compiler"
*****************************************************/
#include <mega16.h>
#include <delay.h>
void main(void)
{
//Настраиваем порты на вход
//Через эти порты мы получаем сигналы от датчиков
DDRB=0x00;
//Включаем подтягивающие резисторы
PORTB=0xFF;
//Настраиваем порты на выход
//Через эти порты мы управляем двигателями
DDRC=0xFF;
//Главный цикл программы. Здесь мы считываем значения с датчиков
//и управляем двигателями
while (1)
{
//Едем вперёд
PORTC.0 = 1;
PORTC.1 = 0;
PORTC.2 = 1;
PORTC.3 = 0;
if (!(PINB & (1<<PINB.0))) // Проверяем правый датчик
{
//Едем назад 1 секунду
PORTC.0 = 0;
PORTC.1 = 1;
PORTC.2 = 0;
PORTC.3 = 1;
delay_ms(1000);
//Заворачиваем
PORTC.0 = 1;
PORTC.1 = 0;
PORTC.2 = 0;
PORTC.3 = 1;
delay_ms(1000);
}
if (!(PINB & (1<<PINB.1))) // Проверяем левый датчик
{
//Едем назад 1 секунду
PORTC.0 = 0;
PORTC.1 = 1;
PORTC.2 = 0;
PORTC.3 = 1;
delay_ms(1000);
//Заворачиваем
PORTC.0 = 0;
PORTC.1 = 1;
PORTC.2 = 1;
PORTC.3 = 0;
delay_ms(1000);
}
};
}
О моём роботе
В данный момент мой робот практически завершён.
На нём установлена беспроводная камера, датчик расстояния (и камера и этот датчик установлены на поворотной башне), датчик препятствия, энкодер, приёмник сигналов с пульта и интерфейс RS-232 для соединения с компьютером. Работает в двух режимах: автономном и ручном (принимает сигналы управления с пульта ДУ), камера также может включаться/выключаться дистанционно или самим роботом для экономии заряда батарей. Пишу прошивку для охраны квартиры (передача изображения на компьютер, обнаружение движений, объезд помещения).
По пожеланиям выкладываю видео:
UPD. Перезалил фотографии и сделал небольшие поправки в тексте.
Статья была опубликована мною в журнале «Хакер» за август 2009 года.
Привет!
В этой статье мы на подробном примере расскажем о том, как построить настоящего робота целиком на инфракструктуре ROS. Это будет наш первый простой робот со своей операционной системой и первый опыт работы с ROS.
Далее мы постараемся пошагово и как можно подробнее рассказать вам о процессе проектирования, конструирования и программирования робота, а также расскажем, с каким трудностями и проблемами мы столкнулись.
Содержание
- Введение
- Цель робота
- Привод робота
- Типы приводов
- Выбор шасси
- Шасси Turtle
- Энкодеры
- ROS
- Бортовой компьютер
- Выбор железа
- Выбор и установка дистрибутива Linux
- Первый запуск
- Настройка Wi-Fi и графической оболочки
- Установка и настройка ROS
- Установка ROS на Raspberry Pi
- Установка ROS на настольный компьютер
- Создание рабочего пространства ROS
- Процесс разработки
- Настойка локальной сети и сети ROS
- Описание робота
- Формат URDF
- 3D-модель робота в САПР
- Экспорт в URDF
- Пакет robot_description
- Приводим в порядок URDF-файл описания робота
- Визуализация URDF-модели
- Footprint робота
- Raspberry Pi HAT и крепление электроники
- Бортовое питание
- Актуализация модели робота
- Низкоуровневые драйверы
- Пакет abot_driver
- Библиотека WiringPi
- Драйвер энкодеров
- Драйвер моторов
- Запуск драйверов
- Тест моторов и настройка параметров PID-контроллера
- Контроль движения
- Пакет robot_control
- Пакет robot_base
- Тестируем движение робота
- Визуализация одометрии
- Дистанционное управление
- Установка геймпада
- Пакет abot_teleop
- Тестируем дистанционное управление
- Навигация
- Лидар RPLIDAR A1
- Теория навигации
- Построение карты
- Стек навигации ROS
- Тестируем автономную навигацию
- Ответы на вопросы
- Заключение
Введение
Создание свеого робота — это не самая сложная, но и не тривиальная задача. Она требует опредёленных навыков хотя бы на минимальном уровне:
- Работа с OS Linux.
- Твердотельное 3D-моделирование.
- Программирование на С++ или Python.
- Навыки 3D-печати.
Также вам понадобится персональный компьютер под управлением OS Linux, Windows и локальная сеть Wi-Fi.
Все исходники — как конструкторские САПР-файлы, так и исходный код — мы разместили в GitHub-репозитории https://github.com/amperka/abot.
Цель робота
Постройка робота начинается с идеи. Прежде чем бежать собирать своего робота, вам нужно ответить для себя на следующие вопросы:
- Как должен выглядеть мой робот?
- Из каких частей/сегментов будет состоять мой робот?
- Что должен делать мой робот и как?
Все промышленные роботы создаются с какой-либо целью. Люди стремится облегчить себе жизнь, переложив часть своих задач на роботов. Машины могут выполнять тяжелую физическую работу: например, как сварочные роботы-манипуляторы или транспортные роботы на автоматизированных складах-хранилищах. Или же роботы могут взяться за опасные для человека задачи: например, обезвредить бомбу вместо сапёра, работать в завалах или токсичных и ядовитых средах. А такие вещи, как робот-пылесос и беспилотный транспорт избавляют человека от рутинных задач для экономии времени.
Назначение робота практически полностью определяет его внешний вид, конструкцию и программу. Обычно робот, созданный для конкнретных задач в одной области, не способен работать в какой-либо другой. Цель создания нашего робота будет скорее обучающей и развлекательной, нежели практической.
Мы попробуем создать «офисного питомца» — робота, который будет жить с нами в офисе и передвигаться туда, куда мы ему скажем. При этом робот должен будет самостоятельно ориентироваться в помещении. Кажется, что навигация в офисе — это лёгкая задача, однако это совсем не так. Мы планируем постепенно добавлять нашему роботу новые функции, но начнём именно с этой конкретной задачи.
Поскольку мы решили, что наш робот не стационарный (как робот-манипулятор), а мобильный, то ему сперва понадобится какое-то подвижное шасси.
Привод робота
Типы приводов
Рассмотрим разные типы приводов мобильных роботов и выясним, какую механику они используют. Затем, сравнивая все плюсы и минусы, выберем привод для нашего робота.
Роботы могут передвигаться в 2D- или 3D-пространстве. Очевидно, что только летающие роботы способны маневрировать во всех плоскостях, и они чрезвычано сложны. Например, летающие дроны ориентируются в помещении или на местности, используя трёхмерные камеры глубины. Постройка такого робота потребует сложнейшего железа и программного обеспечения, поэтому летающего дрона мы не рассматриваем.
Если роботу нужно передвигаться только в двухмерном пространстве, всё становится уже проще. Мы можем рассмотреть движение робота как движение материальной точки в плоскости (X, Y) в прямоугольной или Декартовой системе координат (X, Y, Z).
Движение робота в плоскости может быть голономным (Holonomic) или неголономным (Non-holonomic). Что это значит? При голономном движении робот способен свободно двигаться по любому вектору XY, не меняя при этом своей ориентации. При неголономном движении робот может передвигаться только в нескольких ограниченных направлениях.
Например, обычный автомобиль не может взять и подвинуться строго вправо или влево с места — значит, его движение неголономно. С другой стороны, если бы у автомобиля вместо обычных колёс стояли всенаправленные, то он смог бы двигаться голономно.
Вы, наверное, спрашиваете себя: «Зачем мне всё это нужно знать?» Понимание того, каким именно образом движется робот, и понимание принципов получения проекций его скоростей в системе координат чрезвычайно важно при создании управляющей программы. Чем больше направлений движения и вращения имеет робот, тем сложнее сконструировать его механику и контролировать его движение.
Ещё одиним важным фактором при выборе привода робота является сложность получения одометрии и её точность.
Одометрия — это использование данных с установленных на роботе сенсоров и датчиков для расчёта его текущего положения и ориентации в пространстве. С некоторых приводов получить качественную одометрию очень легко — например, с двухколёсного дифференциального привода (2WD differential drive). Для этого достаточно парочки колёсных энкодеров. С других же приводов получить точную одометрию невероятно трудно: например, для шагающей робо-собаки или робота-гуманоида вам понадобятся десятки различных 2D/3D-сенсоров и сложнейший софт.
Мы попробовали собрать самые популярные способы передвижения для хобби-роботов.
Дифференциальный привод двумя ведущими колёсами с пассивными опорами
Двухколёсный дифференциальный привод — самый простой и распространённый тип привода в любительской робототехнике. Именно он используется в домашних роботах-пылесосах.
Двухколёсное дифференциальное шасси состоит из двух ведущих колёс, которые установлены на противоположных сторонах корпуса, и одного или нескольких всенаправленных (пассивных) колёс или опор. Каждое ведущее колесо приводится в движение собственным мотором и управляется независимо друг от друга. Пассивные колёса-опоры устанавливаются на такое шасси для достижения равновесия всей платформы.
К слову, именно такой тип привода имеют наши Робоняша и Драгстер.
Движение двухколёсного дифференциального привода неголономно. Перемещение робота здесь задаётся линейной скоростью по оси Х (вперёд или назад) и угловой скоростью вокруг оси Z (вращение на месте). Синтез этих двух скоростей заставляет робота поворачивать во время движения.
Вот основные типы движения такого робота:
Особенности шасси:
- Легко сконструировать.
- Легко программировать контроллер движения.
- Легко получить относительно качественную одометрию всего двумя датчиками вращения колёс.
- Шасси не предназначено для движения по бездорожью. Любая значительная преграда на пути может вывести двухколёсную платформу из равновесия. Чаще всего это шасси используется в помещении и для движения по ровной поверхности.
Turtlebot3 от Robotis
PAL Robotics
Мобильный робот MP-500 от Neobotix
The Innok Robotics
Дифференциальный привод Skid-steer
Привод skid-steer — это расширенная версия простого двухколёсного дифференциального привода. В этом приводе равновесие платформы достигается не пассивными колесами, как в двухколёсном варианте, а дополнительными ведущими. На каждой стороне робота может быть четыре, пять, шесть и более колёс, которые управляются общим мотором через передачи и вращаются с одинаковой скоростью. Обычно в этом шасси используются два двигателя (по одному на сторону), но бывают плафтормы, где каждое колесо управляется собственным мотором.
Движение привода skid-steer неголономно. Принцип движения такой же, как и у двухколёсного дифференциального привода.
Пример задания скоростей робота:
Особенности шасси:
- В сравнении с двухколёсной платформой skid-steer обладает повышенной проходимостью. Это достигается благодаря множеству колёс и отсутствию пассивных опор. Робот с большими колесами может быть очень эффективен на пересечённой местности.
- Одометрию так же легко получить, используя датчики вращения колёс. Однако каждое колесо шасси skid-steer нуждается в собственном сенсоре. Точность одометрии в сравенении с двухколёсной платформой заметно ниже. При поворотах робота колёса шасси проскальзывают. При движении такого шасси по ровной местности моменты заноса и скольжения можно определить и исправить программно. Но для получения одометрии при движении платформы skid-steer по пересечённой местности одних только датчиков вращения колёс может быть уже не достаточно.
Husky robot
Wild Thumper 6WD от DAGU Electronics
The Innok Robotics
Дифференциальный привод с гусеницами
Дифференциальный привод с гусеницами (танковое шасси) — версия привода skid-steer с гусеницами вместо дополнительных колёс. Как и ранее, каждая гусеница и сторона робота контролируется одним мотором.
Можно интерпретировать этот привод как двухколёсный дифференциальный, где колесо имеет некруглую форму и увеличенную длину окружности. Или как привод skid-steer с бесконечным количеством колёс на определённой длине.
Движение привода с гусеницами неголономно. Принцип движения тут такой же, как и у привода skid-steer. Только для обработки перемещения робота используются не угловые скорости колёс, а скорости гусениц.
Особенности шасси:
- Танковое шасси обладает самыми высокими эксплуатационными характеристиками на пересечённой местности благодаря форме гусениц и сцеплению с землёй.
- Усложнённая механика. В конструкции танкового шасси множество непростых деталей: части трака, натяжители, опорные ролики и т. д.
- Получить одометрию ещё сложнее, чем при использовании привода skid-steer. При движении танкового шасси тоже происходят проскальзывания гусениц и заносы (особенно это заметно, когда робот-танк вращается на месте на ровной поверхности). Однако ввиду наличия всего двух датчиков вращения программно компенсировать ошибки одометрии очень тяжело. При движении по ровной поверхности одометрия неточная, а на пересечённой местности датчики вращения становятся практически бесполезны, и для одометрии понадобятся другие источники.
Robodyne MAXXII
Tank chassis
Dragon Runner Bomb Disposal Robot
Рулевой привод Аккермана
Привод Ackermann steering — самый распространённый в мире, так как используется в каждом автомобиле. Привод Аккермана состоит из двух ведущих и двух рулевых колёс. Ведущая пара колёс отвечает за движение робота, а рулевые колёса отвечают за повороты. Чтобы избежать заноса и скольжения, рулевое управление Аккермана спроектировано таким образом, что при повороте внутреннее колесо поворачивается на больший угол, чем внешнее. Для каждого колеса угол поворота рассчитывается на основе желаемого диапазона углов поворота робота.
Рулевой привод Аккермана имеет неголономное движение. Этот привод управляется линейной скоростью вдоль оси X и угловой скоростью по оси Z. Но в отличие от дифференциальных приводов, при ненулевой угловой скорости вокруг оси Z линейная скорость по X не может быть равна нулю. Как и автомобиль, робот не сможет развернуться, стоя на месте.
Особенности шасси:
- Рулевое управление Аккермана обычно используют на ровной поверхности для быстродвижущихся роботов, которые нуждаются в большом дорожном просвете и сцеплении с землёй.
- Наличие вращающихся рулевых колёс усложняет конструкцию робота и требует дополнительных двигателей и приводов.
- Отличный пример использования этого привода в робототехнике — настоящие беспилотные автомобили. Кроме того, этот привод используется в хобби-робототехнике, если робот построен на базе радиоуправляемой игрушечной машинки.
VolksBots
RB-CAR от Robotnik
Привод с Omni-колёсами
Этот тип привода использует особые Omni-колёса или поликолеса вместо обычных. Поликолесо — это всенаправленное колесо с небольшими роликами, расположенными по окружности. Оси роликов перпендикулярны оси вращения колеса. Контролируя скорость и направление вращения поликолёс, вы можете заставить робота двигаться в любом направлении — другими словами, сделать его движение голономным.
Обычно шасси с Omni-колёсами насчитывает ровно 3 или 4 колеса. Шасси с тремя колёсами обеспечивает большую тягу, поскольку любая реактивная сила распределяется только через три точки, и робот хорошо сбалансирован даже на неровной местности. Всенаправленные колёса имеют высокую стоимость, поэтому трёхколёсное шасси обходится заметно дешевле варианта с четырьмя.
Чаще всего для трёхколёсного шасси выбирают угол установки 120°. Иногда два колеса параллельны друг другу, а третье ставится перпендикулярно к ним. Последняя конструкция может быть более эффективной, потому что когда колёса расположены под углом 120°, только одно из них является ведущим, а два других по сути тормозят его, снижая общую скорость.
Поскольку колёса на таком шасси не выровнены по осям, каждое из них требует индивидуального расчёта скорости.
Пример определения векторов скоростей колёс для трёхколёсного шасси:
Четырёхколёсное шасси имеет четыре ведущих колеса, расположенных под углом 90° друг к другу. Эта конструкция удобнее для расчёта скоростей, так как два колеса параллельны друг другу, а два других перпендикулярны к ним. Как и в трёхколёсном шасси, КПД всех колёс также не используется на 100%. Но в отличие от трёхколёсного шасси, здесь есть два ведущих колеса и два свободных. Таким образом, четырёхколёсное шасси движется быстрее, чем трёхколёсное. Четвёртое колесо добавляет шасси ещё одну точку опоры, и на неровной местности одно из колёс робота может оказаться в воздухе.
Пример определения скорости вращения колёс для четырёхколёсного шасси:
Особенности шасси:
- Поскольку всенаправленные колёса представляют собой комбинацию из множества роликов, возникает сопротивление вращению, что приводит к повышенному трению и значительным потерям энергии.
- Не все колёса являются ведущими, в каждый момент времени эффективно работают лишь одно-два поликолеса.
- С помощью Omni-колёс достигается голономное движение робота.
- Работа поликолёс изначально строится на принципах проскальзывания. Невозможно поставить датчик вращения на каждый ролик Omni-колеса, поэтому полученная колёсная одометрия совсем неточная.
- Чаще всего роботы с таким шасси используются внутри помещений на ровных и гладких поверхностях.
3WD Omni wheel chassis от NEXUS robot
Soccer robots от RoboFEI Team
King Kong 4WD Omni Wheel chassis
Привод с Mecanum-колёсами
Этот тип привода использует вместо обычных колес колёса Mecanum (шведское колесо Илона), предназначенные для грузоподъёмных и проходимых роботов. По сути это разновидность Omni-колеса, только на шведском колесе Илона ролики по всей окружности обода расположены под углом 45° к плоскости и 45° к оси вращения колеса.
Поворот оси ролика позволяет использовать колёса Mecanum в приводах skid-steer. Эта комбинация объединяет преимущества шасси skid-steer и привода со всенаправленными колёсами. Колёса Илона заменяют обычные для достижения голономного движения робота. Чаще всего этот тип шасси имеет 4 Mecanum-колеса, но иногда встречается и 6 колёс.
При вращении колеса Илона прилагается сила под углом 45° к его оси. Направление вращения определяет направление приложенной силы. Комбинации сил от всех колёс позволяют роботу двигаться в разных направлениях.
Вгляните на схемы получения скоростей робота:
Особенности шасси:
- Привод с колёсами Илона используется, если робот должен обладать голономным движением и высокой грузоподъёмностью.
- Чаще всего этот тип шасси встречается у грузовых роботов, которые работают на ровной и гладкой поверхности. При использовании такого шасси на бездорожье управление движением и получение качественной одометрии с датчиков вращения колеса крайне затруднено.
Kuka robot
Mobile Robot MPO-500 от Neobotix
SUMMIT-XL STEEL от Robotnik
Скелетные роботы
Эти роботы используют конечности или ноги, чтобы перемещаться. Движение таких роботов имитирует естественное движение живого организма.
Роботы на конечностях обладают голономным движением, также как и живые организмы, кинематику которых они повторяют. Например, робот-гексапод может идти в любом направлении, не меняя ориентацию своего тела.
Подобные роботы являюстя самыми мобильными, но и самыми сложными в конструировании. Скелет конечности должен обладать множеством стенепенй свободы. Для этого требуется множество двигателей и приводов, а также сложные системы управления. Из-за большого количества приводов скелетные роботы потребляют больше всего энергии. Для получения одометрии с шагающего шасси используется синтез данных с множества различных сенсоров (энкодеры приводов, IMU-сенсоры, 3D-лидары, 3D RGB-камеры глубины, контактные датчики давления и т. д.), а также машинное обучение.
Agility Robotics
Spot от Boston Dynamics
Другие типы приводов
В хобби-робототехнике существует великое множество уникальных приводов движения, которые используются крайне редко. Вот лишь некоторые из них:
- Segway drive — дифференциальный двухколёсный привод без пассивных колес. Равновесное состояние робота достигается с помощью датчиков и контроллеров. Пример — автономный сегвей.
- Forklift steering drive — разновидность рулевого привода Аккермана, но с задней парой рулевых колёс и передней парой ведущих.
- Independent drive — привод, в котором все колёса являются ведущими и рулевыми одновременно. Колёс может быть четыре, шесть и более. Пример — марсоход.
- Articulated drive — разновидность рулевого привода Аккермана. Чтобы рулить роботом в движении, Articulated drive не поворачивает рулевые колёса, а деформирует всю рулевую часть рамы или шасси.
- Ball drive — привод, при котором робот балансирует и перемещается на сфере.
- Ползучие червеобразные и змееподобные роботы, движение которых основано на трении тела с поверхностью.
Выбор шасси
Шасси робота напрямую зависит от типа привода. Мы выбрали двухколёсный дифференциальный привод с пассивными колёсами — такой же, как в домашних роботах-пылесосах, а они как никто лучше справляются с задачей ориентации в помещении. Кроме того, это самое бюджетное и самое простое в программировании шасси.
При выборе типа привода мы руководствовались эффективностью колёсной одометрии. Самая простая и доступная одометрия для вашего робота — это датчики вращения, установленные на колёсах. Обычно такие датчики скорости представляют собой энкодеры, установленные на валах колёс, валах двигателей или коробок передач.
Сперва мы попробовали танковое шасси Rover 5 с резиновыми гусеницами. Установили дополнительные двигатели и энкодеры на колёса. Но, как оказалось, получить качественную одометрию только с помощью энкодеров довольно сложно. Когда робот вращается на месте и на высоких скоростях, гусеницы регулярно проскальзывают и результирующая одометрия отличается от фактического положения робота. Поэтому мы решили начать с более простого шасси.
Вы можете сначала выбрать тип привода, который вам нравится, а затем самостоятельно построить подходящее робо-шасси. Или наоборот, купить готовое шасси для робота и написать программу-контроллер под него.
Чтобы самостоятельно собрать качественное робо-шасси, нужно обладать некоторыми навыками проектирования машин, разбираться в материалах, комплектующих и умело работать руками. Собранное своими руками шасси даёт вам полное знание всех его деталей, узлов, ключевых моментов и слабых мест. Чем сложнее тип привода и шасси, тем больше вероятность, что вам придётся конструировать его самостоятельно.
С другой стороны, многие производители предлагают высококачественные шасси для хобби-робототехники. Приобретя готовое шасси, вы сможете сэкономить много времени на механической составляющей робота и потратить это время на электронную и программную часть.
Это важно! При покупке готового шасси выбирайте наиболее документированное, с маркировкой деталей, информацией о двигателях и полными чертежами основных деталей и компонентов в САПР.
Шасси Turtle
В нашем роботе мы решили использовать робо-платформу Turtle от DFRobot.
Это шасси для небольшого мобильного робота. Рама изготовлена из металла и состоит из двух согнутых листовых металлических пластин. Обе пластины имеют перфорацию и вырезы для установки электроники. Шасси содержит два мотор-редуктора (160 об/мин, 6 В) типа TT с двухсторонним валом L-образной формы, два пластиковых колеса диаметром 65 мм и 15-миллиметровое стальное шариковое колесо. В комплект шасси входит ещё много других деталей и креплений, но они нам не нужны. Понадобится только рама шасси.
Шасси Turtle недорогое, но и не самого лучшего качества:
- Данная робо-платформа слабо документирована. Мы не нашли чертежей шасси в открытом доступе.
- Покрышки колёс, которые идут в комплекте, пластиковые и бесполезные, потому что у них почти нет сцепления с землёй. Мы сразу же заменили их резиновыми шинами для 2WD и 4WD от того же производителя.
- Коробки передач моторов типа TT имеют пластиковый редуктор 1:120. Было бы лучше, если коробка передач была сделана из металла.
- Двигатели постоянного тока работают без обвязки и генерируют значительные электромагнитные наводки. На полной скорости эти двигатели существенно влияют на работу близлежащих аналоговых и цифровых электронных устройств.
Подводя итог, можно сказать, что это шасси — не предел мечтаний, но оно доступно и популярно в хобби-робототехнике. Выбирая недорогое шасси, будьте готовы отдельно приобрести важные детали и комплектующие, или же изготовить их самостоятельно.
Энкодеры
Для колёсной одометрии нам понадобятся датчики угла поворота или энкодеры. Готовые шасси часто уже оснащены энкодерами, но платформа Turtle поставляется без них. По типу отдаваемых данных энкодеры могут быть абсолютными или инкрементальными.
Абсолютный энкодер выдаёт сигнал, который однозначно соответствует углу поворота вала. Энкодеры этого типа не требуют привязки системы отсчёта к какому-либо нулевому положению.
Инкрементальный энкодер генерирует импульсы на выходе. Контроллер подсчитывает количество импульсов с помощью счётчика и определяет текущее положение вала. Сразу после включения контроллера положение вала неизвестно. Для привязки системы отсчёта к нулевой позиции существует специальная нулевая отметка, через которую вал должен пройти после включения. Основным недостатком энкодеров такого типа является невозможность определить пропуск импульса, вызванный какой-либо причиной. Пропуски импульсов накапливают погрешность в угле поворота вала до тех пор, пока не будет пройдена нулевая отметка. Инкрементальный энкодер может быть и без нулевой отметки. В этом случае отсчёт импульсов, полученный в начале накопления, является началом системы отсчёта.
По принципу действия энкодеры могут быть оптическими, магнитными и механическими. Оптический энкодер использует свет, который падает на фотодиод через щели в металлическом или стеклянном диске, установленном на вращающемся валу. Механический энкодер также имеет вращающийся диск, но здесь угол считывают механические переключатели или контакты. Магнитные энкодеры содержат магнит на вращающемся валу. Эти энкодеры используют датчики Холла для считывания вращения вала с магнитом.
В основном в хобби-робототехнике используются оптические и магнитные инкрементальные энкодеры или магнитные абсолютные энкодеры. Не критично, какой тип энкодера вы будете применять. При выборе энкодера основными параметрами являются количество каналов передачи данных, количество импульсов на оборот (PPR) и максимально допустимая скорость вращения вала.
Наиболее популярны квадратурные энкодеры с двумя каналами А и В. Реже у энкодеров в хобби-сегменте есть канал с нулевой отметкой — Z. Чем выше значение PPR, тем меньше угол поворота вала, который может зафиксировать датчик. Чем точнее энкодер, тем точнее одометрия робота, поэтому не пренебрегайте высококачественными энкодерами и не используйте энкодеры с низким значением PPR. Максимальная скорость вращения может быть любой, так как большинство энкодеров способно работать на очень высоких скоростях. Скорее всего, скорость вращения, которую вы будете измерять, будет в несколько раз меньше максимальной. Даже если вы планируете использовать высокоскоростные BLDC двигатели с высоким значением kv, то существуют энкодеры, которые работают с максимальными скоростями 28000 об/мин, 60000 об/мин или даже больше.
Если вы используете двигатель с коробкой передач, установите поворотный энкодер на вал двигателя, а не на вал колеса или коробки передач. Это важно. Эта настройка уменьшает минимальный читаемый угол поворота колеса и делает вашу одометрию более точной. Энкодеры могут устанавливаться и на оси передач и редукторов, но обычно это делается в приводах скелетных роботов.
Мы обзавелись двумя такими двигателями с энкодерами — мотор-редуктор TT с энкодером (160 об/мин, 6 В, 120:1).
Почему мы выбрали именно эти моторы?
- Во-первых, их конструкция специально разработана для нашего шасси Turtle, и нам не придётся придумывать крепление.
- Во-вторых, производитель исправил существенные недостатки, заменил пластиковые шестерни металлическими и добавил схеме двигателя обвязку.
- Эта сборка имеет квадратурный магнитный энкодер с разрешением 16 PPR, установленный на валу двигателя. Передаточное отношение редуктора 120:1 даёт полное разрешение в 1920 импульсов на оборот колеса с минимальным измеряемым шагом в 0°11’15″. Для поставленной нам задачи такой точности более чем достаточно.
Заменив моторы и убрав всё лишнее, мы получили вот такое шасси:
ROS
Мы разобрались с шасси. Давайте начнём разбираться в программном обеспечении. Для настоящих роботов привычные нам программы микроконтроллеров не подходят. Вместо этого мы используем ROS.
ROS (Robot Operation System) — это операционная система для роботов. Она обеспечивает всю необходимую функциональность для распределённой работы всех узлов робота. На самом деле ROS — это библиотека, надстройка поверх компьютерной операционной системы. ROS предоставляет стандартные возможности операционной системы, такие как аппаратная абстракция, низкоуровневое управление устройствами, реализация часто используемых функций, передача сообщений между процессами и управление пакетами.
ROS имеет графовую архитектуру, где обработка данных происходит в узлах — нодах (nodes
), которые могут принимать и передавать сообщения между собой. ROS состоит из двух частей. Первая, это ядро — roscore
, которое отвечает за работу системы и взаимодействие всех пакетов. Вторая часть — это пользовательские пакеты (packages
) или наборы этих пакетов, организованных в стек.
Пакетов очень и очень много. Поскольку ROS — это проект с открытым исходным кодом, сообщество уже написало большинство пакетов для реализации стандартных функций роботов. Именно благодаря обилию готовых open-source пакетов ROS стал так крут и популярен. Тем не менее, некоторые ноды нам придётся написать самостоятельно: например, низкоуровневые драйверы, но большая часть программного обеспечения уже сделана. Нам останется только собрать всё вместе.
Сам ROS и большинство его пакетов очень хорошо документированы. Вы можете найти ответы практически на любые вопросы в документации ROS Wiki, и мы настоятельно рекомендуем с ней ознакомиться перед началом разработки.
ROS содержит множество пакетов для создания виртуального робота и симулирования его поведения — например, стек пакетов gazebo_ros_pkgs
. C помощью Gazebo вы сможете даже симулировать знаменитого робота Atlas от Boston Dynamics у себя на компьютере.
Изначально ROS предназначен для постройки сложных роботов, которые могут стоить тысячи долларов. Поэтому, прежде чем тратиться на какие-либо дорогостоящие физические узлы, ROS предлагает сначала смоделировать и симулировать робота в виртуальной среде, и только потом собирать его вживую.
Мы сделаем всё наоборот: построим робота из недорогих деталей, которые есть под рукой. Симуляцию использовать не будем, но настроим ROS для нашего конкретного робота.
Чтобы эффективно использовать программное обеспечение ROS и для мониторинга, лучше установить его на две разные машины. Первая машина с ROS — это ваш настольный компьютер под управлением ОС Linux. Вторая машина — это бортовой компьютер, установленный на роботе и тоже работающий на Linux. Позже мы свяжем эти две машины, и ROS будет работать в сети.
Предположим, что у вас уже есть настольный компьютер. Теперь вам нужно разобраться с бортовым.
Бортовой компьютер
Выбор железа
Итак, робот работает на ROS, которой нужен Linux. Давайте выберем подходящий бортовой Linux-компьютер для робота.
Крупные мобильные роботы, способные нести большую нагрузку, имеют на борту большие и мощные компьютеры. Если не стоит вопрос размера и формы бортового компьютера, чаще всего в роботах устанавливают мощный ноутбук. Но наш робот маленький, и на нём нет места для кучи оборудования и соответствующего питания. Поэтому мы и возьмём одноплатный компьютер, которых на сегодняшний день существует целое множество. Перечислим самые популярные:
- Raspberry Pi Zero, 3, 4 — самые популярные платы. Изначально Raspberry Pi предназначен для обучения программированию и Linux. Эти платы самые доступные и хорошо задокументированные. Семейство Raspberry Pi имеет множество реплик и копий, дополненных различными периферийными интерфейсами и устройствами для разных задач: Orange Pi, Rock Pi, Banana Pi.
- NVIDIA Jetson Nano Developer Kit — самая многофункциональная плата. Она может применяться для настольного компьютера, но изначально предназначена для разработки мобильного искусственного интеллекта и машинного обучения. Эта плата использует для вычислений мощный графический процессор NVIDIA из множдества ядер.
- Coral Dev Board — лучшая плата машинного обучения с использованием фреймворка Tensorflow. Она специально заточена для работы с нейронной сетью TensorFlow Lite для микроконтроллеров.
- ODYSSEY X86J4105800 — самый большой и мощный одноплатник. Он способнен работать под полноценной версией Windows 10 и обладает всеми функциями настольного ПК.
- Rock Pi N10 — лучшая плата для машинного обучения, которая имеет вычислительную мощность до 3,0 Терафлопс.
Чтобы выбрать одноплатник для своего робота, тщательно сравните особенности каждой платформы и определите, какая подходит вам лучше всего.
Мы не планируем заниматься машинным обучением или выполнять массивные вычисления, а это означает, что нашему роботу подойдёт практически любой компактный одноплатник.
Мы выбрали Raspberry Pi 4 Model B на 4 ГБ. Плата Raspberry Pi 4 может сильно нагреваться, так что мы установили на неё пару алюминиевых радиаторов для пассивного охлаждения.
Подключите любую клавиатуру и мышь к USB-портам платы. Подключите любой дисплей или монитор с помощью кабеля micro-HDMI.
Всё это нужно нам для первичной установки программного обеспечения и отладки программ. Позже нам уже не понадобится ничего лишнего, так как мы будем удалённо работать с Raspberry Pi по сети с помощью SSH.
Производитель одноплатника рекомендует использовать источник питания 3 А и более. Когда мы установим Raspberry Pi на робота, мы обеспечим плату достаточным напряжением и током, но в первый раз мы можем запитать плату от любого источника питания USB через кабель USB Type-C — например от импульсного блока питания 5 В / 3 А.
Это важно! Не выключайте плату Raspberry Pi 4 с установленной в неё картой памяти простым выдёргиванием кабеля! Используйте корректное завершение работы средствами операционной системы.
Выбор и установка дистрибутива Linux
Прежде чем выбрать ОС для установки, вам нужно определиться с версией ROS, где выпускаются дистрибутивы для различных операционных систем и архитектур: Windows, Ubuntu, Debian и других ОС Linux. Глобальные обновления ROS основаны на глобальных обновлениях ОС Ubuntu. Вскоре после выходна новой версии Ubuntu появляется и новая версия ROS. Предыдущие версии ROS продолжают поддерживаться до тех пор, пока поддерживается дистрибутив Ubuntu, для которого они были созданы. На момент написания этого руководства нам доступны два поддерживаемых дистрибутива ROS:
- ROS Melodic Morenia, нацеленный на Ubuntu 18.04 (Bionic). Поддерживает Ubuntu 17.10 (Artful). Дата релиза — 23 мая 2018. Дата конца поддержки — май 2023 (Bionic).
- ROS Noetic Ninjemys, нацеленный на Ubuntu 20.04 (Focal). Дата релиза — 23 мая 2020. Дата конца поддержки — май 2025 (Focal).
Официальная документация рекомендует установку последней версии ROS Noetic для последней версии Ubuntu Focal. Поскольку ROS живёт благодаря сообществу, то обновление и поддержка различных пакетов может запаздывать, даже если ядро обновляется всегда вовремя. Обратите внимание, что некоторые интересные пакеты всё ещё не обновлены для новой версии Noetic, и вам, возможно, придётся собирать их вручную.
Перейдём в раздел установки ROS Noetic и посмотрим, какие операционки и процессорные архитектуры мы можем использовать:
- Ubuntu Focal на
amd64
,armhf
,arm64
. - Debian Buster на
amd64
,arm64
. - Windows 10 на
amd64
. - Не гарантировано: любой дистрибутив Linux на
amd64
,i686
,arm
,armv6h
,armv7h
,aarch64
.
Raspberry Pi 4 имеет набор инструкций ARMv8-A и поддерживает 32-разрядные и 64-разрядные вычисления. Обычно пользователи устанавливают на Raspberry 32-разрядную ОС. Однако у нашей малины 4 ГБ оперативной памяти, а бывает и версия с 8 ГБ. С таким объёмом оперативной памяти вы можете попробовать 64-разрядную ОС, которая будет справляться с определёнными задачами быстрее.
Для ROS Noetic мы устанавливаем на одноплатник Ubuntu Server 20.04.2 (arm64). Для хранения ОС вам понадобится флеш-карта microSD. Чем больше ёмкость карты и её класс скорости — тем лучше. Мы использовали карточку microSD на 16 ГБ.
Скачайте образ ОС. Зайдите на официальный сайт Ubuntu и перейдите в раздел Downloads
→ Ubuntu for IOT
→ Raspberry Pi 2, 3 or 4
. Загрузите образ 64-разрядной ОС Ubuntu Server для архитектуры arm
.
Установите программу для создания загрузочных флеш-накопителей. Мы используем официальную программу Raspberry Pi Imager. Перейдите на официальный сайт Raspberry Pi Foundation. Затем перейдите в раздел Downloads
→ Raspberry Pi Imager
, загрузите и установите версию для вашей ОС.
Вставьте SD-карту в компьютер и отформатируйте её.
Запустите Raspberry Pi Imager. Затем войдите в меню Operating system
и выберите Use custom
.
Укажите путь к скачанному образу Ubuntu, затем путь к вашей флеш-карте и нажмите кнопку Write
.
Когда запись ОС завершится, вставьте microSD-карту в Raspberry Pi и включите плату, подав на неё питание.
Первый запуск
Убедимся, что Linux работает. При первой загрузке Ubuntu попросит вас изменить стандартный логин ubuntu
и пароль ubuntu
. Установите свои данные для администраторского входа в систему. Обратите внимание: пароль не может быть палиндромом; это одна из стандартных настроек ОС Ubuntu Server.
Ubuntu 20.04.2 LTS ubnutu tty1
ubuntu login: ubuntu
Password:
You are requested to change your password immediately (root enforced)
Changing password for ubuntu.
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-1015-raspi aarch64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed Apr 1 17:25:35 UTC 2020
System load: 0.27 Swap usage: 0% Users logged in: 0
Usage of /: 12.8% of 14.03GB Temperature: 46.7 C
Memory usage: 6% Processes: 134
0 packages can be updated.
0 updates are security updates.
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
ubuntu@ubuntu:~$
Пароль для root
не установлен в Ubuntu по умолчанию, и вход в систему пользователя root
отключён. Включим учетную запись root
и установим для неё пароль. Затем переключимся на root
:
sudo passwd root
su root
При желании вы можете переименовать стандартное имя текущего хоста в более привлекательное. Мы назовём бортовой компьютер robot
.
Отредактируем файл /etc/hostname
и заменим ubuntu
на robot
:
nano /etc/hostname
Затем перезагрузимся и снова войдём в систему под root
:
reboot now
Настройка Wi-Fi и графической оболочки
Теперь нам нужно подключиться к Интернету через Wi-Fi-адаптер на Raspberry. Предполагается, что у вас уже есть точка доступа Wi-Fi.
Первый шаг — определить имя вашего беспроводного сетевого интерфейса. Оно может быть разное, но обычно это wlan0
:
ls /sys/class/net/
Затем перейдём в каталог /etc/netplan
и найдём соответствующие файлы конфигурации Netplan. Файл конфигурации имеет имя типа 50-cloud-init.yaml
:
ls /etc/netplan/
Отредактируем файл конфигурации Netplan:
nano /etc/netplan/50-cloud-init.yaml
Весь файл конфигурации должен выглядеть примерно так, как показано ниже. Убедитесь, что все блоки кода выровнены. Для выравнивания используйте пробелы вместо табуляции. Замените строки SSID
и PASSWORD
на имя и пароль вашей сети Wi-Fi.
# This file is generated from information provided by the datasource. Changes
# to it will not persist across an instance reboot. To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
ethernets:
eth0:
dhcp4: true
optional: true
version: 2
wifis:
wlan0:
optional: true
access-points:
"SSID":
password: "PASSWORD"
dhcp4: true
Запустим службу, перезагрузимся и войдём в систему:
systemctl start wpa_supplicant
reboot now
Применим изменения Netplan и подключимся к беспроводной сети:
sudo netplan generate
sudo netplan apply
Ещё раз перезагрузимся и снова войдём в систему. Теперь наша Raspberry в сети Wi-Fi, и мы можем пропинговать наш сетевой шлюз:
ip addr show
ping 192.168.88.1
Следующим шагом будет установка графического интерфейса для удобства работы с операционной системой. Обновим список пакетов из репозитория, обновим сами пакеты и установим любую понравившуюся графическую оболочку. Например, мы выбрали XFce:
sudo apt-get update && apt-get upgrade
sudo apt-get install xubuntu-desktop
Установка графической оболочки может занять некоторое время. После установки перезагрузимся и войдём в систему под ubuntu
.
Установка и настройка ROS
Мы установим ROS на две машины: на Raspberry Pi и настольный компьютер, а затем обьединим системы ROS в единую сеть.
Зачем нужно два компьютера с ROS? Разработка программного обеспечения для робота тесно связана с ресурсоёмкой визуализацией. Работа с графикой, трёхмерными обьектами и визуализация на Raspberry существенно тормозит разработку ПО.
Кроме этого, некоторые поставленные нами задачи, например навигация в помещении, очень сложные и требуют большой вычислительной мощности. Выполнение подобных алгоритмов на RPi может быть очень медленным, а то и вовсе не реальным.
Если бы вместо Raspberry Pi на нашем роботе стоял мощный ноутбук, мы бы смогли вести весь проект от начала и до конца на одной ROS-машине. В нашем случае ROS на роботе будет оперировать простыми вычислениями и поддерживать работу драйверов низкого уровня, а настольный компьютер с ROS будет заниматься трудоёмкими вычислениями навигации и визуализацией.
Установка ROS на Raspberry Pi
Установим ROS Noetic на Raspberry, следуя рекомендациям из руководства по инсталляции. Откроем новый терминал и продолжим под юзером ubuntu
.
Настроим Raspberry на приём программного обеспечения из репозитория packages.ros.org:
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
Настраиваем ключи:
sudo apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
Обновим список пакетов:
sudo apt-get update
Установим полную версию ROS, используя стек пакетов ros-noetic-desktop-full
:
sudo apt install ros-noetic-desktop-full
Настраиваем переменные окружения ROS и автоматически добавляем их в bash-сеанс при каждом новом запуске Ubuntu:
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
Установим пакет rosdep
, который позволяет вам легко устанавливать системные зависимости для компилируемого исходного кода и необходим для запуска некоторых основных компонентов в ROS:
sudo apt-get install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential
Инициализируем rosdep
:
sudo rosdep init
rosdep update
Убедимся, что ROS на Raspberry работает. Откроем новый терминал и запустим ядро ROS:
roscore
Установка ROS на настольный компьютер
Установка ROS на настольный компьютер ничем не отличается от установки ROS на Raspberry. Вам по-прежнему нужно отталкиваться от версии Linux, которая установлена на вашем настольном компьютере.
В нашем случае компьютер работает под Ubuntu 20.04.1 LTS (Focal), поэтому на него мы так же устанавливаем ROS Noetic.
Создание рабочего пространства ROS
Прежде чем создавать рабочее пространство, мы настоятельно рекомендуем вам прочитать отличные уроки ROS для начинающих. Поначалу официальная документация может показаться сложной и непонятной. Но со временем, когда вы привыкнете к ROS, она ответит вам на все возникающие вопросы. Для начала попытайтесь понять некоторые основные принципы ROS:
- Что такое переменные среды ROS?
- Как выглядит файловая система и какова структура пакета ROS?
- Что такое ноды или узлы (
nodes
), топики или темы (topics
), сервисы (sevices
), сообщения (messages
), издатели (publishers
), подписчики (subscribers
)? - Каким образом вышеописанные обьекты взаимодействуют друг с другом?
Создадим новое рабочее пространство ROS. Это место, где вы храните всё программное обеспечение робота или проекта: пакеты с нодами, исполняемые файлы, файлы конфигурации, файлы описаний и прочее. На самом деле рабочее пространство — просто папка в файловой системе Linux.
Мы назвали наше рабочее пространство ros
. Пакеты с исходным кодом должны находиться в подкаталоге src
.
mkdir -p ~/ros/src
cd ~/ros/
Рабочее пространство в настоящее время пусто, давайте соберём его с помощью catkin. Это удобный инструмент для компиляции исходного кода C++ или Python, создания исполняемых файлов, связывания пакетов, и этот инструмент регулярно используется при работе с ROS.
catkin_make
Взгляните в каталог рабочего пространства. Теперь там появились папки build
и devel
. В папке build
хранятся исполняемые файлы, бинарные файлы и файлы сборки. Папка devel
содержит множество сгенерированных файлов setup.*sh, которые используются для наложения рабочей области ROS поверх среды Linux.
Процесс разработки
Поскольку мы используем ROS на двух компьютерах, то и рабочее пространство нужно создать на обоих машинах и держать там одинаковые и актуальные файлы вашего проекта. Да, звучит не очень удобно, но такова цена за использование двух машин вместо одной.
Добиться актуализации данных и синхронизировать рабочие пространства на настольном компьютере и Raspberry можно с помощью git
или различных утилит типа rsync
.
Далее, если мы производим какие-нибудь изменения в рабочем пространсве ros
, то подразумевается, что эти изменения выполнены как на Raspberry, так и на настольном компьютере. Наш проект будет храниться в клонированном виде в двух местах, но на разных машинах мы будем запускать разные части проекта.
Настойка локальной сети и сети ROS
Настроим общение по сети между настольным компьютером и Raspberry, а также обьединим ROS на обоих в компьютерах в сеть. В документации ROS есть подробная статья о настройке системы на нескольких машинах.
Узнаем IP-адреса Raspberry Pi и десктопного компьютера в локальной сети. В нашем случае компьютер получил сетевой адрес 192.168.88.24
, а RPi — 192.168.88.82
. IP-адрес можно узнать утилитами ip addr
или ifconfig
.
Настольный компьютер имеет имя robot-user
, а Raspberry — имя robot
. Имена можно посмотреть в файле /etc/hostname
.
Отредактируем /etc/hosts
на обеих машинах:
sudo nano /etc/hosts
Добавим в этот файл несколько строк:
192.168.88.24 robot-user
192.168.88.82 robot
В сети ROS может быть запущено только одно ядро roscore
. Именно машина с запущенным ядром отвечает за работу всей системы. В сети ROS она называется master, а остальные машины становятся slave. Мы в качестве master выбираем настольный компьютер robot-user
. Для всех ROS-компьютеров в сети нужно указать, какая именно машина является master.
Добавим новые сетевые переменные окружения ROS в автозапуск. Сперва на настольном компьютере:
echo "ROS_MASTER_URI=http://robot-user:11311" >> ~/.bashrc
echo "ROS_HOSTNAME=robot-user" >> ~/.bashrc
echo "ROS_IP=192.168.88.24" >> ~/.bashrc
А затем то же самое на Raspberry:
echo "ROS_MASTER_URI=http://robot-user:11311" >> ~/.bashrc
echo "ROS_HOSTNAME=robot" >> ~/.bashrc
echo "ROS_IP=192.168.88.82" >> ~/.bashrc
Перезагрузим обе машины и протестируем сеть ROS.
Запустим ядро на настольном компьютере:
roscore
Как мы видим из лога, ядро ROS запустилось на машине, которая имеет сетевое имя robot-user
. А значит, мы уже можем увидеть ядро в сети. Проверим на Raspberry, появились ли какие-либо топики ROS:
rostopic list
У нас появились два системных топика ROS — значит, система на Raspberry увидела наш master
.
Описание робота
Пришло время дать нашему роботу имя. Мы решили назвать его abot
— акроним от Amperka Bot.
Имя робота очень важно, так как оно является своего рода пространством имён (namespace
) при работе с программным обеспечением.
Человек, работающий с роботом, может физически взаимодействовать с ним. В любой момент вы можете потрогать робота, взглянуть на его составные части, взвесить или снять размеры с его деталей. То есть, у вас есть к нему доступ.
Аналогично, программное обеспечение должно иметь полный доступ к роботу в любой момент времени. Для этого программе необходимо предоставить полное описание реального робота (Robot Descriprton).
Формат URDF
Для описания роботов существуют различные форматы. ROS использует формат URDF (Unified Robot Description Format), который является специализацией XML.
С помощью URDF можно описать каждую часть реального робота. Чем полнее описание робота, тем больше функций программного обеспечения можно будет использовать, например, для симуляции физического поведения. В описании все детали робота — сегменты (links), сочленения (joints) и датчики (sensors) — организованы в виде дерева. Описание URDF различается в зависимости от реализации, но есть несколько основных элементов, которые встречаются регулярно:
<link>
описывает кинематические и динамические свойства жёсткой инерционной детали или узла робота (абсолютно твёрдого тела).<visual>
описывает визуальные свойства<link>
. То есть, как выглядит деталь робота. Узел робота может быть условно описан геометрически как куб, цилиндр или сфера, или же задан полигональной 3D-моделью.<collision>
описывает упрощённую геометрию<link>
. Данный элемент используется для задания хитбоксов и областей, которые нужны для физических расчетов во время симуляции.<inertial>
описывает инерционные свойства<link>
. Элемент задаёт массу детали или узла робота, центр масс и тензор инерции в виде матрицы 3×3.<joint>
описывает шарнир, сочленение или соединение двух жёстких частей (двух элементов<link>
). В этом элементе определяется тип соедининия двух деталей, их кинематика и динамика, пределы безопасного движения, физическое демпфирование и параметры трения.<transmission>
описывает взаимосвязь между<joint>
и конкретным приводом. Например, один мотор робота может управлять несколькими сочленениями.
Описание робота с множеством частей и соединений может занимать тысячи строк и быть неудобным для чтения. Решением является язык макросов xacro. С помощью макросов xacro
вы можете создавать более короткие и удобочитаемые XML-файлы.
Для описания нашего abot’a мы используем urdf
и xacro
. Просмотреть примеры описаний роботов можно в уроках URDF.
Это важно! URDF-описание — это неотъемлемая часть роботов, построенных на ROS. Так сделаны сотни роботов, и будьте уверены, каждый из них имеет своё описание.
Вы можете составить описание вашего робота в формате URDF вручную. Это лучше всего подходит для начинающих. Просто создайте новый пустой URDF-файл и описывайте вашего робота элемент за элементом. Однако этот трудоёмкий процесс требует внимания, ведь можно наделать много ошибок.
Процесс создания файла URDF можно автоматизировать с помощью специального программного обеспечения, которое экспортирует 3D-модель робота из CAD-систем в URDF. А для этого вам понадобится 3D-модель робота в САПР (Системе автоматизированного проектирования).
3D-модель робота в САПР
В теории 3D-модель робота можно создать в любом 3D-редакторе, но лучше использовать твердотельное моделирование.
Мы выбрали редактор SolidWorks 2017 для платформы Windows, потому что в нём есть отличный плагин для экспорта проекта в формат URDF.
Старайтесь не описывать сразу всего робота. Добавляйте различные части к модели постепенно. Это поможет вам освоить процесс описания. Мы начали с детализации шасси.
Всё наше шасси пришлось детализировать вручную. Вот почему так важны чертежи от производителя. Глядите, что у нас получилось:
На данный момент наша модель состоит из четырёх сегментов:
abot_base
abot_left_wheel
abot_right_wheel
abot_caster_wheel
И трёх сочленений (joints):
left_wheel_to_base
right_wheel_to_base
caster_wheel_to_base
Для правильного экспорта нужно выполнить несколько действий.
Добавьте системы координат и определите их для всех сегментов. Лучше всего указывать системы координат от исходных точек деталей модели. За начало системы координат лучше взять центр симметрии детали, точку на оси симметрии или центр масс.
ROS и URDF требуют правосторонних систем координат (Правило правой руки). Определите, где находится передняя, задняя и верхняя части вашего робота. Ось X должна указывать вперёд, ось Y — влево, а ось Z — вверх. По умолчанию стандартные виды SolidWorks и система координат повёрнуты на 90 градусов вокруг осей X и Z. Для удобства правильного размещения осей в SolidWorks можно разместить направляющие линии.
Каждый сегмент модели имеет свою систему координат:
CS_BASE
CS_RIGHT_WHEEL
CS_LEFT_WHEEL
CS_CASTER_WHEEL
Система координат CS_BASE
условно расположена в центре нашего робота. CS_LEFT_WHEEL
и CS_RIGHT_WHEEL
находятся в центрах колёс. CS_CASTER_WHEEL
находится в центре сферы всенаправленного опорного колеса.
Добавьте оси вращения для подвижных соединений и сочленений (joints). У нас есть три подвижных соединения: два вращающихся на осях колеса и одно всенаправленное колесо, которое вращается во все стороны и поэтому не нуждается в оси. Для колёс мы поместили оси AXIS_LEFT_WHEEL
и AXIS_RIGHT_WHEEL
.
Если вы планируете использовать описание вашего робота в симуляциях, вам также необходимо задать реальные материалы для всех деталей модели. Вы можете подобрать примерные аналоги из стандартной библиотеки материалов SolidWorks. Экспортёр использует материалы деталей для моделирования и расчёта инерционных параметров сегментов — массы, координат центра масс, а также тензора инерции.
Экспорт в URDF
Чтобы экспортировать модель, мы установили специальный плагин solidworks_urdf_exporter
. Последовательность установки хорошо описана в документации на плагин.
После установки включите этот плагин в SolidWorks. Для этого перейдите в меню «Tools → Add-Ins» и установите флажок рядом с плагином SW2URDF
. Чтобы начать экспорт, перейдите в раздел «Tools – Export as URDF» или в «Files – Export as URDF». Откроется меню экспорта, в котором необходимо указать все сегменты, используемые в модели.
Сначала мы указываем сегмент abot_base
. Это базовый и родительский сегмент. В качестве базового сегмента для робота лучше выбрать что-то массивное — например, каркас робота.
Затем мы определяем три дочерних сегмента для abot_base
и систему координат CS_BASE
. Для задания геометрии этого сегмента мы выбираем все части 3D-модели, кроме боковых колёс и всенаправленного колеса. При экспорте выбранные детали преобразуются в mesh-модели формата STL, необходимые для визуализации.
Следующий шаг — описать сегменты боковых колёс, которые являются сегментами-потомками abot_base
. Мы установили имена соединений abot_left_wheel
и abot_right_wheel
. В качестве систем координат мы выбираем соответствующие CS_LEFT_WHEEL
и CS_RIGHT_WHEEL
. Оси вращения колес — AXIS_LEFT_WHEEL
и AXIS_RIGHT_WHEEL
. В качестве геометрии мы выбираем детали колёс нашей 3D-модели. Для обычного колеса тип соединения (joint) является continuous
. Также задаём имена для сочленений колёс — left_wheel_to_base
и right_wheel_to_base
.
Добавляем последний сегмент abot_caster_wheel
и сочленение caster_wheel_to_base
для всенаправленного колеса. Здесь тип соединения должен быть continuous
, а система координат — CS_CASTER_WHEEL
.
Это важно! Для двухколёсного дифференциального привода последнее всенаправленное колесо никак не влияет на алгоритм задания скоростей движения робота. Мы не можем управлять этим колесом, задавать скорость вращения и контролировать его. То есть, в теории нет необходимости описывать его в модели и можно «слить» сегмент всенаправленного колеса с базовым сегментом всего робота. Но это не так: желательно описывать все подвижные части вашего робота. Если что-то в роботе двигается — значит, для этого нужно сочленение (joint), даже если вы не планируете его использовать. Особенно это важно, если вы планируете использовать симуляции. Например, мы можем не описывать всенаправленное колесо, тогда оно останется в описании робота твёрдой опорой и частью другого сегмента. В этом случае при симуляции движения робота программа не будет знать, что это колесо, и расценит его как твёрдый объект, который роботу приходится «тащить» по полу, преодолевая лишние силы трения, препятствующие движению. Таким образом, движение робота в симуляции и в реальной жизни будет кардинально отличаться.
Когда вы опишите все сегменты и сочленения, нажмите кнопку «Preview and Export…».
Проверьте параметры сочленений и нажмите кнопку «Next».
Проверьте параметры сегментов.
Обратите внимание, если вы укажете материал деталей, программа-экспортёр сама вычислит массу сегментов, их центры масс и тензор инерции. Это крайне важные параметры для симуляции робота!
Завершите экспорт, нажав на кнопку «Export URDF and Meshes…». Укажите имя папки для экспорта. Мы назвали её abot
. Экспортёр создаст в этой папке готовый пакет ROS со многими файлами, которые вам в действительности не нужны. Вам понадобятся файлы 3D-моделей формата *.STL из папки meshes
и файл описания abot.urdf
из папки urdf
.
Отложим эти файлы на некоторое время в сторону.
Пакет robot_description
Вернёмся на Linux и перейдём в наше рабочее пространство ros
настольном компьютере.
Cоздадим первый пакет проекта, который содержит описание робота. Традиционно этот пакет называют robot_description
. Чтобы избежать путаницы с именем, мы назовем его abot_description
. В директории ros/src
в терминале вводим:
catkin_create_pkg abot_description tf rviz urdf xacro
С помощью этой команды вы можете создать пустой пакет ROS, а именно файлы CMakelists.txt
и package.xml
. В команде после имени пакета указываются пакеты зависимости. Для пакета abot_description
мы устанавливаем пакеты зависимостей tf
, urdf
, xacro
и rviz
.
Для каждого пакета ROS не забывайте указывать необходимые пакеты зависимости, а также редактировать файлы CMakelists.txt
и package.xml
при добавлении новых функций. Подробнее о процессе создания пакета читайте в учебной статье.
Внутри пакета мы создаём четыре папки с именами urdf
, meshes
, rviz
, and launch
.
mkdir abot_description/urdf abot_description/meshes abot_description/rviz abot_description/launch
В папку abot_description/meshes
нужно поместить 3D-файлы STL, сгенерированные ранее при экспорте нашей модели в URDF. В папку abot_description/urdf
поместите сгенерированный файл abot.urdf
.
Это важно! Внутри пакета ROS в папке c именем launch
традиционно хранятся файлы запуска нод. Эти файлы имеют расширение .launch. С помощью launch
-файлов можно запустить несколько нод ROS, используя всего один терминал. Кроме этого, через launch
-файл можно задать различные параметры для нод или глобальные параметры для параметрического сервера ROS. Читайте подробнее в документации на roslaunch
.
Приводим в порядок URDF-файл описания робота
Экспортёр генерирует описание URDF в одном большом файле. Это не всегда удобно. Откройте файл abot.urdf
и посмотрите, как выглядит описание робота.
В будущем мы будем постоянно совершенствовать робота и расширять его описание, поэтому для удобства давайте разделим файл abot.urdf
на несколько частей с помощью макросов xacro
и исправим некоторые сгенерированные ошибки.
Мы решили разделить описание на несколько частей:
abot.xacro
— основная информация о роботе и его базовых сегментах.abot_left_wheel.xacro
— описание сегмента левого колеса и его сочленения с базой робота.abot_right_wheel.xacro
— описание сегмента правого колеса и его сочленения с базой робота.abot_caster_wheel.xacro
— описание сегмента всенаправленного колеса и его сочленения с базой робота.abot_materials.xacro
— описание цветов для визуализации.
Давайте разбираться. Начнем с простого: сделаем цвета для визуализации. В папке urdf
cоздадите файл abot_materials.xacro
и заполните его несколькими элементами, которые описывают цвета:
<?xml version="1.0"?>
<robot
xmlns:xacro="http://www.ros.org/wiki/xacro">
<material name="Green">
<color rgba="0.0 1.0 0.0 1.0"/>
</material>
<material name="Blue">
<color rgba="0.0 0.0 1.0 1.0"/>
</material>
<material name="Red">
<color rgba="1.0 0.0 0.0 1.0"/>
</material>
<material name="White">
<color rgba="1.0 1.0 1.0 1.0"/>
</material>
<material name="Yellow">
<color rgba="1.0 1.0 0.0 1.0"/>
</material>
</robot>
Теперь создадим файл abot.xacro
и заполним его информацией о сегменте abot_base
.
Изменим путь до трёхмерных файлов с package://abot/meshes/abot_base.STL
на package://abot_description/meshes/abot_base.STL
.
Включите в abot.xacro
файл с нашими цветами abot_materials.xacro
и замените все экспортированные теги material
на новые. Пусть сегмент abot_base
визуализируется белым цветом.
Вот каким получилось содержание файла abot.xacro
.
<?xml version="1.0" encoding="utf-8"?>
<robot name="abot"
xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- Matherials -->
<xacro:include filename="$(find abot_description)/urdf/abot_matherials.xacro" />
<!-- abot_base -->
<link name="abot_base">
<inertial>
<origin xyz="-0.024498 1.0952E-13 0.022295" rpy="0 0 0"/>
<mass value="0.27459"/>
<inertia ixx="0.00032396" ixy="-1.1142E-12" ixz="-9.1302E-06" iyy="0.00030091" iyz="-3.3253E-10" izz="0.00056103"/>
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_base.STL" />
</geometry>
<material name="White" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_base.STL" />
</geometry>
</collision>
</link>
</robot>
Ещё одна фундаментальная деталь. Согласно конвенции, описание URDF для ROS должно иметь сегмент с именем base_link
. Именно этот сегмент служит отправной точкой для дерева описания робота. Вы можете добавить этот сегмент в дерево описания с любой простой геометрией, например, со сферой радиусом 1 мм.
Мы добавляем в описание base_link
и «прикрепляем» его к сегменту abot_base
с помощью соединения fixed
. Добавляем следующие строки в файл abot.xacro
:
<!-- base_link -->
<link name="base_link">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<sphere radius="0.001" />
</geometry>
</visual>
</link>
<joint name="base_link_to_abot_base" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0" />
<parent link="base_link" />
<child link="abot_base" />
</joint>
Заполним файлы для правого (abot_right_wheel.xacro
), левого (abot_left_wheel.xacro
) и всенаправлленого колеса (abot_caster_wheel.xacro
) подобным образом. Отредактируем все экспортированные данные и разделим их по файлам. Пусть все колёса будут зелёными.
Файл описания левого колеса:
<?xml version="1.0" encoding="utf-8"?>
<robot name="abot"
xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- left_wheel -->
<link name="abot_left_wheel">
<inertial>
<origin xyz="1.9255E-10 0.00056576 -1.0414E-10" rpy="0 0 0"/>
<mass value="0.050464"/>
<inertia ixx="2.0701E-05" ixy="-3.8089E-14" ixz="1.3584E-15" iyy="3.5827E-05" iyz="2.1838E-15" izz="2.0701E-05"/>
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_left_wheel.STL" />
</geometry>
<material name="Green" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_left_wheel.STL" />
</geometry>
</collision>
</link>
<joint name="left_wheel_to_base" type="continuous">
<origin xyz="0 0.068 0.0145" rpy="0 0 0" />
<parent link="abot_base" />
<child link="abot_left_wheel" />
<axis xyz="0 1 0" />
</joint>
</robot>
Файл описания правого колеса:
<?xml version="1.0" encoding="utf-8"?>
<robot name="abot"
xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- right_wheel -->
<link name="abot_right_wheel">
<inertial>
<origin xyz="1.9255E-10 -0.00056576 1.0414E-10" rpy="0 0 0"/>
<mass value="0.050464"/>
<inertia ixx="2.0701E-05" ixy="3.8089E-14" ixz="-1.3584E-15" iyy="3.5827E-05" iyz="2.1838E-15" izz="2.0701E-05"/>
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_right_wheel.STL" />
</geometry>
<material name="Green" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_right_wheel.STL" />
</geometry>
</collision>
</link>
<joint name="right_wheel_to_base" type="continuous">
<origin xyz="0 -0.068 0.0145" rpy="0 0 0" />
<parent link="abot_base" />
<child link="abot_right_wheel" />
<axis xyz="0 1 0" />
</joint>
</robot>
Файл описания всенаправленного колеса:
<?xml version="1.0" encoding="utf-8"?>
<robot name="abot"
xmlns:xacro="http://www.ros.org/wiki/xacro">
<!-- caster_wheel -->
<link name="abot_caster_wheel">
<inertial>
<origin xyz="0 1.6073E-19 0" rpy="0 0 0"/>
<mass value="0.011207"/>
<inertia ixx="2.1965E-07" ixy="-1.5533E-55" ixz="-1.9776E-56" iyy="2.1965E-07" iyz="-2.2674E-40" izz="2.1965E-07"/>
</inertial>
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_caster_wheel.STL" />
</geometry>
<material name="Green" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<mesh filename="package://abot_description/meshes/abot_caster_wheel.STL" />
</geometry>
</collision>
</link>
<joint name="caster_wheel_to_base" type="continuous">
<origin xyz="-0.078 0 -0.011" rpy="0 0 0" />
<parent link="abot_base" />
<child link="abot_caster_wheel" />
<axis xyz="0 1 0" />
</joint>
</robot>
Включим все новые файлы описания колёс в конец главного файла abot.xacro
.
<!-- Wheels -->
<xacro:include filename="$(find abot_description)/urdf/abot_left_wheel.xacro" />
<xacro:include filename="$(find abot_description)/urdf/abot_right_wheel.xacro" />
<xacro:include filename="$(find abot_description)/urdf/abot_caster_wheel.xacro" />
Визуализация URDF-модели
Давайте визуализируем нашего робота в ROS на настольном компьютере и посмотрим, что получится.
Для визуализации используем мощный инструмент rviz
. Вы можете прочитать больше о rviz
в документации на Wiki.
Если вы установили полную версию ROS (ros-desktop-full
), то у вас уже есть все необходимые пакеты для визуализации. Однако нужно ещё установить дополнительный пакет joint-state-publisher-gui
для ручного управления сочленениями. Для нашей ROS Noetic устанавливаем:
sudo apt-get install ros-noetic-joint-state-publisher-gui
Создадим новый файл запуска нод ROS. Подобные файлы имеют разрешение *.launch.
В нашем пакете abot_description
в папке launch
создаём файл display_model.launch
и заполняем его следующими строками:
<launch>
<!-- Args -->
<arg name="gui" default="true" />
<arg name="model" default="$(find abot_description)/urdf/abot.xacro" />
<!-- Params -->
<param name="use_gui" value="$(arg gui)" />
<!-- Robot Description from URDF -->
<param name="robot_description" command="$(find xacro)/xacro --inorder $(arg model)" />
<node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" />
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />
<!-- Rviz -->
<node name="rviz" pkg="rviz" type="rviz" required="false"/>
</launch>
Что делает этот файл? При запуске он загрузит необходимые для визуализации ноды ROS, а также загрузит URDF-описание нашего робота на сервер параметров ROS.
Перейдём в рабочее пространство ros
и соберём его с нашим новым пакетом abot_description
.
cd ~/ros
catkin_make
Загрузим переменные окружения нашего рабочего пространства и запустим display_model.launch
.
source devel/setup.bash
roslaunch abot_description display_model.launch
Если всё сделано верно, появится окно rviz
и окно joint_state_publisher_gui
.
В окне rviz
в меню «Displays → Global Options» устанавливаем значение параметра Fixed Frame
в base_link
.
Нажимаем кнопку «Add» в левой нижней части экрана и добавляем два элемента визуализации: RobotModel
и TF
.
На экране появится модель робота, созданная по нашему описанию URDF, и дерево транформаций.
Выглядит не очень понятно. Это потому что сейчас rviz
имеет стандартные настройки. Вы можете настроить отображение всех данных под себя и сохранить файл настроек с расширением *.rviz.
Например, мы сохраняем настройки rviz
для отображения модели в виде файла abot_model.rviz
в папке abot_description/rviz
и добавляем соответствующий аргумент в файл запуска display_model.launch
.
Теперь наш файл запуска display_model.launch
выглядит таким образом:
<launch>
<!-- Args -->
<arg name="gui" default="true" />
<arg name="rvizconfig" default="$(find abot_description)/rviz/abot_model.rviz" />
<arg name="model" default="$(find abot_description)/urdf/abot.xacro" />
<!-- Params -->
<param name="use_gui" value="$(arg gui)" />
<!-- Robot Description from URDF -->
<param name="robot_description" command="$(find xacro)/xacro --inorder $(arg model)" />
<node name="joint_state_publisher_gui" pkg="joint_state_publisher_gui" type="joint_state_publisher_gui" />
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" />
<!-- Rviz -->
<node name="rviz" pkg="rviz" type="rviz" args="-d $(arg rvizconfig)" required="false"/>
</launch>
А отображение модели стало намного более приятным и понятным:
Можно отключить видимость 3D-моделей и проверить всё дерево элементов вашего описания, а также направления осей систем координат.
Не забудьте, что у нас открыто и второе окно joint_state_publisher_gui
.
Слайдерами в этом окне можно вручную поуправлять всеми сочленениями модели вашего робота. В нашем случае перемещениями слайдера мы вращаем колёса.
Footprint робота
Сейчас при визуализации наш робот находится в точке (0, 0, 0) на плоскости grid
, которая выступает в роли пола, а фиксированный кадр Fixed Frame
, в котором отображается наш робот, является глобальным (Global Frame
). На самом деле робот не должен находиться в этой точке, ведь он стоит на своих колёсах, а не замурован где-то в полу.
Нам необходимо «поднять» робота над полом, а также указать его проекцию на пол — footprint
. В соответствии с конвенцией ROS для этого используется сегмент base_footprint
. Нам нужно ввести в описание робота этот новый сегмент и связать его с базовым сегментом base_link
. Также нужно указать геометрические размеры проекции робота на пол и клиренс.
Добавим новые данные в основной файл описания робота abot.xacro
. Добавим в начало файла параметр clearance
— расстояние в метрах от пола до исходных точек сегментов base_link
и abot_base
. Поместим следующую строку в начало файла abot.xacro
:
<xacro:property name="clearance" value="0.018" />
В качестве проекции робота на пол можно указать простую геометрическую фигуру. Например, наш робот условно круглый, и в качестве проекции мы можем указать цилиндр высотой в 1 мм и радиусом 100 мм, а его цвет на визуализации сделать синим.
Добавляем в abot.xacro
новый сегмент и связываем его с имеющимися.
<!-- base_footprint -->
<link name="base_footprint">
<visual>
<origin xyz="0 0 0" rpy="0 0 0" />
<geometry>
<cylinder length="0.001" radius="0.010" />
</geometry>
<material name="Blue" />
</visual>
</link>
<joint name="base_footprint_to_base_link" type="fixed">
<origin xyz="0 0 ${clearance}" rpy="0 0 0" />
<parent link="base_footprint" />
<child link="base_link" />
</joint>
Давайте снова запустим визуализацию rviz
, но в качестве фиксированного кадра визуализации (Fixed Frame
) укажем base_footprint
.
Теперь наш робот стоит колёсами на земле:
Сейчас дерево сегментов и сочлненений нашего робота выглядит так:
Raspberry Pi HAT и крепление электроники
Следующим шагом будет подключение двигателей и энкодеров к роботу. Одноплатник Raspberry Pi в этом плане — отличный выбор, так как даёт прямой доступ к контактам GPIO.
Однако не всегда удобно подключать электронику к штыревой вилке GPIO напрямую. Лучше использовать специальные платы расширения и адаптеры.
Для нашего робота мы взяли универсальный хаб для Raspberry Pi — Troyka HAT.
Этот адаптер легко вставляется в гребёнку пинов на малинке. Установим Troyka HAT на Raspberry Pi:
Troyka HAT также имеет дополнительный контроллер — расширитель GPIO-портов, который обеспечивает восемь дополнительных портов ввода-вывода с аппаратной поддержкой 12-битного АЦП и 16-битной ШИМ. Позже мы обязательно воспользуемся этой особенностью.
Так выглядит распиновка платы Troyka HAT:
Пришло время прикрепить наш бортовой компьютер к шасси робота. Можно не заморачиваться и прикрепить Raspberry хоть на двухсторонний скотч. Однако, если вы хотите сделать качественного робота, всё следует делать по уму.
Для крепления электроники мы спроектировали и напечатали на 3D-принтере новую деталь для робота в виде панели или диска. На этой детали мы сделали отверстия для крепежа Raspberry и прочих электронных компонентов. Деталь напечатали на Prusa i3 MK3S из серого PLA-пластика eSUN.
Вот так выглядит панель:
Крепим плату Raspberry Pi на напечатанную панель винтами M2,5×10, гайками M2,5 и контрим гроверными шайбами M2,5.
Бортовое питание
Для бортового питания нашего робота мы будем использовать аккумуляторы Li-Ion. Энергии нужно много: одна только Raspberry Pi 4 нуждается в 3 А тока, а ведь помимо неё на борту робота будут и другие потребители. Так что нужны аккумуляторы с большой токоотдачей и ёмкостью. Можно использовать аккумуляторы Li-Pol, которые обычно применяются в радиоуправляемых моделях и способны отдавать токи величиной в 2С и более. Однако слишком уж большие токи нам ни к чему, и мы решили взять именно аккумуляторы Li-Ion в формате 18650 на 3,6 В и 2600 мА·ч.
Мы выбрали аккумуляторы Li-Ion 18650 Ansmann 3.6 В 2600 мА·ч.
Один такой аккумулятор — это одна литий-ионная «банка» 3,6 В с максимальной отдачей тока в 5 А. Всего мы будем использовать четыре аккумулятора, соединенных попарно паралеленно. В сумме это даст нам батарею 7,2 В и 5200 мА·ч. Это не так уж и много для мобильного робота, но на первое время нам хватит. Ещё один существенный плюс этих аккумуляторов — наличие схемы защиты от глубокого разряда и короткого замыкания.
7,2 В с батареи мы можем подать на DC-разъём платы Troyka HAT и на её понижающий DC-DC преобразователь. Этим же напряжением 7,2 В мы можем управлять двумя 6-вольтовыми DC-моторами в шасси.
Аккумуляторы поместим в два батарейных отсека 2×18650.
Батарейные отсеки крепим на панели робота винтами М2×6, гайками М2 и контрим гроверными шайбами М2.
Примерим собранную панель на шасси:
Актуализация модели робота
С добавлением новой напечатанной детали наш робот немного изменился внешне. А значит, изменилась его 3D-модель. И мы должны отредактировать его URDF-описание.
Это важно! Всегда регистрируйте любые изменения реального робота как в САПР-документации, так и в URDF-описании. Это действие не является обязательным, но оно прививает привычку контролировать документацию и поможет вам быстрее находить ошибки в программах и, что ещё важнее, их причины.
Обновляем 3D-модель:
Также обновляем URDF-описание робота. Делаем всё через экспортёр, как и в прошлый раз — не будем заново описывать процесс экспорта. В этот раз у нас изменился только сегмент abot_base
: его <visual>
-составляющая, STL-файл, а также инерционные свойства.
Низкоуровневые драйверы
Давайте начнём писать драйверы для железа нашего робота.
Пакет abot_driver
Создадим в рабочем пространстве ros
новый ROS-пакет, который будет отвечать за драйверы.
Назовём пакет abot_driver
.
Не забываем оформлять файлы CMakelists.txt
и package.xml
для каждого нового пакета. В качестве пакетов зависимостей устанавливаем:
roscpp
std_msgs
Внутри пакета создаём папку с именем src
, в которой хранятся исходные файлы программ согласно конвенции ROS.
Это важно! Ноды ROS, которые мы напишем для этого пакета, будут запускаться только на Raspberry Pi, а не на настольном компьютере.
Библиотека WiringPi
Чтобы использовать GPIO-контакты Raspberry Pi, вам нужна библиотека. Существует множество библиотек, которые различаются по языку и по глубине использования функций — например, на C, С++, C#, Python, JavaScript, Perl, Java и Ruby. Вы можете просмотреть полный список существующих библиотек, который мы нашли. Мы будем использовать библиотеку для C++ WiringPi, потому что писать свои ноды ROS мы будем как раз на C++.
WiringPi — это несложная и популярная библиотека. Однако она уже долгое время не поддерживается, потому что её разработка была прекращена. В ней и сейчас есть нереализованные функции и программные ошибки. Последняя официальная версия библиотеки — 2.52 для Raspberry Pi 4B, и она собрана для архитектуры armhf
, а у нас архитектура arm64
.
На просторах GitHub мы нашли хороший форк библиотеки WiringPi. Мы возьмём именно эту версию и cоберем её под нашу версию Linux.
В терминале на Raspberry Pi вводим:
git clone https://github.com/WiringPi/WiringPi.git
cd WiringPi
./build
Убедимся, что библиотека собралась и установилась правильно:
gpio -v
Чтобы просмотреть назначение всех контактов Raspberry Pi 4B, а также их GPIO/Broadcom/WiringPi-маппинг, используйте команду:
gpio readall
Драйвер энкодеров
Наш первый драйвер — для считывания показаний энкодеров моторов.
Схема подключения энкодеров
Мы используем квадратурные энкодеры, каждый из которых имеет два канала A/B и подключается по двум проводам. Энкодер генерирует простые логические сигналы — HIGH или LOW. Если вал двигателя вращается быстро, то и изменения в логических сигналах также генерируются быстро. Поэтому, чтобы случайно не пропустить какие-либо изменения, мы будем считывать сигналы с энкодеров, используя аппаратные прерывания. Raspberry Pi допускает использование прерываний на всех выводах, и вы можете подключить энкодер к любым неиспользуемым пинам.
Мы подключили энкодер левого двигателя к выводу Broadcom BCM 17
(пин 0 для WiringPi) и BCM 27
(пин 2 для WiringPi). Энкодер правого двигателя — к выводам BCM 24
(пин 5 для WiringPi) и BCM 25
(пин 6 для WiringPi).
Энкодеры питаются напряжением 5 В, которое можно взять с колодок Troyka HAT.
Класс Encoder
Напишем класс С++ для декодирования квадратурного энкодера на RPi с использованием прерываний. Назовём его EncoderWiringPi
.
Чтобы использовать прерывания, мы создали две глобальные Сallback-функции encoderISR1
и encoderISR2
. Необработанные значения тиков энкодера содержатся в переменных encoder_position_1
и encoder_position_2
типа long
.
Задайте значение PPR (импульсов на оборот) ваших энкодеров в коде. Для наших энкодеров значение PULSES_PER_REVOLUTION
равно 1920.
Создаём заголовочный файл С++ encoder_wiring_pi.h
в папке abot_driver/src
.
#ifndef ENCODER_WIRING_PI_H_
#define ENCODER_WIRING_PI_H_
#include <ros/ros.h>
#include <wiringPi.h>
constexpr uint8_t ENCODER_1_PIN_A = 17; // Wiring pi 0 = BCM 17
constexpr uint8_t ENCODER_1_PIN_B = 27; // Wiring pi 2 = BCM 27
constexpr uint8_t ENCODER_2_PIN_A = 24; // Wiring pi 5 = BCM 24
constexpr uint8_t ENCODER_2_PIN_B = 25; // Wiring pi 6 = BCM 25
constexpr uint16_t PULSES_PER_REVOLUTION = 1920;
namespace EncoderWiringPiISR {
volatile long encoder_position_1;
volatile long encoder_position_2;
volatile uint8_t encoder_state_1;
volatile uint8_t encoder_state_2;
void encoderISR(const int pin_A, const int pin_B, volatile long &encoder_position, volatile uint8_t &encoder_state) {
uint8_t val_A = digitalRead(pin_A);
uint8_t val_B = digitalRead(pin_B);
uint8_t s = encoder_state & 3;
if (val_A) s |= 4;
if (val_B) s |= 8;
encoder_state = (s >> 2);
if (s == 1 || s == 7 || s == 8 || s == 14)
encoder_position++;
else if (s == 2 || s == 4 || s == 11 || s == 13)
encoder_position--;
else if (s == 3 || s == 12)
encoder_position += 2;
else if (s == 6 || s == 9)
encoder_position -= 2;
}
void encoderISR1(void) {
encoderISR(ENCODER_1_PIN_A, ENCODER_1_PIN_B, encoder_position_1, encoder_state_1);
}
void encoderISR2(void) {
encoderISR(ENCODER_2_PIN_A, ENCODER_2_PIN_B, encoder_position_2, encoder_state_2);
}
}
class EncoderWiringPi {
public:
EncoderWiringPi(const int &pin_A, const int &pin_B, void (*isrFunction)(void), volatile long* encoder_position);
double getAngle();
private:
int _pin_A;
int _pin_B;
volatile long* _encoder_position;
double _initial_angle;
double ticks2Angle(long position);
};
EncoderWiringPi::EncoderWiringPi(const int &pin_A, const int &pin_B, void (*isrFunction)(void), volatile long* encoder_position) {
_encoder_position = encoder_position;
if (wiringPiSetupSys() < 0) {
throw std::runtime_error("Encoder wiringPi error: GPIO setup error");
}
ROS_INFO("Encoder wiringPi: GPIO setup");
_pin_A = pin_A;
_pin_B = pin_B;
pinMode(_pin_A, INPUT);
pinMode(_pin_B, INPUT);
pullUpDnControl(_pin_A, PUD_UP);
pullUpDnControl(_pin_B, PUD_UP);
if (wiringPiISR(_pin_A, INT_EDGE_BOTH, isrFunction) < 0) {
throw std::runtime_error("Encoder wiringPi error: ISR pinA error");
}
if (wiringPiISR(_pin_B, INT_EDGE_BOTH, isrFunction) < 0) {
throw std::runtime_error("Encoder wiringPi error: ISR pinB error");
}
_initial_angle = ticks2Angle(*_encoder_position);
ROS_INFO("Encoder wiringPi: ISR setup");
}
double EncoderWiringPi::getAngle() {
double current_angle = ticks2Angle(*_encoder_position);
return current_angle - _initial_angle;
}
double EncoderWiringPi::ticks2Angle(long position) {
return position * ((double)2 * M_PI / PULSES_PER_REVOLUTION / 2);
}
#endif // ENCODER_WIRING_PI_H_
Также нам понадобится проверка на «переполнение» переменных encoder_position_1
и encoder_position_2
, но об этом позже.
Нода энкодеров
Теперь напишем ROS-ноду для наших энкодеров. Назовем её encoders
.
Как будет работать эта нода? Класс Encoder
, который мы описали выше, считывает сигналы с энкодеров через прерывания, накапливает их, а затем конвертирует в углы поворота колёс (в радианах). Эти углы затем используются для расчёта одометрии робота и для определения текущей скорости вращения колёс.
Также мы создадим класс EncodersPair
, который использует углы поворота колёс для расчёта:
- Пройденного каждым колесом растояния (
_left_wheel_angle
,_right_wheel_angle
). В нашем случае пройденное расстояние хранится в виде количества оборотов колеса в радианах. - Текущей скорости вращения колеса (
_letf_wheel_velocity
,_right_wheel_velocity
) в радианах в секунду.
Расчёты производятся раз в 10 мс по ROS-таймеру _encoders_timer
— то есть, с частотой 100 герц.
Пройденные колёсами расстояния помещаются в сообщения типа std_msgs/Float64
и публикуются в топики /abot/left_wheel/angle
и /abot/right_wheel/angle
.
Теукщие скорости вращения колёс также помещаются в сообщения типа std_msgs/Float64
и публикуются в топики /abot/left_wheel/current_velocity
и /abot/right_wheel/current_velocity
.
Создадим файл encoders.cpp
в папке abot_driver/src
:
#include "encoder_wiring_pi.h"
#include <std_msgs/Float64.h>
#include <chrono>
typedef boost::chrono::steady_clock time_source;
class EncodersPair {
public:
EncodersPair(double update_rate);
private:
ros::NodeHandle _node;
ros::Publisher _left_wheel_angle_pub;
ros::Publisher _right_wheel_angle_pub;
ros::Publisher _left_wheel_velocity_pub;
ros::Publisher _right_wheel_velocity_pub;
ros::Timer _encoders_timer;
std_msgs::Float64 _left_wheel_angle_msg;
std_msgs::Float64 _right_wheel_angle_msg;
std_msgs::Float64 _left_wheel_velocity_msg;
std_msgs::Float64 _right_wheel_velocity_msg;
EncoderWiringPi _encoder_left;
EncoderWiringPi _encoder_right;
double _left_wheel_angle;
double _right_wheel_angle;
double _left_wheel_velocity;
double _right_wheel_velocity;
double _left_wheel_position;
double _right_wheel_position;
time_source::time_point _last_time;
void encodersCallback(const ros::TimerEvent& event);
};
EncodersPair::EncodersPair(double update_rate) :
_encoder_left(ENCODER_1_PIN_A, ENCODER_1_PIN_B, &EncoderWiringPiISR::encoderISR1, &EncoderWiringPiISR::encoder_position_1),
_encoder_right(ENCODER_2_PIN_A, ENCODER_2_PIN_B, &EncoderWiringPiISR::encoderISR2, &EncoderWiringPiISR::encoder_position_2) {
_left_wheel_angle_pub = _node.advertise<std_msgs::Float64>("/abot/left_wheel/angle", 1);
_right_wheel_angle_pub = _node.advertise<std_msgs::Float64>("/abot/right_wheel/angle", 1);
_left_wheel_velocity_pub = _node.advertise<std_msgs::Float64>("/abot/left_wheel/current_velocity", 1);
_right_wheel_velocity_pub = _node.advertise<std_msgs::Float64>("/abot/right_wheel/current_velocity", 1);
_encoders_timer = _node.createTimer(ros::Duration(update_rate), &EncodersPair::encodersCallback, this);
}
void EncodersPair::encodersCallback(const ros::TimerEvent& event) {
time_source::time_point this_time = time_source::now();
boost::chrono::duration<double> elapsed_duration = this_time - _last_time;
ros::Duration elapsed(elapsed_duration.count());
_last_time = this_time;
_left_wheel_angle = -1 * _encoder_left.getAngle();
_right_wheel_angle = 1 * _encoder_right.getAngle();
_left_wheel_angle_msg.data = _left_wheel_angle;
_right_wheel_angle_msg.data = _right_wheel_angle;
_left_wheel_angle_pub.publish(_left_wheel_angle_msg);
_right_wheel_angle_pub.publish(_right_wheel_angle_msg);
double delta_left_wheel = _left_wheel_angle - _left_wheel_position;
double delta_right_wheel = _right_wheel_angle - _right_wheel_position;
_left_wheel_position += delta_left_wheel;
_left_wheel_velocity = delta_left_wheel / elapsed.toSec();
_right_wheel_position += delta_right_wheel;
_right_wheel_velocity = delta_right_wheel / elapsed.toSec();
_left_wheel_velocity_msg.data = _left_wheel_velocity;
_right_wheel_velocity_msg.data = _right_wheel_velocity;
_left_wheel_velocity_pub.publish(_left_wheel_velocity_msg);
_right_wheel_velocity_pub.publish(_right_wheel_velocity_msg);
}
int main(int argc, char** argv) {
ros::init(argc, argv, "encoders");
EncodersPair encoders_pair(0.01);
ros::spin();
return 0;
}
Добавим новый исполняемый файл в правило сборки CMakelists.txt
пакета abot_driver
:
add_executable(encoders src/encoders.cpp)
target_link_libraries(encoders ${catkin_LIBRARIES} -lwiringPi -lpthread -lcrypt -lm -lrt)
Соберём пакет abot_driver
с новой нодой:
cd ~/ros
catkin_make
Тест энкодеров
Проверим, как работает новая нода encoders
.
Если вдруг вы выключили ядро (roscore
) на настольном компьютере, включите его заново.
В рабочем пространстве на RPi запустим новую ноду вручную под пользователем root
:
cd ~/ros
su root
source devel/setup.bash
rosrun abot_driver encoders
На настольном компьютере проверим, появились ли новые топики /abot/left_wheel/angle
, /abot/right_wheel/angle
, /abot/left_wheel/current_velocity
и /abot/right_wheel/current_velocity
, созданные нодой encoders
.
rostopic list
Посмотрим командой rostopic echo
, какие сообщения приходят в эти топики. Например, посмотрим на пройденный путь и скорость вращения правого колеса:
rostopic echo /abot/right_wheel/angle
rostopic echo /abot/right_wheel/current_velocity
Проверим работу энкодеров. Установим робота на какую-нибудь подставку, а затем будем руками поворачивать колесо и при этом наблюдать за изменениями угла поворота и скорости вращения в мониторе топика.
Например, повернув правое колесо примерно на один оборот, соответствующее значение угла поворота в топике /abot/right_wheel/angle
должно измениться с 0
до примерно 2 * PI
.
Ту же операцию проделаем и с левым колесом.
Драйвер моторов
Наш второй драйвер — для управления двумя DC-моторами, установленными на шасси.
Подключение моторов
Моторы нужно подключить к Raspberry Pi. Но мы не можем напрямую подключить двигатели постоянно тока к плате RPi и управлять ими. Нам нужен специльный модуль, который будет управлять моторами.
Наши DC-моторы потребляют мало тока и не нуждаются в большом напряжении, поэтому в качестве платы управления мы можем использовать небольшой H-мост. Мы взяли двухканальный H-мост в формате Troyka-модуля, который разработан для управления двумя DC-моторами с максимальным током до 1,2 А на канал.
Мы также используем адаптер Troyka Pad 1×2 для более удобного подключения Troyka-модулей и крепления двухканального H-моста к нашей панели электроники. На панели мы зарнее предусмотрели монтажные отверстия для двухъюнитового Troyka Pad.
Закрепляем модуль на панели:
Два контакта D и E управляют одним каналом двигателя. Вывод E (Enable) принимает ШИМ-сигнал, который отвечает за скорость вращения двигателя. Вывод D (Направление) принимает логический (HIGH или LOW) сигнал для задания направления вращения. Всего для управления двумя DC-моторами задействованы четыре контакта.
Схема подключения моторов
Raspberry Pi 4B может генерировать аппаратный ШИМ-сигнал только на двух каналах PWM0
и PWM1
, которыми мы и воспользуемся. Помимо аппаратного ШИМ RPi может генерировать ещё программный ШИМ-сигнал на любом из своих выводов, но такой сигнал будет потреблять значительную часть вычислительной мощности. Канал PWM0
может быть назначен на вывод Broadcom BCM 12
(пин 26 для WiringPi) или BCM 18
(пин 1 для WiringPi). Канал PWM1
может быть назначен на вывод Broadcom BCM 13
(пин 23 для WiringPi) или BCM 19
(пин 24 для WiringPi). Логические контакты для управления направлением вращения моторов можно подлючить к любым пинам Raspberry.
Мы подключили левый двигатель к WiringPi-контактам 7 и 1, а правый двигатель — к WiringPi-контактам 12 и 13:
Класс DCMotor
Напишем простой класс С++ для управления двигателем постоянного тока через H-мост на RPi под ROS.
Вал двигателя может вращаться по часовой стрелке — cw
(clockwise), против часовой стрелки — ccw
(counter clockwise) или остановиться — stop
. Разрешение аппаратного ШИМ на Raspberry составляет 10 бит (максимальное значение — 1023
).
Создим заголовочный файл C++ dc_motor_wiring_pi.h
и поместим его в папку abot_driver/src
.
#ifndef DC_MOTOR_WIRING_PI_H_
#define DC_MOTOR_WIRING_PI_H_
#include <ros/ros.h>
#include <wiringPi.h>
constexpr uint16_t RPI_MAX_PWM_VALUE = 1023;
class DCMotorWiringPi {
public:
DCMotorWiringPi(int8_t direction_pin, int8_t enable_pin);
void cw(uint16_t val);
void ccw(uint16_t val);
void stop();
private:
int8_t _direction_pin;
int8_t _enable_pin;
uint16_t protectOutput(uint16_t val);
};
DCMotorWiringPi::DCMotorWiringPi(int8_t direction_pin, int8_t enable_pin) {
_direction_pin = direction_pin;
_enable_pin = enable_pin;
if (wiringPiSetupGpio() < 0) {
throw std::runtime_error("DCMotor wiringPi error: GPIO setup error");
}
ROS_INFO("DCMotor wiringPi: GPIO setup");
pinMode(_direction_pin, OUTPUT);
pinMode(_enable_pin, PWM_OUTPUT);
stop();
ROS_INFO("DCMotor wiringPi: Motor setup");
}
void DCMotorWiringPi::stop() {
pwmWrite(_enable_pin, 0);
digitalWrite(_direction_pin, 0);
}
void DCMotorWiringPi::cw(uint16_t val) {
pwmWrite(_enable_pin, protectOutput(val));
digitalWrite(_direction_pin, 1);
}
void DCMotorWiringPi::ccw(uint16_t val) {
pwmWrite(_enable_pin, protectOutput(val));
digitalWrite(_direction_pin, 0);
}
uint16_t DCMotorWiringPi::protectOutput(uint16_t val) {
return val > RPI_MAX_PWM_VALUE ? RPI_MAX_PWM_VALUE : val;
}
#endif // DC_MOTOR_WIRING_PI_H_
PID-контроллер
Для управления скоростью вращения колёс мы будем использовать PID-контроллер. Каждому колесу будет присвоен свой PID-контроллер, задача которого — поддержание необходимой скорости вращения.
Реализацию программы PID-контроллера писать самим не обязательно, в ROS уже есть готовый пакет — pid
.
Установим ROS-пакет pid
на Raspberry:
sudo apt-get install ros-noetic-pid
Принцип действия PID-контроллера для колеса нашего робота:
- На вход PID-контроллера (Input или State) поступает текущая скорость вращения колеса (в радианах в секунду), которая считается нодой энкодеров и публикуюется в топики
/abot/right_wheel/current_velocity
и/abot/left_wheel/current_velocity
. - Целью PID-контроллера (Setpoint) является поддержание требуемой скорости вращения колеса (в радианах в секунду). Нужную нам скорость вращения мы будем публиковать в топики
/abot/right_wheel/target_velocity
и/abot/left_wheel/target_velocity
. - На выходе PID-контроллера (Output или Сontrol effort) будет величина ШИМ, которую нужно подать на H-мост для коррекции скорости вращения. Значения ШИМ будем публиковать в топики
/abot/right_wheel/pwm
и/abot/left_wheel/pwm
.
Настроим два PID-контроллера для левого и правого колеса.
В пакете abot_driver
создадим папку launch
. В этой папке создадим новый файл abot_pid.launch
для запуска двух нод controller
из пакета pid
.
<launch>
<node name="controller" pkg="pid" type="controller" ns="/abot/left_wheel" output="screen" >
<param name="node_name" value="left_wheel_pid" />
<param name="Kp" value="1.0" />
<param name="Ki" value="0.0" />
<param name="Kd" value="0.0" />
<param name="upper_limit" value="10.23" />
<param name="lower_limit" value="-10.23" />
<param name="windup_limit" value="10.23" />
<param name="max_loop_frequency" value="100.0" />
<param name="min_loop_frequency" value="100.0" />
<remap from="/abot/left_wheel/setpoint" to="/abot/left_wheel/target_velocity" />
<remap from="/abot/left_wheel/state" to="/abot/left_wheel/current_velocity" />
<remap from="/abot/left_wheel/control_effort" to="/abot/left_wheel/pwm" />
</node>
<node name="controller" pkg="pid" type="controller" ns="/abot/right_wheel" output="screen" >
<param name="node_name" value="right_wheel_pid" />
<param name="Kp" value="1.0" />
<param name="Ki" value="0.0" />
<param name="Kd" value="0.0" />
<param name="upper_limit" value="10.23" />
<param name="lower_limit" value="-10.23" />
<param name="windup_limit" value="10.23" />
<param name="max_loop_frequency" value="100.0" />
<param name="min_loop_frequency" value="100.0" />
<remap from="/abot/right_wheel/setpoint" to="/abot/right_wheel/target_velocity" />
<remap from="/abot/right_wheel/state" to="/abot/right_wheel/current_velocity" />
<remap from="/abot/right_wheel/control_effort" to="/abot/right_wheel/pwm" />
</node>
</launch>
Этим файлом мы запустим две ноды PID-контроллера, которые подписываются на топики /abot/right_wheel/target_velocity
, /abot/left_wheel/target_velocity
, /abot/right_wheel/current_velocity
и /abot/left_wheel/current_velocity
.
Оба PID-контроллера работают с частотой 100 герц (за это отвечают параметры min_loop_frequency
и max_loop_frequency
). Для корректировки скорости наши PID-контроллеры публикуют значения ШИМ в топики /abot/right_wheel/pwm
и /abot/left_wheel/pwm
.
Это важно! Значения ШИМ находятся в диапазоне [-10.23 , 10.23]. Мы решили отправлять их именно в таком диапазоне, а не в диапазоне [-1023 , 1023]. Это обусловлено особенностями подбора коэффициентов контроллера Kp
, Ki
и Kd
в ROS-пакете pid
. По нашему опыту, в ROS не очень часто можно встретить целочисленные значения. Большинство ROS-нод обмениваются сообщениями со значениями типа floating-point (с плавающей запятой) и в диапазоне [-1.0 , 1.0].
На этом этапе мы задаём стандартные значения коэффициентов Kp
, Ki
, Kd
. Настраивать коэффициенты будем чуть позже.
Нода моторов
Теперь мы можем написать окончательную ROS-ноду для моторов робота. Назовем её dc_motors
.
Создадим файл dc_motors.cpp
в папке about_driver/src
.
Как будет работать нода моторов? Она подписывается на два топика ROS — /abot/right_wheel/pwm
и /abot/left_wheel/pwm
. Через эти топики нода получает сообщения типа std_msgs/Float64
, которые содержат значения ШИМ-сигналов коррекции скоростей в диапазоне [-10.23 , 10.23]. Далее эти значения умножаются на 100 и отправляются на H-мосты через объекты класса DCMotorWiringPi
.
#include "dc_motor_wiring_pi.h"
#include <std_msgs/Float64.h>
constexpr uint8_t MOTOR_1_PIN_D = 4; // Wiring pi 7 = BCM 4
constexpr uint8_t MOTOR_1_PIN_E = 18; // Wiring pi 1 = BCM 18
constexpr uint8_t MOTOR_2_PIN_D = 12; // Wiring pi 26 = BCM 12
constexpr uint8_t MOTOR_2_PIN_E = 13; // Wiring pi 23 = BCM 13
DCMotorWiringPi left_dc_motor(MOTOR_1_PIN_D, MOTOR_1_PIN_E);
DCMotorWiringPi right_dc_motor(MOTOR_2_PIN_D, MOTOR_2_PIN_E);
void leftMotorCallback(const std_msgs::Float64& msg) {
int16_t pwm = msg.data * 100;
if (pwm > 0) {
left_dc_motor.ccw(abs(pwm));
} else if (pwm < 0) {
left_dc_motor.cw(abs(pwm));
} else if (pwm == 0) {
left_dc_motor.stop();
}
}
void rightMotorCallback(const std_msgs::Float64& msg) {
int16_t pwm = msg.data * 100;
if (pwm > 0) {
right_dc_motor.ccw(abs(pwm));
} else if (pwm < 0) {
right_dc_motor.cw(abs(pwm));
} else if (pwm == 0) {
right_dc_motor.stop();
}
}
int main(int argc, char **argv) {
ros::init(argc, argv, "dc_motors");
ros::NodeHandle node;
ros::Subscriber left_motor_target_vel_sub = node.subscribe("/abot/left_wheel/pwm", 1, &leftMotorCallback);
ros::Subscriber right_motor_target_vel_sub = node.subscribe("/abot/right_wheel/pwm", 1, &rightMotorCallback);
ros::spin();
return 0;
}
Добавим новый исполняемый файл в правило сборки CMakelists.txt
в нашем пакете abot_driver
.
add_executable(dc_motors src/dc_motors.cpp)
target_link_libraries(dc_motors ${catkin_LIBRARIES} -lwiringPi -lpthread -lcrypt -lm -lrt)
Соберём пакет abot_driver
с новой нодой:
cd ~/ros
catkin_make
Запуск драйверов
Отлично! Наши драйверы готовы.
Давайте создадим новый файл запуска для всех написанных нод, чтобы нам не приходилось каждый раз запускать все драйверы вручную.
В пакете abot_driver
в папке launch
создадим файл запуска abot_drivers.launch
. В нём запустим созданные нами ноды, а также включим файл запуска PID-контроллеров:
<launch>
<node name="encoders" pkg="abot_driver" type="encoders" output="screen" />
<node name="dc_motors" pkg="abot_driver" type="dc_motors" output="screen" />
<include file="$(find abot_driver)/launch/abot_pid.launch" />
</launch>
Теперь мы можем одновременно запускать все драйверы одной командой из-под root
:
su root
source devel/setup.bash
roslaunch abot_driver abot_drivers.launch
На настольном компьютере проверяем, что появились топики от обоих драйверов и от PID-контроллеров:
rostopic list
Тест моторов и настройка параметров PID-контроллера
Протестируем, как работают наши моторы, и настроим коэффициенты PID-контроллеров.
На Raspberry в первом терминале запустим все наши драйверы под пользователем root
:
cd ~/ros
su root
source devel/setup.bash
roslaunch abot_driver abot_drivers.launch
Чтобы публиковать значения в топики вручную через терминалы, используем ROS-утилиту rqt
.
rqt
— это святая святых любого разработчика под ROS. Набор плагинов rqt
предоставляет графический интерфейс для взаимодействия пользователя с нодами и топиками, мониторинга, визуализации графов зависимостей и много чего ещё.
На настольном компьютере в новом терминале вводим:
rqt
Перед нами появится окно rqt
.
Сперва откроем плагин для публикации сообщений в топик. Нажмём «Plugins → Topics → Message Publisher». В появившемся окне добавим два топика задания скоростей — /abot/left_wheel/target_velocity
и /abot/right_wheel/target_velocity
. В эти топики будем публиковать сообщения типа std_msgs/Float64
. В столбце rate
установим частоту публикации сообщений 100 герц. Для запуска процесса публикации нужно установить галочку слева от имени топика.
Теперь откроем плагин для просмотра сообщений в топике. Нажмём «Plugins → Visualisation → Plot». Данный плагин отслеживает значения в указанных топиках и строит график изменения этих значений в реальном времени. Например, для отслеживания скоростей левого колеса добавим два топика — /abot/left_wheel/target_velocity
и /abot/left_wheel/current_velocity
.
Ещё откроем плагин для настройки коэффициентов PID-контроллера в реальном времени. Нажмём «Plugins → Dynamic Reconfigure».
В этом шаге вам нужно скорректировать коэффициенты обоих PID-контроллеров. Нужно добиться, чтобы колесо вращалось именно с заданной скоростью. Также желательно добиться плавной регулировки скорости, без резких разгонов и торможений. Чем больше действительная скорость вращения соответствует реальной, тем легче будет реализовать навигацию робота.
Оперируем значениями в топиках /abot/left_wheel/target_velocity
, /abot/right_wheel/target_velocity
и коэффициентами PID-контроллеров и наблюдаем за скоростями вращения колёс /abot/right_wheel/сurrent_velocity
и /abot/left_wheel/сurrent_velocity
.
Используя rqt
и плагин «Introspection → Node Graph», вы можете взглянуть на все запущенные в вашей системе ROS ноды, а также все топики, на которые они пописаны или в которые они публикуют сообщения.
Это также можно сделать через терминал командой:
rqt_graph
Сейчас граф наших ROS-нод и топиков выглядит так:
Контроль движения
Сейчас мы можем управлять колёсами робота отдельно, а ещё узнать скорость их вращения и поддерживать её. Теперь нам нужно создать контроллер, который будет оперировать скоростью движения всего робота, то есть управлять его приводом.
Пакет robot_control
В ROS есть много готовых контроллеров — ros_controllers
. Ознакомьтесь с ними.
Шасси нашего робота представляет собой дифференциальный двухколёсный привод. Для этого типа привода есть готовое решение — контроллер diff_drive_controller
. Это означает, что нам не нужно будет писать алгоритм получения скоростей дифференциального привода.
Традиционно в ROS пакет с контроллерами робота называют robot_control
. Мы назовём наш пакет abot_control
.
Cоздадим новый пакет с именем abot_control
в рабочей области ros
нашего проекта. Этот пакет будет хранить описание контроллеров робота. Для пакета установим следующие пакеты-зависимости:
controller_manager
hardware_interface
trajectory_msgs
ros_controllers
joint_state_controller
robot_state_publisher
.
Настройки контроллеров робота
В пакете abot_control
создадим папку config
, а в ней файл abot_controllers.yaml
, который будет хранить настройки контроллеров нашего робота. Файлы настроек контроллеров в ROS традиционно имеют формат *.yaml.
Сперва у нашего робота будет два контроллера.
Первый контроллер относится к типу joint_state_controller/JointStateController
. Он отвечает за обновление положений всех сочленений (joints
) вашего робота на параметрическом сервере ROS.
Любому используемому контроллеру нужно дать имя. Мы назвали первый контроллер joint_state_controller
.
Контроллер имеет один-единственный параметр: частоту обновления положений сочленений — publish_rate
. Установим частоту обновления в 100
герц.
joint_state_controller:
type: joint_state_controller/JointStateController
publish_rate: 100
Второй контроллер относится к типу diff_drive_controller/DiffDriveController
. Мы назвали его mobile_abot
.
Этот тип контроллера отвечает непосредственно за движение двухколёсного робота. Как это работает? Мы даём контроллеру входные данные. Например, мы хотим, чтобы робот ехал с линейной скоростью в 1 м/c и угловой скоростью в 2 рад/c. Но не знаем, какие при этом должны быть скорости колёс. Контроллер берёт эти входные данные, а также значения текущих скоростей и положение робота, полученное с одометрии, обрабатывает всё это и на выходе отдаёт необходимые угловые скорости вращения колёс, чтобы наш робот двигался именно заданным образом.
В ROS движение робота конвенционально осуществляется через топики, которые обмениваются сообщениями типа geometry_msgs/Twist
. Сообщение этого типа состоит из двух векторов linear
и angular
типа geometry_msgs/Vector3
. Каждый такой вектор — трёхмерный. Вектор geometry_msgs/Vector3 linear
описывает линейные скорости робота вдоль осей X, Y, Z в глобальной системе координат. Вектор geometry_msgs/Vector3 angular
представляет скорость вращения робота вокруг осей X, Y, Z.
Наш двухколёсный дифференциальный привод обладает неголономным движением. Он контролируется только линейной скростью по оси X и угловой скоростью вокруг оси Z. Таким образом, наш вектор скорости linear
будет всегда иметь нулевые линейные скорости по осям Y, Z и нулевые угловые скорости вокруг осей X и Y.
Опишем принцип работы контроллера. Мы отправляем желаемые векторы скорости робота в топик /cmd_vel
. Контроллер анализирует полученные векторы, вычисляет необходимые скорости вращения правого и левого колеса и отправляет рассчитанные значения в топики моторов левого и правого колеса. Одновременно с этим контроллер считывает текущие углы поворота колёс и вычисляет текущую траекторию и одометрию робота. Сообщения об одометрии имеют тип nav_msgs/Odometry
и публикуются в топик /odom
.
В отличие от первого контроллера, этот имеет множество параметров, но настраиваются только основные из них. Вам необходимо указать:
- Имена сочленений из описания робота для правого и левого колеса —
left_wheel
иright_wheel
. У нас этоleft_wheel_to_base
иright_wheel_to_base
. - Имя базового сегмента робота —
base_frame_id
. У нас этоbase_footprint
. - Максимальную линейную скорость и ускорение робота робота вдоль оси X, а также максимальную угловую скорость и ускорение робота вокруг оси Z. Минимальные значения этих скоростей можно не указывать, по умолчанию они равны максимальным с противоположным знаком.
- Матрицы смещения
twist_covariance_diagonal
иpose_covariance_diagonal
для погрешности одометрии. Эти параметры можно оставить по умолчанию.
Это важно! Откуда можно узнать максимальные значения скоростей и ускорений робота? Эти параметры — расчётные. Например, максимальную линейную скорость робота по оси Х легко вычислить, замерив пройденный путь колеса при максимальной скорости вращения. Тем не менее, точный расчёт максимальных скоростей, особенно ускорений робота — это трудная математическая задача, и для её решения пришлось бы написать десяток тестов. Мы же только новички в робототехнике, поэтому максимальные скорости будем вычислять эмпирически.
Сперва мы установили все скорости и ускорения равными нулю, а затем постепенно увеличивали их и наблюдали за поведением робота во время отладки. Не стоит задавать слишком большие значения скоростей и ускорений. Стоит ещё помнить, что робот не сможет двигаться со скоростью выше той, что указана в файле конфигурации контроллера. Нужно найти золотую середину.
Мы установили частоту работы контроллера в 100
герц, а наши максимальные скорости и ускорения выглядят следующим образом:
mobile_abot:
type : "diff_drive_controller/DiffDriveController"
left_wheel: 'left_wheel_to_base'
right_wheel: 'right_wheel_to_base'
publish_rate: 100.0
pose_covariance_diagonal: [0.001, 0.001, 1000000.0, 1000000.0, 1000000.0, 1000.0]
twist_covariance_diagonal: [0.001, 0.001, 1000000.0, 1000000.0, 1000000.0, 1000.0]
wheel_separation_multiplier: 1.0 # default: 1.0
wheel_radius_multiplier : 1.0 # default: 1.0
cmd_vel_timeout: 0.1
base_frame_id: base_footprint
linear:
x:
has_velocity_limits : true
max_velocity : 0.6 # m/s
has_acceleration_limits: true
max_acceleration : 6.0 # m/s^2
angular:
z:
has_velocity_limits : true
max_velocity : 4.71 # rad/s
has_acceleration_limits: true
max_acceleration : 9.42 # rad/s^2
enable_odom_tf: true
Настраиваем описания робота для контроллера
Для правильного расчёта скоростей и одометрии контроллеру необходимы ещё два параметра: радиус колеса — wheel_radius
и расстояние между колёсами — wheel_separation
. По умолчанию контроллер ищет эти параметры в URDF описании робота robot_description
, а в нашем случае — в abot_description
.
Кроме этого, нам необходимо внести некоторые радикальные изменения в описание робота. В настройках контроллера mobile_abot
мы указали имена сочленений колёс. Однако контроллер типа diff_drive_controller/DiffDriveController
ожидает, что сегменты колёс, прикреплённые к этим сочленениям, имеют круглую форму.
Сейчас в описании нашего робота левое и правое колесо представлены в виде 3D-модели, взятой из файла .STL. Колесо из нашего .STL-файла красиво отображается в визуализации и хоть оно и круглой формы но является лишь набором полигонов. Контроллеру же нужна точная геометрия колеса, то есть оно должно быть описано математически.
Для этого мы создим ещё два сегмента в URDF-описании abot_description
. Назовем их left_wheel
и right_wheel
. В этих сегментах мы опишем визуальную составлящую <visual>
не 3D-моделью, а геометрией <geometry>
. Теперь сочленения left_wheel_to_base
и right_wheel_to_base
будут вести к этим двум новым сегментам колёс. Старые сегменты с 3D-моделями мы удалять не будем, а оставим их зафиксированными на новых. Новые сегменты колёс описываем как цилиндры (<cylinder>
) высотой 26 мм и радиусом 32,5 мм. Таковы реальные размеры наших колес.
Вносим изменения в файл описания abot.xacro
:
<xacro:property name="wheel_radius" value="0.0325"/>
<xacro:property name="wheel_separation" value="0.128"/>
<xacro:property name="wheel_width" value="0.026"/>
<xacro:property name="PI" value="3.1415926"/>
В описание левого колеса:
<link name="left_wheel">
<visual>
<origin xyz="0 0 0" rpy="${PI/2} 0 0" />
<geometry>
<cylinder length="${wheel_width}" radius="${wheel_radius}"/>
</geometry>
<material name="Green" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="${PI/2} 0 0" />
<geometry>
<cylinder length="${wheel_width}" radius="${wheel_radius}"/>
</geometry>
</collision>
</link>
<joint name="left_wheel_to_abot_left_wheel" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0" />
<parent link="left_wheel" />
<child link="abot_left_wheel" />
</joint>
<joint name="left_wheel_to_base" type="continuous">
<origin xyz="0 0.068 0.0145" rpy="0 0 0" />
<parent link="abot_base" />
<child link="left_wheel" />
<axis xyz="0 1 0" />
</joint>
В описание правого колеса:
<link name="right_wheel">
<visual>
<origin xyz="0 0 0" rpy="${PI/2} 0 0" />
<geometry>
<cylinder length="${wheel_width}" radius="${wheel_radius}"/>
</geometry>
<material name="Green" />
</visual>
<collision>
<origin xyz="0 0 0" rpy="${PI/2} 0 0" />
<geometry>
<cylinder length="${wheel_width}" radius="${wheel_radius}"/>
</geometry>
</collision>
</link>
<joint name="right_wheel_to_abot_right_wheel" type="fixed">
<origin xyz="0 0 0" rpy="0 0 0" />
<parent link="right_wheel" />
<child link="abot_right_wheel" />
</joint>
<joint name="right_wheel_to_base" type="continuous">
<origin xyz="0 -0.068 0.0145" rpy="0 0 0" />
<parent link="abot_base" />
<child link="right_wheel" />
<axis xyz="0 1 0" />
</joint>
Дерево нашей модели теперь выглядит подобным образом:
Запуск контроллеров
Создадим новый файл запуска для одновременной активации всех контроллеров.
В пакете abot_control
создадим папку launch
, а в ней новый файл запуска abot_control.launch
.
В новом файле запуска мы загрузим на параметрический сервер ROS параметры наших контроллеров из файла abot_controllers.yaml
и запустим ноду менеджера контроллеров — controller_spawner
:
<launch>
<rosparam file="$(find abot_control)/config/abot_controllers.yaml" command="load" />
<node name="controller_spawner" pkg="controller_manager" type="spawner" respawn="false" output="screen"
args="joint_state_controller mobile_abot" ></node>
</launch>
Пакет robot_base
Мы создали два контроллера и два драйвера. Контроллеры полностью настроены и готовы к работе, но они не знают, с чем именно им нужно работать. Мы должны соединить наши контроллеры с драйверами. Для этого нам нужно написать так называемый hardware_interface
, с помощью которого мы подключим конкретные низкоуровневые драйверы робота к ROS-инфраструктуре более высокого уровня.
Традиционно в ROS для описания hardware_interface
используется пакет с именем robot_base
. Наш пакет уже по привычке называем abot_base
.
Создаём в рабочем пространстве новый пакет abot_base
.
В качестве пакетов зависимостей устанавливаем:
controller_manager
hardware_interface
trajectory_msgs
roscpp
diff_drive_controller
Класс AbotHardwareInterface
В пакете abot_base
необходимо создать определённый класс C++ — наследник класса hardware_interface::RobotHW
пакета hardware_interface
в ROS. Это обязательное условие. Подробнее вы можете прочитать в Wiki-инструкции пакета ros_controls. Данный класс-наследник должен регистрировать все описанные контроллеры робота и взаимодествовать с драйверами.
Мы назвали наш класс AbotHardwareInterface
. Он подписывается на топики драйверов /abot/left_wheel_angle
и /abot/right_wheel_angle
и публикует сообщения в топики /abot/left_wheel_target_velocity
и /abot/right_wheel_target_velocity
.
Два главных метода класса — это updateJointsFromHardware
и writeCommandsToHardware
. Метод updateJointsFromHardware
обрабатывает текущие углы поворота колёс с драйвера, вычисляет текущие скорости вращения и передаёт их в контроллер mobile_abot
. И наоборот, метод writeCommandsToHardware
берёт от контроллера mobile_abot
значения необходимых скоростей колёс и отправляет их в драйвер.
В пакете abot_base
создаём папку src
, а в ней новый заголовочный файл C++ abot_hardware_interface.h
.
#ifndef ABOT_HARDWARE_INTERFACE_H_
#define ABOT_HARDWARE_INTERFACE_H_
#include <boost/assign/list_of.hpp>
#include <sstream>
#include <std_msgs/Float64.h>
#include <std_srvs/Empty.h>
#include <controller_manager/controller_manager.h>
#include <hardware_interface/joint_command_interface.h>
#include <hardware_interface/joint_state_interface.h>
#include <hardware_interface/robot_hw.h>
#include <ros/ros.h>
#include <ros/console.h>
class AbotHardwareInterface : public hardware_interface::RobotHW {
public:
AbotHardwareInterface(ros::NodeHandle node, ros::NodeHandle private_node, double target_max_wheel_angular_speed);
void updateJointsFromHardware(const ros::Duration& period);
void writeCommandsToHardware();
private:
ros::NodeHandle _node;
ros::NodeHandle _private_node;
hardware_interface::JointStateInterface _joint_state_interface;
hardware_interface::VelocityJointInterface _velocity_joint_interface;
ros::Subscriber _left_wheel_angle_sub;
ros::Subscriber _right_wheel_angle_sub;
ros::Publisher _left_wheel_vel_pub;
ros::Publisher _right_wheel_vel_pub;
struct Joint {
double position;
double position_offset;
double velocity;
double effort;
double velocity_command;
Joint()
: position(0)
, velocity(0)
, effort(0)
, velocity_command(0) {}
} _joints[2];
double _left_wheel_angle;
double _right_wheel_angle;
double _max_wheel_angular_speed;
void registerControlInterfaces();
void leftWheelAngleCallback(const std_msgs::Float64& msg);
void rightWheelAngleCallback(const std_msgs::Float64& msg);
void limitDifferentialSpeed(double& diff_speed_left_side, double& diff_speed_right_side);
};
AbotHardwareInterface::AbotHardwareInterface(ros::NodeHandle node, ros::NodeHandle private_node, double target_max_wheel_angular_speed)
: _node(node)
, _private_node(private_node)
, _max_wheel_angular_speed(target_max_wheel_angular_speed) {
registerControlInterfaces();
_left_wheel_vel_pub = _node.advertise<std_msgs::Float64>("/abot/left_wheel/target_velocity", 1);
_right_wheel_vel_pub = _node.advertise<std_msgs::Float64>("/abot/right_wheel/target_velocity", 1);
_left_wheel_angle_sub = _node.subscribe("abot/left_wheel/angle", 1, &AbotHardwareInterface::leftWheelAngleCallback, this);
_right_wheel_angle_sub = _node.subscribe("abot/right_wheel/angle", 1, &AbotHardwareInterface::rightWheelAngleCallback, this);
}
void AbotHardwareInterface::writeCommandsToHardware() {
double diff_angle_speed_left = _joints[0].velocity_command;
double diff_angle_speed_right = _joints[1].velocity_command;
limitDifferentialSpeed(diff_angle_speed_left, diff_angle_speed_right);
std_msgs::Float64 left_wheel_vel_msg;
std_msgs::Float64 right_wheel_vel_msg;
left_wheel_vel_msg.data = diff_angle_speed_left;
right_wheel_vel_msg.data = diff_angle_speed_right;
_left_wheel_vel_pub.publish(left_wheel_vel_msg);
_right_wheel_vel_pub.publish(right_wheel_vel_msg);
}
void AbotHardwareInterface::updateJointsFromHardware(const ros::Duration& period) {
double delta_left_wheel = _left_wheel_angle - _joints[0].position - _joints[0].position_offset;
double delta_right_wheel = _right_wheel_angle - _joints[1].position - _joints[1].position_offset;
if (std::abs(delta_left_wheel) < 1) {
_joints[0].position += delta_left_wheel;
_joints[0].velocity = delta_left_wheel / period.toSec();
} else {
_joints[0].position_offset += delta_left_wheel;
}
if (std::abs(delta_right_wheel) < 1) {
_joints[1].position += delta_right_wheel;
_joints[1].velocity = delta_right_wheel / period.toSec();
} else {
_joints[1].position_offset += delta_right_wheel;
}
}
void AbotHardwareInterface::registerControlInterfaces() {
ros::V_string joint_names = boost::assign::list_of("left_wheel_to_base")("right_wheel_to_base");
for (unsigned int i = 0; i < joint_names.size(); i++) {
hardware_interface::JointStateHandle joint_state_handle(joint_names[i], &_joints[i].position, &_joints[i].velocity, &_joints[i].effort);
_joint_state_interface.registerHandle(joint_state_handle);
hardware_interface::JointHandle joint_handle(joint_state_handle, &_joints[i].velocity_command);
_velocity_joint_interface.registerHandle(joint_handle);
}
registerInterface(&_joint_state_interface);
registerInterface(&_velocity_joint_interface);
}
void AbotHardwareInterface::leftWheelAngleCallback(const std_msgs::Float64& msg) {
_left_wheel_angle = msg.data;
}
void AbotHardwareInterface::rightWheelAngleCallback(const std_msgs::Float64& msg) {
_right_wheel_angle = msg.data;
}
void AbotHardwareInterface::limitDifferentialSpeed(double& diff_speed_left_side, double& diff_speed_right_side) {
double large_speed = std::max(std::abs(diff_speed_left_side), std::abs(diff_speed_right_side));
if (large_speed > _max_wheel_angular_speed) {
diff_speed_left_side *= _max_wheel_angular_speed / large_speed;
diff_speed_right_side *= _max_wheel_angular_speed / large_speed;
}
}
#endif // ABOT_HARDWARE_INTERFACE_H_
Весь этот процесс можно описать классической теорией управления. У нас есть контроллер типа closed-loop. На входе контроллера — вектора скорости движения робота на плоскости (X, Y). На выходе — требуемая скорость вращения колёс для заданной скорости робота. В качестве обратной связи используются текущая скорость вращения колёс и одометрия.
Нода robot_base
Теперь создадим ноду robot_base
, которая запустит наш контроллер замкнутого цикла и свяжет ROS с драйверами. Назовём ноду abot_base
.
Нода будет принимать два параметра ROS:
control_frequency
— частота работы контроллера.max_wheel_angular_speed
— максимальная угловая скрость вращения колеса. Этот параметр нужен, чтобы случайно не отправить на драйвер колеса слишком большую скорость вращения.
Контроллер срабатывает по таймеру control_loop
типа ros::Timer
с частотой control_frequency
.
В пакете abot_base
создаём новый файл abot_base.cpp
:
#include <chrono>
#include <functional>
#include <ros/callback_queue.h>
#include "abot_hardware_interface.h"
typedef boost::chrono::steady_clock time_source;
void controlLoop(AbotHardwareInterface& hardware, controller_manager::ControllerManager& cm, time_source::time_point& last_time) {
time_source::time_point this_time = time_source::now();
boost::chrono::duration<double> elapsed_duration = this_time - last_time;
ros::Duration elapsed(elapsed_duration.count());
last_time = this_time;
hardware.updateJointsFromHardware(elapsed);
cm.update(ros::Time::now(), elapsed);
hardware.writeCommandsToHardware();
}
int main(int argc, char** argv) {
ros::init(argc, argv, "abot_base");
ros::NodeHandle node;
ros::NodeHandle private_node("~");
int control_frequency;
double max_wheel_angular_speed;
private_node.param<int>("control_frequency", control_frequency, 1);
private_node.param<double>("max_wheel_angular_speed", max_wheel_angular_speed, 1.0);
AbotHardwareInterface hardware(node, private_node, max_wheel_angular_speed);
controller_manager::ControllerManager cm(&hardware, node);
ros::CallbackQueue abot_queue;
ros::AsyncSpinner abot_spinner(1, &abot_queue);
time_source::time_point last_time = time_source::now();
ros::TimerOptions control_timer(
ros::Duration(1 / control_frequency),
boost::bind(controlLoop, std::ref(hardware), std::ref(cm), std::ref(last_time)), &abot_queue);
ros::Timer control_loop = node.createTimer(control_timer);
abot_spinner.start();
ros::spin();
return 0;
}
Не забываем пересобирать наш проект при добавлении в него новых исходных файлов C++:
catkin_make
Запуск ноды robot_base
Создадим файл запуска для новой ноды. В пакете abot_base
делаем папку launch
, а в ней новый файл запуска abot_base.launch
:
<launch>
<node name="abot_base_node" pkg="abot_base" type="abot_base_node" output="screen" >
<param name="control_frequency" type="int" value="100" />
<param name="max_wheel_angular_speed" type="double" value="18.0" />
</node>
</launch>
Тестируем движение робота
Пришло время проверить движение нашего робота. У нас есть всё необходимое для этого.
Давайте создадим новый файл запуска для тестов движения робота. Назовём файл bringup.launch
. Разместим его в пакете с описанием робота abot_description
.
Это важно! Данный файл запуска — один из главных, и с этого момента мы будем запускать его только на Raspberry Pi. Ноды ROS, запущенные этим файлом, должны работать только на борту робота!
В создаваемом файле запуска мы:
- Загрузим URDF-описание робота на параметрический сервер ROS.
- Запустим драйверы робота.
- Запустим контроллеры робота.
- Запустим ноду
abot_base
.
<launch>
<param name="robot_description" command="$(find xacro)/xacro '$(find abot_description)/urdf/abot.xacro' --inorder" />
<node name="robot_state_publisher" pkg="robot_state_publisher" type="robot_state_publisher" respawn="false" output="screen" />
<include file="$(find abot_base)/launch/abot_base.launch" />
<include file="$(find abot_control)/launch/abot_control.launch" />
<include file="$(find abot_driver)/launch/abot_drivers.launch" />
</launch>
Для теста управлять роботом будем с настольного компьютера через сеть ROS. Убедитесь, что и робот, и настольный компьютер подключены к сети.
На настольном компьютере запускаем ядро ROS:
roscore
Затем на Raspberry запускаем файл bringup.launch
.
cd ~/ros
su root
source devel/setup.bash
roslaunch abot_description bringup.launch
На настольном компьютере проверим список топиков ROS:
rostopic list
Как видите, у нас появилось много новых топиков.
Самые интересные — топики, созданные нашим контроллером движения дифференциального привода mobile_abot
: /mobile_abot/cmd_vel
и /mobile_abot/odom
. Ради этих топиков мы и создавали контроллеры.
Топик /mobile_abot/cmd_vel
принимает сообщения типа geometry_msgs/Twist
. В него мы отправляем желаемые скорости движения робота.
Топик /mobile_abot/odom
отдаёт нам сообщения типа nav_msgs/Odometry
, которые содержат рассчитанную контроллером одометрию робота, его положение и ориентацию в пространстве.
Сейчас граф наших ROS-нод и топиков выглядит так:
Попробуем порулить роботом.
Для рулёжки будем использовать уже привычную утилиту rqt
и плагин rqt_robot_steering
. Этот плагин специально создан для управления двухколёсным дифференциальным приводом.
На настольном комьютере в новом терминале запустим rqt
. В окне rqt
выбираем «Plugins → Robot Tools → Robot Steering». Откроется окно со слайдерами, используемыми для рулёжки.
В верхней части окна нужно написать имя нашего топика для задания скоростей — /mobile_abot/cmd_vel
.
На краях слайдеров есть поля, куда нужно установить максимальные и минимальные значения скоростей робота. Мы их уже знаем. Их можно взять из файла конфигурации контроллера — abot_controllers.yaml
.
Начнём двигать слайдеры и следить за движением робота.
Визуализация одометрии
Мы проверили работу контроллера в одном направлении — отправили желаемые скорости робота в топик /mobile_abot/cmd_vel
.
Теперь проверим, как работает наш контроллер в обратном направлении — визуализируем одометрию робота.
В пакете abot_description
в папке launch
создадим новый файл запуска для визуализации одометрии. Назовем его display_movement.launch
.
Визуализировать одометрию будем с помощью уже знакомого нам rviz
. В файле запуска запустим ноду rviz
с новым файлом настройки визуализации abot_movement.rviz
.
Новый файл настроек abot_movement.rviz
отличается от прежнего abot_model.rviz
тем, что параметр Fixed Frame
из меню Global Options
теперь имеет значение odom
.
<launch>
<arg name="rvizconfig" default="$(find abot_description)/rviz/abot_movement.rviz" />
<arg name="model" default="$(find abot_description)/urdf/abot.xacro" />
<param name="robot_description" command="$(find xacro)/xacro --inorder $(arg model)" />
<node name="rviz" pkg="rviz" type="rviz" args="-d $(arg rvizconfig)" required="false" />
</launch>
Запускаем все файлы запуска, как в прошлой главе. Если вы вдруг их закрыли, то запускаем на RPi главный файл запуска bringup.launch
, а на настольном компьютере rqt
с плагином rqt_robot_steering
.
Запускаем на настольном компьютере визуализацию одометрии:
cd ~/ros
source devel/setup.bash
roslaunch abot_description display_movement.launch
Откроется окно rviz
с новыми настройками. Попробуйте порулить роботом из rqt_robot_steering
и наблюдайте за визуализацией в rviz
.
Вот она, магия инфраструктуры ROS! Если вы всё сделали верно, то любое реальное движение робота будет регистрироваться и визуализироваться в rviz
! Одна ячейка сетки (grid
) у нас равна 10 см. Мы можем даже не смотреть на реального робота (или даже находиться в километре от него), но мы будем знать, где он находится и куда смотрит.
Изначально при запуске bringup.launch
робот находится в точке с координатами (0, 0, 0) в глобальной системе координат. В этих же координатах находится базовая точка одометрии — odom
. Когда мы начинаем рулить роботом, мы уезжаем от этой базовой точки. В процессе движения rviz
рисует нам вектор до базовой точки, тем самым показывая, как далеко уехал робот с начала работы программы.
Эту визуализацию мы получили, используя лишь простую колёсную одометрию робота — два инкрементных энкодера. Представьте, как точно можно отслеживать робота, если использовать более точные сенсоры и алгоритмы.
Тем не менее, колёсная одометрия не слишком точна. Со временем данные одометрии начинают «плавать» (odometry drift), и в ней накапливаются ошибки. Точность одометрии достигается настройкой драйверов робота и параметров контроллера движения.
Точность колёсной одометрии легко проверить. Можно нарисовать под роботом крестик на стартовой позиции, обозначив таким образом координату (0, 0, 0) в реальном мире. Затем как следует порулить роботом: поделать развороты, повороты, разгоны и торможения. Спустя некоторое время нужно сравнить вектор смещения робота относительно базовой точки odom
в rviz
с реальным смещением робота относительно крестика на полу. Если эти смещения более-менее совпадают — робот смотрит в одну и ту же сторону, находится на примерно одинаковом расстоянии от координаты (0, 0, 0) — то одометрию можно считать точной.
Продолжение следует
На этом пока всё! В следующей части проекта мы сделаем дистанционное управление для робота и научим его автономной навигации в помещении.
Читать далее >
Что мне потребуется?
В первую очередь — микроконтроллер. Он станет мозгом будущего робота. Можно сказать, что микроконтроллер — это крошечный компьютер, размещенный на одной микросхеме. У него есть процессор, оперативная и постоянная память и даже периферийные устройства: интерфейсы ввода и вывода данных, различные таймеры, передатчики, приспособления, которые инициируют работу двигателей. Набор устройств зависит от конкретной модели. Именно микроконтроллер будет получать информацию от внешнего мира через датчики движения, фотокамеры и прочие приспособления, анализировать ее и побуждать робота совершать в ответ какие-то действия.
Микроконтроллер нужно будет установить на печатную плату, запитать его, подсоединить все необходимые устройства (датчики, лампочки, двигатели), а еще собрать из подручных материалов корпус робота. Все детали, которые для этого нужны, можно купить в любом магазине радиотехники.
Если у вас нет профильного образования или опытного наставника, который подробно объяснит, что именно и в какой последовательности устанавливать (и как пользоваться паяльником!), готовьтесь к долгой и кропотливой работе: придется перерыть интернет вдоль и поперек и испортить множество деталей.
Я в ужасе и собираюсь передумать. Нельзя попроще?
Можно. Специально для тех, кто никогда не держал в руках паяльник, но очень хочет попробовать себя в робототехнике, существуют специальные наборы-конструкторы, позволяющие сделать всё то же самое, но быстрее. Самый известный и популярный — Arduino. Его главное преимущество в том, что это не просто игрушка, а целая экосистема: множество обучающих материалов и инструкций, видеокурсы, огромное пользовательское комьюнити — можно задать любой вопрос от новичкового до самого продвинутого. Есть и другие платформы — например, совсем простой конструктор Mindstorms от Lego.
Составы наборов могут быть очень разными, но в каждом есть готовая печатная плата с уже установленным микроконтроллером и всеми дополнительными деталями, которые нужны для решения простых типовых задач. Обычно плату можно напрямую подключить к компьютеру через USB. А дальше среда разработки от производителя поможет сразу же сделать первые шаги в программировании роботов. Например, заставить мигать лампочку на плате.
Теперь всё зависит от вас. Можно, используя готовую плату, реализовать собственный несложный проект: например, сделать машинку, которая движется и останавливается по команде, или гирлянду для новогодней елки. Можно купить набор, уже включающий в себя всё, что нужно для постройки робота определенного типа, и потренироваться на нем. Плюс этого варианта: все детали в наборе подготовлены так, чтобы вы могли соединить их без паяльника или других инструментов.
Участники программы «Робототехника», которую поддерживает фонд Олега Дерипаски «Вольное дело», уже через несколько лет обучения могут создавать куда более сложные проекты и придумывают инженерные решения для самых разных задач. Например, разрабатывают системы для автоматической сортировки мусора. Если вам кажется, что всё это очень сложно, вы правы, но научиться этому может каждый.
У меня в школе была двойка по физике, и вообще я гуманитарий. Мне нужно что-то выучить, прежде чем приступать?
Штурмовать учебники необязательно. Конечно, школьная физика пригодилась бы, но если вы ее забыли, не переживайте — вспомните по ходу дела. Для начала просто погуглите, что такое ток, сопротивление, закон Ома, конденсатор, транзистор — пара десятков статей дадут вам базовые представления о радиотехнике, и этого хватит на первое время. Позже вы легко найдете в сети всю информацию, которая вам понадобится. И усвоите ее на практике — гораздо лучше, чем из учебника.
А программировать надо уметь?
Если умеете, создание первого робота окажется, возможно, даже слишком легким делом. Если не умеете — отличный повод научиться. Дело в том, что программирование робота — штука очень наглядная: вот вы написали код и сразу же загорелась лампочка. Вам нужно, чтобы ваш робот разворачивался, когда до стены осталось меньше 5 см, значит, в программе надо прописать такое условие, всё логично. Именно поэтому детей часто начинают учить программированию на примере робототехники: здесь вместо скучных абстракций сразу получается осязаемый результат в реальном мире. На этом принципе строится обучение по программе «Робототехника». Все участники сразу же могут применить полученные знания на практике.
Перед первой попыткой написать программу для робота достаточно разобраться, что такое цикл и условный оператор. Для тех, кому и это кажется слишком сложным, производители конструкторов часто предусматривают визуальные редакторы: там код вообще не нужно писать, всё настраивается перетаскиванием блоков мышкой. Конечно, никакого сложного функционала так не напрограммируешь, но это уже начало. Позже, если захотите заниматься робототехникой углубленно, полезно будет освоить язык С, который чаще всего используется в этой сфере.
Меня случайно не убьет током?
Нет. Если, конечно, вы не собираетесь начать сразу с постройки огромных промышленных роботов. Пока речь идет о небольших игрушках, сила тока и напряжение настолько малы, что даже если вас и ударит, то вы ничего не почувствуете. Самое страшное, что можно сотворить, конструируя маленького робота, — небольшой взрыв на столе. Но это случится, только если вы перепутаете «плюс» с «минусом» во время работы с электролитическим конденсатором.
Самая вероятная неприятность — некоторое количество испорченных деталей, которые вы попытаетесь подсоединить не так и не туда. Но переживать не стоит: все необходимые расходники недороги, а их поломка тоже важная часть обучения.
Мой робот сможет защитить меня от врагов? Ну или хотя бы тапочки принести?
Самый первый — вряд ли. Точнее, нет ничего невозможного, но для начала лучше поставить перед собой цель попроще. Например, на базе того же Arduino можно собрать самых разных движущихся роботов: они могут ездить просто вперед-назад, по сложной заданной траектории или по нарисованной линии. Робот, который самостоятельно объезжает препятствия или как-то еще меняет свое поведение при приближении к разным объектам, тоже посильная задача. Еще первый робот вполне сможет включать и выключать что-нибудь, ориентируясь на уровень освещенности, совершать какие-то действия в определенный момент, заданный таймером, или по нажатию кнопки.
Ну а в будущем, если продолжите заниматься робототехникой, сможете сделать и робота-помощника, и робота-охранника, который умеет стрелять. Еще можно собрать робот-пылесос собственной модели. В России и в мире постоянно проводятся соревнования по робототехнике, на которых роботы-участники сражаются, играют в футбол, участвуют в гонках и просто демонстрируют свои выдающиеся способности. Например, на «РобоФесте», который ежегодно организует фонд «Вольное дело», можно увидеть сотни разных роботов.
Умение собирать роботов как-то пригодится мне в жизни?
Да, еще как. Вы неизбежно научитесь программировать. Причем будете в состоянии не просто писать код, который что-то как-то делает, но и понимать всю цепочку, по которой набранные вами на клавиатуре символы преобразуются в действия целого механизма. Уметь программировать в наше время почти так же полезно, как знать английский язык: пригодится, даже если вы маркетолог или продавец мороженого.
Знание робототехники при желании позволит вам здорово усовершенствовать быт и даже сделать свое жилище «умным», не покупая дорогих готовых решений. Световой будильник? Запросто. Лампы с датчиками движения? Да легко. Чайник, который начинает кипятиться, получив СМС, и передает кондиционеру сигнал охлаждать, а пылесосу пылесосить? Не так легко, но вполне реализуемо.
Где можно узнать больше о роботах?
Можно пройти один из многочисленных, в том числе совершенно бесплатных онлайн-курсов. Можно выбрать курс, посвященный Arduino, — как, например, этот от МФТИ, или начать с Lego. А можно не привязываться к конкретной платформе и учиться робототехнике в целом — например, на этом курсе от Бауманки. Ну а если вы знаете английский, буквально вся Coursera с программами по робототехнике от ведущих мировых университетов к вашим услугам.
А если мне понравится и я захочу сделать это своей профессией? Куда податься?
Самое очевидное решение — в программисты. Причем не обязательно туда, где работают непосредственно с «железом»: навыки, полученные во время занятий робототехникой, пригодятся в любой сфере — от промышленного до веб-программирования.
Если возникнет желание связать свою дальнейшую судьбу именно с роботами, придется получить соответствующее высшее образование. Специальность «Робототехника и мехатроника» уже появилась во многих технических вузах — в Москве это МГТУ имени Баумана, МИФИ, МЭИ, МИРЭА. Подойдет и факультет радиотехники: большинство нынешних специалистов по робототехнике получали именно такое образование.
Специальный проект Фонда «Вольное дело» Дерипаски и Журнала «Нож»
Содержание
- Изготовление робота
- Создание каркаса
- Материалы
- Использовать существующие коммерческие продукты
- Основной строительный материал
- Плоский материал для конструкции
- Лазерная резка, изогнутый пластик или металл
- 3D-печать
- Полиморф
- Изготовление робота
- Сборка компонентов робота
- Подключение двигателей к контроллерам двигателей
- Подключение аккумуляторов к контроллеру двигателя или к микроконтроллеру
- Техника безопасности при работе с аккумуляторами
- Подключение контроллеров двигателя к микроконтроллеру
- Подключение датчиков к микроконтроллеру
- Устройство связи с микроконтроллером
- Колеса для двигателей
- Электрические компоненты для рамы
- Практическая часть
- Механические детали набора 45544
Изготовление робота
Теперь, вы выбрали все основные компоненты для сборки робота. Изготовление робота начинается со следующего шага — необходимо спроектировать и построить основание или каркас. Каркас держит их всех вместе и придает вашему роботу законченный вид и форму.
Создание каркаса
Нет никакого «идеального» способа создания каркаса. Почти всегда требуется компромисс. Возможно, вам нужен легкий каркас. Но может потребоваться использование дорогостоящих материалов или слишком хрупких материалов.
Вы можете захотеть сделать надежное или большое шасси. Хоты вы понимаете, что это будет дорого, тяжело или сложно в производстве. Ваш «идеальный» каркас или рама может быть очень сложным.Изготовление каркаса робота может потребовать слишком много времени для разработки и создания.
При этом простой каркас может быть не менее хорошим. Идеальная форма встречается редко, но некоторые проекты могут выглядеть более элегантно из-за своей простоты. Возможно другие проекты могут привлечь внимание из-за их сложности.
Материалы
Существует много материалов, которые вы можете использовать для создания основания. Вы используете все множество материалов для создания не только роботов, но и других устройств. Следовательно вы получите хорошее представление о том, что наиболее подходит для данного проекта.
Список предлагаемых строительных материалов, приведенных ниже включает только наиболее распространенные. Как только вы используете некоторые из них, вы сможете поэкспериментировать с теми, которые не входят в список, или объединить их вместе.
Использовать существующие коммерческие продукты
Вероятно, вы видели школьные проекты, которые были основаны на существующих массовых продуктах. В первую очередь таких как бутылки, картонные коробки и т.д. Это, по сути, «повторное использование» продукта.
Оно может либо сэкономить вам много времени и денег. Хотя и может создать дополнительные хлопоты и головную боль. Есть много очень хороших примеров того, как перепрофилировать материалы и сделать из них очень хорошего робота.
Основной строительный материал
Например, изготовление робота из картона. Некоторые из самых основных строительных материалов могут быть использованы для создания отличных каркасов. Одним из самых дешевых и наиболее доступных материалов является картон. Вы часто можете найти картон бесплатно, и его можно легко вырезать, согнуть, склеить и сложить.
Может быть вы можете создать усиленную картонную коробку, которая выглядит намного более красиво. И она соответствует размеру вашего робота. Затем вы можете нанести эпоксидную смолу или клей, чтобы сделать ее более долговечной. В заключение дополнительно можно разукрасить ее.
Плоский материал для конструкции
Один из наиболее распространенных способов сделать раму – это использовать стандартные материалы, такие как лист фанеры, пластика или металла. И просверлить отверстия для подключения всех исполнительных механизмов и электроники. Прочный кусок фанеры может быть довольно толстым и тяжелы. В то самое время как тонкий лист металла может быть слишком гибким.
Например, доску или фанеру из плотной древесины можно легко разрезать с помощью пилы, просверлить (не опасаясь разрушения), покрасить, отшлифовать и т.д. Следовательно вы можете устанавливать устройства с двух сторон. Например, подключить двигатели и колесики колес к нижней части, а электронику и аккумулятор к верхней части. При этом древесина останется неподвижной и твердой.
Лазерная резка, изогнутый пластик или металл
Если вы находитесь на том этапе, когда вам необходим внешний блок, то лучшим вариантом будет высокоточная резка деталей лазером. Любая ошибка в расчетах будет дорогостоящей и приведет к порче материалов. Для изготовления робота нужна собственная мастерская. Возможно нужно найти компанию, производящую такой тип роботов. Может быть она предлагает множество других услуг, включая работы с металлом и покраску.
3D-печать
3D принтер, печатающий раму или каркас, редко бывает наиболее обоснованным решением (потому что он печатает послойно). В результате этого процесса можно создавать очень сложные формы. Такие формы было бы невозможно (или очень сложно) изготовить другими способами.
Отдельная трехмерная печатная деталь может содержать все необходимые монтажные точки для всех электрических и механических компонентов. При этом способе изготовления каркаса сохраняется незначительный вес изделия. Изготовление робота потребует дополнительной обработки и шлифовки.
Поскольку 3D-печать становится более популярной, цена на детали также снижается. Дополнительно преимуществом 3D-печати является не только то, что ваш дизайн легко воспроизводить, но и им легко делиться. При помощи нескольких кликов мышки можно получить все инструкции по дизайну и файлы САПР.
Полиморф
При комнатной температуре полиморф является твердым пластиком. При нагревании (например, в горячей воде) он становится податливым и может быть сформирован в сложные детали. Затем они охлаждаются и затвердевают в прочные пластмассовые детали.
Обычно пластиковые детали требуют высоких температур и необходимы различные формы для изготовления. Изготовление робота таким способом делает их недоступными для большинства любителей. Например, вы можете комбинировать различные формы (цилиндры, плоские листы и т.д.).
Так формируются сложные пластмассовые структуры, которые выглядят как сделанные промышленным способом. Вы также можете экспериментировать с различными формами и достичь с помощью этого материала многого.
Изготовление робота
Конструирование и изготовление робота нужно производить с учетом выбранных материалов и методов. Выполните следующие шаги, чтобы создать эстетичную, простую и структурно обоснованную раму робота меньшего размера.
- Сначала нужно сделать прототип конструкции, выполненный из бумаги, картона или металла.
- Получите все комплектующие, которые потребуются для изготовления робота (электрические и механические), и измерьте их.
- Если у вас нет всех ваших деталей под рукой, вы можете обратиться к размерам, предоставленным производителем.
- Проведите мозговой штурм и набросайте несколько разных конструкций каркаса в общих чертах. Не делайте это слишком подробно.
- После того, как вы выбрали дизайн, убедитесь что компоненты будут хорошо поддерживаться.
- Нарисуйте каждую часть вашего робота в бумаге или картоне со шкалой 1:1 (реальный размер). Вы также можете нарисовать их с помощью программного обеспечения САПР и распечатать их.
- Протестируйте свой дизайн в САПР и в реальной жизни с помощью прототипа бумаги, проверив каждую деталь и соединения.
- Если вы абсолютно уверены, что ваш дизайн правильный, наконец начните изготавливать каркас из выбранных материалов. Помните, дважды измерьте и вырежьте только один раз!
- Перед монтажом рамы проверьте соответствие каждого компонента и, если потребуются, модифицируйте его.
- Соберите свою раму, используя горячий клей, винты, гвозди или любые другие соединения, которые вы выбирали для изготовления своего робота.
- Установите все компоненты на каркас. Так вы только что создали робота с нуля!
Сборка компонентов робота, из приведенного выше списка заслуживает отдельного рассмотрения.
Сборка компонентов робота
На предыдущих уроках вы выбрали электрические компоненты и исполнительные механизмы. Теперь вам нужно, чтобы они все работали вместе. Как всегда, техническое описание и руководства — это ваши друзья, когда вы понимаете, как должно работать ваше роботизированное оборудование.
Подключение двигателей к контроллерам двигателей
Электродвигатель постоянного тока или линейный привод постоянного тока, скорее всего, имеют два провода: красный и черный. Подключите красный провод к клемме M + на контроллере двигателя постоянного тока, а черный — к M-.
Реверсирование проводов приведет только к вращению двигателя в противоположном направлении. У сервомотора, есть три провода: один черный (GND), красный (от 4,8 до 6 В) и желтый (сигнал положения). Контроллер серводвигателя имеет контакты, соответствующие этим проводам, поэтому сервопривод может быть подключен непосредственно к нему.
Подключение аккумуляторов к контроллеру двигателя или к микроконтроллеру
Изготовление робота включает в себя подключение электропитания. Большинство контроллеров моторов имеют две винтовые клеммы для проводов батареи, обозначенных как B + и B-. Если ваша батарея поставляется с разъемом, а ваш контроллер использует винтовые клеммы, вы можете найти разъем для соединения с проводами.
Провода вы можете подключить к винтовому соединению. Хотя вам может потребоваться найти другой способ подключения аккумулятора к контроллеру двигателя.Возможно, что не все электромеханические устройства, которые вы выбрали для своего робота, могут работать при одинаковом напряжении.
Следовательно, могут потребоваться несколько цепей управления батареями или напряжением. Ниже приведены обычные уровни напряжения, используемые в общих компонентах роботизированных платформ:
- электродвигатели постоянного тока — от 3 до 24 В
- стандартные серводвигатели — от 4,8 В до 6 В
- специальные сервомоторы — от 7,4 до 12 В
- шаговые двигатели — от 6 до 12 В
- микроконтроллеры обычно включают регуляторы напряжения — от 3 до 12 В
- датчики — 3,3 В, 5 В и 12 В
- контроллеры постоянного тока — от 3 до 48 В
- стандартные батареи: 3.7V, 4.8V, 6V, 7.4V, 9V, 11.1V и 12V.
Если вы создаёте робота с двигателями постоянного тока, микроконтроллером и, возможно, сервомеханизмом или двумя, то можно легко понять, что одна батарея не может напрямую управлять всем. Прежде всего, мы рекомендуем выбрать батарею, к которой можно напрямую подключать как можно больше устройств.
Батарея с наибольшей емкостью должна быть связана с приводными двигателями. Например, если выбранные вами двигатели рассчитаны на номинальное напряжение 12 В, то ваша основная батарея также должна быть 12 В. Дополнительно вы можете использовать регулятор для питания микроконтроллера на 5 В.
Техника безопасности при работе с аккумуляторами
Внимание: аккумуляторные батареи являются мощными устройствами и могут легко сжечь ваши цепи, если они подключены неправильно. Прежде всегда тройная проверка правильной полярности и возможности работы устройства с энергией, обеспечиваемой батареей.
Если вы не уверены, не «догадывайтесь». Электричество намного быстрее, чем вы, и к тому времени, когда вы поймете, что что-то не так, волшебный синий дым уже пойдет от вашего устройства.
Подключение контроллеров двигателя к микроконтроллеру
Микроконтроллер может взаимодействовать с контроллерами двигателя различными способами:
- Стандартный: контроллер имеет два контакта с маркировкой Rx (прием) и Tx (передача). Подключите контакт Rx контроллера двигателя к выходу Tx микроконтроллера и наоборот.
- I2C: контроллер двигателя будет иметь четыре контакта: SDA, SCL, V, GND. Ваш микроконтроллер будет иметь те же четыре контакта, но не обязательно помеченные. Просто подключите их один к одному.
- PWM (Pulse-width modulation): контроллер двигателя будет иметь как вход ШИМ, так и цифровой вход для каждого двигателя. Подключите входной контакт PWM контроллера двигателя к выходному контакту ШИМ на микроконтроллере. Соедините каждый цифровой входной контакт контроллера двигателя с цифровым выходным выводом на микроконтроллере.
- R / C: Чтобы подключить микроконтроллер к контроллеру двигателя R / C, вам необходимо подключить сигнальный контакт к цифровому выходу на микроконтроллере.
Независимо от способа связи логика контроллера двигателя и микроконтроллер должны совместно использовать один и тот же опорный сигнал заземления. Это достигается путем соединения контактов GND (земля) вместе.
В первую очередь нужно соединить контакты одного и того же логического высокого уровня. Этого можно добиться, используя тот же вывод V+ для питания оба устройства. Переключатель логического уровня требуется, если устройства не используют одни и те же логические уровни (например, 3.3V и 5V)
Подключение датчиков к микроконтроллеру
При изготовлении робота обязательно используются сенсорные устройстве -в первую очередь датчики. Датчики могут быть сопряжены с микроконтроллерами аналогично контроллерам двигателя. Датчики (сенсоры) могут использовать следующие типы связи:
- Цифровой: датчик имеет цифровой вывод сигнала, который подключается непосредственно к цифровому выходу микроконтроллера. Простой переключатель можно рассматривать как цифровой датчик.
- Аналоговый: аналоговые датчики производят аналоговый сигнал напряжения, который должен считываться аналоговым выводом. Если ваш микроконтроллер не имеет аналоговых контактов, вам понадобится отдельная аналого-цифровая схема (АЦП). Кроме того, некоторые датчики с требуемой схемой питания обычно имеют три контакта: V+, GND и Signal. Например, если датчик представляет собой простой переменный резистор, вам потребуется создать делитель напряжения для считывания полученного переменного напряжения.
- Стандартный или I2C: здесь применяются те же принципы связи, которые описаны для контроллеров двигателей.
Устройство связи с микроконтроллером
Большинство коммуникационных устройств (например, XBee, Bluetooth) используют последовательную связь. Следовательно требуются те же соединения RX, TX, GND и V+. Важно отметить, что, хотя несколько последовательных подключений могут использоваться совместно на одних и тех же выводах RX и TX, для предотвращения перекрестных помех, ошибок и сбоев в целом требуется надежное управление.
Если у вас очень мало последовательных устройств, часто бывает проще использовать один последовательный порт для каждого из них.
Колеса для двигателей
В идеале, вы выбрали колеса или звездочки, которые предназначены для установки на вал вашего электродвигателя. Возможно, вам придется подгонять отверстия для соединения двигателей, рулевого управления и различных проводов в одну конструкцию.
Электрические компоненты для рамы
При изготовлении робота вы можете смонтировать электронные компоненты на раме робота при помощи множества методов. Прежде всего убедитесь в том, что ваши крепления надежны. Основные методы креплений включают в себя: винты, гайки, двухсторонний скотч, липучки, клей, стяжки и т. д.
Практическая часть
В нашем случае мы будем использовать набор Lego EV3 и для создания каркаса робота нам потребуются только стандартные детали, которые уже входят в состав набора. Изготовление робота на основе набора Лего является прежде всего относительно несложным и достаточно быстрым.
Механические детали набора 45544
Download Article
Download Article
Do you want to learn how to build your own robot? There are a lot of different types of robots that you can make by yourself. Most people want to see a robot perform the simple tasks of moving from point A to point B. You can make a robot completely from analog components or buy a starter kit from scratch! Building your own robot is a great way to teach yourself both electronics as well as computer programming.
-
1
Gather your components. To build a basic robot, you’ll need several simple components. You can find most, if not all, of these components at your local electronics hobby shop, or several online retailers. In fact, some basic kits may include all of these components as well. This robot does not require any soldering:
- Arduino Uno (or other microcontroller)[1]
- 2 continuous rotation servos
- 2 wheels that fit the servos
- 1 caster roller
- 1 small solderless breadboard (look for a breadboard that has two positive and negative lines on each side)
- 1 distance sensor (with four-pin connector cable)
- 1 mini push-button switch
- 1 10kΩ resistor
- 1 USB A to B cable
- 1 set of breakaway headers
- 1 6 x AA battery holder with 9V DC power jack
- 1 pack of jumper wires or 22-gauge hook-up wire
- Strong double-sided tape or hot glue
- Arduino Uno (or other microcontroller)[1]
-
2
Flip the battery pack over so that the flat back is facing up. You’ll be building the robot’s body using the battery pack as a base.
Advertisement
-
3
Align the two servos on the end of the battery pack. This should be the end that the battery pack’s wire is coming out of The servos should be touching bottoms, and the rotating mechanisms of each should be facing out the sides of the battery pack. The servos must be properly aligned so that the wheels go straight. The wires for the servos should be coming off the back of the battery pack.
-
4
Affix the servos with your tape or glue.[2]
Make sure that they are solidly attached to the battery pack. The backs of the servos should be aligned flush with the back of the battery pack.- The servos should now be taking up the back half of the battery pack.
-
5
Affix the breadboard perpendicularly on the open space on the battery pack. It should hang over the front of the battery pack just a little bit and will extend beyond each side. Make sure that it is securely fastened before proceeding. The “A” row should be closest to the servos.
-
6
Attach the Arduino microcontroller to the tops of the servos. If you attached the servos properly, there should be a flat space made by them touching. Stick the Arduino board onto this flat space so that the Arduino’s USB and Power connectors are facing the back (away from the breadboard). The front of the Arduino should be just barely overlapping the breadboard.
-
7
Put the wheels on the servos. Firmly press the wheels onto the rotating mechanism of the servo. This may require a significant amount of force, as the wheels are designed to fit as tightly as possible for the best traction.
-
8
Attach the caster to the bottom of the breadboard. If you flip the chassis over, you should see a bit of breadboard extending past the battery pack. Attach the caster to this extended piece, using risers if necessary. The caster acts as the front wheel, allowing the robot to easily turn in any direction.[3]
- If you bought a kit, the caster may have come with a few risers that you can use to ensure the caster reaches the ground. i
Advertisement
-
1
Break off two 3-pin headers. You’ll be using these to connect the servos to the breadboard. Push the pins down through the header so that the pins come out at an equal distance on both sides.
-
2
Insert the two headers into pins 1-3 and 6-8 on row E of the breadboard. Make sure that they are firmly inserted.[4]
-
3
Connect the servo cables to the headers, with the black cable on the left side (pins 1 and 6). This will connect the servos to the breadboard. Make sure the left servo is connected to the left header and the right servo to the right header.
-
4
Connect red jumper wires from pins C2 and C7 to red (positive) rail pins. Make sure you use the red rail on the back of the breadboard (closer to the rest of the chassis).
-
5
Connect black jumper wires from pins B1 and B6 to blue (ground) rail pins. Make sure that you use the blue rail on the back of the breadboard. Do not plug them into the red rail pins.
-
6
Connect white jumper wires from pins 12 and 13 on the Arduino to A3 and A8. This will allow the Arduino to control the servos and turn the wheels.
-
7
Attach the sensor to the front of the breadboard. It does not get plugged into the outer power rails on the breadboard, but instead into the first row of lettered pins (J). Make sure you place it in the exact center, with an equal number of pins available on each side.
-
8
Connect a black jumper wire from pin I14 to the first available blue rail pin on the left of the sensor. This will ground the sensor.
-
9
Connect a red jumper wire from pin I17 to the first available red rail pin to the right of the sensor. This will power the sensor.
-
10
Connect white jumper wires from pin I15 to pin 9 on the Arduino, and from I16 to pin 8. This will feed information from the sensor to the microcontroller.
Advertisement
-
1
Flip the robot on its side so that you can see the batteries in the pack. Orient it so that the battery pack cable is coming out to the left at the bottom.
-
2
Connect a red wire to the second spring from the left on the bottom. Make sure that the battery pack is oriented correctly.
-
3
Connect a black wire to the last spring on the bottom-right. These two cables will help provide the correct voltage to the Arduino.
-
4
Connect the red and black wires to the far-right red and blue pins on the back of the breadboard. The black cable should be plugged into the blue rail pin at pin 30. The red cable should be plugged into the red rail pin at pin 30.
-
5
Connect a black wire from the GND pin on the Arduino to the back blue rail. Connect it at pin 28 on the blue rail.
-
6
Connect a black wire from the back blue rail to the front blue rail at pin 29 for each. Do not connect the red rails, as you will likely damage the Arduino.
-
7
Connect a red wire from the front red rail at pin 30 to the 5V pin on the Arduino. This will provide power to the Arduino.
-
8
Insert the push button switch in the gap between rows on pins 24-26. This switch will allow you to turn off the robot without having to unplug the power.
-
9
Connect a red wire from H24 to the red rail in the next available pin to the right of the sensor. This will power the button.
-
10
Use the resistor to connect H26 to the blue rail. Connect it to the pin directly next to the black wire that you connected a few steps ago.
-
11
Connect a white wire from G26 to pin 2 on the Arduino. This will allow the Arduino to register the push button.
Advertisement
-
1
Download and extract the Arduino IDE. This is the Arduino development environment and allows you to program instructions that you can then upload to your Arduino microcontroller. You can download it for free from arduino.cc/en/main/software. Unzip the downloaded file by double-clicking it and move the folder inside to an easy to access location. You won’t be actually installing the program. Instead, you’ll just run it from the extracted folder by double-clicking arduino.exe.[5]
-
2
Connect the battery pack to the Arduino. Plug the battery back jack into the connector on the Arduino to give it power.
-
3
Plug the Arduino into your computer via USB. Windows will likely not recognize the device.
-
4
Press .⊞ Win+R and type devmgmt.msc. This will launch the Device Manager.
-
5
Right-click on the “Unknown device” in the “Other devices” section and select “Update Driver Software.” If you don’t see this option, click “Properties” instead, select the “Driver” tab, and then click “Update Driver.”
-
6
Select “Browse my computer for driver software.” This will allow you to select the driver that came with the Arduino IDE.
-
7
Click “Browse” then navigate to the folder that you extracted earlier. You’ll find a “drivers” folder inside.
-
8
Select the “drivers” folder and click “OK.” Confirm that you want to proceed if you’re warned about unknown software.
Advertisement
-
1
Start the Arduino IDE by double-clicking the arduino.exe file in the IDE folder. You’ll be greeted with a blank project.
-
2
Paste the following code to make your robot go straight. The code below will make your Arduino continuously move forward.
#include <Servo.h> // this adds the "Servo" library to the program // the following creates two servo objects Servo leftMotor; Servo rightMotor; void setup() { leftMotor.attach(12); // if you accidentally switched up the pins for your servos, you can swap the numbers here rightMotor.attach(13); } void loop() { leftMotor.write(180); // with continuous rotation, 180 tells the servo to move at full speed "forward." rightMotor. write(0); // if both of these are at 180, the robot will go in a circle because the servos are flipped. "0," tells it to move full speed "backward." }
-
3
Build and upload the program. Click the right arrow button in the upper-left corner to build and upload the program to the connected Arduino.
- You may want to lift the robot off of the surface, as it will just continue to move forward once the program is uploaded.
-
4
Add the kill switch functionality. Add the following code to the “void loop()” section of your code to enable the kill switch, above the “write()” functions.
if(digitalRead(2) == HIGH) // this registers when the button is pressed on pin 2 of the Arduino { while(1) { leftMotor.write(90); // "90" is neutral position for the servos, which tells them to stop turning rightMotor.write(90); } }
-
5
Upload and test your code. With the kill switch code added, you can upload and test the robot. It should continue to drive forward until you press the switch, at which point it will stop moving. The full code should look like this:
#include <Servo.h> // the following creates two servo objects Servo leftMotor; Servo rightMotor; void setup() { leftMotor.attach(12); rightMotor.attach(13); } void loop() { if(digitalRead(2) == HIGH) { while(1) { leftMotor.write(90); rightMotor.write(90); } } leftMotor.write(180); rightMotor.write(0); }
Advertisement
-
1
Follow an example. The following code will use the sensor attached to the robot to make it turn to the left whenever it encounters an obstacle. See the comments in the code for details about what each part does. The code below is the entire program.
#include <Servo.h> Servo leftMotor; Servo rightMotor; const int serialPeriod = 250; // this limits output to the console to once every 1/4 second unsigned long timeSerialDelay = 0; const int loopPeriod = 20; // this sets how often the sensor takes a reading to 20ms, which is a frequency of 50Hz unsigned long timeLoopDelay = 0; // this assigns the TRIG and ECHO functions to the pins on the Arduino. Make adjustments to the numbers here if you connected differently const int ultrasonic2TrigPin = 8; const int ultrasonic2EchoPin = 9; int ultrasonic2Distance; int ultrasonic2Duration; // this defines the two possible states for the robot: driving forward or turning left #define DRIVE_FORWARD 0 #define TURN_LEFT 1 int state = DRIVE_FORWARD; // 0 = drive forward (DEFAULT), 1 = turn left void setup() { Serial.begin(9600); // these sensor pin configurations pinMode(ultrasonic2TrigPin, OUTPUT); pinMode(ultrasonic2EchoPin, INPUT); // this assigns the motors to the Arduino pins leftMotor.attach(12); rightMotor.attach(13); } void loop() { if(digitalRead(2) == HIGH) // this detects the kill switch { while(1) { leftMotor.write(90); rightMotor.write(90); } } debugOutput(); // this prints debugging messages to the serial console if(millis() - timeLoopDelay >= loopPeriod) { readUltrasonicSensors(); // this instructs the sensor to read and store the measured distances stateMachine(); timeLoopDelay = millis(); } } void stateMachine() { if(state == DRIVE_FORWARD) // if no obstacles detected { if(ultrasonic2Distance > 6 || ultrasonic2Distance < 0) // if there's nothing in front of the robot. ultrasonicDistance will be negative for some ultrasonics if there is no obstacle { // drive forward rightMotor.write(180); leftMotor.write(0); } else // if there's an object in front of us { state = TURN_LEFT; } } else if(state == TURN_LEFT) // if an obstacle is detected, turn left { unsigned long timeToTurnLeft = 500; // it takes around .5 seconds to turn 90 degrees. You may need to adjust this if your wheels are a different size than the example unsigned long turnStartTime = millis(); // save the time that we started turning while((millis()-turnStartTime) < timeToTurnLeft) // stay in this loop until timeToTurnLeft has elapsed { // turn left, remember that when both are set to "180" it will turn. rightMotor.write(180); leftMotor.write(180); } state = DRIVE_FORWARD; } } void readUltrasonicSensors() { // this is for ultrasonic 2. You may need to change these commands if you use a different sensor. digitalWrite(ultrasonic2TrigPin, HIGH); delayMicroseconds(10); // keeps the trig pin high for at least 10 microseconds digitalWrite(ultrasonic2TrigPin, LOW); ultrasonic2Duration = pulseIn(ultrasonic2EchoPin, HIGH); ultrasonic2Distance = (ultrasonic2Duration/2)/29; } // the following is for debugging errors in the console. void debugOutput() { if((millis() - timeSerialDelay) > serialPeriod) { Serial.print("ultrasonic2Distance: "); Serial.print(ultrasonic2Distance); Serial.print("cm"); Serial.println(); timeSerialDelay = millis(); } }
Add New Question
-
Question
How much time will it take to build a robot?
This depends on the complexity of the robot. You could build a simple robot in as little as a day. A more complex robot could take several several months.
-
Question
What is the device called that you need to move robotic hands, arms and legs?
Servos (servomotors) are usually used to move robotic arms. Regular servos have a limited circulation.
-
Question
Why do I need to download the Arduino software?
Arduino code is very simplified and based off of C++. Other micro controllers are also available that use other software.
See more answers
Ask a Question
200 characters left
Include your email address to get a message when this question is answered.
Submit
Advertisement
Thanks for submitting a tip for review!
References
About This Article
Article SummaryX
To build a simple robot that can move on its own, purchase a starter kit, or assemble the components you need from an electronics supplier. You’ll need a microcontroller, the small computer that will serve as your robot’s brain; a pair of continuous rotation servos to drive the wheels; wheels that fit the servos; a caster roller; a small solderless breadboard for building your circuits; a battery holder; a distance sensor; a push button switch, and jumper wires. Affix the servos to the end of the battery pack with double-sided tape or hot glue, making sure the the rotating ends of the servos are on the long sides of the battery pack. Attach the breadboard to the battery pack next to the servos with the tape or hot glue. Place the microcontroller on the flat space on top of the servos and affix firmly there. Press the wheels firmly onto the spindles of the servos. Attach the caster to the front of the breadboard. The caster spins freely, and acts as the front wheel of the robot, making it easy to turn and roll in any direction. Plug the distance sensor to the front of your breadboard. Wire up your robot, connecting the servos, microcontroller, switch and battery pack to your breadboard. Connect your microcontroller to a computer via a USB cable. Upload a basic control program from your computer to the microcontroller. This robot can go forward, backward, stop, and turn away from obstacles. Test your robot on a smooth flat surface, and experiment to see what you can make it do. For more tips, including how to use Arduino software, read on!
Did this summary help you?
Thanks to all authors for creating a page that has been read 1,697,829 times.
Reader Success Stories
-
“The fact that you actually give a list of supplies is great. This is the first website that I found that actually…” more