道具立ては揃いました。プラントのモデル(第3章)とその中の数字(第4章)を手に、 いよいよ姿勢制御に入ります。最初の設計判断は意外なところにあります—— 制御したいのは「角度」なのに、実機の制御ソフトはまず「角速度」を制御するのです。 この章では、その理由を物理から説き起こし、レートループ→角度ループの カスケード制御を実際に設計します。設計した制御系がどこまで攻められるのか、 「理想」と「現実(むだ時間)」の違いも体感してもらいます。
5.1 なぜ角度を直接制御しないのか — 積分遅れの連鎖
制御対象のマルチコプタは物理法則から逃れられません。運動方程式を思い出すと、 トルクは慣性モーメントをスケールファクターとして瞬時に角加速度になります (\(\dot{\omega} = \tau / I\)——加速度とは、実質的に力そのものです)。 しかし角速度はそうはいきません。角加速度を1回積分しなければ現れないので、 遅れます。角度はさらにもう1回積分——もっと遅れます。
角度の誤差だけを見てトルクを決める「1段」の制御では、入力と出力の間に 2階積分分の遅れが挟まります。突風で角度がずらされたとき、 その情報が角度に現れてから修正が効き始めるまでが遠く、 タイムリーに追いつけないおそれがあります。 そこで発想を変えます。まず角速度を制御するのです。 角速度はトルクの1階積分ですから、角度より速く反応でき、 突発的な外乱に早めの対処ができます。角速度制御がうまく行ったら、 その外側に角度の制御を重ねる——まず角速度が早めの対処をし、 その後、角度をきっちり決める。これがカスケード制御です。
5.2 カスケード制御 — 内側を「1」にして、外側が発注する
もう一つの見方があります。フィードバック制御の目的は、突き詰めれば 入力(指令)と出力の関係を 1 にすることです。 指令通りに出力が出るなら、それで勝ちです。 角速度ループが「1」を達成している——つまり、指令した角速度が そのまま実現される——なら、外側の角度ループは中身を気にする必要がありません。 「いまの角度誤差を解消するにはこの角速度が欲しい」と、 角速度制御系に発注すればよいのです。
5.3 内側レートループの設計 — 2次系近似
では内側ループを設計しましょう。第4章で組み立てたロール軸のプラント (正規化入力 → 角速度)は、モータの一次遅れと機体の積分でこう書けます:
これにレートのP制御(ゲイン \(K_{p}^{rate}\))を巻くと、閉ループは見慣れた 2次系になります:
2次系なら挙動は全部知っています。減衰係数 \(\zeta\) を決めれば オーバーシュートが決まり、\(\omega_n\) が速さを決めます。 そこで設計は「\(\zeta\) を選ぶ → ゲインが決まる」の一直線です:
たった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)\) と発注すれば
——時定数 \(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。
5.5 積分器は1本ごとに仕事が違う
ここまで内側も外側もP制御で話してきましたが、実機のファームウェアは 内側に I 項を持ち、外側にも入れています。 なぜ2本も積分器が要るのでしょうか。実は、置き場所によって消せる誤差が違うのです。
- 内側の I はトルクの定常外乱を消します。 重心のずれ、モータの個体差、プロペラの劣化——機体には常に一定の「トリムずれ」 トルクが乗っています。P制御だけだと、これを打ち消すトルクを出すために 誤差が残り続けます(下の実験Aで、角度に約 1.9° のオフセットが残ります)。
- 外側の I はレート計測のバイアスまで消せます。 ジャイロにバイアス(真値からの一定のずれ)があると、内側ループは 「計測された角速度」を指令に合わせるだけなので、真の角速度にはバイアス分の ずれが残り、角度がずれた所で釣り合ってしまいます。これは内側の I では 原理的に消せません——誤差がループの「測定」側に入っているからです。
下の教材で、外乱の種類と積分器の組み合わせを変えて確かめてください。
条件: 帯域比 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。
残る文字は 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.7 | 0.010 |
| pitch | \(1.43\times10^{-3}\) N·m/(rad/s) | 0.7 | 0.025 | |
| yaw | \(1.90\times10^{-3}\) N·m/(rad/s) | 0.8 | 0.010 | |
| 角度(外側) | roll | 5.0 1/s | 2.0 | 0.04 |
| pitch | 5.0 1/s | 2.0 | 0.04 |
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側のゲインをこれ以上上げると(チェックボックスで体験できます)発振・発散します。 遅れた角速度もどきで強く踏ん張ろうとすると、遅れの分だけ「踏むタイミング」がずれて、 自分で揺れを育ててしまうからです。 勝敗を分けたのは制御則の賢さではなく、速い測定(ジャイロ)を 速いループに直結できる構造——これがカスケード制御の本当の強みです。
まとめ
- トルク→角加速度は即時、角速度は積分1回、角度は積分2回の遅れ。 だからまず角速度を制御し、外側で角度を決める(カスケード)。
- フィードバックの目的は入出力を「1」にすること。内側が 1 なら、 外側は角速度を発注するだけでよい。理想では外側はPだけで一次遅れの綺麗な応答。
- 内側レートループは 2次系近似で設計できる: \(K_p^{rate} = 1/(4\zeta^2 K \tau)\)。第4章の同定値がそのままゲインになる。
- 外側の帯域は内側の 1/3 前後が最適——シミュレーションでは 0.26〜0.32 に応答最良(ITAE最小)の谷(むだ時間が長いほど低い側へ)。 むだ時間のある現実では攻めすぎると崖(12 ms なら比率 0.8 で発散)。
- 積分器は置き場所で仕事が違う: 内側I=トルク外乱、 外側I=計測バイアス。\(T_i\) は交差周波数の1桁下に置けば位相コスト約2°。
- 突風への強さの源泉は、ジャイロの速い測定を速い内側ループに直結する構造。 1段PIDは角速度を微分で作るしかなく、ベスト調整でも最大傾き・復帰とも2倍以上劣る。
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)。