Ardupilot Firmware'i Üzerinde Tilt Rotor Kaynak Kodunun İncelenmesi Üzerine Rapor
Yazılım 7 dk okuma

Ardupilot Firmware'i Üzerinde Tilt Rotor Kaynak Kodunun İncelenmesi Üzerine Rapor

Semih Berat Erdoğan

Bilgisayar Mühendisliği

Ardupilot Firmware'i Üzerinde Tilt Rotor Kaynak Kodunun İncelenmesi Üzerine Rapor

1. KISIM



Şekil 1: Arduplane Kaynak Kodları
Şekil 1: Arduplane Kaynak Kodları

Ardupilot arduplane kaynak kodu'nu incelediğimizde karşımıza C++ ve interface amacıyla yazılmış header dosyaları gözükmekte. Amacımız, tilt rotoru incelemek üzerine olduğu için kaynak kodlarının arasında tiltrotor.cpp ve tiltrotor.h kodunu görmekteyiz (bkz. Şekil 1). Bir sonraki sayfalarda bahsettiğimiz kaynak kodlarını açıklayacağız.

tiltrotor.h


tiltrotor.h dosyasını incelemeye geçtiğimiz esnada bizi ilk include kısımları karşılamakta.

#pragma once

#include <AP_Param/AP_Param.h>
#include "transition.h"
#include <AP_Logger/LogStructure.h>

AP_Param: bir tür build edilmiş parametre dosyasıdır. Build edildiğinde  C++ kodlarında kullanılmak amacıyla bir header file oluşturur. Bu header file ile yer kontrol yazılımı aracılığıyla (bknz. Mission Planner ve QGroundControl) veya manuel olarak oluşturulabilir. Param dosyaları cihazın ne tür olacağı, oluşturulduğu türde ne tür davranışlar sergileyeceğini belirleyen dosyalardır. 

Örneğin vtol uçak olarak davranış sergileyecek bir cihazın tailsitter mi yoksa multirotor mu olacağını bize bu parametreler belirler.

transition: Uçağın durum geçişleri, complete durumları, yaw, pitch ve roll gibi değerlerin update'i bu kısımda yer almakta.

// Örnek bir transition class'ı

class Transition
{
public:

Transition(QuadPlane& _quadplane, AP_MotorsMulticopter*& _motors):quadplane(_quadplane),motors(_motors) {};

virtual void update() = 0;

virtual void VTOL_update() = 0;

virtual void force_transition_complete() = 0;

virtual bool complete() const = 0;

virtual void restart() = 0;

virtual uint8_t get_log_transition_state() const = 0;

virtual bool active_frwd() const = 0;

virtual bool show_vtol_view() const = 0;

virtual void set_FW_roll_pitch(int32_t& nav_pitch_cd, int32_t& nav_roll_cd) {};

virtual bool set_FW_roll_limit(int32_t& roll_limit_cd) { return false; }


AP_Logger: İnsansız aracın, durum değişkenleri karşısında (örneğin: takeoff'dan flight mode'a geçmesi) verdiği  Uyarı/Kabul/Red durumlarını terminal çıktısı ve log dosyası olarak veren logging kütüphanesi.


Kaynak kodlarının tümünü incelediğimizde AP_Logger, transition ve AP_Param dosyalarının kaynak kodlarının bütününde import edildiğini ve kodların belirli bir şablona bağlı kalarak yapıyı ve mimariyi okunaklı bir şekilde geliştirdiğini anlayabiliriz.

2. KISIM: tiltrotor.cpp

Kısaca, insansız araçlardaki hareket mekaniğinin ve komutların durum dönüşümlerine bağlı olarak çalıştığını ve belirli parametrelere göre davranış sergilediğini; ayrıca ana C++ dosyasında kullanılacak tüm bileşenlerin header dosyasında tanımlandığını ve asıl kod ile algoritmanın tiltrotor.cpp üzerinde yürütüldüğünü anlamış olduk.

2.1. void Tiltrotor::setup():


 if (!enable.configured() && ((tilt_mask != 0) || (type == TILT_TYPE_BICOPTER))) {
enable.set_and_save(1);
}

if (enable <= 0) {
return;
}

quadplane.thrust_type = QuadPlane::ThrustType::TILTROTOR;

_is_vectored = tilt_mask != 0 && type == TILT_TYPE_VECTORED_YAW;

// true if a fixed forward motor is configured, either throttle, throttle left or throttle right.
// bicopter tiltrotors use throttle left and right as tilting motors, so they don't count in that case.
_have_fw_motor = SRV_Channels::function_assigned(SRV_Channel::k_throttle) ||
((SRV_Channels::function_assigned(SRV_Channel::k_throttleLeft) || SRV_Channels::function_assigned(SRV_Channel::k_throttleRight))
&& (type != TILT_TYPE_BICOPTER));


// check if there are any permanent VTOL motors
for (uint8_t i = 0; i < AP_MOTORS_MAX_NUM_MOTORS; ++i) {
if (motors->is_motor_enabled(i) && !is_motor_tilting(i)) {
// enabled motor not set in tilt mask
_have_vtol_motor = true;
break;
}
}

if (_is_vectored) {
// we will be using vectoring for yaw
motors->disable_yaw_torque();
}

Fonksiyonun içindeki bu kod parçacığını incelediğimizde, başlangıcın belli koşullara bağlı olarak başlatıldığını anlayabiliriz. Örneğin `enable.configured()` bize bool(0 veya 1) değerler göndermekte. Buradan anlayabiliriz ki her başlangıçta enable.configured() değeri 0 olarak gelmekte 

 if (!enable.configured() && ((tilt_mask != 0) || (type == TILT_TYPE_BICOPTER))) {
enable.set_and_save(1);
}

Yani her başlangıçta bu koşul sağlanmakta ve enable.set_and_save(1) yaptığımızda fonksiyonun değeri 1 olmakta. Bu sayede aracın başlatıldığı andaki satırlar bir kere çalışmış olup, çalışma esnasında bir daha çalışmamak kaydıyla tasarlanmıştır.

    if (tilt_mask != 0) {
// setup tilt compensation
motors->set_thrust_compensation_callback(FUNCTOR_BIND_MEMBER(&Tiltrotor::tilt_compensate, void, float *, uint8_t));
if (type == TILT_TYPE_VECTORED_YAW) {
// setup tilt servos for vectored yaw
SRV_Channels::set_range(SRV_Channel::k_tiltMotorLeft, 1000);
SRV_Channels::set_range(SRV_Channel::k_tiltMotorRight, 1000);
SRV_Channels::set_range(SRV_Channel::k_tiltMotorRear, 1000);
SRV_Channels::set_range(SRV_Channel::k_tiltMotorRearLeft, 1000);
SRV_Channels::set_range(SRV_Channel::k_tiltMotorRearRight, 1000);
}
}

Kodun bu kısmında, yorum satırından da anlayacağımız şekilde vektörlü yaw(z ekseni açısı) için tilt servoların uygun servo kanallarıyla eşleşip uygun açıya getirir. SRV_Channels::set_range statik fonksiyon olarak yazılmış set_range fonksiyonunun ilk parametresi uygun motorla eşleşen servo kanalının adresini belirten fonksiyonu, ikinci parametre ise uygun açıya açıya getirilmesi için gereken aralığı(range) belirler.

// set_range fonksiyonunun kaynağı

void SRV_Channels::set_range(SRV_Channel::Function function, uint16_t range)
{
for (uint8_t i=0; i<NUM_SERVO_CHANNELS; i++) {
if (channels[i].function == function) {
channels[i].set_range(range);
}
}
}

 Örneğin 255 girildiğinde -255 ile 255 arasında bir aralık üzerinden dönüşüm yapar. Daha sonrasında standarize edilmiş aralık pwm_from_range fonksiyonu yardımıyla servo outputlarını bu şekilde vereceğiz (fonksiyon isimleri ve algoritmayı okuduğumuz kadarıyla şimdiki tahmin bu yönde).

//pwm_for_range fonksiyonun kaynağı

uint16_t SRV_Channel::pwm_from_range(float scaled_value) const
{
if (servo_max <= servo_min || high_out == 0) {
return servo_min;
}
scaled_value = constrain_float(scaled_value, 0, high_out);
if (reversed) {
scaled_value = high_out - scaled_value;
}
return servo_min + uint16_t( (scaled_value * (float)(servo_max - servo_min)) / (float)high_out );
}

Programın işleyişini anladık. Bundan sonrasını okumak bizim için çok basit. Uçuş kontrolcüsüne statik olarak (dışarıdan) katkısı olan kütüphaneler libraries kısmında tanımlanmış. 

Her insansız araç tipine has isimde dosya oluşturulmuş ve aracın özellikleri bu dosya içerisinde geliştirilmiştir (Örneğin, Arduplane, Arducopter, vs.). Bu geliştirmeler, daha önce de bahsedildiği gibi libraries dosyasındaki bileşenlerin farklı şekilde kullanımlarıyla her cihaza has bir sistem geliştirilmiş bulunmakta.

İşleyişin detaylıca nasıl işlediğini anladığımıza göre, Artık kodlarda kullanılan her bir fonksiyonun arkasındaki işleyişi sadece main koda bakarak mimariyi anlamamız mümkün. 

3. KISIM: `Daha Az Detay Daha Fazla Bilgi`

Kodları sıfırdan inceleyip tek tek ne yaptığını anlamaya çalışmak işimizi zorlaştıracağından, tilt motorlu bir vtol'un arm edildiği andan, serbest uçuş esnasına kadarki, durum değişikliklerini, davranışlarını ve hesaplamalarını anlamamız yeterli



Tilted VTOL UAV:

Tilt VTOL uçaklarda hover moddan forward flight (uçuş) moduna geçiş, yani transition sırasında motorlar ve tilt servo sistemi belirli bir mantıkla çalışır. Hover moddayken, uçak dikey thrust üretir, yani motorlar dikey olarak yukarı yöneliktir ve uçak bir multirotor gibi stabilize edilir. Bu aşamada tilt motorları minimum veya sıfır açıdadır, kanatlar yatay ve sabittir, flight controller roll, pitch ve yaw’u multirotor PID’leri ile kontrol eder.

Transition başlatıldığında, tilt motorları kademeli olarak öne doğru açılır, motorlar artık hem dikey hem de yatay thrust bileşeni üretmeye başlar. Bu süreçte motorların gücü ve tilt açısı dikkatle kontrol edilir: dikey thrust azalırken forward thrust artar, böylece irtifa kaybı önlenir ve uçağın stabilitesi korunur. Motor throttle’ları çoğu zaman ortalama veya normalize edilmiş değerler üzerinden hesaplanır, bu da motorlar arası güç dağılımını dengeler ve ani tilt değişimlerinin neden olacağı yalpalamayı azaltır.

Tilt servo açısı kademeli olarak artırılır, bazen bir limit veya forward flight hedef açısı referans alınır. Bu sayede uçak kontrollü bir şekilde öne doğru eğilir, roll ve pitch PID’leri transition boyunca multirotor kontrolünü destekleyebilir veya kademeli olarak forward flight kontrol algoritmasına geçiş yapabilir. Sonuç olarak, hover moddaki dikey stabilite, forward flight modunda yatay uçuş performansına dönüşürken motor gücü, tilt açısı ve kontrol algoritmaları koordineli şekilde değişir (ChatGpt).

Bu Davranışların Kod Akışı


Tekrar tiltrotor.h'ı incelediğimizde aslında adından her işlevin ne olduğunu anlamamız mümkün. "Mesela is_motor_tilting"  fonksiyonunun, motorların tilt edilip edilmediğine dair bool state döndürdüğüne dair tahminde bulunabiliriz veyahut "get_fully_forward_tilt" fonksiyonunda servoların maksimum veya minimum konumlarına geldiğini anlayabiliriz.

Yine C++ dosyasındaki implementasyona dönecek olursak;

Açı düzenlemeleriyle ilgili tüm temel mevzunun Tiltrotor::vectoring ve Tiltrotor::continuous_update fonksiyonlarında döndüğünü anlayabiliriz. Kod üzerindeki küçük fonksiyonlar ve dönüşüm metodları yazıldıktan sonra tamamiyle bu fonksiyonlar içerisinde kullanılmış vaziyette. Bu kısım sadece iki fonksiyon üzerine yoğunlaşacağımız son kısım.

void Tiltrotor::update(void)
{
if (!enabled() || tilt_mask == 0) {
// no motors to tilt
return;
}

if (type == TILT_TYPE_BINARY) {
binary_update();
} else {
continuous_update();
}

if (type == TILT_TYPE_VECTORED_YAW) {
vectoring();
}
}

Tüm puzzle'ın tamamladığı yer tam da bu fonksiyon(update) Bu fonksiyon içerisinde. Kodların içerisinde gördüğümüz küçük fonksiyon parçacıklarının tamamı bu fonksiyonun(update) içerisindeki üç fonksiyon içinde toparlanmış ve belli koşullara göre bu fonksiyonlar işlevini yerini getiriyor.

    if (!enabled() || tilt_mask == 0) {
// no motors to tilt
return;
}

Herhangi bir motor bir tilt durumuna daha evrilmemişse function scope'dan çıkıp işleve devam etmez. 

    if (type == TILT_TYPE_BINARY) {
binary_update();
} else {
continuous_update();
}

Buradadaki fonksiyona bakarak tilt motorlu uçakların iki türlü çalışabildiğini anlayabiliriz;

1. Binary Tilt:

Servo motorlar sadece maksimum ve minimum açılarda çalışıyor. Mesela kalkışta servolar yukarı bakacak pozisyona geliyor. İleri uçuş moduna geçerken motorlar öne veya arkaya doğru hareket ediyor, ama ara açılar yok; servo ya min ya da max pozisyonda. İleri uçuş tamamen kanatların aerodinamik etkisiyle sağlanıyor.

2. Continuous Update:

Servo motorlar uçuş ve kalkış esnasında esnek hareket yaparlar örneğin ileri ve yukarı doğru hareket için servolar öne doğru eğilir, surface'ler(kanatlar) ile eş zamanlı çalışır. Motorların ne açıda duracağı tamamen kullanıcıya(kumandaya) bağlıdır.

    if (type == TILT_TYPE_VECTORED_YAW) {
vectoring();
Ek Kontrol: Eğer araç Vectored Yaw (motorları yatırarak yön verme) özelliğine sahipse, güncelleme bittikten sonra bir de vectoring() fonksiyonunu çalıştırır.

Tiltrotor::binary_update():

void Tiltrotor::binary_update(void)
{
// motors always active
_motors_active = true;

if (!quadplane.in_vtol_mode()) {
// we are in pure fixed wing mode. Move the tiltable motors
// all the way forward and run them as a forward motor
binary_slew(true);

float new_throttle = SRV_Channels::get_output_scaled(SRV_Channel::k_throttle)*0.01f;
if (current_tilt >= 1) {
const uint32_t mask = is_zero(new_throttle) ? 0 : tilt_mask.get();
// the motors are all the way forward, start using them for fwd thrust
motors->output_motor_mask(new_throttle, mask, plane.rudder_dt);
}
} else {
binary_slew(false);
}
}

Yorum satırı bize şu bilgiyi veriyor:

Eğer vtol modda değilsek:

Tamamen sabit kanat modundayız. Eğimli motorları
tamamen öne doğru hareket ettirin ve onları ileri motor olarak çalıştırın.

`binary_slew(true)`  Motorlari yatırmaya başlar.

"throttle command" verisini alıp, motorun anlayacağı bir dile çeviriyor. Bir nevi "birim dönüştürücü" diyebiliriz.

        if (current_tilt >= 1) {
const uint32_t mask = is_zero(new_throttle) ? 0 : tilt_mask.get();
// the motors are all the way forward, start using them for fwd thrust
motors->output_motor_mask(new_throttle, mask, plane.rudder_dt);
}

Bu kod parçacığının içindeki yorum satırı zaten bize gerekli bilgiyi vermiş

motorlar tamamen öne doğru, ileri itiş için kullanmaya başla

yani; `if(current_tilt) >= 1)` motorların tamamen öne doğru olup olmadığıyla ilgili bir koşul(statement).

void Tiltrotor::continuous_update(void):

    // default to inactive
    _motors_active = false;

    // the maximum rate of throttle change
    float max_change;
Motor durumu: varsayılan => inaktif
itki değişiminin maksimum oranı 

`max_change`  şu anki tahmine göre fonksiyon içerisinde declare edilip döngü veya statement içinde güncel throttle ile kıyaslanması durumunda maximumun atanacağı değişken olarak tanımlanmış olabilir.

 if (!quadplane.in_vtol_mode() && (!plane.arming.is_armed_and_safety_off() || !quadplane.assisted_flight)) {
        // we are in pure fixed wing mode. Move the tiltable motors all the way forward and run them as
        // a forward motor

        // option set then if disarmed move to VTOL position to prevent ground strikes, allow tilt forward in manual mode for testing
        const bool disarmed_tilt_up = !plane.arming.is_armed_and_safety_off() && (plane.control_mode != &plane.mode_manual) && quadplane.option_is_set(QuadPlane::Option::DISARMED_TILT_UP);
        slew(disarmed_tilt_up ? 0.0 : get_forward_flight_tilt());

        max_change = tilt_max_change(false);

        float new_throttle = constrain_float(SRV_Channels::get_output_scaled(SRV_Channel::k_throttle)*0.01, 0, 1);
        if (current_tilt < get_fully_forward_tilt()) {
            current_throttle = constrain_float(new_throttle,
                                                    current_throttle-max_change,
                                                    current_throttle+max_change);
        } else {
            current_throttle = new_throttle;
        }
        if (!plane.arming.is_armed_and_safety_off()) {
            current_throttle = 0;
        } else {
            // prevent motor shutdown
            _motors_active = true;
        }
        if (!quadplane.motor_test.running) {
            // the motors are all the way forward, start using them for fwd thrust
            const uint32_t mask = is_zero(current_throttle) ? 0U : tilt_mask.get();
            motors->output_motor_mask(current_throttle, mask, plane.rudder_dt);
        }
        return;
    }

if (!quadplane.in_vtol_mode() && (!plane.arming.is_armed_and_safety_off() || !quadplane.assisted_flight)) {

Bu parçacıktaki koşul şu;

Araç şu an vtol modundaysa ve arm edilmemişse veyahut yardımlı uçuş aktif değilse

ve sonrasında şu yorum satırları bizi doğrulamış olur

Tamamen sabit kanat modundayız. Eğilebilir motorları tamamen öne doğru hareket ettirin ve ileri motor olarak çalıştırın.
 // option set then if disarmed move to VTOL position to prevent ground strikes, allow tilt forward in manual mode for testing
 const bool disarmed_tilt_up = !plane.arming.is_armed_and_safety_off() && (plane.control_mode != &plane.mode_manual) && quadplane.option_is_set(QuadPlane::Option::DISARMED_TILT_UP);
 slew(disarmed_tilt_up ? 0.0 : get_forward_flight_tilt());

Bu parçacıktaki yorum satırı bize şunu söylüyor:

Seçenek aktifse; motorlar kilitliyken (disarm) pervanelerin yere çarpmasını önlemek için VTOL konumuna geç, test işlemleri için manuel modda motorların ileri doğru yatmasına izin ver.

Aslında baktığımız zaman, kodlardaki geliştirici kodları bizim bütün sorularımızın cevabını veriyor. Yorum satırlarındaki cümleleri kod satırlarındaki keywordlerle bağdaştırmaya çalışırsak gerçekten ilk bakışta kodun ne olduğunu anlayabiliyoruz.

Açık kaynak kodların yeni projelere adapte olabilmesinin sebebi bu işte. Geliştirici işini iyi yapıyorsa çok fazla irdelememize gerek kalmıyor. Hem kod bakımından hem de yorum satırlarındaki notlar bakımından.

Konumuza geri dönecek olursak bu if bloğu içerisinde olası farklı durumlarda kalkış esnasında çarpışma önleme(collision prevention) sağlanmakta.

    // remember the throttle level we're using for VTOL flight
    float motors_throttle = motors->get_throttle();
    max_change = tilt_max_change(motors_throttle<current_throttle);
    current_throttle = constrain_float(motors_throttle,
                                            current_throttle-max_change,
                                            current_throttle+max_change);

Yorum satırı bize şunu diyor;

VTOL uçuşu için kullandığımız gaz kelebeği seviyesini hatırlayın

Yani, geliştiricinin demek istediği şey; Motorların itki seviyesini %10'dan %70'e çıkardığımızda gazı aniden verip uçağın yalpalamasını önlemek için bunu yavaş yavaş, belli bir oranda yapalım, demek istiyor. 

Motorların güncel throttle bilgisini al, bir önceki throttle'dan düşükse maksimum değişikliği ona göre ayarla (fonksiyonun kendi içerisinde parametre kriterine göre hesaplama dönen bir sistem var)

Açıların, throttle(motor itkisi) ile bağları olduğunu buradan anlıyoruz. Yani uçak havadayken servoların açısını değiştiriyor bile olsak itki kendisini bu düzeneğe göre otomatikman ayarlar ve irtifa kaybı yaşamadan hedefimizi gerçekleştirmiş oluruz.

Örneği; servolar yatay hale gelmeye başladığı andan itibaren motor itkisi açıyla orantılı olarak artar. Yatay hale geldiğinde kriter tamamlanmış olur ve sabit kanat bilgisi uçuş kontrolcü tarafından alınmış olur. 

ARM tabanlı uçuş kontrol kartları genelde bu mantıkla işler, işlem belli bir mHz'de iteratif çalışır ve bu iteratif süreç boyunca tüm hareketlilikler oransal olarak hesaplanır (örneğin PID control).

    /*
      we are in a VTOL mode. We need to work out how much tilt is
      needed. There are 5 strategies we will use:

      1) With use of a forward throttle controlled by Q_FWD_THR_GAIN in
         VTOL modes except Q_AUTOTUNE determined by Q_FWD_THR_USE. We set the angle based on a calculated
         forward throttle.

      2) With manual forward throttle control we set the angle based on the
         RC input demanded forward throttle for QACRO, QSTABILIZE and QHOVER.

      3) Without a RC input or calculated forward throttle value, the angle
         will be set to zero in QAUTOTUNE, QACRO, QSTABILIZE and QHOVER.
         This enables these modes to be used as a safe recovery mode.

      4) In fixed wing assisted flight or velocity controlled modes we will
         set the angle based on the demanded forward throttle, with a maximum
         tilt given by Q_TILT_MAX. This relies on Q_FWD_THR_GAIN or Q_VFWD_GAIN
         being set.

      5) if we are in TRANSITION_TIMER mode then we are transitioning
         to forward flight and should put the rotors all the way forward
    */
VTOL modundayız. Ne kadar eğim gerektiğini hesaplamamız gerekiyor.
Kullanacağımız 5 strateji var:
 1) Q_FWD_THR_USE tarafından belirlenen Q_AUTOTUNE hariç VTOL modlarında Q_FWD_THR_GAIN tarafından kontrol edilen ileri gaz kelebeği kullanımı.
Hesaplanan ileri gaz kelebeğine göre açıyı ayarlıyoruz.
 2) Manuel ileri gaz kelebeği kontrolü ile QACRO, QSTABILIZE ve QHOVER için
hesaplanan ileri gaz kelebeğine göre açıyı ayarlıyoruz.
3) RC girişi veya hesaplanan ileri gaz değeri olmadan, açı
QAUTOTUNE, QACRO, QSTABILIZE ve QHOVER'da sıfıra ayarlanacaktır.
 Bu, bu modların güvenli kurtarma modu olarak kullanılmasını sağlar.
 4) Sabit kanatlı destekli uçuş veya hız kontrollü modlarda,
 talep edilen ileri gazı temel alarak, Q_TILT_MAX tarafından verilen maksimum
 eğime göre açıyı ayarlayacağız. Bu, Q_FWD_THR_GAIN veya Q_VFWD_GAIN
 ayarlarına bağlıdır.
5) TRANSITION_TIMER modundaysak, ileri uçuşa geçiyoruz demektir ve
   rotorları tamamen ileriye doğru

Bu uzun yorum satırının bize bahsettiği kod parçacığı şundan ibaret

#if QAUTOTUNE_ENABLED
    if (plane.control_mode == &plane.mode_qautotune) {
        slew(0);
        return;
    }
#endif

    if (!quadplane.assisted_flight &&
        quadplane.get_vfwd_method() == QuadPlane::ActiveFwdThr::NEW &&
        quadplane.is_flying_vtol())
    {
        // We are using the rotor tilt functionality controlled by Q_FWD_THR_GAIN which can
        // operate in all VTOL modes except Q_AUTOTUNE. Forward rotor tilt is used to produce
        // forward thrust equivalent to what would have been produced by a forward thrust motor
        // set to quadplane.forward_throttle_pct()
        const float fwd_g_demand = 0.01 * quadplane.forward_throttle_pct();
        const float fwd_tilt_deg = MIN(degrees(atanf(fwd_g_demand)), (float)max_angle_deg);
        slew(MIN(fwd_tilt_deg * (1/90.0), get_forward_flight_tilt()));
        return;
    } else if (!quadplane.assisted_flight &&
               (plane.control_mode == &plane.mode_qacro ||
               plane.control_mode == &plane.mode_qstabilize ||
               plane.control_mode == &plane.mode_qhover))
    {
        if (quadplane.rc_fwd_thr_ch == nullptr) {
            // no manual throttle control, set angle to zero
            slew(0);
        } else {
            // manual control of forward throttle up to max VTOL angle
            float settilt = 0.01f * quadplane.forward_throttle_pct();
            slew(MIN(settilt * max_angle_deg * (1/90.0), get_forward_flight_tilt())); 
        }
        return;
    }

    if (quadplane.assisted_flight &&
        transition->transition_state >= Tiltrotor_Transition::State::TIMER) {
        // we are transitioning to fixed wing - tilt the motors all
        // the way forward
        slew(get_forward_flight_tilt());
    } else {
        // until we have completed the transition we limit the tilt to
        // Q_TILT_MAX. Anything above 50% throttle gets
        // Q_TILT_MAX. Below 50% throttle we decrease linearly. This
        // relies heavily on Q_VFWD_GAIN being set appropriately.
       float settilt = constrain_float((SRV_Channels::get_output_scaled(SRV_Channel::k_throttle)-MAX(plane.aparm.throttle_min.get(),0)) * 0.02, 0, 1);
       slew(MIN(settilt * max_angle_deg * (1/90.0), get_forward_flight_tilt())); 
    }
yine her if bloğu içerisinde bize yeterince bilgi verilmiş. Örneğin;

else {
        // until we have completed the transition we limit the tilt to
        // Q_TILT_MAX. Anything above 50% throttle gets
        // Q_TILT_MAX. Below 50% throttle we decrease linearly. This
        // relies heavily on Q_VFWD_GAIN being set appropriately.
Geçiş (transition) süreci tamamlanana kadar, motor eğimini (tilt) Q_TILT_MAX değeri ile sınırlandırıyoruz. %50 gazın (throttle) üzerindeki her durumda motorlar direkt Q_TILT_MAX açısına gelir. %50 gazın altında ise açı, gaza bağlı olarak doğrusal (lineer) bir şekilde azalır. Bu mekanizmanın sağlıklı çalışması, Q_VFWD_GAIN parametresinin doğru ayarlanmış olmasına büyük ölçüde bağlıdır.

void Tiltrotor::vectoring(void): 

Bu kısımda "thrust vectoring control" kavramı yapılmakta, peki nedir bu thrust vectoring control?

Thrust Vectoring Control: 

İtme vektörleme, diğer adıyla  itme vektör kontrolü (TVC), bir uçağın, duruşunu veya açısal hızını kontrol etmek için motorlarından gelen itme kuvvetinin yönünü değiştirme yeteneğidir.[1][2][3]
constexpr uint32_t TILT_DELAY_MS = 3000;
if (!plane.arming.is_armed_and_safety_off() && plane.quadplane.option_is_set(QuadPlane::Option::DISARMED_TILT)) {
    if ((now - hal.util->get_last_armed_change()) > TILT_DELAY_MS) {

        float yaw_out = plane.channel_rudder->get_control_in();
        yaw_out /= plane.channel_rudder->get_range();
        
        SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output + yaw_out));
    }
}

Burada amaç, motorlar çalışmazken servoların düzgün çalışıp çalışmadığını kontrol etmek ve pervanelerin durmasını beklemek.

constexpr uint32_t TILT_DELAY_MS = 3000;

3 saniye bekleme süresi (Pervanelerin durması için)

float yaw_out = plane.channel_rudder->get_control_in();
yaw_out /= plane.channel_rudder->get_range();

Kumandanın rudder kolunu oku ve servoya ver

SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output + yaw_out));

Sol motoru Rudder'a göre yatır (Test amaçlı), muhtemelen hesaplamaları pwm sinyaline dönüştüren bir ölçekleme yapılıyor (SRV_channels::set_output_scaled)


const float scaler = (quadplane.FW_vector_throttle_scaling() / plane.get_speed_scaler());
const float gain = fixed_gain * fixed_tilt_limit * scaler;

const float right = gain * SRV_Channels::get_output_scaled(SRV_Channel::k_elevon_right) * (1/4500.0);
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output - right));

Uçak moduna geçtiğinde (motorlar yatayken), artık bir uçak gibi manevra yapması lazım ama bazen kanatlar(surface) yetmez.

const float scaler = (quadplane.FW_vector_throttle_scaling() / plane.get_speed_scaler());
const float gain = fixed_gain * fixed_tilt_limit * scaler;

Hız ölçeklendirmesi (Uçak yavaşsa motorlar daha çok yatar)

const float right = gain * SRV_Channels::get_output_scaled(SRV_Channel::k_elevon_right) * (1/4500.0);
SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output - right));

Elevon komutunu alıp motorun açısına ekliyoruz


throttle_scaler = motors->get_throttle_hover() / throttle;

const float tilt_rad = radians(current_tilt * 90);
float tilt_scale = throttle_scaler * yaw_out * cosf(tilt_rad) + avg_roll_factor * roll_out * sinf(tilt_rad);

SRV_Channels::set_output_scaled(SRV_Channel::k_tiltMotorLeft, 1000 * (base_output + tilt_scale * yaw_range));

Burası işin en "hala anlamadığım ve anlamaya çalışırken terleten" matematik kısmı. Tahminimizdir ki burası hover moddayken tilt açılarının hesabı ile ilgileniyor olabilir.

Özetle bu kadar kodu baştan sona okumamıza gerek yok, zaten geliştirici kodu yeterince açık bir şekilde yazmış, yeterince yorum satırı eklemiştir. Bunu yapmamızdaki tek fayda bir uçuş kontrolcünün firmware'i nasıl çalışır bunu anlamak. 

Furkan hocama selamlar.


 

Kaynakçalar:

1  - "Vectored thrust". Glenn Research Center, NASA. Archived from the original on 5 August 2024. Retrieved 7 August 2024.

2 - "Vertical Take Off and Landing (VTOL) Aircraft with Vectored Thrust for Control and Continuously Variable Pitch Attitude in Hover". technology.nasa.gov. Retrieved 7 August 2024.

3 - "Gimbaled Thrust Interactive". Glenn Research Center, NASA. Archived from the original on 22 July 2024. Retrieved 7 August 2024.

Video Kaynaklar:
How to setup Arduplane for Tiltrotor VTOL

The Twins Cargo Tilt-Rotor VTOL Aircraft - Test Hovers










 






Geri Bildirim

Makaleyi beğendiniz mi?

Görüşleriniz, yazar ve topluluk için değerlidir.