ゼピュロス: 安定化とOndas

ジャイロから翼打ちへの架け橋の理想設計:Ondas呼吸変調、打撃同期フィードフォワード、位相ロック共鳴、空力弾性PIDゲイン変調——そして現在のファームウェアが実際に配線しているものを正直に示す地図。

系譜: OrniFlightと簡略化されたOndas

Ondas は PteronautOS の発明ではありません。 波変調ファミリー — 呼吸ポーズのケイデンス制御、ストローク形状の猛度、非対称ドウェル、翼荷重と PID 効きの空力弾性結合 — は OrniFlight プロジェクトで、その wiki とシミュレーターが高度なアルゴリズムを担っています(含む: servo-efficiency table ).

PteronautOS は 部分的に簡略化した実装を内包します:同じ数学的な呼吸幾何を、ESP8285 1 個 + MPU6050 1 個 + 最大 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)

4 つの生項は、ピッチ 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

4 つすべてが NaN トラップで保護され(どれかの入力が NaN なら 4 つともゼロ化)、壊れた 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変調 — 三チャンネルの呼吸

Ondas(ポルトガル語「波」)は、ピッチ PID の生の P・I・D 項を羽ばたき波形の 3 つの直交次元へ対応付ける 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% では有限のプラトー/矩形形状を、100% では丸みを帯びたピラミッド状の経路を好みます。各半周期は依然として独自の強度を使うため、低強度の半周期は伸長して正弦波的になり、高強度の半周期は直接的な対角線の走行に近づきます。

ダウン・アップスキュー(±100)は、各ハーフストロークの中心をそれぞれ自身の開始→終了軸に沿ってずらします — 強度と同じ半周期ごとの鏡映です。正はストロークを前方に寄せ、翼がより早く最高速度に達して推力を高めます。負は後方へ寄せ、ストロークが運動を終端で消費して推力を弱めます。ワープは単調かつ端点を保存するため、まさに非対称な矩形↔三角の混合であり、位置の跳躍は決してありません。

スロットルの推力形状結合は、1つのノブでストローク滞留と前後中心を融合する。全開でストロークを矩形化し、かつ両半ストロークを前進させる——下降・上昇の双方でピーク速度が早まる。アイドルでは両者中立。単一の攻撃性スカラーが滞留と中心を一体で駆動するため、両翼と両半ストロークが同期して動く——推力制御であり、ロールやピッチではない。

エルロン→skew カップリングは片翼をフロントロードし、他翼を遅らせる——skew 軸上のロールトルクであり、スロットル skew の鏡像の双子。左打ちは左翼に skew を加え右翼から減じ、右打ちは逆。ラダー振幅差動と併用または代替できる。

スルーはスティックのレートに対する過渡的なブーストまたはブレーキです。ガスを入れると両方の半ストロークを短時間 front-load し、ガスを抜くと両方を短時間 late-load します。ローパスされたレート(τ = 0.10 s)がキックを注入し、スティックが静止すると減衰します。2つの結合があります: スロットルレートスルー(対称、推力)とエルロンレートスルー(差動、ロール)。

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 がゼロに落ちるとバイアスは明示的にクリアされ、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 へクランプされます。1 フレームの遅延は本質的です:aeroGainScale はギアボックスミキサーで計算されますが、zephyrusUpdate() が消費します — 250 Hz で 4 ms、機械式サーボには無視できます。

WebUIパネルと設定エンドポイント

オーニソプターパネルは、ビルドに Zephyrus が含まれる場合に 2 つの安定化グループを表示します。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. 全ゲインをゼロに。 手動のみで飛ばします。ジャイロが票を得る前に機体は安定かつ予測可能でなければなりません。
  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 研究を保持しています。