제피로스: 안정화와 Ondas

자이로에서 날갯짓으로 이어지는 다리의 이상적 설계: Ondas 호흡 변조, 스트로크 동기 피드포워드, 위상 고정 공명, 공탄성 PID 게인 변조 — 그리고 현재 펌웨어가 실제로 연결한 것을 정직하게 보여주는 지도.

계보: OrniFlight와 단순화된 Ondas

Ondas는 PteronautOS의 발명이 아닙니다. 파 변조 패밀리 — 호흡-일시정지 케이던스 제어, 스트로크 형태 격렬도, 비대칭 드웰, PID 권위에 대한 날개 하중의 공탄성 결합 — 은 OrniFlight 프로젝트에서, 그 위키와 시뮬레이터가 고급 알고리즘을 담고 있습니다(포함: servo-efficiency table ).

PteronautOS는 부분적으로 단순화된 구현을 내장합니다: 동일한 수학적 호흡 기하를 ESP8285 하나, MPU6050 하나, 최대 9개 서보로 스케일. OrniFlight가 연속 공탄성을 시뮬레이션하고 전체 엔벨로프 패밀리를 오프라인으로 탐구하는 반면, PteronautOS는 4 ms 틱 안에, 80 MHz 코어에서, ExpressLRS 라디오 스택과 같은 실리콘을 공유하며 결정해야 합니다. 이 문서의 모든 게인은 두 프로젝트 모두에 존재하며, 아래 펌웨어 상수는 비행용 하위 집합입니다.

신호 경로 — 제피로스에서 Ornithopter로

자이로에서 날갯짓까지의 브리지는 의도적으로 짧습니다 — 파일 4개, 동적 할당 없음, 큐 없음:

MPU6050 raw (I2C, 1 kHz register reads)
  ▼
Zephyrus::update()            Mahony AHRS → roll°, pitch°, yawRate °/s
  ▼                            dual PID: roll→0°, pitch→0°, yawRate→0°/s
4 raw pitch terms exposed      pitchPTerm · pitchITerm · pitchDTerm · pitchErrorRate
  ▼
ZephyrusFilter.h (250 Hz)      copies terms onto ornithopter.gyroPitch*
  ▼
Ornithopter::_computeServoMixer() / _computeGearboxMixer()
  ▼   Ondas P → phase · PD → dwell · I → asymmetry · SSFF → next stroke
_f[]  →  funcMap → wing / crest-rudder / tail servos (µs)

네 원시 항은 피치 PID가 보정을 계산한 직후 Zephyrus::update()에 기록됩니다:

pitchPTerm     = ZEPHYR_PID_PITCH_KP * pitchErr;                 // raw proportional
pitchITerm     = _pidPitch.integrator;                           // accumulated error
pitchDTerm     = ZEPHYR_PID_PITCH_KD * _pidPitch.lastDerivative; // low-pass filtered D
pitchErrorRate = _pidPitch.lastDerivative;                       // °/s, for SSFF

네 값은 모두 NaN 트랩으로 보호됩니다(입력 중 하나라도 NaN이면 네 값 모두 0) — 손상된 I²C 프레임이 믹서 산술에 도달할 수 없습니다. Ornithopter의 7개 필드가 브리지 데이터를 받습니다:

필드 Zephyrus 소스 소비처
gyroPitchPTerm pitchPTerm Ondas P → 위상 전진(케이던스 채널)
gyroPitchITerm pitchITerm Ondas I → 스트로크 비대칭(밸런스 채널)
gyroPitchDTerm pitchDTerm Ondas D → 드웰 비율(격렬도 채널)
gyroPitchErrorRate pitchErrorRate SSFF 반스트로크 누적
gyroRudderCorrection rudderCorrection 크레스트 러더 µs 오프셋(서보 및 기어박스 커널)
gyroAileronCorrection rollCorrection × gearbox gain 기어박스: 롤 PID → V-테일 에일러론 면
gyroElevatorCorrection pitchCorrection × gain × aeroGainScale 기어박스: 피치 PID → 엘레본/엘리베이터 면

Ondas 변조 — 3채널 호흡

Ondas(포르투갈어: "파도")는 피치 PID의 원시 P, I, D 항을 플래핑 파형의 세 직교 차원에 매핑하는 3채널 호흡 시스템입니다. 이는 고유수용 루프입니다: 자이로의 피치 감각이 날개 움직임을 직접 형성하고, 날개 움직임이 자이로가 느끼는 피치를 바꿉니다.

케이던스 — P → 위상 전진

양의 피치 오류(기수 상승)는 오실레이터 위상을 전진시켜 현재 반스트로크를 줄이고, 음의 오류는 지연시킵니다. 이상 법칙은 오실레이터 스프링을 스케일합니다:

_osc.kGainMod = 1.0f + gyroPitchPTerm * aeroGainScale * cadenceGain * 0.00005f;
// clamped to [0.5, 2.0] — phase can at most double or halve

오실레이터의 advance()가 이를 error = kGain · kGainMod · cadenceTarget − kDamp · cadence로 소비합니다. 튜닝 범위 0–100, 기본 20.

격렬도 — PD 블렌드를 드웰로

P와 D 항은 파형의 드웰을 날카롭게 하거나 부드럽게 하는 단일 격렬도 신호로 혼합됩니다:

ferocitySignal = (gyroPitchPTerm * ferocityPGain * 0.00015f
                 + gyroPitchDTerm * ferocityDGain * 0.0003f) * aeroGainScale;
// clamped to [-0.5, 0.5], added to both stroke and return ferocity

ferocityPGain = 0(기본)이면 D-전용 동작입니다 — 빠른 피치 변화는 더 날카로운 스트로크를 만들고 정상 비행은 정현파로 유지됩니다.

밸런스 — I를 비대칭으로

적분 항은 스트로크 중심을 이동시킵니다: 지속적인 기수 상승 편향은 다운스트로크를, 지속적인 기수 하강 편향은 업스트로크를 강화합니다.

float iBias = gyroPitchITerm * aeroGainScale * balanceGain * 0.0001f;
// clamped to [-3.0, 3.0]; added to strokeFer, subtracted from returnFer

밸런스는 높게 설정하면 가장 불안정하게 만드는 채널입니다 — 이상적인 튜닝 가이드는 낮게(5–15) 유지하고 마지막에 추가합니다. 파일럿 명령, Ondas, SSFF 편향을 결합한 격렬도 적용은 전체 [1.0, 8.0] 창에서 파일럿 권위를 유지합니다:

strokeFer = pilot(stroke) + ferocitySignal + iBias + _ssffFerocityUpBias
returnFer = pilot(return) + ferocitySignal − iBias + _ssffFerocityDownBias

파형 성형과 PD 블렌드

파형 자체는 OrniFlight 스트로크 패밀리를 따릅니다: 각 반쪽은 자신의 격렬도를 쓰고, 공유 반전 임계값이 좌우 날개를 동기화하며, 프로파일별 형태 믹스가 평평한 드웰/고원 스트로크를 둥근 피라미드 경로로 계속 변환합니다. 아래 인터랙티브 탐색기는 임의의 다운/업 격렬도 쌍에 대해 명령된 기하를 렌더링합니다 — 믹서가 매 틱 요청하는 바로 그 곡선입니다.

퍼로시티는 반 스트로크의 성격과 타이밍을 모두 제어합니다. 내부적으로 WebUI의 0–100 설정은 0–8에 매핑됩니다. 다운스트로크 퍼로시티가 높을수록 주기에서 다운스트로크의 비중이 짧아지고, 복귀 퍼로시티가 높을수록 복귀 비중이 짧아집니다. 남은 시간은 반대쪽 절반으로 이전됩니다 — 0에서 멈춤이 없습니다.

프로필별 퍼로시티 형태 컨트롤은 고퍼로시티 특성을 선택합니다. 0%에서는 유한한 플래토/사각 형태를, 100%에서는 둥근 피라미드 경로를 선호합니다. 각 절반은 여전히 자신의 퍼로시티를 사용하므로, 저퍼로시티 절반은 길쭉하고 사인파에 가깝게 유지되고, 고퍼로시티 절반은 직접적인 대각선 이동에 가까워집니다.

다운·업 스큐(±100)는 각 반쪽 스트로크의 중심을 자신의 시작–끝 축을 따라 이동시킵니다 — 퍼로시티와 동일한 반쪽별 미러링입니다. 양수는 스트로크를 앞쪽으로 실어 날개가 최고 속도에 더 일찍 도달하게 하여 추력을 키웁니다. 음수는 뒤로 미뤄 스트로크가 운동을 끝에서 소비하게 하여 추력을 줄입니다. 왜곡은 단조이며 양 끝점을 보존하므로 정확히 비대칭 사각↔삼각 혼합이며, 위치 점프는 절대 없습니다.

스로틀 추력 형태 결합은 하나의 노브로 스트로크 체류와 전후 중심을 융합한다. 풀가스에서 스트로크를 각형화하고 두 반스트로크를 전방 이동시킨다——하강·상승 모두에서 피크 속도가 빨라진다. 아이들에서는 둘 다 중립. 단일 공격성 스칼라가 체류와 중심을 함께 구동하므로 양 날개와 두 반스트로크가 동시에 움직인다——추력 제어이지 롤이나 피치가 아니다.

에일러론→skew 커플링은 한쪽 날개를 프런트로드하고 다른 쪽을 늦춘다 — skew 축 위의 롤 토크로, 스로틀 skew의 거울 쌍둥이다. 왼쪽 스틱은 왼쪽 날개에 skew를 더하고 오른쪽에서 빼며, 오른쪽은 반대다. 러더 진폭 차동과 함께 또는 대신 사용한다.

슬루는 스틱의 레이트에 대한 과도적 부스트 또는 브레이크입니다. 가스를 주면 두 반스트로크를 잠시 front-load하고, 가스를 줄이면 두 반스트로크를 잠시 late-load합니다. 저역 통과된 레이트(τ = 0.10 s)가 킥을 주입하고 스틱이 멈추면 감쇠합니다. 두 결합: 스로틀 레이트 슬루(대칭, 추력)와 에일러론 레이트 슬루(차동, 롤).

Zephyrus slew gain은 같은 과도 원리를 러더에 적용한다: 자세의 급격한 변화가 러더 보정(µs)을 잠시 증폭하고, 저역 통과(τ = 0.12 s) 후 ±80 µs로 하드 클램프한다. 정적 게인을 올리지 않고 급격한 자세 변화에 대한 응답을 날카롭게 한다.

제어축 매트릭스

러더 스틱은 날개 믹서에 요 권한이 없다: 차동 노브(진폭과 격렬도)는 에일러론 스큐와 같은 좌/우 비대칭을 만들며 — 그 비대칭은 롤이다. 진정한 요는 자율 크레스트 러더(Zephyrus, 자이로 전용)에만 존재한다.

제어 입력 파형 파라미터 비행 효과
에일러론 스틱 스큐 (skew) — 차동, 좌/우
러더 스틱 진폭 + 격렬도 — 차동, 좌/우
요 — Zephyrus 자이로 크레스트 러더 요 (자율)

비행 조향 및 slew

전체 주기

현재 파형 정확한 직접 기준 반전 왼쪽 날개 오른쪽 날개

각 절반을 동일 폭으로 늘림

다운 절반 업 절반, 방향 정렬 정확한 대각선 왼쪽 날개 오른쪽 날개

극단 조합 읽기

7 / 1 조합은 분명히 짧고 뾰족한 다운스트로크와 긴 사인파 복귀를 만듭니다. 정확하거나 최대에 가까운 값은 주의가 필요합니다. 현재 지속시간 법칙은 퍼로시티 8 아래 남은 양을 사용합니다. 따라서 8 / 0은 주기의 약 0.125%만 다운스트로크에 할당하고, 8 / 6조차 약 0.5%를 할당합니다. 위치는 연속적이지만, 요청된 속도와 가속도는 물리적으로 도달 불가능해질 수 있습니다.

앵커 감쇠 (k₂)

오실레이터의 감쇠는 튜닝 가능한 앵커로 노출됩니다. 기본 법칙은 케이던스 목표를 향한 감쇠 스프링입니다:

kDamp = 10.0f + anchorGain;                  // anchorGain 0 → k₂=10 (tight)
error = kGain * kGainMod * cadenceTarget
      - kDamp * cadence;

anchorGain = 0(기본)이면 감쇠가 팽팽합니다 — 명령된 케이던스로 빠른 수렴. 값이 높을수록 게인 100에서 k₂가 110을 향해 올라, 리듬을 더 단단히 잠그고 섭동을 거부합니다. 튜닝 범위 0–100.

공명 — 위상 고정 펌프

공진은 위상 고정 록인 증폭기입니다: 피치 오류율을 sin(오실레이터 위상)으로 곱해 누적합니다 — 보정이 가장 효과적인 순간 정확히 스트로크를 강화하고, 날개와 싸울 때는 감쇠합니다.

_resonanceAccum += gyroPitchErrorRate * sinf(_osc.phase)
                 * resonanceGain * 0.01f * dt;
_resonanceAccum *= expf(-dt / 0.15f);        // leaky integrator, τ = 0.15 s
clamp(_resonanceAccum, -2.0f, 2.0f);

누적 값은 다음 프레임의 스트로크 및 복귀 격렬도에 주입됩니다. resonanceGain = 0(기본)이면 전체 경로가 비활성화됩니다. 누설 감쇠는 지속 교란 중 폭주 누적을 방지합니다.

SSFF — 스트로크 동기 피드포워드

스트로크 동기 피드포워드는 모든 PID 항과 철학적으로 다릅니다: 현재 반스트로크 동안 자이로가 측정한 값으로 다음 반스트로크를 보정합니다. 플래핑 사인의 각 영점 교차(날개 반전)에서, 끝난 반스트로크의 평균 피치 오류율이 반대쪽 절반의 격렬도에 대한 편향이 됩니다.

// zero-cross detected: finished one half-stroke, starting the next
meanError = _ssffAccumError / count;
bias      = meanError * ssffGain * 0.00001f;   // clamped ±2.0
// upstroke just ended        → bias feeds the downstroke (power stroke)
// downstroke just ended      → bias feeds the upstroke (recovery)

업 편향은 다운스트로크에 적용되고(이전 업스트로크에 누적된 오류 보정), 다운 편향은 업스트로크에 적용됩니다. ssffGain이 0이 되면 편향이 명시적으로 지워져 WebUI 비활성화가 파형에 낡은 값을 남기지 않습니다. 누산기는 32비트이고 반스트로크마다 리셋 — 250 Hz에서 오버플로하려면 ~24일 연속 플래핑이 필요하므로 실질적 우려가 아닙니다.

공탄성 PID 게인 변조

모든 Ondas 및 SSFF 변조는 오르니톱터가 플래핑 중인지 글라이딩 중인지 아는 계수로 스케일됩니다 — 공탄성 게인 변조. 서보(파형) 커널에서 선택기는 50 µs 히스테리시스를 가진 플랩 게이트입니다:

aeroGainScale = isFlapping ? (aeroFlapCoeff  * 0.01f)
                           : (aeroGlideCoeff * 0.01f);

기어박스 커널에서 선택기는 모터 상태(armed && throttleNorm > 0.1f)입니다. 기본값:

계수 기본값 범위 활성 시
aeroGlideCoeff 40 0–100 날개 정지 — 글라이드, 또는 기어박스 모터 꺼짐
aeroFlapCoeff 40 0–100 날개 플래핑 — 또는 기어박스 모터 구동 중

0–1로 스케일하면 0–100 슬라이더가 날개에 대한 자이로의 권위가 됩니다: 100에서 자이로가 변조를 완전히 소유하고, 0에서 꺼집니다. 커널별 권위:

권위 서보(파형) 커널 기어박스 커널
크레스트 러더 롤 + 요 결합 요만(롤/피치는 다리로)
날개 파형 전체 Ondas/SSFF 호흡 변조
다리 에일러론(V-테일) 롤 PID → 엘레본 믹스의 에일러론 측
다리 엘레본/엘리베이터 피치 PID × aeroGainScale → 엘리베이터 측

기어박스 PID 보정은 믹싱 전 ±250 µs(ZEPHYR_GEARBOX_CLAMP_US)로 클램프되고, 크레스트 러더 경로는 ±200 µs로 클램프됩니다. 한 프레임 지연이 본질적입니다: aeroGainScale은 기어박스 믹서에서 계산되지만 다음 zephyrusUpdate()가 소비합니다 — 250 Hz에서 4 ms, 기계식 서보에는 무시 가능합니다.

WebUI 패널 및 구성 엔드포인트

오르니톱터 패널은 빌드에 Zephyrus가 포함될 때 두 안정화 그룹을 표시합니다. Ondas 및 SSFF 슬라이더(0–100): 케이던스 P→위상, 격렬도 D→드웰, 격렬도 P→드웰, 밸런스 I→비대칭, 앵커 k₂, 공진, SSFF 게인. 공탄성 그룹: 글라이드 계수와 플랩 계수. 모든 값은 POST /pteronautos/config로 영속되고 GET /pteronautos/state로 스트리밍됩니다:

{
  "ornithopter": {
    "cadence_gain": 20.0, "ferocity_d_gain": 20.0, "balance_gain": 10.0,
    "ferocity_p_gain": 0.0, "anchor_gain": 0.0, "resonance_gain": 0.0,
    "ssff_gain": 0.0,
    "aero_glide_coeff": 40.0, "aero_flap_coeff": 40.0,
    "aero_gain_scale": 0.4
  }
}

aero_gain_scale은 계산되며 읽기 전용입니다. POST는 부분 업데이트를 허용하고 서버 측에서 모든 필드를 [0, 100]으로 클램프합니다.

튜닝 가이드

이상적인 비행 시험 순서 — 각 단계는 슬라이더 드래그가 아니라 비행입니다:

  1. 모든 게인을 0으로. 수동으로만 비행; 자이로가 표를 얻기 전에 기체가 안정적이고 예측 가능해야 합니다.
  2. Zephyrus의 크레스트 러더를 활성화(롤+요 PID). 파형을 건드리기 전에 러더가 부드러운 날개 흔들림에 저항하는지 확인하세요.
  3. 케이던스(P→위상) 10부터 위로. 너무 높음 = 덜컹이는 날개, 위상 헌팅; 너무 낮음 = 없음. 적정 = 날개가 피치에 "연결된" 느낌.
  4. 격렬도 D(D→드웰) 10부터. 너무 높음 = 파형이 날카로움과 부드러움 사이 진동; 적정 = 빠른 피치 변화가 스트로크를 날카롭게, 정상 비행은 정현파 유지.
  5. 격렬도 P(선택) 5부터 — P 권위를 드웰에 혼합.
  6. 밸런스(I→비대칭) 마지막에, 낮게. 가장 불안정하게 만드는 채널; 5–15만. 지속 드리프트를 상쇄하지 일시적 충격은 아님.
  7. SSFF 5부터, 케이던스와 격렬도가 유지된 후에만. 너무 높음 = 과보정과 반스트로크 플러터; 적정 = 피치 진동 진폭의 가시적 감쇠.
  8. 공탄성 계수는 마지막에. 먼저 글라이드(날개 정지)를 튜닝하고 그 다음 플랩. 자이로를 권위 있는 부조종사로 유지하되 불안한 조종사가 되지 않게 하세요.

구현 상태 및 미해결 불일치

정직한 지도, 현재 펌웨어(Ornithopter.cpp @ 72c63020). 위의 이상 법칙은 설계입니다; 코드는 전체 파라미터 표면, 상수, WebUI 슬라이더, 브리지 심을 포함하지만 — 비행 시험 스캐폴딩 블록이 현재 자이로 대부분을 서보 커널에서 분리합니다:

// Ornithopter.cpp:269-275  (inside the flapping branch)
#ifdef ZEPHYRUS_ENABLED
    // STEP 7: ALL gyro values hard-zeroed (NaN guard test)
    _osc.kGainMod = 1.0f;              // cadence channel OFF
    gyroRudderCorrection  = 0.0f;      // crest rudder OFF while flapping
    gyroAileronCorrection = 0.0f;
    gyroElevatorCorrection= 0.0f;
#endif

i18n 작업과 함께 "STEP 7" NaN-가드 테스트로 커밋되고 롤백된 적 없는 이 블록은 오늘날의 서보 커널 동작이 다음과 같음을 의미합니다:

이는 수학에 대한 비판이 아니라 알려지고 명명된 상태입니다: 안정화 모듈은 컴파일되고, 매개변수화되고, 브리지되며, STEP-7 스캐폴딩이 제거되고 실제 NaN 정책으로 교체되기를 기다리고 있습니다(Validatio 가드가 이미 MPU 데이터에 깨끗한 경로를 제공). 그때까지 위의 Ondas/SSFF/공진 튜닝 가이드를 비행 준비 사양으로 취급하세요. Zephyrus.cpp:729–737 및 OrnithopterWaveform.h와 검증되었지만 아직 비행 전입니다.

권위 있는 설계 기록은 STABILIZATION.md ; 그 자매 프로젝트 OrniFlight 지속적으로 발전하는 ONDAS 연구를 담고 있습니다.