PID Controller in Embedded System

PID Introduction

The model of PID controller

Where u is the output of PID controller and e is the error of reference and feedback. Kp, Ki and Kd are the parameters of controller, which represents propotion, integral and derivative.

Discretization of PID controller

There are many methods to discretize a controller. We deploy the backward discretization method to the PID controller as following equation:

where Ui represent the integral or sum of the error of past. And the controller after discretization as equation (4) is called “Position type PID controller”. As the equation show, the output of controller is relevant with all of the past errors, so it’s not convenient to implement this controller in embedded software.

From integral to increment

To simplifiy the controller’s implementation, we calculate the difference of output of kth step and (k-1)th step as following equation:

According to above equation, we get:

Where:

As equation (14) shows, we got a controller model and the output is only relevant to the output of last step and the error of current step and last 2 steps. Wc can call it “increment type pid controller” We don’t need to calculate the sum of error of all past steps, so it’s simple and costless to impement this controller in our embedded software.

PID controller implementation

Interface

We can define a PID controller module in C:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

typedef struct PID_REGULATOR
{
float ref; /*!< reference value */
float fed; /*!< feedback value */
float err; /*!< error of reference and feedback value */
float err_2; /*!< error of reference and feedback of last time*/
float out; /*!< output of pi regulator */

float alpha; /*!< alpha parameter */
float beta; /*!< beta parameter */
float gamma; /*!< gama parameter */

float step_max; /*!< step increment upper limit>*/
float step_min; /*!< step increment lower limit>*/
float out_max; /*!< output upper limit */
float out_min; /*!< output lower limit */
} pid_regu_type;

And we need to implement following operation interface of PID controller:

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
/**
* func: create a pid controller
* ret: a pid controller pointer
* param: none
*/
pid_regu_type* create_pid_regu(void);

/**
* func: init a pid controller
* ret: none
* param:
* pid: PID controller pointer to be initialized
* others: thoes parameter of controller
*/
void init_pid_regu(pid_regu_type *pid, float alpha, float beta, float gamma, float step_max,
float step_min, float out_max, float out_min);

/**
* func: set control parameters of pid controller
* ret: none
* param:
* pid: PID controller pointer to be set
* alpha, beta, gamma: control parameters of controller as PID model defined
*/
void set_pid_para(pid_regu_type *pid, float alpha, float beta, float gamma);

/**
* func: set limit parameters of pid controller
* ret: none
* param:
* pid: PID controller pointer to be set
* step_max, step_min: the upper and lower limit of increment of controller output
* out_max, out_min: the upper and lower limit of controller output
*/
void set_pid_limits(pid_regu_type *pid, float step_max, float step_min, float out_max, float out_min);

/**
* func: calculate the output of pid controller
* ret: output of controller of current step
* param:
* pid: PID controller pointer
* ref: reference value(target) of controller
*. fed: feedback value
*/
float calc_pid_output(pid_regut_type *pid, float ref, float fed);

We can encapsulate the definition and interface in a header file which may be named “pid_regu.h”. And then we can implement those interface in a source file “pid_regu.c”. Finnally we got a pid contoller module for common use.

May me we can use the PID controller like following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "pid_regu.h"

// we define a pid controller handler
pid_regu_type *speed_ctrl;

// We create and init a pid controller for special use
void ctrl_init()
{
// ...
speed_ctrl = create_pid_regu();
init_pid_regu(speed_ctrl, 1.1, 1, 0, 1, -1, 100, -100);
// ...
}

// for most, we update the control output in control period interrupt
void ctrl_int_callback()
{
//...
calc_pid_output(speed_ctrl, speed_target, current_speed);
//...
}

Go further

We derive the increment type PID controller and implement it in C language. However, many real controllers are based on PID control, but more complicated than simple PID control. There may be more limitations or requirements in different applications. So we can inherit PID controller module to other controllers where the PID controller is the base class of a real controller. Although inheritance can be implemented in C, it’s more simple and natural to use it in C++. So we can implement the PID controller as a base class of real controllers in c++.