Bir drone'un havada asılı kaldığını izlediğinizde, arka planda ne döndüğünü hiç düşündünüz mü?
Gördüğünüz şey; dört dönen motor, bir plastik gövde ve belki güzel bir kamera. Ama aslında orada saniyede binlerce kez çalışan bir kontrol döngüsü var. Her titreşimi hisseden sensörler, her sapışı düzelten matematiksel algoritmalar ve her motoru milisaniye hassasiyetiyle kontrol eden PWM sinyalleri var.
Drone'lar, gömülü yazılımın en görünür ve en heyecan verici uygulamalarından biridir. Bu yazıda, bir quadcopter'ın nasıl uçtuğunu — donanımdan sensör füzyonuna, PID kontrolünden ESC çıkışına kadar — bir firmware geliştiricisinin gözünden inceliyoruz.
Bir quadcopter'ın çalışmasını anlamak için önce parçalarını tanıyalım:
| Bileşen | Görev |
|---|---|
| Brushless DC Motor (BLDC) | İtme kuvveti üretir — drone'un "kası" |
| ESC (Electronic Speed Controller) | Motoru sürer; MCU'dan PWM alır, üç fazlı sinyal üretir |
| Flight Controller (FC) | Tüm hesapların yapıldığı MCU kartı (STM32 tabanlılar yaygın) |
| IMU (Inertial Measurement Unit) | Jiroskop + ivmeölçer — "denge hissi" |
| Alıcı (Receiver / RX) | Pilot kumanda sinyallerini FC'ye iletir |
| LiPo Pil | Enerji kaynağı |
Bir quadcopter'da dört motor vardır ve her motor ayrı bir ESC tarafından sürülür. Flight controller bu dört ESC'yi bağımsız olarak kontrol ederek droneun yönünü ve yüksekliğini belirler.
Quadcopter'ın hareket mekaniği basit bir fikirden doğar: farklı motorlar farklı hızda döndürülürse, araç döner veya eğilir.
Motorların ikisi saat yönünde (CW), ikisi saat yönünün tersinde (CCW) döner. Bu düzenleme birbiriyle iptal olan torklara yol açar — eğer hepsi aynı hızda dönerse drone kendi ekseni etrafında dönmez.
M1 (CCW) ← → M2 (CW)
↑ [FC] ↑
M3 (CW) ← → M4 (CCW)
Dört temel hareket:
| Hareket | Nasıl Yapılır? |
|---|---|
| Throttle (Yükseklik) | Tüm motorlar eşit hızlandırılır / yavaşlatılır |
| Pitch (İleri-geri) | Ön motorlar yavaşlar, arka motorlar hızlanır (veya tersi) |
| Roll (Sağa-sola yatma) | Sol motorlar yavaşlar, sağ motorlar hızlanır (veya tersi) |
| Yaw (Eksen dönüşü) | CW motorlar hızlanır, CCW motorlar yavaşlar (veya tersi) |
Bütün bu hareketler gerçekte aynı anda gerçekleşir. Flight controller her kareyi ayrı ayrı hesaplar ve dört motora ait hız değerlerini harmanlayarak anlık komutlar üretir.
{component:DroneSimulator}
Bir drone'un firmware'i üç katmandan oluşur:
{
"title": "Drone Firmware Katmanları",
"subtitle": "Saniyede 1000 kez çalışan kontrol döngüsü",
"layout": "vertical",
"nodes": [
{ "id": "input", "label": "Girişler", "sub": "IMU + Pilot Kumanda (RX)", "color": "gray" },
{ "id": "fusion", "label": "Sensör Füzyonu", "sub": "Complementary / Madgwick filtresi", "color": "blue" },
{ "id": "pid", "label": "PID Kontrolü", "sub": "Roll, Pitch, Yaw eksenlerinde", "color": "violet" },
{ "id": "mixer", "label": "Motor Karıştırma","sub": "Mixer → 4 motor güç değeri", "color": "amber" },
{ "id": "pwm", "label": "PWM Çıkışı", "sub": "ESC → BLDC Motor", "color": "green" }
]
}
Bu yığın saniyede 1000 kez (1 kHz) çalışır. Her döngüde:
IMU, jiroskop ve ivmeölçer olmak üzere iki sensörü bir arada barındırır.
Her iki sensörün de zayıf yönleri vardır:
| Sensör | Güçlü Yön | Zayıf Yön |
|---|---|---|
| Jiroskop | Anlık değişimi iyi ölçer | Zamanla kayar (drift) |
| İvmeölçer | Uzun vadede doğru | Titreşimde gürültülü |
Bu yüzden ikisi birlikte kullanılır. Tamamlayıcı filtre (complementary filter) en basit yöntemdir:
#define ALPHA 0.98f
float angle = ALPHA * (angle + gyro_rate * dt) + (1.0f - ALPHA) * accel_angle;
Bu tek satır; jiroskopun anlık hassasiyetini (%98) ve ivmeölçerin uzun vadeli doğruluğunu (%2) birleştirir. Gerçek sistemlerde Madgwick veya Mahony filtreleri kullanılır — ancak fikir aynıdır.
Sensör füzyonundan çıkan açı değerini elimizde tuttuğumuzu varsayalım. Peki pilotun istediği açı ile mevcut açı arasındaki farkı nasıl düzeltiriz?
Cevap: PID (Proportional-Integral-Derivative) kontrol.
typedef struct {
float kp, ki, kd;
float integral;
float prev_error;
} PID;
float pid_compute(PID *pid, float setpoint, float measurement, float dt) {
float error = setpoint - measurement;
pid->integral += error * dt;
float derivative = (error - pid->prev_error) / dt;
pid->prev_error = error;
return pid->kp * error
+ pid->ki * pid->integral
+ pid->kd * derivative;
}
Üç terim ne anlama gelir?
Bir flight controller'da her eksen (roll, pitch, yaw) için ayrı bir PID hesaplanır. Bu üç PID çıkışı ardından mixer'a beslenir.
Mixer, üç PID çıkışını ve throttle değerini alarak her motora ait güç değerini hesaplar:
float motor1 = throttle + pitch_out - roll_out - yaw_out;
float motor2 = throttle + pitch_out + roll_out + yaw_out;
float motor3 = throttle - pitch_out - roll_out + yaw_out;
float motor4 = throttle - pitch_out + roll_out - yaw_out;
// Kırpma: motor PWM 1000–2000 µs aralığında kalmalı
motor1 = clamp(motor1, 1000.0f, 2000.0f);
Her motor değeri bir PWM sinyaline dönüştürülerek ESC'ye gönderilir. ESC bu sinyali üç fazlı elektrik akımına çevirerek BLDC motoru döndürür.
ESC'ler klasik olarak 1000–2000 µs aralığındaki PWM sinyaliyle kontrol edilir:
| PWM Genişliği | Motor Durumu |
|---|---|
| 1000 µs | Minimum (rölanti veya dur) |
| 1500 µs | Orta hız |
| 2000 µs | Maksimum hız |
STM32 üzerinde bir timer kanalından 50 Hz frekansında (20 ms periyot) bu sinyali üretmek standart yaklaşımdır. Daha modern sistemlerde DSHOT protokolü kullanılır — dijital ve çok daha hızlı.
// TIM1 CH1 için CCR değeri ayarla (PSC ve ARR 50Hz'e göre yapılandırılmış)
TIM1->CCR1 = pwm_us_to_ticks(motor1_us);
Bir drone'un kontrol döngüsünün saniyede 1000 kez çalışması rastlantı değildir.
Eğer döngü yavaşlarsa: sensör verileri eskir, PID hesaplamaları yanlış açı üzerinde çalışır ve motorlar geç tepki verir. Rüzgar gibi anlık bozucu etkilerde bu gecikme drone'u devirmek için yeterlidir.
Bu yüzden flight controller firmware'leri gerçek zamanlı çalışma gerektirir:
HAL_GetTick() veya bir timer ile her iterasyon tam 1 ms sürer{
"title": "Kontrol Döngüsü Özeti (1 kHz)",
"subtitle": "Her döngüde beş aşama sırayla çalışır",
"layout": "vertical",
"nodes": [
{ "id": "imu", "label": "IMU", "sub": "Jiroskop + İvmeölçer ham verisi", "color": "gray" },
{ "id": "fusion", "label": "Sensör Füzyonu", "sub": "Roll / Pitch / Yaw açıları", "color": "blue" },
{ "id": "pid", "label": "PID × 3 Eksen", "sub": "Düzeltme çıkışları", "color": "violet" },
{ "id": "mixer", "label": "Mixer", "sub": "Motor1..4 güç değerleri", "color": "amber" },
{ "id": "pwm", "label": "PWM → ESC", "sub": "BLDC Motor kontrolü", "color": "green" }
],
"edges": [
{ "from": "imu", "to": "fusion", "label": "ham veri" },
{ "from": "fusion", "to": "pid", "label": "açı" },
{ "from": "pid", "to": "mixer", "label": "düzeltme" },
{ "from": "mixer", "to": "pwm", "label": "% güç" }
]
}
| Kavram | Özet |
|---|---|
| IMU | Jiroskop + ivmeölçer; drift ve gürültü tamamlayıcı filtre ile dengelenir |
| Complementary Filter | angle = 0.98 * (gyro) + 0.02 * (accel) |
| PID | P hatayı, I birikimi, D salınımı kontrol eder |
| Mixer | Throttle + 3 PID çıkışı → 4 motor değeri |
| PWM / ESC | 1000–2000 µs genişliğindeki sinyal motor hızını belirler |
| Döngü Hızı | 1 kHz — gecikme drone'u devirir |
Gömülü sistemler bazen soyut gelebilir. Ama bu kontrol döngüsünü yazdığınızda ve drone havaya kalktığında, kodunuzun havada döndüğünü görürsünüz. Bu, bare-metal programlamanın en somut ödüllerinden biridir.
Kitap serisini incele →