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.
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 alan | Gelen değer | Birim | Okunacak değer |
|---|---|---|---|
| GLOBAL_POSITION_INT lat | 371591001 | derece × 10 milyon | 37,1591001° |
| GLOBAL_POSITION_INT lon | 387968998 | derece × 10 milyon | 38,7968998° |
| GLOBAL_POSITION_INT relative_alt | 10001 | milimetre | 10,0 m, eve göre |
| GLOBAL_POSITION_INT alt | 510080 | milimetre | 510,08 m, deniz seviyesi |
| GLOBAL_POSITION_INT hdg | 21 | derece × 100 | 0,21° |
| SYS_STATUS voltage_battery | 12600 | milivolt | 12,6 V |
| SYS_STATUS current_battery | 2810 | amper × 100 | 28,1 A |
| VFR_HUD alt | 510.08 | metre | 510,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
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
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:
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ı
TypeError: Object of type MAVLink_global_position_int_message is not JSON serializable
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ı?
Bu konuyu baştan sona öğrenin
Sezon: altı eğitimin hepsi 9 ay, ayda ₺790. Lise ve üniversite öğrencisine ayda ₺390, üye kodunuzla ilk ay ₺351. İstediğiniz ay bırakırsınız. Sezon'a başlayın · Öğrenciyim