Arquitetura: kernels e fluxo de dados
Como um fluxo CRSF se torna batimento de asa e cauda: fronteiras dos módulos, os dois kernels de mistura e a máquina de estados que mantém o pterossauro vivo quando o link morre.
Fluxo de sinal
O PteronautOS é um fork do ExpressLRS 4.x que substituiu a ponta de "passagem PWM" do receptor por uma camada dedicada de controlo de voo. O caminho dos dados é deliberadamente curto:
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
Quatro ideias estruturais mantêm a besta estável e o código pequeno:
- Os canais são normalizados cedo. Cada valor bruto na janela CRSF 172–1811 é mapeado para −1…+1 (
_crsfToNorm) ou para uma percentagem uma única vez, e só essa unidade flui pelos mixers — sem constantes mágicas repetidas. -
As saídas são etiquetadas, não fixadas. Os mixers escrevem em
_f[ServoFunc]. Qual saída PWM física uma etiqueta comanda é decidido depois pelofuncMapdo perfil — veja a Referência de Mixers .Re-fixar um modelo nunca toca na matemática do mixer. - O kernel é uma família em tempo de compilação, uma escolha em tempo de execução. Tanto o batente de forma de onda como o controlador de caixa de velocidades vivem no binário; o perfil ativo escolhe o kernel a cada tick de atualização.
- O failsafe não é um evento, é um estado. Ligação perdida, modo de bancada e sobreposição de stick são portas explícitas em
update(), para que os servos nunca fujam enquanto o rádio está em silêncio.
Dois kernels: onda e caixa de velocidades
Um kernel é o bloco que transforma canais normalizados em µs de função de servo. O PteronautOS tem duas famílias, discriminadas pelo número do perfil de mixer:
| Família | Perfis | Atuadores | Arquétipo de modelo |
|---|---|---|---|
| Kernel de forma de onda (servo) | 0 SERVO_2WING · 1 SERVO_2WING_1RUD · 2 SERVO_4WING | Servos de asa batente (2–4), leme de crista | Ornitóptero de servo por acionamento direto — o PteronautOS clássico |
| Kernel de caixa de velocidades | 3 GEARBOX_2VTAIL_1RUD · 4 GEARBOX_1MOT_2VTAIL · 5 GEARBOX_1MOT_2VTAIL_1RUD · 6 GEARBOX_1ELE_1RUD · 7 GEARBOX_1MOT_1ELE_1RUD | Motor ESC, cauda em V ou profundor + leme | Batente engrenado / motorizado com superfícies de cauda convencionais |
A seleção acontece em update() com o teste único PROFILE_IS_GEARBOX (activeProfile ≥ 3). O perfil em si é um valor de execução: começa na flag de build MIXER_PROFILE (a legada ORNITHOPTER_GEARBOX=1 ainda mapeia para o perfil 5), mas pode ser trocado ao vivo pela WebUI via setOrnithopterProfile() — o firmware re-mira uma nova aeronave sem re-flash. Há oito perfis no total; os seus layouts exatos de funcMap estão tabelados na Referência de Mixers.
Kernel de onda (flapper por servos)
O kernel de forma de onda é o que a maioria entende por "PteronautOS": dois servos de asa executam uma oscilação de fase contínua cuja forma é esculpida a cada ciclo. A sua anatomia:
- Porta de batimento com histerese. O acelerador tem de cruzar o limiar de batimento (bruto ≈303 por defeito, ≈1,08 ms equivalente PWM) para entrar em batimento; só sai abaixo do limiar − 50 de histerese, pelo que um acelerador marginal nunca faz as asas trepidarem.
- Frequência. Um FlappingOscillator avança com o tempo real decorrido (relógio µs, dt limitado a 100 ms). O alvo de cadência mistura o canal CH6 de frequência de batimento e o acelerador via
throttleFrequencyMixdo perfil, dentro de 0,5 Hz…flapBaseFreq/10. - Amplitude. Percentagem do acelerador sobre o teto de velocidade do servo
ampMax = degPerSec / (2·f)(comdegPerSec = 60°/servoSpeed_ms), com limite rígido de 55° pré-multiplicador. A amplitude colapsa naturalmente a alta frequência — a asa nunca exige mais do que o servo consegue entregar. - Ferocidade. As ferocidades de batida descendente (potência) e ascendente (recuperação) são moldadas por batida, moduladas assimetricamente pelo profundor (puxar endurece a batida descendente, empurrar endurece a ascendente) e pelo acoplamento de acelerador, depois limitadas a 0…8 antes da função de forma.
- Diferenciais de guinada. O leme adiciona ferocidade oposta E/D (
rudderFerocityRange) e amplitude oposta E/D (rudderAmplitudeDifferential) enquanto a fase de reversão de batida permanece partilhada entre as asas — a autoridade de curva vem do dwell assimétrico, não de asas dessincronizadas. - Geometria e saída. A onda moldada −1…+1 é escalada em graus, misturada contra a direção de aileron/profundor (autoridade ±60° × escala), deslocada pelo trim de centro de planagem/batimento, limitada a 0…180° e convertida em µs através do envelope configurável
servoMinUs…servoMaxUs(padrões 988…2012) com trim por função. - Ramo de planagem. Abaixo do limiar de batimento o oscilador decai, e as asas mantêm um ângulo de planagem estático — um estado ideal de bancada e lançamento.
Kernel de caixa de velocidades (motor + caudas)
O kernel de caixa de velocidades troca o batimento puro por um trem motorizado mais superfícies de cauda convencionais. Como um motor gira continuamente, este kernel é sem estado por tick — sem oscilador — e em vez disso:
- mapeia o acelerador (armado) para um comando ESC de 1000…2000 µs (
SF_MOTOR), em ralenti quando desarmado; - mistura a cauda: perfis em V usam uma mistura de elevon (aileron+profundor / aileron−profundor), perfis clássicos usam uma superfície de profundor separada;
- deixa o Zephyrus (quando ativo) injetar correções PID de rolagem e arfagem nos servos de cauda, limitadas a ±250 µs (
ZEPHYR_GEARBOX_CLAMP_US) para que uma falha de sensor nunca trave um servo; - mantém o mesmo mixer de leme e envelope de µs/trim/limite do kernel de forma de onda.
Um mesmo ornitóptero pode até carregar as duas personalidades: lançamento batente com o kernel de forma de onda, cruzeiro na caixa de velocidades — a troca de perfil em execução é aquela única linha em update().
Estados: link, bancada e failsafe
update() é chamado numa cadência fixa de ~10 ms a partir do loop WiFi/servo e passa por três flags antes de qualquer mixer executar:
| Estado | Condição | Comportamento |
|---|---|---|
| Voo normal | linkUp + canal armado ativo | Saída completa do kernel segue os sticks |
| Modo bancada / painel | benchMode (painel WebUI, sem ligação RC) |
Sticks neutros são injetados, acelerador estacionado abaixo do limiar de batimento — edições de planagem + trim são visíveis ao vivo |
| Sobreposição de stick | stickOverride (canais virtuais) |
Valores de canal injetados pelo firmware substituem o CRSF por completo (varreduras de servo, testes) |
| Failsafe | ligação perdida (onLinkDown / enterFailsafe) |
Todas as funções centram em 1500 µs, oscilador reiniciado, motor → mín.; a perda de pacotes CRSF também centra ao nível do RX |
O comportamento de centrar na perda é duplicado em duas camadas de propósito: a pilha CRSF faz failsafe dos dados de canal, e a camada Ornithopter faz failsafe da geometria (incluindo o estado de oscilação). Um rádio morto deixa assim uma aeronave planante e controlável — nunca uma aos solavancos.
Mapa do código-fonte
| Módulo | Caminho | Responsabilidade |
|---|---|---|
| Camada Ornithopter |
src/lib/Ornithopter/
|
Seleção de kernel, matemática do mixer, oscilador de forma de onda, perfis, trim |
| Perfis de voo e constantes |
src/lib/Ornithopter/OrnithopterConfig.h
|
Enum MixerProfile, etiquetas ServoFunc, funcMap, todos os padrões numéricos |
| Oscilador / forma |
src/lib/Ornithopter/OrnithopterWaveform.h
|
Integração de fase, moldagem de ferocidade, lógica de reversão |
| Zephyrus (opcional) |
src/lib/Zephyrus/
|
MPU6050 + Mahony AHRS, PID duplo, correção de crista/cauda |
| Estágio de saída PWM |
src/lib/ServoOutput/devServoOutput.cpp
|
Escreve µs sincronizados ao tick CRSF |
| Configuração WebUI |
src/lib/WIFI/devWIFI.cpp
|
Persistência de perfil em execução + afinação (/pteronautos) |
A camada seguinte — exatamente que forma de onda os números produzem, e como cada perfil liga funções às saídas PWM — vive na Referência de Mixers , e o envelope de temporização de servo está documentado no artigo Servo Kernel artigo.