I've implemented a PID controller, but seeing as I'm not an expert in control theory, have I missed any edge cases?

public class PIDController
    public enum PIDMode

    public enum PIDAction

    public PIDMode Mode { get; set; }
    public PIDAction Action { get; set; }

    public double Proportional { get; set; }
    public double Integral { get; set; }
    public double Derivative { get; set; }

    public double Minimum { get; set; }
    public double Maximum { get; set; }

    public double DeltaMinimum { get; set; }
    public double DeltaMaximum { get; set; }

    private double _ProportionalTerm;
    private double _Integrator;
    private double _Derivator;

    public double Setpoint { get; set; }

    private double _Feedback;
    public double Feedback
            return _Feedback;

    public void Calculate(double Input, long Time)
        double output;

        // Compute the error value
        double Error = Setpoint - Input;

        if (Mode == PIDMode.Auto)
            if (Action == PIDAction.Direct)
                Error = 0 - Error;

            // Compute the proportional component
            _ProportionalTerm = 1000.0f * (Error - _Derivator) / (double)Time;

            // Compute the integrator component, clamped to min/max delta movement
            _Integrator += (float)Error * (float)Time / 1000.0f;
            if (_Integrator < DeltaMinimum)
                _Integrator = DeltaMinimum;
            if (_Integrator > DeltaMaximum)
                _Integrator = DeltaMaximum;

            // Add the proportional component
            output = (Proportional * Error);

            // Add the integral component
            output += Integral * _Integrator;

            // Add the derivative component
            output += Derivative * _ProportionalTerm;

            // Clamp output to min/max
            if (output < Minimum)
                output = Minimum;
            if (output > Maximum)
                output = Maximum;
            output = Input;

        // Store values
        _Derivator = Error;

        // Returns the result
        _Feedback = output;

It looks reasonable, but I don't see why you need to make the Proportional term a member of the class, it doesn't need to be saved, at best it might make sense to be able to query it with a get, but a set seems misleading.

I'd be reluctant to allow public setting of the Integral and Derivatives too, querying them might be useful, but letting users of the class set it seems odd.

Forcing the scaling by 1000.0 seems arbitrary and inflexible too, this would make sense to expose as a settable constant in my view.


Having a time parameter for each iteration of a PID loop would imply that it may safely be run at a variable frequency; while there are occasions when that may be unavoidable, and there are ways of handling such variations safely, I would expect most conditions that would cause the sampling loop time to vary would also create considerable 'noise' in the D term, limiting its usefulness.

One approach I've not seen discussed is replacing the PID loop with an IIR filter. A PID loop is a special case of an IIR filter, but with the second tap's time constant being infinitesimal, and the third tap's time constant being nearly infinite. The difference between the first two taps represents the delta, and the third tap represents the integral. Using more "realistic" time constants for the filter taps yields results which differ from a "pure" IIR filter, but in ways that are apt to be beneficial. Using a short-but-not-infinitesimal time constant on the second stage allows one to reduce the amount of high-frequency noise. Using a finite time constant on the third term allows one to effectively limit the "wind-up". Although going beyond three stages would make it harder to analyze system behavior, it may allow the PID system to better deal with various delay factors in the real-world system being controlled.

  • \$\begingroup\$ I'd be interested if you know of any more discussion on expressing a PID as an IIR. \$\endgroup\$
    – detly
    Jun 20 '13 at 5:29
  • \$\begingroup\$ @detly: I don't think I've seen such discussions since I wrote the above. If one figures out the frequency response of the integral and derivative terms, and those of the FIR filter described, the PID response matches the limiting case of the FIR as the second-stage time constant approaches zero and the third stage approaches infinity. Given that there's a limit to the frequencies of interest in the D term, and the amount of windup that's desired on the I term, I find it curious that the FIR model isn't used more. \$\endgroup\$
    – supercat
    Jun 20 '13 at 14:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.