Architecture : kernels et flux de données

Comment un flux CRSF devient battement d'aile et empennage : frontières des modules, les deux kernels de mixage et la machine d'état qui garde le ptérosaure en vie quand le lien meurt.

Flux du signal

PteronautOS est un fork d'ExpressLRS 4.x qui a remplacé la « passe PWM » en bout de récepteur par une couche dédiée de commande de vol. Le chemin des données est volontairement court :

CRSF radio link
  │  2.4 GHz SX1280
  ▼
rx_main / CRSF decoder        → ChannelData[]  (raw 172…1811 µs-window values)
  ▼
Ornithopter::update()          → _readChannels() → voice* fields
  ▼  kernel select: activeProfile ≥ GEARBOX_2VTAIL_1RUD ?
_computeServoMixer()   OR   _computeGearboxMixer()
  ▼
_f[]  (indexed by ServoFunc tag, µs)
  ▼
PWM output stage               → funcMap channels → wing / rudder / motor servos

Quatre idées structurelles gardent la bête stable et le code petit :

  1. Les canaux sont normalisés tôt. Chaque valeur brute de la fenêtre CRSF 172–1811 est mappée une fois vers −1…+1 (_crsfToNorm) ou vers un pourcentage, et seule cette unité traverse les mixeurs — aucune constante magique répétée.
  2. Les sorties sont étiquetées, pas épinglées. Les mixeurs écrivent dans _f[ServoFunc]. Quelle sortie PWM physique pilote une étiquette est décidé plus tard par le funcMap du profil — voir la Référence des mixeurs .Ré-épingler un modèle ne touche jamais aux calculs du mixeur.
  3. Le noyau est une famille à la compilation, un choix à l'exécution. Le batteur à forme d'onde et le contrôleur de boîte de vitesses vivent tous deux dans le binaire ; le profil actif choisit le noyau à chaque tick de mise à jour.
  4. Le failsafe n'est pas un événement, c'est un état. Perte de liaison, mode banc et surcharge des manches sont des portes explicites dans update(), si bien que les servos ne peuvent jamais s'emballer pendant que la radio est silencieuse.

Deux kernels : onde et boîte à engrenages

Un noyau est le bloc qui transforme des canaux normalisés en µs de fonction de servo. PteronautOS a deux familles, distinguées par le numéro de profil du mixeur :

Famille Profils Actionneurs Archétype de modèle
Noyau forme d'onde (servo) 0 SERVO_2WING · 1 SERVO_2WING_1RUD · 2 SERVO_4WING Servos d'aile battante (2–4), gouvernail de crête Ornithoptère à servo direct — le PteronautOS classique
Noyau boîte de vitesses 3 GEARBOX_2VTAIL_1RUD · 4 GEARBOX_1MOT_2VTAIL · 5 GEARBOX_1MOT_2VTAIL_1RUD · 6 GEARBOX_1ELE_1RUD · 7 GEARBOX_1MOT_1ELE_1RUD Moteur ESC, empennage en V ou profondeur + gouverne Batteur motorisé / à engrenages avec empennages classiques

La sélection se fait dans update() par l'unique test PROFILE_IS_GEARBOX (activeProfile ≥ 3). Le profil lui-même est une valeur d'exécution : il part du drapeau de build MIXER_PROFILE (l'ancien ORNITHOPTER_GEARBOX=1 mappe toujours vers le profil 5) mais peut être changé en direct depuis la WebUI via setOrnithopterProfile() — le firmware recible une nouvelle cellule sans reflasher. Il y a huit profils au total ; leurs dispositions funcMap exactes sont tabulées dans la Référence des mixeurs.

Kernel onde (battement par servos)

Le noyau forme d'onde est ce que la plupart entendent par « PteronautOS » : deux servos d'aile exécutent une oscillation à phase continue dont la forme est sculptée à chaque cycle. Son anatomie :

Kernel boîte à engrenages (moteur + empennages)

Le noyau boîte de vitesses troque le pur battement d'aile contre une transmission motorisée plus des empennages classiques. Comme un moteur tourne en continu, ce noyau est sans état à chaque tick — pas d'oscillateur — et à la place :

Un même ornithoptère peut même porter les deux personnalités : lancement battant avec le noyau forme d'onde, croisière sur la boîte de vitesses — le changement de profil en cours d'exécution est cette unique ligne dans update().

États : lien, banc et failsafe

update() est appelé à une cadence fixe d'~10 ms depuis la boucle WiFi/servo et vérifie trois drapeaux avant qu'aucun mixeur ne s'exécute :

État Condition Comportement
Vol normal linkUp + canal d'armement actif La sortie complète du noyau suit les manches
Mode banc / panneau benchMode (panneau WebUI, sans liaison RC) Des manches neutres sont injectés, gaz parqués sous le seuil de battement — les réglages de plané + trim sont visibles en direct
Surcharge des manches stickOverride (canaux virtuels) Les valeurs de canaux injectées par le firmware remplacent entièrement le CRSF (balayages servo, tests)
Failsafe liaison perdue (onLinkDown / enterFailsafe) Toutes les fonctions se centrent à 1500 µs, oscillateur réinitialisé, moteur → min ; la perte de paquets CRSF centre aussi au niveau RX

Le comportement de centrage à la perte est volontairement dupliqué sur deux couches : la pile CRSF met les données de canaux en failsafe, et la couche Ornithopter met la géométrie en failsafe (y compris l'état d'oscillation). Une radio morte laisse donc une cellule planante et contrôlable — jamais une cellule qui bat l'air follement.

Carte des sources

Module Chemin Responsabilité
Couche Ornithopter src/lib/Ornithopter/ Sélection du noyau, calcul des mixeurs, oscillateur de forme d'onde, profils, trim
Profils de vol & constantes src/lib/Ornithopter/OrnithopterConfig.h Enum MixerProfile, étiquettes ServoFunc, funcMap, tous les défauts numériques
Oscillateur / forme src/lib/Ornithopter/OrnithopterWaveform.h Intégration de phase, façonnage de férocité, logique d'inversion
Zephyrus (optionnel) src/lib/Zephyrus/ MPU6050 + AHRS Mahony, PID double, correction de crête/empennage
Étage de sortie PWM src/lib/ServoOutput/devServoOutput.cpp Écrit des µs synchronisés sur le tick CRSF
Configuration WebUI src/lib/WIFI/devWIFI.cpp Profil d'exécution + persistance du réglage (/pteronautos)

La couche du dessous — exactement quelle forme d'onde produisent les nombres et comment chaque profil relie les fonctions aux sorties PWM — vit dans la Référence des mixeurs , et l'enveloppe de temporisation servo est documentée dans l'article Noyau servo article.