Главная chevron_right Наши уроки chevron_right Arduino chevron_right Робот "Микроша", управляемый с помощью геймпада DualShock 2

Робот "Микроша", управляемый с помощью геймпада DualShock 2

Собираем робота «Микроша» на Arduino и управляем им с геймпада DualShock 2: левый стик задаёт направление движения, кнопки «Круг» и «Квадрат» управляют светодиодами

Видео-обложка запускается при наведении

Общие сведения:

В этом уроке мы создадим робота, который будем управлять с помощью удобного геймпада.

Изменяя положение левого стика (джойстика), робот будет менять направление движения, а нажатие на кнопки "КРУГ" или "КВАДРАТ" в правой части геймпада будет включать или выключать светодиодные модули.

Видео:

Нам понадобится:

Для реализации проекта нам необходимо установить библиотеки:

  • PsxNewLib— для работы с геймпадом;
  • DigitalIO - зависимость библиотеки PsxNewLib

О том, как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.

Описание работы робота:

После подачи питания робот готов к работе.

Нажимая на рукоятку джойстика вы выбираете направление движения робота.

Нажимая кнопки на правой части геймпада, вы включаете/выключаете светодиоды.

Схема сборки:

Arduino / Piranha UNO:

Battery Shield:

Установите Battery Shield на Arduino / Piranha UNO: Во время установки Battery Shield должен быть в выключенном состоянии.

Motor Shield:

На Battery Shield установите Motor Shield

Светодиоды:

Подключите светодиоды к Motor Shield

Мотор-редукторы:

Подключите мотор-редукторы к Motor Shield

Крепёж 1:

Закрепите на ПВХ-пластине шаровую опору и мотор-редукторы

Крепёж 2:

Закрепите на ПВХ-конструкторе светодиоды и приёмник геймпада (с помощью стяжки)

Геймпад:

Подключите приёмник геймпада к Motor Shield

Вывод приёмникаВывод Arduino
GNDGND
VCC
DAT (MISO)12
CMD (MOSI)11
CLK (SCK)13
ATT (CS)10

Код программы (скетч):

#include <PsxControllerHwSpi.h>

const uint8_t  PS2_attention      = 10;                                                                           // Вывод Arduino, к которому подключен вывод приёмника геймпада ATT
PsxControllerHwSpi<PS2_attention> psx;                                                                            // Создаём объект "ps2x"

uint8_t  RAW_UP_OR_DOWN     = 0;                                                                                  // переменная "сырых" значений с джойстика по оси Оу
uint8_t  RAW_LEFT_OR_RIGHT  = 0;                                                                                  // переменная "сырых" значений с джойстика по оси Ох
uint8_t  GO_LEFT            = 0;                                                                                  // переменная для хранения отсортированных значений по оси Ох для движения влево
uint8_t  GO_RIGHT           = 0;                                                                                  // переменная для хранения отсортированных значений по оси Ох для движения вправо
uint8_t  GO_UP              = 0;                                                                                  // переменная для хранения отсортированных значений по оси Оу для движения вверх
uint8_t  GO_DOWN            = 0;                                                                                  // переменная для хранения отсортированных значений по оси Оу для движения вниз

uint8_t  pinShield_H2       = 4;                                                                                  // Вывод, подключенный к драйверу, для задания направления вращения левым мотором
uint8_t  pinShield_E2       = 5;                                                                                  // Вывод ШИМ, подключенный к драйверу, для задания скорости левого мотора
uint8_t  pinShield_E1       = 6;                                                                                  // Вывод ШИМ, подключенный к драйверу, для задания скорости правого мотора
uint8_t  pinShield_H1       = 7;                                                                                  // Вывод, подключенный к драйверу, для задания направления вращения правым мотором
uint8_t  valSpeed           = 0;                                                                                  // Максимальная скорость ШИМ (число от 0 до 255)
uint8_t  led_right          = 2;
uint8_t  led_left           = 3;
bool     arrRoute[2]        = {0, 0};                                                                             // Направление движения для каждого мотора ([0]- правый мотор, [1] - левый мотор)
uint16_t arrSpeed[2];                                                                                             // Скорость для каждого мотора ([0]- правый мотор, [1] - левый мотор)
uint8_t  flg;                                                                                                     // Флаг направлений движения машинки
bool     flg_led_right = false;                                                                                   // Флаг работы светодиода красного цвета
bool     flg_led_left  = false;                                                                                   // Флаг работы светодиода синего цвета

// ГЕЙМПАД

// Функция конфигурирования контроллера
void configureGamepad(bool& configured)
{
        if (psx.begin()) {
                // входим врежим конфигурирования
                if (!psx.enterConfigMode())
                        return;
                // если удалосль войти
                else {
                        // включаем аналоговые джойстики
                        psx.enableAnalogSticks();
                        // выходим из режима конфигурации
                        psx.exitConfigMode();
                        // устанавливаем флаг конфигурации
                        configured = true;
                }
        }
}

// Чтение контроллера
void readGamepad(bool& configured)
{
        // если не удалось прочитать - сбрасываем флаг конфигурации
        if (!psx.read())
                configured = false;
}

// Обработка контроллера
void handleGamepad()
{
        static bool configured = false;
        // если не сконфигурирован - конфигурируем, передавая флаг
        if (!configured) {
                configureGamepad(configured);
        }
        // если сконфигурирован - читаем, передавая флаг
        else {
                readGamepad(configured);
        }
}

void setup()
{
  // МОТОРЫ
  pinMode(pinShield_H2, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_H2 как выход (направление вращения левого мотора)
  pinMode(pinShield_E2, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_E2 как выход (скорость вращения левого мотора, ШИМ)
  pinMode(pinShield_E1, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_E1 как выход (скорость вращения правого мотора, ШИМ)
  pinMode(pinShield_H1, OUTPUT);                                                                                  // Конфигурируем вывод pinShield_H1 как выход (направление вращения правого мотора)
  // СВЕТОДИОДЫ
  pinMode(led_right,OUTPUT);
  pinMode(led_left, OUTPUT);
}

void loop()
{
        // обработка геймпада
        handleGamepad();

        // Считываем "сырое" аналоговое значение с левого джойстика
        psx.getLeftAnalog(RAW_LEFT_OR_RIGHT, RAW_UP_OR_DOWN);
        // Если была нажата кнопка "КРУГ", то включаем красный светодоид
        if (psx.buttonPressed(PSB_CIRCLE)) {
                flg_led_right = !flg_led_right;
                digitalWrite(led_right, flg_led_right);
        }
        // Если была нажата кнопка "КВАДРАТ", то включаем синий светодоид
        if (psx.buttonPressed(PSB_SQUARE)) {
                flg_led_left  = !flg_led_left;
                digitalWrite(led_left,  flg_led_left );
        }

        if (RAW_UP_OR_DOWN <=120)   {GO_UP     = map(RAW_UP_OR_DOWN,       0,   120, 255,   0); GO_DOWN  = 0;} else             // Если значение по оси Оу меньше 120, значит джойстик сдвинут вверх, переопределяем диапазон значений с джойстика (переворачиваем его)
                if(RAW_UP_OR_DOWN >=135)   {GO_DOWN   = map(RAW_UP_OR_DOWN,     135,   255,   0, 255); GO_UP    = 0;} else      // Если значение по оси Оу больше 135, значит джойстик сдвинут вниз, переопределяем диапазон значений с джойстика
                {GO_DOWN = 0; GO_UP = 0;}                                                                                       // Если значение находится в диапазоне от 120 до 135 - значит джойстик находится в центральном положении
        if (RAW_LEFT_OR_RIGHT <=120){GO_LEFT   = map(RAW_LEFT_OR_RIGHT,    0,   120, 255,   0); GO_RIGHT = 0;} else             // Если значение по оси Ох меньше 120, значит джойстик сдвинут влево, переопределяем диапазон значений с джойстика (переворачиваем его)
                if(RAW_LEFT_OR_RIGHT >=135){GO_RIGHT  = map(RAW_LEFT_OR_RIGHT,  135,   255,   0, 255); GO_LEFT  = 0;} else      // Если значение по оси Оу больше 135, значит джойстик сдвинут вправо, переопределяем диапазон значений с джойстика
                {GO_LEFT = 0; GO_RIGHT = 0;}                                                                                    // Если значение находится в диапазоне от 120 до 135 - значит джойстик находится в центральном положении
        if (GO_UP){                                                                                                             // Если джойстик был сдвинут вверх, то
                if(!GO_LEFT || !GO_RIGHT) {flg = 2; valSpeed = GO_UP;}    else                                                  // проверяем, сдвинут ли он вправо/влево, и если нет, то устанавливаем флаг движения на север
                if(GO_LEFT)               {flg = 1; valSpeed = GO_UP;}    else                                                  // если джойстик сдвинут влево, то устанавливаем флаг движения на северо-запад
                if(GO_RIGHT)              {flg = 3; valSpeed = GO_UP;}}   else                                                  // если джойстик сдвинут вправо, то устанавливаем флаг движения на северо-восток
                if(GO_DOWN){                                                                                                    // если джойстик сдвинут вниз, то
                if(!GO_LEFT || !GO_RIGHT) {flg = 8; valSpeed = GO_DOWN;}  else                                                  // проверяем, сдвинут ли он вправо/влево, и если нет, то устанавливаем флаг движения на юг
                if(GO_LEFT)               {flg = 7; valSpeed = GO_DOWN;}  else                                                  // если джойстик сдвинут влево, то устанавливаем флаг движения на юго-запад
                if(GO_RIGHT)              {flg = 9; valSpeed = GO_DOWN;}} else                                                  // если джойстик сдвинут вправо, то устанавливаем флаг движения на юго-восток
                if(GO_LEFT)                 {flg = 4; valSpeed = GO_LEFT;}  else                                                // Проверяем, сдвинут ли джойстик только влево, и если да, то устанавливаем флаг движения на запад
                if(GO_RIGHT)                {flg = 6; valSpeed = GO_RIGHT;} else                                                // Проверяем, сдвинут ли джойстик только вправо, и если да, то устанавливаем флаг движения на восток
                                                                                {flg = 5;}                                      // Если джойстик не был сдвинут ни в одну из указанных выше сторон, то устанавливаем флаг остановки
        switch (flg) {// Направ. лев. м.     Направ. прав. м.        Скорость лев. м.                 Скорость прав. м.
                case 1:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = (valSpeed / 2);    arrSpeed[0] = valSpeed;         break;      // С-З
                case 2:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = valSpeed;          arrSpeed[0] = valSpeed;         break;      // С
                case 3:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = valSpeed;          arrSpeed[0] = (valSpeed / 2);   break;      // С-В
                case 4:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = 0;                 arrSpeed[0] = valSpeed;         break;      // З
                case 5:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = 0;                 arrSpeed[0] = 0;                break;      // Стоп
                case 6:       arrRoute[1] = 0;     arrRoute[0] = 0;        arrSpeed[1] = valSpeed;          arrSpeed[0] = 0;                break;      // В
                case 7:       arrRoute[1] = 1;     arrRoute[0] = 1;        arrSpeed[1] = (valSpeed / 2);    arrSpeed[0] = valSpeed;         break;      // Ю-З
                case 8:       arrRoute[1] = 1;     arrRoute[0] = 1;        arrSpeed[1] = valSpeed;          arrSpeed[0] = valSpeed;         break;      // Ю
                case 9:       arrRoute[1] = 1;     arrRoute[0] = 1;        arrSpeed[1] = valSpeed;          arrSpeed[0] = (valSpeed / 2);   break;      // Ю-В
        }
        digitalWrite(pinShield_H2, arrRoute[1]);                                                                         // тогда задаем направление вращения правого мотора
        digitalWrite(pinShield_H1, arrRoute[0]);                                                                         // и левого мотора
        analogWrite(pinShield_E2, arrSpeed[1]);                                                                          // Задаём скорость вращения для правого мотора
        analogWrite(pinShield_E1, arrSpeed[0]);                                                                          // и для левого мотора
        delay(50);                                                                                                       // Задержка 50мс
}

Алгоритм работы скетча:

До кода void setup() определяются переменные, подключаются необходимые библиотеки.

В коде void setup() инициализируется геймпад, настраивается режим работы выводов для моторов и светодиодов.

Код void loop() делится на несколько частей:

  • Опрашивается геймпадСчитывается аналоговое значение с джойстика по оси ОуСчитывается аналоговое значение с джойстика по оси ОхОпрашиваются кнопки "Квадрат" и "Круг"Если они были нажаты, то состояние на выходах меняется на противоположное
  • Если на джойстиках были изменения положения, то:
  • Проверяем оси:Ось Y: Проверяем, было ли изменение в пределах от 0 до 120 - если да, то тогда робот движется вперёд;Проверяем, было ли изменение в пределах от 135 до 255 - если да, то тогда робот движется назад;Ось X:Проверяем, было ли изменение в пределах от 0 до 120 - если да, то тогда робот движется влево;Проверяем, было ли изменение в пределах от 135 до 255 - если да, то тогда робот движется вправо;

Проверяем, какое направление движения было определено и согласно ему задаём значение для флага flg;

В зависимости от флага flg устанавливаем направление вращения и скорость вращения колёс;

Устанавливаем на выводах Piranha Uno аналоговое значение для скорости вращения и цифровое значение для направления вращения.

Ставим задержку 50мс для того, чтобы геймпад опрашивался с некоторой задержкой.

Ссылки:

Комплект для повторения проекта

Что понадобится

Это быстрый ориентир по основным модулям и деталям. Полный список для повторения проекта смотрите в самой статье.

14 позиций
Геймпад DualShock от PS2 геймпада Открыть в iArduino Светодиод Сube - красный (Trema-модуль) Квадратный светодиодный светодиод 15х15 мм, позволит создать необычный дизайн устройства Открыть в iArduino Arduino Uno R3 (USB ATmega16U2) Платформа c микроконтроллером ATmega328P на ядре AVR с чипом USB ATmega16U2 для разработки электронных устройств на языке C++ Открыть в iArduino Piranha Uno R3 Прямой аналог Arduino Uno c микроконтроллером ATmega328P на ядре AVR для разработки электронных устройств на языке C++ Открыть в iArduino Battery Shield (Li-po, 2300 мА·ч) Battery Shield Открыть в iArduino Motor Shield, 2 канала (до 10.8В, 2А) Позволяющая управлять моторами с напряжением 3-10,8V и током до 2А Открыть в iArduino Мотор-редуктор 1:75, 200rpm Миниатюрный 12 мм мотор-редуктор для различных робототехнических проектов Открыть в iArduino Крепеж для мотора N20 Крепеж для мотора Открыть в iArduino Колесо 34 х 7, на вал 3мм, тип D Колесо Открыть в iArduino Шаровая опора (20 мм) Шаровая опора Открыть в iArduino Светодиод Сube - синий (Trema-модуль) Квадратный светодиодный светодиод 15х15 мм, позволит создать необычный дизайн устройства Открыть в iArduino Конструктор ПВХ Чёрный «Крепления Arduino» Набор деталей из решетчатого конструктора для крепления плат форм-фактора Arduino Открыть в iArduino Конструктор ПВХ Чёрный «Пластины L» Набор больших конструктивных деталей из решетчатого конструктора Открыть в iArduino Соединительные силиконовые провода «мама-мама» (20 шт. / 20 см) Пучок из 20 мягких силиконовых проводов «мама-мама» длиной 20 см для прототипирования электронных устройств Открыть в iArduino
Все уроки Arduino