Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

PID controller

Discussion in 'Scripting' started by andeeeee, Nov 24, 2010.

  1. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    The last thing you want is for your PIDs to get out of control and so I've implemented a PID controller class.

    No, a PID controller is actually like a simple autopilot device that keeps a system in a stable state. It works by measuring a numeric value and then comparing that against a desired value known as the setpoint. The difference between the two is called the error value, and the output of the controller is a correction value that should be used somehow to get the system back to the setpoint.

    A good example of this is a cruise control system. Say the desired speed (setpoint) is 50mph. If the car starts going up a hill then its speed will reduce, in which case more power should be applied. The amount of extra power (correction value) should depend on how much slower than the cruising speed the car is going (error value).

    The real issue, of course, is how much correction to apply for how much error. The PID concept (present, integral, derivative) essentially takes an estimate of the present, past and future of the system into account when deciding how to get it to track the setpoint.

    The class is very simple to use, just a matter of setting up the parameters and then determining the correction value with the Update function:-
    Code (csharp):
    1. var pid: PID;    // Set values in the inspector.
    2.   …
    3.  
    4. var correction = pid.Update(setSpeed, actualSpeed, Time.deltaTime);
    The three parameters are the setpoint value, the measured value and the time since the last update. The correction can be used to vary physical forces, angles, distances… almost anything. The main value of the PID controller for games is that it is easy to add a bit of realistic delay and tolerance of noise.

    The theory of PID controllers is explained on Wikipedia and elsewhere on the internet. I've attached a very simple scene that shows how a PID-controlled value can be used to track a target.

    Bon appétit!
     

    Attached Files:

  2. brianchasalow

    brianchasalow

    Joined:
    Jun 3, 2010
    Posts:
    208
    edit: I should mention- either there is a bug in the PID.cs code, or i'm trying to use it in a way it's not intended- lastError has to be assigned before the float deriv definition, or else when i try to use the dFactor value, i get an error that it was never assigned?

    Anyway, this is super awesome, thank you. I used it to stabilize a rigidbody that always wants to face the camera but still be able to roll around, using AddTorque and Cross products. Not sure if there's a better way to do this, but heres what i did:

    Code (csharp):
    1.    
    2.         Vector3 heading = (Camera.main.transform.position - myObject.transform.position).normalized;   
    3.         Vector3 cross = Vector3.Cross(myObject.transform.forward, heading);
    4.  
    5.         if(crossUpdate == null)
    6.             crossUpdate = cross;
    7.  
    8.         crossUpdate = new Vector3(pid.Update(cross.x, crossUpdate.x, Time.deltaTime),
    9.             pid.Update(cross.y, crossUpdate.y, Time.deltaTime),
    10.             pid.Update(cross.z, crossUpdate.z, Time.deltaTime));
    11.         myObject.rigidbody.AddTorque(crossUpdate*1000);
    12.  
    another edit: while good, the pid controller didn't give me quite 'right' values for feeling natural, felt too much like autopilot for this application (go fig!) so i used the AngleAxis 'face towards a direction' code from http://answers.unity3d.com/questions/29725/can-someone-convert-this-c-script-to-javascript
     
    Last edited: Jan 11, 2011
  3. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Thanks for your vigilance, Brian! However, I believe the code I posted is correct (at least I don't get any errors when testing it). If you check out the code you suggested:-
    Code (csharp):
    1. lastError = present;
    2. float deriv = (present - lastError) / timeFrame;
    ...you will see that it is essentially the same as subtracting the present value from itself and so the derivative will always be zero. The purpose of lastError is to remember what the error was on the previous update and so it has to be assigned after all calculations have finished with it on the current update.
     
  4. brianchasalow

    brianchasalow

    Joined:
    Jun 3, 2010
    Posts:
    208
    yeah, that makes sense- ha i'm ridiculous. i was just getting an error that it wasn't initialized when trying to use dFactor, didn't actually think about what it was doing.

    if you just set lastError to zero or somesuch in Start() it's prob safer, yeah? dunno why i was getting an error and you werent. maybe i was getting an error for a completely different reason...more to do with my implementation than the code itself. edited my earlier post so that people don't assume i was correct and try to copy when i was clearly wrong.
     
    Last edited: Jan 11, 2011
  5. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    A PID was never built in. This was a community contribution.
     
  6. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,617
    I know this is ancient, but it's still useful, so I wanted to say thanks!

    I've spent hours here and there getting active stabilisation working nicely on something. 15 minutes ago I thought "there's a known solution for this, I should just use that". And here it is, and 10 minutes later it's working perfectly!
     
    LaneFox likes this.
  7. U-GeeN

    U-GeeN

    Joined:
    Mar 22, 2015
    Posts:
    95
    Can anyone give me a hint on how to solve the 360 degree swap problem with that controller?
    If I try to rotate to negative values I get a negative setpoint and a positive actual value so it becomes unstable.
     
  8. hereandnowlive

    hereandnowlive

    Joined:
    Jul 24, 2015
    Posts:
    2
  9. hughesjs

    hughesjs

    Joined:
    Mar 25, 2017
    Posts:
    1
    Hate to resurrect an old post but I'm just going to drop a wee correction because I work in process automation where PIDs play a crucial part. The 'P' stands for proportional not present...
     
  10. Bezzy

    Bezzy

    Joined:
    Apr 1, 2009
    Posts:
    75
    Try passing in "0f" as your "target" into the PID, and the delta angle (as opposed to the absolute) as the "current", using Mathf.DeltaAngle(0f, yourAngle). This will transform the angle from the range 0..360 over to -180..+180. Think of it like, you're trying to reduce the delta (the error) to zero using the PID.

    Two and a half years later, heh. Not a moment too soon!
     
    Tudor_n and angrypenguin like this.
  11. ristophonics

    ristophonics

    Joined:
    May 23, 2014
    Posts:
    32
    very nice explanation and example!
     
  12. MaximovInk

    MaximovInk

    Joined:
    Oct 17, 2016
    Posts:
    11
    Thank you, man!
     
  13. Why485

    Why485

    Joined:
    Jun 30, 2013
    Posts:
    45
    I just want to go on record that I am thankful to andeeeee, wherever he may be, for this PID controller code. I've been using and modifying the same code based on his PID code for over 5 years now. It's an essential part of my toolkit.
     
    dkstrong, forestrf and Deleted User like this.
  14. Umair3377

    Umair3377

    Joined:
    Jul 5, 2018
    Posts:
    1
    Can we use PID for suspension of car within RCC Controller
     
  15. pappalardodd

    pappalardodd

    Joined:
    Apr 13, 2020
    Posts:
    96
    Anyone still use this PID controller? ... how can you set it to rotate an object in a specific direction? ... (as it is used in this video)
     
  16. FlightOfOne

    FlightOfOne

    Joined:
    Aug 1, 2014
    Posts:
    668
    Hi guys, sorry to revive an old post but I've been trying to figure out how P I D values affect the outcome.

    I have a vehicle that I want to hold at a certain speed and I use torque to drive it. I want to control the applied torque depending on the desired speed.

    I have been playing with p i d values for a while and I sort of getting what I want but it's pure guessing.

    I would appreciate it if someone could explain how these values alter my outcome, in this scenario. Thanks!!

    upload_2021-8-20_14-48-49.png
     
    Last edited: Aug 20, 2021