Gömülü Sistemler··7 dk okuma
Companion Computer Nedir? MAVLink ile Uçuş Kontrolcüsüne Bağlanmak
Tespit modeliniz hedefi buldu — peki bu bilgiyi uçuş kontrolcüsüne nasıl söyleyeceksiniz? UART kablosundan GUIDED mod komutuna kadar tüm zincir.
Jetson üzerinde YOLO modeliniz çalışıyor, kameradan gelen karede hedefi 15-20 FPS ile buluyorsunuz. Sonra şu soruyla karşılaşıyorsunuz: bu tespiti uçuş kontrolcüsüne nasıl bildireceğiz? Aracın o koordinata gitmesini, orada bekleyip yükü bırakmasını kim söyleyecek?
Bizim ilk refleksimiz “her şeyi uçuş kontrolcüsüne yükleyelim” olmuştu. Pixhawk sınıfı bir kartın üzerindeki mikrodenetleyici saniyede yüzlerce kez IMU okuyup motor çıkışı hesaplamakla meşgul. Oraya bir sinir ağı koymak mümkün değil; koysanız bile kontrol döngüsünü bozarsınız.
Çözüm ikiye bölmek: gerçek zamanlı kontrol uçuş kontrolcüsünde (FC — flight controller) kalır, ağır hesaplama yanına takılan ikinci bir bilgisayara taşınır. Bu ikinci bilgisayara companion computer (eşlikçi bilgisayar) deniyor; ArduPilot bunu MAVLink protokolüyle FC’ye bağlanıp uçuş sırasında karar veren bir birim olarak tanımlıyor. Bu yazıda o iki kutuyu bağlayan kabloyu, protokolü ve çalışan kodu anlatıyoruz.
Görev bölüşümü neden bu şekilde?
Uçuş kontrolcüsünün işi katı gerçek zamanlıdır: stabilizasyon döngüsü kaçırılmaz, işletim sistemi bile yoktur (ArduPilot ChibiOS üzerinde koşar). Companion computer’ın işi ise “yumuşak” gerçek zamanlıdır — bir kareyi geç işlemek görevi geciktirir ama aracı düşürmez. Bu ayrım aynı zamanda bir güvenlik sınırıdır: companion çökse bile FC uçmaya devam eder, failsafe devreye girer ve araç RTL (return to launch — kalkış noktasına dönüş) yapar. Tersi doğru değildir.
Tipik companion görevleri: nesne tespiti, gimbal kontrolü, harita üretimi, yer istasyonuna video aktarımı, görev mantığının üst seviyesi. Bizim SUAS 2026 aracımızda Jetson Orin NX 16GB bu rolde: SIYI A8 mini kameradan RTSP akışını alıyor, YOLO11m ile tespit yapıyor ve sonucu MAVLink üzerinden FC’ye konum komutu olarak çeviriyor.
Fiziksel bağlantı: UART, USB, Ethernet
Üç seçenek var ve çoğu takım için doğru cevap birincisi.
| Yöntem | Ayar | Ne zaman tercih edilir |
|---|---|---|
| UART (TELEM1/TELEM2) | SERIAL2_PROTOCOL=2, SERIAL2_BAUD=921 |
Varsayılan seçim. Sadece 3 kablo (TX, RX, GND), FC yeniden başlasa da bağlantı adı değişmez |
| USB (SERIAL0) | Kabloyu tak, /dev/ttyACM0 |
Tezgah üstü test ve parametre yazma. Uçuşta kullanmayın: konnektör titreşimde çıkar, port adı numaralanma sırasına göre kayar |
| Ethernet | NET_ENABLE=1 + NET_P1_* parametreleri |
ArduPilot 4.5 ve üzeri, Ethernet MAC’i olan kartlarda (Pixhawk6X, CubeRed). ROS 2/DDS ve yüksek veri hacmi için |
ArduPilot’ta seri portlar SERIAL0 = USB, SERIAL1 = TELEM1, SERIAL2 = TELEM2, SERIAL3 = GPS şeklinde eşlenir. SERIALx_PROTOCOL değeri 1 ise MAVLink1, 2 ise MAVLink2 konuşulur — companion için 2 kullanın. SERIALx_BAUD parametresi baud’un binde biridir: 57=57600, 115=115200, 921=921600.
Kablolama çaprazdır: FC TX → companion RX, FC RX → companion TX, GND → GND. Pixhawk UART’ları 3.3 V mantık seviyesindedir; 5 V TTL bir kartı doğrudan bağlamayın ve FC’yi companion’ın USB’sinden beslemeyin.
Linux tarafında portun seri konsol olarak kullanılmadığından emin olun. Raspberry Pi’de raspi-config içinden “login shell over serial” kapatılır, donanım UART’ı açık bırakılır; port /dev/serial0 olur. Jetson’da genellikle /dev/ttyTHS1’dir ve nvgetty servisinin kapalı olması gerekir.
MAVLink’i üç kavramda anlamak
Mesaj. MAVLink paket tabanlıdır. Her paket bir System ID, Component ID, Message ID ve veri alanları taşır; MAVLink1’de en fazla 263, MAVLink2’de 280 bayttır. Araç varsayılan olarak system ID 1’dir (MAV_SYSID), yer istasyonları geleneksel olarak 255 kullanır.
Heartbeat. Bağlantı açıldığında her taraf 1 Hz hızında HEARTBEAT (mesaj ID 0) yollar. Bu mesaj hem “hayattayım” sinyalidir hem de aracın uçuş modunu (custom_mode) ve arm durumunu taşır. Kod yazarken ilk yapacağınız şey heartbeat beklemektir, çünkü hedef sistem numarasını oradan öğrenirsiniz.
Stream rate. FC her mesajı kendiliğinden yollamaz; hangisini kaç Hz istediğinizi söylemeniz gerekir. Eski yöntem REQUEST_DATA_STREAM ile mesaj gruplarını istemektir. ArduPilot 4.0 ve üzerinde tercih edilen yöntem MAV_CMD_SET_MESSAGE_INTERVAL (komut ID 511) ile tek tek mesaj istemektir — aralık mikrosaniye cinsindendir, 10 Hz için 100000 yazarsınız.
Sık kullanılan mesajlar ve birimleri:
| Mesaj | ID | İçerik | Birim tuzağı |
|---|---|---|---|
HEARTBEAT |
0 | Mod, arm durumu | custom_mode araç tipine özgüdür |
GLOBAL_POSITION_INT |
33 | Konum, irtifa, hız | lat/lon degE7, irtifa mm, hız cm/s |
ATTITUDE |
30 | roll/pitch/yaw | radyan, derece değil |
SYS_STATUS |
1 | Batarya, sensör sağlığı | gerilim mV, akım cA |
pymavlink ile bağlanmak ve telemetri okumak
pip install pymavlink ile kurulur. Aşağıdaki blok tek başına çalışır; UDP satırını yorumdan çıkarırsanız SITL simülasyonuna da bağlanır.
import time
from pymavlink import mavutil
# Gerçek donanım: Jetson UART'ı üzerinden TELEM2'ye
master = mavutil.mavlink_connection('/dev/ttyTHS1', baud=921600)
# SITL / mavlink-router çıkışı için alternatif:
# master = mavutil.mavlink_connection('udpin:0.0.0.0:14550')
# İlk HEARTBEAT gelene kadar bekle. target_system/target_component burada dolar.
master.wait_heartbeat()
print(f"Bağlandı -> system: {master.target_system}, "
f"component: {master.target_component}")
def set_message_rate(msg_id, hz):
"""Tek bir mesajın gönderim hızını ayarla (ArduPilot 4.0+)."""
master.mav.command_long_send(
master.target_system, master.target_component,
mavutil.mavlink.MAV_CMD_SET_MESSAGE_INTERVAL, # 511
0, # confirmation
msg_id, # param1: mesaj ID
int(1_000_000 / hz), # param2: aralık, MİKROSANİYE
0, 0, 0, 0, 0)
set_message_rate(mavutil.mavlink.MAVLINK_MSG_ID_GLOBAL_POSITION_INT, 10)
set_message_rate(mavutil.mavlink.MAVLINK_MSG_ID_ATTITUDE, 20)
t_end = time.time() + 10
while time.time() < t_end:
msg = master.recv_match(type='GLOBAL_POSITION_INT',
blocking=True, timeout=2)
if msg is None:
print("Telemetri gelmiyor: baud/port/PROTOCOL ayarlarını kontrol et")
continue
print(f"lat={msg.lat / 1e7:.7f} lon={msg.lon / 1e7:.7f} "
f"rel_alt={msg.relative_alt / 1000.0:.1f} m "
f"vz={msg.vz / 100.0:+.2f} m/s")
recv_match bloklayıcıdır; ana görev döngünüzü bloklamak istemiyorsanız telemetriyi ayrı bir thread’de okuyup paylaşılan bir sözlüğe yazın. Biz Jetson’da tam olarak bunu yapıyoruz: tespit döngüsü ile MAVLink döngüsü ayrı thread’lerde koşuyor.
Komut göndermek: mod değiştirme ve GUIDED ile konuma gitme
GUIDED, aracın dışarıdan gelen konum/hız hedeflerini takip ettiği moddur. ArduPilot dokümantasyonu bu modun companion computer ve Lua betikleriyle kullanılabileceğini açıkça belirtir. Ön koşul GPS kilidi ve — dikey eksende hedef veriyorsanız — aracın havada olmasıdır.
POS_ONLY = 0b110111111000 # sadece lat/lon/alt kullan; hız, ivme, yaw yok sayılsın
def set_mode(name, timeout=5):
"""Mod değiştir ve HEARTBEAT'ten doğrula."""
mode_id = master.mode_mapping()[name] # ör. 'GUIDED'
master.mav.set_mode_send(
master.target_system,
mavutil.mavlink.MAV_MODE_FLAG_CUSTOM_MODE_ENABLED,
mode_id)
t_end = time.time() + timeout
while time.time() < t_end:
hb = master.recv_match(type='HEARTBEAT', blocking=True, timeout=1)
if hb and hb.custom_mode == mode_id:
return True
return False
def arm(timeout=10):
master.mav.command_long_send(
master.target_system, master.target_component,
mavutil.mavlink.MAV_CMD_COMPONENT_ARM_DISARM, # 400
0, 1, 0, 0, 0, 0, 0, 0) # param1=1 -> arm
ack = master.recv_match(type='COMMAND_ACK', blocking=True, timeout=timeout)
return ack is not None and ack.result == mavutil.mavlink.MAV_RESULT_ACCEPTED
def goto(lat_deg, lon_deg, rel_alt_m):
"""Kalkış noktasına göreli irtifada bir GPS hedefine git."""
master.mav.set_position_target_global_int_send(
0, # time_boot_ms (kullanılmıyor)
master.target_system, master.target_component,
mavutil.mavlink.MAV_FRAME_GLOBAL_RELATIVE_ALT_INT,
POS_ONLY,
int(lat_deg * 1e7), int(lon_deg * 1e7), float(rel_alt_m),
0, 0, 0, # vx, vy, vz (yok sayılıyor)
0, 0, 0, # afx, afy, afz (ArduPilot bunları zaten yok sayar)
0, 0) # yaw, yaw_rate
if set_mode('GUIDED') and arm():
goto(39.9250000, 32.8360000, 25.0) # hedefi kendi sahanızla değiştirin
else:
print("Mod veya arm reddedildi — arm ön koşullarını kontrol et")
type_mask hangi alanların dikkate alınacağını belirler: bit 0 ise kullanılır, 1 ise yok sayılır. 0b110111111000 “sadece konum” demektir. Bu maskeyi yanlış kurmak en sık yaşanan sessiz hatadır — araç irtifayı değiştirir ama yatayda kıpırdamaz.
Konum hedefi araç oraya varana kadar geçerlidir. Hız veya ivme hedefi gönderiyorsanız GUID_TIMEOUT (varsayılan 3 saniye) içinde yenisini göndermelisiniz, aksi halde araç yavaşlayıp durur. Yatay hızı WP_SPD sınırlar (varsayılan 10 m/s).
MAVROS ve ROS 2 alternatifi
pymavlink tek başına yeterli değilse ROS katmanı devreye girer.
| Yaklaşım | Ne zaman seçilir |
|---|---|
| pymavlink | Tek bir Python süreci, birkaç mesaj tipi, hızlı prototip. Bağımlılık yükü en düşük seçenek |
| MAVROS | Zaten ROS ekosistemindesiniz; hazır node’lar, TF ağacı ve mesaj dönüşümleri işinize yarıyor. MAVLink köprüsü üzerinden çalışır |
| AP_DDS (native DDS) | ArduPilot 4.5 ve üzeri; ROS 2 ile XRCE-DDS üzerinden doğrudan konuşur, aradaki köprü kalkar. Ethernet bağlantısıyla birlikte tercih edilir |
ROS 1’in ömrü 2025’te dolduğu için yeni projelerde ROS 2 tarafına bakmak mantıklı (2026 itibarıyla). Biz simülasyonda Gazebo + SITL + ROS 2 kullanıyoruz, ancak uçuş yazılımının kritik yolunda hâlâ doğrudan pymavlink var: daha az katman, daha az sürpriz.
Sık düşülen tuzaklar
- Seri konsolu kapatmamak. Linux
/dev/serial0üzerinde login shell çalıştırıyorsa MAVLink verisinin içine boot mesajları karışır ve hiçbir paket parse edilmez. - Tek portu iki uygulamayla açmak. Bir seri portu aynı anda yalnızca bir süreç açabilir. Hem kendi kodunuz hem MAVProxy/yer istasyonu bağlanacaksa
mavlink-routerkurun ve portu birden fazla UDP endpoint’e dağıtın. wait_heartbeat()çağırmadan komut göndermek.master.target_systemo ana kadar 0’dır; komutlarınız hiçbir yere gitmez, hata da almazsınız.- Birim karıştırmak. Enlem/boylam degE7 (10⁷ ile çarpılmış tam sayı), irtifa mm, hız cm/s, açı radyan,
SET_MESSAGE_INTERVALmikrosaniye. Bu dördü karıştırıldığında ortaya çıkan hata sessizdir. - Stream rate’i sonuna kadar açmak. 57600 bps’lik bir telemetri linkinde her mesajı 50 Hz istemek bandı doldurur ve paket kaybına yol açar. Sadece ihtiyacınız olan mesajları, ihtiyacınız olan hızda isteyin.
- Companion’a yer istasyonuyla aynı system ID’yi vermek. Companion’ın heartbeat’i GCS failsafe’ini “hâlâ bağlıyım” sanarak susturabilir; gerçekte yer istasyonu bağlantısı kopmuş olsa bile failsafe tetiklenmez.
Güvenlik notları
Arm komutu FC tarafından reddedilebilir; bu bir hata değil, koruma katmanıdır. ARMING_CHECK denetimleri GPS kalitesi, EKF tutarlılığı, pusula sağlığı ve batarya gibi başlıkları kapsar. Kodunuz COMMAND_ACK sonucunu kontrol etmeli, reddedildiğinde görevi başlatmamalıdır.
GCS failsafe’ini companion mimarisinde ayrıca düşünün: FS_GCS_ENABLE açıkken FC, birincil yer istasyonundan (MAV_GCS_SYSID ile belirlenen sistem) gelen heartbeat’i izler ve FS_GCS_TIMEOUT süresi (varsayılan 5 saniye) boyunca sessizlik olursa failsafe tetikler. Companion’ınıza farklı bir system ID verin ki bu koruma çalışmaya devam etsin.
Son olarak, pilotun mod anahtarı her zaman companion’ın komutlarını geçersiz kılabilmelidir. Bizim ~20 m irtifadan iki hedefi tespit edip otonom bırakma testimizin arkasında haftalarca süren SITL tekrarları var; sahada ilk kez çalıştırılan kod diye bir şey olmadı.
Pratik özet
- TELEM2’yi kullanın:
SERIAL2_PROTOCOL=2,SERIAL2_BAUD=921, kablolama TX↔RX çapraz, ortak GND. Uçuşta USB’ye güvenmeyin. - Her bağlantıda
wait_heartbeat()çağırın, sonraMAV_CMD_SET_MESSAGE_INTERVALile yalnızca ihtiyacınız olan mesajları isteyin. - Komutları doğrulayın: mod değişimini
HEARTBEAT.custom_modeile, arm ve diğer komutlarıCOMMAND_ACK.resultile teyit edin. “Gönderdim” ile “kabul edildi” aynı şey değildir. type_maskve birimleri iki kez kontrol edin — degE7, mm, cm/s, radyan, mikrosaniye.- Failsafe’i companion’a rağmen çalışır bırakın: ayrı system ID kullanın, pilot müdahalesini engellemeyin, sahaya çıkmadan önce senaryoyu SITL’de tekrarlayın.
Kaynaklar: ArduPilot dev/copter dokümantasyonu (Companion Computers, Telemetry Port Setup, MAVLink Basics, Requesting Data, Guided Mode, Network Setup) ve mavlink.io common mesaj tanımları.
- companion-computer
- mavlink
- pymavlink
- ardupilot
- jetson