pid_controller 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -0
- data/.travis.yml +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -0
- data/lib/pid_controller.rb +31 -3
- data/lib/pid_controller/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64075129161ab9782dc7fabaf471d096e75bf877
|
4
|
+
data.tar.gz: db661feaafaa8af5d4ff1b4be7c76364e22064ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ee1c8f010b1e235aa883b1e33f6b4fce083c991f0e87614f12a6bf99892814cf8abd57e79165e87ca46c1d7093dc450fa94958aafc441cc894deb5b9212d8f5
|
7
|
+
data.tar.gz: b4919aca65fd37811c77ccf6e7900f4fcd63d9241fd97885ae669480c5e584371761a6faf66d4c6c13458c1f62b9f71f350dde418a10939b06352da27ab73431
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# PidController
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/pid_controller.svg)](https://badge.fury.io/rb/pid_controller)
|
3
|
+
[![Build Status](https://travis-ci.org/gabetax/pid_controller.svg?branch=master)](https://travis-ci.org/gabetax/pid_controller)
|
2
4
|
|
3
5
|
This is a Ruby implementation of a [PID Controller](https://en.wikipedia.org/wiki/PID_controller). A PID controller is a feedback system that is configured with a target setpoint, can read measurements of the system to see how close we are to the setpoint, and will omit an output. Every day examples include:
|
4
6
|
|
data/lib/pid_controller.rb
CHANGED
@@ -4,17 +4,44 @@ require 'pid_controller/version'
|
|
4
4
|
class PidController
|
5
5
|
attr_accessor :setpoint, :kp, :ki, :kd
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(
|
8
|
+
setpoint:,
|
9
|
+
kp: 1.0,
|
10
|
+
ki: 1.0,
|
11
|
+
kd: 1.0,
|
12
|
+
integral_min: nil,
|
13
|
+
integral_max: nil,
|
14
|
+
output_min: nil,
|
15
|
+
output_max: nil
|
16
|
+
)
|
8
17
|
@setpoint = setpoint
|
9
18
|
@kp = kp
|
10
19
|
@ki = ki
|
11
20
|
@kd = kd
|
21
|
+
# Prevents https://en.wikipedia.org/wiki/Integral_windup via bounds
|
22
|
+
@integral_min = integral_min || -Float::INFINITY
|
23
|
+
@integral_max = integral_max || Float::INFINITY
|
24
|
+
@output_min = output_min || -Float::INFINITY
|
25
|
+
@output_max = output_max || Float::INFINITY
|
12
26
|
@integral = 0.0
|
13
27
|
@derivative = 0.0
|
14
28
|
@last_error = nil
|
15
29
|
@last_update = nil
|
16
30
|
end
|
17
31
|
|
32
|
+
unless defined?(0.clamp)
|
33
|
+
# Comparable#clamp is introduced in Ruby 2.4
|
34
|
+
module NumericClamp
|
35
|
+
# I'd refine Comparable itself, but older Rubies can't refine modules
|
36
|
+
refine Numeric do
|
37
|
+
def clamp(min, max)
|
38
|
+
[min, [max, self].min].max
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
using NumericClamp
|
43
|
+
end
|
44
|
+
|
18
45
|
def update(measurement)
|
19
46
|
now = clock_time
|
20
47
|
dt = if @last_update
|
@@ -32,7 +59,7 @@ class PidController
|
|
32
59
|
error = setpoint - measurement.to_f
|
33
60
|
|
34
61
|
if dt > 0.0
|
35
|
-
@integral
|
62
|
+
@integral = (@integral + error * dt).clamp(@integral_min, @integral_max)
|
36
63
|
@derivative = (error - @last_error) / dt
|
37
64
|
end
|
38
65
|
|
@@ -42,7 +69,7 @@ class PidController
|
|
42
69
|
end
|
43
70
|
|
44
71
|
def output
|
45
|
-
p_term + i_term + d_term
|
72
|
+
(p_term + i_term + d_term).clamp(@output_min, @output_max)
|
46
73
|
end
|
47
74
|
|
48
75
|
def p_term
|
@@ -57,6 +84,7 @@ class PidController
|
|
57
84
|
kd * @derivative
|
58
85
|
end
|
59
86
|
|
87
|
+
# Read the monotonic clock. It avoid horrors of leap seconds and NTP.
|
60
88
|
def clock_time
|
61
89
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
62
90
|
end
|