ตัวอย่างการใช้งานและการประยุกต์ใช้ตัวควบคุม ตัวควบคุม PID - คำอธิบายแบบเต็ม แอปพลิเคชัน การประสานงานของอุปกรณ์เอาท์พุตของหน่วยงานกำกับดูแลอย่างต่อเนื่อง

ระบบควบคุมอัตโนมัติ (ACS)ได้รับการออกแบบมาเพื่อเปลี่ยนพารามิเตอร์หนึ่งหรือหลายพารามิเตอร์ของวัตถุควบคุมโดยอัตโนมัติเพื่อสร้างโหมดการทำงานที่ต้องการ ACS ช่วยให้มั่นใจได้ถึงการรักษาความสม่ำเสมอของค่าที่ระบุของพารามิเตอร์ที่ได้รับการควบคุมหรือการเปลี่ยนแปลงตามกฎหมายที่กำหนดหรือปรับเกณฑ์บางอย่างให้เหมาะสมสำหรับคุณภาพของการควบคุม ตัวอย่างเช่น ระบบดังกล่าวได้แก่:

  • ระบบรักษาเสถียรภาพ,
  • ระบบควบคุมโปรแกรม,
  • ระบบติดตาม

นี่เป็นระบบประเภทที่ค่อนข้างกว้างซึ่งสามารถพบได้ทุกที่ แต่สิ่งนี้เกี่ยวอะไรกับ Unity3D และโดยเฉพาะเกม? โดยหลักการแล้ว มันตรงไปตรงมา: ในเกมใดก็ตามที่ใช้การจำลองเป็นองค์ประกอบของการเล่นเกม จะมีการนำปืนอัตตาจรมาใช้ เกมดังกล่าวได้แก่ Kerbal Space Programm, Digital Combat Simulator (เดิมชื่อ Lock On), Strike Suit Zero, ฯลฯ (ใครจะรู้ตัวอย่างเพิ่มเติม - เขียนในความคิดเห็น) โดยหลักการแล้ว เกมใดก็ตามที่จำลองกระบวนการทางกายภาพจริง รวมถึงจลนศาสตร์ที่มีไดนามิกของการเคลื่อนไหว สามารถใช้ปืนที่ขับเคลื่อนด้วยตัวเองได้ - แนวทางนี้ง่ายกว่า เป็นธรรมชาติมากกว่า และผู้พัฒนาก็มีชุดเครื่องมือสำเร็จรูปที่มอบให้โดย Vyshnegradskys, Lyapunovs, Kalmans , Chebyshevs และ Kolomogorovs อื่น ๆ ทุกประเภทดังนั้นคุณสามารถทำได้โดยไม่ต้องสร้างวงล้อใหม่เพราะ มันถูกคิดค้นขึ้นมากจนกลายเป็นวิทยาศาสตร์ที่แยกจากกัน: ทฤษฎีการควบคุมอัตโนมัติ สิ่งสำคัญที่นี่คืออย่าหักโหมจนเกินไป มีปัญหาเดียวคือพวกเขาไม่ได้พูดถึง TAU ทุกที่ ไม่ใช่กับทุกคน มักจะเล็กน้อยและไม่ชัดเจนนัก

ทฤษฎีเล็กน้อย

ระบบควบคุมอัตโนมัติแบบคลาสสิกแสดงในรูปต่อไปนี้:



องค์ประกอบสำคัญของปืนอัตตาจรคือ หน่วยงานกำกับดูแลซึ่งเป็นอุปกรณ์ที่ตรวจสอบสถานะของวัตถุควบคุมและจัดให้มีกฎหมายควบคุมที่จำเป็น กระบวนการควบคุมประกอบด้วย: การคำนวณข้อผิดพลาดในการควบคุมหรือสัญญาณข้อผิดพลาด (ที) อะไรคือความแตกต่างระหว่างสิ่งที่ต้องการ การตั้งค่า(จุดกำหนดหรือ เอสพี ) และค่าปัจจุบันของกระบวนการ (process val หรือ พีวี ) หลังจากนั้นตัวควบคุมจะสร้างสัญญาณควบคุม (ค่าที่ถูกจัดการหรือ เอ็มวี ).


ตัวควบคุมประเภทหนึ่งคือ ตัวควบคุมสัดส่วนปริพันธ์อนุพันธ์ (PID)ซึ่งสร้างสัญญาณควบคุมซึ่งเป็นผลรวมของคำศัพท์สามคำ: สัดส่วน อินทิกรัล และดิฟเฟอเรนเชียล



โดยที่ ข้อผิดพลาดที่ไม่ตรงกันรวมถึง - สัดส่วน - อินทิกรัล - ส่วนประกอบที่แตกต่าง (เงื่อนไข) ของกฎหมายควบคุมซึ่งอธิบายในรูปแบบสุดท้ายโดยสูตรต่อไปนี้




องค์ประกอบตามสัดส่วน P- รับผิดชอบในสิ่งที่เรียกว่า การควบคุมตามสัดส่วน ความหมายก็คือ สัญญาณเอาท์พุตของตัวควบคุมจะตอบโต้การเบี่ยงเบนของตัวแปรควบคุม (ข้อผิดพลาดที่ไม่ตรงกันหรือเรียกอีกอย่างว่าค่าตกค้าง) จากค่าที่ตั้งไว้ ยิ่งข้อผิดพลาดไม่ตรงกันมากเท่าใด การเบี่ยงเบนคำสั่งของคอนโทรลเลอร์ก็จะยิ่งมากขึ้นเท่านั้น นี่เป็นกฎหมายควบคุมที่ง่ายและชัดเจนที่สุด ตำหนิกฎการควบคุมตามสัดส่วนคือตัวควบคุมไม่เคยเสถียรที่ค่าที่กำหนด และการเพิ่มขึ้นของค่าสัมประสิทธิ์สัดส่วนจะนำไปสู่การผันผวนในตัวเองเสมอ นั่นคือเหตุผลที่นอกเหนือจากกฎหมายควบคุมตามสัดส่วนแล้วยังจำเป็นต้องใช้กฎอินทิกรัลและดิฟเฟอเรนเชียลอีกด้วย


ส่วนประกอบสำคัญ Iสะสม (รวม) ข้อผิดพลาดในการควบคุม ซึ่งช่วยให้ตัวควบคุม PID สามารถกำจัดข้อผิดพลาดคงที่ได้ (ข้อผิดพลาดคงที่ ข้อผิดพลาดตกค้าง) หรืออีกนัยหนึ่ง: ลิงค์อินทิกรัล มักจะนำเสนออคติบางอย่างเสมอและหากระบบมีข้อผิดพลาดถาวร ระบบจะชดเชยข้อผิดพลาดเหล่านั้น (เนื่องจากอคติ) แต่ถ้าไม่มีข้อผิดพลาดดังกล่าวหรือมีขนาดเล็กมาก ผลที่ได้จะตรงกันข้าม - ส่วนประกอบสำคัญจะทำให้เกิดข้อผิดพลาดในการเคลื่อนที่ ด้วยเหตุนี้จึงไม่มีการใช้ เช่น ในงานการวางตำแหน่งที่แม่นยำเป็นพิเศษ สำคัญ ข้อเสียเปรียบกฎหมายควบคุมแบบรวมเป็นผลจากความอิ่มตัวของตัวรวมระบบ (การสิ้นสุดของตัวรวมระบบ)


องค์ประกอบดิฟเฟอเรนเชียล Dเป็นสัดส่วนกับอัตราการเปลี่ยนแปลงของการเบี่ยงเบนของตัวแปรควบคุมและได้รับการออกแบบเพื่อต่อต้านการเบี่ยงเบนไปจากค่าเป้าหมายซึ่ง มีการคาดการณ์ไว้ในอนาคต. เป็นที่น่าสังเกตว่าส่วนประกอบส่วนต่างช่วยลดการสั่นแบบหน่วง การควบคุมส่วนต่างมีประสิทธิผลโดยเฉพาะอย่างยิ่งสำหรับกระบวนการที่มีความล่าช้ามาก ข้อเสียกฎหมายควบคุมส่วนต่างคือความไม่แน่นอนต่อผลกระทบของเสียงรบกวน (Differentiation Noise)


ดังนั้น ขึ้นอยู่กับสถานการณ์ สามารถใช้ตัวควบคุม P-, PD-, PI- และ PID ได้ แต่กฎหมายควบคุมหลักส่วนใหญ่เป็นสัดส่วน (แม้ว่าในงานเฉพาะบางอย่างสามารถใช้เฉพาะองค์ประกอบของตัวสร้างความแตกต่างและผู้รวมระบบเท่านั้น)


ดูเหมือนว่าปัญหาในการใช้ตัวควบคุม PID จะถูกแฮ็กมานานแล้ว และที่Habré มีบทความดีๆ สองสามบทความในหัวข้อนี้ รวมถึงใน Unity3D ยังมีบทความที่ดี PID Without a PhD (การแปล) และชุดของ บทความในวารสาร "Modern Automation Technologies" ในสองส่วน: ครั้งแรกและครั้งที่สอง นอกจากนี้ยังมีบทความเกี่ยวกับ Wikipedia พร้อมให้บริการคุณ (อ่านบทความที่สมบูรณ์ที่สุดในเวอร์ชันภาษาอังกฤษ) และในฟอรัมชุมชน Unity3D ไม่ ไม่ และคอนโทรลเลอร์ PID จะปรากฏขึ้นเหมือนกับใน gamedev.stackexchange


คำถามเกี่ยวกับการใช้งานคอนโทรลเลอร์ PID นั้นค่อนข้างลึกกว่าที่คิด มากเสียจนการค้นพบที่น่าอัศจรรย์มากมายรอคอยนัก DIYers รุ่นเยาว์ที่ตัดสินใจใช้แผนการกำกับดูแลดังกล่าวและหัวข้อนี้ก็เกี่ยวข้อง ฉันหวังว่าบทประพันธ์นี้จะเป็นประโยชน์กับใครบางคน เรามาเริ่มกันเลย

พยายามอันดับหนึ่ง

ตามตัวอย่าง เราจะพยายามใช้แผนการควบคุมโดยใช้ตัวอย่างการหมุนการควบคุมในเกมอาร์เคดอวกาศ 2D ง่ายๆ ทีละขั้นตอนโดยเริ่มจากจุดเริ่มต้น (คุณลืมไปแล้วหรือว่านี่คือบทช่วยสอน?)


ทำไมไม่สามมิติ? เนื่องจากการใช้งานจะไม่เปลี่ยนแปลง ยกเว้นว่าคุณจะต้องเปิดตัวควบคุม PID เพื่อควบคุมระดับเสียง การหันเห และการหมุน แม้ว่าปัญหาของการประยุกต์ใช้การควบคุม PID อย่างถูกต้องร่วมกับควอเทอร์เนียนจะน่าสนใจมาก แต่บางทีฉันจะหารือเกี่ยวกับเรื่องนี้ในอนาคต แต่แม้แต่ NASA ก็ชอบมุมออยเลอร์มากกว่าควอเทอร์เนียน ดังนั้นเราจะใช้แบบจำลองง่ายๆ ในสองมิติ เครื่องบิน.


ขั้นแรก เรามาสร้างวัตถุเกมยานอวกาศขึ้นมา ซึ่งจะประกอบด้วยวัตถุยานอวกาศจริง ๆ ที่ระดับบนสุดของลำดับชั้น และแนบวัตถุเครื่องยนต์ลูกเข้ากับมัน (เพื่อประโยชน์ของเอฟเฟกต์พิเศษเท่านั้น) นี่คือสิ่งที่ดูเหมือนสำหรับฉัน:



และเราก็โยนเข้าไปบนวัตถุยานอวกาศ สารวัตรส่วนประกอบทุกชนิด เมื่อมองไปข้างหน้า ฉันจะให้ภาพหน้าจอว่าตอนจบจะเป็นอย่างไร:



แต่นั่นไว้สำหรับทีหลัง และตอนนี้ยังไม่มีสคริปต์อยู่ในนั้น มีเพียงชุดสุภาพบุรุษมาตรฐานเท่านั้น: Sprite Render, RigidBody2D, Polygon Collider, Audio Source (ทำไม?)


ที่จริงแล้วฟิสิกส์เป็นสิ่งที่สำคัญที่สุดสำหรับเราในตอนนี้และการควบคุมจะดำเนินการผ่านฟิสิกส์นั้นโดยเฉพาะ มิฉะนั้น การใช้ตัวควบคุม PID จะสูญเสียความหมาย ปล่อยให้มวลยานอวกาศของเราอยู่ที่ 1 กิโลกรัมและค่าสัมประสิทธิ์แรงเสียดทานและแรงโน้มถ่วงทั้งหมดจะเท่ากับศูนย์ในอวกาศ


เพราะ นอกจากตัวยานอวกาศแล้ว ยังมีวัตถุอวกาศอื่นๆ ที่มีความฉลาดน้อยกว่าอีกมากมาย ดังนั้นก่อนอื่นเราจะอธิบายคลาสพาเรนต์ก่อน ฐานร่างกายซึ่งจะมีลิงก์ไปยังส่วนประกอบของเรา วิธีการเริ่มต้นและการทำลาย ตลอดจนฟิลด์และวิธีการเพิ่มเติมจำนวนหนึ่ง เช่น สำหรับการนำกลไกท้องฟ้าไปใช้:


BaseBody.cs

ใช้ UnityEngine; ใช้ System.Collections; ใช้ System.Collections.Generic; namespace Assets.Scripts.SpaceShooter.Bodies (คลาสสาธารณะ BaseBody: MonoBehaviour ( อ่านอย่างเดียว float _deafultTimeDelay = 0.05f; รายการคงที่สาธารณะ _bodies = รายการใหม่ (); #region RigidBody สาธารณะ Rigidbody2D _rb2d; Collider2D_c2d สาธารณะ; #endregion #region อ้างอิง Transform สาธารณะ _myTransform; GameObject สาธารณะ _myObject; ///

/// วัตถุที่ปรากฏเมื่อถูกทำลาย /// GameObject_explodePrefab สาธารณะ; #endregion #region เสียงสาธารณะ AudioSource _audioSource; /// /// เสียงที่เล่นเมื่อได้รับความเสียหาย /// AudioClip_hitSounds สาธารณะ; /// /// เสียงที่เล่นเมื่อมีวัตถุปรากฏขึ้น /// AudioClip_awakeSounds สาธารณะ; /// /// เสียงที่เล่นก่อนตาย /// AudioClip_deadSounds สาธารณะ; #endregion #region ตัวแปรแรงภายนอก /// /// แรงภายนอกที่กระทำต่อวัตถุ ///สาธารณะ Vector2 _ExternalForces = Vector2 ใหม่ (); /// /// เวกเตอร์ความเร็วปัจจุบัน ///สาธารณะ Vector2 _V = Vector2 ใหม่ (); /// /// เวกเตอร์แรงโน้มถ่วงปัจจุบัน ///สาธารณะ Vector2 _G = Vector2 ใหม่ (); #endregion โมฆะเสมือนสาธารณะ Awake() ( Init(); ) โมฆะเสมือนสาธารณะ เริ่มต้น () ( ) โมฆะเสมือนสาธารณะ Init() ( _myTransform = this.transform; _myObject = gameObject; _rb2d = GetComponent (); _c2d = GetComponentsInChildren (); _audioSource = รับส่วนประกอบ (); เล่นสุ่มเสียง(_awakeSounds); BaseBody bb = GetComponent (); _bodies.Add(bb); ) /// /// การทำลายตัวละคร ///โมฆะเสมือนสาธารณะ Destroy() ( _bodies.Remove(this); for (int i = 0; i< _c2d.Length; i++) { _c2d[i].enabled = false; } float _t = PlayRandomSound(_deadSounds); StartCoroutine(WaitAndDestroy(_t)); } /// /// เรารอสักพักก่อนที่จะถูกทำลาย /// /// รอเวลา /// IEnumerator สาธารณะ WaitAndDestroy (float waitTime) ( ให้ผลตอบแทน return WaitForSeconds ใหม่ (waitTime); if (_explodePrefab) ( Instantiate (_explodePrefab, Transformer.position, Quaternion.identity); ) Destroy (gameObject, _deafultTimeDelay); ) /// /// เล่นเสียงแบบสุ่ม /// /// อาร์เรย์ของเสียง /// ระยะเวลาเสียงที่เล่นสาธารณะ PlayRandomSound (AudioClip audioClip) ( float _t = 0; if (audioClip.Length > 0) ( int _i = UnityEngine.Random.Range (0, audioClip.Length - 1); AudioClip _audioClip = audioClip [_i]; _t = _audioClip.length; _audioSource.PlayOneShot(_audioClip); ) กลับ _t; ) /// /// ได้รับความเสียหาย /// /// ระดับความเสียหายความเสียหายต่อโมฆะเสมือนสาธารณะ (ความเสียหายแบบลอยตัว) ( PlayRandomSound(_hitSounds); ) ) )


ดูเหมือนว่าเราจะอธิบายทุกสิ่งที่จำเป็นมากกว่าที่จำเป็น (ภายในกรอบของบทความนี้) ตอนนี้เรามาสืบทอดคลาสเรือจากมันกันดีกว่า เรือซึ่งควรจะสามารถเคลื่อนที่และหมุนได้:


SpaceShip.cs

ใช้ UnityEngine; ใช้ System.Collections; ใช้ System.Collections.Generic; namespace Assets.Scripts.SpaceShooter.Bodies ( เรือคลาสสาธารณะ: BaseBody ( public Vector2 _movement = new Vector2(); public Vector2 _target = new Vector2(); public float _rotation = 0f; public void FixedUpdate() ( float Torque = ControlRotate( _rotation); Vector2 force = ControlForce(_movement); _rb2d.AddTorque(torque); _rb2d.AddRelativeForce(force); ) public float ControlRotate(Vector2 หมุน) ( float result = 0f; return result; ) สาธารณะ Vector2 ControlForce (การเคลื่อนที่ของ Vector2) ( ผลลัพธ์ Vector2 = Vector2 ใหม่ (); ส่งคืนผลลัพธ์; ) ) )


แม้ว่าจะไม่มีอะไรน่าสนใจ แต่ในขณะนี้มันเป็นเพียงคลาส Stub เท่านั้น


นอกจากนี้เรายังจะอธิบายคลาสฐาน (นามธรรม) สำหรับตัวควบคุมอินพุตทั้งหมด BaseInputController:


BaseInputController.cs

ใช้ UnityEngine; ใช้ Assets.Scripts.SpaceShooter.Bodies; namespace Assets.Scripts.SpaceShooter.InputController ( public enum eSpriteRotation ( Rigth = 0, Up = -90, Left = -180, Down = -270 ) คลาสนามธรรมสาธารณะ BaseInputController: MonoBehaviour ( public GameObject _agentObject; public Ship _agentBody; // Link ไปยังองค์ประกอบตรรกะของเรือ public eSpriteRotation _spriteOrientation = eSpriteRotation.Up; // นี่เป็นเพราะไม่ได้มาตรฐาน // การวางแนวของสไปรต์ "ขึ้น" แทนที่จะเป็น "ขวา" โมฆะสาธารณะนามธรรม ControlRotate (float dt); โมฆะสาธารณะนามธรรม ControlForce (float dt); โมฆะเสมือนสาธารณะเริ่มต้น () ( _agentObject = gameObject; _agentBody = gameObject.GetComponent (); ) โมฆะเสมือนสาธารณะ FixedUpdate() ( float dt = Time.fixedDeltaTime; ControlRotate(dt); ControlForce(dt); ) การอัปเดตโมฆะเสมือนสาธารณะ () ( //TO DO ) ) )


และสุดท้ายคือคลาสตัวควบคุมผู้เล่น ผู้เล่น FigtherInput:


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; //คำนวณการหมุนตามการกดแป้นพิมพ์ที่ลอย 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("Horizontal") * 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 ; สาธารณะ SimplePID() ( Kp = 1f; Ki = 0; Kd = 0.2f; ) SimplePID สาธารณะ (float pFactor, float iFactor, float dFactor) ( this.Kp = pFactor; this.Ki = iFactor; this.Kd = dFactor ; ) การอัปเดตโฟลตสาธารณะ (ข้อผิดพลาดโฟลต, 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:


ControlRotate ลอยสาธารณะ (หมุนเวกเตอร์ 2) ( float MV = 0f; float dt = Time.fixedDeltaTime; // คำนวณข้อผิดพลาด float angleError = Mathf.DeltaAngle (_myTransform.eulerAngles.z, targetAngle); // รับการเร่งความเร็วแก้ไข MV = _angleController .Update (angleError, dt); กลับ MV; )

ตัวควบคุม PID จะทำการวางตำแหน่งเชิงมุมของยานอวกาศอย่างแม่นยำโดยใช้แรงบิดเพียงอย่างเดียว ทุกอย่างยุติธรรม ฟิสิกส์และปืนอัตตาจร เกือบจะเหมือนในชีวิตจริง


และหากไม่มี Quaternion.Lerp ของคุณ

ถ้า (!_rb2d.freezeRotation) rb2d.freezeRotation = จริง; float deltaAngle = Mathf.DeltaAngle(_myTransform.eulerAngles.z, targetAngle); float T = dt * Mathf.Abs(_rotationSpeed ​​​​/ deltaAngle); // แปลงมุมให้เป็นเวกเตอร์ Quaternion rot = Quaternion.Lerp(_myTransform.rotation, Quaternion.Euler(เวกเตอร์ใหม่3(0, 0, targetAngle)), T); // เปลี่ยนการหมุนของวัตถุ _myTransform.rotation = rot;


ซอร์สโค้ดผลลัพธ์ของ Ship.cs อยู่ภายใต้สปอยเลอร์

ใช้ UnityEngine; ใช้ Assets.Scripts.Regulator; namespace Assets.Scripts.SpaceShooter.Bodies ( เรือคลาสสาธารณะ: 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(); โมฆะสาธารณะ FixUpdate() ( float Torque = ControlRotate(_targetAngle); Vector2 force = ControlForce(_movement); _rb2d.AddTorque(torque); _rb2d.AddRelativeForce(force); ) public float ControlRotate(ลอย หมุน) ( float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; // คำนวณข้อผิดพลาด float angleError = Mathf.DeltaAngle (_angle, หมุน); // รับการเร่งความเร็วแก้ไข MV = _angleController อัปเดต ( angleError, dt); return MV; ) public Vector2 ControlForce(การเคลื่อนที่ของ Vector2) ( Vector2 MV = new Vector2(); // โค้ดส่วนหนึ่งสำหรับเอฟเฟกต์พิเศษของเอ็นจิ้นที่ทำงานอยู่เพื่อประโยชน์ของ if (การเคลื่อนไหว != Vector2.zero) ( ถ้า (_flame != null) ( _flame.SetActive(จริง); ) ) else ( if (_flame != null) ( _flame.SetActive(false); ) ) MV = การเคลื่อนไหว; ส่งคืนเอ็มวี; ) ) )


ทั้งหมด? เราจะกลับบ้านกันไหม?



ว้าว! เกิดอะไรขึ้น? ทำไมเรือถึงเลี้ยวแปลกๆ? แล้วเหตุใดมันจึงกระดอนกับวัตถุอื่นอย่างแรง? ตัวควบคุม PID โง่ ๆ นี้ไม่ทำงานใช่ไหม


อย่าตื่นตกใจ! เราลองหาคำตอบกันว่าเกิดอะไรขึ้น


ในขณะนี้ได้รับค่า SP ใหม่ มีการกระโดดอย่างรวดเร็ว (แบบขั้นตอน) ในข้อผิดพลาดที่ไม่ตรงกัน ซึ่งตามที่เราจำได้ คำนวณดังนี้: ดังนั้น มีการกระโดดอย่างรวดเร็วในข้อผิดพลาดอนุพันธ์ ซึ่งเราคำนวณเป็น รหัสบรรทัดนี้:


D = (ข้อผิดพลาด - ข้อผิดพลาดล่าสุด) / dt;

แน่นอนคุณสามารถลองใช้แผนการสร้างความแตกต่างอื่นๆ ได้ เช่น สามจุดหรือห้าจุด หรือ... แต่ก็ยังไม่ได้ช่วยอะไร พวกเขาไม่ชอบอนุพันธ์ของการกระโดดที่แหลมคม - ณ จุดดังกล่าวคือฟังก์ชัน ไม่สามารถหาความแตกต่างได้. อย่างไรก็ตาม การทดลองกับโครงร่างการสร้างความแตกต่างและการบูรณาการที่แตกต่างกันก็คุ้มค่าที่จะลองใช้ แต่ไม่ใช่ในบทความนี้


ฉันคิดว่าถึงเวลาแล้วที่จะสร้างกราฟของกระบวนการเปลี่ยนผ่าน: การกระทำแบบเป็นขั้นตอนจาก S(t) = 0 ถึง SP(t) = 90 องศาสำหรับร่างกายที่มีน้ำหนัก 1 กก. ความยาวแขนบังคับ 1 เมตร และขั้นตอนตารางหาความแตกต่าง 0.02 วินาที - เช่นเดียวกับในตัวอย่างของเราใน Unity3D (อันที่จริงไม่ใช่ทั้งหมด เมื่อสร้างกราฟเหล่านี้ ไม่ได้คำนึงถึงโมเมนต์ความเฉื่อยขึ้นอยู่กับเรขาคณิตของวัตถุแข็ง ดังนั้นกระบวนการชั่วคราวจะเล็กน้อย แตกต่างแต่ยังคงคล้ายกันเพียงพอสำหรับการสาธิต) ค่าทั้งหมดบนแผนภูมิจะแสดงเป็นค่าสัมบูรณ์:


อืม เกิดอะไรขึ้นที่นี่? การตอบสนองของคอนโทรลเลอร์ PID หายไปไหน


ยินดีด้วย เราเพิ่งเจอปรากฏการณ์แบบ "เตะ" นั่นเอง เห็นได้ชัดว่า ณ เวลาที่กระบวนการยังคงเป็น PV = 0 และค่าเซ็ตพอยต์อยู่ที่ SP = 90 จากนั้นด้วยการหาความแตกต่างเชิงตัวเลข เราจะได้ค่าอนุพันธ์ของลำดับที่ 4500 ซึ่งจะถูกคูณด้วย เคดี=0.2และเพิ่มด้วย therom ตามสัดส่วน เพื่อว่าที่เอาต์พุตเราจะได้ค่าความเร่งเชิงมุม 990 และนี่เป็นความชั่วร้ายอย่างสมบูรณ์ในแบบจำลองทางกายภาพของ Unity3D (ความเร็วเชิงมุมจะสูงถึง 18,000 องศา/วินาที... ฉันคิดว่านี่คือ ค่าจำกัดของความเร็วเชิงมุมสำหรับ RigidBody2D)


  • บางทีอาจคุ้มค่าที่จะเลือกค่าสัมประสิทธิ์ด้วยตนเองเพื่อให้การกระโดดไม่แรงนัก?
  • เลขที่! สิ่งที่ดีที่สุดที่เราสามารถทำได้ด้วยวิธีนี้คือแอมพลิจูดเล็กน้อยของการกระโดดแบบอนุพันธ์ แต่ตัวการกระโดดจะยังคงเหมือนเดิม และในกรณีนี้ องค์ประกอบดิฟเฟอเรนเชียลอาจไม่ได้ผลโดยสิ้นเชิง

อย่างไรก็ตาม คุณสามารถทดลองได้

พยายามหมายเลขสอง ความอิ่มตัว

มันสมเหตุสมผลแล้ว หน่วยไดรฟ์(ในกรณีของเราคือกลไกการหลบหลีกเสมือนจริงของ SpaceShip) ไม่สามารถจัดการกับค่าขนาดใหญ่ใด ๆ ที่ตัวควบคุมที่บ้าคลั่งของเราไม่สามารถผลิตได้ ดังนั้นสิ่งแรกที่เราจะทำคือทำให้เอาต์พุตของตัวควบคุมอิ่มตัว:


ControlRotate ลอยสาธารณะ (Vector2 หมุน, แรงผลักดันลอย) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; // คำนวณข้อผิดพลาด float angleError = Mathf.DeltaAngle (_myTransform.eulerAngles.z, targetAngle); / / เราได้รับการเร่งความเร็วที่ถูกต้อง CO = _angleController.Update(angleError, dt); //Saturate MV = CO; if (MV > thrust) MV = thrust; if (MV< -thrust) MV = -thrust; return MV; }

และอีกครั้งที่คลาส Ship ที่เขียนใหม่มีลักษณะดังนี้:

namespace Assets.Scripts.SpaceShooter.Bodies ( เรือคลาสสาธารณะ: 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); โมฆะสาธารณะ FixUpdate () ( _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); // Saturate MV = CO; if (MV > thrust) MV = thrust; 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() { } } }


รูปแบบสุดท้ายของปืนอัตตาจรของเราจะมีลักษณะเช่นนี้


ในขณะเดียวกันก็ชัดเจนว่าเอาต์พุตของคอนโทรลเลอร์ คาร์บอนไดออกไซด์(t)แตกต่างจากตัวแปรควบคุมของกระบวนการเล็กน้อย เอ็มวี(ที).


จริงๆ แล้ว จากที่นี่ คุณสามารถเพิ่มเอนทิตีเกมใหม่ได้แล้ว - หน่วยไดรฟ์ซึ่งกระบวนการจะถูกควบคุม ตรรกะที่อาจซับซ้อนกว่าแค่ Mathf.Clamp() เช่น คุณสามารถแนะนำการแยกค่าต่างๆ ได้ (เพื่อไม่ให้ฟิสิกส์ของเกมโอเวอร์โหลดด้วยค่าที่เข้ามา ในอันดับที่หกหลังจุดทศนิยม) โซนตาย (อีกครั้งไม่ใช่ มันสมเหตุสมผลแล้วที่จะโอเวอร์โหลดฟิสิกส์ด้วยปฏิกิริยาขนาดเล็กพิเศษ) ทำให้เกิดความล่าช้าในการควบคุมและไม่เชิงเส้น (เช่น sigmoid) ของไดรฟ์แล้วดู อะไรมาจากมัน


เมื่อเปิดตัวเกมแล้ว เราจะพบว่าในที่สุดยานอวกาศก็สามารถควบคุมได้:



หากคุณสร้างกราฟ คุณจะเห็นว่าการตอบสนองของคอนโทรลเลอร์เป็นดังนี้:


ที่นี่มีการใช้ค่าที่ทำให้เป็นมาตรฐานแล้ว มุมจะถูกหารด้วยค่า SP และเอาต์พุตของคอนโทรลเลอร์จะถูกทำให้เป็นมาตรฐานโดยสัมพันธ์กับค่าสูงสุดที่เกิดความอิ่มตัวแล้ว

ด้านล่างนี้เป็นตารางที่รู้จักกันดีเกี่ยวกับผลกระทบของการเพิ่มพารามิเตอร์ของคอนโทรลเลอร์ PID ( ฉันจะทำให้แบบอักษรเล็กลงได้อย่างไร ไม่เช่นนั้นตารางยัติภังค์เมอแรงค์จะไม่พอดี):



และอัลกอริธึมทั่วไปสำหรับการปรับคอนโทรลเลอร์ PID ด้วยตนเองมีดังนี้:


  1. เราเลือกค่าสัมประสิทธิ์ตามสัดส่วนโดยปิดการเชื่อมต่อดิฟเฟอเรนเชียลและอินทิกรัลจนกว่าการสั่นในตัวเองจะเริ่มต้นขึ้น
  2. การเพิ่มองค์ประกอบส่วนต่างอย่างค่อยเป็นค่อยไปทำให้เรากำจัดการสั่นในตัวเองออกไป
  3. หากมีข้อผิดพลาดในการควบคุมสารตกค้าง (การเคลื่อนตัว) เราจะกำจัดมันโดยใช้ส่วนประกอบที่เป็นส่วนประกอบ

ไม่มีค่าทั่วไปสำหรับพารามิเตอร์ของตัวควบคุม PID: ค่าเฉพาะขึ้นอยู่กับพารามิเตอร์ของกระบวนการเท่านั้น (ลักษณะการถ่ายโอน): ตัวควบคุม PID ที่ทำงานได้อย่างสมบูรณ์กับวัตถุควบคุมหนึ่งจะไม่ทำงานกับวัตถุควบคุมอื่น นอกจากนี้ ค่าสัมประสิทธิ์สำหรับส่วนประกอบตามสัดส่วน อินทิกรัล และส่วนต่างยังขึ้นอยู่กับกันและกันอีกด้วย


พยายามหมายเลขสาม อนุพันธ์อีกครั้งหนึ่ง

หลังจากติดไม้ยันรักแร้ในรูปแบบของการจำกัดค่าเอาต์พุตของคอนโทรลเลอร์แล้ว เรายังคงไม่สามารถแก้ไขปัญหาที่สำคัญที่สุดของคอนโทรลเลอร์ของเราได้ - ส่วนประกอบส่วนต่างทำงานได้ไม่ดีเมื่อข้อผิดพลาดที่อินพุตของคอนโทรลเลอร์เปลี่ยนไปตามขั้นตอน ในความเป็นจริงมีไม้ค้ำยันอื่น ๆ อีกมากมายเช่นในขณะที่มีการเปลี่ยนแปลง SP อย่างกะทันหัน "ปิด" ส่วนประกอบส่วนต่างหรือติดตั้งตัวกรองความถี่ต่ำผ่านระหว่าง เอสพี(ที)และการดำเนินการเนื่องจากข้อผิดพลาดจะค่อยๆ เพิ่มขึ้น หรือคุณสามารถพลิกกลับได้ทั้งหมดและใช้ตัวกรองคาลมานจริงเพื่อทำให้ข้อมูลอินพุตราบรื่น โดยทั่วไปมีไม้ค้ำยันจำนวนมากและเพิ่ม ผู้สังเกตการณ์แน่นอนว่าฉันต้องการ แต่ไม่ใช่ครั้งนี้


ดังนั้น ลองกลับไปที่อนุพันธ์ของข้อผิดพลาดที่ไม่ตรงกันแล้วดูอย่างละเอียด:



คุณสังเกตเห็นอะไรไหม? หากคุณมองอย่างใกล้ชิด คุณจะพบว่าโดยทั่วไป SP(t) จะไม่เปลี่ยนแปลงเมื่อเวลาผ่านไป (ยกเว้นช่วงเวลาของการเปลี่ยนแปลงขั้นตอนเมื่อตัวควบคุมได้รับคำสั่งใหม่) เช่น อนุพันธ์ของมันคือศูนย์:





กล่าวอีกนัยหนึ่ง แทนที่จะเป็นข้อผิดพลาดอนุพันธ์ที่สามารถหาอนุพันธ์ได้ ไม่ใช่ทุกที่เราสามารถใช้อนุพันธ์ของกระบวนการ ซึ่งในโลกของกลศาสตร์คลาสสิกมักจะมีความต่อเนื่องและแตกต่างไปทุกที่ และแผนภาพของระบบควบคุมอัตโนมัติของเราจะอยู่ในรูปแบบต่อไปนี้:




มาแก้ไขโค้ดคอนโทรลเลอร์กันเถอะ:


ใช้ระบบ; ใช้ System.Collections.Generic; ใช้ System.Linq; ใช้ System.Text; เนมสเปซ Assets.Scripts.Regulator ( SimplePID คลาสสาธารณะ ( public float Kp, Ki, Kd; private float P, I, D; private float LastPV = 0f; public SimplePID() ( Kp = 1f; Ki = 0f; Kd = 0.2f ; ) SimplePID สาธารณะ (float pFactor, float iFactor, float dFactor) ( this.Kp = pFactor; this.Ki = iFactor; this.Kd = dFactor; ) public float Update (ข้อผิดพลาดลอย, float PV, float dt) ( P = ข้อผิดพลาด; I += ข้อผิดพลาด * dt; D = -(PV - LastPV) / dt; LastPV = PV; float CO = Kp * P + Ki * I + Kd * D; กลับ CO; ) ) )

และมาเปลี่ยนวิธี ControlRotate กันสักหน่อย:


ControlRotate ลอยสาธารณะ (Vector2 หมุน, แรงผลักดันลอย) ( 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); //Saturate MV = CO; ถ้า (CO >< -thrust) MV = -thrust; return MV; }

และและและและและ... หากคุณเริ่มเกม คุณจะพบว่าในความเป็นจริงไม่มีอะไรเปลี่ยนแปลงไปนับตั้งแต่การลองครั้งล่าสุด ซึ่งเป็นสิ่งที่จำเป็นต้องได้รับการพิสูจน์ อย่างไรก็ตาม หากคุณลบความอิ่มตัว กราฟการตอบสนองของตัวควบคุมจะมีลักษณะดังนี้:


กระโดด คาร์บอนไดออกไซด์(t)ก็ยังมีอยู่แต่ก็ไม่ใหญ่เท่าเดิมอีกต่อไปแล้ว และที่สำคัญ เป็นที่คาดเดาได้เพราะ มีให้โดยองค์ประกอบตามสัดส่วนโดยเฉพาะ และถูกจำกัดด้วยข้อผิดพลาดที่ไม่ตรงกันสูงสุดที่เป็นไปได้และอัตราขยายตามสัดส่วนของตัวควบคุม PID (และนี่ก็บอกเป็นนัยแล้วว่า เคพีมันสมเหตุสมผลที่จะเลือกน้อยกว่าความสามัคคี เช่น 1/90f) แต่ไม่ได้ขึ้นอยู่กับขั้นตอนของเส้นตารางสร้างความแตกต่าง (เช่น dt). โดยทั่วไป ฉันขอแนะนำอย่างยิ่งให้ใช้อนุพันธ์ของกระบวนการมากกว่าข้อผิดพลาด


ฉันคิดว่าตอนนี้มันจะไม่ทำให้ใครแปลกใจ แต่ในลักษณะเดียวกับที่คุณสามารถแทนที่มันได้ แต่เราจะไม่อยู่กับสิ่งนี้คุณสามารถทดลองตัวเองและบอกในความคิดเห็นว่าอะไรออกมาจากมัน (สิ่งที่น่าสนใจที่สุด)

พยายามหมายเลขสี่ การใช้งานทางเลือกอื่นของคอนโทรลเลอร์ PID

นอกเหนือจากการแสดงตัวควบคุม PID ในอุดมคติที่อธิบายไว้ข้างต้นแล้ว ในทางปฏิบัติมักใช้รูปแบบมาตรฐานโดยไม่มีค่าสัมประสิทธิ์ กี้และ เคดีแทนที่จะใช้ค่าคงที่ชั่วคราว


วิธีการนี้เกิดจากการที่เทคนิคจำนวนหนึ่งสำหรับการปรับแต่งตัวควบคุม PID นั้นขึ้นอยู่กับลักษณะความถี่ของตัวควบคุม PID และกระบวนการ ที่จริงแล้ว TAU ทั้งหมดหมุนรอบลักษณะความถี่ของกระบวนการ ดังนั้นสำหรับผู้ที่ต้องการเจาะลึกและพบกับระบบการตั้งชื่อทางเลือกในทันใด ฉันจะยกตัวอย่างสิ่งที่เรียกว่า แบบฟอร์มมาตรฐานตัวควบคุมพีไอดี:




โดยที่ คือค่าคงที่ของความแตกต่างที่ส่งผลต่อการทำนายสถานะของระบบโดยหน่วยงานกำกับดูแล
- ค่าคงที่การรวมซึ่งส่งผลต่อช่วงเวลาเฉลี่ยข้อผิดพลาดโดยลิงก์อินทิกรัล


หลักการพื้นฐานสำหรับการปรับแต่งคอนโทรลเลอร์ PID ในรูปแบบมาตรฐานนั้นคล้ายคลึงกับคอนโทรลเลอร์ PID ในอุดมคติ:

  • การเพิ่มค่าสัมประสิทธิ์ตามสัดส่วนจะเพิ่มประสิทธิภาพและลดส่วนต่างเสถียรภาพ
  • เมื่อส่วนประกอบสำคัญลดลง ข้อผิดพลาดในการควบคุมจะลดลงเร็วขึ้นเมื่อเวลาผ่านไป
  • การลดค่าคงที่การรวมจะลดส่วนต่างความเสถียร
  • การเพิ่มขึ้นของส่วนประกอบส่วนต่างจะเพิ่มความเสถียรและประสิทธิภาพ

ซอร์สโค้ดของแบบฟอร์มมาตรฐานอยู่ใต้สปอยเลอร์

เนมสเปซ Assets.Scripts.Regulator ( StandartPID คลาสสาธารณะ ( public float Kp, Ti, Td; ข้อผิดพลาด float สาธารณะ, CO; public float P, I, D; โฟลตส่วนตัว LastPV = 0f; StandartPID สาธารณะ () ( Kp = 0.1f; Ti = 10000f; Td = 0.5f; bias = 0f; ) public StandardPID (float Kp, float Ti, float Td) ( this.Kp = Kp; this.Ti = Ti; this.Td = Td; ) public float อัปเดต (float ข้อผิดพลาด, float PV, float dt) ( this.error = error; P = error; I += (1 / Ti) * error * dt; D = -Td * (PV - LastPV) / dt; CO = Kp * ( P + I + D); LastPV = PV; กลับ CO; ) ) )

ค่าเริ่มต้นคือ Kp = 0.01, Ti = 10,000, Td = 0.5 - ด้วยค่าเหล่านี้เรือจะหมุนค่อนข้างเร็วและมีความเสถียรเล็กน้อย


นอกจากตัวควบคุม PID ในรูปแบบนี้แล้วยังเรียกอีกอย่างว่า แบบฟอร์มกำเริบ:



เราจะไม่ยึดติดกับมัน เพราะ... มันมีความเกี่ยวข้องเป็นหลักสำหรับโปรแกรมเมอร์ฮาร์ดแวร์ที่ทำงานกับ FPGA และไมโครคอนโทรลเลอร์ซึ่งการใช้งานดังกล่าวสะดวกและมีประสิทธิภาพมากกว่ามาก ในกรณีของเรา - มามอบบางอย่างให้กับกองบน Unity3D - นี่เป็นเพียงการใช้งานคอนโทรลเลอร์ PID อีกครั้งซึ่งไม่ได้ดีไปกว่าตัวอื่นและยังเข้าใจได้น้อยกว่าด้วยซ้ำ ดังนั้นขอให้ทุกคนชื่นชมยินดีอีกครั้งว่าการเขียนโปรแกรมใน C# ที่สะดวกสบายนั้นดีแค่ไหน และไม่ได้อยู่ใน VHDL ที่น่าขนลุกและแย่มาก

แทนที่จะได้ข้อสรุป คุณจะเพิ่มตัวควบคุม PID ที่ไหนอีก

ทีนี้ลองทำให้การควบคุมของเรือซับซ้อนขึ้นเล็กน้อยโดยใช้การควบคุมแบบดูอัลลูป: ตัวควบคุม PID หนึ่งตัวที่เราคุ้นเคย _angleController ยังคงรับผิดชอบในการวางตำแหน่งเชิงมุม แต่ตัวที่สอง - ใหม่ _angularVelocityController - ควบคุมความเร็วในการหมุน:


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 TorqueCorrectionForAngle = _angleController.Update (angleError, _angle, dt); // ตัวควบคุมเสถียรภาพความเร็ว float angularVelocityError = -_rb2d.angularVelocity; float TorrectionForAngularVelocity = _angularVelocityController.Update (เชิงมุมVelocityError, -angularVelocityError, dt); // รวม เอาต์พุตคอนโทรลเลอร์ CO = TorqueCorrectionForAngle + TorqueCorrectionForAngularVelocity; // ตัวอย่างในขั้นตอนที่ 100 CO = Mathf.Round (100f * CO) / 100f; // Saturate MV = CO; if (CO > thrust) MV = thrust; if (CO< -thrust) MV = -thrust; return MV; }

วัตถุประสงค์ของตัวควบคุมตัวที่สองคือเพื่อลดความเร็วเชิงมุมส่วนเกินโดยการเปลี่ยนแรงบิด - ซึ่งคล้ายกับการเสียดสีเชิงมุมซึ่งเราปิดไว้เมื่อสร้างวัตถุของเกม รูปแบบการควบคุมดังกล่าวจะ [อาจ] ทำให้สามารถรับพฤติกรรมที่เสถียรยิ่งขึ้นของเรือได้ และแม้กระทั่งได้รับโดยมีค่าสัมประสิทธิ์การควบคุมตามสัดส่วนเท่านั้น - ตัวควบคุมตัวที่สองจะรองรับความผันผวนทั้งหมด โดยทำหน้าที่คล้ายกับส่วนประกอบส่วนต่างของตัวควบคุมตัวแรก .


นอกจากนี้ เราจะเพิ่มคลาสอินพุตของผู้เล่นใหม่ - PlayerInputCorvette ซึ่งจะทำการผลัดกันโดยการกดปุ่มซ้ายขวา และเราจะทิ้งการกำหนดเป้าหมายไว้ด้วยเมาส์สำหรับสิ่งที่มีประโยชน์มากขึ้น เช่น สำหรับการควบคุมป้อมปืน . ในเวลาเดียวกัน ตอนนี้เรามีพารามิเตอร์เช่น _turnRate ซึ่งรับผิดชอบความเร็ว/การตอบสนองของการเลี้ยว (ยังไม่ชัดเจนว่าควรวางไว้ที่ใดใน InputCOntroller หรือยังคงจัดส่ง)


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("Horizontal") * _turnSpeed ​​​​* Time.deltaTime; ) public override void ControlForce() ( //ผ่านการเคลื่อนไหว _agentBody._movement = Input .GetAxis("Vertical") * Vector2.up; ) )

นอกจากนี้ เพื่อความชัดเจน เราจะวางสคริปต์เพื่อแสดงข้อมูลการดีบัก

namespace Assets.Scripts.SpaceShooter.UI (ดีบักเกอร์คลาสสาธารณะ: MonoBehaviour ( Ship _ship; BaseInputController _controller; List _pids = รายการใหม่ (); รายการ _names = รายการใหม่ (); Vector2 _orientation = ใหม่ Vector2(); // ใช้สำหรับการเริ่มต้นเป็นโมฆะ Start() ( _ship = GetComponent (); _controller = GetComponent (); _pids.Add(_ship._angleController); _names.Add("ตัวควบคุมมุม"); _pids.Add(_ship._angularVelocityController); _names.Add("ตัวควบคุมความเร็วเชิงมุม"); ) // เรียกการอัพเดตหนึ่งครั้งต่อเฟรม void Update() ( DrawDebug(); ) Vector3 GetDiretion(eSpriteRotation spriteRotation) ( switch (_controller._spriteOrientation) ( case eSpriteRotation.Rigth: return Transformer.right; case eSpriteRotation.Up: return แปลง .up; case eSpriteRotation.Left: return -transform.right; case eSpriteRotation.Down: return -transform.up; ) return Vector3.zero; ) void DrawDebug() ( //ทิศทางการหมุน Vector3 vectorToTarget = แปลงตำแหน่ง + 5f * ใหม่ Vector3 (-Mathf.Sin (_ship._targetAngle * Mathf.Deg2Rad), Mathf.Cos (_ship._targetAngle * Mathf.Deg2Rad), 0f); // ทิศทางปัจจุบัน ส่วนหัวของ Vector3 = แปลงตำแหน่ง + 4f * GetDiretion (_controller. _spriteOrientation); // แรงบิด Vector3 การเร่งความเร็วเชิงมุม = ส่วนหัว - Transformer.right * _ship._Torque; Debug.DrawLine (transform.position, vectorToTarget, Color.white); Debug.DrawLine (transform.position, ส่วนหัว, Color.green); Debug.DrawLine (ส่วนหัว, แรงบิด, Color.red); ) เป็นโมฆะ OnGUI () ( float x0 = 10; ลอย y0 = 100; ลอย dx = 200; ลอย dy = 40; ลอย SliderKpMax = 1; ลอย SliderKpMin = 0; ลอย SliderKiMax = .5f; ลอย SliderKiMin = -.5f; ลอย SliderKdMax = .5f; ลอย SliderKdMin = 0; int i = 0; foreach (SimplePID pid ใน _pids) ( y0 += 2 * dy; GUI.Box(rect ใหม่ (25 + x0, 5 + y0, dx, dy), ""); pid.Kp = GUI.HorizontalSlider (rect ใหม่ ( 25 + x0, 5 + y0, 200, 10), pid.Kp, SliderKpMin, SliderKpMax); pid.Ki = GUI.HorizontalSlider(Rect ใหม่ (25 + x0, 20 + y0, 200, 10), pid.Ki, SliderKiMin, SliderKiMax); pid.Kd = GUI.HorizontalSlider (Rect ใหม่ (25 + x0, 35 + y0, 200, 10), pid.Kd, SliderKdMin, SliderKdMax); GUIStyle style1 = GUIStyle ใหม่ (); style1.alignment = TextAnchor.MiddleRight; style1.fontStyle = FontStyle.Bold; style1.normal.textColor = Color.yellow; style1.fontSize = 9; GUI.Label(rect ใหม่ (0 + x0, 5 + y0, 20, 10), "Kp ", style1); GUI.Label(สี่เหลี่ยมใหม่(0 + x0, 20 + y0, 20, 10), "Ki", ​​​​style1); GUI.Label(สี่เหลี่ยมใหม่(0 + x0, 35 + y0, 20 , 10 ), "Kd", style1); GUIStyle style2 = ใหม่ GUIStyle(); style2.alignment = TextAnchor.MiddleLeft; style2.fontStyle = FontStyle.Bold; style2.normal.textColor = Color.yellow; style2.fontSize = 9 ; GUI .TextField(rect ใหม่ (235 + x0, 5 + y0, 60, 10), pid.Kp.ToString(), style2); GUI.TextField(Rect ใหม่ (235 + x0, 20 + y0, 60, 10), pid Ki.ToString(), style2); GUI.TextField(Rect ใหม่ (235 + x0, 35 + y0, 60, 10), pid.Kd.ToString(), style2); GUI.Label(สี่เหลี่ยมใหม่(0 + x0, -8 + y0, 200, 10), _names, style2); ) ) ) )


คลาส Ship ยังผ่านการกลายพันธุ์ที่ไม่สามารถย้อนกลับได้ และตอนนี้ควรมีลักษณะดังนี้:

namespace Assets.Scripts.SpaceShooter.Bodies ( เรือคลาสสาธารณะ: 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; SimplePID สาธารณะ _angleController = SimplePID ใหม่ (0.1f,0f,0.05f); SimplePID สาธารณะ _angularVelocityController = SimplePID ใหม่ (0f,0f,0f); float ส่วนตัว _torque = 0f; float สาธารณะ _Torque ( รับ ( return _torque; ) ) Vector2 ส่วนตัว _force = vector2 ใหม่ (); Vector2 สาธารณะ _Force ( รับ ( return _force; ) ) โมฆะสาธารณะ FixUpdate() ( _torque = ControlRotate(_targetAngle, _thrust); _force = ControlForce(_movement, _thrust); _rb2d.AddTorque( _torque); _rb2d.AddRelativeForce(_force); ) ControlRotate ลอยสาธารณะ (float targetAngle, float thrust) ( float CO = 0f; float MV = 0f; float dt = Time.fixedDeltaTime; _angle = _myTransform.eulerAngles.z; // ตัวควบคุม มุมการหมุน มุมลอย ข้อผิดพลาด = Mathf.DeltaAngle (_angle, targetAngle); แรงบิดลอย CorrectionForAngle = _angleController.Update (angleError, _angle, dt); //ตัวควบคุมเสถียรภาพความเร็ว float เชิงมุมVelocityError = -_rb2d.เชิงมุมVelocity; แรงบิดลอยตัว CorrectionForAngularVelocity = _angularVelocityController.Update (เชิงมุมVelocityError, -เชิงมุมVelocityError, dt); // เอาต์พุตคอนโทรลเลอร์ทั้งหมด CO = TorqueCorrectionForAngle + TorqueCorrectionForAngularVelocity; //แยกส่วนเป็นขั้นละ 100 CO = Mathf.Round(100f * CO) / 100f; //อิ่มตัว MV = CO; ถ้า (CO > แรงขับ) MV = แรงขับ; ถ้า(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 กว้างขึ้น สัญญาณเอาท์พุต Y ก็จะยิ่งน้อยลงสำหรับค่าเบี่ยงเบน E ที่เท่ากัน

ภายนอกแถบสัดส่วน เอาต์พุต Y จะเป็น 0 หรือ 100%

เมื่อกฎ P ทำงาน ตัวควบคุมจะสร้างพัลส์โดยมีเพียงส่วนประกอบตามสัดส่วนของค่าสัญญาณเอาท์พุตเท่านั้น


เมื่ออุปกรณ์ทำงานในโหมดคอนโทรลเลอร์ PD ขนาดของสัญญาณเอาต์พุต Yi ไม่เพียงขึ้นอยู่กับขนาดของส่วนเบี่ยงเบน Ei เท่านั้น แต่ยังขึ้นอยู่กับอัตราการเปลี่ยนแปลงด้วย:

การเปลี่ยนแปลงสัญญาณเอาต์พุตของคอนโทรลเลอร์โดยมีการเปลี่ยนแปลงส่วนเบี่ยงเบนแบบขั้นตอนจะแสดงในรูป ในช่วงแรกหลังจากการเปลี่ยนแปลงขั้นตอนใน 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 เป็นอุปกรณ์ที่ติดตั้งอยู่ในวงจรควบคุมพร้อมข้อเสนอแนะบังคับ ได้รับการออกแบบมาเพื่อรักษาระดับที่กำหนดไว้ของค่าที่ระบุ เช่น อุณหภูมิของอากาศ

อุปกรณ์จ่ายการควบคุมหรือสัญญาณเอาท์พุตไปยังอุปกรณ์ควบคุม โดยมีพื้นฐานอยู่บนข้อมูลที่ได้รับจากตัวตรวจรู้หรือตัวตรวจรู้ ตัวควบคุมมีความแม่นยำสูงของกระบวนการชั่วคราวและคุณภาพของการปฏิบัติงานที่ได้รับมอบหมาย

ค่าสัมประสิทธิ์ตัวควบคุม PID สามตัวและหลักการทำงาน

การทำงานของคอนโทรลเลอร์ PID คือการจัดเตรียมสัญญาณเอาท์พุตเกี่ยวกับพลังงานที่จำเป็นในการรักษาพารามิเตอร์ที่ถูกควบคุมในระดับที่กำหนด ในการคำนวณตัวบ่งชี้จะใช้สูตรทางคณิตศาสตร์ที่ซับซ้อนซึ่งมี 3 ค่าสัมประสิทธิ์ - สัดส่วน, อินทิกรัล, ส่วนต่าง

ให้เรานำภาชนะใส่น้ำมาเป็นวัตถุควบคุมซึ่งจำเป็นต้องรักษาอุณหภูมิในระดับที่กำหนดโดยการปรับระดับการเปิดวาล์วด้วยไอน้ำ

องค์ประกอบตามสัดส่วนจะปรากฏขึ้นในขณะที่ข้อมูลอินพุตไม่ตรงกัน พูดง่าย ๆ ดูเหมือนว่านี้ - นำความแตกต่างระหว่างอุณหภูมิจริงกับอุณหภูมิที่ต้องการคูณด้วยค่าสัมประสิทธิ์ที่ปรับได้และรับสัญญาณเอาต์พุตที่ควรจ่ายให้กับวาล์ว เหล่านั้น. ทันทีที่องศาลดลง กระบวนการทำความร้อนจะเริ่มขึ้น หากสูงกว่าระดับที่ต้องการ การปิดเครื่องหรือแม้แต่การระบายความร้อนจะเกิดขึ้น

ถัดมาคือส่วนประกอบสำคัญ ซึ่งออกแบบมาเพื่อชดเชยอิทธิพลของสภาพแวดล้อมหรืออิทธิพลรบกวนอื่นๆ ที่มีต่อการรักษาอุณหภูมิของเราในระดับที่กำหนด เนื่องจากมีปัจจัยเพิ่มเติมที่มีอิทธิพลต่ออุปกรณ์ควบคุมอยู่เสมอ ในขณะที่ข้อมูลมาถึงเพื่อคำนวณส่วนประกอบตามสัดส่วน ตัวเลขจึงเปลี่ยนแปลงไปแล้ว และยิ่งอิทธิพลภายนอกมากเท่าไร ความผันผวนของตัวบ่งชี้ก็จะยิ่งแข็งแกร่งขึ้นเท่านั้น มีไฟกระชากในกำลังไฟที่จ่าย

ส่วนประกอบสำคัญจะพยายามส่งกลับค่าหากมีการเปลี่ยนแปลง โดยอิงตามค่าอุณหภูมิที่ผ่านมา กระบวนการนี้อธิบายไว้ในรายละเอียดเพิ่มเติมในวิดีโอด้านล่าง

อินทิกรัลใช้เพื่อกำจัดข้อผิดพลาดโดยการคำนวณข้อผิดพลาดคงที่ สิ่งสำคัญในกระบวนการนี้คือการเลือกค่าสัมประสิทธิ์ที่ถูกต้อง มิฉะนั้นข้อผิดพลาด (ไม่ตรงกัน) จะส่งผลต่อส่วนประกอบสำคัญด้วย

องค์ประกอบที่สามของ PID กำลังสร้างความแตกต่าง ได้รับการออกแบบมาเพื่อชดเชยผลกระทบของความล่าช้าที่เกิดขึ้นระหว่างผลกระทบต่อระบบและผลตอบรับ ตัวควบคุมสัดส่วนจะจ่ายไฟจนกว่าอุณหภูมิจะถึงระดับที่ต้องการ แต่ข้อผิดพลาดมักเกิดขึ้นเมื่อข้อมูลส่งผ่านไปยังอุปกรณ์ โดยเฉพาะอย่างยิ่งที่ค่าสูง นี่อาจทำให้เกิดความร้อนสูงเกินไป ส่วนต่างทำนายความเบี่ยงเบนที่เกิดจากความล่าช้าหรืออิทธิพลของสิ่งแวดล้อม และลดกำลังไฟฟ้าที่จ่ายล่วงหน้า

การตั้งค่าตัวควบคุม PID

ตัวควบคุม PID ได้รับการกำหนดค่าโดยใช้ 2 วิธี:

  1. การสังเคราะห์เกี่ยวข้องกับการคำนวณพารามิเตอร์ตามแบบจำลองของระบบ การตั้งค่านี้แม่นยำ แต่ต้องใช้ความรู้เชิงลึกเกี่ยวกับทฤษฎีการควบคุมอัตโนมัติ เป็นเรื่องของวิศวกรและนักวิทยาศาสตร์เท่านั้น เนื่องจากจำเป็นต้องคำนึงถึงลักษณะการบริโภคและทำการคำนวณมากมาย
  2. วิธีการด้วยตนเองนั้นขึ้นอยู่กับการลองผิดลองถูก ในการทำเช่นนี้จะใช้ข้อมูลของระบบสำเร็จรูปเป็นพื้นฐานและมีการปรับเปลี่ยนค่าสัมประสิทธิ์การควบคุมอย่างน้อยหนึ่งค่า หลังจากเปิดเครื่องและสังเกตผลลัพธ์สุดท้ายแล้ว พารามิเตอร์จะเปลี่ยนไปในทิศทางที่ต้องการ และต่อๆ ไปจนได้ประสิทธิภาพตามที่ต้องการ

วิธีการวิเคราะห์และการปรับเปลี่ยนทางทฤษฎีนั้นไม่ค่อยได้ใช้ในทางปฏิบัติมากนัก ซึ่งเกิดจากการเพิกเฉยต่อลักษณะของวัตถุควบคุมและอิทธิพลที่รบกวนที่อาจเกิดขึ้นมากมาย โดยทั่วไปคือวิธีการทดลองโดยอาศัยการสังเกตระบบ

กระบวนการอัตโนมัติสมัยใหม่ถูกนำมาใช้เป็นโมดูลพิเศษที่ควบคุมโดยโปรแกรมเพื่อปรับค่าสัมประสิทธิ์ตัวควบคุม

วัตถุประสงค์ของตัวควบคุม PID

ตัวควบคุม PID ได้รับการออกแบบมาเพื่อรักษาค่าบางอย่างให้อยู่ในระดับที่ต้องการ เช่น อุณหภูมิ ความดัน ระดับในถัง อัตราการไหลในท่อ ความเข้มข้นของบางสิ่งบางอย่าง ฯลฯ โดยการเปลี่ยนการควบคุมบนแอคทูเอเตอร์ เช่น วาล์วควบคุมอัตโนมัติ โดยใช้สัดส่วน การอินทิเกรต และปริมาณที่ทำให้เกิดความแตกต่างในการตั้งค่า

วัตถุประสงค์การใช้งานคือการรับสัญญาณควบคุมที่แม่นยำซึ่งสามารถควบคุมอุตสาหกรรมขนาดใหญ่และแม้แต่เครื่องปฏิกรณ์ของโรงไฟฟ้าได้

ตัวอย่างวงจรควบคุมอุณหภูมิ

ตัวควบคุม PID มักใช้เพื่อควบคุมอุณหภูมิ ลองดูกระบวนการอัตโนมัตินี้โดยใช้ตัวอย่างง่ายๆ ของการทำความร้อนน้ำในภาชนะ

ภาชนะเต็มไปด้วยของเหลวซึ่งจะต้องได้รับความร้อนจนถึงอุณหภูมิที่ต้องการและรักษาในระดับที่กำหนด มีการติดตั้งเซ็นเซอร์วัดอุณหภูมิภายในถัง - หรือเชื่อมต่อโดยตรงกับตัวควบคุม PID

เพื่อให้ความร้อนแก่ของเหลว เราจะจ่ายไอน้ำพร้อมวาล์วควบคุมอัตโนมัติดังแสดงในรูปด้านล่าง ตัววาล์วรับสัญญาณจากตัวควบคุม ผู้ปฏิบัติงานป้อนค่าอุณหภูมิที่กำหนดในตัวควบคุม PID ที่ต้องคงไว้ในถัง

หากการตั้งค่าสัมประสิทธิ์ตัวควบคุมไม่ถูกต้อง อุณหภูมิของน้ำจะผันผวน โดยวาล์วเปิดจนสุดหรือปิดสนิท ในกรณีนี้ จำเป็นต้องคำนวณค่าสัมประสิทธิ์ตัวควบคุม PID แล้วป้อนอีกครั้ง หากทุกอย่างถูกต้อง หลังจากช่วงเวลาสั้นๆ ระบบจะปรับระดับกระบวนการและอุณหภูมิในภาชนะจะคงอยู่ที่จุดที่ตั้งไว้ ในขณะที่ระดับการเปิดของวาล์วควบคุมจะอยู่ในตำแหน่งตรงกลาง

คอนโทรลเลอร์ PID เป็นอุปกรณ์สำเร็จรูปที่จะอนุญาตให้ผู้ใช้ใช้อัลกอริธึมซอฟต์แวร์สำหรับควบคุมอุปกรณ์บางอย่างของระบบอัตโนมัติ การสร้างและกำหนดค่าระบบควบคุมจะง่ายขึ้นมากหากคุณใช้อุปกรณ์สำเร็จรูป เช่น ตัวควบคุม universal PID TRM148 สำหรับ 8 ช่องจากบริษัท Aries

สมมติว่าคุณต้องบำรุงรักษาสภาพภูมิอากาศที่ถูกต้องในเรือนกระจกโดยอัตโนมัติ: โดยคำนึงถึงอุณหภูมิของดินใกล้กับรากพืช ความกดอากาศ ความชื้นในอากาศและดิน และรักษาพารามิเตอร์ที่ตั้งไว้โดยการควบคุมพัดลม ไม่มีอะไรง่ายไปกว่านี้แล้ว เพียงแค่กำหนดค่าคอนโทรลเลอร์ PID

ก่อนอื่นมาจำกันก่อนว่าคอนโทรลเลอร์ PID คืออะไร? ตัวควบคุม PID เป็นอุปกรณ์พิเศษที่ดำเนินการปรับพารามิเตอร์เอาต์พุตอย่างแม่นยำอย่างต่อเนื่องในสามวิธี: ตามสัดส่วน บูรณาการและต่างกัน และพารามิเตอร์เริ่มต้นคือพารามิเตอร์อินพุตที่ได้รับจากเซ็นเซอร์ (ความดัน ความชื้น อุณหภูมิ แสง ฯลฯ)

พารามิเตอร์อินพุตจะถูกส่งไปยังอินพุตของตัวควบคุม PID จากเซ็นเซอร์ เช่น จากเซ็นเซอร์ความชื้น ตัวควบคุมจะได้รับค่าแรงดันหรือกระแส วัดค่า จากนั้นทำการคำนวณตามอัลกอริธึม และในที่สุดก็จะส่งสัญญาณไปยังเอาต์พุตที่เกี่ยวข้อง ซึ่งส่งผลให้ระบบอัตโนมัติได้รับการดำเนินการควบคุม ความชื้นในดินลดลง - เปิดการรดน้ำสักครู่

เป้าหมายคือเพื่อให้ได้ค่าความชื้นที่ผู้ใช้กำหนด หรือตัวอย่าง: การส่องสว่างลดลง - เปิดไฟโตแลมป์เหนือต้นไม้ เป็นต้น

การควบคุมพีไอดี

ในความเป็นจริง แม้ว่าทุกอย่างจะดูเรียบง่าย แต่ภายในตัวควบคุม คณิตศาสตร์นั้นซับซ้อนกว่า ทุกอย่างไม่ได้เกิดขึ้นในขั้นตอนเดียว หลังจากเปิดการชลประทาน ตัวควบคุม PID จะทำการวัดอีกครั้ง โดยวัดว่าค่าอินพุตเปลี่ยนแปลงไปมากน้อยเพียงใด - นี่คือวิธีที่พบข้อผิดพลาดในการควบคุม ผลกระทบถัดไปต่อผู้บริหารจะได้รับการปรับเปลี่ยนโดยคำนึงถึงข้อผิดพลาดด้านกฎระเบียบที่วัดได้ และต่อๆ ไปในแต่ละขั้นตอนการควบคุมจนกว่าจะบรรลุเป้าหมาย - พารามิเตอร์ที่ผู้ใช้ระบุ - บรรลุผล

องค์ประกอบสามประการที่เกี่ยวข้องกับการควบคุม ได้แก่ สัดส่วน อินทิกรัล และดิฟเฟอเรนเชียล แต่ละส่วนประกอบมีระดับความสำคัญของตัวเองในแต่ละระบบเฉพาะ และยิ่งการมีส่วนร่วมของส่วนประกอบหนึ่งหรือส่วนประกอบอื่นมากเท่าไร ก็ควรมีการเปลี่ยนแปลงในกระบวนการกำกับดูแลมากขึ้นเท่านั้น

องค์ประกอบตามสัดส่วนนั้นง่ายที่สุด ยิ่งมีการเปลี่ยนแปลงมาก ค่าสัมประสิทธิ์ก็จะยิ่งมากขึ้น (สัดส่วนในสูตร) ​​และเพื่อลดผลกระทบ ก็เพียงพอแล้วที่จะลดค่าสัมประสิทธิ์ (ตัวคูณ)

สมมติว่าความชื้นในดินในเรือนกระจกต่ำกว่าค่าที่ตั้งไว้มาก - ดังนั้นเวลาในการรดน้ำควรนานขึ้นตามปริมาณที่เท่ากันเนื่องจากความชื้นในปัจจุบันต่ำกว่าค่าที่ตั้งไว้ นี่เป็นตัวอย่างคร่าวๆ แต่นี่คือหลักการทั่วไป

องค์ประกอบที่เป็นส่วนประกอบ - จำเป็นต้องเพิ่มความแม่นยำในการควบคุมตามเหตุการณ์การควบคุมก่อนหน้านี้: ข้อผิดพลาดก่อนหน้านี้จะถูกรวมเข้าด้วยกัน และจะมีการแก้ไขเพื่อให้ได้รับการเบี่ยงเบนเป็นศูนย์ในที่สุดเมื่อทำการควบคุมในอนาคต

สุดท้ายคือองค์ประกอบส่วนต่าง อัตราการเปลี่ยนแปลงของตัวแปรควบคุมจะถูกนำมาพิจารณาด้วย ไม่ว่าค่าที่ระบุจะเปลี่ยนแปลงอย่างราบรื่นหรือกะทันหันก็ตาม การดำเนินการตามกฎระเบียบไม่ควรนำไปสู่การเบี่ยงเบนค่ามากเกินไปในระหว่างการควบคุม

สิ่งที่เหลืออยู่คือการเลือกอุปกรณ์สำหรับการควบคุม PID วันนี้มีหลายรายการในตลาดมีหลายช่องที่ให้คุณเปลี่ยนพารามิเตอร์หลายรายการพร้อมกันดังในตัวอย่างข้างต้นพร้อมเรือนกระจก

มาดูการออกแบบคอนโทรลเลอร์โดยใช้ตัวอย่างคอนโทรลเลอร์ universal PID TRM148 จากบริษัท Aries กัน

เซ็นเซอร์อินพุตแปดตัวส่งสัญญาณไปยังอินพุตที่เกี่ยวข้อง สัญญาณจะถูกปรับขนาด, กรอง, แก้ไข, สามารถดูค่าได้บนจอแสดงผลโดยการสลับด้วยปุ่ม

เอาต์พุตของอุปกรณ์ได้รับการผลิตขึ้นโดยการดัดแปลงที่แตกต่างกันโดยต้องผสมผสานสิ่งต่อไปนี้:

    รีเลย์ 4 A 220 V;

    ออปโตคัปเปลอร์ของทรานซิสเตอร์ชนิด n–p–n 400 mA 60 V;

    ออปโตคัปเปลอร์ triac 50 mA 300 V;

    DAC “พารามิเตอร์กระแส 4...20 mA”;

    DAC “แรงดันไฟฟ้าพารามิเตอร์ 0...10 V”;

    เอาต์พุต 4...6 V 100 mA สำหรับควบคุมโซลิดสเตตรีเลย์

ดังนั้นการดำเนินการควบคุมอาจเป็นแบบอะนาล็อกหรือดิจิทัล - สิ่งเหล่านี้คือพัลส์ที่มีความกว้างแปรผันและอะนาล็อก - ในรูปแบบของแรงดันหรือกระแสที่เปลี่ยนแปลงได้อย่างราบรื่นในช่วงรวม: จาก 0 ถึง 10 V สำหรับแรงดันไฟฟ้าและจาก 4 ถึง 20 mA สำหรับสัญญาณปัจจุบัน

สัญญาณเอาท์พุตเหล่านี้ถูกใช้อย่างแม่นยำในการควบคุมแอคชูเอเตอร์ เช่น ปั๊มระบบชลประทาน หรือรีเลย์ที่เปลี่ยนองค์ประกอบความร้อนหรือมอเตอร์ควบคุมวาล์วเปิดและปิด มีตัวบ่งชี้สัญญาณบนแผงควบคุม


ในการโต้ตอบกับพีซี ตัวควบคุม TRM148 มีอินเทอร์เฟซ RS-485 ซึ่งช่วยให้:

    กำหนดค่าอุปกรณ์บนพีซี (มีโปรแกรมกำหนดค่าให้ฟรี)

    ส่งค่าปัจจุบันของค่าที่วัดได้, กำลังเอาต์พุตของคอนโทรลเลอร์, รวมถึงพารามิเตอร์ที่ตั้งโปรแกรมได้ไปยังเครือข่าย

  • รับข้อมูลการดำเนินงานจากเครือข่ายเพื่อสร้างสัญญาณควบคุม

เป็นที่ถกเถียงกันอยู่ว่าประสิทธิภาพสูงสุดนั้นมาจาก ป-ลอว์, - ขึ้นอยู่กับอัตราส่วน tp / T d .

อย่างไรก็ตาม หากการได้รับ P-regulator Kr มีขนาดเล็ก (ส่วนใหญ่มักจะสังเกตได้ด้วยความล่าช้า) สิ่งนี้ไม่ได้ให้ความแม่นยำในการควบคุมสูงเพราะ ในกรณีนี้ค่าจะมีขนาดใหญ่

ถ้า Kp > 10 แสดงว่า P-regulator เป็นที่ยอมรับได้ และหาก Kp< 10, то требуется введение в закон управления составляющей.

กฎหมายควบคุม PI

ที่พบมากที่สุดในทางปฏิบัติคือ ตัวควบคุมพีไอ,ซึ่งมีข้อดีดังต่อไปนี้:

  1. ให้การควบคุมเป็นศูนย์
  2. ค่อนข้างง่ายในการตั้งค่าเพราะว่า... มีการปรับพารามิเตอร์เพียงสองตัวเท่านั้น ได้แก่ อัตราขยาย Kp และค่าคงที่เวลาการรวม Ti ในตัวควบคุมดังกล่าว สามารถปรับค่าอัตราส่วน Kp/Ti-min ให้เหมาะสมได้ ซึ่งรับประกันการควบคุมด้วยการควบคุมรูต-ค่าเฉลี่ย-กำลังสองขั้นต่ำที่เป็นไปได้
  3. ความไวต่อสัญญาณรบกวนต่ำในการวัด (ต่างจากตัวควบคุม PID)

กฎหมายควบคุมพีไอดี

สำหรับลูปควบคุมที่สำคัญที่สุด เราสามารถแนะนำให้ใช้ได้ , ให้ประสิทธิภาพสูงสุดในระบบ

อย่างไรก็ตาม โปรดทราบว่าการดำเนินการนี้ทำได้เฉพาะกับการตั้งค่าที่เหมาะสมที่สุดเท่านั้น (มีการกำหนดค่าพารามิเตอร์สามตัว)

ด้วยความล่าช้าที่เพิ่มขึ้นในระบบ การเปลี่ยนเฟสเชิงลบจะเพิ่มขึ้นอย่างรวดเร็ว ซึ่งจะช่วยลดผลกระทบของส่วนประกอบส่วนต่างของคอนโทรลเลอร์ ดังนั้นคุณภาพของตัวควบคุม PID สำหรับระบบที่มีความล่าช้ามากจึงเทียบได้กับคุณภาพของตัวควบคุม PI

นอกจากนี้ การมีอยู่ของสัญญาณรบกวนในช่องการวัดในระบบที่มีตัวควบคุม PID ทำให้เกิดความผันผวนแบบสุ่มอย่างมีนัยสำคัญในสัญญาณควบคุมของตัวควบคุม ซึ่งเพิ่มความแปรปรวนของข้อผิดพลาดในการควบคุมและการสึกหรอของกลไก

ดังนั้นควรเลือกตัวควบคุม PID สำหรับระบบควบคุมที่มีระดับเสียงค่อนข้างต่ำและความล่าช้าในการควบคุม ตัวอย่างของระบบดังกล่าว ได้แก่ ระบบควบคุมอุณหภูมิ



ดำเนินการต่อในหัวข้อ:
พลาสเตอร์

ทุกคนรู้ว่าซีเรียลคืออะไร ท้ายที่สุดแล้วมนุษย์เริ่มปลูกพืชเหล่านี้เมื่อกว่า 10,000 ปีก่อน นั่นเป็นเหตุผลว่าทำไมถึงมีชื่อซีเรียลต่างๆ เช่น ข้าวสาลี ข้าวไรย์ ข้าวบาร์เลย์ ข้าว...

บทความใหม่
/
เป็นที่นิยม