Class: PIDController

Inherits:
StatefulController show all
Defined in:
lib/device_control.rb

Overview

A PIDController is a StatefulController that calculates

  • Proportion (current error)

  • Integral (accumulated error)

  • Derivative (error slope, last_error)

The sum of these terms is the output

Constant Summary collapse

ZN =

Ziegler-Nichols method for tuning PID gain knobs en.wikipedia.org/wiki/Ziegler%E2%80%93Nichols_method

{
  #           Kp     Ti    Td     Ki     Kd
  #     Var:  Ku     Tu    Tu    Ku/Tu  Ku*Tu
  'P'    => [1/2r],
  'PI'   => [9/20r, 4/5r,   nil, 27/50r],
  'PD'   => [ 4/5r,  nil,  1/8r,  nil, 1/10r],
  'PID'  => [ 3/5r, 1/2r,  1/8r, 6/5r, 3/40r],
  'PIR'  => [7/10r, 2/5r, 3/20r, 7/4r, 21/200r],
  # less overshoot than standard PID
  'some' => [ 1/3r, 1/2r,  1/3r, 2/3r, 1/11r],
  'none' => [ 1/5r, 1/2r,  1/3r, 2/5r, 2/30r],
}

Constants inherited from StatefulController

StatefulController::HZ, StatefulController::TICK

Instance Attribute Summary collapse

Attributes inherited from StatefulController

#dt, #error, #last_error, #sum_error

Attributes inherited from Controller

#measure, #setpoint

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from StatefulController

#input=

Methods inherited from Controller

#input=

Methods included from Updateable

#update

Constructor Details

#initialize(setpoint, dt: TICK) {|_self| ... } ⇒ PIDController

Returns a new instance of PIDController.

Yields:

  • (_self)

Yield Parameters:

  • _self (PIDController)

    the object that the method was called on



226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/device_control.rb', line 226

def initialize(setpoint, dt: TICK)
  super

  # gain / multipliers for PID; tunables
  @kp, @ki, @kd = 1.0, 1.0, 1.0

  # optional clamps for PID terms and output
  @p_range = (-Float::INFINITY..Float::INFINITY)
  @i_range = (-Float::INFINITY..Float::INFINITY)
  @d_range = (-Float::INFINITY..Float::INFINITY)
  @o_range = (-Float::INFINITY..Float::INFINITY)

  yield self if block_given?
end

Instance Attribute Details

#d_rangeObject

Returns the value of attribute d_range.



224
225
226
# File 'lib/device_control.rb', line 224

def d_range
  @d_range
end

#i_rangeObject

Returns the value of attribute i_range.



224
225
226
# File 'lib/device_control.rb', line 224

def i_range
  @i_range
end

#kdObject

Returns the value of attribute kd.



224
225
226
# File 'lib/device_control.rb', line 224

def kd
  @kd
end

#kiObject

Returns the value of attribute ki.



224
225
226
# File 'lib/device_control.rb', line 224

def ki
  @ki
end

#kpObject

Returns the value of attribute kp.



224
225
226
# File 'lib/device_control.rb', line 224

def kp
  @kp
end

#o_rangeObject

Returns the value of attribute o_range.



224
225
226
# File 'lib/device_control.rb', line 224

def o_range
  @o_range
end

#p_rangeObject

Returns the value of attribute p_range.



224
225
226
# File 'lib/device_control.rb', line 224

def p_range
  @p_range
end

Class Method Details

.tune(type, ku, tu) ⇒ Object

ku = ultimate gain, tu = oscillation period output includes ti and td, which are not necessary typically kp, ki, and kd are used



213
214
215
216
217
218
219
220
221
222
# File 'lib/device_control.rb', line 213

def self.tune(type, ku, tu)
  record = ZN[type.downcase] || ZN[type.upcase] || ZN.fetch(type)
  kp, ti, td, ki, kd = *record
  kp *= ku if kp
  ti *= tu if ti
  td *= tu if td
  ki *= (ku / tu) if ki
  kd *= (ku * tu) if kd
  { kp: kp, ti: ti, td: td, ki: ki, kd: kd }
end

Instance Method Details

#derivativeObject



255
256
257
# File 'lib/device_control.rb', line 255

def derivative
  (@kd * (@error - @last_error) / @dt).clamp(@d_range.begin, @d_range.end)
end

#integralObject



251
252
253
# File 'lib/device_control.rb', line 251

def integral
  (@ki * @sum_error).clamp(@i_range.begin, @i_range.end)
end

#outputObject



241
242
243
244
245
# File 'lib/device_control.rb', line 241

def output
  (self.proportion +
   self.integral +
   self.derivative).clamp(@o_range.begin, @o_range.end)
end

#proportionObject



247
248
249
# File 'lib/device_control.rb', line 247

def proportion
  (@kp * @error).clamp(@p_range.begin, @p_range.end)
end

#to_sObject



259
260
261
262
263
264
265
266
# File 'lib/device_control.rb', line 259

def to_s
  super +
    [format(" Gain:\t%.3f\t%.3f\t%.3f",
            @kp, @ki, @kd),
     format("  PID:\t%+.3f\t%+.3f\t%+.3f\t= %.5f",
            self.proportion, self.integral, self.derivative, self.output),
    ].join("\n")
end