Referência de mixers

Dos sticks e do giroscópio até aos µs no fio: a geometria completa de cada mixer que o PteronautOS pode selecionar — eixos de asa, misturas do leme, elevons, trims e limites.

Esta página documenta a camada de mixer de src/lib/Ornithopter/Ornithopter.cpp e o sistema de perfis de OrnithopterConfig.h. Para a arquitetura envolvente, comece pelo artigo Arquitetura ; para temporização de pulso e faixas de trim, veja o Servo Kernel artigo.

Etiquetas de função de servo

Cada saída de cada mixer é um valor µs escrito em _f[], indexado por uma etiqueta ServoFunc. As etiquetas são transporte — dizem o que um sinal significa, nunca onde está ligado:

Etiqueta Nome Significado
0 SF_NONE Reservado — sem função (os primeiros slots PWM ficam livres para BOOT e I2C)
1 / 2 SF_LEFT_WING / SF_RIGHT_WING Asas batentes dianteiras (kernel de forma de onda)
3 SF_RUDDER Leme de cabeça/crista (forma de onda) ou leme de barbatana (caixa) — mistura de guinada + rolagem
4 SF_MOTOR Acelerador ESC brushless (apenas kernel de caixa)
5 / 6 SF_VTAIL_LEFT / SF_VTAIL_RIGHT Superfícies de cauda em V, mistura de elevon (kernel de caixa)
7 SF_ELEVATOR Profundor separado tradicional (perfis de caixa 6/7)
8 / 9 SF_BACK_LEFT_WING / SF_BACK_RIGHT_WING Par de asas traseiras de uma aeronave de 4 asas (espelho do par dianteiro, trim próprio)

Um perfil de execução fornece o funcMap: um array que mapeia índice de saída PWM → ServoFunc. As três primeiras vagas (PWM 1…3) são sempre SF_NONE — pertencem à correia BOOT e ao barramento I2C — por isso as funções reais de servo começam no índice PWM 3 (o comentário do firmware explica: índice 3 = CH4, 4 = CH5, 5 = CH6).

Os oito perfis de mixer

Existem oito perfis de mixer. Os perfis 0–2 acionam o kernel de forma de onda (asas batentes), os perfis 3–7 o kernel de caixa de velocidades (motor + caudas):

# Perfil Kernel Servos funcMap (PWM 3 · 4 · 5)
0 SERVO_2WING forma de onda 2 Asa esquerda · Asa direita · —
1 SERVO_2WING_1RUD forma de onda 3 Asa esquerda · Asa direita · Leme
2 SERVO_4WING forma de onda 4 (traseira-direita partilha o espelho da traseira-esquerda) Asa esquerda · Asa direita · Asa traseira esquerda
3 GEARBOX_2VTAIL_1RUD caixa 3 Leme · Cauda V esquerda · Cauda V direita
4 GEARBOX_1MOT_2VTAIL caixa 3 Motor · Cauda V esquerda · Cauda V direita
5 GEARBOX_1MOT_2VTAIL_1RUD caixa 4 (cauda V direita partilha o espelho esquerdo) Leme · Motor · Cauda V esquerda
6 GEARBOX_1ELE_1RUD caixa 2 Leme · Profundor · —
7 GEARBOX_1MOT_1ELE_1RUD caixa 3 Leme · Motor · Profundor

Seleção: flag de build MIXER_PROFILE (legada ORNITHOPTER_GEARBOX=1 → perfil 5), depois sobreponível em execução pela WebUI (setOrnithopterProfile). O teste de kernel é simplesmente activeProfile ≥ GEARBOX_2VTAIL_1RUD.

Mixagem dos eixos da asa (kernel de onda)

Todos os valores de canal entram nos mixers normalizados: _crsfToNorm mapeia a janela bruta 172…1811 linearmente para −1…+1. Os comandos de direção são então escalados pelos sliders de autoridade por perfil (aileronScale, elevatorScale, 0–100%) vezes ±60° (ORNI_STEER_MAX_DEG).

Geometria em espelho. Os servos de asa são montados em espelho, o que troca o significado dos termos de modo comum e diferencial. O código trata assim o aileron (rolagem) como comum — soma-se às duas asas, tornando-se uma deflexão física diferencial — e o profundor (arfagem) como diferencial no código, tornando-se uma deflexão física comum. As equações de ângulo, com ORNI_NEUTRAL_ANGLE_DEG = 100° e o multiplicador 2× aplicado apenas aos termos de desvio:

angleLeft  = NEUTRAL + (aileron + elevator + flapCentre − degL) · 2
angleRight = NEUTRAL + (aileron − elevator − flapCentre + degR) · 2

flapping:  degL/R = amplitudeL/R · shapedWave(rawPhase, ferocityL/R, …)
glide:     degL = degR = 0 → static glide offset takes over

degL/degR são as deflexões instantâneas de batida da asa esquerda e direita, cada uma o produto da sua própria amplitude e de uma onda moldada por ferocidade. Profundor para cima fortalece a batida descendente; profundor para baixo a ascendente (ferocidade assimétrica). A guinada divide as asas de duas formas complementares:

Os ângulos são limitados a 0…180° e convertidos em pulsos com o envelope configurável servoMinUs…servoMaxUs (padrões 988…2012): µs = min + ângulo·(max−min)/180, mais o trim por função. Os offsets de centro de planagem/batimento (glideAngleDeg, flappingAngleDeg, ±15°) entram como termos estáticos no respetivo ramo — assim os centros de planagem e batimento podem ser afinados independentemente por perfil de voo.

Mixagem do leme (ambos os kernels)

O mixer de leme é partilhado verbatim por ambos os kernels — uma equação, duas aeronaves:

mix  = rudderNorm · rudderYawWeight  +  aileronNorm · rudderRollWeight
mix  = clamp(mix, −1 … +1)
µs   = 1500 + mix · 500            (rudder centre ± 500 µs)
       + gyroRudderCorrection       (Zephyrus, when enabled)
       + servoTrimUs[SF_RUDDER]

Padrões: rudderYawWeight = 65%, rudderRollWeight = 35% (afináveis em execução na WebUI, guardados por backup). O acoplamento de rolagem no leme é o que permite a um pterossauro pesado de asa manter curvas coordenadas sem ginástica de fio de guinada; reduza o peso de rolagem se o leme de cabeça parecer agressivo demais na rolagem.

Mixagem da cauda e elevons (caixa de velocidades)

O kernel de caixa executa sem estado a cada tick. Os seus três sub-mixers:

VtailLeft  = 1500 + (aileron + elevator)·scale + rollPID + pitchPID + trimL
VtailRight = 1500 + (aileron − elevator)·scale − rollPID + pitchPID + trimR
scale = 500 µs;  both aileron/elevator sums clamped to −1…+1 first

No kernel de caixa, o Zephyrus injeta ambas as correções PID de rolagem e arfagem nos servos de cauda (rolagem anti-fase, arfagem modo comum), cada uma limitada a ±250 µs (ZEPHYR_GEARBOX_CLAMP_US). Uma falha de giroscópio pode portanto abanar, mas nunca travar, um servo de cauda.

Injeção Zéfiros, trims e failsafe

Existem três pontos de injeção diferentes consoante o kernel e o eixo:

Eixo Kernel de forma de onda Kernel de caixa
Leme / guinada gyroRudderCorrection somado aos µs do leme de crista gyroRudderCorrection somado aos µs do leme de barbatana
Rolagem (aileron) — (asas de forma de onda não carregam PID de rolagem) gyroAileronCorrection, anti-fase na cauda V, limitado a ±250 µs
Arfagem (profundor) — (injetado como modulação de onda na camada Zephyrus) gyroElevatorCorrection, modo comum, limitado a ±250 µs

Trims e limites. Cada função tem um trim por servo com sinal (servoTrimUs[tag], padrão 0, ±300 µs) somado por último, e cada valor passa por _clampServo — um limite de segurança absoluto de 500…2500 µs que supera tudo. No failsafe ou ligação perdida, todas as funções voltam a 1500 µs e um motor de caixa ao mínimo, pelo que o modelo plana sempre para o neutro.

Pronto para a camada prática? O Afinação de Forma de Onda tutorial mostra como estes números se sentem nos sticks, e o Arquitetura artigo mostra onde cada mixer se situa na pipeline.