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.



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.
| Eklem | Hareketi | Yük durumu | Önerilen servo |
|---|---|---|---|
| Taban (bel) | Kolu sağa sola döndürür | Ağırlık kaldırmaz ama tüm kolu taşır | MG996R + rulman |
| Omuz | Kolu öne ve arkaya kaldırır | En çok zorlanan eklem | MG996R ya da 20 kg·cm sınıfı |
| Dirsek | Ön kolu büker | Orta-yüksek | MG996R |
| Bilek (yukarı-aşağı) | Kıskacın açısını ayarlar | Düşük | MG90S |
| Bilek dönüşü | Kıskacı kendi ekseninde çevirir | Çok düşük | MG90S ya da SG90 |
| Kıskaç (gripper) | Nesneyi tutar ve bırakır | Sıkma kuvveti | SG90 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ça | 4 eksenli kol | 6 servolu kol | Not |
|---|---|---|---|
| Kontrol kartı | Arduino Uno ya da Nano | Arduino Uno, Nano ya da ESP32 | Nano'nun ek A6 ve A7 girişleri pot sayısını artırır |
| Servo motor | 4 adet SG90 ya da MG90S | 3 adet MG996R + 3 adet MG90S | Seçimini aşağıdaki tork hesabıyla doğrula |
| Servo sürücü | Gerekmez | PCA9685 16 kanallı modül | I2C ile yalnızca 2 sinyal kablosu |
| Güç kaynağı | 5 V 3 A adaptör | 5–6 V, en az 5 A adaptör | Servolar Arduino'dan beslenmez |
| Kondansatör | 470–1000 µF, 16 V | 1000–2200 µF, 16 V | Servo besleme hattına, kutup yönüne dikkat ederek |
| Kontrol elemanı | 4 adet 10 kΩ potansiyometre | 2 adet joystick modülü ya da HC-05 | İkisini birlikte de kullanabilirsin |
| Buton | 2 adet (kayıt, oynat) | Joystick butonları yeterli | INPUT_PULLUP ile ek direnç gerekmez |
| Mekanik | 3 mm MDF ya da 3D baskı parçalar | 4 mm MDF, 3D baskı ya da alüminyum braket | 608 rulman, M3 cıvata ve kontra somun |
Mekanik yapı: MDF mi, 3D baskı mı?
| Malzeme | Artısı | Eksisi | Kime uygun? |
|---|---|---|---|
| 3–4 mm MDF (lazer kesim) | Ucuz, hafif, kolay vidalanır ve yapıştırılır | Nemden etkilenir, vida delikleri zamanla genişler | MeArm tarzı ilk kollar, okul projeleri |
| 3 mm akrilik | Şık görünür, nemden etkilenmez | Kırılgandır; vida sıkarken çatlayabilir | Sergi 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 esner | Kendi tasarımını yapanlar; yük taşıyan parçalarda PETG |
| Alüminyum braket kiti | Çok sağlam, MG996R ile uyumlu hazır parçalar | Ağır ve pahalıdır, omuza daha fazla yük biner | Daha ağır nesne kaldıracak 6 servolu kollar |
| Karton, dondurma çubuğu | Neredeyse bedava, hızlı prototip | Dayanı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?
| Model | Tork | Hız (60° için) | Ağırlık | Dişli | Kilitlenme akımı | Robot koldaki yeri |
|---|---|---|---|---|---|---|
| SG90 | 1,8 kg·cm (4,8 V) | 0,10 sn (4,8 V) | 9 g | Plastik | Yaklaşık 0,6–0,75 A | Kıskaç, hafif bilek, küçük 4 eksenli kol |
| MG90S | 1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V) | 0,10 sn (4,8 V) | 13,4 g | Metal | Yaklaşık 0,7–1 A | Bilek, bilek dönüşü, kıskaç |
| MG996R | 9,4 kg·cm (4,8 V), 11 kg·cm (6 V) | 0,17 sn (4,8 V) | 55 g | Metal | 2,5 A (6 V) | Taban, omuz, dirsek |
| DS3218 | 19 kg·cm (5 V), 21,5 kg·cm (6,8 V) | 0,16 sn (5 V) | 60 g | Metal | Yaklaşık 2–3 A | Uzun 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.

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ça | Kütle | Omuz eksenine uzaklık | Tork |
|---|---|---|---|
| Üst kol parçaları (MDF ve cıvatalar) | 40 g | 6 cm (ağırlık merkezi) | 0,04 × 6 = 0,24 kg·cm |
| Dirsek servosu (MG996R) | 55 g | 12 cm | 0,055 × 12 = 0,66 kg·cm |
| Ön kol parçaları | 30 g | 18 cm | 0,03 × 18 = 0,54 kg·cm |
| Bilek servosu + kıskaç (MG90S, SG90 ve parçalar) | 50 g | 25 cm | 0,05 × 25 = 1,25 kg·cm |
| Tutulan nesne | 50 g | 30 cm | 0,05 × 30 = 1,50 kg·cm |
| Toplam | 225 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.
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 tipi | Tahmini akım | Önerilen kaynak |
|---|---|---|
| 4 adet SG90 ya da MG90S'li küçük kol | Hareket ederken 0,5–1 A, tepe 2–3 A | 5 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 kol | Hareket ederken 2–3 A, hepsi zorlanınca 8–10 A | 5–6 V, en az 5 A anahtarlamalı adaptör (rahat kullanım için 10 A) |
| Taşınabilir kol | Kol tipine göre | 2S 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 |
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 Uno | Raspberry Pi Pico |
|---|---|---|
| Taban servosu sinyal (turuncu kablo) | D3 | GP10 |
| Omuz servosu sinyal | D5 | GP11 |
| Dirsek servosu sinyal | D6 | GP12 |
| Kıskaç servosu sinyal | D9 | GP13 |
| Pot 1–3 orta bacak (taban, omuz, dirsek) | A0, A1, A2 | GP26, GP27, GP28 |
| Pot 4 orta bacak (kıskaç) | A3 | Yok; kıskaç GP14'teki butonla |
| Pot kenar bacakları | 5V ve GND | 3V3 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 GND | Harici kaynağın eksisi + Pico GND |

Montajdan önce kalibrasyon: servoları ortala
- Kornaları takmadan önce aşağıdaki kodu yükle; dört servo da sırayla 90 dereceye gider ve orada bekler.
- 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.
- Koddaki CENTER_ONLY değerini false yap, TEST ile bir eklem seç ve A0'daki potansiyometreyi yavaşça çevir.
- 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.
- 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.
- Bulduğun değerleri sonraki kodlardaki MIN_A ve MAX_A dizilerine yaz.
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.
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.
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}| İşlem | Ne yaparsın? | Kol ne yapar? |
|---|---|---|
| Poz kaydetme | Kayıt butonuna (D7) kısa bas | LED bir kez yanıp söner, poz EEPROM'a yazılır |
| Kayıtları silme | Kayıt butonunu 2 saniye basılı tut | LED beş kez yanıp söner, adım sayısı sıfırlanır |
| Oynatma | Oynat butonuna (D8) bas ve bırak | LED yanar, pozlar sırayla ve yumuşak geçişle tekrarlanır |
| Oynatmayı durdurma | Oynatma sırasında oynat butonunu basılı tut | Kol o anki pozu bitirir ve durur |
| Normal moda dönüş | Oynatma bitince hiçbir şey yapma | Kol 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.
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 pini | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| GND | GND | GND | GND |
| VCC (yalnızca yonganın beslemesi) | 5V | 3V3 | 3V3 |
| SDA | A4 | GPIO 21 | GP4 |
| SCL | A5 | GPIO 22 | GP5 |
| V+ klemensi (servo beslemesi) | Harici 5–6 V | Harici 5–6 V | Harici 5–6 V |
| OE | Boş bırak | Boş bırak | Boş bırak |
| Servo konnektörleri 0–5 | Taban, omuz, dirsek, bilek, bilek dönüşü, kıskaç | Aynı sıra | Aynı sıra |
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.

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.

| Komut | Anlamı | Örnek |
|---|---|---|
| B + açı | Taban | B90 |
| S + açı | Omuz | S120 |
| E + açı | Dirsek | E45 |
| W + açı | Bilek (yukarı-aşağı) | W100 |
| R + açı | Bilek dönüşü | R0 |
| G + açı | Kıskaç | G110 (kapat), G40 (aç) |
| H | Başlangıç pozuna dön | H |
| K / P / X / C | Pozu kaydet / döngüyle oynat / durdur / kayıtları sil | K |
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.
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.
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.
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 loopBir 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
| Belirti | Olası sebep | Çözüm |
|---|---|---|
| Kol dururken servolar titriyor, vızıldıyor | Pot okumalarındaki gürültü ya da zayıf besleme | Yumuş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ı kopuyor | Servoların kalkış akımı gerilimi düşürüyor | Servoları ayrı 5–6 V kaynaktan besle, GND'leri birleştir, servoları açılışta sırayla başlat |
| Bluetooth komutu gelince servolar seğiriyor | SoftwareSerial kesmeleri Servo kütüphanesinin darbelerini bozuyor | Servoları PCA9685'e taşı ya da ESP32'nin dahili Bluetooth'unu kullan |
| Açılışta kol hızla bir yere sıçrıyor | Servolar son konumlarını bilmez, ilk komutta tam hızla gider | Kolu kapatmadan önce başlangıç pozuna getir, write() komutunu attach()'tan önce ver |
| Omuz kolu kaldıramıyor ya da yavaşça çöküyor | Tork yetersiz | Tork 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ıyor | Servo mekanik sınıra dayanıyor | Kalibrasyonda bulduğun MIN_A ve MAX_A sınırlarını koda yaz |
| Kıskaç nesneyi tutarken servo ısınıyor | Kıskaç kapanmaya çalışmayı sürdürüyor, servo kilitli kalıyor | Kapalı açıyı nesneye yeni değdiği değere ayarla, parmaklara lastik ekle |
| PCA9685 bağlı ama servolar hiç dönmüyor | V+ klemensine besleme verilmemiş ya da GND ortak değil | V+ girişine 5–6 V ver, I2C adresini (0x40) kontrol et |
| Eklem yük altında kayıyor | Plastik korna dişi sıyırmış ya da orta vida takılmamış | Metal korna kullan, kornayı ortadaki vidayla mile sabitle |
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.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
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

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

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

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.

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

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

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
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 200'den fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

