道具立ては揃いました。プラントのモデル(第3章)とその中の数字(第4章)を手に、 いよいよ姿勢制御に入ります。最初の設計判断は意外なところにあります—— 制御したいのは「角度」なのに、実機の制御ソフトはまず「角速度」を制御するのです。 この章では、その理由を物理から説き起こし、レートループ→角度ループの カスケード制御を実際に設計します。設計した制御系がどこまで攻められるのか、 「理想」と「現実(むだ時間)」の違いも体感してもらいます。

5.1 なぜ角度を直接制御しないのか — 積分遅れの連鎖

制御対象のマルチコプタは物理法則から逃れられません。運動方程式を思い出すと、 トルクは慣性モーメントをスケールファクターとして瞬時に角加速度になります (\(\dot{\omega} = \tau / I\)——加速度とは、実質的に力そのものです)。 しかし角速度はそうはいきません。角加速度を1回積分しなければ現れないので、 遅れます。角度はさらにもう1回積分——もっと遅れます。

トルク τ 1/I 即時 角加速度 遅れ 1 角速度 遅れ 2 角度
入力(トルク)から角度までは積分2回分の遅れ。角速度までなら1回分で済む。 制御で「どの信号を掴むか」は、この連鎖のどこに介入するかの選択である。

角度の誤差だけを見てトルクを決める「1段」の制御では、入力と出力の間に 2階積分分の遅れが挟まります。突風で角度がずらされたとき、 その情報が角度に現れてから修正が効き始めるまでが遠く、 タイムリーに追いつけないおそれがあります。 そこで発想を変えます。まず角速度を制御するのです。 角速度はトルクの1階積分ですから、角度より速く反応でき、 突発的な外乱に早めの対処ができます。角速度制御がうまく行ったら、 その外側に角度の制御を重ねる——まず角速度が早めの対処をし、 その後、角度をきっちり決める。これがカスケード制御です。

5.2 カスケード制御 — 内側を「1」にして、外側が発注する

もう一つの見方があります。フィードバック制御の目的は、突き詰めれば 入力(指令)と出力の関係を 1 にすることです。 指令通りに出力が出るなら、それで勝ちです。 角速度ループが「1」を達成している——つまり、指令した角速度が そのまま実現される——なら、外側の角度ループは中身を気にする必要がありません。 「いまの角度誤差を解消するにはこの角速度が欲しい」と、 角速度制御系に発注すればよいのです。

外側: 角度ループ(遅い・きっちり) 内側: 角速度ループ(速い)=これが「1」になる θ_ref 角度制御 P(+I) r_cmd 角速度制御 PID モータ+機体 K/(s(τs+1)) r 1/s θ ジャイロ(角速度を直接・即座に測る) 姿勢推定(角度は推定フィルタ経由・第8章)
カスケード制御のブロック線図。内側(角速度)ループはジャイロの速い測定で トルクを直接動かし、外側(角度)ループは「欲しい角速度」を内側に発注する。 角速度はジャイロで直接・即座に測れるのに対し、角度は センサ融合(第8章)を経てくる——速い測定を速いループに割り当てる構造でもある。
設計の分業: 内側ループの仕事は「速さ」——突発外乱を角度に波及する前に 受け止めること。外側ループの仕事は「正確さ」——角度を狙った値にきっちり合わせること。 1つのループに両方を頼むより、役割を分けたほうが設計も調整も見通しがよくなります。 ドローンに限らず、モータドライブ(電流→速度→位置)などでも定番の構造です。

5.3 内側レートループの設計 — 2次系近似

では内側ループを設計しましょう。第4章で組み立てたロール軸のプラント (正規化入力 → 角速度)は、モータの一次遅れと機体の積分でこう書けます:

$$G_{rate}(s) = \frac{K}{s\,(\tau s + 1)}, \qquad K \approx 102\ \mathrm{rad/s^2},\quad \tau = 0.02\ \mathrm{s}$$

これにレートのP制御(ゲイン \(K_{p}^{rate}\))を巻くと、閉ループは見慣れた 2次系になります:

$$\frac{r}{r_{cmd}} = \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2}, \qquad \omega_n = \sqrt{\frac{K_{p}^{rate}\,K}{\tau}}, \quad \zeta = \frac{1}{2\sqrt{K_{p}^{rate}\,K\,\tau}}$$

2次系なら挙動は全部知っています。減衰係数 \(\zeta\) を決めれば オーバーシュートが決まり、\(\omega_n\) が速さを決めます。 そこで設計は「\(\zeta\) を選ぶ → ゲインが決まる」の一直線です:

$$K_{p}^{rate} = \frac{1}{4\zeta^2 K \tau} \;\xrightarrow{\ \zeta = 0.7\ }\; K_{p}^{rate} \approx 0.25, \qquad \omega_n \approx 36\ \mathrm{rad/s}\ (\approx 5.7\ \mathrm{Hz})$$

たった1行の式ですが、中身は第4章の実測が詰まっています——\(K\) には推力係数と アーム長と慣性モーメント、\(\tau\) にはモータの実効時定数。 プラントを測ったからゲインが計算できるのです。 これが「2次モデル近似設計」で、ワークショップで実際に使われて 実機でも案外うまく飛んだ、実績のある流儀です。 (もう一つの流儀「ループ整形」は次章で扱います。) なお実機のファームウェアはPだけでなく I・D も使っています—— それぞれの役割を学んだ後、5.5節の最後で実機のゲイン表をまとめて読みます。

5.4 外側ループはどこまで攻められるか — 「1/3ルール」を実証する

内側が「1」なら、外側の角度ループは楽勝です。\(\dot\theta = r = r_{cmd}\) ですから、 角度Pゲイン \(K_p^{ang}\) で \(r_{cmd} = K_p^{ang}(\theta_{ref}-\theta)\) と発注すれば

$$\frac{\theta}{\theta_{ref}} = \frac{1}{\frac{1}{K_p^{ang}}s + 1}$$

——時定数 \(1/K_p^{ang}\) の一次遅れ。オーバーシュートなし、ゲインを上げるほど速い、 いいことずくめです。理想的には、外側は比例制御だけでも結構いけるのです。

しかし「内側が 1」はどこまで本当でしょうか。内側ループにも帯域があり (\(\omega_n \approx 36\) rad/s)、それより速い指令には追従できません。 さらに現実のシステムにはむだ時間——センサの読み出し、フィルタ、 制御周期、モータ指令の反映までの純粋な待ち時間——があります。 StampFly の飛行データからは 10 ms 前後のむだ時間が同定されています。 外側を攻めるほど「内側 ≒ 1」の仮定が壊れた領域を使うことになり、どこかで破綻するはずです。

経験則では、外側の帯域は内側の 1/3 前後が目安とされます。 本当にそうなのか、同定済みの StampFly モデルで確かめてみましょう。 下の教材で、帯域比 \(\omega_{out}/\omega_{in}\) とむだ時間 \(L\) を動かして、 角度ステップ応答がどう変わるかを見てください。 下段の曲線は応答の良さの指標 ITAE (時間重み付き誤差積分 \(\int t\,|e|\,dt\)。誤差の絶対値に経過時間の重みを掛けて 足し合わせたもので、あとまで残る誤差ほど重く数える。小さいほど「速く・きれいに」 収束)で、谷の位置=ちょうどよい帯域比です。

条件: 内側はレートP(\(\zeta=0.7\) 設計、\(\omega_{in} \approx 36\) rad/s)、外側は角度P。 むだ時間は制御入力に挿入。破線は帯域比 1/3(同じむだ時間)の参照応答。 Python での同一解析は notes/calc/cascade_bandwidth_study.py。

実験結果のまとめ — 「1/3」は経験則ではなく最適点: 応答が最良になる谷は帯域比 0.26〜0.32 にあり、 むだ時間が長いほど谷は低い比率側へ動きます(\(L=0\) で 0.32、12 ms で 0.26) ——攻めるなら下側に外すのが安全です。 理想(\(L=0\))では攻めても壊れ方が優しい——比率 1.0 でもオーバーシュートが 増えるだけです。ところが現実(\(L=12\) ms)にはがあります。 比率 0.5 で整定は3倍悪化し、0.8 以上では発散。 しかも谷を超えてゲインを上げると、整定はむしろ遅くなります。 「外側を攻めれば機敏になる」という直感は、むだ時間のある現実では成り立たないのです。

5.5 積分器は1本ごとに仕事が違う

ここまで内側も外側もP制御で話してきましたが、実機のファームウェアは 内側に I 項を持ち、外側にも入れています。 なぜ2本も積分器が要るのでしょうか。実は、置き場所によって消せる誤差が違うのです。

下の教材で、外乱の種類と積分器の組み合わせを変えて確かめてください。

条件: 帯域比 1/3・むだ時間 12 ms・\(T_i\) は実機値。実験Aの外乱は角加速度換算 10 rad/s²(トリムずれ相当)、実験Bはジャイロ出力に +0.1 rad/s の一定バイアス。 理論オフセット: 実験A(Iなし)\(d/(K K_p^{rate} K_p^{ang})=1.89°\)、 実験B(外側Iなし)\(-b/K_p^{ang}=-0.48°\)。 Python での同一解析は notes/calc/cascade_integrator_study.py。

Iのコストはほぼタダ?: 積分器を足す代償は、交差周波数 (ループの応答速度を決める周波数)での位相遅れ \(\arctan(1/(\omega T_i))\) です。 実機の \(T_i\) で計算すると内側 2.3°・外側 2.4°——どちらも \(T_i\) を 交差周波数の1桁下に置くことで、安定余裕をほとんど食わずに定常誤差だけを 消す設計になっています。ただし出力が飽和する場面では積分器は 溜まりすぎる(ワインドアップ)ので、実機は条件付き積分(飽和中は積分を止める) というアンチワインドアップ処理と、各段の出力制限を併用しています。 外側の I を強くしすぎると位相を食ってカスケードの利点を殺すので、 「外側にも場合によっては入れる」——控えめに、が実務の相場です。

残る文字は D(微分)です。D の役割は、誤差が増える速さに先回りして ブレーキを掛けること——つまり減衰を足すことです。周波数の目で見れば位相を進める 働きがあり、むだ時間や遅れで足りなくなった位相を少し取り戻して、 そのぶん \(K_p\) を高くできます。ただし微分はノイズを増幅するので、実機では 工夫が要ります: 指令ではなく測定値側だけを微分する D-on-M 構成(指令のステップを微分すると出力に衝撃が走る「微分キック」を避ける)と、 フィルタ付き微分です。\(T_d\) は控えめに、が基本です(実装の詳細は第6章で)。

これで P・I・D の役割が出そろいました。仕上げに、実機 StampFly の ゲイン表を読んでみましょう。

ループ\(K_p\)\(T_i\) [s]\(T_d\) [s]
レート(内側)roll\(9.76\times10^{-4}\) N·m/(rad/s)0.70.010
pitch\(1.43\times10^{-3}\) N·m/(rad/s)0.70.025
yaw\(1.90\times10^{-3}\) N·m/(rad/s)0.80.010
角度(外側)roll5.0 1/s2.00.04
pitch5.0 1/s2.00.04
実機 StampFly ファームウェアの姿勢制御ゲイン(飛行実績値)。レートループの \(K_p\) は 物理単位(トルク指令)で、慣性モーメントで割ると本文の正規化ゲインに対応する。 実際、\(K_p\) の pitch/roll 比 1.46 は慣性モーメント比 \(I_{yy}/I_{xx} = 1.45\) (第4章の2点吊り実測)とほぼ一致——軸ごとの慣性の違いを打ち消して、 同じ角加速度応答を狙っている証拠である。\(T_i\) は交差周波数の1桁下(本文の 位相コスト約2°)、\(T_d\) はさらに小さい。離散化は 400 Hz の Tustin 変換 (双一次変換: 微分方程式をデジタル計算用の漸化式に直す標準的な方法)、 微分は D-on-M 構成——この実装の中身は第6章で読み解く。

5.6 突風に強いのはなぜか — 1段PID vs カスケード

最後に、この章の主張「まず角速度」を正面から検証します。 対戦カードは、角度1段PID(角度誤差からPIDで直接トルクを決める)対 カスケード。どちらも同じ機体・同じむだ時間で、 それぞれベストに調整してあります。違いは1点だけ—— 角速度の情報をどこから得るかです。カスケードはジャイロで直接測ります。 1段PIDは角速度を測る場所を持たないので、角度(推定フィルタ経由)を 微分して作るしかありません。フィルタと微分でおよそ 30 ms 遅れた 角速度もどきです。

条件: 突風=0.2 秒間の一定トルク外乱(角加速度換算)。両者ともむだ時間 12 ms・ 角度は時定数 20 ms の推定フィルタ経由。1段PIDのレート推定は微分+10 ms フィルタ。 ゲインは各構成で外乱応答が最良になるよう探索済み(1段: \(K_p^{rate}\)相当 0.225・ 角度換算 3.0 / カスケード: 0.25・11.9)。 探索の再現は notes/qa_log.md 参照。

同じ強さの突風に対して、カスケードは最大傾き・復帰時間とも半分以下です。 1段PID側のゲインをこれ以上上げると(チェックボックスで体験できます)発振・発散します。 遅れた角速度もどきで強く踏ん張ろうとすると、遅れの分だけ「踏むタイミング」がずれて、 自分で揺れを育ててしまうからです。 勝敗を分けたのは制御則の賢さではなく、速い測定(ジャイロ)を 速いループに直結できる構造——これがカスケード制御の本当の強みです。

まとめ

2次系近似は「\(\zeta\) を選べば終わり」の明快な設計でしたが、この章の PID は まだ「使った」だけです。次章では PID そのものを分解します——教科書の式から、 不完全微分・微分先行・積分の実装まで、実機で動くコードの中身へ。 さらに第7章では同じプラントを周波数の目(ループ整形)で見直し、 5.4 節の「崖」の正体を種明かしします。

本章の対話記録: notes/qa_log.md の Q5-1〜Q5-5。数値実験の再現コードは notes/calc/cascade_bandwidth_study.py(帯域分離), cascade_integrator_study.py(積分器の役割)。 実機ゲインはファームウェア params.cpp の飛行実績値(2026-06-27)。