Panduan · 16 mnt baca

Lengan Robot Arduino: Memilih Servo, Torsi, PCA9685, dan Bluetooth

Buat lengan robot Arduino langkah demi langkah: rangka MDF atau cetak 3D, hitungan torsi MG996R, kendali pot dan joystick, rekam/putar ulang, serta kode PCA9685 dan Bluetooth.

Lengan robot Arduino adalah robot seukuran meja yang sendi-sendinya digerakkan servo, sehingga bisa menjepit benda, mengangkatnya, berputar, lalu meletakkannya di tempat lain. Tersusun dari pangkal, bahu, siku, pergelangan, dan gripper, proyek ini mengajarkan desain mekanik, pemilihan motor, manajemen daya, dan coding sekaligus. Kabar baiknya, kamu bisa membuatnya di rumah dengan Arduino Uno, beberapa servo, dan komponen MDF atau cetak 3D, lalu menjalankan versi mini lengan robot pabrik langsung di mejamu.

Di panduan ini kita membahas lengan robot 4 dan 6 sumbu dari awal sampai akhir: memilih bahan rangka, hitungan torsi untuk memilih antara SG90, MG90S, dan MG996R, catu daya eksternal 5 V, kendali dengan potensiometer dan joystick, merekam dan memutar ulang gerakan, driver servo 16 kanal PCA9685, kendali Bluetooth dari ponselmu, serta solusi untuk dua masalah paling umum: getaran dan reset. Kalau kamu belum yakin cara kerja servo, mulai dulu dari Apa Itu Motor Servo?.

SG90
SG90
MG90S
MG90S
MG996R
MG996R

Bagaimana cara kerja lengan robot, dan berapa sumbu yang kamu butuhkan?

Bayangkan lengan robot seperti lengan manusia. Setiap sendi punya satu motor servo, dan Arduino memberi tahu setiap servo “pergi ke sudut ini”. Berkat potensiometer di dalamnya, servo tahu posisi porosnya dan tetap menahan sudut itu meski sedang menanggung beban. Dengan mengubah sudut-sudut sendi secara bersamaan, kamu menggerakkan gripper ke titik yang kamu mau. Jumlah gerakan independen yang bisa dilakukan sebuah lengan disebut derajat kebebasan (DOF), atau singkatnya sumbu.

SendiGerakanBebanServo yang disarankan
Pangkal (pinggang)Memutar lengan ke kiri dan kananTidak mengangkat apa pun, tetapi menopang seluruh lenganMG996R + bearing
BahuMengangkat lengan maju dan mundurSendi dengan beban paling beratMG996R atau servo kelas 20 kg·cm
SikuMenekuk lengan bawahSedang sampai tinggiMG996R
Pergelangan (pitch)Mengatur sudut gripperRendahMG90S
Putaran pergelangan (roll)Memutar gripper pada porosnyaSangat rendahMG90S atau SG90
GripperMenjepit dan melepas bendaGaya jepitSG90 atau MG90S

Kebanyakan kit “lengan robot 6 sumbu” di pasaran punya 6 servo; karena gripper tidak dihitung sebagai sumbu, secara teknis itu lengan 5-DOF plus gripper. Untuk lengan pertamamu, desain 4 sumbu (pangkal, bahu, siku, gripper) adalah awal terbaik: 4 servo, 4 potensiometer, dan Arduino Uno sudah cukup, pengkabelannya tetap sederhana, dan masalah torsi jarang muncul. Sendi pergelangan berguna kalau kamu perlu menjaga gripper tetap datar atau menjepit benda dari berbagai sudut; begitu kamu naik ke 5–6 servo, driver PCA9685 nyaris wajib.

Daftar komponen

KomponenLengan 4 sumbuLengan 6 servoCatatan
KontrolerArduino Uno atau NanoArduino Uno, Nano, atau ESP32Input tambahan A6 dan A7 di Nano memungkinkan lebih banyak pot
Motor servo4× SG90 atau MG90S3× MG996R + 3× MG90SCek pilihanmu dengan hitungan torsi di bawah
Driver servoTidak perluModul PCA9685 16 kanalHanya 2 kabel sinyal lewat I2C
Catu dayaAdaptor 5 V 3 AAdaptor 5–6 V, minimal 5 AServo tidak pernah diberi daya dari Arduino
Kapasitor470–1000 µF, 16 V1000–2200 µF, 16 VDi jalur daya servo, perhatikan polaritasnya
Kendali4× potensiometer 10 kΩ2× modul joystick atau HC-05Kamu juga bisa memakai keduanya
Tombol2 (rekam, putar)Tombol joystick sudah cukupTanpa resistor tambahan dengan INPUT_PULLUP
MekanikMDF 3 mm atau komponen cetak 3DMDF 4 mm, cetak 3D, atau braket aluminiumBearing 608, baut M3, dan mur pengunci

Desain mekanik: MDF atau cetak 3D?

BahanKelebihanKekuranganPaling cocok untuk
MDF 3–4 mm (potong laser)Murah, ringan, mudah dibaut dan dilemPeka terhadap kelembapan, lubang baut melebar seiring waktuLengan pertama gaya MeArm, proyek sekolah
Akrilik 3 mmTampilannya keren, tidak terpengaruh kelembapanGetas; bisa retak ketika baut dikencangkanPajangan dan karya pameran
Cetak 3D (PLA / PETG)Komponen rumit, dudukan servo dengan ukuran pasWaktu cetak lama; PLA melentur perlahan di suhu panas dan di bawah beban terus-menerusDesain sendiri; PETG untuk komponen penahan beban
Kit braket aluminiumSangat kaku, komponen siap pakai untuk MG996RBerat dan mahal, menambah beban di bahuLengan 6 servo untuk mengangkat benda yang lebih berat
Kardus, stik es krimHampir gratis, prototipe cepatRingkih, sendinya cepat oblakMenguji ukuran, proyek pertama bareng anak-anak
  • Buat lengan pendek dan ringan: Beban di bahu naik sebanding dengan jarak. Membuat lengan 24 cm, bukan 30 cm, memangkas torsi bahu sekitar seperlima.
  • Taruh servo yang berat dekat pangkal: Memindahkan servo siku ke bawah, ke dekat bahu, lalu menggerakkan siku lewat batang dorong (parallel linkage) adalah trik cerdas yang dipakai lengan seperti MeArm; bahu tidak perlu lagi mengangkat servo itu.
  • Pakai bearing di pangkal: Jangan gantungkan berat seluruh lengan pada poros servo pangkal. Bearing 608 (lubang 8 mm, diameter luar 22 mm, tebal 7 mm; bearing skateboard klasik) atau bearing meja putar (lazy Susan) yang menanggung beban, servo cukup memutarnya.
  • Topang sendi di kedua sisi: Sementara servo memegang sendi di sisi horn, tambahkan engsel di sisi seberangnya dengan baut M3 dan mur pengunci. Dengan begitu poros servo tidak menanggung beban tekuk dan gigi-giginya jauh lebih awet.
  • Pakai horn logam: Pasang horn logam bundar 25 gigi (25T) di setiap MG996R dan kunci ke poros dengan sekrup tengah. Horn plastik mudah selip dan alurnya aus di sendi yang bebannya berat.
  • Tambahkan karet di jari gripper: Selembar karet tipis di ujung jari membuat gripper bisa memegang benda tanpa selip dan dengan gaya jepit yang lebih kecil.

Memilih servo: SG90, MG90S, atau MG996R?

ModelTorsiKecepatan (per 60°)BeratGigiArus stallPeran di lengan robot
SG901,8 kg·cm (4,8 V)0,10 dtk (4,8 V)9 gPlastikSekitar 0,6–0,75 AGripper, pergelangan ringan, lengan kecil 4 sumbu
MG90S1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V)0,10 dtk (4,8 V)13,4 gLogamSekitar 0,7–1 APergelangan, putaran pergelangan, gripper
MG996R9,4 kg·cm (4,8 V), 11 kg·cm (6 V)0,17 dtk (4,8 V)55 gLogam2,5 A (6 V)Pangkal, bahu, siku
DS321819 kg·cm (5 V), 21,5 kg·cm (6,8 V)0,16 dtk (5 V)60 gLogamSekitar 2–3 ABahu pada lengan yang panjang atau berat

Angka torsi di tabel adalah torsi stall: gaya maksimum yang bisa diberikan servo sebelum porosnya berhenti, bukan nilai yang bisa dipakai terus-menerus. Ketika servo mendekati batas itu, arusnya naik mendekati arus stall, servo memanas dan mulai bergetar. Aturan praktisnya, torsi yang dibutuhkan sebuah sendi sebaiknya tidak lebih dari setengah torsi stall servo. Tegangan juga berpengaruh: MG996R memberi 9,4 kg·cm pada 4,8 V dan 11 kg·cm pada 6 V, jadi servo yang sama sekitar 17% lebih kuat hanya karena tegangan catu dayanya. Karena itu catu daya 6 V masuk akal untuk lengan MG996R, tetapi 6 V adalah batas atas untuk SG90 dan MG90S. Untuk pengkabelan dan batas-batas MG996R, lihat juga panduan motor servo MG996R.

Motor servo MG996R bergigi logam
MG996R: gigi logam, torsi 11 kg·cm pada 6 V, dan alur output 25 gigi; pilihan standar untuk pangkal, bahu, dan siku lengan robot.

Hitungan torsi: sanggupkah servo bahu mengangkat lengan ini?

Torsi yang harus diangkat servo adalah jumlah massa setiap komponen dikalikan jarak horizontalnya dari sumbu sendi: Torsi (kg·cm) = Σ massa (kg) × jarak (cm). Kondisi terburuk terjadi ketika lengan terentang mendatar, karena semua jarak sedang maksimum; ketika lengan menunjuk lurus ke atas, lengan gaya itu turun hampir nol. Untuk mengubahnya ke newton meter, kalikan dengan 0,098 (1 kg·cm ≈ 0,098 N·m). Sebagai contoh, mari hitung beban bahu sebuah lengan meja dengan jarak 12 cm dari bahu ke siku, 12 cm dari siku ke pergelangan, 6 cm dari pergelangan ke ujung gripper, dan MG996R di siku, dengan gripper memegang benda 50 g:

KomponenMassaJarak dari sumbu bahuTorsi
Komponen lengan atas (MDF dan baut)40 g6 cm (pusat massa)0,04 × 6 = 0,24 kg·cm
Servo siku (MG996R)55 g12 cm0,055 × 12 = 0,66 kg·cm
Komponen lengan bawah30 g18 cm0,03 × 18 = 0,54 kg·cm
Servo pergelangan + gripper (MG90S, SG90, dan komponennya)50 g25 cm0,05 × 25 = 1,25 kg·cm
Benda yang dipegang50 g30 cm0,05 × 30 = 1,50 kg·cm
Total225 g—4,19 kg·cm (≈ 0,41 N·m)

Dalam posisi diam, lengan ini butuh 4,19 kg·cm di bahu. Untuk menutup beban tambahan dari akselerasi dan pengereman, dan supaya servo tetap jauh dari stall, kalikan 2: 8,4 kg·cm. Jadi bahu butuh minimal MG996R (9,4 kg·cm pada 4,8 V, 11 kg·cm pada 6 V); 1,8–2,2 kg·cm milik SG90 atau MG90S bahkan tidak akan menggeser lengan ini. Kalau dihitung dengan cara yang sama untuk siku, lengan bawah (0,03 × 6), pergelangan dan gripper (0,05 × 13), serta benda (0,05 × 18) totalnya 1,73 kg·cm, atau 3,5 kg·cm dengan margin keamanan, jadi siku juga butuh MG996R. Di pergelangan hanya tersisa gripper dan benda, jadi bebannya sekitar 0,4 kg·cm dan MG90S sudah lebih dari cukup. Hitungan yang sama juga memberi tahu benda terberat yang bisa dibawa lengan: naikkan massa benda, hitung ulang totalnya, dan pastikan tetap di bawah setengah torsi servo.

Ada tiga cara murah untuk mengurangi torsi bahu: perpendek lengan, pindahkan servo siku ke bahu dan gerakkan siku dengan batang dorong, lalu tambahkan pemberat penyeimbang di belakang bahu atau pegas yang menarik lengan ke atas. Misalnya, pemberat 400 g yang dipasang 5 cm di belakang sumbu bahu menghasilkan torsi lawan 0,4 × 5 = 2 kg·cm dan hampir memotong separuh beban lengan di atas; akibatnya bearing pangkal menanggung sedikit lebih banyak berat.

Catu daya: kenapa servo tidak diberi daya dari Arduino?

Sebagian besar masalah lengan robot berasal dari catu daya. MG996R menarik 0,5–0,9 A ketika bergerak dan sampai 2,5 A ketika stall; bahkan SG90 yang kecil bisa melewati 0,6 A ketika stall. Ketika Arduino Uno berjalan dari USB, total arus yang tersedia dari pin 5V-nya sekitar 500 mA, yaitu batas USB, dan board itu sendiri memakai sebagian darinya. Ketika tiga servo besar mulai bergerak bersamaan, tegangannya anjlok, mikrokontroler reset, dan lengan melompat kembali ke pose awalnya. Solusinya, beri daya servo dari sumber 5–6 V terpisah dan sambungkan semua ground jadi satu; Arduino tetap boleh diberi daya dari USB atau adaptornya sendiri. Untuk pilihan catu daya, lihat panduan Cara Memberi Daya ke Arduino, dan untuk memperkirakan berapa lama lengan bertenaga baterai bisa bekerja, coba kalkulator umur baterai.

Jenis lenganPerkiraan arusCatu daya yang disarankan
Lengan kecil dengan 4× SG90 atau MG90S0,5–1 A ketika bergerak, puncak 2–3 AAdaptor 5 V 3 A atau modul LM2596 (maks. 3 A, sekitar 2 A tanpa heatsink)
Lengan 6 servo dengan 3× MG996R + 3× MG90S2–3 A ketika bergerak, 8–10 A kalau semuanya stallAdaptor switching 5–6 V, minimal 5 A (10 A supaya cadangannya lega)
Lengan portabelTergantung lengannyaLiPo 2S (7,4 V) + modul step-down 5 A atau lebih (UBEC) yang diatur ke 6 V
Catu daya yang harus dihindari—Baterai kotak 9 V (PP3, tidak sanggup memberi arusnya), pin 5V Arduino, 4× baterai alkaline AA pada lengan MG996R
Jangan pernah memberi daya servo dari pin 5V Arduino, dan selalu sambungkan negatif catu daya eksternal ke GND Arduino; tanpa ground bersama, servo tidak bisa membaca sinyal dan bergerak sembarangan. Pasang kapasitor elektrolit di jalur daya servo tepat di dekat servo, minimal 100 µF per servo (1000–2200 µF, 16 V atau lebih untuk lengan 6 servo), dengan kaki panjang di positif; kapasitor elektrolit yang terpasang terbalik bisa menggembung dan meledak. Jalur daya breadboard dirancang untuk sekitar 1 A, jadi pada lengan MG996R tarik kabel catu daya langsung ke terminal sekrup atau input V+ di PCA9685.

Pengkabelan lengan 4 sumbu

Tabel di bawah menunjukkan pengkabelan Arduino Uno dan Raspberry Pi Pico untuk lengan 4 sumbu yang dikendalikan potensiometer; kode di panduan ini memakai pin-pin ini. Di Uno, library Servo bisa menggerakkan servo dari pin digital mana pun, tetapi karena memakai Timer1, analogWrite berhenti bekerja di pin 9 dan 10 selama servo terpasang. Kamu bisa mengecek susunan pin board-mu di alat diagram pin.

SambunganArduino UnoRaspberry Pi Pico
Sinyal servo pangkal (kabel oranye)D3GP10
Sinyal servo bahuD5GP11
Sinyal servo sikuD6GP12
Sinyal servo gripperD9GP13
Wiper pot 1–3 (pangkal, bahu, siku)A0, A1, A2GP26, GP27, GP28
Wiper pot 4 (gripper)A3Tidak ada; gripper memakai tombol di GP14
Kaki luar pot5V dan GND3V3 dan GND
Tombol rekam dan putar (sisi lainnya ke GND)D7 dan D8—
Kabel merah servo (+)Positif 5–6 V eksternalPositif 5–6 V eksternal
Kabel cokelat servo (−)Negatif catu daya eksternal + GND ArduinoNegatif catu daya eksternal + GND Pico
Potensiometer putar 10 kΩ
Potensiometer 10 kΩ: kaki luarnya ke 5V dan GND, kaki tengah (wiper) ke input analog; ketika porosnya diputar, sendi ikut berputar dengan proporsi yang sama.

Kalibrasi sebelum merakit: tengahkan servo

  1. Sebelum memasang horn, upload kode di bawah; keempat servo bergerak ke 90 derajat satu per satu lalu menunggu di sana.
  2. Tahan lengan dalam pose netral (misalnya lengan atas tegak, lengan bawah mendatar, gripper setengah terbuka), pasang setiap horn pada gigi alur yang paling dekat dengan pose itu, lalu kunci dengan sekrup tengah.
  3. Ubah CENTER_ONLY menjadi false di kode, pilih satu sendi dengan TEST, lalu putar potensiometer di A0 pelan-pelan.
  4. Perhatikan sudutnya di Serial Monitor dan catat sudut terkecil dan terbesar yang bisa dicapai sendi tanpa menabrak apa pun atau menarik kabel.
  5. Atur sudut tertutup gripper ke titik ketika jari-jarinya baru saja bersentuhan; servo yang terus berusaha menjepit lebih kuat akan tetap stall dan memanas.
  6. Masukkan nilai yang kamu temukan ke array MIN_A dan MAX_A di sketch-sketch berikutnya.
C++ (Arduino IDE)
1#include <Servo.h>
2
3const byte SERVO_PIN[4] = {3, 5, 6, 9};   // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
4const byte TEST = 1;                       // denenecek servo (0–3) / servo under test (0–3)
5const bool CENTER_ONLY = true;             // true: hepsi 90°'de bekler / true: all wait at 90°
6
7Servo servo[4];
8
9void setup() {
10  Serial.begin(9600);
11  for (byte i = 0; i < 4; i++) {
12    servo[i].write(90);                    // önce açıyı ver / set the angle first
13    servo[i].attach(SERVO_PIN[i]);         // sonra bağla: zıplamaz / then attach: no jump
14    delay(300);                            // akım darbelerini ayır / spread the current surges
15  }
16}
17
18void loop() {
19  if (CENTER_ONLY) return;                 // korna takarken burada kal / stay here while fitting horns
20  int angle = map(analogRead(A0), 0, 1023, 0, 180);   // A0'daki pot / pot on A0
21  servo[TEST].write(angle);
22  Serial.print(F("Servo "));
23  Serial.print(TEST);
24  Serial.print(F(" aci / angle: "));
25  Serial.println(angle);                   // sınırları buradan oku / read the limits here
26  delay(50);
27}

Ada detail kecil tetapi penting di kode ini: karena servo.write(90) dipanggil sebelum attach(), servo langsung mendapat sudut yang diminta sejak pulsa pertamanya. Kalau urutannya dibalik, library lebih dulu mengirim pulsa default 1500 µs (sekitar 90°); kalau sudut awalmu bukan 90, lengan melompat ke tengah lalu ke target begitu dinyalakan. Menyalakan servo satu per satu dengan jeda 300 ms juga menyebar lonjakan arus ketika daya pertama kali masuk.

Mengendalikan lengan robot dengan potensiometer

Cara kendali yang paling intuitif adalah satu potensiometer per sendi: berapa pun sudut poros pot, sendi bergerak ke sudut yang sama, jadi kamu sedang melakukan kendali posisi. Kalau pot dipasang di model mini lengan (kardus atau cetak 3D, proporsinya sama), lengan besar meniru gerakan model itu satu banding satu ketika kamu menggerakkannya dengan tangan. Namun pembacaan analog secara alami bergoyang beberapa angka, dan goyangan itu muncul sebagai getaran servo; kode ini punya dua cara untuk mengatasinya.

C++ (Arduino IDE)
1#include <Servo.h>
2
3const byte SERVO_PIN[4] = {3, 5, 6, 9};        // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
4const byte POT_PIN[4]   = {A0, A1, A2, A3};    // her eklem için bir pot / one pot per joint
5const byte MIN_A[4] = {0, 15, 20, 40};         // kalibrasyonda bulduğun alt sınırlar / lower limits from calibration
6const byte MAX_A[4] = {180, 165, 160, 110};    // üst sınırlar / upper limits
7
8Servo servo[4];
9float smooth[4];                               // süzülmüş pot değeri / filtered pot value
10int lastAngle[4];                              // son yazılan açı / last written angle
11
12void setup() {
13  for (byte i = 0; i < 4; i++) {
14    smooth[i] = analogRead(POT_PIN[i]);
15    lastAngle[i] = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
16    servo[i].write(lastAngle[i]);              // önce açı, sonra attach / angle first, then attach
17    servo[i].attach(SERVO_PIN[i]);
18    delay(300);                                // servolar sırayla uyansın / wake the servos one by one
19  }
20}
21
22void loop() {
23  for (byte i = 0; i < 4; i++) {
24    smooth[i] += (analogRead(POT_PIN[i]) - smooth[i]) * 0.2;   // yumuşatma filtresi / low-pass filter
25    int angle = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
26    if (abs(angle - lastAngle[i]) >= 2) {      // ölü bant: küçük gürültüyü yok say / deadband: ignore small noise
27      servo[i].write(angle);
28      lastAngle[i] = angle;
29    }
30  }
31  delay(15);
32}

Array smooth adalah filter low-pass sederhana: setiap putaran loop hanya menambahkan 20% dari pembacaan baru, jadi lonjakan mendadak diredam sementara lengan tetap merespons dengan cepat. Ubah 0.2 menjadi 0.1 dan gerakannya jadi lebih halus tetapi lebih lambat menanggapi. Cara kedua adalah deadband: selama sudut baru tidak berbeda minimal 2 derajat dari sudut terakhir yang ditulis, tidak ada yang dikirim ke servo, sehingga getaran berhenti ketika lengan diam. Karena map() langsung mengubah rentang pot ke batas aman setiap sendi, sendi tidak pernah menabrak batas mekaniknya meskipun pot diputar mentok.

Merekam dan memutar ulang gerakan

Yang benar-benar membuat lengan robot menjadi robot adalah kemampuannya mengulang sendiri gerakan yang sudah kamu tunjukkan. Caranya sederhana: kamu menggerakkan lengan ke sebuah pose dengan pot lalu menekan tombol rekam, dan Arduino menyimpan sudut keempat sendi. Setelah merekam beberapa pose, tekan putar dan lengan mengulanginya berurutan, dengan transisi yang dihaluskan. Pose-pose itu ditulis ke EEPROM Arduino Uno yang berukuran 1024 byte, jadi tetap tersimpan meski daya dimatikan; setiap pose memakan 4 byte, jadi rekaman 100 pose hanya memakai 401 byte.

C++ (Arduino IDE)
1#include <Servo.h>
2#include <EEPROM.h>
3
4const byte SERVO_PIN[4] = {3, 5, 6, 9};        // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
5const byte POT_PIN[4]   = {A0, A1, A2, A3};
6const byte MIN_A[4] = {0, 15, 20, 40};         // güvenli sınırlar / safe limits
7const byte MAX_A[4] = {180, 165, 160, 110};
8const byte BTN_REC = 7, BTN_PLAY = 8, LED = 13; // butonların diğer ucu GND'de / buttons go to GND
9const byte MAX_STEPS = 100;                    // 100 adım x 4 bayt = 400 bayt / 100 steps x 4 bytes
10
11Servo servo[4];
12float smooth[4];                               // süzülmüş pot değeri / filtered pot value
13byte pos[4];                                   // servoların şu anki açısı / current servo angles
14byte stepCount;                                // kayıtlı adım sayısı / number of saved steps
15
16bool pressed(byte pin) {                       // basılıyken true / true while pressed
17  return digitalRead(pin) == LOW;
18}
19
20byte potAngle(byte i) {
21  smooth[i] += (analogRead(POT_PIN[i]) - smooth[i]) * 0.2;   // yumuşatma / smoothing
22  return map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
23}
24
25void blink(byte n) {
26  for (byte k = 0; k < n; k++) {
27    digitalWrite(LED, HIGH); delay(80);
28    digitalWrite(LED, LOW);  delay(80);
29  }
30}
31
32// Tüm eklemleri aynı anda hedefe götürür / moves all joints to the target together
33void moveTo(const byte target[4], byte msPerDegree) {
34  byte start[4];
35  int steps = 0;
36  for (byte i = 0; i < 4; i++) {
37    start[i] = pos[i];
38    int d = abs((int)target[i] - (int)start[i]);
39    if (d > steps) steps = d;                  // en uzun hareket / longest move
40  }
41  for (int s = 1; s <= steps; s++) {
42    for (byte i = 0; i < 4; i++) {
43      pos[i] = start[i] + (long)((int)target[i] - (int)start[i]) * s / steps;   // ara açı / in-between angle
44      servo[i].write(pos[i]);
45    }
46    delay(msPerDegree);
47  }
48}
49
50void saveStep() {
51  if (stepCount >= MAX_STEPS) { blink(3); return; }           // hafıza dolu / memory full
52  for (byte i = 0; i < 4; i++) EEPROM.update(1 + stepCount * 4 + i, pos[i]);
53  stepCount++;
54  EEPROM.update(0, stepCount);                 // 0. bayt adım sayısını tutar / byte 0 holds the step count
55  blink(1);
56}
57
58void playBack() {
59  byte target[4];
60  for (byte s = 0; s < stepCount; s++) {
61    for (byte i = 0; i < 4; i++) target[i] = EEPROM.read(1 + s * 4 + i);
62    moveTo(target, 12);                        // derece başına 12 ms / 12 ms per degree
63    delay(400);                                // her pozda kısa bekleme / short pause at each pose
64    if (pressed(BTN_PLAY)) break;              // oynatırken basınca durur / press again to stop
65  }
66}
67
68void syncToPots() {                            // potların konumuna yumuşakça dön / ease back to the pots
69  byte target[4];
70  for (byte i = 0; i < 4; i++) {
71    smooth[i] = analogRead(POT_PIN[i]);
72    target[i] = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
73  }
74  moveTo(target, 12);
75}
76
77void setup() {
78  pinMode(BTN_REC, INPUT_PULLUP);
79  pinMode(BTN_PLAY, INPUT_PULLUP);
80  pinMode(LED, OUTPUT);
81  stepCount = EEPROM.read(0);
82  if (stepCount > MAX_STEPS) stepCount = 0;    // boş EEPROM 255 okur / a blank EEPROM reads 255
83  for (byte i = 0; i < 4; i++) {
84    smooth[i] = analogRead(POT_PIN[i]);
85    pos[i] = potAngle(i);
86    servo[i].write(pos[i]);                    // önce açı, sonra attach / angle first, then attach
87    servo[i].attach(SERVO_PIN[i]);
88    delay(300);
89  }
90}
91
92void loop() {
93  // Kayıt: kısa bas = pozu kaydet, 2 sn basılı tut = hepsini sil / Record: short press = save pose, hold 2 s = erase all
94  if (pressed(BTN_REC)) {
95    unsigned long t0 = millis();
96    while (pressed(BTN_REC) && millis() - t0 < 2000) {}
97    if (millis() - t0 >= 2000) {
98      stepCount = 0;
99      EEPROM.update(0, 0);
100      blink(5);
101    } else {
102      saveStep();
103    }
104    while (pressed(BTN_REC)) {}                // bırakılmasını bekle / wait for release
105    delay(50);                                 // buton arkı / debounce
106  }
107
108  // Oynat: kaydedilen pozları sırayla tekrarla / play: repeat the saved poses in order
109  if (pressed(BTN_PLAY) && stepCount > 0) {
110    while (pressed(BTN_PLAY)) {}
111    delay(50);
112    digitalWrite(LED, HIGH);
113    playBack();
114    while (pressed(BTN_PLAY)) {}
115    syncToPots();
116    digitalWrite(LED, LOW);
117  }
118
119  // Normal mod: kolu potlarla sür / normal mode: drive the arm with the pots
120  for (byte i = 0; i < 4; i++) {
121    byte a = potAngle(i);
122    if (abs((int)a - (int)pos[i]) >= 2) {
123      pos[i] = a;
124      servo[i].write(a);
125    }
126  }
127  delay(15);
128}
AksiYang kamu lakukanYang dilakukan lengan
Simpan poseTekan singkat tombol rekam (D7)LED berkedip sekali, pose ditulis ke EEPROM
Hapus rekamanTahan tombol rekam selama 2 detikLED berkedip lima kali, jumlah langkah kembali ke nol
PutarTekan dan lepas tombol putar (D8)LED menyala, pose-pose diputar ulang berurutan dengan transisi halus
Hentikan pemutaranTahan tombol putar selama pemutaranLengan menyelesaikan pose yang sedang berjalan lalu berhenti
Kembali ke mode normalTidak perlu melakukan apa-apa setelah pemutaran selesaiLengan pelan-pelan kembali ke posisi yang ditunjuk pot, LED padam

Fungsi moveTo() adalah jantung sketch ini: fungsi ini mencari sendi yang jarak tempuhnya paling jauh, membagi gerakan menjadi sebanyak itu langkah, lalu memajukan semua sendi secara proporsional di setiap langkah. Dengan begitu setiap sendi tiba di target pada waktu yang sama, dan lengan bergerak luwes, bukan seperti robot yang menyentak satu sendi demi satu sendi. Dengan 12 ms per derajat, gerakan 90 derajat butuh sekitar 1,1 detik. Kamu bisa menurunkan nilai ini supaya pemutaran lebih cepat, tetapi tanpa beban pun MG996R memutar 60 derajat dalam 0,17 detik, sekitar 3 ms per derajat; kalau lebih rendah dari itu, servo tidak bisa mengikuti perintah dan gerakannya jadi patah-patah.

Setiap sel EEPROM bisa ditulis sekitar 100.000 kali. Karena kodenya memakai EEPROM.update(), hanya sel yang nilainya berubah yang ditulis, jadi dalam praktiknya kamu tidak akan pernah membuatnya aus. Untuk urutan gerakan yang lebih panjang, kamu bisa menyimpan rekaman di kartu SD atau memakai EEPROM 4 KB milik Arduino Mega.

Driver servo PCA9685: 6 servo dan sinyal tanpa getaran

Begitu kamu naik ke 5–6 servo atau menambah modul lain seperti Bluetooth, modul PCA9685 membuat semuanya jauh lebih mudah. Board ini adalah generator PWM 16 kanal 12-bit; ia berkomunikasi dengan Arduino hanya lewat dua kabel memakai I2C dan membangkitkan pulsa servo dengan clock-nya sendiri, terlepas dari prosesor. Alamat default-nya 0x40; dengan mengubah alamat lewat jumper solder A0–A5, kamu bisa merangkai sampai 62 board, atau 992 servo. Pada 50 Hz, periode 20 ms dibagi menjadi 4096 langkah, jadi satu langkah sekitar 4,9 µs; rentang 544–2400 µs mencakup sekitar 380 langkah, artinya resolusinya kira-kira setengah derajat.

Pin PCA9685Arduino UnoESP32Raspberry Pi Pico
GNDGNDGNDGND
VCC (hanya untuk catu daya chip)5V3V33V3
SDAA4GPIO 21GP4
SCLA5GPIO 22GP5
Terminal V+ (daya servo)5–6 V eksternal5–6 V eksternal5–6 V eksternal
OEBiarkan tidak tersambungBiarkan tidak tersambungBiarkan tidak tersambung
Header servo 0–5Pangkal, bahu, siku, pergelangan, putaran pergelangan, gripperUrutan yang samaUrutan yang sama
PCA9685 punya dua catu daya terpisah: VCC hanya memberi daya ke chip, sementara servo mendapat daya lewat input V+ di terminal sekrup. Kalau V+ tidak diberi daya, board terdeteksi di I2C tetapi servo tidak bergerak. Di Arduino Uno, A4 dan A5 dipakai untuk I2C, jadi dengan PCA9685 tersambung hanya A0–A3 yang tersisa sebagai input analog; kalau kamu butuh lebih banyak potensiometer, pakai Arduino Nano dengan input tambahan A6 dan A7.

Mengendalikan lengan robot dengan joystick

Joystick bekerja berbeda dari potensiometer: ia kembali ke tengah dengan sendirinya ketika dilepas. Kalau sudut langsung diikat ke posisi joystick, sendi langsung melompat ke 90 derajat begitu kamu melepasnya. Cara yang benar adalah kendali kecepatan: makin jauh joystick didorong, makin cepat sendi berputar, dan ketika dilepas, sendi tetap di tempatnya. Di kode di bawah, dua joystick menggerakkan empat sendi (joystick kiri untuk pangkal dan bahu, joystick kanan untuk siku dan pergelangan); tombol joystick kiri membuka dan menutup gripper, sedangkan tombol joystick kanan mengirim lengan ke pose awal. Sambungkan output VRx dan VRy joystick kiri ke A0 dan A1, output VRy dan VRx joystick kanan ke A2 dan A3, dan pin SW ke D2 dan D4; joystick boleh diberi daya dari pin 5V Arduino. Untuk mengompilasi kodenya, instal Adafruit PWM Servo Driver Library dari Library Manager dan setujui dependensi Adafruit BusIO.

Modul joystick analog dua sumbu
Modul joystick: output VRx dan VRy terbaca sekitar 512 ketika diam, dan tombol SW menarik ke GND ketika ditekan. Dua modul bisa menggerakkan empat sendi lengan robot.
C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_PWMServoDriver.h>
3
4Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);   // varsayılan adres / default address
5
6// Kanal: 0 taban, 1 omuz, 2 dirsek, 3 bilek, 4 bilek dönüşü, 5 kıskaç / Channel: 0 base, 1 shoulder, 2 elbow, 3 wrist, 4 wrist roll, 5 gripper
7const byte N = 6;
8const float MIN_A[N] = {0, 15, 20, 10, 0, 40};        // güvenli sınırlar / safe limits
9const float MAX_A[N] = {180, 165, 160, 170, 180, 110};
10const float HOME[N]  = {90, 90, 90, 90, 90, 40};      // başlangıç pozu, kıskaç açık / home pose, gripper open
11const byte JOY[4] = {A0, A1, A2, A3};                  // J1 X, J1 Y, J2 Y, J2 X → kanal / channel 0, 1, 2, 3
12const int8_t DIR[4] = {1, -1, 1, 1};                   // ters giden ekseni -1 yap / set -1 to invert an axis
13const byte BTN_GRIP = 2, BTN_HOME = 4;                 // joystick SW pinleri / joystick SW pins
14const float MAX_SPEED = 90;                            // en yüksek hız, derece/sn / top speed, degrees/s
15const float DT = 0.02;                                 // 20 ms döngü / 20 ms loop
16const int DEADZONE = 60;                               // ortadaki ölü bölge / dead zone around center
17
18float target[N], current[N];
19bool gripClosed = false;
20
21void writeServo(byte ch, float a) {
22  // Servo.h ile aynı 544–2400 µs aralığı / same 544–2400 µs range as Servo.h
23  pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));
24}
25
26bool newPress(byte pin, byte k) {                      // her basışta bir kez true / true once per press
27  static bool prev[2] = {HIGH, HIGH};
28  bool now = digitalRead(pin);
29  bool edge = (prev[k] == HIGH && now == LOW);
30  prev[k] = now;
31  return edge;
32}
33
34void setup() {
35  pinMode(BTN_GRIP, INPUT_PULLUP);
36  pinMode(BTN_HOME, INPUT_PULLUP);
37  pca.begin();
38  pca.setOscillatorFrequency(27000000);                // çoğu modülde ~27 MHz / ~27 MHz on most boards
39  pca.setPWMFreq(50);                                  // servolar için 50 Hz / 50 Hz for servos
40  Wire.setClock(400000);                               // hızlı I2C / fast I2C
41  for (byte i = 0; i < N; i++) {
42    target[i] = current[i] = HOME[i];
43    writeServo(i, HOME[i]);
44    delay(300);                                        // servolar sırayla uyansın / wake servos one by one
45  }
46}
47
48void loop() {
49  static unsigned long last = 0;
50  if (millis() - last < 20) return;                    // 50 Hz güncelleme / 50 Hz update
51  last = millis();
52
53  // Joystick hızı belirler, konumu değil / the joystick sets speed, not position
54  for (byte i = 0; i < 4; i++) {
55    int v = analogRead(JOY[i]) - 512;
56    if (abs(v) > DEADZONE) {
57      target[i] += DIR[i] * (v / 512.0) * MAX_SPEED * DT;
58      target[i] = constrain(target[i], MIN_A[i], MAX_A[i]);
59    }
60  }
61  if (newPress(BTN_GRIP, 0)) {                         // kıskaç aç/kapa / toggle gripper
62    gripClosed = !gripClosed;
63    target[5] = gripClosed ? MAX_A[5] : MIN_A[5];
64  }
65  if (newPress(BTN_HOME, 1)) {                         // başlangıç pozuna dön / go to home pose
66    for (byte i = 0; i < N; i++) target[i] = HOME[i];
67    gripClosed = false;
68  }
69
70  // Her eklem hedefe hız sınırıyla gider / each joint follows its target with a speed limit
71  for (byte i = 0; i < N; i++) {
72    float d = constrain(target[i] - current[i], -MAX_SPEED * DT, MAX_SPEED * DT);
73    if (d != 0) {
74      current[i] += d;
75      writeServo(i, current[i]);
76    }
77  }
78}

DEADZONE mencegah selisih kecil seperti 490–530 yang terbaca di posisi tengah membuat lengan merayap sendiri. MAX_SPEED mengatur kecepatan, dalam derajat per detik, ketika joystick didorong mentok; 90 derajat per detik adalah nilai awal yang aman. Kalau sebuah sendi bergerak berlawanan dengan joystick, jangan tukar kabelnya, cukup ubah nilai yang sesuai di array DIR menjadi -1. Kode ini menyimpan target (target) dan posisi sebenarnya (current) untuk setiap sendi; karena posisi sebenarnya mendekati target dengan batas kecepatan, lengan meluncur kembali dengan halus bahkan ketika kamu menekan tombol pose awal. Putaran pergelangan tetap di 90 derajat di sketch ini; kamu bisa mengendalikannya dengan perintah R di bagian Bluetooth. Teknik pengaturan waktu tanpa blocking ini dijelaskan lengkap di millis() vs delay().

Kendali Bluetooth dari ponselmu (HC-05)

Modul HC-05 membuat sambungan serial nirkabel antara ponselmu dan Arduino. Pin TXD-nya masuk ke D10 Arduino, dan pin RXD-nya tersambung ke D11 lewat pembagi tegangan: D11 → 1 kΩ → RXD → 2 kΩ → GND. Pembagi ini menurunkan sinyal 5 V Arduino ke sekitar 3,3 V yang diharapkan HC-05. Kecepatan data default modul ini 9600 baud dan PIN pairing-nya biasanya 1234. Ponsel mengirim perintah teks sederhana: satu huruf memilih sendi, angka sesudahnya memberi sudut, dan baris baru atau titik koma mengakhiri perintah.

Modul serial Bluetooth HC-05
HC-05: modul serial yang dipasangkan dengan ponselmu lewat Bluetooth klasik; VCC menerima 5 V, tetapi input RXD bekerja dengan logika 3,3 V.
PerintahArtiContoh
B + sudutPangkalB90
S + sudutBahuS120
E + sudutSikuE45
W + sudutPergelangan (pitch)W100
R + sudutPutaran pergelanganR0
G + sudutGripperG110 (tutup), G40 (buka)
HKembali ke pose awalH
K / P / X / CSimpan pose / putar berulang / berhenti / hapus poseK
C++ (Arduino IDE)
1#include <Wire.h>
2#include <SoftwareSerial.h>
3#include <Adafruit_PWMServoDriver.h>
4
5SoftwareSerial bt(10, 11);                 // D10 ← HC-05 TXD, D11 → bölücü / divider → HC-05 RXD
6Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
7
8const byte N = 6;
9const char NAME[N] = {'B', 'S', 'E', 'W', 'R', 'G'};   // taban, omuz, dirsek, bilek, dönüş, kıskaç / base, shoulder, elbow, wrist, roll, gripper
10const byte MIN_A[N] = {0, 15, 20, 10, 0, 40};          // güvenli sınırlar / safe limits
11const byte MAX_A[N] = {180, 165, 160, 170, 180, 110};
12const byte HOME[N]  = {90, 90, 90, 90, 90, 40};
13const byte MAX_POSES = 30;
14const float SPEED = 1.5;                   // 20 ms'de en fazla 1,5° (75°/sn) / max 1.5° per 20 ms (75°/s)
15
16float current[N];
17byte target[N];
18byte poses[MAX_POSES][N];                  // kaydedilen pozlar (RAM) / recorded poses (RAM)
19byte poseCount = 0, playIndex = 0;
20bool playing = false;
21char buf[12];
22byte len = 0;
23
24void writeServo(byte ch, float a) {
25  pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));   // 544–2400 µs
26}
27
28void handle(char *cmd) {                   // ör. / e.g. "S120", "H", "K", "P"
29  char c = toupper(cmd[0]);
30  if (c == 'H') {                          // başlangıç pozu / home pose
31    for (byte i = 0; i < N; i++) target[i] = HOME[i];
32    playing = false;
33  } else if (c == 'K') {                   // pozu kaydet / save pose
34    if (poseCount < MAX_POSES) {
35      for (byte i = 0; i < N; i++) poses[poseCount][i] = target[i];
36      poseCount++;
37    }
38    bt.print(F("Poz / pose: "));
39    bt.println(poseCount);
40  } else if (c == 'P') {                   // döngüyle oynat / play in a loop
41    if (poseCount > 0) { playing = true; playIndex = 0; }
42  } else if (c == 'X') {                   // durdur / stop
43    playing = false;
44  } else if (c == 'C') {                   // kayıtları sil / clear poses
45    poseCount = 0;
46    playing = false;
47  } else {
48    for (byte i = 0; i < N; i++) {
49      if (c == NAME[i]) {
50        target[i] = constrain(atoi(cmd + 1), MIN_A[i], MAX_A[i]);
51        playing = false;                   // elle komut oynatmayı keser / a manual command stops playback
52      }
53    }
54  }
55}
56
57bool arrived() {
58  for (byte i = 0; i < N; i++)
59    if (fabs(target[i] - current[i]) > 0.5) return false;
60  return true;
61}
62
63void setup() {
64  bt.begin(9600);                          // HC-05 varsayılan hızı / HC-05 default baud
65  pca.begin();
66  pca.setOscillatorFrequency(27000000);
67  pca.setPWMFreq(50);
68  for (byte i = 0; i < N; i++) {
69    target[i] = HOME[i];
70    current[i] = HOME[i];
71    writeServo(i, HOME[i]);
72    delay(300);                            // servolar sırayla uyansın / wake servos one by one
73  }
74}
75
76void loop() {
77  // Komutları satır satır topla / collect commands line by line
78  while (bt.available()) {
79    char ch = bt.read();
80    if (ch == '\n' || ch == '\r' || ch == ';') {
81      if (len > 0) { buf[len] = '\0'; handle(buf); len = 0; }
82    } else if (len < sizeof(buf) - 1) {
83      buf[len++] = ch;
84    }
85  }
86
87  static unsigned long last = 0, arrivedAt = 0;
88  if (millis() - last < 20) return;        // 50 Hz güncelleme / 50 Hz update
89  last = millis();
90
91  if (playing) {
92    if (!arrived()) arrivedAt = millis();  // hareket sürüyor / still moving
93    else if (millis() - arrivedAt > 300) { // her pozda 300 ms bekle / hold each pose 300 ms
94      for (byte i = 0; i < N; i++) target[i] = poses[playIndex][i];
95      playIndex = (playIndex + 1) % poseCount;   // sona gelince başa dön / wrap around
96      arrivedAt = millis();
97    }
98  }
99
100  for (byte i = 0; i < N; i++) {
101    float d = constrain(target[i] - current[i], -SPEED, SPEED);   // hız sınırı / speed limit
102    if (d != 0) {
103      current[i] += d;
104      writeServo(i, current[i]);
105    }
106  }
107}

Di sketch ini servo disambungkan ke PCA9685, bukan langsung ke Arduino, dan itu disengaja. SoftwareSerial mematikan interrupt selama sekitar 1 ms setiap kali menerima satu byte; kalau servo digerakkan dari pin Arduino dengan library Servo, pulsanya akan memanjang selama waktu itu dan lengan akan tersentak di setiap perintah. Karena PCA9685 membangkitkan pulsanya sendiri, sinyalnya tetap stabil sekali seberapa pun sibuknya sambungan Bluetooth. Perekaman dan pemutaran sekarang juga dilakukan dari ponsel: gerakkan lengan ke sebuah pose dengan slider lalu kirim K; setelah beberapa pose, P membuat lengan mengulanginya terus-menerus dan X menghentikannya. Di versi ini pose disimpan di RAM dan hilang ketika board dimatikan; kalau kamu ingin pose tetap tersimpan, tambahkan metode EEPROM dari sketch sebelumnya.

Di ponsel, aplikasi siap pakai saja sudah cukup: instal aplikasi terminal serial Bluetooth atau aplikasi kontroler Bluetooth dengan slider di Android, lalu beri setiap slider awalan seperti “S” ditambah akhiran baris. Kalau kamu ingin membuat antarmuka sendiri dengan tombol dan slider, panduan aplikasi Bluetooth MIT App Inventor memandumu langkah demi langkah. Karena HC-05 memakai Bluetooth klasik, modul ini tidak bisa dipakai dengan iPhone; untuk iPhone kamu butuh solusi Bluetooth Low Energy seperti BLE ESP32.

Lengan robot nirkabel dengan ESP32

Karena ESP32 sudah punya Bluetooth klasik bawaan, kamu tidak butuh HC-05: dengan library BluetoothSerial, board ini muncul di ponselmu sebagai “BlueGrays-RobotKol” dan memahami perintah yang sama. Sambungkan PCA9685 ke GPIO 21 (SDA) dan GPIO 22 (SCL) dan beri daya VCC-nya dari 3V3; sinyal servo lalu keluar pada 3,3 V, yang bisa dibaca tanpa masalah oleh sebagian besar servo. BluetoothSerial hanya bekerja di ESP32 klasik (ESP32-WROOM-32, DevKit); ESP32-S2, S3, dan C3 tidak mendukung Bluetooth klasik.

C++ (Arduino IDE)
1#include <Wire.h>
2#include <BluetoothSerial.h>
3#include <Adafruit_PWMServoDriver.h>
4
5BluetoothSerial bt;                        // klasik Bluetooth SPP / classic Bluetooth SPP
6Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
7
8const int N = 6;
9const char NAME[N] = {'B', 'S', 'E', 'W', 'R', 'G'};   // taban, omuz, dirsek, bilek, dönüş, kıskaç / base, shoulder, elbow, wrist, roll, gripper
10const int MIN_A[N] = {0, 15, 20, 10, 0, 40};
11const int MAX_A[N] = {180, 165, 160, 170, 180, 110};
12const int HOME[N]  = {90, 90, 90, 90, 90, 40};
13float current[N], target[N];
14String line;
15
16void writeServo(int ch, float a) {
17  pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));   // 544–2400 µs
18}
19
20void handle(String cmd) {                  // ör. / e.g. "E45", "H"
21  cmd.trim();
22  if (cmd.length() == 0) return;
23  char c = toupper(cmd[0]);
24  if (c == 'H') {
25    for (int i = 0; i < N; i++) target[i] = HOME[i];
26    return;
27  }
28  int value = cmd.substring(1).toInt();
29  for (int i = 0; i < N; i++)
30    if (c == NAME[i]) target[i] = constrain(value, MIN_A[i], MAX_A[i]);
31}
32
33void setup() {
34  Serial.begin(115200);
35  bt.begin("BlueGrays-RobotKol");          // telefonda görünen ad / name shown on the phone
36  Wire.begin(21, 22);                      // SDA = GPIO 21, SCL = GPIO 22
37  pca.begin();
38  pca.setOscillatorFrequency(27000000);
39  pca.setPWMFreq(50);
40  for (int i = 0; i < N; i++) {
41    current[i] = target[i] = HOME[i];
42    writeServo(i, HOME[i]);
43    delay(300);
44  }
45  Serial.println("Bluetooth hazir / ready");
46}
47
48void loop() {
49  while (bt.available()) {
50    char ch = bt.read();
51    if (ch == '\n' || ch == ';') { handle(line); line = ""; }
52    else if (line.length() < 12) line += ch;
53  }
54
55  static unsigned long last = 0;
56  if (millis() - last < 20) return;        // 50 Hz güncelleme / 50 Hz update
57  last = millis();
58  for (int i = 0; i < N; i++) {
59    float d = constrain(target[i] - current[i], -1.5f, 1.5f);   // yumuşak hareket / smooth motion
60    if (d != 0) {
61      current[i] += d;
62      writeServo(i, current[i]);
63    }
64  }
65}

Lengan robot dengan Raspberry Pi Pico (MicroPython)

Kamu tidak butuh library tambahan untuk menggerakkan servo di Raspberry Pi Pico: atur PWM ke 50 Hz dan berikan lebar pulsa dalam nanodetik dengan duty_ns(). Dengan 16 kanal PWM, Pico bisa menggerakkan 6 servo secara langsung; pasangan pin yang berbagi slice yang sama juga berbagi frekuensi, tetapi karena semua servo memakai 50 Hz, itu tidak jadi masalah. Batasannya ada di sisi analog: Pico hanya punya tiga pin ADC yang bisa dipakai (GP26, GP27, GP28). Karena itu di kode di bawah tiga sendi digerakkan pot, sedangkan gripper membuka dan menutup dengan tombol di GP14. Karena Pico bekerja di 3,3 V, sambungkan pot ke 3V3; servo tetap diberi daya dari catu daya eksternal 5 V seperti sebelumnya.

MicroPython
1from machine import Pin, PWM, ADC
2import time
3
4# Servo pinleri: taban, omuz, dirsek, kıskaç / servo pins: base, shoulder, elbow, gripper
5servos = [PWM(Pin(p)) for p in (10, 11, 12, 13)]
6for s in servos:
7    s.freq(50)                                  # 50 Hz servo sinyali / 50 Hz servo signal
8
9pots = [ADC(26), ADC(27), ADC(28)]              # Pico'da 3 ADC pini var / the Pico has 3 ADC pins
10button = Pin(14, Pin.IN, Pin.PULL_UP)           # kıskaç butonu GND'ye / gripper button to GND
11
12MIN_A = (0, 15, 20, 40)                         # güvenli sınırlar / safe limits
13MAX_A = (180, 165, 160, 110)
14
15def write(i, angle):
16    angle = max(MIN_A[i], min(MAX_A[i], angle))
17    servos[i].duty_ns(544000 + int(angle * 10311))   # 544–2400 µs aralığı / 544–2400 µs range
18
19smooth = [p.read_u16() for p in pots]
20grip_closed = False
21last_btn = 1
22write(3, MIN_A[3])                              # kıskaç açık başlar / gripper starts open
23
24while True:
25    for i in range(3):
26        smooth[i] += (pots[i].read_u16() - smooth[i]) * 0.2   # yumuşatma / smoothing
27        write(i, MIN_A[i] + (MAX_A[i] - MIN_A[i]) * smooth[i] / 65535)
28    b = button.value()
29    if b == 0 and last_btn == 1:                # yeni basış / new press
30        grip_closed = not grip_closed
31        write(3, MAX_A[3] if grip_closed else MIN_A[3])
32    last_btn = b
33    time.sleep_ms(20)                           # 50 Hz döngü / 50 Hz loop

Melangkah lebih jauh: kinematika invers

Sejauh ini kita mengatur sudut setiap sendi satu per satu; itu disebut kinematika maju (forward kinematics). Kinematika invers melakukan kebalikannya: kamu memberikan titik (x, y) yang ingin dicapai gripper, dan kode menghitung sudut-sudut sendi yang diperlukan. Untuk lengan dua ruas dengan panjang bahu–siku L1 dan siku–pergelangan L2, hitung dulu D = (x² + y² − L1² − L2²) / (2·L1·L2). Sudut siku adalah θ2 = acos(D) dan sudut bahu adalah θ1 = atan2(y, x) + atan2(L2·sin θ2, L1 + L2·cos θ2) (solusi siku-ke-atas). Contohnya, dengan L1 = L2 = 12 cm dan titik 15 cm di depan serta 5 cm di atas bahu, hasilnya D ≈ −0,13, θ2 ≈ 97,6°, dan θ1 ≈ 67,2°: lengan atas menunjuk 67 derajat di atas garis horizontal dan lengan bawah menekuk ke bawah 97,6 derajat relatif terhadapnya. Kalau nilai mutlak D lebih besar dari 1, titik itu di luar jangkauan lengan. Sudut pangkal dihitung terpisah dari posisi target dilihat dari atas. Ketika mengubah hasilnya menjadi sudut servo, perhitungkan arah pemasangan dan titik tengah setiap servo.

Getaran dan reset: kesalahan umum dan solusinya

GejalaKemungkinan penyebabSolusi
Servo bergetar dan berdengung ketika lengan diamNoise di pembacaan pot atau catu daya yang lemahTambahkan filter penghalus dan deadband 2°, pakai catu daya eksternal dan kapasitor 1000 µF
Arduino reset ketika lengan bergerak, sambungan HC-05 putusArus awal servo menarik tegangan turunBeri daya servo dari catu daya 5–6 V terpisah, satukan ground-nya, nyalakan servo satu per satu ketika daya masuk
Servo tersentak setiap kali perintah Bluetooth datangInterrupt SoftwareSerial mengganggu pulsa library ServoPindahkan servo ke PCA9685 atau pakai Bluetooth bawaan ESP32
Lengan menyentak ke suatu arah begitu dinyalakanServo tidak ingat posisi terakhirnya dan bergerak dengan kecepatan penuh pada perintah pertamaParkir lengan di pose awal sebelum dimatikan, panggil write() sebelum attach()
Bahu tidak sanggup mengangkat lengan atau pelan-pelan turunTorsi tidak cukupLakukan hitungan torsi; pakai MG996R, lengan yang lebih pendek, pemberat penyeimbang, atau pegas
Sebuah sendi stall di salah satu ujung dan panasServo mendorong batas mekanikMasukkan batas MIN_A dan MAX_A dari kalibrasi ke kode
Servo gripper memanas ketika memegang bendaGripper terus berusaha menutup dan servo tetap stallAtur sudut tertutup ke titik ketika gripper baru saja menyentuh benda, tambahkan karet di jari-jarinya
PCA9685 sudah tersambung tetapi tidak ada servo yang bergerakTidak ada daya di terminal V+ atau tidak ada ground bersamaBerikan 5–6 V ke V+ dan cek alamat I2C (0x40)
Sebuah sendi selip ketika diberi bebanHorn plastik sudah aus alurnya atau sekrup tengahnya tidak adaPakai horn logam dan kunci ke poros dengan sekrup tengah
Cara cepat untuk tahu apakah masalahnya di daya atau di kode: jalankan sketch kalibrasi (CENTER_ONLY = false) tanpa servo tersambung dan perhatikan sudutnya di Serial Monitor sambil memutar pot. Kalau nilainya terlihat benar, masalahnya ada di catu daya; ukur tegangan jalur servo dengan multimeter ketika lengan bergerak, dan kalau turun di bawah 4,5 V, catu daya atau pengkabelanmu tidak sanggup menanganinya. Untuk semua penyebab reset, lihat Kenapa Arduino-ku terus reset?.

Ide proyek lengan robot

  • Lengan pemilah warna: Baca warna kubus dengan sensor warna TCS3200 dan biarkan lengan menjatuhkan setiap warna ke kotaknya sendiri.
  • Kendali gerakan tangan: Dengan MPU6050 di sarung tangan, lengan robot ikut miring searah pergelangan tanganmu.
  • Remote nirkabel: Sambungkan dua joystick ke Arduino terpisah dan kirim perintahnya dengan nRF24L01; kamu mendapat remote control yang tidak butuh ponsel.
  • Kendali dari browser: Buat slider di halaman web dengan ESP32 dan kendalikan lengan dari perangkat apa pun di jaringan Wi-Fi yang sama; WebSocket ESP32 menjaga latensinya tetap minimal.
  • Mengambil dari konveyor: Buat ban berjalan kecil dengan motor stepper; ketika TCRT5000 mendeteksi benda, lengan mengambilnya dari ban.
  • Proyek lomba dan sekolah: Lengan rekam-dan-putar ulang adalah pilihan kuat di antara ide proyek untuk lomba sains, karena menampilkan mekanik, elektronika, dan perangkat lunak sekaligus.

Lengan robot Arduino adalah proyek yang mengajarkan desain mekanik, hitungan torsi, manajemen daya, dan coding gerakan halus sekaligus, dan proyek ini makin berkembang seiring usaha yang kamu curahkan. Mulailah dengan lengan 4 servo yang dikendalikan potensiometer, tambahkan rekam dan putar ulang, lalu naik ke 6 servo dengan PCA9685 dan Bluetooth. Di aplikasi BlueGrays kamu akan menemukan proyek seperti kendali servo dengan potensiometer dan joystick, kendali Bluetooth dari ponsel, dan koreografi 32 servo, lengkap dengan skema rangkaian dan kode langkah demi langkah, jadi kamu bisa mencoba setiap sendi lenganmu satu per satu lebih dulu.

Pertanyaan umum

Motor servo apa yang sebaiknya kupakai untuk lengan robot Arduino?

Untuk sendi yang menanggung beban seperti pangkal, bahu, dan siku, MG996R bergigi logam dengan torsi 9–11 kg·cm adalah pilihan yang bagus. Untuk sendi ringan seperti pergelangan dan gripper, MG90S atau SG90 sudah cukup. Lengan kecil yang hanya memakai SG90 tetap bisa bekerja asalkan dibuat pendek dan ringan, tetapi hanya sanggup mengangkat benda seberat beberapa puluh gram.

Catu daya apa yang dibutuhkan lengan robot?

Adaptor 5 V 3 A sudah cukup untuk lengan kecil dengan 4 servo SG90 atau MG90S. Untuk lengan 6 servo dengan 3 MG996R dan 3 servo kecil, pakai adaptor switching 5–6 V minimal 5 A, atau 10 A supaya cadangannya lega. Jangan lupa menyambungkan negatif adaptor ke GND Arduino.

Berapa banyak servo yang bisa dikendalikan Arduino?

Library Servo bisa menggerakkan sampai 12 servo di Arduino Uno dan 48 di Mega. Namun batas sebenarnya adalah arus: semua servo sebaiknya diberi daya dari catu daya eksternal. Modul PCA9685 menggerakkan 16 servo hanya dengan dua pin I2C dan bisa dirangkai sampai 62 modul.

Kenapa servo lengan robotku bergetar?

Penyebab paling umum adalah catu daya yang lemah, ground bersama yang tidak ada, dan noise di pembacaan potensiometer. Pakai catu daya eksternal dengan kapasitor 1000 µF di jalur servo, lalu tambahkan filter penghalus dan deadband di kode. Kalau SoftwareSerial dan library Servo dipakai bersamaan, memindahkan servo ke PCA9685 menghilangkan getarannya sepenuhnya.

Seberapa berat benda yang bisa diangkat lengan robot SG90?

Torsi stall SG90 sekitar 1,8 kg·cm; supaya aman, anggarkan separuhnya, yaitu 0,9 kg·cm. Karena lengan dan servo sudah memakai sebagian anggaran itu untuk beratnya sendiri, lengan SG90 dengan panjang sekitar 15 cm dalam praktiknya hanya bisa membawa benda ringan 20–30 g, seperti tutup botol atau kubus busa kecil.

Bagaimana cara mengendalikan lengan robot dari ponsel?

Sambungkan modul Bluetooth HC-05 ke Arduino dan pakai aplikasi terminal serial Bluetooth atau aplikasi kontroler slider di ponselmu untuk mengirim perintah teks seperti “S120”. Dengan ESP32, Bluetooth bawaannya membuatmu tidak butuh modul tambahan. HC-05 tidak bisa dipakai dengan iPhone, jadi untuk iPhone kamu butuh modul BLE atau BLE ESP32.

Lihat semua proyek
Menggerakkan Motor Servo dengan Potensiometer
#36Pemula
Premium

Menggerakkan Motor Servo dengan Potensiometer

Ketika kamu memutar kenop potensiometer, lengan motor servo ikut berputar: satu ujung kenop berarti nol derajat, ujung lainnya berarti setengah putaran. Arduino terus membaca posisi kenop dan servo la

4.6(37)
52
Kendali Servo dengan Joystick
#60Menengah
Premium

Kendali Servo dengan Joystick

Arahkan dua motor dengan joystick, persis seperti memutar kamera keamanan. Dorong tuas ke kiri atau kanan dan servo bawah berputar perlahan, dorong ke atas atau bawah dan servo atas mendongak dan menu

4.7(42)
58
Menggunakan Motor Servo
#35Pemula
Premium

Menggunakan Motor Servo

Lengan motor servo kecil berputar ke tiga posisi bergantian: penuh ke awal, tepat ke tengah, dan penuh ke akhir, menunggu dua detik di setiap perhentian. Arduino memberi tahu motor sudut mana yang har

4.7(48)
67
Motor Servo ESP32 (Kendali Potensiometer)
#118Menengah
Premium

Motor Servo ESP32 (Kendali Potensiometer)

Di proyek ini, kita mengendalikan motor servo SG90 memakai potensiometer. Kita mengatur sudut servo dengan memetakan nilai analog yang dibaca dari potensiometer ke rentang 0–180 derajat. Library ESP32Servo menghasilkan sinyal servo di pin GPIO ESP32 yang sesuai dan bisa menjadi output; proyek ini memakai GPIO 18. Ini metode kendali dasar untuk lengan robot, gimbal kamera, dan mekanisme pintu otomatis.

4.3(29)
42
Kendalikan LED dari Ponselmu lewat Bluetooth
#34Pemula
Premium

Kendalikan LED dari Ponselmu lewat Bluetooth

Latihan kecil untuk menyalakan dan mematikan lampu kamarmu dari ponsel. Modul Bluetooth meneruskan huruf yang dikirim dari ponselmu ke Arduino; satu huruf menyalakan LED merah, huruf lain menyalakan L

4.8(68)
89
Mobil Kendali Bluetooth dengan L298N
#80Mahir
Premium

Mobil Kendali Bluetooth dengan L298N

Jadikan ponselmu remote dan kendarai mobil robotmu sendiri. Arduino menerima perintah dari aplikasi remote control di ponselmu lewat modul Bluetooth dan memutar roda dengan driver motor; mobil maju, m

5.0(38)
65
Koreografi Lingkaran Servo - Pola Animasi dengan 32 Servo
#102Mahir
Premium

Koreografi Lingkaran Servo - Pola Animasi dengan 32 Servo

Proyek animasi visual dengan 32 motor servo yang disusun melingkar di Arduino Mega. Servo-servo dikelola dalam satu array, tersambung ke pin 22-53 Mega, dan menampilkan empat urutan gerak: getaran kacau ketika semua servo bergerak ke sudut acak, sapuan serempak ketika semuanya bergerak bersamaan, gelombang yang berputar halus mengelilingi lingkaran, dan terakhir jarum kompas yang berayun ke kiri dan kanan.

4.5(40)
62