Softivation

Python'dan Pixhawk'a görev yüklenmiyor, istek tipine bak

Raspberry Pi'deki Python kodu Pixhawk'a görev yüklerken bekliyorsa ArduPilot satırları eski istek adıyla istiyor ve senin kodun o adı hiç almıyor demektir.

Furkan IŞIKAY 8 dk okuma

Raspberry Pi'deki Python kodun rotayı Pixhawk'a göndermeye başlıyor ve ilk satırdan sonra donup kalıyor. Hata mesajı yok, imleç yanıp sönüyor. Kabloyu suçlayamazsın: heartbeat geliyor, parametreleri okuyabiliyorsun.

Kod yanlış mesajı bekliyor. Pixhawk'ın satırları hangi adla istediğini ve iki adı da karşılayan düzeltilmiş kodu aşağıda bulacaksın.

Kodun beklediği mesaj hiç gelmiyor

Görev yükleme bir soru-cevap zinciridir. Kod önce kaç satır göndereceğini söyler. Otopilot satırları sıfırdan başlayarak tek tek ister, kod her isteğe o satırla cevap verir ve son satırdan sonra otopilot bir onay mesajı döner.

MAVLink'in güncel belgesi, otopilotun satırları MISSION_REQUEST_INT ile istemesi gerektiğini yazıyor ve eski MISSION_REQUEST mesajını kullanımdan kalkmış sayıyor. İnternetteki örneklerin çoğu bu yüzden yalnızca yeni adı bekler. ArduPilot 4.7.2 ise satırları hâlâ eski adla istiyor; kaynak kodda istek gönderen fonksiyon eski mesajı çağırıyor.

Pixhawk aynı isteği tekrarlıyor, kod başka tip beklediği için hiçbirini almıyor

ArduPilot MISSION_REQUEST gönderiyor; yalnızca MISSION_REQUEST_INT bekleyen kod 8 saniye boyunca hiçbir isteği almıyor ve yükleme iptal ediliyor
Mavi oklar Pixhawk'tan gelen istekler. Kırmızı çubuğun 8. saniyede bittiği yere bak: yükleme orada iptal edildi.

Şekilde mavi oklar Pixhawk'tan çıkan istekler: hepsi sıfırıncı satırı istiyor ve yaklaşık bir saniye arayla tekrarlanıyor. Kırmızı çubuk senin kodun. İstekler ona ulaşıyor, fakat filtre başka bir ad aradığı için kodun hiçbirini almıyor ve 8. saniyede otopilot yüklemeyi iptal ediyor.

Takılan kodda recv_match çağrısı genellikle yalnızca MISSION_REQUEST_INT tipini bekler ve bir zaman sınırı taşımaz. Otopilot vazgeçtikten sonra bile kod orada beklemeye devam eder.

Yarım kalan görev eski rotayla karışıyor

Asıl risk iptalden sonra başlıyor. Otopilot yeni görevin satır sayısını alır almaz eski listeyi o uzunlukta keser, gelen satırları da tek tek üzerine yazar. Yükleme yarıda kalırsa yazılmamış satırlarda bir önceki rota durur.

Teknofest provasını düşün. Dün 20 metrede uçan beş satırlık bir rota yükledin. Bugün aynı uzunlukta, 30 metrede ve daha geniş bir rotayı Pi'den gönderiyorsun ve yükleme ikinci noktada düşüyor. Geri okuduğunda kalkış satırı 30 metre görünür, sonraki üç nokta ise dünkü koordinatlar ve dünkü 20 metre.

Kod onayı kontrol etmiyorsa bunu fark etmezsin. AUTO'ya geçtiğinde drone bu karışık rotayı uçar. Onay mesajını görmeden ve rotayı geri okumadan AUTO'ya geçme.

Hepsini art arda göndermek de kurtarmıyor

Bir başka yaygın deneme, istekleri hiç beklemeden beş satırı peş peşe yollamak. Paket kaybolmadıkça otopilot satırları sırayla kabul eder. Telemetri radyosunda tek bir paket düşerse otopilot sonraki satırları sıra dışı sayıp atar ve eksik satırı her saniye yeniden ister. Kod artık dinlemediği için 8. saniyede yine iptal gelir.

Kalıcı çözüm, kodu isteklere göre çalıştırmak: otopilot hangi satırı isterse onu göndermek ve iki istek adını da kabul etmek.

Düzeltilmiş kod

Aşağıdaki kod iki istek tipine de cevap veriyor, sıra dışı satır uyarısında beklemeye devam ediyor ve son onayın sonucunu okuyor. Koordinatları tam sayı biçimiyle (MISSION_ITEM_INT) gönderdiği için ondalık kayıp olmuyor. Onaydan sonra rotayı geri okuyup her satırı senin listenle karşılaştırıyor.

Python
import time
from pymavlink import mavutil

# Raspberry Pi'de seri hat için ör.: mavutil.mavlink_connection("/dev/serial0", baud=921600, source_system=255)
m = mavutil.mavlink_connection("tcp:127.0.0.1:5760", source_system=255)
m.wait_heartbeat()
print("Bağlandı: sysid", m.target_system)

M = mavutil.mavlink
# (komut, enlem, boylam, irtifa). 0. satır Home'un yeri, otopilot onu kendi konumuyla doldurur.
GOREV = [
    (M.MAV_CMD_NAV_WAYPOINT, 0, 0, 0),
    (M.MAV_CMD_NAV_TAKEOFF, 0, 0, 20),
    (M.MAV_CMD_NAV_WAYPOINT, 37.1600, 38.7969, 20),
    (M.MAV_CMD_NAV_WAYPOINT, 37.1600, 38.7985, 20),
    (M.MAV_CMD_NAV_WAYPOINT, 37.1591, 38.7985, 20),
]


def oge_gonder(seq):
    cmd, lat, lon, alt = GOREV[seq]
    frame = M.MAV_FRAME_GLOBAL if seq == 0 else M.MAV_FRAME_GLOBAL_RELATIVE_ALT
    m.mav.mission_item_int_send(
        m.target_system, m.target_component, seq, frame, cmd,
        0, 1, 0, 0, 0, 0,
        round(lat * 1e7), round(lon * 1e7), alt,
        M.MAV_MISSION_TYPE_MISSION)


def gorev_yukle(bekleme=10):
    m.mav.mission_count_send(m.target_system, m.target_component,
                             len(GOREV), M.MAV_MISSION_TYPE_MISSION)
    son_istek = time.time()
    while time.time() - son_istek < bekleme:
        msg = m.recv_match(type=["MISSION_REQUEST", "MISSION_REQUEST_INT", "MISSION_ACK"],
                           blocking=True, timeout=1)
        if msg is None or msg.mission_type != M.MAV_MISSION_TYPE_MISSION:
            continue
        if msg.get_type() == "MISSION_ACK":
            if msg.type == M.MAV_MISSION_INVALID_SEQUENCE:
                continue          # sıra dışı öğe; otopilot eksik olanı yeniden isteyecek
            return msg.type
        print("İstendi:", msg.get_type(), "seq", msg.seq)
        oge_gonder(msg.seq)
        son_istek = time.time()
    return None


def gorev_oku():
    m.mav.mission_request_list_send(m.target_system, m.target_component, M.MAV_MISSION_TYPE_MISSION)
    adet = m.recv_match(type="MISSION_COUNT", blocking=True, timeout=5).count
    satirlar = []
    for seq in range(adet):
        m.mav.mission_request_int_send(m.target_system, m.target_component, seq, M.MAV_MISSION_TYPE_MISSION)
        o = m.recv_match(type="MISSION_ITEM_INT", blocking=True, timeout=3)
        satirlar.append((o.seq, o.command, o.x, o.y, o.z))
    m.mav.mission_ack_send(m.target_system, m.target_component, M.MAV_MISSION_ACCEPTED, M.MAV_MISSION_TYPE_MISSION)
    return satirlar


sonuc = gorev_yukle()
if sonuc is None:
    print("Otopilot yanıt vermedi: hattı ve sysid'i kontrol et")
elif sonuc != M.MAV_MISSION_ACCEPTED:
    print("Görev reddedildi:", M.enums["MAV_MISSION_RESULT"][sonuc].name)
else:
    print("Görev kabul edildi")
    for seq, cmd, x, y, z in gorev_oku()[1:]:
        _, lat, lon, alt = GOREV[seq]
        tamam = (x, y) == (round(lat * 1e7), round(lon * 1e7)) and z == alt
        print("seq", seq, "komut", cmd, "eşleşiyor" if tamam else "FARKLI")

Pi'de seri hattan bağlanıyorsan bağlantı satırını yorumdaki gibi kendi portun ve hızınla değiştir. Listenin sıfırıncı satırı Home'un yeridir; otopilot onu kendi konumuyla doldurur, bu yüzden karşılaştırma birinci satırdan başlar.

Ne göreceksin

Kodu çalıştırınca Pixhawk her satırı ayrı ayrı ister, onay gelir ve geri okunan satırlar listenle eşleşir:

Kod
Bağlandı: sysid 1
İstendi: MISSION_REQUEST seq 0
İstendi: MISSION_REQUEST seq 1
İstendi: MISSION_REQUEST seq 2
İstendi: MISSION_REQUEST seq 3
İstendi: MISSION_REQUEST seq 4
Görev kabul edildi
seq 1 komut 22 eşleşiyor
seq 2 komut 16 eşleşiyor
seq 3 komut 16 eşleşiyor
seq 4 komut 16 eşleşiyor

İsteklerin MISSION_REQUEST adıyla geldiğine dikkat et; düzeltme tam olarak bu satırları yakalamak için. Bir satırda FARKLI görürsen o satır yazılmamış ya da eski rotadan kalmıştır, AUTO'ya geçmeden yüklemeyi tekrarla.

Hata mesajları

Mission upload timeout

Otopilot 8 saniye boyunca beklediği satırı alamadı ve yüklemeyi iptal etti. Bu mesaj yalnızca görevi yükleyen bağlantıya gider; Mission Planner ayrı bir radyodan bağlıysa onun ekranında görünmeyebilir. Kodun STATUSTEXT mesajlarını okursa burada görürsün.

got MISSION_ITEM; GCS should send MISSION_ITEM_INT

Kod satırları ondalıklı eski mesajla gönderiyor. Yükleme yine kabul edilir ama koordinatlar 32 bit ondalığa yuvarlanır. Gönderimi mission_item_int_send ile yap, enlem ve boylamı 10.000.000 ile çarpıp tam sayıya yuvarla.

Flight plan received

Başarı mesajı: otopilot son satırı kabul etti. Kodunda bu noktada gelen onayın sonucu 0 (ACCEPTED) olmalı.

Kursta devamı

Python ile görev yükleme, GUIDED ve AUTO komutları ve yardımcı bilgisayardan kontrol adım adım Python ile Sıfırdan İHA Kontrolü eğitiminde. Yeni eklenen ders aynı yüklemeyi otopilotun kaynak kodundan ve uçuş kaydından izliyor; yarım kalan görevi temizleyip yeniden yükleyen kod da orada.

Bu yazı işinize yaradı mı?

Ücretsiz kaynakTEKNOFEST İHA yazılım hazırlık kontrol listesiTakımınızın yazılım hazırlığını aşama aşama işaretleyin; yazdırılabilir liste.