-
Notifications
You must be signed in to change notification settings - Fork 48
/
PID.c
155 lines (132 loc) · 3.75 KB
/
PID.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* Floating point PID control loop for Microcontrollers
Copyright (C) 2015 Jesus Ruben Santa Anna Zamudio.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Author website: http://www.geekfactory.mx
Author e-mail: ruben at geekfactory dot mx
*/
#include "PID.h"
pid_t pid_create(pid_t pid, float* in, float* out, float* set, float kp, float ki, float kd)
{
pid->input = in;
pid->output = out;
pid->setpoint = set;
pid->automode = false;
pid_limits(pid, 0, 255);
// Set default sample time to 100 ms
pid->sampletime = 100 * (TICK_SECOND / 1000);
pid_direction(pid, E_PID_DIRECT);
pid_tune(pid, kp, ki, kd);
pid->lasttime = tick_get() - pid->sampletime;
return pid;
}
bool pid_need_compute(pid_t pid)
{
// Check if the PID period has elapsed
return(tick_get() - pid->lasttime >= pid->sampletime) ? true : false;
}
void pid_compute(pid_t pid)
{
// Check if control is enabled
if (!pid->automode)
return false;
float in = *(pid->input);
// Compute error
float error = (*(pid->setpoint)) - in;
// Compute integral
pid->iterm += (pid->Ki * error);
if (pid->iterm > pid->omax)
pid->iterm = pid->omax;
else if (pid->iterm < pid->omin)
pid->iterm = pid->omin;
// Compute differential on input
float dinput = in - pid->lastin;
// Compute PID output
float out = pid->Kp * error + pid->iterm - pid->Kd * dinput;
// Apply limit to output value
if (out > pid->omax)
out = pid->omax;
else if (out < pid->omin)
out = pid->omin;
// Output to pointed variable
(*pid->output) = out;
// Keep track of some variables for next execution
pid->lastin = in;
pid->lasttime = tick_get();;
}
void pid_tune(pid_t pid, float kp, float ki, float kd)
{
// Check for validity
if (kp < 0 || ki < 0 || kd < 0)
return;
//Compute sample time in seconds
float ssec = ((float) pid->sampletime) / ((float) TICK_SECOND);
pid->Kp = kp;
pid->Ki = ki * ssec;
pid->Kd = kd / ssec;
if (pid->direction == E_PID_REVERSE) {
pid->Kp = 0 - pid->Kp;
pid->Ki = 0 - pid->Ki;
pid->Kd = 0 - pid->Kd;
}
}
void pid_sample(pid_t pid, uint32_t time)
{
if (time > 0) {
float ratio = (float) (time * (TICK_SECOND / 1000)) / (float) pid->sampletime;
pid->Ki *= ratio;
pid->Kd /= ratio;
pid->sampletime = time * (TICK_SECOND / 1000);
}
}
void pid_limits(pid_t pid, float min, float max)
{
if (min >= max) return;
pid->omin = min;
pid->omax = max;
//Adjust output to new limits
if (pid->automode) {
if (*(pid->output) > pid->omax)
*(pid->output) = pid->omax;
else if (*(pid->output) < pid->omin)
*(pid->output) = pid->omin;
if (pid->iterm > pid->omax)
pid->iterm = pid->omax;
else if (pid->iterm < pid->omin)
pid->iterm = pid->omin;
}
}
void pid_auto(pid_t pid)
{
// If going from manual to auto
if (!pid->automode) {
pid->iterm = *(pid->output);
pid->lastin = *(pid->input);
if (pid->iterm > pid->omax)
pid->iterm = pid->omax;
else if (pid->iterm < pid->omin)
pid->iterm = pid->omin;
pid->automode = true;
}
}
void pid_manual(pid_t pid)
{
pid->automode = false;
}
void pid_direction(pid_t pid, enum pid_control_directions dir)
{
if (pid->automode && pid->direction != dir) {
pid->Kp = (0 - pid->Kp);
pid->Ki = (0 - pid->Ki);
pid->Kd = (0 - pid->Kd);
}
pid->direction = dir;
}