Flight controller jest systemem czasu rzeczywistego, ponieważ poprawny wynik dostarczony za późno może być równie bezużyteczny jak wynik błędny. Harmonogram musi przeprowadzić świeżą próbkę IMU przez estymację, regulator i wyjście aktuatorów przed deadline, równocześnie obsługując wolniejsze sensory, łączność, logowanie i diagnostykę. Kluczowe są najgorsze czasy, jitter i blokowanie, nie średnie obciążenie CPU widoczne na stole.

Spis treści#

Czas rzeczywisty nie oznacza „szybko”#

System czasu rzeczywistego ma przewidywalną granicę odpowiedzi. Pętla wykonująca się zwykle w 100 µs, ale raz na minutę blokująca na 20 ms, jest gorsza dla sterowania od pętli o stabilnym czasie 300 µs, jeśli deadline wynosi 1 ms.

Hard real-time oznacza, że przekroczenie deadline jest niedopuszczalne z punktu widzenia wymagania. Soft real-time toleruje okazjonalne spóźnienie z degradacją jakości. W FC wewnętrzna rate loop i publikacja aktuatorów są bliżej hard real-time, telemetria i interfejs użytkownika — soft real-time, a logowanie surowych danych może mieć kontrolowaną utratę.

Nie oznacza to, że hobbystyczny autopilot ma formalnie udowodnione WCET. Oznacza, że architektura rozdziela krytyczność, mierzy najgorsze czasy i ma zachowanie po przekroczeniu.

Łańcuch przyczynowy sterowania#

Ważny jest czas end-to-end:

fizyczna próbka IMU
 -> data-ready
 -> transfer SPI/DMA
 -> korekcja i filtracja
 -> estymator orientacji
 -> regulator rate
 -> mixer/control allocation
 -> serializacja DShot/PWM/CAN
 -> reakcja ESC/silnika

Każdy etap ma własne oczekiwanie, wykonanie i bufor. Suma średnich nie opisuje najgorszego opóźnienia, ponieważ fazy mogą się niekorzystnie ułożyć. Próbka przychodząca tuż po zakończeniu cyklicznego zadania czeka niemal cały okres.

Numer generacji próbki powinien przejść przez tor. Pozwala zmierzyć, która próbka IMU wyprodukowała które wyjście. Sam timestamp uruchomienia regulatora nie wykrywa, że regulator ponownie użył starego stanu.

Okres, deadline, WCET i jitter#

Dla zadania i zapisuje się:

  • T_i — okres lub minimalny odstęp zdarzeń;
  • D_i — względny deadline;
  • C_i — worst-case execution time;
  • R_i — najgorszy czas odpowiedzi wraz z interferencją;
  • J_i — jitter aktywacji lub zakończenia;
  • B_i — maksymalny czas blokowania przez zasób.

Warunek użyteczny ma postać R_i ≤ D_i, nie tylko C_i ≤ T_i. Czas odpowiedzi obejmuje wykonanie zadań wyższego priorytetu, blokowanie i opóźnienia scheduler-a.

WCET nie jest największą wartością z krótkiego lotu. Pomiar daje observed maximum. Do budżetu dodaje się ścieżki błędów, cache miss, konflikt DMA, przerwania oraz margines. Formalne WCET na współczesnym MCU z cache jest trudne, więc praktyka łączy ograniczoną konstrukcję z agresywnymi testami.

Jitter pętli zmienia efektywne dt. Regulator i filtry powinny używać zmierzonego czasu próbki w dopuszczalnym zakresie, ale zmienne dt nie jest usprawiedliwieniem dowolnego jitteru. Po przekroczeniu granicy stan jest odrzucany lub system przechodzi w degradację.

Klasy zadań FC#

Przykładowy podział:

Klasa Wyzwalanie Typowa krytyczność Polityka przeciążenia
IMU acquisition data-ready krytyczna zachowaj świeżość, licz utraty
rate control nowy stan/okres krytyczna deadline/failsafe
attitude/position okres/obserwacje wysoka kontrolowany skip pojedynczej korekcji
actuator output po regulatorze krytyczna watchdog wyjścia
GNSS/baro/mag zdarzenie wysoka/średnia kolejka ograniczona, odrzuć stare
RC/failsafe zdarzenie + timeout krytyczna najnowsza ramka, stały limit wieku
telemetry budżetowana niska ogranicz rate/drop
logging buforowana niska drop z licznikiem
parameter/storage serwisowa niska zakaz blokowania w locie

Częstotliwość nie wyznacza sama priorytetu. Rzadki sygnał failsafe może mieć krótki deadline, a szybki strumień debugowy — niski priorytet.

Harmonogram cykliczny#

Time-triggered superloop uruchamia funkcje według tabeli ticków. Jest prosty, deterministyczny i dobrze działa na małym MCU, jeśli wszystkie funkcje są nieblokujące.

co 1 tick:  IMU -> estimator -> rate -> outputs
co 4 tick:  attitude/navigation
co 10 tick: RC health, power
co 20 tick: telemetry scheduler
co 100 tick: housekeeping

Zaleta to jawny budżet i brak wielu stosów. Wada: jedno przekroczenie opóźnia wszystkie późniejsze funkcje, a zdarzenie tuż po swoim slocie czeka. Tabela musi uwzględniać fazy, aby kilka ciężkich funkcji nie startowało razem.

ArduPilot AP_Scheduler jest przykładem budżetowanego scheduler-a pętli głównej: zadania mają rates, priorytety i oczekiwany maksymalny czas; scheduler wybiera zadania mieszczące się w pozostałym budżecie i rejestruje slips/overruns. Źródłem prawdy jest aktualny kod, ponieważ szczegóły ewoluują.

RTOS z priorytetami stałymi#

Preemptive RTOS pozwala zadaniu wyższego priorytetu przerwać niższe. FreeRTOS wybiera najwyższe gotowe zadanie; time slicing między równymi priorytetami jest osobnym ustawieniem.

Zadanie periodyczne powinno blokować się do absolutnego terminu, a nie wykonywać delay(period) po zakończeniu pracy. W przeciwnym razie czas wykonania dodaje się do okresu i faza dryfuje.

TickType_t next = xTaskGetTickCount();
for (;;) {
    vTaskDelayUntil(&next, period_ticks);
    run_bounded_cycle();
}

Dla częstotliwości kHz tick RTOS może być zbyt gruby. Data-ready IRQ, high-resolution timer i dedykowana work queue/loop dają dokładniejsze wyzwalanie. Nie należy zwiększać tick rate bez policzenia kosztu przerwań.

Stałe priorytety są analizowalne, ale biblioteka, sterownik i ISR muszą respektować reguły. Ukryty mutex w printf może wprowadzić nieprzewidziane blokowanie.

Zdarzenia zamiast odpytywania#

Polling w stałym okresie jest prosty, lecz dodaje od zera do jednego okresu latency. Sensor z pinem data-ready powinien aktywować odczyt zdarzeniem. DMA kończy transfer, a publikacja nowej próbki budzi estymator.

GPIO DRDY ISR
  -> timestamp hardware
  -> schedule SPI transaction
DMA complete
  -> validate/FIFO decode
  -> publish sample generation N
estimator callback/task
  -> update state N
control task
  -> publish output N

Event-driven nie oznacza nieograniczonego tworzenia pracy. Jeśli producent jest szybszy od konsumenta, kolejka rośnie. Dla bieżącego stanu często właściwa jest polityka „najnowsza próbka + licznik pominiętych”, podczas gdy integracja IMU może wymagać przetworzenia wszystkich rekordów FIFO.

Każdy kanał ma bounded queue i jawne zachowanie overflow. Dynamiczna alokacja wiadomości na ścieżce krytycznej utrudnia granice czasu i fragmentuje heap.

Work queues#

PX4 rozróżnia niezależne tasks oraz work queue tasks współdzielące wątek, stos i priorytet. Work item może być uruchamiany po czasie lub callbackiem aktualizacji uORB. Oszczędza RAM i przełączenia kontekstu.

Współdzielona kolejka jest kooperacyjna: jeden work item nie może blokować, spać ani wykonywać długiego I/O, bo zatrzyma inne elementy tej samej kolejki. Ciężka funkcja trafia do osobnego tasku lub innej kolejki.

Dobór kolejki jest częścią architektury krytyczności. Driver IMU i rate controller mogą współdzielić kolejkę zapewniającą kolejność i małe opóźnienie, ale logger nie powinien jej blokować. Nazwa modułu nie określa automatycznie właściwego priorytetu.

Monitoring musi raportować work items, nie tylko wątki. Obciążenie wątku 80% nie pokazuje, który element spowodował deadline miss.

Priorytety#

Priorytet wynika z deadline i zależności. Ogólna reguła rate-monotonic daje wyższy priorytet krótszym okresom przy określonych założeniach, ale FC ma zadania sporadyczne, wspólne zasoby i pipeline, więc potrzebna jest analiza konkretnego zestawu.

Praktyczna kolejność:

  1. IRQ timestamp/data movement o minimalnej długości;
  2. acquisition głównego IMU;
  3. estymacja i rate control;
  4. publikacja aktuatorów oraz failsafe input;
  5. wolniejsze sensory/nawigacja;
  6. protokoły i telemetria krytyczna;
  7. logowanie i housekeeping.

ISR nie powinien wykonywać algorytmu EKF. Najwyższy priorytet służy zabezpieczeniu czasu i danych, po czym budzi zadanie. Długi ISR blokuje wszystkie zadania niezależnie od ich priorytetów.

Zbyt wiele poziomów utrudnia analizę. Dwa zadania o równym priorytecie mogą dzielić time slice w sposób zależny od ticka; jeśli wymagana jest kolejność, lepiej użyć jednego pipeline albo jawnego zdarzenia.

Priority inversion#

Priority inversion zachodzi, gdy wysokie zadanie czeka na mutex trzymany przez niskie, a średnie zadania wypierają posiadacza. Priority inheritance chwilowo podnosi priorytet posiadacza, ale nie usuwa czasu jego sekcji krytycznej.

Rozwiązania:

  • nie współdzielić zasobu na ścieżce krytycznej;
  • przekazywać dane wiadomościami lub snapshotem;
  • utrzymywać sekcję bardzo krótką i bez I/O;
  • używać mutexu z priority inheritance, nie binarnego semafora udającego mutex;
  • stosować priority ceiling, jeśli RTOS/projekt go wspiera;
  • mierzyć maksymalny czas oczekiwania.

Przykład błędny: telemetry task blokuje mutex loggera, zapisuje sektor SD, a rate task próbuje dopisać komunikat diagnostyczny pod tym samym mutexem. Poprawna ścieżka rate tylko umieszcza rekord w bounded lock-free/szybkiej kolejce lub odrzuca go.

Sekcje krytyczne i przerwania#

Wyłączenie przerwań jest globalnym blokowaniem. Kilka mikrosekund w sterowniku może być akceptowalne, ale kasowanie flash albo kopiowanie dużego bufora nie. Maksymalny czas maskowania IRQ mierzy się hardware timerem.

Priorytety NVIC i mechanizm RTOS mają osobne konwencje numeryczne. W Cortex-M niższa liczba NVIC zwykle oznacza wyższy priorytet; konfiguracja configMAX_SYSCALL_INTERRUPT_PRIORITY ogranicza, które ISR mogą wołać API RTOS. Błąd może uszkodzić listy scheduler-a.

W ISR używa się wariantów FromISR, wykonuje minimalną pracę i żąda przełączenia kontekstu tylko gdy obudzono ważniejsze zadanie. Stos przerwań również ma limit.

Atomowy zapis 32-bitowy nie oznacza spójnego snapshotu kilku pól. Stosuje się seqlock, double buffer lub sekcję krytyczną o zmierzonej długości.

DMA nie jest „za darmo”#

DMA odciąża CPU od kopiowania, ale konkuruje o SRAM, cache i bus matrix. Równoczesny SPI RX, DShot, ADC i zapis pamięci mogą opóźnić dostęp rdzenia.

Plan określa:

  • mapę requestów DMA i konflikty kanałów;
  • priorytet arbitra DMA;
  • regiony pamięci dostępne dla danego kontrolera;
  • cache clean/invalidate;
  • lifetime i wyrównanie bufora;
  • maksymalny burst;
  • completion timeout i recovery.

Callback „DMA complete” nie zawsze jest końcem transmisji fizycznej. SPI może nadal przesuwać bit, UART wysyłać stop, a timer DShot dopiero kończyć sekwencję.

Duży DMA o niskim priorytecie porcjuje się tak, aby nie blokował pamięci przez długi burst. Obciążenie testuje się z wszystkimi kanałami jednocześnie.

Pamięć, cache i magistrale#

WCET zależy od lokalizacji kodu i danych. ITCM/DTCM może dawać przewidywalniejszy dostęp, ale część DMA go nie widzi. SRAM w innej domenie może mieć większą latencję. Cache miss i write-back tworzą bursty.

Krytyczny kod oraz małe struktury można umieścić w deterministycznej pamięci po sprawdzeniu linker map. Nie przenosi się tam wszystkiego bez budżetu pojemności.

Heap w locie wprowadza zmienny czas i ryzyko fragmentacji. Duże bufory, kolejki i stosy przydziela się statycznie. Watermark stosu jest mierzony w najgorszych ścieżkach, w tym błędach i zagnieżdżonych przerwaniach.

Fałszywe współdzielenie cache i bariery mają znaczenie na MCU wielordzeniowym. RP2040 czy H7 dual-core wymagają jawnej własności danych i mechanizmu komunikacji; drugi rdzeń nie jest automatycznym „darmowym schedulerem”.

Budżet pętli#

Dla pętli 1 kHz okres wynosi 1000 µs. Przykładowy budżet projektowy, nie uniwersalna recepta:

Etap Budżet Uwaga
acquisition/decode IMU 120 µs wraz z oczekiwaniem magistrali
estymacja orientacji 220 µs najgorsza ścieżka korekcji
rate controller 80 µs z filtrami i sanity checks
allocation/output 100 µs do rozpoczęcia fizycznej ramki
interferencja ISR 120 µs wszystkie źródła
blokowanie 60 µs udowodnione maksimum
margines 300 µs wzrost funkcji i niepewność

Budżet nie powinien sumować się dokładnie do 100%. Margines jest wymaganiem. Jeśli typowe obciążenie to 20%, ale worst-case 105%, system nie jest schedulable.

Wielorate pipeline może uruchamiać gyro/rate 4 kHz, attitude 1 kHz i position 50 Hz. Przejścia między rates wymagają jawnego sample-and-hold, filtracji oraz timestampów.

Overrun i load shedding#

Overrun jest zdarzeniem, nie tylko liczbą w debug. Scheduler zapisuje zadanie, start, elapsed, poprzedni stan i wpływ na kolejne deadline.

Reakcja wielostopniowa:

  1. odrzuć niekrytyczny log/telemetrię;
  2. zmniejsz rate pracy tła;
  3. anuluj transfer pliku/parametrów;
  4. ogranicz algorytm opcjonalny według wcześniej testowanego profilu;
  5. przy utracie krytycznego toru uruchom failsafe/watchdog.

Nie wolno dynamicznie wyłączać losowych części EKF tylko po to, by „odzyskać CPU”, jeśli taki wariant nie został zweryfikowany. Profile degradacji są zdefiniowane przed lotem.

Load shedding ma histerezę i minimalny czas powrotu. Inaczej system oscyluje między trybami co okres. Powrót funkcji następuje po stabilnym zapasie, nie po jednym szybkim cyklu.

Pomiar runtime#

Cycle counter DWT lub timer wysokiej rozdzielczości mierzy wejście/wyjście każdego krytycznego elementu. Histogram jest lepszy od samej średniej i maksimum: pokazuje ogon rozkładu oraz zmianę po włączeniu funkcji.

Metryki:

  • execution min/max/percentyle;
  • response time od zdarzenia;
  • release jitter i completion jitter;
  • deadline misses;
  • queue depth/overflow;
  • czas maskowania IRQ;
  • czas blokowania mutexu;
  • CPU idle/spare budget;
  • numer próbki end-to-end;
  • maksymalny wiek danych wejściowych.

Instrumentacja musi mieć ograniczony koszt. Trace zapisuje kompaktowy rekord do RAM, a nie wykonuje printf w każdej pętli. Narzut mierzy się i odejmuje tylko wtedy, gdy jest stabilny.

Wrap timera obsługuje arytmetyka unsigned. Synchronizacja timerów między rdzeniami wymaga wspólnego źródła albo kalibracji.

Synchronizacja czasu próbki#

Scheduler nie może utożsamiać czasu callbacku z czasem fizycznej próbki. IMU może buforować rekordy, GNSS serializować wiadomość dziesiątki ms, a CAN czekać na arbitraż.

Hierarchia timestampu:

  1. timestamp sprzętowy data-ready/PPS;
  2. licznik czasu wewnątrz sensora skorelowany z MCU;
  3. rekonstrukcja z ODR i kolejności FIFO;
  4. czas początku/końca transferu z korekcją znanego opóźnienia;
  5. callback software jako najsłabsze przybliżenie.

Estymator otrzymuje measurement time i arrival time. Opóźnione obserwacje są aplikowane do właściwego stanu lub odrzucane po przekroczeniu okna historii.

Zmiana źródła zegara albo reset sensora tworzy nową generację mapowania czasu. Nie wolno kontynuować ekstrapolacji przez nieciągłość licznika.

Projekt bare-metal#

Minimalny FC na AVR/8051 może nie mieć RTOS, ale nadal realizuje te same kontrakty. Timer ustala bazowy tick, ISR buforują zdarzenia, a superloop wykonuje bounded state machines.

Każda funkcja ma:

  • warunek gotowości;
  • maksymalną porcję pracy na wywołanie;
  • deadline i timeout;
  • stan zamiast blokującego oczekiwania;
  • licznik postępu;
  • reakcję overflow.

Operacje matematyczne dobiera się do MCU: fixed-point, lookup tables i rozłożenie obliczeń wolnego filtra między tickami. Upraszczanie nie może usunąć kontroli zakresu i saturacji.

Historyczna platforma jest dobrym narzędziem edukacyjnym, ale ma mniejszy zapas i mniej sprzętowej izolacji. Pomiar pinem GPIO na oscyloskopie często jest najprostszym profilerem WCET.

Projekt z RTOS#

RTOS daje priorytety, timeouty, kolejki i osobne stosy, ale zwiększa liczbę stanów. Zalecenia:

  • statyczne tworzenie krytycznych zadań i kolejek;
  • blokowanie na zdarzeniu zamiast spin;
  • absolutne okresy;
  • mutexy tylko dla krótkich zasobów;
  • brak I/O w shared high-priority work queue;
  • jawna granica ISR/task;
  • stack watermark i MPU, jeśli dostępne;
  • supervisor monitorujący postęp, nie tylko stan scheduler-a.

Tickless idle jest użyteczny dla energooszczędnego systemu, lecz flight stack w locie zwykle ma częste deadline. Przejście w low power nie może przestawić wspólnej osi czasu ani pominąć timerów aktuatorów.

Scheduler configuration jest wersjonowany razem z firmware. Zmiana priorytetu jednego tasku wymaga ponownego testu pełnego obciążenia.

Testy przeciążeniowe#

Testy wykonuje się najpierw w unit/SITL, potem HIL i na stanowisku z rzeczywistymi peryferiami. Scenariusze:

  • maksymalny rate wszystkich sensorów;
  • równoczesny zapis logu i transfer parametrów;
  • pełne UART/CAN i retransmisje;
  • cache cold oraz duże DMA;
  • błędy magistral prowadzące do timeout/recovery;
  • brak karty, pełna pamięć i wolny erase;
  • burst GNSS/telemetrii;
  • długie critical sections wstrzyknięte testowo;
  • sztuczne WCET każdego tasku;
  • przepełnienie każdej kolejki;
  • wrap timerów i liczników;
  • przełączenie sensora/estymatora;
  • brownout i restart peryferium.

Kryterium to brak deadline miss krytycznej ścieżki i zgodna reakcja load shedding. „Nie zawiesił się przez godzinę” jest zbyt słabe.

Regresja przechowuje histogramy i limity dla konkretnej płytki/kompilacji. Optymalizacja kompilatora, logging i zmiana częstotliwości MCU mogą zmienić timing bez zmiany algorytmu.

Lista projektowa#

  • Każde zadanie ma okres/minimum inter-arrival, deadline i observed WCET.
  • Zmierzono response time end-to-end od próbki do aktuatora.
  • Priorytety wynikają z deadline i zależności.
  • ISR są krótkie i nie wykonują algorytmów wysokiego poziomu.
  • Wszystkie kolejki są ograniczone i mają politykę overflow.
  • Blokujące I/O nie działa na krytycznej work queue.
  • Mutexy mają zmierzony maksymalny czas i właściwe dziedziczenie priorytetu.
  • DMA, cache i bus matrix są częścią testu obciążenia.
  • Timestamp opisuje pomiar, nie przypadkowy callback.
  • Overrun uruchamia zdefiniowany load shedding.
  • Watchdog zależy od postępu całego pipeline.
  • Heap i stos mają potwierdzony budżet.
  • Testy obejmują błędy i recovery, nie tylko nominalny lot.
  • Konfiguracja scheduler-a jest wersjonowana i testowana per platforma.

Powiązane tematy#

Przypisy#

  1. FreeRTOS, Task scheduling — model priorytetów, preempcji i time slicing.
  2. FreeRTOS, Kernel books and reference manuals — zadania, synchronizacja, mutexy i mechanizmy RTOS.
  3. PX4, Architectural Overview — tasks, work queues, uORB i ograniczenia pracy współdzielonej.
  4. PX4, Module Template for Full Applications — implementacja ScheduledWorkItem i sposoby aktywacji work item.
  5. ArduPilot, AP_Scheduler source — implementacja rates, budżetów, priorytetów, slips i overruns w otwartym autopilocie.

Utworzono: 15 sierpnia 2026. Ostatnia aktualizacja: 15 sierpnia 2026. Źródła zweryfikowano: 15 sierpnia 2026.

Źródła z centralnego rejestru

  1. FreeRTOS: task scheduling [dokumentacja projektu]
  2. FreeRTOS Reference Manual [dokumentacja projektu]
  3. PX4 Architectural Overview [dokumentacja]
  4. PX4: module and work queue task template [dokumentacja projektu]
  5. ArduPilot: AP_Scheduler source [repozytorium open source]