メインコンテンツへスキップ
見出し画像

【StormWorks:A004】#40 高度一定制御を修正する

    前回取得できたデータ

    画像

    高度の収束に対してI項の寄与が少なく、D項は全く仕事してないように見える

    解釈の正確性を期すために、GPT5.6 Terra 高の回答

    データを入れたエクセルファイルを渡して検証を依頼。

    合ってる。Sheet 3 で、前回の推定はほぼ実測で確定した。

    項実測範囲判断P項-0.252 ~ +0.0177制御出力の主成分I項-0.0200 ~ +0.00119下限 -0.02 に飽和D項-0.00341 ~ +0.00228符号は正しいが、絶対量が小さい

    特にI項は、2,385 tick中622 tickで -0.0200 に張り付いている。手動上昇を離した直後の tick 4269 では、

    P = -0.22162 I = -0.02000 ← 飽和 D = -0.00122

    となっている。integral の下限 -100 と ki = 0.0002 による、理論上のI項下限 -0.02 と完全に一致する。UM-A004C_Record_2026-08-28.xlsx (3!A1:M2386)

    D項については「ゼロ」ではない。離した直後の tick 4233 は、

    P = -0.01704 I = -0.00010 D = -0.00341

    で、Pに対しては約20%ある。ただしこの時点の上昇速度は約 9.7 m/s なのに、Dの制動は -0.0034 しかない。その後はP項が大きくなる一方でD項は -0.003 → -0.001 へ減るので、機体を止めるほどの仕事はできていない。

    理由はその通りで、今のDは m/tick を使っているから。

    derivative = error - last_error

    固定ターゲットならこれは -高度変化量/tick。60 tick/s換算では、今の kd = 0.02 は実質 0.00033 × 上昇速度(m/s) くらいの弱さになる。

    さらに跳ねる直接の流れも、ログとLuaが一致している。

    手動中 → integral = 0 を毎tick実行 手動解除 → 必要だったホバー補正が消えた状態で自動へ戻る → I項はゼロから再学習 → しかし最大でも -0.02 まで → P項主体で大きく行き過ぎる

    なので、最低限ここは変えたい。

    -- 手動中に消さない -- integral = 0 ← 削除

    そしてI項は「積分値」ではなく「I項そのもの」を保持すると扱いやすい。

    local i_term = 0 -- 自動モード i_term = clamp(i_term + error * ki, -0.15, 0.15) gyro_updown = error * kp + i_term + derivative * kd

    この場合、最後に i_term * ki はしない。±0.15 なら、ログ上必要だった -0.10 前後の定常補正も作れる。

    次はDを上下速度として明示化するのがいい。

    vertical_speed = (alt - last_alt) * 60 d_term = -kv * vertical_speed

    これなら kv が「1 m/s上昇時にどれだけ下降側へ補正するか」になる。今の挙動なら、Iを残す・I上限を広げる・速度Dに替える、この3点でかなり素直になるはず。

    GPT5.6 Terra 高

    修正案に従ってPIDを修正する

    ◯if文内のintegral = 0を削除(今回はコメントアウト)

    ◯積分値ではなくI項そのものを保持する(error * ki自体を積み上げていく)
    ◯微分項ではなく上下位置情報の微分=上下速度として取り扱う

    おそらくした2つはコードの見やすさに起因した修正か?

    最終的なコード

    function clamp(x, min, max)
        return math.max(min, math.min(max, x))
    end
    
    local target_alt = nil
    local integral = 0
    local last_error = 0
    
    local last_alt = nil
    local i_term = 0
    
    function onTick()
        local updown = input.getNumber(1) --  -11
        local alt    = input.getNumber(2) -- 
    
        local deadzone = 0.01
    
        -- PID
        local kp = 0.05
        local ki = 0.0002
        local kv = 0.005
    
        -- 
        if target_alt == nil then
            target_alt = alt
        end
    
    
        if last_alt == nil then
            last_alt = alt
        end
    
        local vertical_speed = (alt - last_alt) * 60
        last_alt = alt
    
    
    
        local manual = math.abs(updown) > deadzone
    
        local gyro_updown
    
    	--Variable declaration for data monitoring
        local error = 0
        local p_term = 0
        local d_term = 0
    
        if manual then
            -- 
            -- 
            target_alt = alt
    
            -- PID
            -- integral = 0
            last_error = 0
    
            gyro_updown = updown
        else
            -- 
            error = target_alt - alt
    
    		i_term = clamp(i_term + error * ki, -0.15, 0.15)
    
            p_term = error * kp
    		d_term = -kv * vertical_speed
    
            gyro_updown = p_term + i_term + d_term
        end
    
        -- 
        gyro_updown = clamp(gyro_updown, -1, 1)
    
        output.setNumber(1, gyro_updown)
        output.setNumber(6, target_alt) -- 
    	
    	output.setNumber(9, p_term)
    	output.setNumber(10, i_term)
    	output.setNumber(11, d_term)
    
    end

    データ採取

    安定飛行後、大きく上昇、その後大きく下降、その後微小操作の順

    画像

    上昇時は大きくズレてその後追従、下降時はズレが少なく安定してるように見えた。概ねそのような軌道をしてるが、上昇後に外から見て高度が安定してるように見えたのは、ターゲットよりも下の高度で安定してたのか

    じゃあパラメータを修正。ではなく

    上昇後のオーバーシュート原因

    データを見ると

    画像

    AXIS4のインプットが1じゃなくなる→上昇操作終了→その後GYRO UP/DOWN OUTPUTがしばらく0.01に張り付く

    つまり制御量最大=フルブレーキ状態
    PID側をいくら操作しても意味がない。
    つまり今回の修正は見送り。

    ターゲット高度より下で収束:I項の数字が溜まりすぎ

    I項のパラメータ制御でなんとかするより、大きい値を積み足さない制御が必要

        if manual then
            -- 
            -- 
            target_alt = alt
    
            -- PID
            -- integral = 0
            last_error = 0
    
            gyro_updown = updown
    	else
       	 error = target_alt - alt
    
       	 local i_zone = 0.5
    
     	   if math.abs(error) < i_zone then
           	 i_term = clamp(i_term + error * ki, -0.15, 0.15)
    	    end
    
     	   p_term = error * kp
     	   d_term = -kv * vertical_speed
    
     	   gyro_updown = p_term + i_term + d_term
    	end

    というわけで制御の中核であるif文を修正して、自動制御中に目標高度との差が小さくなって初めてI項の積み足しを始める構成に変更

    再度データ採取


    画像

    完全ビタ付けとは行かないが、さっきと違ってターゲット高度に収束している。微小操作時の初期応答での跳ねる動作が気になるが、ここまで来ると後はパラメータを調整すれば良い。

    というわけで一旦完成。おまけで飛行動画

    上下移動テスト。いい感じ

    高度維持制御無しで上下にフラフラしてる初期型、高度維持制御を付けただけで跳ねるからドンピシャに合わせづらい改良型を経て、格段に位置合わせしやすくなった。


    あっ

    画像
    画像

    回転数過大状態で上昇させたらひっくり返った

    今後は強風状態でも維持できるように位置固定制御かなー

     
     
    ストームワークスとCodexとBlenderで思いつくままわちゃわちゃやってます。 発信というより備忘録メイン。 絵を描いたり小説書いたりもしたり。 創作設定を山ほど作るのも好き

    あなたへのおすすめ