レギュレータの使用例と応用例。 PID コントローラー - 完全な説明、アプリケーション。 連続調整器の出力機器の連携
自動制御システム (ACS)必要な動作モードを確立するために、制御オブジェクトの 1 つ以上のパラメータを自動的に変更するように設計されています。 ACS は、規制パラメータの指定値の一定性の維持、または所定の法律に従ってその変更を保証するか、管理の品質に関する特定の基準を最適化します。 たとえば、そのようなシステムには次のようなものがあります。
- 安定化システム、
- プログラム制御システム、
- 追跡システム
これは、どこにでも見られるかなり幅広い種類のシステムです。 しかし、これは Unity3D、そしておそらく特にゲームとどのような関係があるのでしょうか? 原則として、これは簡単です。ゲームプレイの要素としてシミュレーションを何らかの形で使用するゲームでは、自走砲が実装されます。そのようなゲームには、たとえば、Kerbal Space Programm、Digital Combat Simulator (旧名 Lock On)、Strike suit Zero、等 (もっと多くの例を知っている人は、コメントに書いてください)。 原則として、実際の物理プロセスをシミュレートするゲーム (モーション ダイナミクスを備えた単純な運動学を含む) は、いずれか 1 つまたは別の自走砲を実装できます。このアプローチはよりシンプルで自然であり、開発者はすでに、あらゆる種類のヴィシュネグラドスキー、リャプノフ、カルマン、チェビシェフ、その他のコロモゴロフなので、車輪を再発明せずに済みます。 それはすでに発明されているため、別の科学、つまり自動制御理論となっています。 ここで重要なのは、無理をしないことです。 問題は 1 つだけあります。彼らは TAU について、どこでも、誰に対しても話しているわけではありません。多くの場合、あまり明確ではなく、ほとんど話されていません。
ちょっとした理論
古典的な自動制御システムを次の図に示します。
自走砲の重要な要素は次のとおりです。 レギュレーターこれは、制御オブジェクトの状態を監視し、必要な制御則を提供するデバイスです。 制御プロセスには以下が含まれます。 制御誤差または誤差信号の計算 e(t) 望ましい はどう違いますか? 設定(設定値または SP ) およびプロセスの現在値 (プロセス値または PV )、その後、レギュレータは制御信号(操作値または MV ).
レギュレーターの一種としては、 比例積分微分 (PID) コントローラー、比例、積分、微分の 3 つの項の合計である制御信号を生成します。
ここで、ミスマッチ誤差、および制御則の - 比例、 - 積分、 - 微分コンポーネント (項) は、最終的な形式では次の式で記述されます。
比例成分P- いわゆるものを担当します 比例制御。その意味は、コントローラーの出力信号が、設定値からの制御変数 (不一致エラー、または残差とも呼ばれる) の偏差を打ち消すことです。 不一致誤差が大きいほど、コントローラの指令偏差も大きくなります。 これは最も単純かつ明白な制御法則です。 欠陥比例制御則は、レギュレータが特定の値で安定することはなく、比例係数の増加は常に自己発振を引き起こすというものです。 そのため、比例制御法則に加えて、積分法と微分法を使用する必要があります。
インテグラルコンポーネントI制御誤差を蓄積(積分)することで、PID コントローラーで静的誤差(定常誤差、残留誤差)を除去することができます。 言い換えると、インテグラルリンク 常に何らかのバイアスが生じますまた、システムが永続的なエラーの影響を受ける場合は、(そのバイアスにより) それを補正します。 しかし、そのような誤差が存在しないか、無視できるほど小さい場合、効果は逆になり、積分コンポーネント自体が変位誤差を導入します。 このため、超高精度の位置決めタスクなどには使用されません。 鍵 不利益積分制御則は、積分器の飽和効果 (積分器ワインドアップ) です。
差動成分Dは制御変数の偏差の変化率に比例し、目標値からの偏差を打ち消すように設計されています。 将来的に予測される。 差動コンポーネントによって減衰振動が除去されることは注目に値します。 差動制御は遅延が大きいプロセスに特に効果的です。 不利益微分制御則は、ノイズ (微分ノイズ) の影響に対する不安定性です。
したがって、状況に応じて、P、PD、PI、および PID レギュレーターを使用できますが、主な制御法則は主に比例です (ただし、一部の特定のタスクでは、微分器と積分器の要素のみが排他的に使用できます)。
PID コントローラーの実装の問題は長い間騙されてきたようです。Habré には、Unity3D を含むこのトピックに関する優れた記事がいくつかあります。また、PID Without a PhD (翻訳) という優れた記事や、一連の記事もあります。ジャーナル「Modern Automation Technologies」の記事は、第 1 部と第 2 部の 2 部構成になっています。 Wikipedia にも記事があります (英語版で最も完全な記事をお読みください)。 Unity3D コミュニティ フォーラムでは、いいえ、いいえ、Gamedev.stackexchange と同じように PID コントローラーがポップアップします。
PID コントローラーの実装に関する問題は、思っているよりもいくらか根が深いです。 このような規制スキームの導入を決定した若いDIY愛好家には、多くの素晴らしい発見が待っているほどであり、このトピックは関連性があります。 この作品が誰かの役に立つことを願っていますので、始めましょう。
試みその1
例として、単純な 2D 空間のアーケード ゲームでの回転制御の例を使用して、最初から段階的に制御スキームを実装してみます (これがチュートリアルであることを忘れていますか?)。
なぜ 3D ではないのでしょうか? ピッチ、ヨー、ロールを制御するために PID コントローラーを上げる必要があることを除いて、実装は変わりません。 クォータニオンと合わせて PID 制御を正しく適用する問題は非常に興味深いので、おそらく将来議論することになるでしょうが、NASA ですらクォータニオンではなくオイラー角を好むため、2 次元上の単純なモデルで間に合わせます。飛行機。
まず、宇宙船のゲーム オブジェクト自体を作成しましょう。これは、階層の最上位にある実際の船のオブジェクト自体で構成され、それに子 Engine オブジェクトをアタッチします (純粋に特殊効果のため)。 私にとっては次のようになります。
そして、宇宙船オブジェクト自体に投入します 検査官あらゆる種類のコンポーネント。 今後を見据えて、最終的にどのようになるかをスクリーンショットで示します。
しかし、それは後の話で、今のところスクリプトは含まれておらず、標準的な紳士用セット (Sprite Render、RigidBody2D、Polygon Collider、Audio Source) だけが含まれています (なぜ?)。
実際、今私たちにとって最も重要なのは物理学であり、それによってのみ制御が行われます。そうしないと、PID コントローラーを使用する意味が失われます。 また、宇宙船の質量を 1 kg のままにしておき、宇宙ではすべての摩擦係数と重力係数がゼロに等しいとします。
なぜなら 宇宙船自体に加えて、他にもあまり賢くない宇宙オブジェクトがたくさんあるので、最初に親クラスについて説明します。 ベースボディこれには、コンポーネント、初期化メソッドと破棄メソッド、さらには天体力学を実装するための多数の追加フィールドやメソッドへのリンクが含まれます。
BaseBody.cs
UnityEngine を使用する。 System.Collections を使用します。 System.Collections.Generic を使用します。 namespace Assets.Scripts.SpaceShooter.Bodies ( public class BaseBody: MonoBehaviour ( readonly float _deafultTimeDelay = 0.05f; public static List
(この記事の枠内で) 必要なことはすべて、必要以上に説明したようです。 ここから船クラスを継承しましょう 船、移動したり回転したりできるはずです。
SpaceShip.cs
UnityEngine を使用する。 System.Collections を使用します。 System.Collections.Generic を使用します。 namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _rotation = 0f; public voidFixedUpdate() ( float Torque = ControlRotate( _rotation); Vector2 Force = ControlForce(_movement); _rb2d.AddTorque(torque); _rb2d.AddRelativeForce(force); ) public float ControlRotate(Vector2 回転) ( float result = 0f; return result; ) public Vector2 ControlForce(Vector2 の動き) ( Vector2 の結果 = new Vector2(); 結果を返す; ) ) )
何も興味深いものはありませんが、現時点では単なるスタブ クラスです。
すべての入力コントローラーの基本 (抽象) クラス BaseInputController についても説明します。
BaseInputController.cs
UnityEngine を使用する。 Assets.Scripts.SpaceShooter.Bodies を使用します。 namespace Assets.Scripts.SpaceShooter.InputController ( public enum eSpriteRotation ( Rigth = 0, Up = -90, Left = -180, Down = -270 ) public abstract class BaseInputController: MonoBehaviour ( public GameObject _agentObject; public Ship _agentBody; // リンク船のロジック コンポーネントに public eSpriteRotation _spriteOrientation = eSpriteRotation.Up; //これは、非標準の // スプライトの向きが「右」ではなく「上」であることが原因です public abstract void ControlRotate(float dt); public abstract void ControlForce (float dt); public virtual void Start() ( _agentObject = gameObject; _agentBody = gameObject.GetComponent
そして最後に、プレーヤー コントローラー クラス プレイヤーファイター入力:
PlayerInput.cs
UnityEngine を使用する。 Assets.Scripts.SpaceShooter.Bodies を使用します。 namespace Assets.Scripts.SpaceShooter.InputController ( public class PlayerFigtherInput: BaseInputController ( public override void ControlRotate(float dt) ( // プレーヤーを基準としたマウスの位置を決定します Vector3 worldPos = Input.mousePosition; worldPos = Camera.main.ScreenToWorldPoint (worldPos); // マウス ポインタの座標を保存 float dx = -this.transform.position.x + worldPos.x; float dy = -this.transform.position.y + worldPos.y; // 方向 Vector2 ターゲットを渡す= new Vector2(dx, dy ); _agentBody._target = target; //キーストロークに従って回転を計算 float targetAngle = Mathf.Atan2(dy, dx) * Mathf.Rad2Deg; _agentBody._targetAngle = targetAngle + (float)_spriteOrientation ; ) public override void ControlForce( float dt) ( //パスの動き _agentBody._movement = Input.GetAxis("Vertical") * Vector2.up + Input.GetAxis("水平") * Vector2.right; ) ) )
私たちは終わったようです、今、私たちはついにこれすべてが何のために始まったのかに進むことができます。 PID コントローラー (忘れていないといいのですが)。 その実装は非常に簡単に思えます。
システムを使用する; System.Collections.Generic を使用します。 System.Linq を使用します。 System.Text を使用します。 namespace Assets.Scripts.Regulator ( // この属性は、レギュレーター フィールドが // インスペクターおよびシリアル化されたパブリック クラス SimplePID ( public float Kp, Ki, Kd; private float lastError; private float P, I, D) に表示されるために必要です; public SimplePID() ( Kp = 1f; Ki = 0; Kd = 0.2f; ) public SimplePID(float pFactor, float iFactor, float dFactor) ( this.Kp = pFactor; this.Ki = iFactor; this.Kd = dFactor ; ) public float Update(float error, float dt) ( P = エラー; I += エラー * dt; D = (エラー - lastError) / dt; lastError = エラー; float CO = P * Kp + I * Ki + D * Kd ; CO を返す; ) )
何もないところから係数のデフォルト値を取得します。これは、比例制御則の自明な単一係数 Kp = 1、微分制御則の係数の小さな値 Kd = 0.2 になります。これは、削除する必要があります。予想される変動と Ki のゼロ値が選択されています。これは、私たちのソフトウェアではモデルに静的エラーがないためです (ただし、いつでも静的エラーを導入することができ、インテグレーターの助けを借りて英雄的に戦うことができます)。
次に、SpaceShip クラスに戻り、作成したクラスを ControlRotate メソッドで宇宙船回転コントローラーとして使用してみます。
public float ControlRotate(Vector2rotate) ( float MV = 0f; float dt = Time.fixedDeltaTime; //エラーを計算 float angleError = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); //補正加速度 MV = _angleController .Update (angleError, dt); MV を返す; )
PID コントローラーは、トルクのみを使用して宇宙船の正確な角度位置決めを実行します。 物理学や自走砲など、すべてが公平で、現実世界とほとんど同じです。
そして、これらのクォータニオンなしで。あなたのLerp
if (!_rb2d.freezeRotation) rb2d.freezeRotation = true; float deltaAngle = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); float T = dt * Mathf.Abs(_rotationSpeed / deltaAngle); // 角度をベクトルに変換します Quaternion rot = Quaternion.Lerp(_myTransform.rotation, Quaternion.Euler(new Vector3(0, 0, targetAngle)), T); // オブジェクトの回転を変更します _myTransform.rotation = rot;
Ship.cs の結果のソース コードはスポイラーの下にあります
UnityEngine を使用する。 Assets.Scripts.Regulator を使用します。 namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public GameObject _flame; public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _targetAngle = 0f; public float _angle = 0f; public SimplePID _angleController = new SimplePID(); public voidFixedUpdate() ( float トルク = ControlRotate(_targetAngle); Vector2 Force = ControlForce(_movement); _rb2d.AddTorque(torque); _rb2d.AddRelativeForce(force); ) public float ControlRotate(float) rotate) ( float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; //誤差を計算 float angleError = Mathf.DeltaAngle(_angle,rotate); //補正加速度 MV = _angleController を取得します。 Update( angleError, dt); return MV; ) public Vector2 ControlForce(Vector2 Movement) ( Vector2 MV = new Vector2(); //if (movement != のための、実行中のエンジンの特殊効果のためのコード) Vector2.zero) (if (_flame != null) ( _flame.SetActive(true); ) ) else ( if (_flame != null) ( _flame.SetActive(false); ) ) MV = 動き; MVを返します。 ) ) )
全て? 家に帰りましょうか?
なんと! 何が起こっていますか? なぜ船は奇妙な方向に回転するのですか? そして、なぜ他の物体に激しく反射するのでしょうか? この愚かなPIDコントローラーが機能していないのでしょうか?
慌てないで! 何が起こっているのかを理解してみましょう。
新しい SP 値を受信した瞬間に、誤差の不一致に急激な (段階的な) ジャンプがあり、これは次のように計算されます。 したがって、微分誤差にも急激なジャンプがあり、これを次のように計算します。このコード行:
D = (エラー - 最終エラー) / dt;
もちろん、他の差別化スキーム、たとえば 3 ポイント、5 ポイントなどを試すこともできますが、それでも役に立ちません。 まあ、彼らは鋭いジャンプの導関数を好みません - そのような点では関数 微分可能ではありません。 ただし、微分と統合のさまざまなスキームを試してみる価値はありますが、この記事ではその限りではありません。
移行プロセスのグラフを作成する時期が来たと思います。体重 1 kg、フォース アームの長さ 1 メートル、および微分グリッド ステップに対する S(t) = 0 から SP(t) = 90 度までの段階的な動作です。 0.02 秒 - Unity3D の例と同様に (実際には完全ではありません。これらのグラフを作成する際、慣性モーメントが固体の形状に依存することが考慮されていなかったため、過渡的なプロセスはわずかに変化します)異なりますが、デモンストレーションするには十分に似ています)。 グラフ上のすべての値は絶対値で示されています。
うーん、ここで何が起こっているのでしょうか? PID コントローラーの応答はどこへ行ったのでしょうか?
おめでとうございます。ちょうど「キック」のような現象に遭遇しました。 明らかに、プロセスがまだ PV = 0 で、設定値がすでに SP = 90 であるとき、数値微分により 4500 程度の導関数値が得られ、これに次の値が乗算されます。 Kd=0.2比例定数を加算すると、出力では角加速度の値 990 が得られます。これは、Unity3D 物理モデルではすでに完全に法外です (角速度は 18000 度/秒に達します...これは次のとおりだと思います) RigidBody2D の角速度の制限値)。
- おそらく、ジャンプがそれほど強くならないように係数を手動で選択する価値があるでしょうか?
- いいえ! この方法で達成できる最善のことは、微分ジャンプの振幅が小さいことですが、ジャンプ自体は元のままになり、この場合、微分成分は完全に無効になる可能性があります。
ただし、実験することはできます。
試みその2。 飽和
それは論理的です ドライブユニット(私たちの場合、SpaceShip の仮想操縦エンジン)、私たちのクレイジーなレギュレーターが生成できる大きな値を処理できません。 したがって、最初に行うことは、レギュレーターの出力を飽和させることです。
public float ControlRotate(Vector2回転, float thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; //エラーを計算する float angleError = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); / / 補正加速度を取得します CO = _angleController.Update(angleError, dt); //飽和 MV = CO; if (MV > thrust) MV = thrust; if (MV< -thrust) MV = -thrust; return MV; }
もう一度書き直した Ship クラスは次のようになります。
namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public GameObject _flame; public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _targetAngle = 0f; public float _angle = 0f; public float _thrust = 1f; public SimplePID _angleController = new SimplePID(0.1f,0f,0.05f); public voidFixedUpdate() ( _torque = ControlRotate(_targetAngle, _thrust); _force = ControlForce(_movement); _rb2d.AddTorque(_torque); _rb2d.AddRelativeForce(_force); ) public float ControlRotate(float targetAngle, float thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; //エラーを計算 float angleError = Mathf.DeltaAngle(_myTransform. eulerAngles .z, targetAngle); //補正加速度を取得 CO = _angleController.Update(angleError, dt); //飽和 MV = CO; if (MV > 推力) MV = 推力; if (MV< -thrust) MV = -thrust; return MV; } public Vector2 ControlForce(Vector2 movement) { Vector2 MV = new Vector2(); if (movement != Vector2.zero) { if (_flame != null) { _flame.SetActive(true); } } else { if (_flame != null) { _flame.SetActive(false); } } MV = movement * _thrust; return MV; } public void Update() { } } }
自走砲の最終的なスキームは次のようになります。
同時に、コントローラーの出力が明らかになります。 CO(t)プロセスの制御変数とはわずかに異なります MV(t).
実際には、この場所からすでに新しいゲーム エンティティを追加できます - ドライブユニットこれを通じてプロセスが制御されます。そのロジックは単なる Mathf.Clamp() よりも複雑になる可能性があります。たとえば、値の離散化を導入できます (値が入ってくることでゲームの物理に過負荷がかからないようにするため)小数点以下 6 桁)、デッド ゾーン(これも、超小さな反応で物理学を過負荷にするのは意味がありません)、ドライブの制御と非線形性(シグモイドなど)に遅延を導入します。それから何が起こるのか。
ゲームを起動すると、宇宙船がついに制御可能になったことがわかります。
グラフを作成すると、コントローラーの応答が次のようになっていることがわかります。
ここでは、正規化された値がすでに使用されており、角度は SP 値で除算され、コントローラーの出力は飽和がすでに発生している最大値に対して正規化されています。
以下は、PID コントローラーのパラメーターを増加した場合の影響を示すよく知られた表です ( フォントを小さくするにはどうすればよいですか?そうしないとメレンゲのハイフネーション表が収まりません。):
PID コントローラーを手動で調整するための一般的なアルゴリズムは次のとおりです。
- 自己発振が始まるまで微分リンクと積分リンクをオフにして比例係数を選択します。
- 差動成分を徐々に増加させて自己発振を除去します。
- 制御誤差(変位)が残留する場合は積分成分を用いて除去します。
PID コントローラーのパラメーターには一般的な値はありません。特定の値はプロセスのパラメーター (その伝達特性) にのみ依存します。ある制御オブジェクトで完全に動作する PID コントローラーは、別の制御オブジェクトでは動作しません。 さらに、比例、積分、微分成分の係数も相互依存しています。
試みその3。 またしてもデリバティブ
コントローラーの出力値を制限する形で松葉杖を取り付けても、コントローラーの最も重要な問題はまだ解決していません。コントローラーの入力における誤差が段階的に変化すると、差動コンポーネントが適切に機能しません。 実際には、他にも多くの松葉杖があります。たとえば、SP が急激に変化した瞬間に、差動コンポーネントを「オフ」にするか、差動コンポーネントの間にローパスフィルターをインストールします。 SP(t)誤差が徐々に増加する演算を行うことも、完全に方向転換して実数カルマン フィルターを使用して入力データを平滑化することもできます。 一般に、松葉杖はたくさんありますが、 観察者もちろんそうしたいですが、今回はそうではありません。
したがって、不一致エラーの導関数に戻って注意深く見てみましょう。
何か気づきましたか? よく見ると、一般に、SP(t) は時間の経過とともに変化しないことがわかります (コントローラーが新しいコマンドを受信したときのステップ変化の瞬間を除く)。 その導関数はゼロです。
言い換えれば、微分可能な微分誤差の代わりに、 どこにもないプロセスの導関数を使用できます。古典力学の世界では通常、どこでも連続で微分されます。自動制御システムの図はすでに次の形式になっています。
コントローラーのコードを変更してみましょう。
システムを使用する; System.Collections.Generic を使用します。 System.Linq を使用します。 System.Text を使用します。 namespace Assets.Scripts.Regulator ( public class SimplePID ( public float Kp, Ki, Kd; private float P, I, D; private float lastPV = 0f; public SimplePID() ( Kp = 1f; Ki = 0f; Kd = 0.2f ; ) public SimplePID(float pFactor, float iFactor, float dFactor) ( this.Kp = pFactor; this.Ki = iFactor; this.Kd = dFactor; ) public float Update(float error, float PV, float dt) ( P =エラー; I += エラー * dt; D = -(PV - lastPV) / dt; lastPV = PV; float CO = Kp * P + Ki * I + Kd * D; return CO; ) )
そして、ControlRotate メソッドを少し変更してみましょう。
public float ControlRotate(Vector2回転, float thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; //エラーを計算する float angleError = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); / / 補正加速度を取得します CO = _angleController.Update(angleError, _myTransform.eulerAngles.z, dt); //飽和 MV = CO; if (CO >< -thrust) MV = -thrust; return MV; }
そして、そして、そして... ゲームを開始すると、実際には前回の試行以来何も変わっていないことがわかります。これを証明する必要がありました。 ただし、飽和を取り除くと、レギュレーターの応答グラフは次のようになります。
ジャンプ CO(t)はまだ存在しますが、もはや最初の頃ほど大きくはありません。そして最も重要なのは、それが予測可能になったことです。 は比例コンポーネントによってもっぱら提供され、考えられる最大不一致誤差と PID コントローラーの比例ゲインによって制限されます (これはすでに次のことを示唆しています) Kp 1 未満 (たとえば 1/90f) を選択することは理にかなっていますが、微分グリッドのステップには依存しません (つまり、 dt)。 一般に、エラーではなくプロセス導関数を使用することを強くお勧めします。
これで誰も驚かないと思いますが、同じように に置き換えることもできますが、これについては詳しく説明しません。自分で実験して、その結果何が得られたかをコメントで教えてください(最も興味深いものです)
試みその4。 PID コントローラーの代替実装
上で説明した PID コントローラーの理想的な表現に加えて、実際には係数のない標準形式がよく使用されます。 キそして K Dの代わりに一時定数が使用されます。
このアプローチは、多くの PID コントローラー調整手法が PID コントローラーとプロセスの周波数特性に基づいているという事実によるものです。 実際には、TAU 全体はプロセスの周波数特性を中心に展開しているため、さらに深く知りたい人や突然別の命名法に遭遇した人のために、いわゆる例を示します。 標準形式 PIDコントローラー:
ここで、 はレギュレータによるシステム状態の予測に影響を与える微分定数です。
- 積分定数。積分リンクによる誤差平均化間隔に影響します。
標準形式の PID コントローラーを調整するための基本原則は、理想的な PID コントローラーと似ています。
- 比例係数を大きくすると、パフォーマンスが向上し、安定余裕が減少します。
- 積分成分が減少すると、制御誤差は時間の経過とともにより早く減少します。
- 積分定数を小さくすると、安定余裕が小さくなります。
- 差動成分の増加により、安定性マージンとパフォーマンスが向上します
標準形式のソースコードはスポイラーの下にあります。
namespace Assets.Scripts.Regulator ( public class StandartPID ( public float Kp, Ti, Td; public float error, CO; public float P, I, D; private float lastPV = 0f; public StandartPID() ( Kp = 0.1f; Ti = 10000f; Td = 0.5f; バイアス = 0f; ) public StandardPID(float Kp, float Ti, float Td) ( this.Kp = Kp; this.Ti = Ti; this.Td = Td; ) public float Update(float error, float PV, float dt) ( this.error = エラー; P = エラー; I += (1 / Ti) * エラー * dt; D = -Td * (PV - lastPV) / dt; CO = Kp * ( P + I + D); lastPV = PV; CO を返す; ) ) )
デフォルト値は Kp = 0.01、Ti = 10000、Td = 0.5 です。これらの値を使用すると、船は非常に速く回転し、ある程度の安定性のマージンが得られます。
この形式の PID コントローラーに加えて、いわゆる 再発形:
それについては詳しく説明しません。なぜなら... これは主に、FPGA やマイクロコントローラーを扱うハードウェア プログラマーに関係しており、そのような実装ははるかに便利で効率的です。 私たちの場合 - Unity3D の山に何かを与えましょう - これは PID コントローラーの別の実装にすぎませんが、他のものと比べて優れているわけでもなく、さらに理解しにくいものでもあります。そこで、もう一度、居心地の良い C# でプログラミングすることがどれほど優れているかをみんなで喜びましょう。たとえば、不気味でひどい VHDL ではありません。
結論の代わりに。 他にどこに PID コントローラーを追加しますか?
ここで、デュアル ループ制御を使用して船の制御を少し複雑にしてみます。1 つの PID コントローラー (すでにおなじみの _angleController) は依然として角度位置決めを担当しますが、2 番目の新しい _angularVelocityController (回転速度) は回転速度を制御します。
public float ControlRotate(float targetAngle, float thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; //回転角度コントローラー float angleError = Mathf.DeltaAngle(_angle, targetAngle); float torcCorrectionForAngle = _angleController.Update(angleError, _angle, dt); //速度安定化コントローラー float angularVelocityError = -_rb2d.angularVelocity; floattorkCorrectionForAngularVelocity = _angularVelocityController.Update(angularVelocityError, -angularVelocityError, dt); //合計コントローラー出力 CO =torqueCorrectionForAngle +torqueCorrectionForAngularVelocity; //100 ステップでサンプル CO = Mathf.Round(100f * CO) / 100f; //飽和 MV = CO; if (CO > 推力) MV = 推力; if (CO< -thrust) MV = -thrust; return MV; }
2 番目のレギュレーターの目的は、トルクを変更することで過剰な角速度を減衰させることです。これは、ゲーム オブジェクトの作成時にオフにした角摩擦の存在に似ています。 このような制御スキームにより、船舶のより安定した挙動を得ることが可能になり、比例制御係数のみで対処することも可能になります。2 番目のレギュレーターはすべての変動を減衰させ、最初のレギュレーターの差動コンポーネントと同様の機能を実行します。 。
さらに、新しいプレーヤー入力クラス PlayerInputCorvette を追加します。このクラスでは、左右のキーを押すことによってターンが実行されます。また、砲塔の制御など、より便利なもののためにマウスでターゲットの指定を残します。 。 同時に、_turnRate などのパラメーターが追加されました。これはターンの速度/応答性を担当します (InputCONtroller または Ship のどこに配置する方が良いかは明確ではありません)。
public class PlayerCorvetteInput: BaseInputController ( public float _turnSpeed = 90f; public override void ControlRotate() ( // マウス ポインター Vector3 を検索します worldPos = Input.mousePosition; worldPos = Camera.main.ScreenToWorldPoint(worldPos); // 相対値を格納しますマウスポインタの座標 float dx = -this.transform.position.x + worldPos.x; float dy = -this.transform.position.y + worldPos.y; //マウスポインタの方向を渡す Vector2 target = new Vector2( dx, dy); _agentBody. _target = target; //キーストロークに応じて回転を計算 _agentBody._rotation -= Input.GetAxis("水平") * _turnSpeed * Time.deltaTime; ) public override void ControlForce() ( //パス移動 _agentBody._movement = Input .GetAxis("Vertical") * Vector2.up; ) )
また、わかりやすくするために、デバッグ情報を表示するスクリプトを膝の上に置きます。
namespace Assets.Scripts.SpaceShooter.UI ( public class Debugger: MonoBehaviour ( Ship _ship; BaseInputController _controller; List
Ship クラスも不可逆的な突然変異を起こしており、次のようになります。
namespace Assets.Scripts.SpaceShooter.Bodies ( public class Ship: BaseBody ( public GameObject _flame; public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _targetAngle = 0f; public float _angle = 0f; public float _thrust = 1f; public SimplePID _angleController = new SimplePID(0.1f,0f,0.05f); public SimplePID _angularVelocityController = new SimplePID(0f,0f,0f); private float _torque = 0f; public float _Torque ( get ( return _torque; ) ) private Vector2 _force = new Vector2(); public Vector2 _Force ( get ( return _force; ) ) public voidFixedUpdate() ( _torque = ControlRotate(_targetAngle, _thrust); _force = ControlForce(_movement, _thrust); _rb2d.AddTorque( _torque); _rb2d.AddRelativeForce(_force); ) public float ControlRotate(float targetAngle, float thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; //コントローラー回転角度 float angleError = Mathf.DeltaAngle(_angle, targetAngle); floattorkCorrectionForAngle = _angleController.Update(angleError, _angle, dt); //速度安定化コントローラー float angularVelocityError = -_rb2d.angularVelocity; floattorkuCorrectionForAngularVelocity = _angularVelocityController.Update(angularVelocityError, -angularVelocityError, dt); //合計コントローラー出力 CO = 角度のトルク補正 + 角速度のトルク補正; //100 ステップで離散化 CO = Mathf.Round(100f * CO) / 100f; //飽和MV = CO; if (CO > 推力) MV = 推力; if(CO< -thrust) MV = -thrust; return MV; } public Vector2 ControlForce(Vector2 movement, float thrust) { Vector2 MV = new Vector2(); if (movement != Vector2.zero) { if (_flame != null) { _flame.SetActive(true); } } else { if (_flame != null) { _flame.SetActive(false); } } MV = movement * thrust; return MV; } public void Update() { } } }
他の例へのさらにリンク
比例帯 X p は、偏差 E と同様に、制御パラメータの単位で表されます。 比例帯 X p が広いほど、同じ偏差 E に対する出力信号 Y は小さくなります。
比例帯の外側では、Y 出力は 0 または 100% になります。
P 則が動作すると、コントローラーは出力信号値の比例成分のみが存在するパルスを生成します。
デバイスが PD コントローラー モードで動作する場合、出力信号 Yi の大きさは偏差 Eli の大きさだけでなく、その変化率にも依存します。
偏差を段階的に変化させた場合のコントローラ出力信号の変化を図に示します。 E i のステップ変化後の最初の期間では、コントローラーは制御パルスを発行します。このパルスでは、不一致 E i によって生じる比例成分に加えて、値に応じた差分 (斜線部分) ΔYd が追加されます。 ΔE i とτ l 係数の関係。 後続のパルスでは、E i に変化がないため、比例成分のみが存在します。
この図は、最初の瞬間、偏差がないとき (E i =0)、出力信号がない (Y i =0) ことを示しています。 偏差E i が現れるとパルスが現れ、その期間は徐々に増加します。 パルスには、E の値に依存する比例成分 (パルスの陰影のない部分) と積分成分 (陰影の部分) が含まれています。 パルス持続時間の増加は、不一致 E i と係数 τ i に依存する積分成分の増加により発生します。
微分比例積分コントローラーは、変更可能な特定のパラメーターを維持するために自動化システムにインストールされるデバイスです。
一見、すべてがわかりにくいですが、PID 制御はダミーのために説明できます。 電子システムやデバイスにあまり詳しくない人。
PIDコントローラーとは何ですか?
PID コントローラーは、必須のフィードバックを備えた制御回路に組み込まれたデバイスです。 気温などの指定値の確立されたレベルを維持するように設計されています。
デバイスは、センサーまたはセンサーから受信したデータに基づいて、制御信号または出力信号を制御デバイスに供給します。 コントローラーは、一時的なプロセスの精度が高く、割り当てられたタスクのパフォーマンスの品質が優れています。
3 つの PID コントローラー係数と動作原理
PID コントローラーの仕事は、制御パラメーターを所定のレベルに維持するために必要な電力に関する出力信号を提供することです。 インジケーターを計算するには、比例、積分、微分の 3 つの係数を含む複雑な数式が使用されます。
蒸気でバルブの開度を調整して一定の温度に保つ必要がある水を入れた容器を規制の対象とします。
比例成分は入力データと不一致の瞬間に現れます。 簡単に言うと次のようになります。実際の温度と望ましい温度の差が取得され、調整可能な係数が乗算され、バルブに供給される出力信号が得られます。 それらの。 温度が下がるとすぐに加熱プロセスが開始され、温度が望ましいレベルを超えると、シャットダウンが発生したり、さらには冷却が行われます。
次に、温度を一定レベルに維持する際の環境の影響やその他の邪魔な影響を補償するように設計された統合コンポーネントが登場します。 制御対象のデバイスに影響を与える追加の要因が常に存在するため、比例成分を計算するためのデータが到着した時点で、数値はすでに変化しています。 そして、外部からの影響が大きくなればなるほど、指標の変動も大きくなります。 供給電力が急増しています。
積分コンポーネントは、過去の温度値に基づいて、温度が変化した場合はその値を返そうとします。 このプロセスについては、以下のビデオで詳しく説明されています。
積分は、静的誤差を計算することで誤差を除去するために使用されます。 このプロセスで重要なことは、正しい係数を選択することです。そうでないと、エラー (不一致) が積分コンポーネントにも影響します。
PID の 3 番目のコンポーネントは微分です。 これは、システムへの影響とフィードバックの間に発生する遅延の影響を補償するように設計されています。 比例コントローラーは、温度が目的のレベルに達するまで電力を供給しますが、情報がデバイスに渡されるとき、特に値が大きい場合には常にエラーが発生します。 過熱の原因となる可能性があります。 ディファレンシャルは遅延や環境の影響による偏差を予測し、供給電力を事前に削減します。
PIDコントローラーのセットアップ
PID コントローラーは 2 つの方法を使用して構成されます。
- 合成には、システム モデルに基づいてパラメータを計算することが含まれます。 この設定は正確ですが、自動制御理論に関する深い知識が必要です。 エンジニアと科学者のみが対象となります。 消費特性を取得して大量の計算を行う必要があるため。
- 手動による方法は試行錯誤に基づいています。 これを行うには、既製のシステムのデータが基礎として取得され、1 つまたは複数のレギュレーター係数にいくつかの調整が行われます。 スイッチを入れて最終結果を観察した後、パラメータを希望の方向に変更します。 望ましいレベルのパフォーマンスが達成されるまで、同様に実行されます。
分析と調整の理論的な方法が実際に使用されることはほとんどありません。これは、制御オブジェクトの特性と、考えられる多数の妨害影響が無知であるためです。 より一般的なのは、システムの観察に基づく実験方法です。
最新の自動プロセスは、コントローラー係数を調整するためのプログラムによって制御される特殊なモジュールとして実装されています。
PIDコントローラーの目的
PID コントローラーは、自動制御バルブなどのアクチュエーターの制御動作を変更することで、温度、圧力、タンク内のレベル、パイプライン内の流量、何かの濃度など、必要なレベルに特定の値を維持するように設計されています。 、その設定には比例、積分、微分の量を使用します。
使用目的は、大規模産業や発電所の原子炉さえも制御できる正確な制御信号を取得することです。
温度制御回路例
PID コントローラーは温度の調整によく使用されます。容器内の水を加熱する簡単な例を使用して、この自動プロセスを見てみましょう。
コンテナには液体が充填されており、希望の温度まで加熱し、所定のレベルに維持する必要があります。 温度測定センサーはタンク内に設置されるか、PID コントローラーに直接接続されます。
液体を加熱するために、自動制御弁を用いて下図のように蒸気を供給します。 バルブ自体はレギュレーターから信号を受け取ります。 オペレーターは、タンク内で維持する必要がある温度設定値を PID コントローラーに入力します。
レギュレータの係数設定を誤ると、バルブが全開または全閉した状態で水温が変動します。 この場合、PID コントローラの係数を計算し、再入力する必要があります。 すべてが正しく行われていれば、短時間後にシステムはプロセスを平準化し、コンテナ内の温度は設定値に維持され、制御バルブの開度は中間の位置になります。
PID コントローラーは、自動化システムの特定の機器を制御するためのソフトウェア アルゴリズムをユーザーが実装できる既製のデバイスです。 Aries 社の 8 チャネル用ユニバーサル PID コントローラ TRM148 などの既製のデバイスを使用すると、制御システムの構築と構成がはるかに簡単になります。
温室内の正しい気候条件の維持を自動化する必要があるとします。植物の根元近くの土壌の温度、気圧、空気と土壌の湿度を考慮し、ファンを制御して設定パラメータを維持します。 これほど簡単なことはありません。PID コントローラーを設定するだけです。
まず PID コントローラーとは何かを思い出してみましょう。 PID コントローラは、出力パラメータを比例、積分、微分の 3 つの方法で継続的に精密に調整する特殊なデバイスで、初期パラメータはセンサーから受信した入力パラメータ (圧力、湿度、温度、照度など) です。
入力パラメータは、センサー、たとえば湿度センサーから PID コントローラーの入力に供給されます。 レギュレータは電圧または電流値を受け取り、それを測定し、そのアルゴリズムに従って計算を行い、最終的に対応する出力に信号を送信します。その結果、自動化システムは制御アクションを受け取ります。 土壌水分が減少しました - 散水は数秒間オンになりました。
目標は、ユーザーが指定した湿度値を達成することです。 または、たとえば、照明が低下しました - 植物の上のファイトランプを点灯するなどです。
PID制御
実際、見かけ上はすべてが単純ですが、レギュレータの内部では数学がより複雑になり、すべてが 1 つのステップで行われるわけではありません。 灌漑をオンにした後、PID コントローラーは再度測定を行い、入力値がどれだけ変化したかを測定します。これにより、制御エラーが検出されます。 執行機関に対する次の影響は、測定された規制誤差などを考慮して調整され、目標 (ユーザー指定のパラメータ) が達成されるまで各制御ステップで調整されます。
調整には、比例、積分、微分の 3 つの要素が関係します。 各コンポーネントには、それぞれの特定のシステムにおいて独自の重要性があり、いずれかのコンポーネントによる貢献が大きいほど、規制プロセスにおいてそのコンポーネントをより大きく変更する必要があります。
比例成分が最も単純で、変化が大きいほど係数(式における比例)が大きくなり、影響を軽減するには係数(乗数)を小さくすればよい。
温室内の土壌水分が設定湿度よりもはるかに低いとします。その場合、現在の湿度が設定湿度よりも低いため、水やりの時間は同じ量だけ長くする必要があります。 これは大まかな例ですが、これが一般的な原則です。
積分コンポーネント - 以前の制御イベントに基づいて制御精度を高める必要があります。将来の調整時に最終的にゼロ偏差を得るために、以前のエラーが統合され、修正が行われます。
最後に、差動コンポーネントです。 ここでは、制御変数の変化率が考慮されます。 したがって、規制値が滑らかに変化するか急激に変化するかにかかわらず、規制措置は規制中に値の過度の逸脱につながるべきではありません。
あとは、PID 制御するデバイスを選択するだけです。 現在、市場には多くのものが市販されており、上記の温室の例のように、複数のパラメーターを一度に変更できるマルチチャンネルのものもあります。
Aries 社の TRM148 ユニバーサル PID コントローラーの例を使用して、コントローラーの設計を見てみましょう。
入力 8 つのセンサーは、対応する入力に信号を提供します。 信号はスケーリング、フィルタリング、補正され、ボタンで切り替えることでその値をディスプレイに表示できます。
デバイス出力は、次の必要な組み合わせでさまざまな変更を加えて製造されます。
リレー 4 A 220 V;
トランジスタフォトカプラ n-p-n タイプ 400 mA 60 V;
トライアックフォトカプラ 50 mA 300 V;
DAC「パラメータ電流 4...20 mA」;
DAC「パラメータ電圧 0...10 V」;
ソリッドステートリレーを制御するための 4...6 V 100 mA 出力。
したがって、制御動作はアナログでもデジタルでも可能です。 - これらは可変幅のアナログパルスであり、統一された範囲で滑らかに変化する電圧または電流の形式です。電圧信号の場合は 0 ~ 10 V、電流信号の場合は 4 ~ 20 mA です。
これらの出力信号は、灌漑システムのポンプや、発熱体やバルブ制御モーターをオン/オフするリレーなどのアクチュエーターを制御するために正確に使用されます。 レギュレータパネルには信号インジケータがあります。
PC と通信するために、TRM148 レギュレータには RS-485 インターフェイスが装備されており、次のことが可能です。
- ネットワークから運用データを受信して制御信号を生成します。
PC 上でデバイスを設定します (設定プログラムは無料で提供されます)。
測定値の現在の値、コントローラの出力電力、およびプログラム可能なパラメータをネットワークに送信します。
最高のパフォーマンスが得られるのは、 P法、 - 比率 tp / T d に基づきます。
ただし、P レギュレータ Kr のゲインが小さい場合 (ほとんどの場合、遅延が発生します)、高い制御精度は得られません。 この場合、値は大きくなります。
Kp > 10 の場合、P レギュレータは許容可能であり、Kp の場合は許容されます。< 10, то требуется введение в закон управления составляющей.
PI規制法
実際に最も一般的なのは、 PIコントローラー、これには次のような利点があります。
- ゼロ規制を提供します。
- セットアップが非常に簡単なので... 調整されるパラメータは 2 つだけ、つまりゲイン Kp と積分時定数 Ti です。 このようなコントローラーでは、Kp/Ti-min 比の値を最適化することが可能であり、これにより、可能な限り最小限の二乗平均平方根制御による制御が保証されます。
- 測定時のノイズに対する感度が低い (PID コントローラーとは異なります)。
PID制御法
最も重要な制御ループについては、次の使用をお勧めします。 , システムで最高のパフォーマンスを提供します。
ただし、これは最適な設定 (3 つのパラメーターが構成されている) でのみ行われることに注意してください。
システムの遅延が増加すると、負の位相シフトが急激に増加し、レギュレータの差動コンポーネントの影響が減少します。 したがって、遅延が大きいシステムの PID コントローラーの品質は、PI コントローラーの品質と同等になります。
さらに、PID コントローラを備えたシステムの測定チャネルにノイズが存在すると、コントローラの制御信号がランダムに大きく変動し、制御誤差の変動や機構の磨耗が増加します。
したがって、ノイズ レベルと制御遅れが比較的低い制御システムには PID コントローラを選択する必要があります。 このようなシステムの例としては、温度制御システムがあります。