В этом уроке мы узнаем, как сделать роботизированную руку на основе платы Arduino, которой можно управлять по беспроводной сети и программировать с помощью специально созданного приложения для Android. Мы рассмотрим весь процесс ее создания, начиная с проектирования и 3D-печати деталей робота, подключения электронных компонентов и программирования Arduino, до разработки собственного приложения для Android для управления роботом-манипулятором.
С помощью ползунков в приложении мы можем вручную управлять движением каждого сервопривода или оси руки робота. Также с помощью кнопки «Сохранить» мы можем записать каждую позицию или шаг, а затем рука робота сможет автоматически запускать и повторять эти шаги. С помощью этой же кнопки мы можем приостановить автоматическую работу, а также сбросить или удалить все шаги, чтобы можно было записать новые.
Также на нашем сайте вы можете посмотреть аналогичные проекты роботизированных рук:
- простейшая роботизированная рука на Arduino Uno;
- роботизированная рука на Arduino с записью и воспроизведением действий;
- управляемая жестами роботизированная рука на Arduino Nano;
- роботизированная рука на микроконтроллере PIC16F877A.
Необходимые компоненты
- Плата Arduino Uno (купить на AliExpress).
- Сервомоторы MG-996 (3 шт.) (купить на AliExpress).
- Сервомоторы SG 90 (3 шт.) (купить на AliExpress).
- Bluetooth модуль HC-05 (купить на AliExpress).
- Источник питания постоянного тока 5 В, 2 А.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
3D модель роботизированной руки
Для начала я спроектировал манипулятор робота с помощью программного обеспечения для 3D-моделирования Solidworks. Рука имеет 5 степеней свободы.
Для первых трех осей (талии, плеча и локтя) я использовал сервоприводы MG996R, а для остальных двух осей (поворота запястья и шага запястья), а также захвата, я использовал более слабые микросервоприводы SG90.
Вы можете скачать эту 3D-модель, а также файлы STL для 3D-печати на сайте Cults3D.
3D-печать руки робота
Используя мой новый 3D-принтер Creality CR-10, я напечатал в 3D все детали руки робота Arduino.
Все детали для нашей руки были готовы всего за несколько часов.
Сборка роботизированной руки
Итак, на этом этапе мы готовы собрать нашу роботизированную руку. Я начал с основания, к которому прикрепил первый серводвигатель с помощью винтов, входящих в комплект поставки. Затем на выходном валу сервопривода я закрепил круглый болт.
А поверх него я положил верхнюю часть и закрепил ее двумя винтами.
Здесь снова сначала идет сервопривод, затем круглый рупор на следующую деталь, а затем они крепятся друг к другу с помощью болта на выходном валу.
Здесь мы можем заметить, что на оси плеча было бы неплохо установить какую-нибудь пружину или, в моем случае, я использовал резиновую ленту, чтобы немного помочь сервоприводу, потому что этот сервопривод также несет на себе весь вес остальной части руки в качестве полезной нагрузки.
Аналогичным образом я продолжил собирать остальную часть руки робота. Что касается механизма захвата, для его сборки я использовал болты и гайки диаметром около 4 миллиметров.
Наконец, я прикрепил захватный механизм к последнему сервоприводу и роботизированная рука была готова.
Схема проекта
Схема роботизированной руки на Arduino управляемой со смартфона представлена на следующем рисунке.
Для этого проекта нам нужна плата Arduino и Bluetooth-модуль HC-05 для связи со смартфоном. Управляющие контакты шести серводвигателей подключены к шести цифровым контактам платы Arduino.
Для питания сервоприводов нам нужно напряжение 5 В, но оно должно поступать от внешнего источника питания, поскольку Arduino не может выдержать тот ток, который все они могут потреблять. Источник питания должен выдерживать ток не менее 2 А. Итак, как только мы соединили все вместе, мы можем перейти к программированию платы Arduino и созданию приложения для Android.
Код программы роботизированной руки
Так как код немного длиннее, для лучшего понимания я буду размещать исходный код программы по разделам с описанием каждого раздела. И в конце этой статьи я выложу полный исходный код.
Итак, сначала нам нужно подключить библиотеку SoftwareSerial для последовательной связи с модулем Bluetooth, а также библиотеку для работы с серводвигателями. Обе эти библиотеки включены в Arduino IDE, поэтому вам не нужно устанавливать их извне. Затем нам нужно определить шесть сервоприводов, модуль Bluetooth HC-05 и некоторые переменные для хранения текущего и предыдущего положения сервоприводов, а также массивы для хранения положений или шагов для автоматического режима.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <SoftwareSerial.h> #include <Servo.h> Servo servo01; Servo servo02; Servo servo03; Servo servo04; Servo servo05; Servo servo06; SoftwareSerial Bluetooth(3, 4); // Arduino(RX, TX) - HC-05 Bluetooth (TX, RX) int servo1Pos, servo2Pos, servo3Pos, servo4Pos, servo5Pos, servo6Pos; // current position int servo1PPos, servo2PPos, servo3PPos, servo4PPos, servo5PPos, servo6PPos; // previous position int servo01SP[50], servo02SP[50], servo03SP[50], servo04SP[50], servo05SP[50], servo06SP[50]; // for storing positions/steps int speedDelay = 20; int index = 0; String dataIn = ""; |
В разделе настройки нам необходимо инициализировать сервоприводы и модуль Bluetooth и переместить руку робота в исходное положение. Мы сделаем это с помощью функции write(), которая просто перемещает сервопривод в любое положение от 0 до 180 градусов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void setup() { servo01.attach(5); servo02.attach(6); servo03.attach(7); servo04.attach(8); servo05.attach(9); servo06.attach(10); Bluetooth.begin(38400); // Default baud rate of the Bluetooth module Bluetooth.setTimeout(1); delay(20); // Robot arm initial position servo1PPos = 90; servo01.write(servo1PPos); servo2PPos = 150; servo02.write(servo2PPos); servo3PPos = 35; servo03.write(servo3PPos); servo4PPos = 140; servo04.write(servo4PPos); servo5PPos = 85; servo05.write(servo5PPos); servo6PPos = 80; servo06.write(servo6PPos); } |
Далее в разделе цикла с помощью функции Bluetooth.available() мы постоянно проверяем, есть ли входящие данные со смартфона. Если это правда, с помощью функции readString() мы читаем данные как строку и сохраняем их в переменную dataIn. В зависимости от поступивших данных мы сообщим руке робота, что делать.
1 2 3 |
// Check for incoming data if (Bluetooth.available() > 0) { dataIn = Bluetooth.readString(); // Read the data as string |
Управление Android-приложением
Давайте теперь посмотрим на приложение Android и посмотрим, какие данные оно на самом деле отправляет на Arduino.
Я создал приложение с помощью онлайн-приложения MIT App Inventor, и вот как оно работает. Сверху у нас есть две кнопки для подключения смартфона к Bluetooth-модулю HC-05. Затем слева у нас есть изображение руки робота, а справа у нас есть шесть ползунков для управления сервоприводами и один ползунок для управления скоростью.
Каждый ползунок имеет разное начальное, минимальное и максимальное значение, соответствующее суставам руки робота. В нижней части приложения у нас есть три кнопки: SAVE, RUN и RESET (СОХРАНИТЬ, ЗАПУСК и СБРОС), с помощью которых мы можем запрограммировать манипулятор робота на автоматический запуск. Ниже также есть метка, показывающая количество сохраненных шагов. Чтобы лучше понять этот раздел статьи рекомендую ознакомиться со статьей про создание приложения Android для проекта Arduino с помощью MIT App Inventor.
Хорошо, теперь давайте посмотрим на блоки приложения. Во-первых, с левой стороны у нас расположены колодки для подключения смартфона к модулю Bluetooth.
Затем у нас есть блоки ползунков для управления положением сервоприводов и блоки кнопок для программирования руки робота. Поэтому, если мы изменим положение ползунка, используя функцию Bluetooth .SendText, мы отправим текст в Arduino. Этот текст состоит из префикса, который указывает, какой ползунок был изменен, а также текущее значение ползунка.
Также вы можете скачать готовый файл проекта приложения MIT Inventor для управления роботизированной рукой.
Таким образом, в коде программы Arduino с помощью функции startWith() мы проверяем префикс каждых входящих данных и знаем, что делать дальше. Например, если префикс «s1», мы знаем, что нам нужно переместить сервопривод номер один. Используя функцию substring(), мы получаем оставшийся текст или значение позиции, преобразуем его в целое число и используем это значение для перемещения сервопривода в эту позицию.
1 2 3 4 |
// If "Waist" slider has changed value - Move Servo 1 to position if (dataIn.startsWith("s1")) { String dataInS = dataIn.substring(2, dataIn.length()); // Extract only the number. E.g. from "s1120" to "120" servo1Pos = dataInS.toInt(); // Convert the string into integer |
Здесь мы можем просто вызвать функцию write(), и сервопривод перейдет в это положение, но в этом случае сервопривод будет работать на максимальной скорости, что слишком много для руки робота. Вместо этого нам нужно контролировать скорость сервоприводов, поэтому я использовал несколько циклов FOR, чтобы постепенно перемещать сервопривод из предыдущего положения в текущее, реализуя время задержки между каждой итерацией. Изменяя время задержки, вы можете изменить скорость сервопривода.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// We use for loops so we can control the speed of the servo // If previous position is bigger then current position if (servo1PPos > servo1Pos) { for ( int j = servo1PPos; j >= servo1Pos; j--) { // Run servo down servo01.write(j); delay(20); // defines the speed at which the servo rotates } } // If previous position is smaller then current position if (servo1PPos < servo1Pos) { for ( int j = servo1PPos; j <= servo1Pos; j++) { // Run servo up servo01.write(j); delay(20); } } servo1PPos = servo1Pos; // set current position as previous position } |
Тот же метод используется для управления каждой осью манипулятора робота.
Под ними находится кнопка СОХРАНИТЬ. Если мы нажмем кнопку SAVE, положение каждого серводвигателя сохранится в массиве. При каждом нажатии индекс увеличивается, поэтому массив заполняется шаг за шагом.
1 2 3 4 5 6 7 8 9 10 |
// If button "SAVE" is pressed if (dataIn.startsWith("SAVE")) { servo01SP[index] = servo1PPos; // save position into the array servo02SP[index] = servo2PPos; servo03SP[index] = servo3PPos; servo04SP[index] = servo4PPos; servo05SP[index] = servo5PPos; servo06SP[index] = servo6PPos; index++; // Increase the array index } |
Затем, если мы нажмем кнопку RUN, мы вызовем пользовательскую функцию runservo(), которая выполняет сохраненные шаги. Давайте посмотрим на эту функцию. Итак, здесь мы выполняем сохраненные шаги снова и снова, пока не нажмем кнопку RESET. С помощью цикла FOR мы пробегаем все позиции, хранящиеся в массивах, и заодно проверяем, есть ли у нас входящие данные со смартфона. Этими данными может быть кнопка «ПУСК/ПАУЗА», которая приостанавливает работу робота и при повторном нажатии продолжает автоматические движения. Кроме того, если мы изменим положение ползунка скорости, мы будем использовать это значение для изменения времени задержки между каждой итерацией в приведенных ниже циклах 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 |
// Automatic mode custom function - run the saved steps void runservo() { while (dataIn != "RESET") { // Run the steps over and over again until "RESET" button is pressed for (int i = 0; i <= index - 2; i++) { // Run through all steps(index) if (Bluetooth.available() > 0) { // Check for incomding data dataIn = Bluetooth.readString(); if ( dataIn == "PAUSE") { // If button "PAUSE" is pressed while (dataIn != "RUN") { // Wait until "RUN" is pressed again if (Bluetooth.available() > 0) { dataIn = Bluetooth.readString(); if ( dataIn == "RESET") { break; } } } } // If SPEED slider is changed if (dataIn.startsWith("ss")) { String dataInS = dataIn.substring(2, dataIn.length()); speedDelay = dataInS.toInt(); // Change servo speed (delay time) } } // Servo 1 if (servo01SP[i] == servo01SP[i + 1]) { } if (servo01SP[i] > servo01SP[i + 1]) { for ( int j = servo01SP[i]; j >= servo01SP[i + 1]; j--) { servo01.write(j); delay(speedDelay); } } if (servo01SP[i] < servo01SP[i + 1]) { for ( int j = servo01SP[i]; j <= servo01SP[i + 1]; j++) { servo01.write(j); delay(speedDelay); } } |
Аналогично тому, как объяснялось ранее с этими операторами IF и циклами FOR, мы перемещаем сервоприводы в следующую позицию. Наконец, если мы нажмем кнопку RESET, мы очистим все данные из массивов до нуля, а также сбросим индекс до нуля, чтобы мы могли перепрограммировать манипулятор робота новыми движениями.
1 2 3 4 5 6 7 8 9 10 |
// If button "RESET" is pressed if ( dataIn == "RESET") { memset(servo01SP, 0, sizeof(servo01SP)); // Clear the array data to 0 memset(servo02SP, 0, sizeof(servo02SP)); memset(servo03SP, 0, sizeof(servo03SP)); memset(servo04SP, 0, sizeof(servo04SP)); memset(servo05SP, 0, sizeof(servo05SP)); memset(servo06SP, 0, sizeof(servo06SP)); index = 0; // Index to 0 } |
И все, теперь мы можем наслаждаться и развлекаться с роботизированной рукой.
Вот полный код робота-манипулятора на Arduino:
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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 |
/* DIY Arduino Robot Arm Smartphone Control by Dejan, www.HowToMechatronics.com */ #include <SoftwareSerial.h> #include <Servo.h> Servo servo01; Servo servo02; Servo servo03; Servo servo04; Servo servo05; Servo servo06; SoftwareSerial Bluetooth(3, 4); // Arduino(RX, TX) - HC-05 Bluetooth (TX, RX) int servo1Pos, servo2Pos, servo3Pos, servo4Pos, servo5Pos, servo6Pos; // current position int servo1PPos, servo2PPos, servo3PPos, servo4PPos, servo5PPos, servo6PPos; // previous position int servo01SP[50], servo02SP[50], servo03SP[50], servo04SP[50], servo05SP[50], servo06SP[50]; // for storing positions/steps int speedDelay = 20; int index = 0; String dataIn = ""; void setup() { servo01.attach(5); servo02.attach(6); servo03.attach(7); servo04.attach(8); servo05.attach(9); servo06.attach(10); Bluetooth.begin(38400); // Default baud rate of the Bluetooth module Bluetooth.setTimeout(1); delay(20); // Robot arm initial position servo1PPos = 90; servo01.write(servo1PPos); servo2PPos = 150; servo02.write(servo2PPos); servo3PPos = 35; servo03.write(servo3PPos); servo4PPos = 140; servo04.write(servo4PPos); servo5PPos = 85; servo05.write(servo5PPos); servo6PPos = 80; servo06.write(servo6PPos); } void loop() { // Check for incoming data if (Bluetooth.available() > 0) { dataIn = Bluetooth.readString(); // Read the data as string // If "Waist" slider has changed value - Move Servo 1 to position if (dataIn.startsWith("s1")) { String dataInS = dataIn.substring(2, dataIn.length()); // Extract only the number. E.g. from "s1120" to "120" servo1Pos = dataInS.toInt(); // Convert the string into integer // We use for loops so we can control the speed of the servo // If previous position is bigger then current position if (servo1PPos > servo1Pos) { for ( int j = servo1PPos; j >= servo1Pos; j--) { // Run servo down servo01.write(j); delay(20); // defines the speed at which the servo rotates } } // If previous position is smaller then current position if (servo1PPos < servo1Pos) { for ( int j = servo1PPos; j <= servo1Pos; j++) { // Run servo up servo01.write(j); delay(20); } } servo1PPos = servo1Pos; // set current position as previous position } // Move Servo 2 if (dataIn.startsWith("s2")) { String dataInS = dataIn.substring(2, dataIn.length()); servo2Pos = dataInS.toInt(); if (servo2PPos > servo2Pos) { for ( int j = servo2PPos; j >= servo2Pos; j--) { servo02.write(j); delay(50); } } if (servo2PPos < servo2Pos) { for ( int j = servo2PPos; j <= servo2Pos; j++) { servo02.write(j); delay(50); } } servo2PPos = servo2Pos; } // Move Servo 3 if (dataIn.startsWith("s3")) { String dataInS = dataIn.substring(2, dataIn.length()); servo3Pos = dataInS.toInt(); if (servo3PPos > servo3Pos) { for ( int j = servo3PPos; j >= servo3Pos; j--) { servo03.write(j); delay(30); } } if (servo3PPos < servo3Pos) { for ( int j = servo3PPos; j <= servo3Pos; j++) { servo03.write(j); delay(30); } } servo3PPos = servo3Pos; } // Move Servo 4 if (dataIn.startsWith("s4")) { String dataInS = dataIn.substring(2, dataIn.length()); servo4Pos = dataInS.toInt(); if (servo4PPos > servo4Pos) { for ( int j = servo4PPos; j >= servo4Pos; j--) { servo04.write(j); delay(30); } } if (servo4PPos < servo4Pos) { for ( int j = servo4PPos; j <= servo4Pos; j++) { servo04.write(j); delay(30); } } servo4PPos = servo4Pos; } // Move Servo 5 if (dataIn.startsWith("s5")) { String dataInS = dataIn.substring(2, dataIn.length()); servo5Pos = dataInS.toInt(); if (servo5PPos > servo5Pos) { for ( int j = servo5PPos; j >= servo5Pos; j--) { servo05.write(j); delay(30); } } if (servo5PPos < servo5Pos) { for ( int j = servo5PPos; j <= servo5Pos; j++) { servo05.write(j); delay(30); } } servo5PPos = servo5Pos; } // Move Servo 6 if (dataIn.startsWith("s6")) { String dataInS = dataIn.substring(2, dataIn.length()); servo6Pos = dataInS.toInt(); if (servo6PPos > servo6Pos) { for ( int j = servo6PPos; j >= servo6Pos; j--) { servo06.write(j); delay(30); } } if (servo6PPos < servo6Pos) { for ( int j = servo6PPos; j <= servo6Pos; j++) { servo06.write(j); delay(30); } } servo6PPos = servo6Pos; } // If button "SAVE" is pressed if (dataIn.startsWith("SAVE")) { servo01SP[index] = servo1PPos; // save position into the array servo02SP[index] = servo2PPos; servo03SP[index] = servo3PPos; servo04SP[index] = servo4PPos; servo05SP[index] = servo5PPos; servo06SP[index] = servo6PPos; index++; // Increase the array index } // If button "RUN" is pressed if (dataIn.startsWith("RUN")) { runservo(); // Automatic mode - run the saved steps } // If button "RESET" is pressed if ( dataIn == "RESET") { memset(servo01SP, 0, sizeof(servo01SP)); // Clear the array data to 0 memset(servo02SP, 0, sizeof(servo02SP)); memset(servo03SP, 0, sizeof(servo03SP)); memset(servo04SP, 0, sizeof(servo04SP)); memset(servo05SP, 0, sizeof(servo05SP)); memset(servo06SP, 0, sizeof(servo06SP)); index = 0; // Index to 0 } } } // Automatic mode custom function - run the saved steps void runservo() { while (dataIn != "RESET") { // Run the steps over and over again until "RESET" button is pressed for (int i = 0; i <= index - 2; i++) { // Run through all steps(index) if (Bluetooth.available() > 0) { // Check for incomding data dataIn = Bluetooth.readString(); if ( dataIn == "PAUSE") { // If button "PAUSE" is pressed while (dataIn != "RUN") { // Wait until "RUN" is pressed again if (Bluetooth.available() > 0) { dataIn = Bluetooth.readString(); if ( dataIn == "RESET") { break; } } } } // If speed slider is changed if (dataIn.startsWith("ss")) { String dataInS = dataIn.substring(2, dataIn.length()); speedDelay = dataInS.toInt(); // Change servo speed (delay time) } } // Servo 1 if (servo01SP[i] == servo01SP[i + 1]) { } if (servo01SP[i] > servo01SP[i + 1]) { for ( int j = servo01SP[i]; j >= servo01SP[i + 1]; j--) { servo01.write(j); delay(speedDelay); } } if (servo01SP[i] < servo01SP[i + 1]) { for ( int j = servo01SP[i]; j <= servo01SP[i + 1]; j++) { servo01.write(j); delay(speedDelay); } } // Servo 2 if (servo02SP[i] == servo02SP[i + 1]) { } if (servo02SP[i] > servo02SP[i + 1]) { for ( int j = servo02SP[i]; j >= servo02SP[i + 1]; j--) { servo02.write(j); delay(speedDelay); } } if (servo02SP[i] < servo02SP[i + 1]) { for ( int j = servo02SP[i]; j <= servo02SP[i + 1]; j++) { servo02.write(j); delay(speedDelay); } } // Servo 3 if (servo03SP[i] == servo03SP[i + 1]) { } if (servo03SP[i] > servo03SP[i + 1]) { for ( int j = servo03SP[i]; j >= servo03SP[i + 1]; j--) { servo03.write(j); delay(speedDelay); } } if (servo03SP[i] < servo03SP[i + 1]) { for ( int j = servo03SP[i]; j <= servo03SP[i + 1]; j++) { servo03.write(j); delay(speedDelay); } } // Servo 4 if (servo04SP[i] == servo04SP[i + 1]) { } if (servo04SP[i] > servo04SP[i + 1]) { for ( int j = servo04SP[i]; j >= servo04SP[i + 1]; j--) { servo04.write(j); delay(speedDelay); } } if (servo04SP[i] < servo04SP[i + 1]) { for ( int j = servo04SP[i]; j <= servo04SP[i + 1]; j++) { servo04.write(j); delay(speedDelay); } } // Servo 5 if (servo05SP[i] == servo05SP[i + 1]) { } if (servo05SP[i] > servo05SP[i + 1]) { for ( int j = servo05SP[i]; j >= servo05SP[i + 1]; j--) { servo05.write(j); delay(speedDelay); } } if (servo05SP[i] < servo05SP[i + 1]) { for ( int j = servo05SP[i]; j <= servo05SP[i + 1]; j++) { servo05.write(j); delay(speedDelay); } } // Servo 6 if (servo06SP[i] == servo06SP[i + 1]) { } if (servo06SP[i] > servo06SP[i + 1]) { for ( int j = servo06SP[i]; j >= servo06SP[i + 1]; j--) { servo06.write(j); delay(speedDelay); } } if (servo06SP[i] < servo06SP[i + 1]) { for ( int j = servo06SP[i]; j <= servo06SP[i + 1]; j++) { servo06.write(j); delay(speedDelay); } } } } } |