В данной статье мы рассмотрим создание аниматронного глаза на основе платы Arduino. Модель данного глаза будет напечатана на 3D принтере. Автор данного проекта (ссылка на оригинал приведена в конце данной статьи) прежде чем приступить к созданию данного глаза изучил достаточно много работ по мехатронике. Огромное спасибо он хотел бы сказать пользователю Will Cogley за создание 3D модели данного глаза и других необходимых ресурсов и опубликование их в открытом доступе.
Существует две версии дизайна данного аниматронного глаза, одна попроще, а другая посложнее. В этой статье мы рассмотрим создание модели глаза которая попроще.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- Сервомотор SG90 – 6 шт. (купить на AliExpress).
- Винты M2, M3 и M4.
- Макетная плата.
- Соединительные провода.
- 3D принтер.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Сборка механизма аниматронного глаза
Для начала нам необходимо напечатать все компоненты нашего аниматронного глаза (показанные на рисунке ниже) на 3D принтере. 3D модели всех этих компонентов доступны по следующей ссылке. Потом вам необходимо сам глаз и некоторые другие компоненты его механизма отшлифовать чтобы они лучше прилегали друг к другу при сборке.
Сборку механизма проекта целесообразно начать с закрепления 5 или 6 сервомоторов на модуле Servo_Block как показано на рисунке ниже.
Далее необходимо соединить модуль сервоприводов (Servo_Block) с основной базой проекта (Main_Base) как показано на рисунке ниже. Автор проекта использовал установочные винты 12mm M3 для изготовления ног устройства.
Далее необходимо подсоединить адаптер глаза (Eye-Adaptor) для обоих глаз. После этого к адаптеру необходимо подсоединить держатель глаза (Eye-Holder) и трехточечный коннектор с вилкой. На этом этапе вы должны получить следующую конструкцию:
После того как это будет сделано необходимо подключить последний 6-й сервомотор к базе Sub_Base и потом соединить эту базу с основной базой (Main_Base) с помощью винтов. После этого можно вставлять PlaceHolder Eye в адаптер глаза. X-плечо (X-Arm) и Y-плечо (Y-Arm) также необходимо закрепить с помощью винтов.
Потом необходимо подсоединить коннекторы века глаза к веку – для этого мы также использовали винты M3. Если вы все сделали правильно, то конструкция глаза у вас должна выглядеть как на рисунке ниже: веки должны быть соединены с коннекторами век и скреплены винтами с плечами сервомоторов.
Схема проекта
Схема 3D аниматронного глаза на основе платы Arduino представлена на следующем рисунке.
Как видите, она достаточно проста. В оригинальном проекте от Will Cogley управление 6 сервомоторами производилось с помощью микросхемы PCA9685, представляющую собой 16-канальный 12-битный драйвер управления сервомоторами, но мы обошлись без нее поскольку библиотека для управления сервомоторами платы Arduino (Servo Library) прекрасно справляется с управлением 6 сервомоторами. Плата Arduino Nano как раз содержит 6 контактов, на которых возможно формирование ШИМ сигналов, все эти контакты мы и будем использовать для управления сервомоторами.
Объяснение программы для Arduino
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Для управления сервомоторами в нашем проекте мы будем использовать библиотеку Servo.h. Более подробно про подключение сервомотора к плате Arduino вы можете прочитать в этой статье.
Первым делом в коде программы мы подключим необходимую нам библиотеку Servo.h и создадим 6 объектов для управления 6 сервомоторами.
1 2 3 4 5 6 7 |
#include <Servo.h> Servo top_left_eyelid; Servo bottom_left_eyelid; Servo top_right_eyelid; Servo bottom_right_eyelid; Servo Yarm; Servo Xarm; |
Далее, в функции setup(), мы укажем к каким контактам какие сервомоторы подключены.
1 2 3 4 5 6 |
top_left_eyelid.attach(10); bottom_left_eyelid.attach(11); top_right_eyelid.attach(5); bottom_right_eyelid.attach(6); Yarm.attach(9); Xarm.attach(3); |
Затем инициализируем последовательную связь для целей отладки и вызовем ряд функций для того чтобы открыть глаз и установить его в центральное положение. Для стабильности работы добавим задержку.
1 2 3 4 |
Serial.begin(9600); open_eye(); eye_ball_centert(); delay(2000); |
Также мы запрограммируем ряд функций для управления нашим аниматронным глазом – и это будет наиболее важная часть нашей программы. Функция open_eye() будет открывать глаз, для этого необходимо один из сервомоторов повернуть в направлении по часовой стрелке, а другой – против часовой стрелки. Эти действия необходимо выполнить для обоих век (левого и правого).
1 2 3 4 5 6 |
void open_eye() { top_left_eyelid.write(55); bottom_left_eyelid.write(36); top_right_eyelid.write(2); bottom_right_eyelid.write(160); } |
В функции close_eye() (закрытия глаза) мы будем делать те же самые действия что и в функции open_eye(), но наоборот (в обратном порядке).
1 2 3 4 5 6 |
void close_eye() { top_left_eyelid.write(2); bottom_left_eyelid.write(120); top_right_eyelid.write(46); bottom_right_eyelid.write(55); } |
Функции look_up() и look_down() будут управлять движениями Y-плеча вверх и вниз соответственно.
1 2 3 4 5 6 |
void look_up() { Yarm.write(132); } void look_down() { Yarm.write(45); } |
Функции eye_ball_left() и eye_ball_right() управляют движениями глаза по горизонтали.
1 2 3 4 5 6 |
void eye_ball_left() { Xarm.write(50); } void eye_ball_right() { Xarm.write(130); } |
Функция eye_ball_centert() устанавливает глаз по центру, то есть выставляет угол 90 градусов для X-плеча и Y-плеча.
1 2 3 4 |
void eye_ball_centert() { Xarm.write(90); Yarm.write(90); } |
Далее запрограммируем ряд функций, которые облегчат нам работу с нашим аниматронным глазом. Первой из них будет функция synchronous_close() – она будет один раз закрывать и открывать глаз.
1 2 3 4 5 6 |
void synchronous_close() { close_eye(); delay(420); open_eye(); delay(222); } |
Функция random_close() будет случайно открывать и закрывать глаз. Смотрится это впечатляюще.
1 2 3 4 5 6 |
void random_close() { close_eye(); delay(random(220, 880)); open_eye(); delay(random(220, 880)); } |
В функции random_movement() мы используем функцию random() внутри функции delay чтобы смоделировать случайные движения глаза.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void random_movement() { Xarm.write(60); delay(random(250, 340)); Yarm.write(80); delay(random(250, 340)); Xarm.write(120); delay(random(250, 340)); Yarm.write(140); Xarm.write(60); delay(random(250, 340)); Yarm.write(80); delay(random(250, 340)); Xarm.write(120); delay(random(250, 340)); Yarm.write(140); eye_ball_centert(); delay(300); synchronous_close(); random_close(); } |
Внутри основной функции программы void loop() мы буде двигать глаз влево, вправо, по центру, затем вверх и вниз. После этого мы будем делать моргание глаза со случайными движениями. А в конце у нас будут два цикла for – они показывают пример того как сделать движения глаза плавными.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
void loop() { eye_ball_left(); delay(680); eye_ball_right(); delay(680); eye_ball_centert(); delay(450); synchronous_close(); eye_ball_centert(); delay(450); look_up(); delay(400); look_down(); delay(400); eye_ball_centert(); delay(300); random_close(); delay(450); look_up(); delay(400); look_down(); delay(400); random_movement(); delay(400); eye_ball_centert(); delay(300); top_left_eyelid.write(2); bottom_left_eyelid.write(120); delay(200); top_left_eyelid.write(55); bottom_left_eyelid.write(36); delay(200); open_eye(); delay(500); for (int i = 60; i < 120; i++) { Xarm.write(i); Yarm.write(i - 5); delay(10); } eye_ball_centert(); delay(400); synchronous_close(); for (int i = 120; i > 60; i--) { Xarm.write(i); Yarm.write(i - 5); delay(10); } } |
Тестирование и отладка работы проекта
Первая проблема, с которой столкнулся автор данного проекта, заключалась в том, что держатели глаз (eye_place_holders) двигались недостаточно гладко и между ними и веками (eye_lids) было достаточно большое трение.
Решением этой проблемы явилось ослабление (или полный отказ) винтов, которые прикрепляют держатели глаз (Eye-Holder) к основанию устройства. Эти винты выделены красным цветом на следующей картинке.
Следующей проблемой явилось то, как подключить оси серводвигателей к векам (eye_lid) и X- и Y-плечам. Эта проблема оказалась достаточно трудной для решения потому что без правильных прокладок вся конструкция нашего проекта не могла двигаться гладко. На рисунке ниже представлено фото всех осей серводвигателей в нашем проекте.
Все остальное работало отлично и результаты этого процесса вы можете посмотреть в следующем видео.
Исходный код программы (скетча)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
#include <Servo.h> Servo top_left_eyelid; Servo bottom_left_eyelid; Servo top_right_eyelid; Servo bottom_right_eyelid; Servo Yarm; Servo Xarm; void setup() { top_left_eyelid.attach(10); bottom_left_eyelid.attach(11); top_right_eyelid.attach(5); bottom_right_eyelid.attach(6); Yarm.attach(9); Xarm.attach(3); Serial.begin(9600); open_eye(); eye_ball_centert(); delay(2000); } void loop() { eye_ball_left(); delay(680); eye_ball_right(); delay(680); eye_ball_centert(); delay(450); synchronous_close(); //synchronous_close(); eye_ball_centert(); delay(450); look_up(); delay(400); look_down(); delay(400); eye_ball_centert(); delay(300); random_close(); delay(450); look_up(); delay(400); look_down(); delay(400); random_movement(); delay(400); eye_ball_centert(); delay(300); top_left_eyelid.write(2); bottom_left_eyelid.write(120); delay(200); top_left_eyelid.write(55); bottom_left_eyelid.write(36); delay(200); open_eye(); delay(500); for (int i = 60; i < 120; i++) { Xarm.write(i); Yarm.write(i - 5); delay(10); } eye_ball_centert(); delay(400); synchronous_close(); for (int i = 120; i > 60; i--) { Xarm.write(i); Yarm.write(i - 5); delay(10); } } void random_movement() { Xarm.write(60); delay(random(250, 340)); Yarm.write(80); delay(random(250, 340)); Xarm.write(120); delay(random(250, 340)); Yarm.write(140); Xarm.write(60); delay(random(250, 340)); Yarm.write(80); delay(random(250, 340)); Xarm.write(120); delay(random(250, 340)); Yarm.write(140); eye_ball_centert(); delay(300); synchronous_close(); random_close(); } void random_close() { close_eye(); delay(random(220, 880)); open_eye(); delay(random(220, 880)); } void synchronous_close() { close_eye(); delay(420); open_eye(); delay(222); } void eye_ball_left() { Xarm.write(50); } void eye_ball_right() { Xarm.write(130); } void eye_ball_centert() { Xarm.write(90); Yarm.write(90); } void look_up() { Yarm.write(132); } void look_down() { Yarm.write(45); } void close_eye() { top_left_eyelid.write(2); bottom_left_eyelid.write(120); top_right_eyelid.write(46); bottom_right_eyelid.write(55); } void open_eye() { top_left_eyelid.write(55); bottom_left_eyelid.write(36); top_right_eyelid.write(2); bottom_right_eyelid.write(160); } |
При монтаже произошла ошибка и Eye Holder был смонтирован вверх ногами, поэтому и винты мешались и их пришлось выкрутить
Ну в результате у вас получилось собрать данный проект до конца?
Да, спасибо вашему сайту за инструкцию, собрал на Nano c дополнительным питанием и библиотекой ServoEasing
То ли это я так плохо распечатал, а потом и отшлифовал, то ли тут в целом, не оптимально рассчитана нагрузка на сервоприводы - очень плохо двигаются детали.
Сейчас экспериментирую с программированием мимики
https://disk.yandex.ru/i/KAybdiTcWDe6VA
Спасибо вам за конструктивный комментарий и фото собранной конструкции. Надеемся, у вас получится исправить все недочеты в нем