Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

高度制御の理論的導出 #328

Closed
Tracked by #319
tmori opened this issue Sep 12, 2024 · 54 comments
Closed
Tracked by #319

高度制御の理論的導出 #328

tmori opened this issue Sep 12, 2024 · 54 comments

Comments

@tmori
Copy link
Contributor

tmori commented Sep 12, 2024

設計メモ

  • ミキサーは入れないでおく。
    • 変換できるという前提をおく。
    • 仮に変換でマイナス値になった場合、0にするというのを入れると、不連続なるので理論的な解析が難しい。

ブロック線図

スクリーンショット 2024-09-13 10 13 28

ローター側

$\dot{\Omega}(t) = (K_r u(t) - \Omega(t))/ T_r$

  • $K_r$ - ローターのゲイン定数
  • $T_r$ - ローターの時定数
  • $u(t)$ - ローターのデューティー比 ($0.0 \le d(t) \le 1.0$)

$\Omega(s)/U(s) = G(s) = K_r/(T_r s + 1)$

$T= N A \Omega^{2}$

$N$ はローター数で、今回は4。

ここで、 $U(s)$ は、平衡状態からの差分で考える。

$U(s) = U_0 + \Delta U(s)$

ここから、伝達関数を求めるとこうなる。

スクリーンショット 2024-09-13 8 31 36

ちなみに、 $T(s) $ はこうなる。

$T(s) = T_0 + \Delta T(s)$

高度運動

z軸はNED座標系。

対象の運動方程式:

$\ddot{z} = -\frac{u(t)}{m} + g - \frac{d}{m} \dot{z}$

ここで $u(t) = T_0 + \Delta T(t)$ として、 $T_0$ の項は、重力項を打ち消すものとする。

ラプラス変換:

$s^{2}Z(s) = -\frac{\Delta T(s)}{m} - \frac{d}{m} s Z(s)$

$Z(s) = -\frac{\Delta T(s)}{ms^{2}} - \frac{d}{ms} Z(s)$

両辺に $ms$ をかけて、右辺の $Z(s)$ を左辺に移動してまとめる。

$(ms + d) Z(s) = -\frac{\Delta T(s)}{s^{1}} $

$Z(s) = -\frac{ \Delta T(s)}{s(ms + d)}$

@tmori
Copy link
Contributor Author

tmori commented Sep 12, 2024

注意:この情報は間違っているので、このスレッドの後を参照。

補足情報

$( \Delta T(s) / \Delta U(s) )$ の導出:

線形化では、動作点 $( U_0 )$ に対する入力の小さな変動 $( \Delta U(s) )$ に対するトルクの変動 $( \Delta T(s) )$ を考えます。具体的には、次のように展開されます:

  1. 元の関係式:
    $T(s) = N A \Omega(s)^2 = N A \left( \frac{K_r}{T_r s + 1} U(s) \right)^2$

  2. 動作点周りでの摂動
    入力 $( U(s) )$ を定常値 $( U_0 )$ とその変化 $( \Delta U(s) )$ に分けます:
    $U(s) = U_0 + \Delta U(s)$
    このとき、 $( U(s)^2 )$ は次のように展開されます:
    $U(s)^2 = (U_0 + \Delta U(s))^2 = U_0^2 + 2 U_0 \Delta U(s) + \Delta U(s)^2$
    ここで、高次の小さな変動項 $( \Delta U(s)^2 )$ は無視できるため、次のように近似できます:
    $U(s)^2 \approx U_0^2 + 2 U_0 \Delta U(s)$

  3. トルクの変動 $( \Delta T(s) )$ を求める
    これにより、トルク $( T(s) )$ の変動は次のように表されます:
    $T(s) = N A \left( \frac{K_r}{T_r s + 1} \right)^2 (U_0^2 + 2 U_0 \Delta U(s))$
    したがって、トルクの変動 $( \Delta T(s) ) $ は次の形になります:
    $\Delta T(s) \approx 2 N A U_0 \cdot \left( \frac{K_r}{T_r s + 1} \right)^2 \Delta U(s)$

  4. 線形化された伝達関数 $( \frac{\Delta T(s)}{\Delta U(s)} )$
    動作点周りの小さな摂動に対するトルクの変動に対して、伝達関数は次のように表されます:
    $\frac{\Delta T(s)}{\Delta U(s)} = 2 N A U_0 \cdot \frac{K_r^2}{(T_r s + 1)^2}$

結論:

線形化された摂動系の伝達関数 $( \frac{\Delta T(s)}{\Delta U(s)} )$ は次のようになります:

$\frac{\Delta T(s)}{\Delta U(s)} = 2 N A U_0 \cdot \frac{K_r^2}{(T_r s + 1)^2}$

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

プラント全体

ローターの伝達関数

$\frac{\Delta T(s)}{\Delta U(s)} = 2 N A U_0 \cdot \frac{K_r^2}{(T_r s + 1)^2}$

高度運動の伝達関数

$\frac{Z(s)}{\Delta T(s)} = -\frac{1}{s(ms + d)}$

プラント全体の伝達関数

$\frac{Z(s)}{\Delta U(s)} = -\frac{2 N A U_0 K_r^2}{s(ms + d)(T_r s + 1)^2}$

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

制御側

$C(s) = K_p + K_i/s + sK_d$

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

各種パラメータ

以下を前提とする
#326

  • m: 0.71
  • N: 4
  • Kr: 2896(最大回転数)
  • Tr: 1.93e-02
  • A: 8.3x10-7
  • $U_0$ : $\sqrt{mg/NA Kr^{2}}$
    • ホバリング状態であり、 $T_0$ が、重力と一致する
    • $T_0 = NA K_r^{2} U_0^2$ から、 $U_0$ を求めた

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

L(s) を求める

$C(s) = K_p + K_i/s + sK_d = \frac{K_d s^{2} + Kp s + Ki}{s}$

$P(s) = -\frac{2 N A U_0 K_r^2}{s(ms + d)(T_r s + 1)^2}$

$L(s) = C(s) P(s) = -\frac{2 N A U_0 K_r^2(K_d s^{2} + Kp s + Ki)}{s^{2}(ms + d)(T_r s + 1)^2}$

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

閉ループ特性方程式 1+𝐿(𝑠)を求める

$1 + L(s) = \frac{s^{2}(ms + d)(T_r s + 1)^2 - 2 N A U_0 K_r^2(K_d s^{2} + Kp s + Ki)}{s^{2}(ms + d)(T_r s + 1)^2}$

展開された分子を整理した結果は次の通りです:

$\text{分子} = Tr^2 m s^5 + (Tr^2 d + 2 Tr m) s^4 + (2 Tr d + m) s^3 + (d - 2 A K_d K_r^2 N U_0) s^2 - 2 A K_p K_r^2 N U_0 s - 2 A K_i K_r^2 N U_0$

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

うーん、そもそも不安定であることが判明してしまった。まいった。

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

わかった!NED座標系だから、PID制御パラメータは全部マイナスにしないといけないということか!

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

調整後のパラメータ

{
    "plants": [
        {
            "comment": "Rotor",
            "num": [
                "2 * N * A * U0 * Kr**2"
            ],
            "den": [
                "Tr**2",
                "2*Tr",
                "1"
            ]
        },
        {
            "comment": "Altitude",
            "num": [
                "-1"
            ],
            "den": [
                "m",
                "d",
                "0"
            ]
        }
    ],
    "controller": {
        "num": [
            "Kd",
            "Kp",
            "Ki"
        ],
        "den": [
            "1",
            "0"
        ]
    },
    "constants": {
        "Kp": -3.6182108001838924,
        "Ki": -0.1,
        "Kd": -0.5599136193021486,
        "m": 0.71,
        "g": 9.81,
        "N": 4,
        "d": 0.05,
        "A": 8.3e-7,
        "Tr": 1.93e-02,
        "Kr": 2896,
        "U0": "math.sqrt( (m * g) / (N * A * (Kr**2)) )",
        "Wc": 20,
        "PM": 30
    }    
}

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

閉ループ伝達関数の極

左反平面!

スクリーンショット 2024-09-13 15 13 23
システムの極: [-7.81364819e+01 +0.j         -8.31995767e+00+21.83536742j
 -8.31995767e+00-21.83536742j -8.89321092e+00 +0.j

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

ステップ応答

スクリーンショット 2024-09-13 15 14 59

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

ボード線図と位相線図

スクリーンショット 2024-09-13 15 16 10
ゲイン余裕 (Gain Margin): 11.013343356723455 dB
位相余裕 (Phase Margin): 30.009422184890894 degrees
ゲイン余裕発生周波数: 44.97084634065474 rad/s
位相余裕発生周波数: 20.059957375420947 rad/s

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

箱庭での実験結果

このパラメータを使ってもうまくいかなかった。
振動してしまう。

以下にしたら良い感じだった。

PID_PARM_ALT_Kp 1.0
PID_PARM_ALT_Ki 0.10
PID_PARM_ALT_Kd 50.0

箱庭の制御は高度を正負反転しているので、正の値が正しい

スクリーンショット 2024-09-13 16 47 03

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

このギャップがどこからくるものなのか?

  • ミキサーの不連続性による影響もありそう。結構、推力をマイナスにしようとしている
  • そもそも推力値が物理的な上限、下限に入ってない可能性もあるか。。
  • 離散時間での誤差もありそう
  • 制御周期の問題もあるかも

@tmori
Copy link
Contributor Author

tmori commented Sep 13, 2024

検証のためにミキサーなしでやってみよう

プラント

$Z(s) = -\frac{ \Delta T(s)}{s(ms + d)}$

$T(s) = T_0 + \Delta T(s)$

$T_0 = mg$

制御

$C(s) = K_p + K_i/s + sK_d$

結果

伝達関数ベースで導出したパラメータを箱庭ドローンシミュレータに設定したが同じ動きにならなかった。

うーん、やっぱり、根本的なところで何かが違う気がするなー。こまった

@kenjihiranabe
Copy link
Collaborator

kenjihiranabe commented Sep 13, 2024

あれ?

  1. 元の関係式:
    $T(s) = N A \Omega(s)^2 = N A \left( \frac{K_r}{T_r s + 1} U(s) \right)^2$

ですが、これ違ってませんか?

$T(t) = N A \Omega(t)^2$

なので、 $f(t)^2$ をラプラス変換する必要があって( $\Omega(t)^2$ )、これは不能です。ですが、 $t$ の領域で $U_0$$T_0$ のまわりで線形化することはできて、そこからラプラス変換ですね。カンファレンス聴きながら、もう一回式を追ってみます。

@kenjihiranabe
Copy link
Collaborator

#328 (comment)

$\Delta T(s)/\Delta U(s)$ は通常の一時遅れでした。(U->Ω->T)

$\Delta T(s)/\Delta U(s) = 2 \Omega_0 N A \frac{K_r}{Tr s + 1}$

EAE0DC0D-99F0-45A6-B60C-82BE9E47AD65_4_5005_c
2B95BF6E-EA8C-4DE6-8E73-0CBEB01CD1C8

@kenjihiranabe
Copy link
Collaborator

とりあえず、全体の流れを整理して、式をもう一度作ってみた。

  • u でなくΔu を使う(そうしないと、ΔΩが作れない)-> U0 を足さない
  • Lの先頭のマイナスが気になる。z座標系をにしたときに、L(s)+1 = 0 の極が異なってしまう(まだどこかミスしている)。
    IMG_4511

@kenjihiranabe
Copy link
Collaborator

野波先生の 式(2.92) と上記 P(s) は一致するので、よしとする。

@kenjihiranabe
Copy link
Collaborator

zが下向きの時、u=duty や Ωを上げる方向なので、Kp はマイナス値になるんですね。

@kenjihiranabe
Copy link
Collaborator

手計算した結果、この式と全く一緒になりました!
https://colab.research.google.com/drive/14hSlp1h4ffHOOTq3G3upaA6x1kknOPqh

@kenjihiranabe
Copy link
Collaborator

いろいろ Kp, Ki, Kd を触ってみて、再度、理論的に分かったこと。

  • Ki は不要。高度の運動方程式に 1/s があるので、「内部モデル原理」により、誤差は必ず 0 になる。
  • Kd は安定化のために必ず必要。ないと位相が -180 を超えてしまう。

Kp = -1, Ki=0, Kd = -10 で位相余裕を確保してみた。

image

@tmori
Copy link
Contributor Author

tmori commented Sep 19, 2024

箱庭で実験した結果です。

測定条件

  • 目標高度:-3m
  • Kp: -1
  • Kd: -1, -10

測定結果

Kp=-1, Kd=-1

目標高度に到達しましたが、整定時間が長いのと、結構振動しますね。。

NG c(Steady state value)  : -2.999   (Target: 0±-0.030 m)
OK T_r(Rise time)         : 1.550 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 1.659 s (Target: ≤ 5.000 s)
NG O_s(Maximum overshoot) : 2.216   (Target: ≤ 1.000 m)
NG T_s(5% settling time)  : 79.721 s (Target: ≤ 20.000 s)
スクリーンショット 2024-09-19 11 20 29

Kp=-1, Kd=-10

目標高度に到達しました。整定時間も短くなりましたが、まだ振動しますね。。

NG c(Steady state value)  : -3.000   (Target: 0±-0.030 m)
OK T_r(Rise time)         : 1.614 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 1.687 s (Target: ≤ 5.000 s)
NG O_s(Maximum overshoot) : 1.646   (Target: ≤ 1.000 m)
NG T_s(5% settling time)  : 27.410 s (Target: ≤ 20.000 s)
スクリーンショット 2024-09-19 11 22 46

@tmori
Copy link
Contributor Author

tmori commented Sep 19, 2024

ちなみに、自分の方で箱庭用にパラメータ調整した結果はこんな感じです。

  • Kp = -10
  • Ki = -0.1
  • Kd = -500
NG c(Steady state value)  : -3.001   (Target: 0±-0.030 m)
OK T_r(Rise time)         : 1.474 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 0.981 s (Target: ≤ 5.000 s)
OK O_s(Maximum overshoot) : 0.129   (Target: ≤ 1.000 m)
OK T_s(5% settling time)  : 2.031 s (Target: ≤ 20.000 s)
スクリーンショット 2024-09-19 11 25 18

これに対して、Ki = 0.0にしてみたところ、結果は、ほぼ同じですね。

NG c(Steady state value)  : -3.000   (Target: 0±-0.030 m)
OK T_r(Rise time)         : 1.473 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 0.980 s (Target: ≤ 5.000 s)
OK O_s(Maximum overshoot) : 0.129   (Target: ≤ 1.000 m)
OK T_s(5% settling time)  : 2.031 s (Target: ≤ 20.000 s)

@kenjihiranabe
Copy link
Collaborator

ありがとう! Kp=-1, Ki=0, Kd=-1 で線形と箱庭で比較。
線形
image
箱庭
image

結構線形モデルと挙動が違うなぁ。線形だとダンパが効くけど、箱庭では効いていない。
ダンパになるのは、Kd と空気抵抗係数、だけど、空気抵抗係数はそろえている?

@tmori
Copy link
Contributor Author

tmori commented Sep 19, 2024

結構線形モデルと挙動が違うなぁ。線形だとダンパが効くけど、箱庭では効いていない。
ダンパになるのは、Kd と空気抵抗係数、だけど、空気抵抗係数はそろえている?

空気抵抗係数は 0.05 で、一致させていますね。

箱庭のパラメータ:

"airFrictionCoefficient": [ 0.05, 0 ],

@kenjihiranabe
Copy link
Collaborator

ありがとう。なるほど。そうなると、そもそも線形化したモデルは、箱庭のモデルから乖離している気がするなぁ。。。どこか計算間違っているのかなぁ。二人とも同じ答えになったしなぁ。。。どう詰めていくのかが今は不明です。
箱庭側のBode線図って実測(システム同定)でかけないものか、、、control にそんな機能あるといいのにな、Chirp 信号を入れて測るらしいが、、、
https://blog.control-theory.com/entry/2024/03/28/153529

@kenjihiranabe
Copy link
Collaborator

ここで、箱庭のパラメータと合わせている、ということでしたが、

"airFrictionCoefficient": [ 0.05, 0 ],

たしか、機体側にもこのパラメータなかったでしたっけ?(確か、x, y, z の3方向で別々に設定していたはず)

@tmori
Copy link
Contributor Author

tmori commented Sep 19, 2024

ここで、箱庭のパラメータと合わせている、ということでしたが、

"airFrictionCoefficient": [ 0.05, 0 ],

たしか、機体側にもこのパラメータなかったでしたっけ?(確か、x, y, z の3方向で別々に設定していたはず)

こちらは、機体側のパラメータになりますね。x, y, z の3方向で別々の空気摩擦抵抗設定できるようにする話はまだ未対応ですね。。

@kenjihiranabe
Copy link
Collaborator

https://github.com/toppers/hakoniwa-px4sim/blob/267c15cdd1ae9b2e83df08474d9c00bd8f791e91/hakoniwa/src/config/drone_config.hpp#L163C1-L164C1

ここは、このコンフィグパラメータを読んでいるということであってますか?

@tmori
Copy link
Contributor Author

tmori commented Sep 19, 2024

https://github.com/toppers/hakoniwa-px4sim/blob/267c15cdd1ae9b2e83df08474d9c00bd8f791e91/hakoniwa/src/config/drone_config.hpp#L163C1-L164C1

ここは、このコンフィグパラメータを読んでいるということであってますか?

はい、あってます。

@tmori
Copy link
Contributor Author

tmori commented Sep 21, 2024

箱庭のPID制御プログラム、よくみてみたら誤りがありました。

    double calculate(double sp, double input) {
        target = sp;
        double error = target - input;
        integral += error * delta_time;

        double derivative = (first_time) ? 0.0 : error - prev_error;
        first_time = false;
        prev_error = error;
        return Kp * error + Ki * integral + Kd * derivative;
    }

PID制御式(連続時間):
スクリーンショット 2024-09-22 8 25 26

PID制御式(離散時間):
スクリーンショット 2024-09-22 8 26 00

Kdのところ、Δtで割らないとダメですね。。

- double derivative = (first_time) ? 0.0 : error - prev_error;
+ double derivative = (first_time) ? 0.0 : (error - prev_error) / delta_time;

@tmori
Copy link
Contributor Author

tmori commented Sep 21, 2024

さらに制御周期は連続系と同じようにシミュレーション周期と一致させてみたら

NG c(Steady state value)  : -3.000   (Target: 0±-0.030 m)
OK T_r(Rise time)         : 2.044 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 0.646 s (Target: ≤ 5.000 s)
OK O_s(Maximum overshoot) : 0.000   (Target: ≤ 1.000 m)
OK T_s(5% settling time)  : 2.788 s (Target: ≤ 20.000 s)

振動が止まりました。ちなみにミキサーエラーはなしでした(Ω^2はマイナスになってない)

スクリーンショット 2024-09-22 8 31 40

@tmori
Copy link
Contributor Author

tmori commented Sep 21, 2024

Kp=1.0, Kd=0.3の結果

Kdが連続系と少しずれてい流?

スクリーンショット 2024-09-22 8 58 07

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

throttle_gainのパラメータが悪さしていました。

PID制御で決めた値は以下のように推力に変換されます。

    double run_thrust_control(double power) {
        return (this->mass * this->gravity) + (this->throttle_gain * power);
    }
  • (this->mass * this->gravity)は、 $T_0$ に対応します。
  • powerは、PID制御で決まる値です。
    • 決めた値は、±PID_PARAM_MAX_POWER でリミット入れてます。
    • 今回採用した値:PID_PARAM_MAX_POWER 9.81

ここで、throttle_gain は10.0でしたが、これは連続系での解析では1.0に相当するので、これを連続系に合わせて、1.0に変更しました。

結果:
スクリーンショット 2024-09-22 10 02 59

NG c(Steady state value)  : 3.000   (Target: 0±-0.030 m)
OK T_r(Rise time)         : 1.582 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 1.098 s (Target: ≤ 5.000 s)
OK O_s(Maximum overshoot) : 0.022   (Target: ≤ 1.000 m)
OK T_s(5% settling time)  : 4.253 s (Target: ≤ 20.000 s)

PIDパラメータ:

PID_PARM_ALT_Kp 1.0
PID_PARM_ALT_Ki 0.0
PID_PARM_ALT_Kd 1.0

グラフとしては同じようにオーバーシュートしてきました。ただ、応答時間は約10倍くらい遅いのが気になるところ。。

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

ミキサーの種類

ミキサーは、linear でないと、同じ結果になりませんね。
Noneの場合は、ホバリングしませんでした。。

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

伝達関数とシミュレーションの条件を変えてみる

伝達関数側 :ローターを外して、直接、推力を与える
シミュレータ:ミキサーとロータを外して、直接、推力を与える

結果、ほぼ同じ振る舞いで、同じ時間スケールに見える。

伝達関数側のステップ応答

image

シミュレータの応答

スクリーンショット 2024-09-22 10 58 42

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

考察

伝達関数側のモデルが、シミュレータの側のものと一致していない可能性があるのでは?

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

シミュレータの処理の流れ(高度制御)

  1. [高度]→PID制御→[推力]
  2. [推力]→ミキサ→[Duty]
  3. [Duty]→ロータ→[RotorSpeed]
  4. [RotorSpeed]→スラスタ→[推力]

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

ロータの処理

double rotor_omega_acceleration(
    double Kr /* gain constant */,
    double Tr /* time constant */,
    double omega, /* in rpm */
    double duty_rate /* 0-1 of PWM */)
{
    /**
     * See Nonami's book (2.48)
     * G(s) = Kr/(Tr s + 1). 
     */
    return (Kr * duty_rate - omega) / Tr;
}

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

スラスタの処理

double rotor_thrust(
    double A, /* the A parameter in Trust = A*(Omega)^2 */
    double omega /* in rpm */ )
{
    /**
     * Nonami's book (2.50)
     * T = A * (Omega)^2
     */
    return A * (omega * omega);
}

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

ミキサー処理

        // linearized version
        PwmDuty run_linear(double mass, double thrust, double torque_x, double torque_y, double torque_z)
        {
            const double m = mass; // pass through mass from caller
            const int N = ROTOR_NUM;
            const double g = GRAVITY;
            const double A = param_A;
            const double T0 = m*g; // equibilium
            const double omega0 = std::sqrt(T0/(N*A)); // equibilium
            const double delta_T = thrust - T0;
            const double Kr = param_Kr;
            
            glm::dvec4 delta_U = glm::dvec4(delta_T, torque_x, torque_y, torque_z);
            glm::dvec4 delta_omega_times_omega0 = 0.5 * MA_inv * delta_U;
            glm::dvec4 delta_omega = delta_omega_times_omega0/omega0;

            glm::dvec4 omega = omega0 + delta_omega;

            PwmDuty duty = {};
            for (int i = 0; i < ROTOR_NUM; i++) {
                if (omega[i] < 0) {
                    if (mixer_info.enableErrorLog) {
                        std::cout << "ERROR: thrust:" << thrust << " tx: " << torque_x << " ty: " << torque_y << " tz: " << torque_z << std::endl;
                        std::cout << "ERROR: Invalid caluculation of Omega to duty because of omega("<< i << ") is minus value(" << omega[i] << ")..." << std::endl;
                    }
                    omega[i] = 0.0;
                }
                else {
                    duty.d[i] = omega[i] / Kr;
                }
                if (mixer_info.enableDebugLog) {
                    std::cout << "Motor " << i << ": Omega = " << omega[i] << ", PWM Duty = " << duty.d[i] << std::endl;
                }                
            }
            return duty;
        }

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

ブロック線図をもう一度

スクリーンショット 2024-09-22 11 54 04

@kenjihiranabe
Copy link
Collaborator

抜けてないと思う。位置だけなら、ミックス行列かけても単にN倍なので。

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

プログラムからMixerの伝達関数を求めてみる

delta_T = thrust - T0
delta_U = glm::dvec4(delta_T, torque_x, torque_y, torque_z)
delta_omega_times_omega0 = 0.5 * MA_inv * delta_U
delta_omega = delta_omega_times_omega0/omega0
omega = omega0 + delta_omega;
duty.d[i] = omega[i] / Kr;

トルクは0と考えてよくて、MA_inv は、おそらく 1/A だけが残る。

そうすると、こうなるのでは?

$\frac{\Delta U}{\Delta T} = \frac{1}{2A\Omega_0 K_r}$

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

レッツとらい!

プログラムの都合上、Controller側にMixerかけないので、Plant側に書いた。

{
    "plants": [
        {
            "comment": "Mixer",
            "num": [
                "1"
            ],
            "den": [
                "2 * N * A * W0 * Kr"
            ]
        },
        {
            "comment": "Rotor",
            "num": [
                "2 * N * A * W0 * Kr"
            ],
            "den": [
                "Tr",
                "1"
            ]
        },
        {
            "comment": "Altitude",
            "num": [
                "-1"
            ],
            "den": [
                "m",
                "d",
                "0"
            ]
        }
    ],
    "controller": {
        "num": [
            "Kd",
            "Kp",
            "Ki"
        ],
        "den": [
            "1",
            "0"
        ]
    },
    "constants": {
        "Kp": -1,
        "Ki": 0.0,
        "Kd": -1,
        "m": 0.71,
        "g": 9.81,
        "N": 4,
        "d": 0.05,
        "A": 8.3e-7,
        "Tr": 1.93e-02,
        "Kr": 2896,
        "W0": 1448,
        "U0": "math.sqrt( (m * g) / (N * A * (Kr**2)) )",
        "Wc": 1002,
        "PM": 90
    }
}

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

伝達関数モデルのステップ応答結果

ボード線図:
image

ゲイン余裕 (Gain Margin): inf dB
位相余裕 (Phase Margin): 59.34687263836736 degrees
ゲイン余裕発生周波数: nan rad/s
位相余裕発生周波数: 1.6457394067612157 rad/s

ステップ応答:
image

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

箱庭ドローンシミュレータの結果

NG c(Steady state value)  : 1.000   (Target: -1±-0.010 m)
OK T_r(Rise time)         : 1.576 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 1.198 s (Target: ≤ 5.000 s)
OK O_s(Maximum overshoot) : 0.085   (Target: ≤ 1.000 m)
OK T_s(5% settling time)  : 4.373 s (Target: ≤ 20.000 s)
スクリーンショット 2024-09-22 12 17 54

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

おー、ほぼ理論通りの結果になった!!
やったー。

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

ログリプレイしてみた。

2024-09-22.13.02.45.mov

@tmori
Copy link
Contributor Author

tmori commented Sep 22, 2024

パラメータ調整してみる。

  • Kp = 1.0
  • Ki = 0.0
  • Kd = 1.0

考察

理論ベースでの考察として、

  • 位相余裕は60度くらいあって安定感はある。
  • カットオフ周波数は、1.6rad/secくらいしかないので、即応性が気になる。

実際、箱庭ドローンシミュレータでシミュレーションしてみると、

  • 整定時間が4.373 sなので、もう少し早くしたい。

トライ

理論ベースでパラメータ調整してみた。

  • Kp = 10
  • Ki = 0
  • Kd = -4
ゲイン余裕 (Gain Margin): inf dB
位相余裕 (Phase Margin): 61.563315860183536 degrees
ゲイン余裕発生周波数: nan rad/s
位相余裕発生周波数: 6.053714347494267 rad/s

image

位相余裕はほぼそのままで、カットオフ周波数は3倍近くなった!

ステップ応答

image

箱庭ドローンシミュレータで実験してみる。

オーバーシュートがなくなって、整定時間が1秒近く短縮した。

NG c(Steady state value)  : 1.000   (Target: -1±-0.010 m)
OK T_r(Rise time)         : 0.565 s (Target: ≤ 10.000 s)
OK T_d(Delay time)        : 0.384 s (Target: ≤ 5.000 s)
OK O_s(Maximum overshoot) : 0.033   (Target: ≤ 1.000 m)
OK T_s(5% settling time)  : 0.794 s (Target: ≤ 20.000 s)
INFO: DONE

スクリーンショット 2024-09-22 13 42 05

リプレイ結果:

2024-09-22.13.43.21.mov

@kenjihiranabe
Copy link
Collaborator

kenjihiranabe commented Sep 23, 2024

いい感じですね。理論とシミュレーションが合うのが気持ちいい。最初はカットオプが小さくて、ちょっと反応鈍いと思ったので、徐々に早めて、それでも位相余裕保てていて、とてもいいと思いました。

後で議論したいのは、

スクリーンショット 2024-09-22 11 54 04

これで「抜けている?」のところは、「抜けていない」と重む。

  1. プラントの入力はローターであり(ΔU)、トラスト(ΔT)でない

としておき、「PID の出力を ΔT でなく ΔU 」とすれば、吸収できてしまうと思う。つまりPIDの入力は z 残差、出力は ΔUとする。としてしまえば、一気に1つのPIDで、左のミキサーなしで解決可能。

もしかして、ミキサーありなしで共通のPIDを使いたい、ということであれば、そんな設計になるのかと思う。(でも分母分子がキャンセルするだけで、矛盾してしまわないか不安)。つまりミキサーを通す、というのは「プラント都合」なので、そのまま(プラントの入出力に従って)PIDすればよいと思う。

@tmori
Copy link
Contributor Author

tmori commented Sep 23, 2024

理解しました!

あとは、この辺りの線形モデル化のドキュメントで説明ですね。

@tmori
Copy link
Contributor Author

tmori commented Nov 6, 2024

こちらで管理するので、一旦、クローズ

toppers/hakoniwa-drone-education#25

@tmori tmori closed this as completed Nov 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants