Softivation

Drone ilk hedefi atlıyor (Python ile ArduPilot GUIDED)

Drone ilk hedefi atlıyor ve doğrudan ikinci noktaya gidiyorsa Python kodun varışı beklemiyordur. ArduPilot GUIDED'de varışı ölçen kodu kopyala, çalıştır.

Furkan IŞIKAY 7 dk okuma

Python'dan drone'a iki nokta gönderiyorsun: önce kuzeydeki köşe, sonra doğudaki hedef. Drone havalanıyor, birinci noktaya hiç uğramadan çapraz bir çizgiyle ikinciye gidiyor. Kodda hata mesajı yok, iki komut da gitmiş görünüyor.

Bazı takımlar bunu fark edince iki komutun arasına otopilotun "vardım" mesajını bekleyen bir satır koyuyor ve bu kez kod birinci noktada sonsuza kadar duruyor. İki belirtinin kökü aynı: kod, drone'un hedefe ne zaman vardığını bilmiyor.

Drone neden birinci noktaya uğramıyor

GUIDED modda gönderdiğin her konum hedefi bir öncekinin üstüne yazılır. Otopilotta bir hedef kuyruğu yoktur; Python satırı bitirip bir sonrakine geçtiğinde drone hâlâ kalkış noktasının yakınındadır ve yeni hedef eskisini anında siler.

Aşağıdaki şemada iki uçuşun üstten izi var. Kırmızı izde iki hedef art arda gönderilmiş; drone 1. hedefe en fazla 27 metre yaklaşıyor ve doğrudan 2. hedefe dönüyor. Mavi izde kod birinci noktaya varışı beklemiş, drone köşeyi dönüp ikinciye geçmiş.

İkinci hedef birinciyi siliyor: iki uçuşun izi

Art arda gönderilen iki hedefte drone birinci hedefe 27 metreden fazla yaklaşmıyor
Kırmızı izin 1. hedefe en çok yaklaştığı yere bak.

Yük bırakma görevinde drone'u önce hedefin üstüne gönderip sonra servoyu açan bir kod yazdığını düşün. Varışı beklemezsen Python servo satırına hemen geçer, drone ise o sırada hâlâ yoldadır ve yük hedefin çok gerisinde düşer.

Otopilot vardığını söylemiyor mu

Varsayılan ayarlarda söylemez. ArduCopter 4.7'de GUID_OPTIONS parametresi 0 iken konum hedefi doğrudan konum kontrolcüsüne gider ve varışta MISSION_ITEM_REACHED mesajı gönderilmez. Bu mesajı bekleyen kod bu yüzden takılır.

Mission Planner'ın gösterdiği hedef mesafesi de NAV_CONTROLLER_OUTPUT mesajındaki wp_dist alanından gelir ve tam sayı metredir. Drone hedefe 0,7 metre uzaktayken bu alan 0 yazar; 1 metreden küçük bir eşiği bu sayıyla kontrol edemezsin.

En sağlam yol, kodun drone'un konumunu kendisinin okuyup hedefe olan mesafeyi hesaplaması. Konum zaten her saniye birkaç kez GLOBAL_POSITION_INT ile geliyor; eksik olan tek şey bu sayıyı bir eşikle karşılaştıran döngü.

En sık yapılan hata: hedef komutlarını art arda yazıp drone'un sırayla gideceğini sanmak. DroneKit'teki simple_goto da aynı şekilde hemen döner; arkasından gelen her komut önceki hedefi iptal eder.

Varışı kod kendisi ölçsün

Aşağıdaki program drone'u kaldırır, 40 metre kuzeye ve sonra 40 metre doğuya gönderir. Her hedefin ardından varis_bekle fonksiyonu yatay mesafe 1 metrenin, irtifa farkı 0,5 metrenin altına inene kadar sonraki satıra geçmez.

Pythonhedef_bekle.py
import math
import time
from pymavlink import mavutil

BAGLANTI = "tcp:127.0.0.1:5760"   # Pi UART: "/dev/serial0"
BAUD = 57600                       # Pixhawk tarafindaki SERIALx_BAUD ile ayni
ESIK_M = 1.0                       # yatay varis esigi (metre)
IRTIFA_ESIK_M = 0.5

m = mavutil.mavlink_connection(BAGLANTI, baud=BAUD, source_system=255)
m.wait_heartbeat()
m.mav.request_data_stream_send(m.target_system, m.target_component,
                               mavutil.mavlink.MAV_DATA_STREAM_ALL, 10, 1)


def mesafe_m(lat1, lon1, lat2, lon2):
    r = 6371000.0
    p1, p2 = math.radians(lat1), math.radians(lat2)
    a = (math.sin((p2 - p1) / 2) ** 2 +
         math.cos(p1) * math.cos(p2) * math.sin(math.radians(lon2 - lon1) / 2) ** 2)
    return 2 * r * math.asin(math.sqrt(a))


def hedef_gonder(lat, lon, irtifa):
    m.mav.set_position_target_global_int_send(
        0, m.target_system, m.target_component,
        mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,
        0b110111111000,                 # yalnizca konum
        int(lat * 1e7), int(lon * 1e7), irtifa,
        0, 0, 0, 0, 0, 0, 0, 0)


def varis_bekle(lat, lon, irtifa, zaman_asimi=60):
    baslangic = time.time()
    while time.time() - baslangic < zaman_asimi:
        if m.flightmode != "GUIDED":
            print("Mod degisti:", m.flightmode, "- bekleme iptal")
            return False
        p = m.recv_match(type="GLOBAL_POSITION_INT", blocking=True, timeout=2)
        if p is None:
            continue
        yatay = mesafe_m(p.lat / 1e7, p.lon / 1e7, lat, lon)
        dikey = abs(p.relative_alt / 1000 - irtifa)
        if yatay < ESIK_M and dikey < IRTIFA_ESIK_M:
            print(f"Varildi: yatay {yatay:.2f} m, dikey {dikey:.2f} m, t={p.time_boot_ms/1000:.1f} s")
            return True
    print("Zaman asimi: hedefe varilamadi")
    return False


def kuzey_dogu(lat0, lon0, kuzey_m, dogu_m):
    dlat = kuzey_m / 6378137.0
    dlon = dogu_m / (6378137.0 * math.cos(math.radians(lat0)))
    return lat0 + math.degrees(dlat), lon0 + math.degrees(dlon)


# GPS, GUIDED, ARM, kalkis
while True:
    g = m.recv_match(type="GPS_RAW_INT", blocking=True)
    if g.fix_type >= 3 and g.satellites_visible >= 6:
        break
m.set_mode("GUIDED")
for deneme in range(5):            # ARM ilk denemede reddedilebilir
    m.arducopter_arm()
    m.recv_match(type="HEARTBEAT", blocking=True, timeout=3)
    if m.motors_armed():
        break
    time.sleep(8)
m.mav.command_long_send(m.target_system, m.target_component,
                        mavutil.mavlink.MAV_CMD_NAV_TAKEOFF, 0, 0, 0, 0, 0, 0, 0, 10)
ev = m.recv_match(type="GLOBAL_POSITION_INT", blocking=True)
lat0, lon0 = ev.lat / 1e7, ev.lon / 1e7
while m.recv_match(type="GLOBAL_POSITION_INT", blocking=True).relative_alt < 9500:
    pass
print("Kalkis tamam")

# iki nokta: once 40 m kuzey, sonra 40 m dogu
for kuzey, dogu in ((40, 0), (40, 40)):
    lat, lon = kuzey_dogu(lat0, lon0, kuzey, dogu)
    hedef_gonder(lat, lon, 10)
    if not varis_bekle(lat, lon, 10):
        break

m.set_mode("LAND")
print("LAND")
  • Gir: BAGLANTI satırına kendi hattını yaz. Raspberry Pi'nin UART'ında "/dev/serial0" ve Pixhawk'taki SERIALx_BAUD ile aynı baud.
  • Gör: Her noktada bir "Varildi" satırı. Satırdaki yatay değer eşiğin altında olmalı.
  • Değilse: 60 saniyede "Zaman asimi" yazıyorsa eşik drone'un havada tutabildiği konumdan dar. Önce ESIK_M'yi 1,5 ya da 2 yap, sonra GPS'in yatay doğruluğuna bak.

Fonksiyon her turda modu da kontrol eder. Pilot kumandadan LOITER'a ya da RTL'e alırsa bekleme biter ve kod bir sonraki hedefi göndermez; böylece pilotun aldığı kontrolü Python geri almaz.

Ne göreceksin

Programı çalıştırıp drone iki noktayı da tamamlarsa terminalde şunu görürsün:

Çıktı
Kalkis tamam
Varildi: yatay 0.94 m, dikey 0.02 m, t=4716.3 s
Varildi: yatay 0.71 m, dikey 0.01 m, t=4724.0 s
LAND

t değeri otopilotun açılıştan beri saydığı süredir. İki satır arasındaki fark, 40 metrelik ikinci bacağın kaç saniye sürdüğünü verir; bu örnekte yaklaşık 8 saniye.

Hata mesajları ve anlamları

Mode change to Guided failed: requires position

Otopilot konum kestirimi hazır değilken GUIDED'e geçmeyi reddetti. Kod o zaman başka bir modda kalır ve varis_bekle ilk turda "Mod degisti" yazıp çıkar. Programdaki GPS döngüsü bu yüzden 3D fix ve en az 6 uydu görmeden GUIDED komutuna geçmez.

Zaman asimi: hedefe varilamadi

Drone 60 saniye içinde eşiğin içine girmedi. Ya eşik dar, ya hedef çok uzak, ya da drone rüzgârda konumunu eşik kadar sıkı tutamıyor. Hedefe olan son mesafeyi yazdırıp eşikle karşılaştır.

Mod degisti: LAND - bekleme iptal

Beklerken mod GUIDED'den çıktı: pilot müdahale etti ya da bir failsafe devreye girdi. Kod bilerek durur; sebebini Mission Planner'ın Messages sekmesinde ara.

Python ile otonom kontrolü baştan sona öğren

Varışın otopilot tarafında nasıl karar verildiğini, MISSION_ITEM_REACHED'in hangi ayarla geldiğini ve logda hangi alan doldu diye bakacağını Python ile Sıfırdan İHA Kontrolü eğitimindeki derste adım adım anlatıyoruz.

Bu yazı işinize yaradı mı?

Ücretsiz kaynakArduPilot SITL kurulum rehberiKodunuzu sahaya çıkmadan SITL'de deneyin: kurulum ve bağlantı baştan sona.