0
\$\begingroup\$

I have the below given implementation of the PID controller.

  float T;          // execution period in seconds
  float Kp;         // proportional gain
  float b;          // weighting factor of the proportional component
  float Ti;         // integration time constant in seconds
  float Ki;         // integration gain
  float Td;         // derivative time constant in seconds
  float Nd;         // derivative filter coefficient
  float ad;         // weighting coefficient of the derivative component in derivative increment calculation
  float bd;         // weighting coefficient of the controlled variable in derivative increment calculation
  float u;          // current value of the action
  float u_min;      // minimum value of the action
  float u_max;      // maximum value of the action
  float r1;         // previous value of the reference i.e. r(k-1)
  float y1;         // previous value of the controlled variable i.e. y(k-1)
  float y2;         // beforlast value fo the controlled variable i.e. y(k-2)
  float e1;         // previous value of the error i.e. e(k-1)
  float u1;         // previous value of the action i.e. u(k-1)
  float ud1;        // previous value of the derivative component i.e. ud(k-1)
  float ud2;        // beforelast value of the derivative component i.e. ud(k-2)

// e(k) = r(k) - y(k)
float e = r - y;

// dup(k)= kp*b*[r(k) - r(k-1)] - kp*[y(k) - y(k-1)]
float dup = Kp * b * (r - r1) - Kp * (y - y1);
// dui(k) = ki*[e(k) + e(k-1)]
float dui = Ki * (e + e1);
// dud(k) = ad*[ud(k-1) - ud(k-2)] - bd*[y(k) - 2*y(k-1) + y(k-2)]
float dud = ad * (ud1 - ud2) - bd * (y - 2.0f * y1 + y2);

// ud(k) = ud(k-1) + dud(k)
float ud = ud1 + dud;
// du(k) = dup(k) + dui(k)
float du = dup + dui + dud;
// u(k) = u(k-1) + du(k)
u = u1 + du;

// action limitation
if (u > u_max) {
  u = u_max;
} else if (u < u_min) {
  u = u_min;
}

// r(k-1) = r(k)
r1 = r;
// y(k-2) = y(k-1)
y2 = y1;
// y(k-1) = y(k)
y1 = y;
// e(k-1) = e(k)
e1 = e;
// u(k-1) = u(k)
u1 = u;
// ud(k-2) = ud(k-1)
ud2 = ud1;
// ud(k-1) = ud(k)
ud1 = ud;

The implementation is in the so called velocity form and includes the setpoint weighting, filtering of the derivative component and a mechanism avoiding the derivative kick. I need to extend the existing implementation with the dead-zone i.e. insensitivity at the control error. In other words I need to have some tolerance band around the zero control error \$\left<e_{min}, e_{max}\right>\$ where the action at the controller output isn't modified.

I have following idea how to integrate the dead-zone into the existing implementation

if (e < e_min || e > e_max) {
    // control error is outside the dead-zone
    // dup(k)= kp*b*[r(k) - r(k-1)] - kp*[y(k) - y(k-1)]
    dup = Kp * b * (r - r1) - Kp * (y - y1);
    // dui(k) = ki*[e(k) + e(k-1)]
    dui = Ki * (e + e1);
    // dud(k) = ad*[ud(k-1) - ud(k-2)] - bd*[y(k) - 2*y(k-1) + y(k-2)]
    dud = ad * (ud1 - ud2) - bd * (y - 2.0f * y1 + y2);
} else {
   // control error is inside the dead-zone 
   dup = 0;
   dui = 0;
   dud = 0;
}

Can you see any disadvantage in this implementation of the dead-zone?

\$\endgroup\$
2
  • \$\begingroup\$ What are you trying to accomplish? As to whether it might work in your application, try it. However, perhaps your goal could be accomplished in a different way, perhaps dual pid, or input filtering, or output limiting. \$\endgroup\$ May 9, 2023 at 11:55
  • 1
    \$\begingroup\$ For code like this, I have a personal rule: there should be more comments than code. Tell the next person what it is doing in nauseating detail. The next person should be able to read the comments and know what is going on before understanding what the code does. With this, I can't make heads or tails of it. \$\endgroup\$
    – Smith
    May 9, 2023 at 13:57

2 Answers 2

2
\$\begingroup\$

Generally, I prefer to have dead zone implemented outside of PID controller. Simply because DZ is a property of a particular application logic, while PID controller by itself is universal library code. It has only two tasks, which are a) get system to the setpoint and b) keep it there. In typical applications using position form you won't be able to achieve the latter if you implement dead zone the way you are suggesting.

To understand why, you should look at the physical meaning of the integral term, which is to introduce an offset to counteract persistent external disturbance or system offset. For example, a heat loss in the thermal system or a weight lifted by a motor. In such systems the control is non-zero, even when system is stationary.

The velocity form is more convenient here, because the integral term is already included in u1, so nullifying du will preserve the offset.

So yes, you can implement dead zone in the controller, if you wish. I would do it a bit different, though, by simply nullifying an error in the beginning and leaving the rest of calculations as they were. And that can be easily done from the outside of PID controller, by passing setpoint as current value for process variable when real value is within a dead zone.

\$\endgroup\$
1
\$\begingroup\$

I've done this before with h Bridges and thermal control on peltier devices. I ended up great limiting the output of the PID.

I don't have the code but I would calculate the velocity and if it was greater than a certain limit per second I would just cap it at that velocity.

If x[t-1]-x[t] > limit then
Out =limit
Else
Out = x[t]

And then of course you'd also have to do this for the negative rate case. Could actually calculate the velocity and divide it but there's no point, just adds extra cycles.

That way if for some reason the PID you wanted to go from positive to negative it couldn't do so fast enough to burn out the electronics. If it went from full on to full off it would burn out the h bridge.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.