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:
- Diferencial de ferocidade (
rudderFerocityRange): a esquerda recebe+ferocidade, a direita−ferocidade— as duas asas dwell de forma diferente mas mantêm uma fase de reversão partilhada (limiarShared), por isso as curvas são puro torque de rolagem, nunca dessincronização. - Diferencial de amplitude (
rudderAmplitudeDifferential): amplitude esquerda ×(1+Δ), direita ×(1−Δ) — divergência simétrica em torno da batida definida pelo acelerador.
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:
- Motor —
SF_MOTOR: quando armado, acelerador 0…1 mapeia para 1000…2000 µs; desarmado ou ligação perdida força o mínimo (motor parado). - Leme — equação idêntica à secção acima.
- Caudas — duas famílias. Perfis clássicos (6/7) acionam um profundor separado:
µs = 1500 + elevatorNorm·500 + pitchPID + trim. Perfis em V (3/4/5) misturam em elevon ambos os canais nos dois servos de cauda:
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.