forked from rusefi/rusefi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
test_pid.cpp
175 lines (131 loc) · 4.87 KB
/
test_pid.cpp
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* @file test_pid_auto.cpp
*
* @date Sep 29, 2019
* @author Andrey Belomutskiy, (c) 2012-2020
*/
// see also idle.timingPid test
#include "pch.h"
#include "efi_pid.h"
TEST(util, pid) {
pid_s pidS;
pidS.pFactor = 50;
pidS.iFactor = 0.5;
pidS.dFactor = 0;
pidS.offset = 0;
pidS.minValue = 10;
pidS.maxValue = 90;
pidS.periodMs = 1;
Pid pid(&pidS);
ASSERT_FLOAT_EQ( 90, pid.getOutput(14, 12, 0.1)) << "getValue#90";
ASSERT_FLOAT_EQ( 10, pid.getOutput(14, 16, 0.1)) << "getValue#10";
ASSERT_FLOAT_EQ(10, pid.getOutput(14, 16, 1));
pid.updateFactors(29, 0, 0);
ASSERT_FLOAT_EQ(10, pid.getOutput(14, 16, 1));
// ASSERT_FLOAT_EQ(68, pid.getIntegration());
ASSERT_FLOAT_EQ(10, pid.getOutput(14, 16, 1));
// ASSERT_FLOAT_EQ(0, pid.getIntegration());
ASSERT_FLOAT_EQ(10, pid.getOutput(14, 16, 1));
// ASSERT_FLOAT_EQ(68, pid.getIntegration());
pidS.pFactor = 1;
pidS.iFactor = 0;
pidS.dFactor = 0;
pidS.offset = 0;
pidS.minValue = 0;
pidS.maxValue = 100;
pidS.periodMs = 1;
pid.reset();
ASSERT_FLOAT_EQ( 50, pid.getOutput(/*target*/50, /*input*/0)) << "target=50, input=0";
ASSERT_FLOAT_EQ( 0, pid.iTerm) << "target=50, input=0 iTerm";
ASSERT_FLOAT_EQ( 0, pid.getOutput(/*target*/50, /*input*/70)) << "target=50, input=70";
ASSERT_FLOAT_EQ( 0, pid.iTerm) << "target=50, input=70 iTerm";
ASSERT_FLOAT_EQ( 0, pid.getOutput(/*target*/50, /*input*/70)) << "target=50, input=70 #2";
ASSERT_FLOAT_EQ( 0, pid.iTerm) << "target=50, input=70 iTerm #2";
ASSERT_FLOAT_EQ( 0, pid.getOutput(/*target*/50, /*input*/50)) << "target=50, input=50";
ASSERT_FLOAT_EQ( 0, pid.iTerm) << "target=50, input=50 iTerm";
}
static void commonPidTestParameters(pid_s * pidS) {
pidS->pFactor = 0;
pidS->iFactor = 50;
pidS->dFactor = 0;
pidS->offset = 0;
pidS->minValue = 10;
pidS->maxValue = 40;
pidS->periodMs = 1;
}
static void commonPidTest(Pid *pid) {
pid->iTermMax = 45;
ASSERT_FLOAT_EQ( 12.5, pid->getOutput(/*target*/50, /*input*/0)) << "target=50, input=0 #0";
ASSERT_FLOAT_EQ( 12.5, pid->getIntegration());
ASSERT_FLOAT_EQ( 25 , pid->getOutput(/*target*/50, /*input*/0)) << "target=50, input=0 #1";
ASSERT_FLOAT_EQ( 37.5, pid->getOutput(/*target*/50, /*input*/0)) << "target=50, input=0 #2";
ASSERT_FLOAT_EQ( 37.5, pid->getIntegration());
ASSERT_FLOAT_EQ( 40.0, pid->getOutput(/*target*/50, /*input*/0)) << "target=50, input=0 #3";
ASSERT_FLOAT_EQ( 45, pid->getIntegration());
}
TEST(util, parallelPidLimits) {
pid_s pidS;
commonPidTestParameters(&pidS);
Pid pid(&pidS);
commonPidTest(&pid);
}
TEST(util, industrialPidLimits) {
pid_s pidS;
commonPidTestParameters(&pidS);
PidIndustrial pid(&pidS);
commonPidTest(&pid);
}
TEST(util, pidIndustrial) {
pid_s pidS;
pidS.pFactor = 1.0;
pidS.iFactor = 1.0;
pidS.dFactor = 1.0;
pidS.offset = 0;
pidS.minValue = 0;
pidS.maxValue = 100;
pidS.periodMs = 1;
PidIndustrial pid;
pid.initPidClass(&pidS);
// we want to compare with the "normal" PID controller
Pid pid0(&pidS);
// no additional features
pid.derivativeFilterLoss = 0;
pid.antiwindupFreq = 0;
float industValue = pid.getOutput(/*target*/1, /*input*/0);
// check if the first output is clamped because of large deviative
ASSERT_FLOAT_EQ(100.0, industValue);
// check if all output of the 'zeroed' PidIndustrial (w/o new features) is the same as our "normal" Pid
for (int i = 0; i < 10; i++) {
float normalValue = pid0.getOutput(1, 0);
ASSERT_FLOAT_EQ(normalValue, industValue) << "[" << i << "]";
industValue = pid.getOutput(1, 0);
}
pid.reset();
// now test the "derivative filter loss" param (some small value)
pid.derivativeFilterLoss = 0.01;
// now the first value is less (and not clipped!) due to the derivative filtering
ASSERT_FLOAT_EQ(67.671669f, pid.getOutput(1, 0));
// here we still have some leftovers of the initial D-term
ASSERT_FLOAT_EQ(45.4544487f, pid.getOutput(1, 0));
// but the value is quickly fading
ASSERT_FLOAT_EQ(30.6446342f, pid.getOutput(1, 0));
pid.reset();
// now test much stronger "derivative filter loss"
pid.derivativeFilterLoss = 0.1;
// now the first value is much less due to the derivative filtering
ASSERT_NEAR(10.5288095f, pid.getOutput(1, 0), EPS4D);
// here we still have some leftovers of the initial D-term
ASSERT_NEAR(10.0802946f, pid.getOutput(1, 0), EPS4D);
// but the fading is slower than with 'weaker' derivative filter above
ASSERT_NEAR(9.65337563f, pid.getOutput(1, 0), EPS4D);
pid.reset();
pid.derivativeFilterLoss = 0;
// now test "anti-windup" param
pid.antiwindupFreq = 0.1;
// the first value is clipped, and that's when the anti-windup comes into effect
ASSERT_FLOAT_EQ(100.0f, pid.getOutput(1, 0));
// it stores a small negative offset in the I-term to avoid it's saturation!
ASSERT_NEAR(-0.0455025025f, pid.getIntegration(), EPS4D);
// and that's why the second output is smaller then that of normal PID (=1.00999999)
ASSERT_NEAR(0.959497511f, pid.getOutput(1, 0), EPS4D);
}