Arquitectura: kernels y flujo de datos
Cómo un flujo CRSF se convierte en aleteo y cola: límites de los módulos, los dos kernels de mezcla y la máquina de estados que mantiene vivo al pterosaurio cuando el enlace muere.
Flujo de señal
PteronautOS es un fork de ExpressLRS 4.x que sustituyó la cola de «paso directo PWM» del receptor por una capa dedicada de control de vuelo. La ruta de datos es deliberadamente corta:
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
Cuatro ideas estructurales mantienen a la bestia estable y el código pequeño:
- Los canales se normalizan pronto. Cada valor crudo de la ventana CRSF 172–1811 se mapea una vez a −1…+1 (
_crsfToNorm) o a un porcentaje, y sólo esa unidad fluye por los mezcladores — sin constantes mágicas repetidas. -
Las salidas se etiquetan, no se fijan. Los mezcladores escriben en
_f[ServoFunc]. Qué salida PWM física comanda cada etiqueta lo decide después elfuncMapdel perfil — véase la Referencia de Mezcladores .Reasignar un modelo nunca toca la matemática del mezclador. - El kernel es una familia en tiempo de compilación y una elección en tiempo de ejecución. Tanto el aleteo por forma de onda como el controlador de caja de engranajes viven en el binario; el perfil activo elige el kernel en cada tick.
- El failsafe no es un evento, es un estado. Enlace caído, modo banco y anulación de stick son puertas explícitas en
update(), de modo que los servos jamás se desbocan con la radio en silencio.
Dos kernels: onda y caja reductora
Un kernel es el bloque que convierte canales normalizados en µs de función de servo. PteronautOS tiene dos familias, distinguidas por el número de perfil del mezclador:
| Familia | Perfiles | Actuadores | Arquetipo de modelo |
|---|---|---|---|
| Kernel de forma de onda (servo) | 0 SERVO_2WING · 1 SERVO_2WING_1RUD · 2 SERVO_4WING | Servos de ala batiente (2–4), timón de cresta | Ornitóptero de servo de accionamiento directo — el PteronautOS clásico |
| Kernel de caja de engranajes | 3 GEARBOX_2VTAIL_1RUD · 4 GEARBOX_1MOT_2VTAIL · 5 GEARBOX_1MOT_2VTAIL_1RUD · 6 GEARBOX_1ELE_1RUD · 7 GEARBOX_1MOT_1ELE_1RUD | Motor ESC, cola en V o elevador + timón | Aleteador engranado / motorizado con superficies de cola convencionales |
La selección ocurre en update() con la única prueba PROFILE_IS_GEARBOX (activeProfile ≥ 3). El perfil en sí es un valor de ejecución: arranca de la bandera de compilación MIXER_PROFILE (el legado ORNITHOPTER_GEARBOX=1 aún mapea al perfil 5) pero puede cambiarse en vivo desde la WebUI vía setOrnithopterProfile() — el firmware reorienta una nueva aeronave sin reflash. Hay ocho perfiles en total; sus disposiciones exactas de funcMap están tabuladas en la Referencia de Mezcladores.
Kernel de onda (aleteo por servos)
El kernel de forma de onda es lo que la mayoría entiende por «PteronautOS»: dos servos de ala ejecutan una oscilación de fase continua cuya forma se esculpe cada ciclo. Su anatomía:
- Puerta de aleteo con histéresis. El acelerador debe cruzar el umbral de aleteo (bruto ≈303 por defecto, ≈1,08 ms equivalente PWM) para entrar en aleteo; sale sólo por debajo del umbral − 50 de histéresis, de modo que un acelerador marginal nunca hace castañear las alas.
- Frecuencia. Un FlappingOscillator avanza con el tiempo real transcurrido (reloj µs, dt limitado a 100 ms). El objetivo de cadencia mezcla el canal CH6 de frecuencia de aleteo y el acelerador vía
throttleFrequencyMixdel perfil, dentro de 0,5 Hz…flapBaseFreq/10. - Amplitud. Porcentaje de acelerador del techo de velocidad del servo
ampMax = degPerSec / (2·f)(condegPerSec = 60°/servoSpeed_ms), con tope duro de 55° pre-multiplicador. La amplitud colapsa por tanto de forma natural a alta frecuencia — el ala jamás exige más de lo que el servo puede dar. - Ferocidad. Las ferocidades de batida descendente (potencia) y ascendente (recuperación) se modelan por batida, moduladas asimétricamente por el elevador (tirar endurece la descendente, empujar la ascendente) y por el acoplamiento de acelerador, y luego se acotan 0…8 antes de la función de forma.
- Diferenciales de guiñada. El timón añade ferocidad opuesta I/D (
rudderFerocityRange) y amplitud opuesta I/D (rudderAmplitudeDifferential) mientras la fase de reversión de batida permanece compartida entre las alas — la autoridad de giro viene del dwell asimétrico, no de alas desincronizadas. - Geometría y salida. La onda moldeada −1…+1 se escala a grados, se mezcla contra la dirección de alerón/elevador (autoridad ±60° × escala), se compensa con el trim de centro de planeo/aleteo, se limita a 0…180° y se convierte a µs a través de la envolvente configurable
servoMinUs…servoMaxUs(por defecto 988…2012) con trim por función. - Rama de planeo. Por debajo del umbral de aleteo el oscilador decae y las alas mantienen un ángulo de planeo estático — un estado ideal de banco y lanzamiento.
Kernel de caja reductora (motor + colas)
El kernel de caja de engranajes cambia el aleteo puro por un tren motorizado más superficies de cola convencionales. Como un motor gira continuamente, este kernel es sin estado por tick — sin oscilador — y en su lugar:
- mapea el acelerador (armado) a un comando ESC de 1000…2000 µs (
SF_MOTOR), en ralentí al desarmar; - mezcla la cola: los perfiles en V usan una mezcla de elevón (alerón+elevador / alerón−elevador), los clásicos usan una superficie de elevador separada;
- deja que Zephyrus (cuando está activo) inyecte correcciones PID de alabeo y cabeceo en los servos de cola, limitadas a ±250 µs (
ZEPHYR_GEARBOX_CLAMP_US) para que un fallo de sensor jamás cale un servo; - conserva el mismo mezclador de timón y la envolvente µs/trim/limitación que el kernel de forma de onda.
Un mismo ornitóptero puede incluso llevar ambas personalidades: lanzamiento aleteando con el kernel de forma de onda, crucero con la caja de engranajes — el cambio de perfil en ejecución es esa única línea en update().
Estados: enlace, banco y failsafe
update() se llama con una cadencia fija de ~10 ms desde el bucle WiFi/servo y verifica tres banderas antes de ejecutar ningún mezclador:
| Estado | Condición | Comportamiento |
|---|---|---|
| Vuelo normal | linkUp + canal armado activo | La salida completa del kernel sigue a los sticks |
| Modo banco / panel | benchMode (panel WebUI, sin enlace RC) |
Se inyectan sticks neutros, el acelerador se aparca bajo el umbral de aleteo — los ajustes de planeo + trim son visibles en vivo |
| Anulación de stick | stickOverride (canales virtuales) |
Los valores de canal inyectados por el firmware sustituyen al CRSF por completo (barridos de servo, pruebas) |
| Failsafe | enlace perdido (onLinkDown / enterFailsafe) |
Todas las funciones centran a 1500 µs, oscilador reiniciado, motor → mín.; la pérdida de paquetes CRSF también centra a nivel de RX |
El comportamiento de centrar ante pérdida se duplica a propósito en dos capas: la pila CRSF aplica failsafe a los datos de canal, y la capa Ornithopter aplica failsafe a la geometría (incluido el estado de oscilación). Un radio muerto deja así una aeronave planeadora y controlable — nunca una aleteando a lo loco.
Mapa del código fuente
| Módulo | Ruta | Responsabilidad |
|---|---|---|
| Capa Ornithopter |
src/lib/Ornithopter/
|
Selección de kernel, matemática del mezclador, oscilador de forma de onda, perfiles, trim |
| Perfiles de vuelo y constantes |
src/lib/Ornithopter/OrnithopterConfig.h
|
Enum MixerProfile, etiquetas ServoFunc, funcMap, todos los valores numéricos por defecto |
| Oscilador / forma |
src/lib/Ornithopter/OrnithopterWaveform.h
|
Integración de fase, modelado de ferocidad, lógica de reversión |
| Zephyrus (opcional) |
src/lib/Zephyrus/
|
MPU6050 + Mahony AHRS, PID dual, corrección de cresta/cola |
| Etapa de salida PWM |
src/lib/ServoOutput/devServoOutput.cpp
|
Escribe µs sincronizados con el tick CRSF |
| Configuración WebUI |
src/lib/WIFI/devWIFI.cpp
|
Perfil en ejecución + persistencia de ajuste (/pteronautos) |
La capa inferior — exactamente qué forma de onda producen los números y cómo cada perfil conecta funciones a salidas PWM — vive en la Referencia de Mezcladores , y la envolvente de temporización del servo se documenta en el artículo Servo Kernel artículo.