Управление движением робота с одного джойстика

Содержание

Коды проекта

Ниже вы можете скачать или скопировать необходимые скетчи для данного урока. Далее я объясню все основные моменты программирования проекта. Также перед началом работы нужно скачать и установить необходимые библиотеки:

Теперь переходим непосредственно к коду проекта.

Теперь нужно передать значения роботу через радио модуль и неплохо было бы вывести их в терминал. Воспользуемся функциями sendData и printData :

Осталось только вызвать функции в loop :

Переходим к роботу.

Тут нам нужно будет написать алгоритм «дешифровки значений». Для его работы понадобится функция управления двигателями.

Вначале программы объявим пины на которые подключен драйвер двигателей (если вы таковой используете). Константно зададим границу скорости (середину максимального значения типа byte ). И напишем функцию управления двигателями.

Она принимает на вход 4 параметра:

1 – вращение вперед, 0 – назад. Скорость в диапазоне от 0 до 255. Как известно, ток течет от высокого потенциала к низкому, поэтому если подать на первый пин высокий сигнал, а на второй низкий, ток пойдет от первого ко второму и двигатель начнет вращаться в соответствующую сторону. Вместо постоянного высокого сигнала можно отправлять ШИМ сигнал, тогда мы сможем контролировать еще и скорость вращения, так и сделаем. Если принятое направление 1 (вперед), то на первый пин подадим низкий сигнал, а на второй ШИМ-ом принятую скорость, если направление 0, то наоборот. Для другого двигателя аналогично. Пропишем это в функции Motor.

Теперь можно писать сам алгоритм «дешифровки».

Напоследок еще перспектива оптимизации. У каждого двигателя есть минимальные значения тока и напряжения, при которых он сможет работать, то есть двигатель робота будет вращаться только тогда, когда значения ШИМа превышают какой-то порог значений, назовем его «мертвым» интервалом. Этот порог в наибольшей степени зависит от мощности источника питания и самого двигателя, обычно он имеет значение от 30 до 70.

Поскольку двигатель в промежутке этих значений не работает, можно ими воспользоваться для кодирования чего-либо еще. При самом малом пороге в 30, мы получим 30 позиций кодировки с каждого двигателя, при комбинировании позиций с 2-х двигателей уже 900! Если от вашего робота не требуется мгновенная скорость отклика и количество отправляемых пакетов велико (>300-500), то можно потратить до 20% пакетов на отправку дополнительных кодов в «мертвых» интервалах, и вы этого не заметите.

Чем больше пакетов за 1 секунду получает робот, тем большее их количество в процентном соотношении можно потратить на отправку кодов, но чем больше их теряется, тем больший процент стоит оставлять для пакетов скорости, если вы хотите своевременного отклика. Для вылавливания этих пакетов нужно будет просто проверить, что значения скорости меньше заданной вами границы, а как обрабатывать их дальше (через математические вычисления, if или switch-case конструкции) и для чего применять подумайте сами.

Как отследить текущее направление джойстика?

Очень полезный кусок кода. На основании значений положений X и Y мы можем определить, находится ли джойстик по центу или он смещен в одном из восьми направлений (вверх, вправо-вверх, вправо, вправо-вниз, вниз, влево-вниз, влево, влево-вверх).

Так как значения в каждом из направлений будет в диапазоне от 0 до 1023, можно предположить, что центр будет находиться в диапазоне 511-512. Но это не совсем так. Настолько точно текущее значение мы не получим. И если мы определим неверное значение, можем получить информацию о движении джойстика, хотя он стоял по центру и не двигался.

Для этого мы введем диапазон значений и будем считать, что любое значение в этом диапазоне будет считаться центром:

|—|—|—|

0 505 515 1023

Этот диапазон не является «истиной последней инстанции». Вам надо его подстроить под ваш джойстик, так. Эти значения вводятся в код в виде констант:

const int X_THRESHOLD_LOW = 505;

const int X_THRESHOLD_HIGH = 515;

const int Y_THRESHOLD_LOW = 500;

const int Y_THRESHOLD_HIGH = 510;

Теперь мы преобразуем каждую координату из диапазона от 0 до 1023 в диапазон от -1 до 1. Для координаты Х – 1 значит перемещение влево, 0 означает отсутствие перемещения, а 1 – перемещение вправо. Для направления Y -1 означает перемещение вниз, 0 означает отсутствие перемещения, а 1 – перемещение вверх.

Мы начнем с установки значения в каждом направлении 0 («центр»). После этого мы используем выражения if/else для проверки, принимает ли значение положения в любом из направлений большее или меньшее значение чем наш диапазон:

x_direction = 0;

y_direction = 0;

x_position = analogRead(PIN_ANALOG_X);

y_position = analogRead(PIN_ANALOG_Y);

if (x_position > X_THRESHOLD_HIGH) {

x_direction = 1;

} else if (x_position

x_direction = -1;

}

if (y_position > Y_THRESHOLD_HIGH) {

y_direction = 1;

} else if (y_position

y_direction = -1;

}

В Arduino IDE есть функция map(), которую, теоретически могли бы использовать вместо if/else, но в данном случае метод усложняется из-за вопросов центрирования, так что применять здесь map мы не будем.

В примере, который представлен ниже, вы увидите, что в дальнейшем if/else используются для отображения направления – вы можете спокойно изменить этот пример под ваши задачи:

const byte PIN_ANALOG_X = 0;

const byte PIN_ANALOG_Y = 1;

const int X_THRESHOLD_LOW = 505;

const int X_THRESHOLD_HIGH = 515;

const int Y_THRESHOLD_LOW = 500;

const int Y_THRESHOLD_HIGH = 510;

int x_position;

int y_position;

int x_direction;

int y_direction;

void setup() {

Serial.begin(9600);

}

void loop () {

x_direction = 0;

y_direction = 0;

x_position = analogRead(PIN_ANALOG_X);

y_position = analogRead(PIN_ANALOG_Y);

if (x_position > X_THRESHOLD_HIGH) {

x_direction = 1;

} else if (x_position

x_direction = -1;

}

if (y_position > Y_THRESHOLD_HIGH) {

y_direction = 1;

} else if (y_position

y_direction = -1;

}

if (x_direction == -1) {

if (y_direction == -1) {

Serial.println(«left-down»);

} else if (y_direction == 0) {

Serial.println(«left»);

} else {

// y_direction == 1

Serial.println(«left-up»);

}

} else if (x_direction == 0) {

if (y_direction == -1) {

Serial.println(«down»);

} else if (y_direction == 0) {

Serial.println(«centered»);

} else {

// y_direction == 1

Serial.println(«up»);

}

} else {

// x_direction == 1

if (y_direction == -1) {

Serial.println(«right-down»);

} else if (y_direction == 0) {

Serial.println(«right»);

} else {

// y_direction == 1

Serial.println(«right-up»);

}

}

Принцип действия

Его составной частью будет алгоритм «архивации» скорости.

Для начала рассмотрим принцип движения нашего робота (немного известной вам теории). Его ходовая часть состоит из 4-х мотор-редукторов (по одному на каждое колесо). Поэтому движение происходит следующим образом:

Вперед. Все 4 двигателя вращаются вперед с равной скоростью.

Назад. Все 4 двигателя вращаются назад с равной скоростью.

Плавный поворот направо. Правые двигатели вращаются медленнее левых. Но все 4 вращаются в одну сторону.

Плавный поворот налево. Левые двигатели вращаются медленнее правых. Но все 4 вращаются в одну сторону.

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

Резкий поворот налево. Правые двигатели вращаются вперед, левые двигатели вращаются назад. Если все 4 вращаются с одной скоростью, то робот развернется на месте.

Таким образом, для управления движением робота, то есть подачей управляющего сигнала на драйвер двигателя (можно рассматривать это как прямое подключение, представив, что Ардуино способно отдавать со своих пинов большой ток) нам необходимо знать направление вращения и модуль скорости для правых и левых двигателей. Будем хранить их в соответствующих переменных speed_right и speed_left .

Учимся управлять серводвигателями через джойстики при помощи Arduino

Процесс сборки самоделки:

Шаг первый. Подключаем серводвигателиПроцесс сборки самоделки начинается с подключения серводвигателей. Для того чтобы собрать предварительный макет, применяется монтажная плата. Потом можно будет сделать отдельный шилд. На рисунке можно увидеть, как именно все подключается.

1. На модуле джойстика можно найти выходы U/R+ и L/R+. Через эти выходы происходит подключение питания. Соответственно сюда нужно подать напряжение +5V от соответствующего пина на Arduino.

2. Еще на джойстике присутствует два разъема под названием L/R и два разъема U/D. Их нужно подключить к аналоговым выходам А3 и А4.

3. Ну и в заключении землю на джойстике нужно соединить с землей на Arduino.

После сборки подключение нужно перепроверить. Именно из-за ошибок подключения в большинстве случаев возникают проблемы. Особенно это касается случаев, когда используется монтажная плата и на ней находится много подключений.

Шаг третий. Скетч для ArduinoКод очень простой и в нем присутствуют подробные комментарии. Приведенный код нужно просто скопировать в Arduino IDE. После того как код будет загружен, двигатели не должны двигаться. Они должны начинать двигаться только при нажатии кнопки на джойстике.

Проблемы, которые могут возникнуть и способы их решения1. Если двигатели не включаются, нужно перепроверить подключение. Для подключения двигателей используются выходы типа ШИМ, а для подключения джойстиков применяются аналоговые выходы.

2. Бывает такое, что сразу после загрузки кода двигатели начинают вибрировать. Такое бывает если неправильно подключить пины U/D+ L/R+. Подключение нужно тщательно проверить. Чтобы не сжечь плату во время проверки, ее нужно обязательно отключить от компьютера.

3. Если все перепроверено, но двигатели все равно не хотят работать, можно попробовать переподключить джойстик. Его нужно снять с монтажной платы, а затем установить назад с некоторым усилием. Коннекторы джойстика должны хорошо войти в макетку.

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

Подключаем серводвигатели

Начнем с подключения двух серводвигателей (для предварительного макета используется монтажная плата. В последствии можно сваять отдельный шилд).

На рисунке ниже представлена исчерпывающая информация про подключение.

Красный кабель двигателей (питание) — к пину 5V на Arduino

Черный кабель двигателей (земля) — к пину GND на Arduino

Желтый кабель от двигателя Right & Left (на некоторых моделях он белого цвета) — к пину 11.

Желтый кабель от двигателя Up & Down (на некоторых моделях он белого цвета) — к пину 4.

Не забывайте, что коннекторы управляющего сигнала на сервоприводах должны подключаться к ШИМ выходам Arduino.

Подрулевое управление: за и против

Подрулевое управление магнитолой – это своеобразная опция, предложенная владельцам Рено в качестве варианта доступа к функциям медиасистемы. Аналогичные приспособления как рулевая панель – уже привычный девайс. В данном случае он реализован как периферийное устройство. Опытные автомобилисты владельцы Renault говорят о том, что внешние кнопки для проигрывания музыки действительно очень удобны.


Джойстик

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

Данная опция доступна для машин Renault Лагуна, Лачетти. Само устройство производится фирмой Pioneer, что уже является гарантией качества и продуманной эргономики.


Джойстик

Преимущества, которые дает переключатель подрулевой управления магнитолой:

  • удобная и недорогая установка при выполнении функции дистанционного доступа;
  • невысокая стоимость девайса;
  • простая конструкция и схема работы;
  • через время действие становится рефлекторным, что очень удобно и позволяет отказаться от дистанционного доступа.

Нужно сказать, что джойстики довольно популярны у производителей. В продаже можно встретить различные приспособления для тюнинга. Например, переключатель для зеркала, навигации, бортового компьютера. Данные опции часто в входят в пакеты расширения вместе с навигаторами, автомобильным ТВ, музыкальным чейнджером и другими устройствами.


Джойстик установленный под рулем

Идея заимствована из спортивных автомобилей и концепт-каров, где реализуется так называемый «интуитивный» дизайн. Данные приспособления Pioneer отлично подходят для автомобилистов, любящих экстремальное вождение, и людей, совершающих на автомобиле дальние путешествия.

Алгоритм

Исходя из вышеописанного составляем алгоритм, преобразующий показания горизонтальной и вертикальной осей джойстика в две «вертикальные» оси скорости двигателей.

Вот его принцип:

Вводим следующие константы: — verticalSticCenter – значения, соответствующие центральному положению джойстика в вертикальной оси (средняя скорость робота равна ); — horizontalSticPin – значения, соответствующие центральному положению джойстика в горизонтальной оси (скорость разворота равна ) — k – коэффициент отклонения, от центральных положений, позволяет исключить влияния «шумов» на показания в положении полной остановки робота) Считываем показания вертикальной оси потенциометра в переменную speed , хранящую значение средней скорости. Считанные значения нужно перевести в диапазон типа byte , разделив на 4 (можно использовать функцию map ), в таком случае средняя скорость будет изменяться от 255 до 0. Затем вычесть из них значение verticalSticCenter , теперь средняя скорость изменяется от 128 до -128

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

Считываем значения горизонтальной оси в переменную horizontal_value так же приведя к диапазону byte , будем называть их скоростью поворота. Если значения скорости поворота отклонились более, чем на k от центра в право, то скорость левого двигателя приравниваем к сумме средней скорости и величине отклонения в угловой скорости от условного 0 (вычитаем значения скорости поворота из значений ее центра), а скорость правого к их разности. Если значения скорости поворота отклонились более, чем на k от центра влево, то скорость левого двигателя приравниваем к разности средней скорости и величине отклонения от условного центра (из скорости поворота вычитаем значения условного 0), а правого к их сумме. Не выполнение условий пунктов 5 и 6 говорит о том, что значения скорости поворота лежат в пределах центра (условного 0), в этом случае приравниваем скорости обоих двигателей к средней скорости. Теперь необходимо ограничить полученные значения скоростей, что бы по модулю они не выходили за половину максимального значения типа byte для предотвращения переполнения переменных в пункте 9. Просто сравним значения с константно-объявленной границей, и если они больше, то приравняем их к ней. Сейчас скорости двигателей лежат в диапазоне от -128 до 128, но мы хотим хранить их в массиве типа byte , который не может хранить отрицательные числа, поэтому «зашифруем их»: — элемент массива data будет хранить скорость левого двигателя, а 1 скорость второго. — Если скорость будет отрицательной, мы положим в data ее модуль, а если положительной, то прибавим к ней половину диапазона значений типа byte . Таким образом значения от 0 до 128 хранят скорость вращения назад, а значения от 128 до 255 вперед.

Скетч Arduino — использование библиотеки AccelStepper

Управление шаговым двигателем без библиотеки идеально подходит для простых приложений с одним двигателем. Но если вы хотите управлять несколькими шаговыми двигателями, то вам понадобится библиотека.

Итак, для нашего следующего эксперимента мы будем использовать расширенную библиотеку шаговых двигателей под названием AccelStepper library. Она поддерживает:

  • Ускорение и замедление.
  • Одновременное управление несколькими шаговыми двигателями с независимым шагом для каждого двигателя.

Эта библиотека не включена в IDE Arduino, поэтому вам необходимо сначала установить ее.

Установка библиотеки

Чтобы установить библиотеку, перейдите в Эскиз> Include Library> Manage Libraries… Подождите, пока диспетчер библиотек загрузит индекс библиотек и обновит список установленных библиотек.

Отфильтруйте результаты поиска, набрав «Accelstepper». Щелкните первую запись и выберите «Установить».

Скетч Arduino

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

Пояснение к скетчу:

Мы начинаем с подключения недавно установленной библиотеки AccelStepper.

Определяем выводы Arduino, к которым подключаются выводы STEP и DIR A4988. Устанавливаем motorInterfaceType значение 1. (1 означает внешний шаговый драйвер с выводами Step и Direction).

Затем мы создаем экземпляр библиотеки с именем myStepper.

В функции setup() мы сначала устанавливаем максимальную скорость двигателя 1000. Затем мы устанавливаем коэффициент ускорения для двигателя, чтобы добавить ускорение и замедление к движениям шагового двигателя.

Затем мы устанавливаем обычную скорость 200 и количество шагов, например, 200 (поскольку NEMA 17 совершает 200 шагов за оборот).

В функции loop() мы используем оператор If, чтобы проверить, как далеко двигателю нужно проехать (путем чтения distanceToGo), пока он не достигнет целевой позиции (moveTo). Как только distanceToGo станет равен нулю мы переключаем двигатель в противоположное направление, изменив moveTo на противоположное значение относительно его текущего положения.

Теперь в конце цикла мы вызываем функцию run(). Это самая важная функция, поскольку шаговый двигатель не будет работать, пока эта функция не будет выполнена.

4 Управление яркостью и цветом светодиодас помощью аналогового джойстика и Arduino

Обычно джойстик используют для управления электродвигателями. Но почему бы не использовать его, например, для управления яркостью светодиода? Давайте подключим по приведённой схеме RGB светодиод (или три обычных светодиода) к цифровым портам 9, 10 и 11 Arduino, не забывая, конечно, о резисторах.

Подключение RGB светодиода и джойстика к Arduino

Будем менять яркость соответствующих цветов при изменении положения джойстика по осям, как показано на рисунке.

Из-за того, что джойстик может быть не точно отцентрирован производителем и иметь середину шкалы не на отметке 512, а варьироваться в диапазоне примерно от 490 до 525, то светодиод может слегка светиться даже когда джойстик находится в нейтральном положении. Если вы хотите, чтобы он был полностью выключен, то внесите в программу соответствующие поправки.

Диаграмма распределения яркости красного, синего и зелёного каналов светодиода в зависимости от положения ручки джойстика

Ориентируясь на приведённую диаграмму, напишем скетч управления Arduino яркостью RGB светодиода с помощью джойстика.

const int pinRed    = 9;
const int pinGreen  = 10;
const int pinBlue   = 11;
const int swPin = 8; 
const int pinX      = A1; // X 
const int pinY      = A2; // Y 
const int ledPin    = 13;
boolean ledOn = false;  // текущее состояние кнопки
boolean prevSw = false; // предыдущее состояние кнопки

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(pinRed, OUTPUT);
  pinMode(pinGreen, OUTPUT);
  pinMode(pinBlue, OUTPUT);
  pinMode(pinX, INPUT);
  pinMode(pinY, INPUT);
  pinMode(swPin, INPUT);  
  digitalWrite(swPin, HIGH); // включаем встроенный подтягивающий резистор
}

void loop() {
  if (isLedOn()) freeMode(); // если нажата кнопка и горит светодиод на пине 13, включаем режим "фонарик"
  else discoMode(); // иначе включаем "цветомузыку"
}

boolean isLedOn() { // Определяем нажатие кнопки
  if (digitalRead(swPin) == HIGH && prevSw == LOW) {
    ledOn = !ledOn;
    prevSw = HIGH;
  }
  else  prevSw = digitalRead(swPin); 
  digitalWrite(ledPin, ledOn); // включаем светодиод на пине 13
  return ledOn;
}

void freeMode() { // Режим "фонарик"
  int X = analogRead(pinX); // считываем положение джойстика
  int Y = analogRead(pinY);
  int RED = map(Y, 512, 1023, 0, 255); // маппинг значений
  int GREEN = map(X, 512, 1023, 0, 255);
  int BLUE = map(X, 511, 0, 0, 255);
  analogWrite(pinRed, RED);     // включение каналов R,G,B
  analogWrite(pinGreen, GREEN);
  analogWrite(pinBlue, BLUE);
}

void discoMode() { // Режим "цветомузыка"
    for (int i=0; i }

Сначала объявим соответствие пинов и две переменные – ledOn и prevSw – для работы с кнопкой. В процедуре setup() назначим пинам функции и подключим к пину кнопки подтягивающий резистор командой digitalWrite(swPin, HIGH).

В цикле loop() определяем нажатие кнопки джойстика. При нажатии на кнопку переключаем режимы работы между режимом «фонарика» и режимом «цветомузыки».

В режиме freeMode() управляем яркостью светодиодов с помощью наклона джойстика в разные стороны: чем сильнее наклон по оси, тем ярче светит соответствующий цвет. Причём преобразование значений берёт на себя функция map(значение, отНижнего, отВерхнего, кНижнему, кВерхнему).

Функция map() очень полезна и удобна в применении. Она переносит измеренные значения (отНижнего, отВерхнего) по осям джойстика в желаемый диапазон яркости (кНижнему, кВерхнему). Можно то же самое сделать обычными арифметическими действиями, но запись с помощью функции map() существенно короче.

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

В результате получился фонарик из трёхцветного RGB светодиода, яркость свечения каждого цвета которого задаётся с помощью джойстика. А при нажатии на кнопку происходит включение режима «цветомузыка». Я сделал специальную печатную плату с Arduino Pro Mini и джойстиком, и у меня он используется в качестве ночника для ребёнка 🙂

Управление яркостью и цветом RGB светодиода с помощью аналогового джойстика, подключённого к Arduino

Таким образом, мы научились подключать к Arduino аналоговый двухосевой джойстик с кнопкой и считывать с него показания. Вы можете придумать и реализовать более интересное применение джойстику, чем наш пример.

Подводные камни в работе геймпада

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

Второй проблемой можно назвать наличие так называемых мертвых зон. Два крайних значения при наибольших отклонениях должно быть равным 0 В и напряжению питания. В действительности эти значения могут различаться, так как не используется весь электрический диапазон изменения сопротивления. Для решения этой проблемы крайние точки могут соответствовать значениям 1 кОм и 9 кОм.

XBox One original Gamepad

Второй по простоте и доступности вариант. Работает точно так же: купил-подключил-работает. Если вам не нравится проводное подключение — потом отдельно можно достать wireless-модуль, который Microsoft обещала выпустить (видимо, вместе с Windows 10). Соответственно, плюсы и минусы примерно те же самые.

Плюсы и минусы: + Проще простого, чистый Plug And Play; + Все игры на Windows с поддержкой геймпада рассчитаны именно на XBox-раскладку; + Возможно подключить по радиоканалу и играть без проводов; — Не самая приятная цена, беспроводной модуль продаётся отдельно; — Тяжелее, чем конкуренты, к тому же требует двух батареек/аккумуляторов формата АА (в некоторых странах батареи идут в комплекте); — Не всем нравится ориентация стиков (аналоговых органов управления), дело привычки и личных предпочтений.