+7-960-0655211 (Билайн)
+7-987-4207734 (МТС)
интернет-магазин
доставка по России и СНГ
работаем с 2010 года

Сферический четвероногий робот

ГлавнаяИнформацияСтатьи

Сферический четвероногий робот





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

Я не знаю что со мной...

Первые две недели я очень упорно пытался, чтобы просто начать делать что-то простое, например, помигать светодиодом.

Шли недели, затем месяцы. Проект за проектом и удовольствие разбираться с Arduino и пытаться реализовать что-то новое не угасло, скорее наоборот.

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

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








Вес: 440г
Диаметр: 130 мм
Время работы: 15 минут
Язык программирования: Arduino C/C++


Список деталей




Электроника:

1 х Arduino Nano
1 х Сервоконтроллер на микросхеме PCA9685 (16-канальный 12-битный драйвер ШИМ)
10 х Угловой штекер на плату с шагом 2.54мм, трёхрядный (можно угловой двухрядный, а для третьего ряда взять прямой однорядный и припаять его проводами)
10 х Микросервопривод MS-12016 MG или аналогичный по габаритам
3 x NIMH аккумулятора 1800мАч
1 x Держатель аккумуляторов
1 х Ультразвуковой дальномер SRF05
Не обязательно:
4 x Адресуемые светодиоды RGB Smart NeoPixel
1x 470 Ом резистор для NeoPixel


Механические детали:

18 х резьбонарезающий винт Phillips 1x6 (качалка сервопривода к элементам корпуса)
10 х стандартный винт (качалка к сервомотору)
17 х резьбонарезающий винт Phillips 1,6x6 - (фиксация элементов конструкции)
2 х резьбонарезающий винт Phillips 2x8 - (фиксация ультразвукового датчика)
20 x винт с головкой DIN 84, 2x10 - (крепление сервоприводов)
5 х резьбонарезающий винт Philips 2x8 - (крепление электронных плат)
4 х нейлоновые стяжки для фиксации проводов

Печатные детали:
Все конструктивные элементы были сделаны для 3D-печати. Файлы для печати можно скачать с Thingiverse по этой ссылке.

Двери








Двери скрывают ноги, когда они находятся в убранном положении. Поэтому он выглядит как сфера.

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

Задавать для сервоприводов сразу конечную позицию не получиться, т.к. скорость вращения вала серводвигателя слишком высока. Если пытаться так резко перемещать дверь, она будет подклинивать, что с одной стороны не хорошо для деталей, а с другой может сильно повысить потребляемый сервоприводом ток (электроника в сервоприводе от этого даже может выйти из строя).  Что бы дверь могла нормально открываться и закрываться, в коде нужно реализовать управление скоростью вращения вала сервопривода. В данном случае управление скоростью реализовано в цикле, в котором понемногу изменяется положение, а затем для задержки вызывается функции delay.


Ноги

















Очевидно, что они должны позволять поднимать и опускать роботов.

Ноги должны быть невидимы в режиме ожидания.

Дизайн

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

Для того, чтобы компенсировать отсутствие третьего сервопривода («классический» вариант ноги с тремя сервоприводами для бедра, колена и голеностопного сустава) и чтобы обеспечить подъемное движение робота, второй сервопривод также используется для движения ноги вверх и вниз. Конструкция сделана так, чтобы нога не двигалась по прямой в одной оси. Для этого используется кинематическое движение параллелограмма.

Чтобы оптимизировать пространство, структура голени является частью корпуса.


Электроника

Для каждой ноги используется по два сервопривода, ног у этого робота четыре, т.е. на ноги у нас суммарно 8 сервоприводов. Для минимизации количества выводов и проводов от платы Arduino Nano, а также упрощения кода, сервоприводы управляются сервоконтроллером с интерфейсом I2C. Интерфейс I2C даёт нам возможность подключить драйвер сервоприводов, используя всего два вывода от Arduino.

Походка

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








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

Голова









В верхней части головы устанавливается ультразвуковой дальномер, с помощью которого определяют препятствия. Установленный в корпусе сервопривод позволяет поворачивать верхнюю крышку, что даёт возможность осматриваться по сторонам, не перемещая всего робота.
Верхняя часть также является крышкой робота, которую легко снять чтобы получить доступ у к электронике и зарядить аккумуляторы.
На внутренней части верхней крышки есть углубления, повторяющие форму качалки сервопривода, поэтому качалка очень просто вставляется в эти углубления с минимальным зазором. Чтобы получить доступ к электронике, снимите крышку, просто приподняв её.
Ультразвуковой датчик расстояния подключается к плате Arduino с помощью разъема JR, это даёт возможность очень легко его подключать и отключать.
Для предотвращения резкого движения серводвигателя, в коде добавлен цикл с задержками, который делает движения сервопривода более медленными.

Светодиоды









Сверху нижней части корпуса закреплены 4 светодиода, которые хорошо видно в щели между нижней и верхней частью корпуса. Эти адресуемые светодиоды не являются существенной частью для функционирования робота, их подключение не обязательное.
Установленные светодиоды делают робота более привлекательным. Они дают возможность сигнализировать о текущем состоянии или обнаружении препятствий. Появляется возможность создавать анимации, которые можно совмещать с движениями робота.
Для данного робота были использованы модули адресуемых RGB светодиодов NeoPixel от Adafruit, на которых запаяны WS2812. Подобные модули производятся и другими компаниями. Если будете использовать другие, обратите внимание на размер модулей, что бы они не были слишком большими и их можно было установить в этом роботе.
Кроме WS2812, есть WS2811, WS2813, SK2812, LDP8806 и много других. Если на Вашем модуле запаяны другие светодиоды, изучите, они подключаются также как эти или их нужно по другому подключать. Так же при необходимости внесите изменения в код (подключение соответствующей библиотеки, инициализация и функции управления).

Ультразвуковой датчик






Ультразвуковой датчик используется для проверки наличия препятствий перед роботом.
Датчик также используется в качестве переключателя для запуска и остановки робота.

Обнаружение препятствий

Датчик излучает ультразвуковые волны, которые распространяются в воздухе и отражаясь от препятствий, возвращаются обратно. Электроника ультразвукового датчика измеряет время, между излучением звуковой волны и их возвращением. Скорость звука известна и по этому времени можно вычислить расстояние до препятствий.
Когда робот обнаруживает, что перед ним препятствие, он останавливается. Затем происходит поворачивание головы вправо, чтобы измерить расстояние. После этого голова поворачивается влево и опять измеряется расстояние. Затем робот поворачивается в ту сторону, где препятствия находятся дальше.
Реализуемый алгоритм не единственно возможный. Если интересно создавать алгоритмы или данный алгоритм ещё по каким причинам не подходит, Вы можете реализовать более подходящий.




«Переключатель»

В роботе нет физического переключателя или схемы отключения питания. Чтобы включить или отключить питание, необходимо поднять крышку и отсоединить аккумулятор. 
Для прекращения или возобновления движения, когда ультразвуковой датчик обнаруживает препятствие или его отсутствие, вместо физического выключателя всё необходимое реализовано программно.
Прекращение дальнейших движений происходит, когда препятствие обнаружено слишком близко. Возобновление движения происходит, когда перед роботом больше нет слишком близких препятствий.


Сборка





В сборке нет ничего сложного, нет каких-то особых хитростей или подводных камней. По изображениям очень легко понять, как собирать и в какой последовательности, но на всякий случай прокомментирую некоторые моменты.
Не забудьте ослабить кабели сервоприводов для ног, сервоприводы должны свободно двигаться. Аналогично и с проводами ультразвукового датчика.
Все провода нужно зафиксировать нейлоновыми стяжками к внутренней части конструкции.
На плату сервоконтроллера запаяны угловые штекерные разъемы. Прямые разъёмы не используются, т.к. в корпусе для этого недостаточно места.




Если у Вас нет трёхрядных угловых штекеров, можно реализовать и по-другому. Можно запаять один двухрядный угловой штекер, а сверху него положить прямой однорядный и припаять его выводы проводами.
Ещё один вариант – это запаять провода напрямую на плату сервоконтроллера и сервоприводов. Что бы не портить провода сервопривода, желательно их выпаять (если в будущем понадобиться, их можно будет запаять обратно), а вместо них впаять другие провода.
Для суставов ног сделаны зазоры, 0.1 мм в осевом и 0.1 мм в радиальном направлении.




Вы можете получить эти значения непосредственно с помощью 3D-принтера или немного подточив детали. Размеры печатаемых деталей могут немного отличаться в зависимости от принтера, пластика и настроек слайсера, учитывайте это. Если Вам понадобиться немного изменить детали, и Вы хорошо умеете пользоваться инженерными програграммами, в архиве кроме stl файлов есть stp (step).
Конструктивные части были собраны с помощью саморезов.


Подключение электроники




Плата сервоконтроллера подключается к Arduino по I2C интерфейсу. Выводы SDA и SCL от сервоконтроллера подключаются к выводам A4 и A5 на Arduino Nano.

Исходный код

Управляющий код сделан с использованием подхода, называемого конечный автомат. Конечный автомат (FSM — Finite-state machine) - это модель вычислений, основанная на гипотетической машине состояний. В один момент времени только одно состояние может быть активным. Следовательно, для выполнения каких-либо действий, машина должна менять свое состояние.
Для управления сервоприводами используются константные значения. Все эти значения прописаны в файле «global.h», который подключается в скетче «robot.ino». также не забывайте, что файл скетча должен находиться внутри папки, которая называется также, как и файл скетча. Если файл скетча называется «robot.ino», то имя папки, в которой он находится, должно быть «robot». В этой же папке должен находится и подключаемый файл «global.h».
Все сервоприводы должны быть откалиброваны, т.е. полученные значения должны быть переопределены в соответствии с вашими настройками.


Скетч Robot.ino:


//***********************************************
//
//      SPHERICAL QUADRUPED ARDUINO ROBOT
//
//***********************************************

//************************************ LIBRAIRIES
#include "global.h"
#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
Adafruit_NeoPixel strip(Led_Count, Led_Pin, NEO_GRB + NEO_KHZ800);

//***************************************** SETUP
void setup() {

  // US
  pinMode (Ultrasonic_Sensor_Trigger_Pin, OUTPUT);
  digitalWrite(Ultrasonic_Sensor_Trigger_Pin, LOW);
  pinMode (Ultrasonic_Sensor_Echo_Pin, INPUT);

  // LED
  strip.begin();
  strip.show();
  strip.setBrightness(50);

  // SERVO
  pwm.begin();
  pwm.setPWMFreq(60);
  Servo_Start_Position();
}

//****************************************** LOOP
void loop() {

  switch (Sequence) {
    case Stand_By:
      if (Ultrasonic_Sensor() < Distance_To_Activate_Switch) Sequence = Legs_Deployment;
      break;

    case Legs_Deployment:
      Door(Opening);
      Legs_Partially_Unfolded(); // DEPLOY THE LEGS UP TO TOUCH THE GROUND
      Legs_Unfolded(); // DEPLOY THE LEGS TO LIFT THE ROBOT

    case Moving_Forward:
      Led_Walking();
      while (Ultrasonic_Sensor() > Distance_To_Activate_Switch) { // CONTINUE TO WALK UNTIL SWITCH ACTIVATION
        if (Ultrasonic_Sensor() > Distance_Obstacle) walk();
        else if (Head_Rotation() == Left) Rotation(Left);
        else Rotation(Right);
      }

    case Folding_Legs:
      Legs_Partially_Folded(); // FOLD THE LEGS UP TO PUT DOWN THE ROBOT
      Legs_Folded(); // FOLD THE LEGS UP TO THE RETRACTED POSITION
      Door(Closing);

    case Restart:
      Sequence = Stand_By;
      strip.clear();
      strip.show();
  }
}

//******************************** START POSITION
void Servo_Start_Position() {

  Hip_Centered();
  pwm.setPWM(Head_Servo_Pin, 0, Head_Servo_Start_Position);
  Legs_Folded();
  pwm.setPWM(Door_Servo_Pin, 0, Door_Servo_Close);
  delay(1000);
}

//****************************************** HEAD
int Head_Rotation() {

  int Head_Angle = Head_Servo_Start_Position;
  long Distance_Left, Distance_Right;

  for (byte rotation_step = 0; rotation_step < 4; rotation_step++) {

    // MEASURMENT
    if (rotation_step == 1 ) {
      Distance_Left = Ultrasonic_Sensor();
      delay(1000);
    }
    if (rotation_step == 3) {
      Distance_Right = Ultrasonic_Sensor();
      delay(1000);
    }

    // LED ANIMATION
    if (rotation_step == 0 || rotation_step == 3 ) Led_Door_Head(Rotation_Left, Head_Led);
    else if (rotation_step == 1) Led_Door_Head(Rotation_Right, Head_Led);


    // HEAD ROTATION
    for (byte pitch = 0; pitch < 20; pitch++) {
      pwm.setPWM(Head_Servo_Pin, 0, Head_Angle);
      if (rotation_step == 0 || rotation_step == 3 ) Head_Angle += 2;
      else Head_Angle -= 2;
      delay(Head_Servo_Speed);
    }
  }

  // SELECT THE FREE SIDE
  if (Distance_Left > Distance_Right) return Left;
  else return Right;
}

//****************************************** KNEE
// FOLDED
void Legs_Folded() {

  Knee_Servo_Front_Right (Knee_Servo_Front_Right_Bent);
  Knee_Servo_Front_Left (Knee_Servo_Front_Left_Bent);
  Knee_Servo_Rear_Right (Knee_Servo_Rear_Right_Bent);
  Knee_Servo_Rear_Left (Knee_Servo_Rear_Left_Bent);
  delay(2000);
}

// PARTIALLY UNFOLDED
void Legs_Partially_Unfolded() {

  Led_Leg();

  Knee_Servo_Front_Right (Knee_Servo_Front_Right_Partially_Unfolded);
  Knee_Servo_Front_Left (Knee_Servo_Front_Left_Partially_Unfolded);
  Knee_Servo_Rear_Right (Knee_Servo_Rear_Right_Partially_Unfolded);
  Knee_Servo_Rear_Left (Knee_Servo_Rear_Left_Partially_Unfolded);
  delay(2000);
}

// KNEE UNFOLDED LOW SPEED
void Legs_Unfolded() {

  for (byte a = 0; a < 20; a++) { // LOOP TO REDUCE THE SERVOMOTOR SPEED
    Knee_Servo_Front_Right(map(a, 0, 20, Knee_Servo_Front_Right_Partially_Unfolded, Knee_Servo_Front_Right_Unfolded));
    Knee_Servo_Front_Left(map(a, 0, 20, Knee_Servo_Front_Left_Partially_Unfolded, Knee_Servo_Front_Left_Unfolded));
    Knee_Servo_Rear_Right(map(a, 0, 20, Knee_Servo_Rear_Right_Partially_Unfolded, Knee_Servo_Rear_Right_Unfolded));
    Knee_Servo_Rear_Left(map(a, 0, 20, Knee_Servo_Rear_Left_Partially_Unfolded, Knee_Servo_Rear_Left_Unfolded));
    delay(Knee_Servo_Speed);
  }
  delay(2000);
}

// KNEE BENT LOW SPEED
void Legs_Partially_Folded() {

  Led_Leg();

  for (byte a = 0; a < 20; a++) { // LOOP TO REDUCE THE SERVOMOTOR SPEED
    Knee_Servo_Front_Right(map(a, 0, 20, Knee_Servo_Front_Right_Unfolded, Knee_Servo_Front_Right_Partially_Unfolded));
    Knee_Servo_Front_Left(map(a, 0, 20, Knee_Servo_Front_Left_Unfolded, Knee_Servo_Front_Left_Partially_Unfolded));
    Knee_Servo_Rear_Right(map(a, 0, 20, Knee_Servo_Rear_Right_Unfolded, Knee_Servo_Rear_Right_Partially_Unfolded));
    Knee_Servo_Rear_Left(map(a, 0, 20, Knee_Servo_Rear_Left_Unfolded, Knee_Servo_Rear_Left_Partially_Unfolded));
    delay(Knee_Servo_Speed);
  }
  delay(2000);
}

// COMMAND
void Knee_Servo_Front_Right (int pulse) {

  pwm.setPWM(Knee_Servo_Front_Right_Pin, 0, pulse);
}

void Knee_Servo_Front_Left (int pulse) {

  pwm.setPWM(Knee_Servo_Front_Left_Pin, 0, pulse);
}

void Knee_Servo_Rear_Right (int pulse) {

  pwm.setPWM(Knee_Servo_Rear_Right_Pin, 0, pulse);
}

void Knee_Servo_Rear_Left (int pulse) {

  pwm.setPWM(Knee_Servo_Rear_Left_Pin, 0, pulse);
}

//******************************************* HIP
// CENTERED
void Hip_Centered() {

  Hip_Servo_Front_Right (Hip_Servo_Front_Right_Centered);
  Hip_Servo_Front_Left (Hip_Servo_Front_Left_Centered);
  Hip_Servo_Rear_Right (Hip_Servo_Rear_Right_Centered);
  Hip_Servo_Rear_Left (Hip_Servo_Rear_Left_Centered);
}

// COMMAND
void Hip_Servo_Front_Right (int pulse) {

  pwm.setPWM(Hip_Servo_Front_Right_Pin, 0, pulse);
}

void Hip_Servo_Front_Left (int pulse) {

  pwm.setPWM(Hip_Servo_Front_Left_Pin, 0, pulse);
}

void Hip_Servo_Rear_Right (int pulse) {

  pwm.setPWM(Hip_Servo_Rear_Right_Pin, 0, pulse);
}

void Hip_Servo_Rear_Left (int pulse) {

  pwm.setPWM(Hip_Servo_Rear_Left_Pin, 0, pulse);
}

//****************************************** WALK
void walk() {

  // SEE INSTRUCTABLES DETAIL FOR THE WALKING STRATEGY

  Knee_Servo_Front_Right(Knee_Servo_Front_Right_Unfolded - Knee_Vertical_Amplitude);
  Knee_Servo_Rear_Left(Knee_Servo_Rear_Left_Unfolded - Knee_Vertical_Amplitude);
  delay(delay_knee);

  Hip_Servo_Front_Right (Hip_Servo_Front_Right_Centered - Hip_Servo_Front_Right_Move);
  Hip_Servo_Rear_Left (Hip_Servo_Rear_Left_Centered + Hip_Servo_Rear_Left_Move);
  delay(delay_hip);

  Knee_Servo_Front_Right(Knee_Servo_Front_Right_Unfolded + Knee_Vertical_Amplitude);
  Knee_Servo_Rear_Left(Knee_Servo_Rear_Left_Unfolded + Knee_Vertical_Amplitude);
  delay(delay_knee);

  Hip_Servo_Front_Right (Hip_Servo_Front_Right_Centered);
  Hip_Servo_Rear_Left (Hip_Servo_Rear_Left_Centered);
  delay(delay_hip);

  Knee_Servo_Front_Right(Knee_Servo_Front_Right_Unfolded);
  Knee_Servo_Rear_Left(Knee_Servo_Rear_Left_Unfolded);
  delay(delay_knee);

  Knee_Servo_Front_Left(Knee_Servo_Front_Left_Unfolded - Knee_Vertical_Amplitude);
  Knee_Servo_Rear_Right(Knee_Servo_Rear_Right_Unfolded - Knee_Vertical_Amplitude);
  delay(delay_knee);

  Hip_Servo_Front_Left (Hip_Servo_Front_Left_Centered + Hip_Servo_Front_Left_Move);
  Hip_Servo_Rear_Right (Hip_Servo_Rear_Right_Centered - Hip_Servo_Rear_Right_Move);
  delay(delay_hip);

  Knee_Servo_Front_Left(Knee_Servo_Front_Left_Unfolded + Knee_Vertical_Amplitude);
  Knee_Servo_Rear_Right(Knee_Servo_Rear_Right_Unfolded + Knee_Vertical_Amplitude);
  delay(delay_knee);

  Hip_Servo_Front_Left (Hip_Servo_Front_Left_Centered);
  Hip_Servo_Rear_Right (Hip_Servo_Rear_Right_Centered);
  delay(delay_hip);

  Knee_Servo_Front_Left(Knee_Servo_Front_Left_Unfolded);
  Knee_Servo_Rear_Right(Knee_Servo_Rear_Right_Unfolded);
  delay(delay_knee);
}

//************************************** ROTATION
void Rotation (byte Direction) {

  // SEE INSTRUCTABLES DETAIL FOR THE ROTATION STRATEGY

  for (byte a = 0; a < 4; a++) {
    Knee_Servo_Front_Right(Knee_Servo_Front_Right_Unfolded - Knee_Vertical_Amplitude);
    Knee_Servo_Rear_Left(Knee_Servo_Rear_Left_Unfolded - Knee_Vertical_Amplitude);
    delay(delay_knee);

    if (Direction == Right) {
      Hip_Servo_Front_Right (Hip_Servo_Front_Right_Centered - Hip_Servo_Front_Right_Move);
      Hip_Servo_Rear_Left (Hip_Servo_Rear_Left_Centered - Hip_Servo_Rear_Left_Move);
    }
    else {
      Hip_Servo_Front_Right (Hip_Servo_Front_Right_Centered + Hip_Servo_Front_Right_Move);
      Hip_Servo_Rear_Left (Hip_Servo_Rear_Left_Centered + Hip_Servo_Rear_Left_Move);
    }
    delay(delay_hip);

    Knee_Servo_Front_Right(Knee_Servo_Front_Right_Unfolded + Knee_Vertical_Amplitude);
    Knee_Servo_Rear_Left(Knee_Servo_Rear_Left_Unfolded + Knee_Vertical_Amplitude);
    delay(delay_knee);

    Hip_Servo_Front_Right (Hip_Servo_Front_Right_Centered);
    Hip_Servo_Rear_Left (Hip_Servo_Rear_Left_Centered);
    delay(delay_hip);

    Knee_Servo_Front_Right(Knee_Servo_Front_Right_Unfolded);
    Knee_Servo_Rear_Left(Knee_Servo_Rear_Left_Unfolded);
    delay(delay_knee);

    Knee_Servo_Front_Left(Knee_Servo_Front_Left_Unfolded - Knee_Vertical_Amplitude);
    Knee_Servo_Rear_Right(Knee_Servo_Rear_Right_Unfolded - Knee_Vertical_Amplitude);
    delay(delay_knee);

    if (Direction == Right) {
      Hip_Servo_Front_Left (Hip_Servo_Front_Left_Centered - Hip_Servo_Front_Left_Move);
      Hip_Servo_Rear_Right (Hip_Servo_Rear_Right_Centered - Hip_Servo_Rear_Right_Move);
    }
    else {
      Hip_Servo_Front_Left (Hip_Servo_Front_Left_Centered + Hip_Servo_Front_Left_Move);
      Hip_Servo_Rear_Right (Hip_Servo_Rear_Right_Centered + Hip_Servo_Rear_Right_Move);
    }
    delay(delay_hip);

    Knee_Servo_Front_Left(Knee_Servo_Front_Left_Unfolded + Knee_Vertical_Amplitude);
    Knee_Servo_Rear_Right(Knee_Servo_Rear_Right_Unfolded + Knee_Vertical_Amplitude);
    delay(delay_knee);

    Hip_Servo_Front_Left (Hip_Servo_Front_Left_Centered);
    Hip_Servo_Rear_Right (Hip_Servo_Rear_Right_Centered);
    delay(delay_hip);

    Knee_Servo_Front_Left(Knee_Servo_Front_Left_Unfolded);
    Knee_Servo_Rear_Right(Knee_Servo_Rear_Right_Unfolded);
    delay(delay_knee);
  }
}

//***************************** ULTRASONIC SENSOR
long Ultrasonic_Sensor() {

  digitalWrite(Ultrasonic_Sensor_Trigger_Pin, HIGH);
  delayMicroseconds(10);
  digitalWrite(Ultrasonic_Sensor_Trigger_Pin, LOW);
  Measure = pulseIn(Ultrasonic_Sensor_Echo_Pin, HIGH);
  Distance_mm = Measure / 58;
  delay(100);
  return Distance_mm;
}

//****************************************** DOOR
void Door (int State) {

  if (State == Closing) {
    Led_Door_Head(Rotation_Left, Door_Led);

    for (byte a = 0; a < 20; a++) { // LOOP TO REDUCE THE SERVOMOTOR SPEED
      pwm.setPWM(Door_Servo_Pin, 0, map(a, 0, 20, Door_Servo_Open, Door_Servo_Close));
      delay(Door_Servo_Speed);
    }
  }
  else {
    Led_Door_Head(Rotation_Right, Door_Led);

    for (byte a = 0; a < 20; a++) { // LOOP TO REDUCE THE SERVOMOTOR SPEED
      pwm.setPWM(Door_Servo_Pin, 0, map(a, 0, 20, Door_Servo_Close, Door_Servo_Open));
      delay(Door_Servo_Speed);
    }
  }
  delay(1000);
}

//******************************************* LED
// DOOR & HEAD
void Led_Door_Head(int Direction, int Item) {

  // CREATE A LEDS ANIMATION WHO TURN AROUND THE ROBOT ON THE SAME DIRECTION THAT THE MECHANISM

  if (Direction == Rotation_Left) {
    for (byte a = 0; a < 3; a++) {
      for (byte b = 0; b <= 3; b++) {
        strip.clear();
        if (Item == Head_Led) strip.setPixelColor(b, 0, 0, 250);
        else strip.setPixelColor(b, 250, 0, 0);
        strip.show();
        delay(100);
      }
    }
  }
  else { // ROTATION RIGHT
    for (byte a = 0; a < 3; a++) {
      for (byte b = 3; b >= 0; b--) {
        strip.clear();
        if (Item == Head_Led) strip.setPixelColor(b, 0, 0, 250);
        else strip.setPixelColor(b, 250, 0, 0);
        strip.show();
        delay(100);
      }
    }
  }
  strip.clear();
  strip.show();
}

// LEG
void Led_Leg() {
  strip.clear();
  strip.setPixelColor(0, 250, 0, 0);
  strip.setPixelColor(1, 250, 0, 0);
  strip.setPixelColor(2, 250, 0, 0);
  strip.setPixelColor(3, 250, 0, 0);
  strip.show();
}

// WALK
void Led_Walking() {
  strip.clear();
  strip.setPixelColor(0, 0, 250, 0);
  strip.setPixelColor(1, 0, 250, 0);
  strip.setPixelColor(2, 0, 250, 0);
  strip.setPixelColor(3, 0, 250, 0);
  strip.show();
  delay(2000);
}


Подключаемый файл global.h:


#include "Arduino.h"

// ROBOT
enum {Stand_By, Legs_Deployment, Moving_Forward, Folding_Legs, Restart};
byte Sequence = Stand_By;
enum {Rotation_Left, Rotation_Right, Head_Led, Door_Led, Opening, Closing, Right, Left};
const byte Distance_To_Activate_Switch (4);
const byte Distance_Obstacle (20);

// LED
const byte Led_Pin (12);
const byte Led_Count (4);

// ULTRASONIC SENSOR
const byte Ultrasonic_Sensor_Trigger_Pin (6);
const byte Ultrasonic_Sensor_Echo_Pin (8);
long Measure = 0;
long Distance_mm = 0;

// DOOR
const byte Door_Servo_Pin (1);
const int Door_Servo_Open (330);
const int Door_Servo_Close (205);
const int Door_Servo_Speed (150);

// HIP
const byte Hip_Servo_Front_Right_Pin (14);
const int Hip_Servo_Front_Right_Centered (283);
const int Hip_Servo_Front_Right_Move (70);

const byte Hip_Servo_Front_Left_Pin (12);
const int Hip_Servo_Front_Left_Centered (321);
const int Hip_Servo_Front_Left_Move (66);

const byte Hip_Servo_Rear_Right_Pin (3);
const int Hip_Servo_Rear_Right_Centered (347);
const int Hip_Servo_Rear_Right_Move (62);

const byte Hip_Servo_Rear_Left_Pin (11);
const int Hip_Servo_Rear_Left_Centered (268);
const int Hip_Servo_Rear_Left_Move (64);

const int delay_hip (400);

// KNEE
const byte Knee_Servo_Front_Right_Pin (2);
const int Knee_Servo_Front_Right_Bent (220);
const int Knee_Servo_Front_Right_Partially_Unfolded (343);
const int Knee_Servo_Front_Right_Unfolded (429);

const byte Knee_Servo_Front_Left_Pin (13);
const int Knee_Servo_Front_Left_Bent (216);
const int Knee_Servo_Front_Left_Partially_Unfolded (335);
const int Knee_Servo_Front_Left_Unfolded (423);

const byte Knee_Servo_Rear_Right_Pin (15);
const int Knee_Servo_Rear_Right_Bent (211);
const int Knee_Servo_Rear_Right_Partially_Unfolded (340);
const int Knee_Servo_Rear_Right_Unfolded (408);

const byte Knee_Servo_Rear_Left_Pin (4);
const int Knee_Servo_Rear_Left_Bent (200);
const int Knee_Servo_Rear_Left_Partially_Unfolded (349);
const int Knee_Servo_Rear_Left_Unfolded (436);

const byte Knee_Servo_Speed (130);
const byte delay_knee (150);
const byte Knee_Vertical_Amplitude (8);

// HEAD
const byte Head_Servo_Pin (0);
const int Head_Servo_Start_Position (339);
const int Head_Servo_Speed (100);

В скетче кроме «global.h» ещё подключаются библиотеки Adafruit_NeoPixel и Adafruit_PWMServoDriver:
#include <Adafruit_NeoPixel.h>
#include <Adafruit_PWMServoDriver.h>

Если эти библиотеки не установлены в Arduino Ide, при компиляции скетча появятся ошибки, выглядит это примерно так:




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




Когда библиотека установлена, около её названия будет отображаться надпись «INSTALLED» («УСТАНОВЛЕНО»):


Заключение

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



Автор: Greg06
Перевод: RobotoTehnika.ru