Роботизированные руки с каждым годом находят все большее применение в жизни современного общества, особенно в тех приложениях, в которых требуется скорость, точность и безопасность действий. Их можно не только увидеть в современных кинофильмах, например, в серии фильмов "Железный человек", но их еще в большом количестве и ассортименте производят такие компании как Fanuc, Kuka, Denso, ABB, Yaskawa и т.д. Эти роботизированные руки используются на производственных линиях по сборке автомобилей, горнодобывающей промышленности, химических заводах и т.п.
В этой статье мы рассмотрим создание роботизированной руки (Robotic Arm) с помощью платы Arduino и сервомоторов MG995. Эта роботизированная рука будет иметь 4 степени свободы (без учета захвата) и управляться с помощью потенциометров. Кроме этого, мы также сможем записывать и в дальнейшем воспроизводить все движения руки – именно так во многих случаях и программируются роботизированные руки для работы на реальных производствах.
Необходимые компоненты
- Плата Arduino Nano (купить на AliExpress).
- Сервомотор MG-995 (5 шт.) (купить на AliExpress).
- Потенциометр (5 шт.) (купить на AliExpress).
- Перфорированная плата.
- Скелет роботизированной руки и крепления.
Примечание: скелет данной роботизированной руки полностью напечатан на 3D принтере. Если у вас есть доступ к 3D принтеру, то вы можете самостоятельно напечатать все части этой руки на нем по приведенным ниже файлам. Если у вас нет доступа к 3D принтеру, то вы можете изготовить скелет этой руки из акрилового волокна или дерева. В простейшем случае вы можете изготовить скелет этой руки из обычных листов картона, как сделано в простейшей роботизированной руке на основе платы Arduino.
3D печать и сборка роботизированной руки
Вначале мы пытались разработать дизайн этой руки самостоятельно, но потом обнаружили что на сервисе Thingiverse есть достаточно много потрясающих дизайнов подобных роботизированных рук и решили "не изобретать заново велосипед". Мы обнаружили, что роботизированная рука Robotic Arm V2.0 by Ashing будет прекрасно работать с сервомоторами MG995, поэтому она отлично подходит для нашего проекта.
Поэтому перейдите по приведенной ссылке на Thingiverse и скачайте там файлы модели этой руки. Всего рука содержит 14 компонентов для 3D печати, STL файлы для всех из них можно скачать по приведенной ссылке. Мы использовали программное обеспечение Cura 3.2.1 от Ultimaker для обработки STL файлов и 3D принтер TEVO tarantula для печати компонентов руки.
Дизайн всех компонентов этой руки достаточно прост, поэтому его можно напечатать практически на любом 3D принтере. У нас на печать всех компонентов руки ушло примерно 4,5 часа. Инструкция по сборке руки приведена на этой странице, поэтому в нашей статье не будем останавливаться на ней подробно.
Единственный момент, на который нам бы хотелось обратить внимание – возможно вам придется вручную подровнять (подшлифовать) края некоторых компонентов чтобы "втиснуть" туда сервомотор - обычно крепления для сервомоторов проектируются с таким расчетом, чтобы запихивать в них сервомоторы приходилось с некоторым усилием, так они будут лучше держаться. Вам понадобится 20 болтов диаметром 3 мм для сборки компонентов этой роботизированной руки.
Перед тем как окончательно закрутить болты удостоверьтесь в том, что сервомоторам ничего не мешает вращаться в необходимых направлениях. Провода к трем сервомоторам вам придется тянуть снаружи конструкции руки, при необходимости удлинить эти провода можно использовать соединители папа-мама. Удостоверьтесь в том, что при работе руки эти провода не будут перекручиваться или каким либо другим образом мешать работе руки. После сборки у нас получилась рука следующего вида:
Работа схемы
Схема роботизированной руки на Arduino представлена на следующем рисунке.
Поскольку сервомоторы MG995 работают от питающего напряжения 5V, то их можно запитать от соответствующего разъема платы Arduino. Управляющие контакты сервомоторов подключены к цифровым контактам платы Arduino, на которых возможно формирование сигналов ШИМ (широтно-импульсной модуляции). Потенциометры подключены к аналоговым контактам платы Arduino для управления сервомоторами.
В любой момент времени у нас будет двигаться только один сервомотор, поэтому потребляемый ток не превысит 150 мА, в связи с чем регулятор питания платы Arduino без проблем выдержит такую нагрузку. Сама плата Arduino в нашем проекте запитывалась через USB кабель от компьютера.
После пайки и сборки компонентов на перфорированной плате у нас получилась конструкция, показанная на следующем рисунке. Дополнительно в конструкцию проекта мы добавили разъем для подключения батарейки если в дальнейшем потребуется питание от нее.
Если вы раньше не сталкивались с сервомоторами рекомендуем прочитать статью про подключение сервомотора к плате Arduino.
Объяснение программы для Arduino
В программе мы должны предусмотреть возможность записи движений пользователя по вращению ручек потенциометров и их воспроизведения когда это потребуется. Поэтому в программе нам необходимо два режима – режим записи (Record mode) и режим воспроизведения (Play mode). Пользователь сможет переключаться между этими двумя режимами с помощью монитора последовательной связи (serial monitor). Полный текст программы приведен в конце данной статьи, здесь же мы рассмотрим его наиболее важные фрагменты.
Как обычно, программу начнем с подключения необходимых заголовочных файлов. Мы будем использовать библиотеку Servo.h для управления сервомоторами. У нас 5 сервомоторов в проекте, поэтому каждому из них необходимо дать уникальное имя. Также мы инициализируем переменные, которые затем будем использовать в программе. Мы сделали все переменные глобальными – но вы самостоятельно можете оптимизировать эту часть программы если захотите. Также мы инициализировали массив saved_data, в который мы будем записывать движения руки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <Servo.h> //Servo header file //объявим объекты для 5 сервомоторов Servo Servo_0; Servo Servo_1; Servo Servo_2; Servo Servo_3; Servo Gripper; //глобальные объявления переменных int S0_pos, S1_pos, S2_pos, S3_pos, G_pos; int P_S0_pos, P_S1_pos, P_S2_pos, P_S3_pos, P_G_pos; int C_S0_pos, C_S1_pos, C_S2_pos, C_S3_pos, C_G_pos; int POT_0,POT_1,POT_2,POT_3,POT_4; int saved_data[700]; //массив для записи движений руки int array_index=0; char incoming = 0; int action_pos; int action_servo; |
Внутри функции void setup мы инициализируем последовательную связь со скоростью 9600 бод/с. Также мы укажем плате Arduino к каким ее контактам подключены сервомоторы – в нашем случае это контакты 3,5,6,9 и 10. Также в функции setup поскольку она запускается при подаче питания на плату Arduino мы зададим начальное положение руки с помощью поворотов осей сервомоторов на заданные углы. Также в функции setup мы выведем в окно монитора последовательной связи сообщение с просьбой нажать клавишу R или P чтобы потом мы могли выполнить заданное действие (запись или воспроизведение).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void setup() { Serial.begin(9600); //последовательная связь для целей отладки //контакты, к которым подключены сервомоторы Servo_0.attach(3); Servo_1.attach(5); Servo_2.attach(6); Servo_3.attach(9); Gripper.attach(10); //поворачиваем сервомоторы в начальное положение для руки Servo_0.write(70); Servo_1.write(100); Servo_2.write(110); Servo_3.write(10); Gripper.write(10); Serial.println("Press 'R' to Record and 'P' to play"); //Instrust the user } |
Также мы запрограммируем функцию Read_POT, которая будет считывать аналоговые значения с выходов всех потенциометров и преобразовывать их в значения для позиций сервомоторов. Как мы знаем, значения на выходе АЦП (аналогово-цифровой преобразователь) платы Arduino могут изменяться в диапазоне 0-1023, а позиция осей сервомоторов может изменяться только в диапазоне 0-180. Поскольку сервомоторы у нас бюджетные и потому не очень точные, то в целях безопасности целесообразно сузить диапазон их используемых углов поворота с 0-180 до 0-170. Поэтому мы будем использовать функцию map чтобы конвертировать диапазон 0-1023 в диапазон 10-170 для всех наших 5 сервомоторов.
1 2 3 4 5 6 7 8 9 |
void Read_POT() //функция для считывания аналоговых значений с потенциометров и преобразования их в значения для сервомоторов { POT_0 = analogRead(A0); POT_1 = analogRead(A1); POT_2 = analogRead(A2); POT_3 = analogRead(A3); POT_4 = analogRead(A4); //Read the Analog values form all five POT S0_pos = map(POT_0,0,1024,10,170); //Map it for 1st Servo (базовый мотор) S1_pos = map(POT_1,0,1024,10,170); //Map it for 2nd Servo (мотор для бедра) S2_pos = map(POT_2,0,1024,10,170); //Map it for 3rd Servo (мотор для плеча) S3_pos = map(POT_3,0,1024,10,170); //Map it for 4th Servo (мотор для шеи) G_pos = map(POT_4,0,1024,10,170); //Map it for 5th Servo (мотор для захвата) } |
Код для режима записи
В режиме записи мы должны записывать все движения руки – то есть позицию и номер каждого сервомотора. Эти данные мы будем записывать в массив saved_data.
При работе с сервомоторами существует такая проблема как их дрожание/колебание (jitter) во время их работы. Для решения этой проблемы существует множество способов. Но первоначально вы должны определить из-за чего именно возникает эта проблема – из-за схемы управления сервомотором или из-за неточности значения позиции, которая записывается в сервомотор. В нашем случае мы использовали монитор последовательной связи и обнаружили что значение позиции сервомотора не является постоянным и иногда колеблется/изменяется случайным образом.
Таким образом, мы запрограммировали плату Arduino таким образом, чтобы считывать значения с выходов потенциометров дважды и затем сравнивать их между собой. Значение признается истинным (правильным) только тогда когда оба значения совпадают, иначе значение игнорируется. И этот прием решил проблему колебания/джиттера сервомоторов в нашем случае. Также убедитесь в том, что потенциометры надежно (крепко) подсоединены к аналоговым контактам платы Arduino (в нашем случае они припаяны). Любое не очень надежное соединение может приводить к джиттеру. Переменная P_x_pos используется для сохранения старых значений потенциометров, а новые значения потенциометров считываются и преобразуются в заданный диапазон с помощью ранее описанной функции Read_POT.
1 2 3 4 5 6 7 8 |
Read_POT(); //считываем значения с потенциометров в 1-й раз //сохраняем их в переменных чтобы сравнить их потом P_S0_pos = S0_pos; P_S1_pos = S1_pos; P_S2_pos = S2_pos; P_S3_pos = S3_pos; P_G_pos = G_pos; Read_POT(); //считываем значения с потенциометров во 2-й раз |
Теперь, мы должны устанавливать позицию сервомотора если значение истинно. После этого мы еще должны сохранить номер сервомотора и его позицию в массив. Можно было бы, конечно, использовать два массива для раздельного хранения номера сервомотора и его позиции, но в целях экономии памяти и уменьшения сложности программы мы решили использовать один массив при помощи добавления разделителя к позиции перед сохранением ее в массиве.
1 2 3 4 5 6 7 8 9 10 11 12 |
if (P_S0_pos == S0_pos) //если старое и новое значения совпадают { Servo_0.write(S0_pos); //управляем позицией оси сервомотора if (C_S0_pos != S0_pos) //если значение с выхода потенциометра изменилось { saved_data[array_index] = S0_pos + 0; //сохраняем новую позицию в массив. Ноль добавляется для нулевого мотора (для понимания сути операции) array_index++; //увеличиваем индекс массива } C_S0_pos = S0_pos; //сохраняем предыдущее значение чтобы потом проверять изменилось ли значение на выходе потенциометра (повернули ли его ручку) } |
Число разделитель в массиве для нулевого сервомотора (Servo_0) у нас будет 0, для Servo_1 – 1000 и т.д. для Servo_3 – 3000, а для захвата (Gripper) – 4000. Фрагмент кода, в котором в массив записываются наши данные с добавлением разделителя, выглядит следующим образом:
1 2 3 4 5 |
saved_data[array_index] = S0_pos + 0; //сохраняем новую позицию в массив. Ноль в качестве разделителя добавляется для нулевого сервомотора (для понимания сути операции) saved_data[array_index] = S1_pos + 1000; //1000 добавляется для 1-го сервомотора в качестве разделителя saved_data[array_index] = S2_pos + 2000; //2000 добавляется для 2-го сервомотора в качестве разделителя saved_data[array_index] = S3_pos + 3000; //3000 добавляется для 3-го сервомотора в качестве разделителя saved_data[array_index] = G_pos + 4000; //4000 добавляется для 4-го сервомотора (захват) в качестве разделителя |
Код для режима воспроизведения
Когда у нас все движения сервомоторов уже записаны в массиве saved_data мы можем переключиться в режим воспроизведения этих действий при помощи ввода символа ‘P” в окне монитора последовательной связи. В режиме воспроизведения у нас есть доступ к каждому элементу этого массива и, зная известные нам разделители, мы можем разделить значения этого массива чтобы извлечь из них номер мотора и его позицию.
Мы будем использовать цикл for для навигации по элементам массива. Затем в две переменные с именами action_servo и action_pos мы будем записывать номер сервомотора и его позицию соответственно. Чтобы определить номер сервомотора мы будем делить значение из массива на 1000, а позиция сервомотора будет извлекаться из трех последних цифр этого числа (значения из массива).
К примеру, если в массиве сохранено значение 3125, то это будет означать, что 3-й сервомотор необходимо повернуть в позицию 125.
1 2 3 4 |
for (int Play_action=0; Play_action<array_index; Play_action++) //навигация по элементам массива { action_servo = saved_data[Play_action] / 1000; //первый символ элемента массива будет обозначать номер сервомотора action_pos = saved_data[Play_action] % 1000; // три последних символа элемента массива будут обозначать позицию сервомотора |
После этого все, что остается сделать, это повернуть нужный сервомотор на заданную позицию. Для выполнения этих операций мы использовали оператор выбора switch case.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
switch(action_servo){ //проверяем каким сервомотором необходимо управлять case 0: //Если это 0-й мотор Servo_0.write(action_pos); break; case 1:// Если это 1-й мотор Servo_1.write(action_pos); break; case 2:// Если это 2-й мотор Servo_2.write(action_pos); break; case 3:// Если это 3-й мотор Servo_3.write(action_pos); break; case 4:// Если это 4-й мотор Gripper.write(action_pos); break; |
Основная функция loop
Внутри основной функции программы loop нам необходимо всего лишь проверять что пользователь ввел в окне монитора последовательной связи и, соответственно, запускать на выполнение режим записи или режим воспроизведения. Если пользователь нажал ‘R’ – активируется режим записи, а если ‘P’ – режим воспроизведения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
void loop() { if (Serial.available() > 1) //если что то принято из последовательного монитора { incoming = Serial.read(); if (incoming == 'R') Serial.println("Robotic Arm Recording Started......"); if (incoming == 'P') Serial.println("Playing Recorded sequence"); } if (incoming == 'R') //если пользователь выбрал режим записи Record(); if (incoming == 'P') //если пользователь выбрал режим воспроизведения Play(); } |
Тестирование работы роботизированной руки
Сделайте все соединения, которые показаны на выше приведенной схеме, и загрузите программу в плату Arduino. Подайте питание на плату Arduino Nano через порт USB с вашего компьютера и откройте окно монитора последовательной связи – в нем вы увидите приветственное сообщение.
Теперь введите R в окне монитора последовательной связи и нажмите ввод. Внизу монитора последовательной связи должна быть установлена опция Newline. Пример работы программы в этом режиме показан на следующем рисунке:
Информация, показанная на этом рисунке, может быть использована для отладки. Цифры, начинающиеся с 69, это текущая позиция сервомоторов с 0-го до 5-го. Значения индекса – это размер массива. Помните, что максимальный размер массива ограничен 700 числами, поэтому старайтесь не превышать этот размер. После того как вы завершите запись нажмите P и ввод в окне монитора последовательной связи и программа переключится в режим воспроизведения и на экране тогда появится примерно следующая картина:
Во время режима воспроизведения роботизированная рука будет повторять те же самые движения, которые она совершала в режиме записи. Эти движения она будет выполнять снова и снова до тех пор пока вы не прервете ее работу из окна монитора последовательной связи.
Исходный код программы (скетча)
Код программы достаточно подробно объяснен выше в статье, поэтому, я надеюсь, вы без особо труда разберетесь в нем и при необходимости измените его по своему желанию. Перевод комментариев к коду программы также представлен в предыдущем разделе статьи, здесь второй раз решил не переводить.
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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
/* Robotic ARM with Record and Play option using Arduino Code by: B. Aswinth Raj Website: http://www.circuitdigest.com/ Dated: 05-08-2018 */ #include <Servo.h> //Servo header file //Declare object for 5 Servo Motors Servo Servo_0; Servo Servo_1; Servo Servo_2; Servo Servo_3; Servo Gripper; //Global Variable Declaration int S0_pos, S1_pos, S2_pos, S3_pos, G_pos; int P_S0_pos, P_S1_pos, P_S2_pos, P_S3_pos, P_G_pos; int C_S0_pos, C_S1_pos, C_S2_pos, C_S3_pos, C_G_pos; int POT_0,POT_1,POT_2,POT_3,POT_4; int saved_data[700]; //Array for saving recorded data int array_index=0; char incoming = 0; int action_pos; int action_servo; void setup() { Serial.begin(9600); //Serial Monitor for Debugging //Declare the pins to which the Servo Motors are connected to Servo_0.attach(3); Servo_1.attach(5); Servo_2.attach(6); Servo_3.attach(9); Gripper.attach(10); //Write the servo motors to initial position Servo_0.write(70); Servo_1.write(100); Servo_2.write(110); Servo_3.write(10); Gripper.write(10); Serial.println("Press 'R' to Record and 'P' to play"); //Instruct the user } void Read_POT() //Function to read the Analog value form POT and map it to Servo value { POT_0 = analogRead(A0); POT_1 = analogRead(A1); POT_2 = analogRead(A2); POT_3 = analogRead(A3); POT_4 = analogRead(A4); //Read the Analog values form all five POT S0_pos = map(POT_0,0,1024,10,170); //Map it for 1st Servo (Base motor) S1_pos = map(POT_1,0,1024,10,170); //Map it for 2nd Servo (Hip motor) S2_pos = map(POT_2,0,1024,10,170); //Map it for 3rd Servo (Shoulder motor) S3_pos = map(POT_3,0,1024,10,170); //Map it for 4th Servo (Neck motor) G_pos = map(POT_4,0,1024,10,170); //Map it for 5th Servo (Gripper motor) } void Record() //Function to Record the movements of the Robotic Arm { Read_POT(); //Read the POT values for 1st time //Save it in a variable to compare it later P_S0_pos = S0_pos; P_S1_pos = S1_pos; P_S2_pos = S2_pos; P_S3_pos = S3_pos; P_G_pos = G_pos; Read_POT(); //Read the POT value for 2nd time if (P_S0_pos == S0_pos) //If 1st and 2nd value are same { Servo_0.write(S0_pos); //Control the servo if (C_S0_pos != S0_pos) //If the POT has been turned { saved_data[array_index] = S0_pos + 0; //Save the new position to the array. Zero is added for zeroth motor (for understading purpose) array_index++; //Increase the array index } C_S0_pos = S0_pos; //Saved the previous value to check if the POT has been turned } //Similarly repeat for all 5 servo Motors if (P_S1_pos == S1_pos) { Servo_1.write(S1_pos); if (C_S1_pos != S1_pos) { saved_data[array_index] = S1_pos + 1000; //1000 is added for 1st servo motor as differentiator array_index++; } C_S1_pos = S1_pos; } if (P_S2_pos == S2_pos) { Servo_2.write(S2_pos); if (C_S2_pos != S2_pos) { saved_data[array_index] = S2_pos + 2000; //2000 is added for 2nd servo motor as differentiator array_index++; } C_S2_pos = S2_pos; } if (P_S3_pos == S3_pos) { Servo_3.write(S3_pos); if (C_S3_pos != S3_pos) { saved_data[array_index] = S3_pos + 3000; //3000 is added for 3rd servo motor as differentiater array_index++; } C_S3_pos = S3_pos; } if (P_G_pos == G_pos) { Gripper.write(G_pos); if (C_G_pos != G_pos) { saved_data[array_index] = G_pos + 4000; //4000 is added for 4th servo motor as differentiator array_index++; } C_G_pos = G_pos; } //Print the value for debugging Serial.print(S0_pos); Serial.print(" "); Serial.print(S1_pos); Serial.print(" "); Serial.print(S2_pos); Serial.print(" "); Serial.print(S3_pos); Serial.print(" "); Serial.println(G_pos); Serial.print ("Index = "); Serial.println (array_index); delay(100); } void Play() //Functon to play the recorded movements on the Robotic ARM { for (int Play_action=0; Play_action<array_index; Play_action++) //Navigate through every saved element in the array { action_servo = saved_data[Play_action] / 1000; //The fist character of the array element is split for knowing the servo number action_pos = saved_data[Play_action] % 1000; //The last three characters of the array element is split to know the servo postion switch(action_servo){ //Check which servo motor should be controlled case 0: //If zeroth motor Servo_0.write(action_pos); break; case 1://If 1st motor Servo_1.write(action_pos); break; case 2://If 2nd motor Servo_2.write(action_pos); break; case 3://If 3rd motor Servo_3.write(action_pos); break; case 4://If 4th motor Gripper.write(action_pos); break; } delay(50); } } void loop() { if (Serial.available() > 1) //If something is received from serial monitor { incoming = Serial.read(); if (incoming == 'R') Serial.println("Robotic Arm Recording Started......"); if (incoming == 'P') Serial.println("Playing Recorded sequence"); } if (incoming == 'R') //If user has selected Record mode Record(); if (incoming == 'P') //If user has selected Play Mode Play(); } |
И серводвигатель MG 995 при пиковой нагрузке потребляет больше одного ампера. Я бы посоветовал подключать их от отдельного источника питания.
Да, это верно. В большинстве других аналогичных проектов на нашем сайте эти серводвигатели запитаны от отдельного источника питания
Переменные :C_S0_pos, C_S1_pos, C_S2_pos, C_S3_pos, C_G_pos; что делают в этом коде? По моему это ошибка. В коде поменять C_S0_pos на P_S0_pos. и далее по списку. И по-моему тогда все должно заработать
Переменные C_S0_pos, C_S1_pos, C_S2_pos, C_S3_pos, C_G_pos хранят предыдущие значения потенциометров. Мы изменяем положение конкретного сервомотора только если изменилось положение соответствующего ему потенциометра. Поэтому в этом плане не вижу никакой ошибки в коде
Здравствуйте а у вас сервопривод на основании поворачивается только на 180 градусов или 360?
Добрый вечер. Они все однотипные и поворачиваются только на 180 градусов.
Отличный сайт.Много интересного.Хотелось бы увидеть серворук с шестью а то и семью моторами. с оптимизированным кодом. Кто сможет написать код для такого?
А что вы понимаете под оптимизированным кодом? Чем вас не устраивает код в данном проекте?
Здравствуйте, можно фото платы с обратной стороны. Не до конца понял как всё соединять
Добрый вечер. К сожалению, фото платы с обратной стороны у меня нет поскольку эта статья является переводом с иностранного сайта. Но на схеме вроде бы подробно все показано что куда соединять, что конкретно вам непонятно?
У меня почему-то не работает один сервопривод подключенный к порту D3, а остальные начинают крутиться при подаче напряжения. Вот я и думаю что навряд-ли ошибка в коде
Ну а вы попробуйте подключить не сразу все сервоприводы, а подключайте их по одному и проверяйте работают они или нет. Можно даже попробовать подключать один и тот же сервопривод к разным контактам платы. Код ведь легко изменить для управления одним сервоприводом. Так вы быстро найдете причину в чем у вас неисправность - в сервоприводах, контактах платы или коде.
Может ли это быть из-за того что сервоприводы SG 90, а не MG 995?
Именно такой эффект, как вы описали, вряд ли возможен при смене серводвигателей с MG 995 на SG 90, но все таки SG 90 - это одни из самых дешевых серводвигателей на рынке и поэтому у них качество компонентов, конечно, похуже, чем у более дорогих моделей. И на работу этих серводвигателей в некоторых проектах все же есть определенные жалобы. Не сказать, конечно, что сильно плохие серводвигатели, но определенные недостатки у них, конечно, есть
Добрый день! У меня такая же проблема. я так и не смог решить ее. Скажите у Вас получилось? Если решили подскажите ответ, пожалуйста!
Добрый вечер. Какая у вас проблема? Не работает только один сервопривод, подключенный к порту D3?
Добрый день. Происходит следующее: включается одновременно четыре сервомотора и вращаются без остановки не реагируют на потенциометры. В мониторе последовательной связи идет постоянная запись пока не отключишь питание.
От админа: а попробуйте из функции void loop() весь код удалить, чтобы выполнилась только часть кода из функции void setup (). Что при этом у вас будет происходить?
Здравствуйте! Собрал подобную руку, управляется также с потенциометров. Меня напрягает один момент, когда серво упирается в конструкцию, а на потенциометре еще запас хода и его выкручиваешь до конца, начинают хаотично дергаться все (их 4) сервоприводы. Скажите так и должно быть? Скетч самый простой наверно, через val (0, 1023, 0, 180)
Добрый вечер. Да, проблема дрожания сервоприводов может наблюдаться и с ней желательно бороться. Но в представленной здесь статье ведь описан способ борьбы с этой проблемой - считывать значения с выходов потенциометров дважды и затем сравнивать их между собой. Значение признается истинным (правильным) только тогда когда оба значения совпадают, иначе значение игнорируется
Здравствуйте! Подскажите, что за элементы размещены на макетке помимо потенциометров и платы? Это конденсатор? Имеет ли он отношение к руке, или никуда не подключен?
И большое спасибо за статью!)
Добрый вечер. Анна, это коннекторы (разъемы), к которым подключаются провода от серводвигателей
Задолбала проклятая быдло - петушиная реклама!!!
Для чего вообще вставлять эту поганую дрянь между тех. описанием проекта?
Это сильно отвлекает и раздражает!!!
Кому вообще интересно просматривать эту херню?
Не вставляйте пожалуйста больше эту глупую рекламу в промежутках между теста!
Олег, на такую достаточно длинную статью я насчитал всего 3 рекламных блока в тексте статьи - по сегодняшним меркам это достаточно мало. И при этом на моем сайте нет никакой назойливой рекламы - всплывающих блоков по центру или в углах, которые нужно принудительно закрывать чтобы получить доступ к контенту. Реклама контекстная, подстраивается под интересы конкретного пользователя, то есть если содержание рекламы вам очень неприятно - значит до захода на мой сайт вы ходили по "неприятным" сайтам, здесь я уже ничего не могу изменить.
При этом весь контент на моем сайте полностью бесплатен - я не прошу никаких денег за то, к примеру, чтобы получить доступ к коду программы как это делают на некоторых других похожих сайтах. Я развиваю сайт только на доходы от рекламы и большинство сайтов поступают точно также. Не будет рекламы - и количество полезного контента в сети интернет снизится в разы.
Наверное, вы зашли на сайт в плохом настроении, поэтому так резко высказались. Хотел бы пожелать вам в будущем более хорошего настроения