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 :
- 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. -
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 lefuncMapdu profil — voir la Référence des mixeurs .Ré-épingler un modèle ne touche jamais aux calculs du mixeur. - 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.
- 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 :
- Porte de battement avec hystérésis. Les gaz doivent franchir le seuil de battement (brut ≈303 par défaut, ≈1,08 ms équivalent PWM) pour entrer en battement ; on n'en sort que sous le seuil − 50 d'hystérésis, si bien qu'un gaz marginal ne fait jamais claquer les ailes.
- Fréquence. Un FlappingOscillator avance avec le temps réel écoulé (horloge µs, dt plafonné à 100 ms). La cadence cible mélange le canal CH6 de fréquence de battement et les gaz via le
throttleFrequencyMixdu profil, entre 0,5 Hz…flapBaseFreq/10. - Amplitude. Pourcentage de gaz du plafond de vitesse du servo
ampMax = degPerSec / (2·f)(avecdegPerSec = 60°/servoSpeed_ms), plafonné dur à 55° avant multiplicateur. L'amplitude s'effondre donc naturellement à haute fréquence — l'aile n'exige jamais plus que ce que le servo peut fournir. - Férocité. Les férocités de course descendante (puissance) et montante (récupération) sont façonnées par course, modulées asymétriquement par la profondeur (tirer durcit la descente, pousser durcit la montée) et par le couplage des gaz, puis bornées 0…8 avant la fonction de forme.
- Différentiels de lacet. Le gouvernail ajoute une férocité opposée G/D (
rudderFerocityRange) et une amplitude opposée G/D (rudderAmplitudeDifferential) tandis que la phase d'inversion de course reste partagée entre les ailes — l'autorité de virage vient d'un dwell asymétrique, pas d'ailes désynchronisées. - Géométrie & sortie. L'onde façonnée −1…+1 est mise à l'échelle en degrés, mélangée au pilotage aileron/profondeur (autorité ±60° × échelle), décalée par le trim de centre de vol plané/battement, bornée 0…180°, puis convertie en µs via l'enveloppe configurable
servoMinUs…servoMaxUs(défauts 988…2012) avec trim par fonction. - Branche de vol plané. Sous le seuil de battement, l'oscillateur décroît et les ailes tiennent un angle de plané statique — un état idéal de banc et de lancement.
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 :
- mappe les gaz (armé) vers une commande ESC de 1000…2000 µs (
SF_MOTOR), ralenti au désarmement ; - mélange l'empennage : les profils en V utilisent un mélange élevon (aileron+profondeur / aileron−profondeur), les profils classiques une surface de profondeur séparée ;
- laisse Zephyrus (quand activé) injecter des corrections PID de roulis et tangage dans les servos d'empennage, bornées à ±250 µs (
ZEPHYR_GEARBOX_CLAMP_US) pour qu'un glitch de capteur ne cale jamais un servo ; - conserve le même mixeur de gouverne et la même enveloppe µs/trim/borne que le noyau forme d'onde.
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.