Rehberler · 14 dk okuma

Arduino Robot Kol Yapımı: Servo Seçimi, Tork Hesabı ve Bluetooth

Arduino robot kol yapımı adım adım: MDF ya da 3D baskı gövde, MG996R tork hesabı, pot ve joystick kontrolü, kaydet-oynat, PCA9685 ve Bluetooth kodları.

Arduino robot kol, servo motorlarla hareket eden eklemleri sayesinde bir nesneyi tutup kaldırabilen, döndürüp başka bir yere bırakabilen masaüstü boyutunda bir robottur. Taban, omuz, dirsek, bilek ve kıskaçtan oluşan bu yapı; mekanik tasarımı, motor seçimini, güç yönetimini ve kodlamayı tek projede öğretir. Üstelik bir Arduino Uno, birkaç servo ve MDF ya da 3D baskı parçalarla evde yapılabilir; fabrikalardaki endüstriyel robot kolların küçük bir kopyasını kendi masanda çalıştırırsın.

Bu rehberde 4 ve 6 eksenli robot kolu baştan sona ele alıyoruz: gövde için malzeme seçimi, SG90, MG90S ve MG996R arasında doğru servoyu bulmak için tork hesabı, harici 5 V besleme, potansiyometre ve joystick ile kontrol, hareketleri kaydedip tekrar oynatma, 16 kanallı PCA9685 servo sürücü, telefondan Bluetooth kontrol ve en sık karşılaşılan iki sorun olan titreme ile resetlenmenin çözümleri. Servo motorun çalışma mantığını bilmiyorsan önce Servo Motor Nedir? yazısına göz atabilirsin.

SG90
SG90
MG90S
MG90S
MG996R
MG996R

Robot kol nasıl çalışır, kaç eksen gerekir?

Bir robot kolu insan koluna benzetebilirsin. Her eklemde bir servo motor bulunur ve Arduino her servoya "şu açıya git" komutu gönderir. Servo, içindeki potansiyometre sayesinde milinin nerede olduğunu bilir ve yük altında bile o açıyı korur. Eklem açılarını birlikte değiştirerek kıskacı istediğin noktaya götürürsün. Kolun yapabildiği bağımsız hareketlerin sayısına serbestlik derecesi (DOF), günlük dilde ise eksen denir.

EklemHareketiYük durumuÖnerilen servo
Taban (bel)Kolu sağa sola döndürürAğırlık kaldırmaz ama tüm kolu taşırMG996R + rulman
OmuzKolu öne ve arkaya kaldırırEn çok zorlanan eklemMG996R ya da 20 kg·cm sınıfı
DirsekÖn kolu bükerOrta-yüksekMG996R
Bilek (yukarı-aşağı)Kıskacın açısını ayarlarDüşükMG90S
Bilek dönüşüKıskacı kendi ekseninde çevirirÇok düşükMG90S ya da SG90
Kıskaç (gripper)Nesneyi tutar ve bırakırSıkma kuvvetiSG90 ya da MG90S

Piyasadaki "6 eksenli robot kol" kitlerinin çoğunda 6 servo vardır; kıskaç bir eksen sayılmadığı için teknik olarak bunlar 5 serbestlik dereceli bir kol ile bir kıskaçtır. İlk kolun için 4 eksenli bir tasarım (taban, omuz, dirsek, kıskaç) en iyi başlangıçtır: 4 servo, 4 potansiyometre ve bir Arduino Uno yeter, kablolama sade kalır ve tork sorunları azdır. Bilek eklemleri kıskacı yatay tutmak ya da nesneyi farklı açılardan kavramak gerektiğinde işe yarar; 5–6 servoya geçtiğinde PCA9685 sürücü neredeyse zorunlu hâle gelir.

Malzeme listesi

Parça4 eksenli kol6 servolu kolNot
Kontrol kartıArduino Uno ya da NanoArduino Uno, Nano ya da ESP32Nano'nun ek A6 ve A7 girişleri pot sayısını artırır
Servo motor4 adet SG90 ya da MG90S3 adet MG996R + 3 adet MG90SSeçimini aşağıdaki tork hesabıyla doğrula
Servo sürücüGerekmezPCA9685 16 kanallı modülI2C ile yalnızca 2 sinyal kablosu
Güç kaynağı5 V 3 A adaptör5–6 V, en az 5 A adaptörServolar Arduino'dan beslenmez
Kondansatör470–1000 µF, 16 V1000–2200 µF, 16 VServo besleme hattına, kutup yönüne dikkat ederek
Kontrol elemanı4 adet 10 kΩ potansiyometre2 adet joystick modülü ya da HC-05İkisini birlikte de kullanabilirsin
Buton2 adet (kayıt, oynat)Joystick butonları yeterliINPUT_PULLUP ile ek direnç gerekmez
Mekanik3 mm MDF ya da 3D baskı parçalar4 mm MDF, 3D baskı ya da alüminyum braket608 rulman, M3 cıvata ve kontra somun

Mekanik yapı: MDF mi, 3D baskı mı?

MalzemeArtısıEksisiKime uygun?
3–4 mm MDF (lazer kesim)Ucuz, hafif, kolay vidalanır ve yapıştırılırNemden etkilenir, vida delikleri zamanla genişlerMeArm tarzı ilk kollar, okul projeleri
3 mm akrilikŞık görünür, nemden etkilenmezKırılgandır; vida sıkarken çatlayabilirSergi ve vitrin projeleri
3D baskı (PLA / PETG)Karmaşık parçalar, tam ölçü servo yuvalarıBaskı süresi uzun; PLA sıcakta ve sürekli yük altında esnerKendi tasarımını yapanlar; yük taşıyan parçalarda PETG
Alüminyum braket kitiÇok sağlam, MG996R ile uyumlu hazır parçalarAğır ve pahalıdır, omuza daha fazla yük binerDaha ağır nesne kaldıracak 6 servolu kollar
Karton, dondurma çubuğuNeredeyse bedava, hızlı prototipDayanıksızdır, eklemler boşluk yaparÖlçüleri denemek, çocuklarla ilk deneme
  • Kolu kısa ve hafif tut: Omuzdaki yük mesafeyle doğru orantılı artar. Kolu 30 cm yerine 24 cm yapmak omuz torkunu yaklaşık beşte bir azaltır.
  • Ağır servoları tabana yakın yerleştir: Dirsek servosunu omuz hizasına alıp dirseği bir itme çubuğuyla (paralel bağlantı) sürmek, MeArm gibi kolların kullandığı akıllıca bir yöntemdir; omuz o servonun ağırlığını taşımak zorunda kalmaz.
  • Tabanda rulman kullan: Kolun bütün ağırlığını taban servosunun miline bindirme. Bir 608 rulman (8 mm iç çap, 22 mm dış çap, 7 mm kalınlık; bilinen adıyla kaykay rulmanı) ya da döner tabla rulmanı yükü taşır, servo yalnızca döndürür.
  • Eklemleri iki taraftan destekle: Servo eklemi korna tarafından tutarken karşı tarafa bir M3 cıvata ve kontra somunla pim koy. Böylece servonun mili eğilme yükü almaz ve dişliler daha uzun yaşar.
  • Metal korna kullan: MG996R için 25 dişli (25T) metal yuvarlak korna tak ve kornayı ortadaki vidayla mile sabitle. Plastik kornalar ağır eklemlerde kayar ve dişini kaybeder.
  • Kıskaç parmaklarına lastik ekle: Parmak uçlarına ince bir kauçuk şerit yapıştırmak, nesneyi daha az sıkma kuvvetiyle kaymadan tutmanı sağlar.

Servo seçimi: SG90, MG90S ya da MG996R?

ModelTorkHız (60° için)AğırlıkDişliKilitlenme akımıRobot koldaki yeri
SG901,8 kg·cm (4,8 V)0,10 sn (4,8 V)9 gPlastikYaklaşık 0,6–0,75 AKıskaç, hafif bilek, küçük 4 eksenli kol
MG90S1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V)0,10 sn (4,8 V)13,4 gMetalYaklaşık 0,7–1 ABilek, bilek dönüşü, kıskaç
MG996R9,4 kg·cm (4,8 V), 11 kg·cm (6 V)0,17 sn (4,8 V)55 gMetal2,5 A (6 V)Taban, omuz, dirsek
DS321819 kg·cm (5 V), 21,5 kg·cm (6,8 V)0,16 sn (5 V)60 gMetalYaklaşık 2–3 AUzun ya da ağır kolların omzu

Tablodaki tork değeri kilitlenme (stall) torkudur: servonun milini durdurmadan önce verebildiği en büyük kuvvettir, sürekli çalışabileceği değer değildir. Servo bu sınıra yaklaştıkça çektiği akım kilitlenme akımına çıkar, ısınır ve titremeye başlar. Pratik kural, bir eklemde ihtiyaç duyduğun torkun servonun kilitlenme torkunun en fazla yarısı olmasıdır. Gerilim de önemlidir: MG996R 4,8 V'ta 9,4 kg·cm, 6 V'ta 11 kg·cm verir; yani aynı servo yalnızca besleme gerilimiyle yaklaşık %17 güçlenir. Bu yüzden MG996R'li kollarda 5 V yerine 6 V besleme mantıklıdır, ama SG90 ve MG90S için 6 V üst sınırdır. MG996R'nin bağlantısı ve sınırları için MG996R servo motor yazısına da bakabilirsin.

MG996R metal dişli servo motor
MG996R: metal dişli, 6 V'ta 11 kg·cm tork ve 25 dişli çıkış mili; robot kolun taban, omuz ve dirsek eklemleri için standart seçim.

Tork hesabı: omuz servosu bu kolu kaldırır mı?

Bir servonun kaldırması gereken tork, eklemden sonra gelen her parçanın kütlesi ile eklem eksenine olan yatay uzaklığının çarpımlarının toplamıdır: Tork (kg·cm) = Σ kütle (kg) × uzaklık (cm). En kötü durum kolun tamamen yatay uzandığı andır, çünkü bütün uzaklıklar en büyük değerini alır; dik duran kolda bu kaldıraç kolu neredeyse sıfıra iner. Sonucu newton metreye çevirmek için 0,098 ile çarp (1 kg·cm ≈ 0,098 N·m). Örnek olarak omuz–dirsek arası 12 cm, dirsek–bilek arası 12 cm, bilekten kıskaç ucuna 6 cm olan ve dirseğinde MG996R bulunan bir masaüstü kolun omuz yükünü hesaplayalım; kıskaç 50 gramlık bir nesne tutuyor:

ParçaKütleOmuz eksenine uzaklıkTork
Üst kol parçaları (MDF ve cıvatalar)40 g6 cm (ağırlık merkezi)0,04 × 6 = 0,24 kg·cm
Dirsek servosu (MG996R)55 g12 cm0,055 × 12 = 0,66 kg·cm
Ön kol parçaları30 g18 cm0,03 × 18 = 0,54 kg·cm
Bilek servosu + kıskaç (MG90S, SG90 ve parçalar)50 g25 cm0,05 × 25 = 1,25 kg·cm
Tutulan nesne50 g30 cm0,05 × 30 = 1,50 kg·cm
Toplam225 g—4,19 kg·cm (≈ 0,41 N·m)

Bu kol, omuzda kıpırdamadan dururken 4,19 kg·cm ister. Hızlanma ve frenlemenin getirdiği ek yükü karşılamak ve servoyu kilitlenme sınırından uzak tutmak için bu değeri 2 ile çarp: 8,4 kg·cm. Demek ki omuz için en az bir MG996R gerekir (4,8 V'ta 9,4 kg·cm, 6 V'ta 11 kg·cm); SG90 ya da MG90S'in 1,8–2,2 kg·cm'lik torku bu kolu yerinden bile oynatamaz. Aynı hesabı dirsek için yaparsan ön kol (0,03 × 6), bilek ile kıskaç (0,05 × 13) ve nesne (0,05 × 18) toplam 1,73 kg·cm, güvenlik payıyla 3,5 kg·cm eder; dirsek de MG996R ister. Bilekte yalnızca kıskaç ve nesne kaldığı için yük 0,4 kg·cm civarındadır ve MG90S rahatça yeter. Bu hesap kolun taşıyabileceği en ağır nesneyi de söyler: nesnenin kütlesini artırıp toplamı yeniden hesapla ve servonun yarı torkunu geçme.

Omuz torkunu düşürmenin üç ucuz yolu var: kolu kısaltmak, dirsek servosunu omuz hizasına taşıyıp dirseği bir itme çubuğuyla sürmek ve omzun arkasına bir karşı ağırlık ya da kolu yukarı çeken bir yay eklemek. Örneğin omuz ekseninin 5 cm arkasına konan 400 gramlık bir ağırlık 0,4 × 5 = 2 kg·cm'lik karşı tork üretir ve yukarıdaki kolun yükünü neredeyse yarıya indirir; bu durumda taban rulmanının taşıdığı yük biraz artar.

Güç kaynağı: servolar neden Arduino'dan beslenmez?

Robot kol projelerindeki sorunların büyük kısmı beslemeden çıkar. Bir MG996R hareket ederken 0,5–0,9 A, zorlandığında 2,5 A'e kadar akım çeker; küçük SG90 bile kilitlenince 0,6 A'i geçebilir. Arduino Uno USB'den beslendiğinde 5V pininden alabileceğin toplam akım USB'nin sınırı olan yaklaşık 500 mA'dir ve bunun bir kısmını kartın kendisi harcar. Üç büyük servo aynı anda kalkışa geçtiğinde gerilim bir anda düşer, mikrodenetleyici resetlenir ve kol başlangıç pozuna sıçrar. Çözüm, servoları ayrı bir 5–6 V kaynaktan beslemek ve bütün GND'leri birleştirmektir; Arduino'yu USB'den ya da kendi adaptöründen beslemeye devam edebilirsin. Kaynak seçenekleri için Arduino güç besleme yazısına, pille çalışan bir kolun ne kadar dayanacağını tahmin etmek için pil ömrü hesaplama aracına bakabilirsin.

Kol tipiTahmini akımÖnerilen kaynak
4 adet SG90 ya da MG90S'li küçük kolHareket ederken 0,5–1 A, tepe 2–3 A5 V 3 A adaptör ya da LM2596 modülü (en fazla 3 A, soğutucusuz yaklaşık 2 A)
3 MG996R + 3 MG90S'li 6 servolu kolHareket ederken 2–3 A, hepsi zorlanınca 8–10 A5–6 V, en az 5 A anahtarlamalı adaptör (rahat kullanım için 10 A)
Taşınabilir kolKol tipine göre2S LiPo (7,4 V) + 6 V'a ayarlı, 5 A ve üzeri düşürücü modül (UBEC)
Kaçınılacak kaynaklar—9 V kare pil (akım veremez), Arduino'nun 5V pini, MG996R'li kolda 4 adet AA alkalin pil
Servoları asla Arduino'nun 5V pininden besleme ve harici kaynağın eksisini Arduino'nun GND'sine mutlaka bağla; ortak GND yoksa servolar sinyali anlamaz ve rastgele hareket eder. Servo besleme hattına, servoların hemen yanına servo başına en az 100 µF hesabıyla bir elektrolitik kondansatör koy (6 servoluk kolda 1000–2200 µF, en az 16 V) ve uzun bacağı artıya gelecek şekilde tak; ters takılan elektrolitik kondansatör şişip patlayabilir. Breadboard'un güç rayları yaklaşık 1 A için tasarlanmıştır; MG996R'li bir kolda besleme kablolarını doğrudan klemense ya da PCA9685'in V+ girişine bağla.

4 eksenli kol için devre bağlantısı

Aşağıdaki tablo potansiyometreli 4 eksenli kolun Arduino Uno ve Raspberry Pi Pico bağlantısıdır; bu yazıdaki kodlar bu pinlere göre yazıldı. Uno'da Servo kütüphanesi her dijital pinden servo sürebilir, ancak kütüphane Timer1'i kullandığı için servo bağlıyken 9 ve 10 numaralı pinlerde analogWrite çalışmaz. Kartının pin yerleşimini Pinout aracında görebilirsin.

BağlantıArduino UnoRaspberry Pi Pico
Taban servosu sinyal (turuncu kablo)D3GP10
Omuz servosu sinyalD5GP11
Dirsek servosu sinyalD6GP12
Kıskaç servosu sinyalD9GP13
Pot 1–3 orta bacak (taban, omuz, dirsek)A0, A1, A2GP26, GP27, GP28
Pot 4 orta bacak (kıskaç)A3Yok; kıskaç GP14'teki butonla
Pot kenar bacakları5V ve GND3V3 ve GND
Kayıt ve oynat butonları (diğer uç GND)D7 ve D8—
Servo kırmızı kablo (+)Harici 5–6 V artıHarici 5–6 V artı
Servo kahverengi kablo (−)Harici kaynağın eksisi + Arduino GNDHarici kaynağın eksisi + Pico GND
10 kΩ döner potansiyometre
10 kΩ potansiyometre: kenar bacaklar 5V ve GND'ye, orta bacak analog girişe bağlanır; mili çevirdikçe eklem de aynı oranda döner.

Montajdan önce kalibrasyon: servoları ortala

  1. Kornaları takmadan önce aşağıdaki kodu yükle; dört servo da sırayla 90 dereceye gider ve orada bekler.
  2. Kolu nötr bir pozda tut (örneğin üst kol dik, ön kol yatay, kıskaç yarı açık) ve kornaları bu poza en yakın dişe takıp ortadaki vidayla sabitle.
  3. Koddaki CENTER_ONLY değerini false yap, TEST ile bir eklem seç ve A0'daki potansiyometreyi yavaşça çevir.
  4. Seri Monitör'de açıyı izle; eklemin hiçbir yere çarpmadan ve kabloları germeden gidebildiği en küçük ve en büyük açıyı not et.
  5. Kıskacın kapalı açısını parmakların birbirine yeni değdiği an olarak belirle; daha fazla sıkmaya çalışan servo durmadan zorlanır ve ısınır.
  6. Bulduğun değerleri sonraki kodlardaki MIN_A ve MAX_A dizilerine yaz.
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}

Kodda küçük ama önemli bir ayrıntı var: servo.write(90) komutu attach()'tan önce verildiği için servo ilk darbede doğrudan istenen açıyı alır. Sıra tersine olursa kütüphane önce varsayılan 1500 µs'lik darbeyi (yaklaşık 90°) gönderir; başlangıç açın 90'dan farklıysa kol açılışta önce ortaya, sonra hedefe sıçrar. Servoları aralarında 300 ms bekleyerek tek tek başlatmak da açılıştaki toplam akım darbesini böler.

Potansiyometre ile robot kol kontrolü

En sezgisel kontrol yöntemi her eklem için bir potansiyometre kullanmaktır: pot mili hangi açıdaysa eklem de o açıya gider, yani konum kontrolü yaparsın. Potları kolun küçük bir maketine (aynı oranlarda karton ya da 3D baskı) yerleştirirsen maketi elle oynattığında büyük kol hareketi birebir kopyalar. Analog okumalar ise doğası gereği birkaç basamak oynar ve bu oynama servoya titreme olarak yansır; kodda bunun için iki önlem var.

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}

smooth dizisi basit bir alçak geçiren filtredir: her döngüde yeni okumanın yalnızca %20'si eklenir, böylece ani sıçramalar yumuşar ama kol yine de çabuk tepki verir. 0,2 yerine 0,1 yazarsan hareket daha yumuşak ama daha gecikmeli olur. İkinci önlem ölü banttır: yeni açı son yazılan açıdan en az 2 derece farklı değilse servoya komut gönderilmez ve duran koldaki titreme kesilir. map() pot aralığını doğrudan her eklemin güvenli sınırlarına çevirdiği için pot sonuna kadar çevrilse bile eklem mekanik sınıra dayanmaz.

Hareketleri kaydetme ve tekrar oynatma

Robot kolu gerçekten robot yapan özellik, gösterdiğin hareketi kendi başına tekrarlamasıdır. Yöntem basit: kolu potlarla bir poza getirip kayıt butonuna basarsın ve Arduino dört eklemin açısını saklar. Birkaç poz kaydettikten sonra oynat butonuna bastığında kol pozları sırayla, aralarındaki geçişi yumuşatarak tekrar eder. Pozlar Arduino Uno'nun 1024 baytlık EEPROM belleğine yazıldığı için kartı kapatıp açtığında da silinmez; her poz 4 bayt tuttuğundan 100 pozluk bir kayıt yalnızca 401 bayt yer kaplar.

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}
İşlemNe yaparsın?Kol ne yapar?
Poz kaydetmeKayıt butonuna (D7) kısa basLED bir kez yanıp söner, poz EEPROM'a yazılır
Kayıtları silmeKayıt butonunu 2 saniye basılı tutLED beş kez yanıp söner, adım sayısı sıfırlanır
OynatmaOynat butonuna (D8) bas ve bırakLED yanar, pozlar sırayla ve yumuşak geçişle tekrarlanır
Oynatmayı durdurmaOynatma sırasında oynat butonunu basılı tutKol o anki pozu bitirir ve durur
Normal moda dönüşOynatma bitince hiçbir şey yapmaKol yavaşça potların gösterdiği konuma döner, LED söner

moveTo() fonksiyonu kodun kalbidir: en uzun yolu gidecek eklemi bulur, hareketi o kadar adıma böler ve her adımda bütün eklemleri orantılı olarak ilerletir. Böylece eklemler hedefe aynı anda varır ve kol, eklemlerini tek tek oynatan bir robot gibi değil, akıcı bir hareketle gider. Derece başına 12 ms, 90 derecelik bir hareketin yaklaşık 1,1 saniye sürmesi demektir. Daha hızlı tekrar için bu değeri düşürebilirsin, ama MG996R yüksüzken bile 60 dereceyi 0,17 saniyede, yani derece başına yaklaşık 3 ms'de döner; bunun altına inersen servolar komutlara yetişemez ve hareket sarsıntılı olur.

EEPROM'un her hücresi yaklaşık 100.000 kez yazılabilir. Kod EEPROM.update() kullandığı için yalnızca değeri değişen hücrelere yazar ve bu ömür pratikte hiç tükenmez. Daha uzun hareket dizileri için kayıtları SD karta yazabilir ya da Arduino Mega'nın 4 KB'lık EEPROM'unu kullanabilirsin.

PCA9685 servo sürücü: 6 servo ve titremesiz sinyal

Servo sayısı 5–6'ya çıktığında ya da Bluetooth gibi ek modüller eklediğinde PCA9685 modülü işini çok kolaylaştırır. Bu kart 16 kanallı, 12 bit çözünürlüklü bir PWM üretecidir; Arduino ile yalnızca iki kabloyla, I2C üzerinden konuşur ve servo darbelerini kendi saatiyle, işlemciden bağımsız üretir. Varsayılan adresi 0x40'tır; kartın üzerindeki A0–A5 lehim köprüleriyle adresi değiştirerek 62 karta, yani 992 servoya kadar çıkabilirsin. 50 Hz'de 20 ms'lik periyot 4096 parçaya bölündüğü için bir adım yaklaşık 4,9 µs'dir; 544–2400 µs aralığı yaklaşık 380 adım eder, bu da yarım dereceye yakın bir çözünürlük demektir.

PCA9685 piniArduino UnoESP32Raspberry Pi Pico
GNDGNDGNDGND
VCC (yalnızca yonganın beslemesi)5V3V33V3
SDAA4GPIO 21GP4
SCLA5GPIO 22GP5
V+ klemensi (servo beslemesi)Harici 5–6 VHarici 5–6 VHarici 5–6 V
OEBoş bırakBoş bırakBoş bırak
Servo konnektörleri 0–5Taban, omuz, dirsek, bilek, bilek dönüşü, kıskaçAynı sıraAynı sıra
PCA9685'te iki ayrı besleme vardır: VCC yalnızca yongayı besler, servolar ise klemensteki V+ girişinden beslenir. V+ boş kalırsa kart I2C'de görünür ama servolar kıpırdamaz. Arduino Uno'da A4 ve A5 I2C için kullanıldığından PCA9685 bağlıyken analog giriş olarak yalnızca A0–A3 kalır; daha fazla potansiyometre gerekiyorsa ek A6 ve A7 girişleri olan Arduino Nano'yu kullan.

Joystick ile robot kol kontrolü

Joystick potansiyometreden farklı çalışır: bırakıldığında ortaya döner. Açıyı doğrudan joystick'in konumuna bağlarsan, kolu bıraktığın an eklem 90 dereceye fırlar. Doğru yöntem hız kontrolüdür: joystick'i ne kadar ittiysen eklem o kadar hızlı döner, bıraktığında olduğu yerde kalır. Aşağıdaki kodda iki joystick dört eklemi sürer (sol joystick taban ve omuz, sağ joystick dirsek ve bilek); sol joystick'in butonu kıskacı açıp kapatır, sağdaki ise kolu başlangıç pozuna götürür. Sol joystick'in VRx ve VRy çıkışlarını A0 ve A1'e, sağ joystick'in VRy ve VRx çıkışlarını A2 ve A3'e, SW pinlerini D2 ve D4'e bağla; joystick'leri Arduino'nun 5V pininden besleyebilirsin. Kodu derlemek için Kütüphane Yöneticisi'nden Adafruit PWM Servo Driver Library'yi kur ve Adafruit BusIO bağımlılığını da kabul et.

İki eksenli analog joystick modülü
Joystick modülü: VRx ve VRy çıkışları ortada yaklaşık 512 okur, SW butonu basılınca GND'ye çeker. İki modülle bir robot kolun dört eklemini sürebilirsin.
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, joystick ortadayken okunan 490–530 gibi küçük sapmaların kolu kendi kendine kaydırmasını engeller. MAX_SPEED joystick sonuna kadar itildiğindeki hızı derece/saniye olarak belirler; 90 derece/saniye ilk denemeler için güvenlidir. Bir eklem joystick'in tersine gidiyorsa kabloları değiştirme, DIR dizisindeki ilgili değeri -1 yap. Kod her eklem için bir hedef (target) ve bir gerçek konum (current) tutar; gerçek konum hedefe hız sınırıyla yaklaştığı için başlangıç pozu butonuna basıldığında bile kol sıçramadan, akıcı biçimde döner. Bilek dönüşü bu kodda 90 derecede sabit kalır; onu Bluetooth bölümündeki R komutuyla yönetebilirsin. Beklemeden çalışan bu zamanlama yönteminin ayrıntısı millis() ve delay() yazısında.

Telefondan Bluetooth kontrol (HC-05)

HC-05 modülü telefonla Arduino arasında kablosuz bir seri hat kurar. Modülün TXD pini Arduino'nun D10'una, RXD pini ise bir gerilim bölücü üzerinden D11'e bağlanır: D11 → 1 kΩ → RXD → 2 kΩ → GND. Bölücü, Arduino'nun 5 V'luk sinyalini HC-05'in beklediği yaklaşık 3,3 V'a indirir. Modülün varsayılan veri hızı 9600 baud, eşleştirme şifresi genellikle 1234'tür. Telefon basit metin komutları gönderir: bir harf eklemi seçer, ardından gelen sayı açıyı verir, satır sonu ya da noktalı virgül komutu bitirir.

HC-05 Bluetooth seri modülü
HC-05: klasik Bluetooth ile telefona bağlanan seri modül; VCC 5 V ile beslenir ama RXD girişi 3,3 V seviyesindedir.
KomutAnlamıÖrnek
B + açıTabanB90
S + açıOmuzS120
E + açıDirsekE45
W + açıBilek (yukarı-aşağı)W100
R + açıBilek dönüşüR0
G + açıKıskaçG110 (kapat), G40 (aç)
HBaşlangıç pozuna dönH
K / P / X / CPozu kaydet / döngüyle oynat / durdur / kayıtları silK
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}

Bu kodda servolar doğrudan Arduino'ya değil PCA9685'e bağlı ve bu bilinçli bir seçim. SoftwareSerial her baytı alırken kesmeleri yaklaşık 1 ms kapatır; servolar Servo kütüphanesiyle Arduino pinlerinden sürülseydi bu sırada darbe süreleri uzar ve kol her komutta seğirirdi. PCA9685 darbeleri kendisi ürettiği için Bluetooth trafiği ne kadar yoğun olursa olsun sinyal titremez. Kayıt ve oynatma da artık telefondan yapılır: kolu kaydırıcılarla bir poza getirip K gönderirsin, birkaç poz sonra P ile kol bu pozları döngü hâlinde tekrarlar, X ile durur. Pozlar bu sürümde RAM'de tutulur ve kart kapanınca silinir; kalıcı olmalarını istersen önceki koddaki EEPROM yöntemini ekleyebilirsin.

Telefon tarafında hazır bir uygulama yeterli: Android'de bir Bluetooth seri terminal ya da kaydırıcılı bir Bluetooth kontrol uygulaması kurup her kaydırıcıya "S" gibi bir ön ek ve satır sonu tanımla. Kendi düğmeli ve kaydırıcılı arayüzünü yapmak istersen MIT App Inventor ile Bluetooth uygulaması yazısı adım adım anlatıyor. HC-05 klasik Bluetooth kullandığı için iPhone ile çalışmaz; iPhone için ESP32 BLE gibi Bluetooth Low Energy kullanan bir çözüm gerekir.

ESP32 ile kablosuz robot kol

ESP32'nin içinde klasik Bluetooth bulunduğu için HC-05'e gerek kalmaz: BluetoothSerial kütüphanesiyle kart telefonda "BlueGrays-RobotKol" adıyla görünür ve aynı komutları anlar. PCA9685'i GPIO 21 (SDA) ve GPIO 22 (SCL) pinlerine bağla, VCC'yi 3V3'ten besle; servo sinyalleri 3,3 V seviyesinde çıkar ve servoların büyük çoğunluğu bunu sorunsuz algılar. BluetoothSerial yalnızca klasik ESP32'de (ESP32-WROOM-32, DevKit) çalışır; ESP32-S2, S3 ve C3 klasik Bluetooth desteklemez.

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}

Raspberry Pi Pico (MicroPython) ile robot kol

Raspberry Pi Pico'da servo sürmek için ek kütüphane gerekmez: PWM'i 50 Hz'e ayarlayıp darbe genişliğini duty_ns() ile nanosaniye cinsinden verirsin. Pico'nun 16 PWM kanalı olduğu için 6 servoyu doğrudan sürebilir; aynı dilimi (slice) paylaşan pin çiftlerinin frekansı ortaktır, ama bütün servolar 50 Hz kullandığından bu sorun olmaz. Kısıt analog taraftadır: Pico'da kullanılabilir yalnızca üç ADC pini (GP26, GP27, GP28) vardır. Bu yüzden aşağıdaki kodda üç eklem potla sürülür, kıskaç ise GP14'teki butonla açılıp kapanır. Pico 3,3 V ile çalıştığı için potları 3V3'e bağla; servoları yine harici 5 V kaynaktan besle.

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

Bir adım ileri: ters kinematik

Şimdiye kadar her eklemin açısını tek tek verdik; buna ileri kinematik denir. Ters kinematik tersini yapar: kıskacın gitmesini istediğin noktayı (x, y) verirsin, kod gereken eklem açılarını hesaplar. Omuz–dirsek uzunluğu L1, dirsek–bilek uzunluğu L2 olan iki parçalı bir kolda önce D = (x² + y² − L1² − L2²) / (2·L1·L2) bulunur. Dirsek açısı θ2 = acos(D), omuz açısı θ1 = atan2(y, x) + atan2(L2·sin θ2, L1 + L2·cos θ2) olur (dirsek yukarıda çözümü). Örneğin L1 = L2 = 12 cm iken omzun 15 cm önündeki ve 5 cm yukarısındaki nokta için D ≈ −0,13, θ2 ≈ 97,6° ve θ1 ≈ 67,2° çıkar: üst kol yataydan 67 derece yukarı bakar, ön kol ise üst kola göre 97,6 derece aşağı bükülür. D'nin mutlak değeri 1'den büyükse nokta kolun erişim alanının dışındadır. Taban açısı, hedefin üstten bakıştaki konumundan ayrıca hesaplanır. Hesaplanan açıları servo açısına çevirirken her servonun takılış yönünü ve orta noktasını hesaba katman gerekir.

Titreme ve resetlenme: sık hatalar ve çözümleri

BelirtiOlası sebepÇözüm
Kol dururken servolar titriyor, vızıldıyorPot okumalarındaki gürültü ya da zayıf beslemeYumuşatma filtresi ve 2°'lik ölü bant ekle, harici kaynak ve 1000 µF kondansatör kullan
Kol hareket edince Arduino resetleniyor, HC-05 bağlantısı kopuyorServoların kalkış akımı gerilimi düşürüyorServoları ayrı 5–6 V kaynaktan besle, GND'leri birleştir, servoları açılışta sırayla başlat
Bluetooth komutu gelince servolar seğiriyorSoftwareSerial kesmeleri Servo kütüphanesinin darbelerini bozuyorServoları PCA9685'e taşı ya da ESP32'nin dahili Bluetooth'unu kullan
Açılışta kol hızla bir yere sıçrıyorServolar son konumlarını bilmez, ilk komutta tam hızla giderKolu kapatmadan önce başlangıç pozuna getir, write() komutunu attach()'tan önce ver
Omuz kolu kaldıramıyor ya da yavaşça çöküyorTork yetersizTork hesabını yap; MG996R, daha kısa kol, karşı ağırlık ya da yay kullan
Bir eklem uçta takılıp ısınıyorServo mekanik sınıra dayanıyorKalibrasyonda bulduğun MIN_A ve MAX_A sınırlarını koda yaz
Kıskaç nesneyi tutarken servo ısınıyorKıskaç kapanmaya çalışmayı sürdürüyor, servo kilitli kalıyorKapalı açıyı nesneye yeni değdiği değere ayarla, parmaklara lastik ekle
PCA9685 bağlı ama servolar hiç dönmüyorV+ klemensine besleme verilmemiş ya da GND ortak değilV+ girişine 5–6 V ver, I2C adresini (0x40) kontrol et
Eklem yük altında kayıyorPlastik korna dişi sıyırmış ya da orta vida takılmamışMetal korna kullan, kornayı ortadaki vidayla mile sabitle
Sorunun güçten mi koddan mı geldiğini anlamanın kısa yolu: servoları bağlamadan kalibrasyon kodunu (CENTER_ONLY = false) çalıştır ve potu çevirirken Seri Monitör'de açıları izle. Değerler düzgünse sorun beslemededir; kol hareket ederken servo hattının gerilimini multimetreyle ölç, 4,5 V'un altına düşüyorsa kaynak ya da kablolar yetersizdir. Resetlenmenin bütün sebepleri için Arduino neden resetleniyor? yazısına bakabilirsin.

Robot kol ile proje fikirleri

  • Renk ayıklayan kol: Bir TCS3200 renk sensörü ile küplerin rengini oku, kol her rengi kendi kutusuna bıraksın.
  • El hareketiyle kontrol: Eldivene takılan bir MPU6050 ile bileğini eğdiğinde robot kol da aynı yöne eğilsin.
  • Kablosuz kumanda: İki joystick'i ayrı bir Arduino'ya bağla ve komutları nRF24L01 ile gönder; telefona ihtiyaç duymayan bir uzaktan kumanda elde edersin.
  • Tarayıcıdan kontrol: ESP32 ile bir web sayfasında kaydırıcılar oluştur ve kolu aynı Wi-Fi ağındaki her cihazdan yönet; ESP32 WebSocket gecikmeyi en aza indirir.
  • Banttan alma: Bir step motorla küçük bir konveyör bant yap; TCRT5000 nesneyi gördüğünde kol onu banttan alsın.
  • Yarışma ve okul projesi: Kaydet-oynat özellikli bir kol, TÜBİTAK proje fikirleri arasında mekaniği, elektroniği ve yazılımı birlikte gösteren güçlü bir seçenektir.

Arduino robot kol; mekanik tasarımı, tork hesabını, güç yönetimini ve yumuşak hareket kodlamayı aynı anda öğreten, emek verdikçe büyüyen bir projedir. 4 servolu, potansiyometreli bir kolla başla, kaydet-oynat özelliğini ekle, sonra PCA9685 ve Bluetooth ile 6 servoya çık. BlueGrays uygulamasında potansiyometre ve joystick ile servo kontrolü, telefondan Bluetooth kontrol ve 32 servolu koreografi gibi projeleri devre şemaları ve adım adım kodlarıyla bulabilir, kolunun her eklemini önce tek tek deneyebilirsin.

Sıkça sorulan sorular

Arduino robot kol için hangi servo motor kullanılmalı?

Taban, omuz ve dirsek gibi yük taşıyan eklemler için metal dişli ve 9–11 kg·cm torklu MG996R iyi bir seçimdir. Bilek ve kıskaç gibi hafif eklemlerde MG90S ya da SG90 yeterlidir. Yalnızca SG90'larla yapılan küçük kollar kısa ve hafif tutulursa çalışır ama ancak birkaç on gramlık nesneleri kaldırabilir.

Robot kol için kaç amperlik adaptör gerekir?

4 adet SG90 ya da MG90S kullanan küçük bir kol için 5 V 3 A adaptör yeterlidir. 3 MG996R ve 3 küçük servodan oluşan 6 servolu bir kolda en az 5 A, rahat bir kullanım için 10 A'lik 5–6 V anahtarlamalı adaptör önerilir. Adaptörün eksisini Arduino'nun GND'sine bağlamayı unutma.

Arduino'ya kaç servo motor bağlanabilir?

Servo kütüphanesi Arduino Uno'da 12, Mega'da 48 servoya kadar sürebilir. Asıl sınır ise akımdır; servoların hepsi harici bir kaynaktan beslenmelidir. Bir PCA9685 modülü yalnızca iki I2C pini kullanarak 16 servo sürer ve 62 modüle kadar zincirlenebilir.

Robot kol servoları neden titriyor?

En sık sebepler yetersiz güç kaynağı, ortak GND eksikliği ve potansiyometre okumalarındaki gürültüdür. Harici kaynak ve servo hattına 1000 µF kondansatör kullan, kodda yumuşatma filtresi ve ölü bant uygula. SoftwareSerial ile Servo kütüphanesi birlikte kullanılıyorsa servoları PCA9685'e taşımak titremeyi tamamen keser.

SG90 servolu robot kol ne kadar ağırlık kaldırır?

SG90'ın kilitlenme torku yaklaşık 1,8 kg·cm'dir; güvenli kullanım için bunun yarısını, yani 0,9 kg·cm'yi hesaba kat. Kolun ve servoların kendi ağırlığı da bu bütçeden harcandığı için 15 cm civarındaki SG90'lı bir kol pratikte yalnızca 20–30 gramlık hafif nesneleri, örneğin bir şişe kapağını ya da küçük bir köpük küpü taşıyabilir.

Robot kol telefondan nasıl kontrol edilir?

Arduino'ya bir HC-05 Bluetooth modülü bağlanır ve telefondaki bir Bluetooth seri terminal ya da kaydırıcılı kontrol uygulaması "S120" gibi metin komutları gönderir. ESP32 kullanıyorsan dahili Bluetooth sayesinde ek modül gerekmez. HC-05 iPhone ile çalışmadığından iPhone için BLE destekli bir modül ya da ESP32 BLE gerekir.

Tüm projeleri gör
Potansiyometre İle Servo Motor Kullanım
#36Başlangıç
Premium

Potansiyometre İle Servo Motor Kullanım

Potansiyometrenin düğmesini çevirdikçe servo motorun kolu da onunla birlikte döner; düğmenin bir ucu sıfır dereceye, öbür ucu yarım tura karşılık gelir. Arduino düğmenin konumunu sürekli okur ve servo

4.6(37)
52
Joystick İle Servo Kontrol
#60Orta
Premium

Joystick İle Servo Kontrol

Bir oyun kolu çubuğuyla iki motoru yönet, tıpkı bir güvenlik kamerasını çevirir gibi. Çubuğu sağa sola ittiğinde alttaki servo motor yavaşça döner, yukarı aşağı ittiğinde üstteki servo başını kaldırıp

4.7(42)
58
Servo Motor Kullanım
#35Başlangıç
Premium

Servo Motor Kullanım

Küçük bir servo motorun kolu sırayla üç konuma döner: en başa, tam ortaya ve en sona, her durakta iki saniye bekleyerek. Arduino motora hangi açıda durması gerektiğini söyler, motor da oraya gidip ora

4.7(48)
67
ESP32 Servo Motor (Potansiyometre Kontrolü)
#118Orta
Premium

ESP32 Servo Motor (Potansiyometre Kontrolü)

Bu projede potansiyometre ile SG90 servo motoru kontrol ediyoruz. Potansiyometreden okunan analog değeri 0-180 derece aralığına map'leyerek servo açısını ayarlıyoruz. ESP32Servo kütüphanesi, ESP32'nin çıkış verebilen uygun GPIO'larında servo sinyali üretir; bu projede GPIO 18 kullanılır. Bu, robotik kollar, kamera gimbal'ları ve otomatik kapı mekanizmaları için temel bir kontrol yöntemidir.

4.3(29)
42
Bluetooth İle Telefondan Led Yakma
#34Başlangıç
Premium

Bluetooth İle Telefondan Led Yakma

Telefonundan odadaki ışıkları açıp kapatmanın küçük bir provası. Bluetooth modülü telefondan gelen harfleri Arduino'ya iletir; bir harf kırmızı LED'i, başka bir harf yeşil LED'i yakar, aynı harflerin

4.8(68)
89
L298N ile Bluetooth Kontrollü Araba
#80İleri
Premium

L298N ile Bluetooth Kontrollü Araba

Telefonunu kumandaya çevirip kendi robot arabanı sür. Arduino, Bluetooth modülü üzerinden telefondaki bir kumanda uygulamasından gelen komutları alır ve motor sürücüyle tekerlekleri döndürür; araba il

5.0(38)
65
Servo Çember Koreografisi — 32 Servo ile Animasyonlu Desenler
#102İleri
Premium

Servo Çember Koreografisi — 32 Servo ile Animasyonlu Desenler

Arduino Mega üzerinde çember şeklinde dizilmiş 32 servo motorla çalışan görsel bir animasyon projesi. Servolar tek bir dizi (array) içinde yönetiliyor ve Mega'nın 22–53 numaralı pinlerine bağlanarak dört farklı hareket dizisi sergiliyor: tüm servoların rastgele açılara gittiği kaotik bir titreşim, hepsinin aynı anda hareket ettiği senkron süpürme, çember etrafında akıcı şekilde dönen bir dalga ve son olarak sağa sola sallanan bir pusula ibresi. Dönen dalga efekti, her servonun aktif servoya olan

4.5(40)
62