Softivation

Sensör ve devre laboratuvarı

Kart ve sensörler gerçek çipler gibi davranır: I2C adresi, kimlik yazmacı, uyku modu, bayt sırası, ölçek, gürültü ve sapma. Her görevde bir sensör çalışmıyor ya da yanlış ölçüyor; tahmin edin, çalıştırın, bağlantı şemasına ve grafiğe bakın, eksik satırı yazın.

  • Kod sitenin geri kalanından ayrı bir alanda çalışır; hesabınıza, bu sayfaya ve internete erişemez.
  • İlk çalıştırmada Python tarayıcınıza iner (birkaç saniye), sonra hızlıdır.

IMU: uyuyan sensörü uyandırın

MPU6050 I2C taramasında görünüyor, kimliği doğru, ama ivme hep sıfır. Önce tahmin edin: kablo mu kopuk, sensör mü bozuk, yoksa başka bir şey mi?

import math
from sv_lab import Gorev
from sv_lab.devre import Kart, Ortam, MPU6050, s16

kart = Kart(Ortam("salinim"))          # elde sallanan kart
kart.bagla(MPU6050())
print("I2C adresleri:", [hex(a) for a in kart.i2c_tara()])
print("WHO_AM_I:", hex(kart.i2c_oku(0x68, 0x75)[0]))
# TODO: sensörü uyandırın (ipucu: kart.i2c_yaz(0x68, 0x6B, ...))

for _ in range(300):
    b = kart.i2c_oku(0x68, 0x3B, 6)    # ACCEL_XOUT_H … ACCEL_ZOUT_L, büyük uçlu
    ax, ay, az = (s16(b[i], b[i + 1]) / 16384 for i in (0, 2, 4))   # 2 g aralığı: 16384 LSB/g
    kart.kaydet("az (g)", az)
    kart.kaydet("roll", math.degrees(math.atan2(ay, az)))
    kart.bekle_ms(20)

Gorev("IMU'yu uyandır, ivmeden roll", [
    ("ivmeölçer yerçekimini görüyor", lambda k: max(k.seri("az (g)")) > 0.8),
    ("roll gerçeğe yakın (RMS < 3 derece)", lambda k: k.hata("roll", "roll")[1] < 3),
]).denetle(kart)
kart.devre()
kart.ciz("roll", gercek={"roll": "roll"}, birim="derece", baslik="Roll: ivmeden")
Takıldınız mı?

MPU6050 uyku modunda açılır: PWR_MGMT_1 (0x6B) yazmacına 0 yazılmadan ölçüm yapmaz. Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi

Tümleyen filtre: jiroskop kayar, ivmeölçer titrer

Jiroskobu toplayınca açı yavaş yavaş kayar; ivmeölçer motor titreşiminde gürültülüdür. İkisini nasıl birleştirirsiniz? ALFA = 0 ile çalıştırıp üç çizgiyi karşılaştırın.

import math
from sv_lab import Gorev
from sv_lab.devre import Kart, Ortam, MPU6050, s16

kart = Kart(Ortam("salinim"))
kart.bagla(MPU6050(titresim=0.12))    # motorların titreşimi
kart.i2c_yaz(0x68, 0x6B, 0x00)
ALFA = 0.0   # TODO: jiroskoba ne kadar güveneceksiniz? 0 ile 1 arası, ör. 0.98
DT = 0.01
roll_jiro = roll_filtre = 0.0
for _ in range(1500):
    b = kart.i2c_oku(0x68, 0x3B, 14)  # ivme (6) + sıcaklık (2) + jiroskop (6)
    ay, az = s16(b[2], b[3]), s16(b[4], b[5])
    gx = s16(b[8], b[9]) / 131.0       # 250 derece/sn aralığı: 131 LSB/(derece/sn)
    roll_ivme = math.degrees(math.atan2(ay, az))
    roll_jiro += gx * DT
    roll_filtre = ALFA * (roll_filtre + gx * DT) + (1 - ALFA) * roll_ivme
    kart.kaydet("ivmeden", roll_ivme)
    kart.kaydet("jiroskoptan", roll_jiro)
    kart.kaydet("tümleyen", roll_filtre)
    kart.bekle_ms(10)

print("RMS hata  (derece)  ivmeden %.1f  jiroskoptan %.1f  tümleyen %.1f" % tuple(kart.hata(a, "roll")[1] for a in ("ivmeden", "jiroskoptan", "tümleyen")))
Gorev("Tümleyen filtre", [
    ("filtre gerçeğe yakın (RMS < 2,5 derece)", lambda k: k.hata("tümleyen", "roll")[1] < 2.5),
    ("filtre ivmeölçerden iyi", lambda k: k.hata("tümleyen", "roll")[1] < k.hata("ivmeden", "roll")[1]),
]).denetle(kart)
kart.ciz("ivmeden", "jiroskoptan", "tümleyen", gercek={"tümleyen": "roll"}, birim="derece", baslik="Roll")
Takıldınız mı?

ALFA = 0 yalnız ivmeölçeri kullanır; jiroskoba ağırlık verin (ör. 0,98). Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi

Barometre: basınçtan irtifa

Araç 5. saniyede kalkıp 30 m'ye çıkıyor. BMP280 kimliği doğru, kalibrasyon okunuyor, ama irtifa hep sıfır. Neden? Çalıştırıp devre şemasındaki uyarıya bakın.

from sv_lab import Gorev
from sv_lab.devre import Kart, Ortam, BMP280, bmp280_dengele

kart = Kart(Ortam("kalkis"))
kart.bagla(BMP280())
print("Kimlik:", hex(kart.i2c_oku(0x76, 0xD0)[0]))      # 0x58
kal = kart.i2c_oku(0x76, 0x88, 24)                       # kalibrasyon bloğu
# TODO: ölçümü başlatın (ipucu: kart.i2c_yaz(0x76, 0xF4, ...))

p0 = None
for _ in range(40):
    b = kart.i2c_oku(0x76, 0xF7, 6)                      # basınç (20 bit) + sıcaklık (20 bit)
    adc_p = (b[0] << 12) | (b[1] << 4) | (b[2] >> 4)
    adc_t = (b[3] << 12) | (b[4] << 4) | (b[5] >> 4)
    sicaklik, p = bmp280_dengele(kal, adc_t, adc_p)
    p0 = p0 or p                                         # kalkış noktasının basıncı
    irtifa = 44330 * (1 - (p / p0) ** (1 / 5.255))
    kart.kaydet("irtifa", irtifa)
    kart.bekle_ms(1000)
print("Son ölçüm: %.1f C, %.0f Pa, %.1f m" % (sicaklik, p, irtifa))

Gorev("Barometrik irtifa", [
    ("yerdeyken irtifa ~0", lambda k: all(abs(v) < 1 for v in k.seri("irtifa")[:5])),
    ("30 m'de 1 m'den az hata", lambda k: abs(k.seri("irtifa")[-1] - 30) < 1),
]).denetle(kart)
kart.devre()
kart.ciz("irtifa", gercek={"irtifa": "irtifa"}, birim="m", baslik="İrtifa")
Takıldınız mı?

BMP280 uyku modunda açılır: ctrl_meas (0xF4) yazmacına ölçüm ayarı yazılmalı (0x27 = sıcaklık ×1, basınç ×1, normal mod). Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi

Pusula: manyetik sapmayı kalibre edin

Kara aracının kartı kendi ekseninde iki tur dönüyor. Gövdedeki elektronik pusulaya sabit bir sapma ekliyor. Kalibrasyonsuz yön ne kadar şaşar? Tahmin edin, sonra en büyük ve en küçük değerlerden sapmayı bulun.

import math
from sv_lab import Gorev
from sv_lab.devre import Kart, Ortam, HMC5883L, s16

kart = Kart(Ortam("donus"))            # kendi ekseninde 18 derece/sn
kart.bagla(HMC5883L())
kart.i2c_yaz(0x1E, 0x02, 0x00)         # sürekli ölçüm modu

def oku():
    b = kart.i2c_oku(0x1E, 0x03, 6)
    return s16(b[0], b[1]), s16(b[4], b[5])     # sıra X, Z, Y: Y son iki bayt

xs, ys = [], []
for _ in range(400):                   # 20 sn: bir tam turdan fazla
    mx, my = oku()
    xs.append(mx); ys.append(my)
    kart.bekle_ms(50)
x0, y0 = 0, 0   # TODO: sapmayı bulun: (max(xs) + min(xs)) / 2, y için de aynısı
print("Sapma: x0 = %.0f, y0 = %.0f" % (x0, y0))

for _ in range(200):
    mx, my = oku()
    kart.kaydet("yön", math.degrees(math.atan2(-(my - y0), mx - x0)) % 360)
    kart.bekle_ms(50)

Gorev("Pusula kalibrasyonu", [
    ("yön hatası 5 dereceden küçük", lambda k: k.hata("yön", "yaw")[0] < 5),
]).denetle(kart)
kart.ciz("yön", gercek={"yön": "yaw"}, birim="derece", baslik="Yön (pusula)")
Takıldınız mı?

Sapma (hard iron) çıkarılmadı: her eksende (en büyük + en küçük) / 2. Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi

GPS: NMEA cümlesinden konum

Multikopter 40 m kuzeye, 25 m doğuya uçuyor; GPS seri porttan GGA cümlesi gönderiyor. 4011.31 gibi bir enlem 40,1131° mü? Tahmin edin, sonra izi karşılaştırın.

import math
from sv_lab import Arac, Gorev
from sv_lab.devre import Kart, Ortam, GPS

iha = Arac("multikopter", ad="SV-1")
iha.bekle(4).mod("GUIDED").arm().kalk(10)
iha.git(40, 25)
iha.calistir(40)

kart = Kart(Ortam(arac=iha))           # kartı uçan aracın üstüne koyun
kart.bagla(GPS())
EV = (40.1885, 29.0610)                # kalkış noktası (derece)

def derece(nmea):
    x = float(nmea)
    return x / 100   # TODO: NMEA derece-dakikadır (ddmm.mmmm); ondalık dereceye çevirin

for _ in range(40):
    for satir in kart.seri_oku():
        p = satir.split(",")           # $GPGGA,saat,enlem,N,boylam,E,...
        en, boy = derece(p[2]), derece(p[4])
        kart.kaydet("kuzey (m)", (en - EV[0]) * 111320)
        kart.kaydet("doğu (m)", (boy - EV[1]) * 111320 * math.cos(math.radians(EV[0])))
    kart.bekle_ms(1000)
print("Son cümle:", satir)

Gorev("GPS konumu", [
    ("kuzey 3 m'den yakın", lambda k: k.hata("kuzey (m)", "kuzey")[0] < 3),
    ("doğu 3 m'den yakın", lambda k: k.hata("doğu (m)", "dogu")[0] < 3),
]).denetle(kart)
kart.ciz("kuzey (m)", "doğu (m)", gercek={"kuzey (m)": "kuzey", "doğu (m)": "dogu"}, birim="m", baslik="GPS konumu")
Takıldınız mı?

NMEA enlemi derece ve dakikadır (ddmm.mmmm): int(x / 100) + (x % 100) / 60. Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi

Batarya: gerilim bölücüyle ölçüm

Deniz aracının 3S LiPo bataryası 12,6 V; kartın analog girişi en çok 5 V okur. 10 kΩ ve 3,3 kΩ'luk bölücüden gelen değeri doğru gerilime nasıl çevirirsiniz?

from sv_lab import Gorev
from sv_lab.devre import Kart, Ortam, GerilimBolucu

kart = Kart(Ortam("masa"))
kart.bagla(GerilimBolucu("A0", r1=10000, r2=3300))
ORAN = 1.0   # TODO: bölücü oranı: (R1 + R2) / R2

for _ in range(60):
    ham = kart.analog_oku("A0")        # 0–1023, 5 V referans
    v_pin = ham * 5.0 / 1023
    kart.kaydet("batarya (V)", v_pin * ORAN)
    kart.bekle_ms(1000)
print("Son okuma: %.2f V" % kart.seri("batarya (V)")[-1])

Gorev("Batarya gerilimi", [
    ("gerçeğe 0,05 V'tan yakın", lambda k: k.hata("batarya (V)", "batarya")[1] < 0.05),
    ("pin 5 V'u aşmadı", lambda k: not any("referansın" in m for _, m in k.olaylar)),
]).denetle(kart)
kart.devre()
kart.ciz("batarya (V)", gercek={"batarya (V)": "batarya"}, birim="V", baslik="Batarya")
Takıldınız mı?

Bölücü oranı yok: Vbat = Vpin · (R1 + R2) / R2. Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi

Ultrasonik mesafe: süreyi metreye

HC-SR04 yankının dönüş süresini verir. Kart 35 °C sıcakta. 343 m/sn ile hesaplayınca kaç santim şaşarsınız? Önce tahmin edin.

from sv_lab import Gorev
from sv_lab.devre import Kart, Ortam, HCSR04

SICAKLIK = 35.0
kart = Kart(Ortam("salinim", sicaklik=SICAKLIK, mesafe=1.2))
kart.bagla(HCSR04(trig=9, echo=10))

for _ in range(100):
    kart.dijital_yaz(9, 1); kart.dijital_yaz(9, 0)   # 10 mikrosaniyelik tetik darbesi
    sure = kart.darbe_olc(10)                          # mikrosaniye
    mesafe = sure * 1e-6 * 343.0   # TODO: süre gidiş-dönüştür; ses hızı da sıcaklığa bağlı
    kart.kaydet("mesafe (m)", mesafe)
    kart.bekle_ms(100)

Gorev("Ultrasonik mesafe", [
    ("1 cm'den az hata (RMS)", lambda k: k.hata("mesafe (m)", "mesafe")[1] < 0.01),
]).denetle(kart)
kart.devre()
kart.ciz("mesafe (m)", gercek={"mesafe (m)": "mesafe"}, birim="m", baslik="Mesafe")
Takıldınız mı?

Süre gidiş-dönüştür (ikiye bölün) ve ses hızı sıcaklıkla değişir: 331,3 + 0,606·T. Kodu kendi yapay zekânıza da sorabilirsiniz; önerdiği satırı burada çalıştırın, ölçütlerin hangisini karşıladığına bakın.

Bu konunun tamamı: Sensörlerden Uçuşa Temel İHA Eğitimi