Введение:
В этом уроке мы дополним робота «Дройдика» модулем ИК-приёмник и будем управлять направлением и скоростью движения робота с помощью ИК-пульта.
Скорость и направление движения робота будет зависеть от нажатой клавиши на пульте. Робот сможет выполнять такие команды как движение вперёд или назад, с заворотом или без, разворот на месте влево или вправо.
Видео:
Waiting...
Нам понадобится:
Робот "Дройдик":
- Образовательный набор «Дройдик» x1 шт.
- Trema ИК-приёмник х1шт;
- ИК-пульт х1шт;
Библиотеки:
- Библиотека iarduino_IR_RX - для работы с ИК-приёмниками/передатчиками;
- Библиотеки SoftwareSerial и Servo входят в базовый набор Arduino IDE и не требуют установки.
О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki - Установка библиотек в Arduino IDE.
Схема подключения ИК-приёмника:
Trema ИК-приёмник | Trema Shield |
---|---|
вывод G | чёрная колодка "Земля" |
вывод V | красная колодка "+5V" |
вывод S | 10 цифровой вывод (D10) |
Вы можете изменить вывод D10 для подключения ИК-приёмника на любой другой, указав его в скетче при определении константы IR
.
Схема подключения робота «Дройдика»:
Соберите механику, подключите Trema Shield, сервоприводы и откалибруйте робота, как это описано в уроке 38 Сборка «Дройдика». Далее на боковую панель установите ИК-приёмник.
Сервоприводы | Trema Shield | |
---|---|---|
Верхние суставы | Левая нога «Дройдика» | вывод 4 на белой колодке |
Правая нога «Дройдика» | вывод 5 на белой колодке | |
Нижние суставы | Левая нога «Дройдика» | вывод 6 на белой колодке |
Правая нога «Дройдика» | вывод 7 на белой колодке |
Вы можете изменить выводы 4-7 для подключения сервоприводов на любые другие, указав их в скетче при определении констант PIN_LEFT_TOP
, PIN_RIGHT_TOP
, PIN_LEFT_BOT
, PIN_RIGHT_BOT
.
Трехпроводные шлейфы сервоприводов устанавливаются следующим образом:
- Оранжевый провод подключается к выводу на белой колодке.
- Красный провод подключается к выводу на красной колодке.
- Коричневый провод подключается к выводу на чёрной колодке.
Код программы для чтения сигналов с пульта:
#include "iarduino_IR_RX.h" // Подключаем библиотеку для работы с ИК-приёмником iarduino_IR_RX IR(10); // Объявляем объект IR и указываем вывод, к которому подключён ИК-приёмник void setup() { Serial.begin(9600); // Инициируем передачу данных в монитор порта на скорости 9600 бит/сек IR.begin(); // Инициируем работу с ИК-приёмником } void loop() { // Если в буфере имеются данные, принятые с пульта (удерживается кнопка), то выводим код нажатой кнопки if (IR.check(true)) Serial.println(IR.data, HEX); }
Данный скетч пригодится вам в том случае, если вы решите взять любой другой ИК-пульт вместо нашего. В этом случае вам надо будет определить коды кнопок, используя данный скетч, а затем указать их уже в основном коде "Дройдика", там где указана функцияIR.data
.
Код программы для «Дройдика»:
#define GO_LEFT_UP 1 // Определяем константу для движения влево-вверх #define GO_UP 2 // Определяем константу для движения вверх #define GO_RIGHT_UP 3 // Определяем константу для движения вправо-вверх #define GO_LEFT 4 // Определяем константу для движения влево #define STOP 5 // Определяем константу для движения стоп #define GO_RIGHT 6 // Определяем константу для движения вправо #define GO_LEFT_DOWN 7 // Определяем константу для движения влево-вниз #define GO_DOWN 8 // Определяем константу для движения вниз #define GO_RIGHT_DOWN 9 // Определяем константу для движения вправо-вниз #define PIN_ECHO 2 // Определяем константу с номером вывода подключённым к выводу ECHO датчика расстояний (можно указывать только те выводы Arduino, которые могут работать с внешними прерываниями) #define PIN_TRIG 3 // Определяем константу с номером вывода подключённым к выводу TRIG датчика расстояний (может быть любым) #define PIN_LEFT_TOP 4 // Определяем константу с номером вывода подключённым к верхнему сервоприводу левой ноги (может быть любым) #define PIN_RIGHT_TOP 5 // Определяем константу с номером вывода подключённым к верхнему сервоприводу правой ноги (может быть любым) #define PIN_LEFT_BOT 6 // Определяем константу с номером вывода подключённым к нижнему сервоприводу левой ноги (может быть любым) #define PIN_RIGHT_BOT 7 // Определяем константу с номером вывода подключённым к нижнему сервоприводу правой ноги (может быть любым) #define CENTR_LEFT_TOP 119 // Определяем константу центрального угла в градусах для верхнего сервопривода левой ноги (по умолчанию = 100) #define CENTR_RIGHT_TOP 65 // Определяем константу центрального угла в градусах для верхнего сервопривода правой ноги (по умолчанию = 80 ) #define CENTR_LEFT_BOT 79 // Определяем константу центрального угла в градусах для нижнего сервопривода левой ноги (по умолчанию = 60 ) #define CENTR_RIGHT_BOT 76 // Определяем константу центрального угла в градусах для нижнего сервопривода правой ноги (по умолчанию = 120) #define MAX_STEP_SIZE 25 // Определяем константу размера шага в градусах поворота верхних сервоприводов (чем больше угол, тем шире шаг) #define MAX_STEP_HEIGHT 20 // Определяем константу высоты шага в градусах наклона в стороны при ходьбе (чем больше угол, тем выше шаг) #define SPEED_DELAY 13 // Определяем константу обратно пропорциональную скорости движения (чем больше значение, тем медленнее скорость) #define IR_SIGNAL_WAITING_TIME 100 // Определяем константу времени ожидания прихода пакета от ИК-пульта // Подключаем библиотеки: #include "Servo.h" // Подключаем библиотеку для работы с сервоприводами #include "iarduino_IR_RX.h" // Подключаем библиотеку для работы с ИК-приёмником // Создаём объекты: Servo OBJ_SERVO_LEFT_TOP; // Создаём объект servoLEFT_TOP для работы с верхним левым сервоприводом Servo OBJ_SERVO_RIGHT_TOP; // Создаём объект OBJ_SERVO_RIGHT_TOP для работы с верхним правым сервоприводом Servo OBJ_SERVO_LEFT_BOT; // Создаём объект OBJ_SERVO_LEFT_BOT для работы с нижним левым сервоприводом Servo OBJ_SERVO_RIGHT_BOT; // Создаём объект OBJ_SERVO_RIGHT_BOT для работы с нижним правым сервоприводом iarduino_IR_RX IR(10); // Объявляем объект IR, с указанием вывода к которому подключён ИК-приёмник // Создаём переменные: uint8_t ValPosition = 224; // Определяем переменную (движение) для хранения текущей позиции шага (счёт от 0 до 255 или обратно), начальная позиция 224 int8_t ValTurning = 0; // Определяем переменную (поворот ) для пересчета размера шага в градусах поворота верхних сервоприводов (-10 - влево ... 0 - прямо ... +10 вправо) uint8_t MaxLeftSize = MAX_STEP_SIZE; // Определяем переменную максимального размера шага в градусах поворота верхнего левого сервопривода (чем меньше угол, тем сильнее робот будет уходить вправо) uint8_t MaxRightSize = MAX_STEP_SIZE; // Определяем переменную максимального размера шага в градусах поворота верхнего правого сервопривода (чем меньше угол, тем сильнее робот будет уходить влево ) uint8_t Flg = 0; // Флаг кнопок uint32_t FlgTime; // Флаг начала отправки пакетов void setup(){ IR.begin(); // Инициируем работу с ИК-приёмником // Указываем объектам сервоприводов их выводы: OBJ_SERVO_LEFT_TOP. attach(PIN_LEFT_TOP ); // Указываем объекту OBJ_SERVO_LEFT_TOP работать с выводом PIN_LEFT_TOP OBJ_SERVO_RIGHT_TOP.attach(PIN_RIGHT_TOP); // Указываем объекту OBJ_SERVO_RIGHT_TOP работать с выводом PIN_RIGHT_TOP OBJ_SERVO_LEFT_BOT. attach(PIN_LEFT_BOT ); // Указываем объекту OBJ_SERVO_LEFT_BOT работать с выводом PIN_LEFT_BOT OBJ_SERVO_RIGHT_BOT.attach(PIN_RIGHT_BOT); // Указываем объекту OBJ_SERVO_RIGHT_BOT работать с выводом PIN_RIGHT_BOT // Устанавливаем центральные углы сервоприводов: OBJ_SERVO_LEFT_TOP. write (CENTR_LEFT_TOP ); // Устанавливаем центральную позицию (угол CENTR_LEFT_TOP ) для сервопривода подключённого к выводу PIN_LEFT_TOP OBJ_SERVO_RIGHT_TOP.write (CENTR_RIGHT_TOP); // Устанавливаем центральную позицию (угол CENTR_RIGHT_TOP) для сервопривода подключённого к выводу PIN_RIGHT_TOP OBJ_SERVO_LEFT_BOT. write (CENTR_LEFT_BOT ); // Устанавливаем центральную позицию (угол CENTR_LEFT_BOT ) для сервопривода подключённого к выводу PIN_LEFT_BOT OBJ_SERVO_RIGHT_BOT.write (CENTR_RIGHT_BOT); // Устанавливаем центральную позицию (угол CENTR_RIGHT_BOT) для сервопривода подключённого к выводу PIN_RIGHT_BOT } void loop(){ if (IR.check(true)) { // Настраиваем считывание данных при нажатой и зажатой кнопках // Флаг времени кнопки if (IR.data == 0xFFA25D) { FlgTime = millis(); Flg = GO_LEFT_UP; } else if (IR.data == 0xFF629D) { FlgTime = millis(); Flg = GO_UP; } else if (IR.data == 0xFFE21D) { FlgTime = millis(); Flg = GO_RIGHT_UP; } else if (IR.data == 0xFF22DD) { FlgTime = millis(); Flg = GO_LEFT; } else if (IR.data == 0xFFC23D) { FlgTime = millis(); Flg = GO_RIGHT; } else if (IR.data == 0xFFE01F) { FlgTime = millis(); Flg = GO_LEFT_DOWN; } else if (IR.data == 0xFFA857) { FlgTime = millis(); Flg = GO_DOWN; } else if (IR.data == 0xFF906F) { FlgTime = millis(); Flg = GO_RIGHT_DOWN;} } if (FlgTime > millis()) FlgTime = 0; // Если millis() переполнен, то сбрасываем флаг в ноль if ((FlgTime + IR_SIGNAL_WAITING_TIME) > millis()) { // Если пакеты с пульта приходят в течении 100 мс if(millis() % SPEED_DELAY == 0){ switch (Flg) { //Направ. движения Направ. вращения case GO_LEFT_UP: ValPosition += 2; ValTurning = -5; break; case GO_UP: ValPosition += 2; ValTurning = 0; break; case GO_RIGHT_UP: ValPosition += 2; ValTurning = 5; break; case GO_LEFT: ValPosition += 2; ValTurning = -10; break; case GO_RIGHT: ValPosition += 2; ValTurning = 10; break; case GO_LEFT_DOWN: ValPosition -= 2; ValTurning = -5; break; case GO_DOWN: ValPosition -= 2; ValTurning = 0; break; case GO_RIGHT_DOWN: ValPosition -= 2; ValTurning = 5; break; } } MaxRightSize = MAX_STEP_SIZE; if(ValTurning<0){ MaxRightSize = map(ValTurning, 0,-10, MAX_STEP_SIZE, 0);} /* Прямо или влево */ // Корректируем значение MaxRightSize (размера шага правой ноги) в соответствии со значением ValTurning. MaxLeftSize = MAX_STEP_SIZE; if(ValTurning>0){ MaxLeftSize = map(ValTurning, 0, 10, MAX_STEP_SIZE, 0);} /* Прямо или вправо */ // Корректируем значение MaxLeftSize (размера шага левой ноги) в соответствии со значением ValTurning. if(ValPosition<64 ){OBJ_SERVO_LEFT_TOP. write(map(ValPosition, 0, 63, CENTR_LEFT_TOP - MaxLeftSize , CENTR_LEFT_TOP + MaxLeftSize )); // Левая нога поворачивается вправо => отходит назад. OBJ_SERVO_RIGHT_TOP.write(map(ValPosition, 0, 63, CENTR_RIGHT_TOP - MaxRightSize , CENTR_RIGHT_TOP + MaxRightSize ));}else // Правая нога поворачивается вправо => выходит вперёд. if(ValPosition<128){OBJ_SERVO_LEFT_BOT. write(map(ValPosition, 64, 127, CENTR_LEFT_BOT - MAX_STEP_HEIGHT , CENTR_LEFT_BOT +(MAX_STEP_HEIGHT/2))); // Левая нога наклоняется вправо => переносит центр тяжести с себя на правую ногу, которая станет опорной. OBJ_SERVO_RIGHT_BOT.write(map(ValPosition, 64, 127, CENTR_RIGHT_BOT -(MAX_STEP_HEIGHT/2) , CENTR_RIGHT_BOT + MAX_STEP_HEIGHT ));}else // Правая нога наклоняется вправо => опускается вниз (становится опорной) и поднимает левую ногу. if(ValPosition<192){OBJ_SERVO_LEFT_TOP. write(map(ValPosition, 128, 191, CENTR_LEFT_TOP + MaxLeftSize , CENTR_LEFT_TOP - MaxLeftSize )); // Левая нога поворачивается влево => выходит вперёд. OBJ_SERVO_RIGHT_TOP.write(map(ValPosition, 128, 191, CENTR_RIGHT_TOP + MaxRightSize , CENTR_RIGHT_TOP - MaxRightSize ));}else // Правая нога поворачивается влево => отходит назад. /*ValPosition<255*/{OBJ_SERVO_LEFT_BOT. write(map(ValPosition, 192, 255, CENTR_LEFT_BOT +(MAX_STEP_HEIGHT/2) , CENTR_LEFT_BOT - MAX_STEP_HEIGHT )); // Левая нога наклоняется влево => опускается вниз (становится опорной) и поднимает правую ногу. OBJ_SERVO_RIGHT_BOT.write(map(ValPosition, 192, 255, CENTR_RIGHT_BOT + MAX_STEP_HEIGHT , CENTR_RIGHT_BOT -(MAX_STEP_HEIGHT/2)));} // Правая нога наклоняется влево => переносит центр тяжести с себя на левую ногу, которая станет опорной. } }
Константы CENTR_LEFT_TOP
, CENTR_LEFT_TOP
, CENTR_LEFT_TOP
, CENTR_LEFT_TOP
должны быть изменены (откалиброваны) на действительные углы сервоприводов в градусах, при которых все суставы робота находятся в центральном положении. Это выполняется с использованием калибровочного скетча, как описано в уроке 38 Сборка «Дройдика».
В данном коде управление роботом осуществляется в три основных этапа: получение данных с пульта; изменение значений переменных ValPosition
и ValTurning
; установка сервоприводов в требуемые позиции.
- Получение данных с пульта
- Данный блок начинается с оператора
if
в условии которого написаноIR.check(true)
. Это условие верно, если с ИК-пульта приходят пакеты на ИК-приёмник. - Далее следует оператор
if
условием которого являетсяIR.data ==
. Далее указан код кнопки ИК-пульта. Если код совпадает, то выполняется код в теле оператора, код которого совпал с принятым от ИК-пульта. - При выполнении кода в теле оператора
if
так же обновляется значение счётчика времениFlgTime
, который в последствии будет использоваться для ограничения времени на получение пакета (в скетче выше время получения указано ниже в виде(FlgTime + IR_SIGNAL_WAITING_TIME) > millis())
.
- Данный блок начинается с оператора
- Изменение значений переменных valPosition, valTurning
- Данный блок начинается с оператора
if
в условии которого написано(FlgTime + IR_SIGNAL_WAITING_TIME) > millis())
,что ограничивает время принятия пакета на времяIR_SIGNAL_WAITING_TIME
, а затем стоит операторif
с условиемmillis() % SPEED_DELAY == 0
. Это условие будет верно каждыеSPEED_DELAY
секунд, потому что символ%
в условии это операция "остаток от деления". В данном случае остаток от деленияmillis()
наSPEED_DELAY
должен быть равен 0, то естьmillis()
должна делиться нацело (без остатка). - Далее следует оператор
Switch (условие) { case (условие): код }
, который отвечает за выполнение кода вcase
при совпадении с ним условия изSwitch
. В данном блоке определены все движения Дройдика. - Переменная
ValPosition
используется в следующем блоке и определяет изменение позиции шага от 0 до 255. Чем выше значение, тем на больший угол поворачиваются сервоприводы; - Переменная
ValTurning
используется в следующем блоке и определяет степень отклонения от центра (для совершения движения в сторону или разворота) от -10 до +10; - Значения переменным
valPosition
иvalTurning
присваиваются в соответствии с принятыми данными о нажатой кнопке на ИК-пульте
- Данный блок начинается с оператора
- Установка сервоприводов в требуемые позиции - выполняется при наличии связи с пультом или в течении
IR_SIGNAL_WAITING_TIME
после получения последнего пакета.- Движение осуществляется в соответствии со значениями переменных
ValPosition
иvalTurning
. - Полный шаг робота разбит на 256 частей (от 0 до 255) на которые и указывает значение переменной
valPosition
. - Если значение переменной
ValPosition
не меняется, то робот стоит на месте. - Если значение переменной
ValPosition
увеличивается, то робот будет идти вперёд (переход от 255 к 0 считается увеличением). - Если значение переменной
ValPosition
убывает, то робот будет идти назад (переход от 0 к 255 считается уменьшением). - Чем быстрее выполняется увеличение/уменьшение переменной
ValPosition
, тем быстрее шагает робот. - Так как в предыдущем блоке переменная
ValPosition
либо уменьшается, либо увеличивается, значит скорость её уменьшения или увеличения зависит от условияmillis() % SPEED_DELAY == 0
, которое определяет промежутки времени между изменением переменнойValPosition
. - Значение переменной
ValTurning
ограничивает движение левой или правой ноги, что приводит к повороту робота влево или вправо. Переменная может содержать значения от -10 (максимальный поворот влево), 0 (без поворота), до +10 (максимальный поворот вправо).
- Движение осуществляется в соответствии со значениями переменных
Управление:
Управление Дройдиком начинается после того, как на него будет подано питание и все сервоприводы будут выставлены в центральные положения (требуется предварительная калибровка). После этого кнопками пульта со стрелками можно управлять направлением движения робота.
Обсуждение