Unity PID 制御アルゴリズムの視覚学習
序章
最近シングルチップマイコンの勉強をしていたときに、偶然PIDアルゴリズムについて調べたのですが、PIDというのは実に洗練されたアルゴリズムです。バランスカーのバランス、ドローンの飛行制御、温度制御などに使用できます。。待ってください、これは非常に便利で強力です。そこで、Unity を使用して、pid 調整プロセスをシミュレートする小さなプログラムを作成する予定です。
アイデア、必要性
コミュニティに水を供給するための1000m3のプールがあり、コミュニティの水の使用量(プールの水の消費量)は時間の経過とともに変化します、つまり、負荷は時々変化します。プールに水を注入できるスイッチを設置し、プールの水位を可能な限り 500m3 に保つように注水スイッチの開閉を制御する制御アルゴリズムを作成する必要があります。
吐水・注水の最大速度は100m3/s、
水位センサーの読み取り頻度は1回/sです。
一連の考え
水の消費量
水の消費率はランダムに変化しますが、この指標は温度と同様に突然変化することができないため、水の消費率をシミュレートするための小さなアルゴリズムを作成しました。
private bool bGrow = true;
public float CurrValue {
get; private set; }
private void MakeData()
{
float mid = ( maxValue - minValue ) * 0.5f + minValue;
float g;
if (bGrow)
g = CurrValue > mid ? Mathf.InverseLerp(mid*1.2f, maxValue, CurrValue) : 0.1f;
else
g = CurrValue < mid ? Mathf.InverseLerp(mid*0.8f, minValue, CurrValue) : 0.1f;
// g为概率
if (RandomBool(g))
bGrow = !bGrow;
if( bGrow )
CurrValue += Random.Range(0f, 5f);
else
CurrValue -= Random.Range(0f, 5f);
}
注:
実際、ここでのより洗練された方法は、パーリン ノイズを使用して消費速度の変化をシミュレートすることですが、この記事を書いた時点では、パーリン ノイズの魔法についてまだ学んでいなかったので、シミュレートするためのぎこちないアルゴリズムを作成しました。それ:
インターネットを長い間検索しましたが、これより洗練されたアルゴリズムが見つかりませんでした。自分で書きました。一般的な意味は、現在の速度傾向が上昇傾向にあるか下降傾向にあるかを示す値を使用し、次の値を計算しますbool
。データを更新するたびに反転する確率が高く、現在値が最大値または最小値に近いほど反転する確率が高く、逆に中間値に近いほど反転する確率が高くなります。反転が小さいほど、この傾向に応じてランダムな値をその都度増減させます。このようにして、水使用量の速度曲線を大まかにシミュレートし、ピーク水使用量の設定に合わせて大きなサイクルで往復させることができます。
制御アルゴリズム
現在誤差=目標水位−現在水位累積誤差=累積誤差+今回誤差※ Δ時間誤差変化率=(今回誤差−前回誤差)/Δ時間予想注水速度=今回誤差※K p +累積誤差※K i + 誤差変化率※ K d 注水速度 = Mathf.C ランプ(目標注水速度、0 、最大注水速度) 現在誤差 = 目標水位 - 現在水位 \\ 累積誤差 = 累積誤差 + 現在誤差 * ΔTime\\ エラー変化率 = (現在のエラー - 前回のエラー) / ΔTime\\ 予想される注水速度 = 現在のエラー * K_p + 累積誤差 * K_i + エラー変化率 * K_d\\ 注水速度 = Mathf.Clamp( 予想水射出速度、0、最大水射出速度)現在のエラー=目標水位−現在の水位累積誤差=累積誤差+現在のエラー∗Δ時間_ _ _エラー変化率=(現在のエラー−最後のエラー) / Δ時間_ _予想される注水速度=現在のエラー∗Kp+累積誤差∗K私は+エラー変化率∗Kd水注入速度=M a th f . Clam p (所望の水噴射速度、_ _0 、注水速度)_
// 每1秒读取一次水位传感器
private IEnumerator PIDController()
{
while (gameObject.activeSelf)
{
float err = targetPool - waterLeft;
totalErr += err * controlTime;
float de = (err - lastError) / controlTime;
lastError = err;
pidText.text = $"Kp={kp:0.0} Ki={ki:0.0} Kd={kd:0.0} Err={err:#00.00} TotalErr:{totalErr:00.00} deltaErr:{de:00.00}";
productionSpeed = Mathf.Clamp(err * kp + totalErr * ki + de * kd, 0f, 200f);
inSpeedTex.text = productionSpeed.ToString("000");
yield return new WaitForSeconds(controlTime);
}
}
水位を更新する
private void Update()
{
waterLeft = Mathf.Clamp(waterLeft + (productionSpeed - CurrValue) * Time.deltaTime, 0f, 1000f);
}
レンダリング
上の折れ線グラフは水の消費速度データ、下の折れ線グラフはプールの水位の履歴データ、左側のヒストグラムは現在の水位です。
チューニングプロセス
まず KI と KD をゼロに設定し、最初に KP を調整します。KP は実際には比例係数であり、主導的な役割を果たします。KP を徐々に増加させ、制御曲線がちょうど上下に振動するようになったら、少し下に調整してから、KD を調整します。 , KDは誤差を示す阻害要因です
トレンドは現在の調整トレンドを弱めるために使われますKDが大きすぎると高周波で発振し、小さすぎるとKPを抑える効果がありません振動。
最後に KI を調整します。KI はゲイン係数であり、誤差は長期誤差を補うために統合されます。
KP=1.5~2、KI=0.1~0.2、KD=0.1~0.2の場合に良好に動作します。基本的に制御曲線は安定した直線であり、使用量がどのように変化しても、水位は常に 500m3 (50%) に保たれます。