Softivation

Raspberry Pi'de Pixhawk GPS koordinatları yanlış geliyor

Raspberry Pi'de Pixhawk GPS koordinatları yanlış geliyorsa veri bozuk değil: enlem 10 milyonla çarpılmış, irtifa milimetre. Doğru çeviri ve iki tuzak.

Furkan IŞIKAY 7 dk okuma

Pi'yi Pixhawk'a bağladın, konumu okuyup yer istasyonuna gönderiyorsun. Ekranda enlem 371591001, irtifa 10001, batarya 12600 diye görünüyor; haritadaki işaret ya hiç çıkmıyor ya da dünyanın öbür ucuna düşüyor.

Kablo sağlam, hat da sağlam. Sayılar doğru, yalnızca birimleri çevrilmemiş.

Sayılar neden bu kadar büyük geliyor

Pixhawk konumu yer istasyonuna ondalıklı sayı olarak yollamaz. Enlemi 10 milyonla çarpıp tam sayı yapar, irtifayı milimetreye, yönü derecenin yüzde birine çevirir. Santimetre hassasiyeti bu sayede küçük bir pakete sığar.

Mission Planner bu çarpanları bilir ve ekrana derece, metre diye basar. Pi'deki kodun ise bilmez. Mesajı olduğu gibi JSON'a döküp gönderirsen yer istasyonun ham sayıyı görür.

Drone eve göre 10 metrede beklerken otopilottan gelen değerler ve okunuşları şöyle:

Mesaj ve alanGelen değerBirimOkunacak değer
GLOBAL_POSITION_INT lat371591001derece × 10 milyon37,1591001°
GLOBAL_POSITION_INT lon387968998derece × 10 milyon38,7968998°
GLOBAL_POSITION_INT relative_alt10001milimetre10,0 m, eve göre
GLOBAL_POSITION_INT alt510080milimetre510,08 m, deniz seviyesi
GLOBAL_POSITION_INT hdg21derece × 1000,21°
SYS_STATUS voltage_battery12600milivolt12,6 V
SYS_STATUS current_battery2810amper × 10028,1 A
VFR_HUD alt510.08metre510,08 m, deniz seviyesi

Son satırdaki VFR_HUD irtifası zaten metre olarak gelir ama eve göre değil, deniz seviyesine göredir. Drone 10 metredeyken Pi'de 500'ün üstünde bir sayı görüyorsan yanlış alanı okuyorsun; eve göre irtifa için relative_alt'ı kullan.

Örnek: hedef yer istasyonundan Pi'ye geliyor

Teknofest görevinde hedefi yer istasyonunda haritaya tıklayarak seçtiğini düşün. Harita sana 37.1590 gibi bir derece verir, Pi de bunu otopilota iletecek. Bu kez yön ters: dereceyi 10 milyonla çarpman ve tam sayıya çevirmen gerekir.

Çarpmayı unutursan Python hata verir, çünkü paketteki enlem alanı tam sayı bekler. Çarpıp int() ile çevirmeyi atlarsan da aynı hatayı alırsın: 37.1590 * 1e7 hâlâ ondalıklı bir sayıdır.

Bölerken de konum kayabilir

Çeviriyi doğru yönde yapsan da iki tuzak kalır. Python'da // ile bölersen kesir atılır ve 371591001 // 10000000 sonucu 37 olur. Yer istasyonunda enlemi 32 bitlik float bir değişkende tutarsan sayı yuvarlanır.

Şekil ikisinin bedelini aynı enlem üzerinde gösteriyor. Kırmızı çubuklara bak: float32'de konum 42 santimetrelik adımlarla zıplar, tam sayı bölme ise drone'u 17,7 kilometre öteye taşır.

Enlemi saklama biçimi ve kayıp

Enlem saklama biçimine göre kaybolan mesafe
Değerler 37,159° enlemi için.

Pixhawk'tan gelen tam sayının kendi adımı 1,1 santimetredir. Python'un normal float'ı bunun çok altında kalır; / ile bölüp sonucu öyle saklarsan konumdan bir şey kaybetmezsin.

Çözüm

Birimi Pi'de çevir, yer istasyonuna hazır değer gönder. Aşağıdaki kod konumu ve bataryayı okur, her alanı kendi çarpanına böler ve tek satır JSON basar.

Pi'de bağlantı adresi /dev/serial0 olarak yazılı. Başka bir port kullanıyorsan çalıştırırken argüman olarak ver: python3 pi_konum_json.py /dev/ttyAMA0

Pythonpi_konum_json.py
import sys
import json
from pymavlink import mavutil

BAGLANTI = sys.argv[1] if len(sys.argv) > 1 else "/dev/serial0"
m = mavutil.mavlink_connection(BAGLANTI, baud=57600)
m.wait_heartbeat()
m.mav.request_data_stream_send(m.target_system, m.target_component,
                               mavutil.mavlink.MAV_DATA_STREAM_ALL, 4, 1)


def paket(gp, ss):
    return {
        "enlem": gp.lat / 1e7,                 # degE7 -> derece
        "boylam": gp.lon / 1e7,                # degE7 -> derece
        "irtifa_m": gp.relative_alt / 1000,    # mm -> m, eve gore
        "yon_derece": gp.hdg / 100,            # cdeg -> derece
        "voltaj_v": ss.voltage_battery / 1000, # mV -> V
        "akim_a": None if ss.current_battery == -1 else round(ss.current_battery / 100, 2),  # cA -> A
    }


gp = m.recv_match(type="GLOBAL_POSITION_INT", blocking=True)
ss = m.recv_match(type="SYS_STATUS", blocking=True)
print("ham:", gp.lat, gp.lon, gp.relative_alt, gp.hdg, ss.voltage_battery, ss.current_battery)
print(json.dumps(paket(gp, ss)))

Drone 10 metrede beklerken kodu çalıştırırsan şunu görürsün; senin konumun ve voltajın farklı olur ama biçim aynıdır:

Çıktı
ham: 371591001 387968998 9999 21 12600 2808
{"enlem": 37.1591001, "boylam": 38.7968998, "irtifa_m": 9.999, "yon_derece": 0.21, "voltaj_v": 12.6, "akim_a": 28.08}

akim_a null geliyorsa güç modülün akım ölçmüyor demektir. Otopilot ölçmediği akım için -1 yollar; kod bu değeri boş bırakır.

Hata Mesajları

Mesaj nesnesini doğrudan json.dumps'a verdin. msg.to_dict() ile sözlüğe çevirince hata kalkar, ama yer istasyonuna yine ham sayılar gider. Alanları tek tek çevirip yeni bir sözlük kur.

struct.error: required argument is not an integer

Hedef koordinatı otopilota derece olarak ya da çarpıp tam sayıya çevirmeden gönderdin. int(round(enlem * 1e7)) ile gönderirsen paket gider.

struct.error: 'i' format requires -2147483648 <= number <= 2147483647

Zaten 10 milyonla çarpılmış bir değeri bir kez daha çarptın. Otopilottan okuduğun lat alanını geri yolluyorsan çarpmadan gönder.

Pi ile yer istasyonu arasındaki hattı öğren

Pi'den yer istasyonuna giden paketi kurmayı, otopilotun bataryayı hangi alana nasıl yazdığını ve gönderdiğin hedefin neden eski bir konum paketiyle yanlış doğrulanabildiğini İHA Haberleşme Teknolojileri eğitiminde adım adım anlatıyoruz.

Özel Türkçe yer kontrol istasyonu kütüphanesi: C# ile yazılmış, Türkçe yorumlu bir MAVLink YKİ kütüphanesi; kendi yer istasyonunu seri port, TCP ya da UDP üzerinden araca bağlayıp kurarsın.

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.