Skip to content

Commit f5dcd92

Browse files
author
Daniel Kroening
authored
Merge pull request #244 from theyoucheng/case-study
Clarified the inputs for the PID case study.
2 parents 0e5070b + 455c36b commit f5dcd92

File tree

2 files changed

+132
-89
lines changed

2 files changed

+132
-89
lines changed

doc/html-manual/cover.shtml

Lines changed: 110 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,13 @@ is plotted in the Figure below.
5353

5454
</center>
5555

56-
<pre><code class="c numbered">01: // CONSTANTS:
57-
02: #define MAX_CLIMB_SUM_ERR 100
56+
<pre><code class="c numbered">01: // CONSTANTS:
57+
02: #define MAX_CLIMB_SUM_ERR 10
5858
03: #define MAX_CLIMB 1
5959
04:
6060
05: #define CLOCK 16
6161
06: #define MAX_PPRZ (CLOCK*600)
62-
07:
62+
07:
6363
08: #define CLIMB_LEVEL_GAZ 0.31
6464
09: #define CLIMB_GAZ_OF_CLIMB 0.75
6565
10: #define CLIMB_PITCH_OF_VZ_PGAIN 0.05
@@ -70,88 +70,99 @@ is plotted in the Figure below.
7070
15: const float climb_pgain=CLIMB_PGAIN;
7171
16: const float climb_igain=CLIMB_IGAIN;
7272
17: const float nav_pitch=0;
73-
18:
74-
19: // 1) the target speed in vertical direction
75-
20: float desired_climb;
76-
21: // 2) vertical speed of the UAV, estimated by a control function
77-
22: float estimator_z_dot;
78-
23:
79-
24: /** PID funciton OUTPUTS */
80-
25: float desired_gaz;
81-
26: float desired_pitch;
82-
27: // Accumulated error in the system
83-
28: float climb_sum_err;
84-
29:
85-
30: /** Computes desired_gaz and desired_pitch */
86-
31: void climb_pid_run()
87-
32: {
88-
33:
89-
34: float err=estimator_z_dot-desired_climb;
90-
35:
91-
36: float fgaz=climb_pgain*(err+climb_igain*climb_sum_err)+CLIMB_LEVEL_GAZ+CLIMB_GAZ_OF_CLIMB*desired_climb;
92-
37:
93-
38: float pprz=fgaz*MAX_PPRZ;
94-
39: desired_gaz=((pprz>=0 && pprz<=MAX_PPRZ) ? pprz : (pprz>MAX_PPRZ ? MAX_PPRZ : 0));
95-
40:
96-
41: /** pitch offset for climb */
97-
42: float pitch_of_vz=(desired_climb>0) ? desired_climb*pitch_of_vz_pgain : 0;
98-
43: desired_pitch=nav_pitch+pitch_of_vz;
99-
44:
100-
45: climb_sum_err=err+climb_sum_err;
101-
46: if (climb_sum_err>MAX_CLIMB_SUM_ERR) climb_sum_err=MAX_CLIMB_SUM_ERR;
102-
47: if (climb_sum_err<-MAX_CLIMB_SUM_ERR) climb_sum_err=-MAX_CLIMB_SUM_ERR;
103-
48:
104-
49: /** Estimates the vertical speed */
105-
50: estimator_z_dot=climb_pgain * err + desired_climb;
73+
18:
74+
19: /** PID function INPUTS */
75+
20: // The user input: target speed in vertical direction
76+
21: float desired_climb;
77+
22: // Vertical speed of the UAV detected by GPS sensor
78+
23: float estimator_z_dot;
79+
24:
80+
25: /** PID function OUTPUTS */
81+
26: float desired_gaz;
82+
27: float desired_pitch;
83+
28:
84+
29: /** The state variable: accumulated error in the control */
85+
30: float climb_sum_err=0;
86+
31:
87+
32: /** Computes desired_gaz and desired_pitch */
88+
33: void climb_pid_run()
89+
34: {
90+
35:
91+
36: float err=estimator_z_dot-desired_climb;
92+
37:
93+
38: float fgaz=climb_pgain*(err+climb_igain*climb_sum_err)+CLIMB_LEVEL_GAZ+CLIMB_GAZ_OF_CLIMB*desired_climb;
94+
39:
95+
40: float pprz=fgaz*MAX_PPRZ;
96+
41: desired_gaz=((pprz>=0 && pprz<=MAX_PPRZ) ? pprz : (pprz>MAX_PPRZ ? MAX_PPRZ : 0));
97+
42:
98+
43: /** pitch offset for climb */
99+
44: float pitch_of_vz=(desired_climb>0) ? desired_climb*pitch_of_vz_pgain : 0;
100+
45: desired_pitch=nav_pitch+pitch_of_vz;
101+
46:
102+
47: climb_sum_err=err+climb_sum_err;
103+
48: if (climb_sum_err>MAX_CLIMB_SUM_ERR) climb_sum_err=MAX_CLIMB_SUM_ERR;
104+
49: if (climb_sum_err<-MAX_CLIMB_SUM_ERR) climb_sum_err=-MAX_CLIMB_SUM_ERR;
105+
50:
106106
51: }
107107
52:
108108
53: int main()
109109
54: {
110-
55: /** Non-deterministic initialisation */
111-
56: desired_climb=nondet_float();
112-
57: estimator_z_dot=nondet_float();
113-
58:
114-
59: /** Range of initial values of variables */
115-
60: __CPROVER_assume(desired_climb>=-MAX_CLIMB && desired_climb<=MAX_CLIMB);
116-
61: __CPROVER_assume(estimator_z_dot>=-MAX_CLIMB && estimator_z_dot<=MAX_CLIMB);
117-
62:
118-
63: while(1)
119-
64: {
120-
65: climb_pid_run();
121-
66: }
122-
67:
123-
68: return 0;
124-
69: }
110+
55:
111+
56: while(1)
112+
57: {
113+
58: /** Non-deterministic input values */
114+
59: desired_climb=nondet_float();
115+
60: estimator_z_dot=nondet_float();
116+
61:
117+
62: /** Range of input values */
118+
63: __CPROVER_assume(desired_climb>=-MAX_CLIMB && desired_climb<=MAX_CLIMB);
119+
64: __CPROVER_assume(estimator_z_dot>=-MAX_CLIMB && estimator_z_dot<=MAX_CLIMB);
120+
65:
121+
66: __CPROVER_input("desired_climb", desired_climb);
122+
67: __CPROVER_input("estimator_z_dot", estimator_z_dot);
123+
68:
124+
69: climb_pid_run();
125+
70:
126+
71: __CPROVER_output("desired_gaz", desired_gaz);
127+
72: __CPROVER_output("desired_pitch", desired_pitch);
128+
73:
129+
74: }
130+
75:
131+
76: return 0;
132+
77: }
125133
</code></pre>
126134

127135
<p class="justified">
128136
In order to test the PID controller, we construct a main control loop,
129-
which repeatedly invokes the function <code>climb_pid_run</code> (line 65).
130-
In the beginning of the main function, the values of the desired speed <code>desired_climb</code>
131-
and the estimated speed <code>estimated_z_dot</code> are initialised non-deterministically.
132-
Subsequently, the <code>__CPROVER_assume</code> statement in lines 60 and 61 guarantees that
133-
their values are bounded within a range.
137+
which repeatedly invokes the function <code>climb_pid_run</code> (line 69).
138+
This PID function has two input variables: the desired speed <code>desired_climb</code>
139+
and the estimated speed <code>estimated_z_dot</code>.
140+
In the beginning of each loop iteration, values of the inputs are assigned non-deterministically.
141+
Subsequently, the <code>__CPROVER_assume</code> statement in lines 63 and 64 guarantees that
142+
both values are bounded within a valid range.
143+
The <code>__CPROVER_input</code> and <code>__CPROVER_output</code> will help clarify the inputs
144+
and outputs of interest for generating test suites.
134145
</p>
135146

136147
<p class="justified">
137148
To demonstrate the automatic test suite generation in CBMC,
138-
we call the following command
149+
we call the following command and we are going to explain the command line options one by one.
139150
</p>
140151

141-
<pre><code>cbmc pid.c --cover mcdc --unwind 25 --trace --xml-ui
152+
<pre><code>cbmc pid.c --cover mcdc --unwind 6 --trace --xml-ui
142153
</code></pre>
143154

144155
<p class="justified">
145156
The option <code>--cover mcdc</code> specifies the code coverage criterion.
146-
There are four conditional statements in the PID function: in line 39, line 42,
147-
line 46 and line 47.
157+
There are four conditional statements in the PID function: in line 41, line 44,
158+
line 48 and line 49.
148159
To satisfy MC/DC, the test suite has to meet multiple requirements.
149160
For instance, each conditional statement needs to evaluate to <em>true</em> and <em>false</em>.
150-
Consider the condition <code>"pprz>=0 && pprz<=MAX_PPRZ"</code> at line 39. CBMC
161+
Consider the condition <code>"pprz>=0 && pprz<=MAX_PPRZ"</code> in line 41. CBMC
151162
instruments three coverage goals to control the respective evaluated results of <code>"pprz>=0"</code> and
152163
<code>"pprz<=MAX_PPRZ"</code>.
153164
We list them in below and they satisfy the MC/DC rules.
154-
Note that <code>MAX_PPRZ</code> is defined as 16 * 600 at line 06 of the program.
165+
Note that <code>MAX_PPRZ</code> is defined as 16 * 600 in line 06 of the program.
155166
</p>
156167

157168
<pre>
@@ -170,21 +181,48 @@ goals at the same time. Each trace corresponds to a test case.
170181
</p>
171182

172183
<p class="justified">
173-
For the example above, the following test suites are generated.
184+
In the end, the following test suites are automatically generated for testing the PID controller.
185+
A test suite consists of a sequence of input parameters that are
186+
passed to the PID function <code>climb_pid_run</code> at each loop iteration.
187+
For example, Test 1 covers the MC/DC goal with id="climb_pid_run.coverage.1".
174188
<pre><code>Test suite:
175-
T1: desired_climb=-1.000000f, estimator_z_dot=1.000000f
176-
(goal id: climb_pid_run.coverage.1)
177-
T2: desired_climb=1.000000f, estimator_z_dot=-1.000000f
178-
(goal id: climb_pid_run.coverage.2)
179-
T3: desired_climb=0.000000f, estimator_z_dot=0.000000f
180-
(goal id: climb_pid_run.coverage.3)
189+
Test 1.
190+
(iteration 1) desired_climb=-1.000000f, estimator_z_dot=1.000000f
191+
192+
Test 2.
193+
(iteration 1) desired_climb=-1.000000f, estimator_z_dot=1.000000f
194+
(iteration 2) desired_climb=1.000000f, estimator_z_dot=-1.000000f
195+
196+
Test 3.
197+
(iteration 1) desired_climb=0.000000f, estimator_z_dot=-1.000000f
198+
(iteration 2) desired_climb=1.000000f, estimator_z_dot=-1.000000f
199+
200+
Test 4.
201+
(iteration 1) desired_climb=1.000000f, estimator_z_dot=-1.000000f
202+
(iteration 2) desired_climb=1.000000f, estimator_z_dot=-1.000000f
203+
(iteration 3) desired_climb=1.000000f, estimator_z_dot=-1.000000f
204+
(iteration 4) desired_climb=1.000000f, estimator_z_dot=-1.000000f
205+
(iteration 5) desired_climb=0.000000f, estimator_z_dot=-1.000000f
206+
(iteration 6) desired_climb=1.000000f, estimator_z_dot=-1.000000f
207+
208+
Test 5.
209+
(iteration 1) desired_climb=-1.000000f, estimator_z_dot=1.000000f
210+
(iteration 2) desired_climb=-1.000000f, estimator_z_dot=1.000000f
211+
(iteration 3) desired_climb=-1.000000f, estimator_z_dot=1.000000f
212+
(iteration 4) desired_climb=-1.000000f, estimator_z_dot=1.000000f
213+
(iteration 5) desired_climb=-1.000000f, estimator_z_dot=1.000000f
214+
(iteration 6) desired_climb=-1.000000f, estimator_z_dot=1.000000f
181215
</code></pre>
182216
</p>
183217

184218
<p class="justified">
185-
The option <code>--unwind 25</code> unwinds the loop inside the main
186-
function body 25 times. The number of the unwinding operation is
187-
adjustable, and an introduction to the use of loop unwinding can be found
219+
The option <code>--unwind 6</code> unwinds the loop inside the main
220+
function body six times. In order to achieve the complete coverage on all the instrumented goals
221+
in the PID function <code>climb_pid_run</code>, the loop must be unwound sufficient enough times.
222+
For example, <code>climb_pid_run</code> needs to be called at least six times for evaluating the
223+
condition <code>climb_sum_err>MAX_CLIMB_SUM_ERR</code> in line 48 to <em>true</em>.
224+
This corresponds to the Test 5.
225+
An introduction to the use of loop unwinding can be found
188226
in <a href="cbmc-loops.shtml">Understanding Loop Unwinding</a>.
189227
</p>
190228

doc/html-manual/pid.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// CONSTANTS:
2-
#define MAX_CLIMB_SUM_ERR 100
2+
#define MAX_CLIMB_SUM_ERR 10
33
#define MAX_CLIMB 1
44

55
#define CLOCK 16
@@ -16,17 +16,18 @@ const float climb_pgain=CLIMB_PGAIN;
1616
const float climb_igain=CLIMB_IGAIN;
1717
const float nav_pitch=0;
1818

19-
// 1) the target speed in vertical direction
19+
/** PID function INPUTS */
20+
// The user input: target speed in vertical direction
2021
float desired_climb;
21-
// 2) vertical speed of the UAV detected by GPS sensor
22+
// Vertical speed of the UAV detected by GPS sensor
2223
float estimator_z_dot;
2324

24-
/** PID funciton OUTPUTS */
25+
/** PID function OUTPUTS */
2526
float desired_gaz;
2627
float desired_pitch;
27-
// Accumulated error in the system
28-
float climb_sum_err;
2928

29+
/** The state variable: accumulated error in the control */
30+
float climb_sum_err=0;
3031

3132
/** Computes desired_gaz and desired_pitch */
3233
void climb_pid_run()
@@ -47,25 +48,29 @@ void climb_pid_run()
4748
if (climb_sum_err>MAX_CLIMB_SUM_ERR) climb_sum_err=MAX_CLIMB_SUM_ERR;
4849
if (climb_sum_err<-MAX_CLIMB_SUM_ERR) climb_sum_err=-MAX_CLIMB_SUM_ERR;
4950

50-
/** The control function for estimating the vertical speed */
51-
estimator_z_dot=climb_pgain * err + desired_climb;
52-
5351
}
5452

55-
5653
int main()
5754
{
58-
/** Non-deterministic initialisation */
59-
desired_climb=nondet_float();
60-
estimator_z_dot=nondet_float();
61-
62-
/** Range of initial values of variables */
63-
__CPROVER_assume(desired_climb>=-MAX_CLIMB && desired_climb<=MAX_CLIMB);
64-
__CPROVER_assume(estimator_z_dot>=-MAX_CLIMB && estimator_z_dot<=MAX_CLIMB);
6555

6656
while(1)
6757
{
58+
/** Non-deterministic input values */
59+
desired_climb=nondet_float();
60+
estimator_z_dot=nondet_float();
61+
62+
/** Range of input values */
63+
__CPROVER_assume(desired_climb>=-MAX_CLIMB && desired_climb<=MAX_CLIMB);
64+
__CPROVER_assume(estimator_z_dot>=-MAX_CLIMB && estimator_z_dot<=MAX_CLIMB);
65+
66+
__CPROVER_input("desired_climb", desired_climb);
67+
__CPROVER_input("estimator_z_dot", estimator_z_dot);
68+
6869
climb_pid_run();
70+
71+
__CPROVER_output("desired_gaz", desired_gaz);
72+
__CPROVER_output("desired_pitch", desired_pitch);
73+
6974
}
7075

7176
return 0;

0 commit comments

Comments
 (0)