Gerçek lövye ile model uçurmak

Gerçek lövye ile model uçurmak

Sümer abi çok acayip bişey olmuş bu :) Çok beğendim :)
 
Gerçek lövye ile model uçurmak

Sümer bey, gerçekten tebrik ederim. Süper bir iş olmuş! :bravo: Şimdi bir de modelin içine bir kamera koyup, bir gözlük ile FPV yapmak kaldı. İşte o zaman var ya...

Yalnız elinde kumandayı tutan yanındaki arkadaşın eli, acil durum için sürekli eğitim mandalının üzerinde, değil mi?
 
Gerçek lövye ile model uçurmak

Omer Erkan' Alıntı:
Yalnız elinde kumandayı tutan yanındaki arkadaşın eli, acil durum için sürekli eğitim mandalının üzerinde, değil mi?

Aynen öyle. Trainer kablolu uçuş sonuçta. Ben lövye ile öğrenci konumundayım ana kumanda ise hocanın elinde. Ben saçmaladığımda o hemen kumandayı devralıyor.

Sistem için uzun vadeli planlarım var.
1- Daha iyi bir mekanik altyapı oluşturmak.
2- Daha fazla sayıda giriş çıkışı olan bir kart (Arduino Mega gibi) kullanıp bir LCD gösterge eklemek.
3- LCD gösterge ve birkaç ek buton yardımıyla model hafızaları oluşturmak ve (hesabıma göre 50 kadar modeli hafızaya almak).
4- EPA, Expo, Subtrim vs gibi temel ayarları LCD ekran ve butonlarla yapmak.
5- Bir FPV ekranı eklemek.

Zafer yine laf edecek ama bugün ciddi bir sorun yaşadık. Havada değil tabii... :) Ayarlar esnasında oldu. Sebebi de hazır kütüphane kullanımı. Arkadaşlarımız hazır kütüphaneleri seviyor ama eğer kullandığınız kütüphanenin her türlü inceliğini bilmiyorsanız özellikle zamanlaması kritik işlemlerde beklenmedik sorunlar yaşayabiliyorsunuz.
Kısaca anlatayım...
Oluşturduğum yazılım trim değerlerini işlemci bünyesindeki EEPROM'a kaydediyor. Başlangıçta son değerleri oradan okuyor. Trimler değiştikçe de EEPROM'daki değerleri update ediyor. Bu amaçla EEPROM library kullanıyorum çünkü henüz kendi imkanlarımla EEROM'a müdahale edebilecek durumda değilim.
Bugün test aşamasında baktık ki peşpeşe birkaç tık trim verdiğimizde sistem kilitleniyor. Sonunda EEPROM'a yazma olayını iptal ederek uçabildik. Yoksa kumanda kaybına varabilecek sorunlar yaşanabilirdi.
Az önce epey bir kaynak karıştırdım. EEPROM'a yazma işlemi karmaşık bir iş. Peşpeşe birkaç byte yazacak olursak milisaniye düzeyinde gecikmeler oluyor. Benim orijinal kodda herhangi bir trim değiştiğinde tüm trimler yeniden EEPROM'a yazılıyordu. Yani toplam sekiz byte. Bu da 30 - 40 mS zaman demek. Bu süre boyunca PPM sinyali üretilemiyor. Master kumanda da bu kadar uzun süre sinyal gelmeyince artık trainer kablosu takılı değil diye algılıyor. Sinyal sonradan geri gelse de bir şey değişmiyor. Hatta Aurora yazılımındaki bir bug nedeniyle bazen ana kumanda da kontrol etmemeye başlıyor ve model kontrolsüz kalıyor. Korkunç!!!
İşte bu gece bu özelliği saptayınca iki ayrı önlem aldım. Birincisi sadece değişen trim değerini EEPROM'a yazıyorum. İkincisi her byte'ı yazdıktan sonra zamanı kontrol ediyorum ve fazla gecikme olduysa bir "porsiyon" PPM sinyali üretip sonra EEPROM ile işime devam ediyorum. Tabii yine de aklıma gelmeyen bir sorun olabilir. Uçak ile test etmek şart.
Demem o ki, çok basit bir EEPROM işlevi için bile kütüphane kullanınca o kütüphanenin ayrıntılarını anlamak gerekiyor ki böyle beklenmedik sıkıntılar yaşanmasın.
 
Gerçek lövye ile model uçurmak

Helal olsun Sümer abi. Gerçekten süper bir alet çıkartmışsın, bende lövyeyle uçmak isterim ama ne programlama bilirim nede evde kartlarım var. Bende şöyle birşey düşünüyordum bunun için, modeli biri eğitim diğeri ana kumanda olarak iki kumandayla eşleştireceğim. Kumandalardan birini Sol stick için, diğerini sağ stick için kullanacağım. Mekanizmada değişiklikler yapınca biraz kabada olsa uygulanabilir görünüyor.
 
Gerçek lövye ile model uçurmak

Helal olsun Sümer abi :thumbup: :saygilar:
 
Gerçek lövye ile model uçurmak

Inisten Sonraki hakli gurur ve sevincin gercekten her seyi acikliyor abi. Yeni upgrade sonrasi sistem Cok daha mukemmel olacak belli. Ellerine emegine saglik. Tum bu surecleri paylasimlarinla bizleri de ortak ettigin icin sonsuz tesekkurler Abi :bravo: :thumbup:
 
Gerçek lövye ile model uçurmak

Sağolun arkadaşlar. Yazılım işi oturduğun yerden olduğu için nispeten kolay. Gerçi olmadık yerlerde öngörülemeyen hatalar çıkıyor ama sistematik bir analizle sonunda hatayı bulabiliyorsun. Bana en zor gelen işin mekanik boyutu. Sistemin mekaniğinden memnun değilim. Merkezleme sorunu var. Bu da hassasiyeti azaltıyor. Daha farklı bir mekanizma tasarlamak gerekecek. Zaten şu anki ilk öncelik sıramda bu var. Mekanik yeterince hassas olmazsa en iyi kodu da yazsan nafile. :)
 
Gerçek lövye ile model uçurmak

Sümer Yamaner' Alıntı:
...
Bu da 30 - 40 mS zaman demek. Bu süre boyunca PPM sinyali üretilemiyor. Master kumanda da bu kadar uzun süre sinyal gelmeyince artık trainer kablosu takılı değil diye algılıyor. Sinyal sonradan geri gelse de bir şey değişmiyor. Hatta Aurora yazılımındaki bir bug nedeniyle bazen ana kumanda da kontrol etmemeye başlıyor ve model kontrolsüz kalıyor. Korkunç!!!
...

Sümer bey, bu durum kablosuz eğitim sisteminde de olabilir mi? Yani kullandığımız FrSky alıcı da PPM sinyali üretiyor ve kullandığım öğretmen kumandası Aurora. Acaba öğrenci kumandası uzun süre hareketsiz kalınca benzer bir durum oluşabilir mi?
 
Gerçek lövye ile model uçurmak

Sanmıyorum çünkü alıcı zaten var olan sinyali çıkışa iletiyor. Kendisi yeniden üretmiyor.

SM-N910C cihazımdan Tapatalk kullanılarak gönderildi
 
Gerçek lövye ile model uçurmak

Hava şiddetli rüzgarlı olunca uçuş günü iptal oldu. Ben de kodun temel işlevlerini baştan yazdım bugün.
Hatırlarsanız EEPROM yazılması esnasında oluşan gecikme sistemin yanıt vermemesine yol açıyordu. Ben de madem zaman kritik işlem PPM sinyalinin oluşturulması, o halde onu interruptlara havale ederim diye düşündüm. Kısa sayılabilecek bir çalışma ve test aşaması sonrası aşağıdaki kod oluştu. Şimdi tek derdim kanal isimlerinin aileron, elevator vs gibi oluşu. Onları da bir array içinde toplayacağım daha sonra. Ama şu an bu kod hem simülatörde hem de gerçek alıcı verici ve servolarla gayet güzel çalışıyor. Endpoint ayarları, trim ayarları, gaz kanalında trimin sadece alt uca etki etmesi gibi tüm gerekli özellikler de sorunsuz çalışıyor. Kod aynı zamanda simülatör kumandası için de kullanılabilir.

Kod:
/* Uçuş alanında kullanılacak lövye sistemi için program
   Aileron A0
   Elevatör A1
   Throttle A2
   Rudder A3

   Aileron trim left D2
   Aileron trim right D3
   Elevatör trim down D4
   Elevatör trim up D5
   Rudder trim left D6
   Rudder trim right D7
   Throttle trim down D8
   Throttle trim up D9

   Kanal 5 D11
   Kanal 6 D12



   PPM çıkışı D10

   Elevator Down ve Throttle Up trimlerini birlikte 3 saniye
   basılı tutunca tüm trimler sıfırlanıyor

   Elevator Up ve Throttle down trimlerini birlikte 3 saniye
   basılı tutunca çıkış sinyali invert oluyor
*/

#include <EEPROM.h>

const int atva = 100; // Aileron travel percentage
const int etva = 100; // Elevator travel percentage
const int rtva = 100; // Rudder travel percentage

// Throttle endpointleri modele göre belirlenecek
unsigned int epa1 = 100; // Percent (Low end)
unsigned int epa2 = 100; // Percent (High end)
unsigned int tepa1;
unsigned int tepa2;

boolean a_rev;
boolean e_rev;
boolean t_rev;
boolean r_rev;
boolean ch5_rev;
boolean ch6_rev;
boolean ch7_rev;
boolean ch8_rev;

const int aileronin = 0;
const int elevatorin = 1;
const int throttlein = 2;
const int rudderin = 3;

const int PPMout = 10;
const int LED = 13;

const int aileronleft = 2;
const int aileronright = 3;
const int elevatordown = 4;
const int elevatorup = 5;
const int rudderleft = 6;
const int rudderright = 7;
const int throttledown = 8;
const int throttleup = 9;

const int ch5in = 11;
const int ch6in = 12;
const int ch7in = 11;
const int ch8in = 12;

// Donanımsal verilere göre elde edilen endpointler.
const int a1 = 106;
const int a2 = 782;
const int e1 = 150;
const int e2 = 846;
const int t1 = 0;
const int t2 = 793;
const int r1 = 290;
const int r2 = 472;

// Donanımın sunduğu hareket alanları
int a;
int e;
int t;
int r;

int channels[8];
volatile int channels_int[8];

int ailerontrim;
int elevatortrim;
int throttletrim;
int ruddertrim;

byte keyread;
byte keyread1;
byte keyread2;

int resetcount = 0;
int modecount = 0;

boolean sinyal = HIGH; // Pozitif PPM pulse ile başlıyoruz istenirse reverse ediliyor
boolean LEDstatus = HIGH;

volatile boolean PPM_ON = false;
unsigned int keycounter;
volatile int current_channel;
volatile unsigned int framelength = 16000;


ISR(TIMER1_COMPA_vect)
{
  TCNT1 = 0;
  if (digitalRead(PPMout) == sinyal)
  {
    digitalWrite(PPMout, !sinyal);
    OCR1A = 600;
  }
  else
  {
    digitalWrite(PPMout, sinyal);
    if (current_channel < :coolxf:
    {
      OCR1A = 2 * channels_int[current_channel] - 600;
      current_channel++;
      PPM_ON = true;
    }
    else
    {
      current_channel = 0;
      OCR1A = (22500 - framelength) * 2;
      PPM_ON = false;
    }
  }
}


void setup()
{
  for (int n = 2; n < 10; n++)
  {
    pinMode(n, INPUT_PULLUP);
  }
  pinMode(ch5in, INPUT_PULLUP);
  pinMode(ch6in, INPUT_PULLUP);
  pinMode(PPMout, OUTPUT);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
  digitalWrite(PPMout, sinyal);
  EEPROMread();
  
  TCCR1A = 0;
  TCCR1B = 2; // Prescaler 8
  TIMSK1 = 0;

  a = a2 - a1;
  e = e2 - e1;
  t = t2 - t1;
  r = r2 - r1;
  epa1 = 500 - (5 * epa1); // Bu aşamada artık throttle epa değerleri
  epa2 = 500 + (5 * epa2); // 0 ile 1000 arasında değişir hale getiriliyor

  TCNT1 = 0;
  OCR1A = 12000; // İlk sync pulse 6 mS
  TIMSK1 = 2;
}

void loop()
{
  digitalWrite(LED, LEDstatus);
  readall();
  while(PPM_ON);
  framelength = 0;
  for(int n = 0; n < 8; n++)
  {
    channels_int[n] = channels[n];
    framelength = framelength + channels[n];
  }
  keys();
}


void EEPROMread()
{
  /* Aileron 0, 1 MSB, LSB
     Elevator 2, 3 MSB, LSB
     Throttle 4, 5 MSB, LSB
     Rudder 6, 7 MSB, LSB
  */
  ailerontrim = (EEPROM.read(0) << :coolxf: + EEPROM.read(1);
  elevatortrim = (EEPROM.read(2) << :coolxf: + EEPROM.read(3);
  throttletrim = (EEPROM.read(4) << :coolxf: + EEPROM.read(5);
  ruddertrim = (EEPROM.read(6) << :coolxf: + EEPROM.read(7);
}

void EEPROMwrite()
{
  /* Aileron 0, 1 MSB, LSB
     Elevator 2, 3 MSB, LSB
     Throttle 4, 5 MSB, LSB
     Rudder 6, 7 MSB, LSB
  */
  int x;
  byte m;
  byte l;

  x = ailerontrim >> 8;
  m = x & 255;
  l = ailerontrim & 255;
  EEPROM.update(0, m);
  EEPROM.update(1, l);
  x = elevatortrim >> 8;
  m = x & 255;
  l = elevatortrim & 255;
  EEPROM.update(2, m);
  EEPROM.update(3, l);
  x = throttletrim >> 8;
  m = x & 255;
  l = throttletrim & 255;
  EEPROM.update(4, m);
  EEPROM.update(5, l);
  x = ruddertrim >> 8;
  m = x & 255;
  l = ruddertrim & 255;
  EEPROM.update(6, m);
  EEPROM.update(7, l);
}

void resettrims()
{
  resetcount++;
  if (resetcount > 100) // Throttle up ve elevator down 2 saniyeden fazla basılı tutulduysa...
  {
    ailerontrim = 0;
    elevatortrim = 0;
    throttletrim = 0;
    ruddertrim = 0;
    EEPROMwrite();
    resetcount = 0;
    digitalWrite(LED, LOW);
    for (int z = 0; z < 5; z++)
    {
      digitalWrite(LED, HIGH);
      delay(200);
      digitalWrite(LED, LOW);
      delay(200);
    }
  }
}

void changemode()
{
  modecount++;
  if (modecount > 100)
  {
    modecount = 0;
    sinyal = !sinyal;
    digitalWrite(LED, LOW);
    for (int z = 0; z < 10; z++)
    {
      digitalWrite(LED, HIGH);
      delay(100);
      digitalWrite(LED, LOW);
      delay(100);
    }
  }
}

void keys()
{
  keyread1 = PIND | B00000011;
  keyread2 = PINB | B11111100;
  keyread = keyread1 & B11111100;
  keyread = keyread | (keyread2 & B00000011);
  /* keyread1 PORT D'yi okuyor (digital 0 - 7)
     keyread2 PORT B'yi okuyor (digital 8 - 13)
     keyread2'deki iki LSBit daha sonra keyread'daki iki LSBit yerine aktarılıyor
     Böylece...
     keyread
     -------------------------------------------------
     I  7  I  6  I  5  I  4  I  3  I  2  I  1  I  0  I
     I Rud I Rud I Elv I Elv I Ail I Ail I Thr I Thr I
     I  R  I  L  I Up  I Dn  I  R  I  L  I Up  I Dn  I
     -------------------------------------------------
  */
  if (keyread < 255)
  {
    /* 2 saniye boyunca Elevator down ve throttle up trimi
       basılı tutulursa trimler resetleniyor
       Bu süreyi resetcount değişkeni tutuyor ve resettrims()
       altprogramında güncellenip 100'ü geçince
       reset işlemi uygulanıyor
    */
    if (keyread == B11101101)
    {
      resettrims();
      return;
    }
    if (keyread == B11011110)
    {
      changemode();
      return;
    }
    resetcount = 0;
    keycounter++;
    if ((keycounter & B00000011) != 0) return; // Her dört frame'de bir trimler değiştiriliyor

    if ((keyread & B00000001) == 0)
    {
      if (t_rev == 0)
      {
        throttletrim--;
      }
      else
      {
        throttletrim++;
      }
    }

    if ((keyread & B00000010) == 0)
    {
      if (t_rev == 0)
      {
        throttletrim++;
      }
      else
      {
        throttletrim--;
      }
    }

    if ((keyread & B00000100) == 0)
    {
      if (a_rev == 0)
      {
        ailerontrim--;
      }
      else
      {
        ailerontrim++;
      }
    }

    if ((keyread & B00001000) == 0)
    {
      if (a_rev == 0)
      {
        ailerontrim++;
      }
      else
      {
        ailerontrim--;
      }
    }

    if ((keyread & B00010000) == 0)
    {
      if (e_rev == 0)
      {
        elevatortrim--;
      }
      else
      {
        elevatortrim++;
      }
    }

    if ((keyread & B00100000) == 0)
    {
      if (e_rev == 0)
      {
        elevatortrim++;
      }
      else
      {
        elevatortrim--;
      }
    }

    if ((keyread & B01000000) == 0)
    {
      if (r_rev == 0)
      {
        ruddertrim--;
      }
      else
      {
        ruddertrim++;
      }
    }

    if ((keyread & B10000000) == 0)
    {
      if (r_rev == 0)
      {
        ruddertrim++;
      }
      else
      {
        ruddertrim--;
      }
    }

    if (throttletrim < -300) throttletrim = -300;
    if (throttletrim > 300) throttletrim = 300;
    if (ailerontrim < -300) ailerontrim = -300;
    if (ailerontrim > 300) ailerontrim = 300;
    if (elevatortrim < -300) elevatortrim = -300;
    if (elevatortrim > 300) elevatortrim = 300;
    if (ruddertrim < -300) ruddertrim = -300;
    if (ruddertrim > 300) ruddertrim = 300;

    EEPROMwrite();
  }
}

void readall()
{
  channels[0] = analogRead(aileronin);
  channels[0] = analogRead(aileronin);
  channels[0] = analogRead(aileronin);
  channels[0] = map(channels[0], a1, a2, 0, 1000);
  channels[0] = map(channels[0], 0, 1000, (500 - 5 * atva), (500 + 5 * atva));
  if (a_rev == 0)
  {
    channels[0] = 1000 + channels[0] + ailerontrim;
  }
  else
  {
    channels[0] = 2000 - channels[0] + ailerontrim;
  }

  channels[1] = analogRead(elevatorin);
  channels[1] = analogRead(elevatorin);
  channels[1] = analogRead(elevatorin);
  channels[1] = map(channels[1], e1, e2, 0, 1000);
  channels[1] = map(channels[1], 0, 1000, (500 - 5 * etva), (500 + 5 * etva));
  if (e_rev == 0)
  {
    channels[1] = 1000 + channels[1] + elevatortrim;
  }
  else
  {
    channels[1] = 2000 - channels[1] + elevatortrim;
  }

  channels[2] = analogRead(throttlein);
  channels[2] = analogRead(throttlein);
  channels[2] = analogRead(throttlein);
  tepa1 = epa1 + throttletrim;
  tepa2 = epa2;
  channels[2] = map(channels[2], t1, t2, tepa1, tepa2);
  if (t_rev == 0)
  {
    channels[2] = 1000 + channels[2];
  }
  else
  {
    channels[2] = 2000 - channels[2];
  }

  channels[3] = analogRead(rudderin);
  channels[3] = analogRead(rudderin);
  channels[3] = analogRead(rudderin);
  channels[3] = map(channels[3], r1, r2, 0, 1000);
  channels[3] = map(channels[3], 0, 1000, (500 - 5 * rtva), (500 + 5 * rtva));
  if (r_rev == 0)
  {
    channels[3] = 1000 + channels[3] + ruddertrim;
  }
  else
  {
    channels[3] = 2000 - channels[3] + ruddertrim;
  }

  if (ch5_rev == 0)
  {
    channels[4] = 1000 + 1000 * digitalRead(ch5in);
  }
  else
  {
    channels[4] = 2000 - 1000 * digitalRead(ch5in);
  }

  if (ch6_rev == 0)
  {
    channels[5] = 1000 + 1000 * digitalRead(ch6in);
  }
  else
  {
    channels[5] = 2000 - 1000 * digitalRead(ch6in);
  }

  if (ch7_rev == 0)
  {
    channels[6] = 1000 + 1000 * digitalRead(ch7in);
  }
  else
  {
    channels[6] = 2000 - 1000 * digitalRead(ch7in);
  }
  if (ch8_rev == 0)
  {
    channels[7] = 1000 + 1000 * digitalRead(ch8in);
  }
  else
  {
    channels[7] = 2000 - 1000 * digitalRead(ch8in);
  }

  for (int n = 0; n < 8; n++)
  {
    channels[n] = constrain(channels[n], 800, 2200);
  }
}
 
Gerçek lövye ile model uçurmak

Bugün de boş geçmemişsin be abi :halay:


Sent from my iPhone using Tapatalk
 
Gerçek lövye ile model uçurmak

Sanırım expo da oldu! :yuppi:

Değerleri yazdırıp Excel'e aktarıp grafik çizdirdim. Tam olması gerektiği gibi çıkıyor. Her bir kanal expo hesabı 800 mikrosaniye kadar sürüyor (kayar nokta hesaplamaları olduğu için). Aileron, elevator ve rudder kanallarında olacağı için yaklaşık 2.4 mS ek süre gerektiriyor. Normalde girişleri okuma süresi yaklaşık 1.5 mS idi. Demek ki kabaca 4 mS olacak. Yani yaklaşık 6 mS süreli sync darbesi içinde halledilebilecek...

Sıra geldi gerçek hayatta teste!

Burada - % 30 expo ve % 80 endpoint ayarı ile üretilen sinyallerin grafiğini görüyorsunuz.

[attachimg=1]

NOT: Simülatörde belirgin olarak fark etti expolu hali.
 

Ekli dosyalar

  • Expo.jpg
    Expo.jpg
    37.7 KB · Görülme: 119
Gerçek lövye ile model uçurmak

Sıfırcı Hoca'dan uçuşa uygunluk sertifikamızı da aldık ayrıntılı testler sonucunda.

[attachimg=1]
 

Ekli dosyalar

  • pic0001.jpg
    pic0001.jpg
    39.7 KB · Görülme: 89
Gerçek lövye ile model uçurmak

Sümer Yamaner' Alıntı:
Sıfırcı Hoca'dan uçuşa uygunluk sertifikamızı da aldık ayrıntılı testler sonucunda.
Boyle bir hocasi olan ogrencinin zaten basarili olmasina sasmamak lazim :laugh: :D