|
|
|
@ -3,24 +3,19 @@ from numbers import Number |
|
|
|
|
|
|
|
|
|
from common.numpy_fast import clip, interp |
|
|
|
|
|
|
|
|
|
def apply_deadzone(error, deadzone): |
|
|
|
|
if error > deadzone: |
|
|
|
|
error -= deadzone |
|
|
|
|
elif error < - deadzone: |
|
|
|
|
error += deadzone |
|
|
|
|
else: |
|
|
|
|
error = 0. |
|
|
|
|
return error |
|
|
|
|
|
|
|
|
|
class PIController(): |
|
|
|
|
def __init__(self, k_p, k_i, k_f=0., pos_limit=None, neg_limit=None, rate=100): |
|
|
|
|
|
|
|
|
|
class PIDController(): |
|
|
|
|
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=None, neg_limit=None, rate=100): |
|
|
|
|
self._k_p = k_p # proportional gain |
|
|
|
|
self._k_i = k_i # integral gain |
|
|
|
|
self._k_d = k_d # feedforward gain |
|
|
|
|
self.k_f = k_f # feedforward gain |
|
|
|
|
if isinstance(self._k_p, Number): |
|
|
|
|
self._k_p = [[0], [self._k_p]] |
|
|
|
|
if isinstance(self._k_i, Number): |
|
|
|
|
self._k_i = [[0], [self._k_i]] |
|
|
|
|
if isinstance(self._k_d, Number): |
|
|
|
|
self._k_d = [[0], [self._k_d]] |
|
|
|
|
|
|
|
|
|
self.pos_limit = pos_limit |
|
|
|
|
self.neg_limit = neg_limit |
|
|
|
@ -38,24 +33,29 @@ class PIController(): |
|
|
|
|
def k_i(self): |
|
|
|
|
return interp(self.speed, self._k_i[0], self._k_i[1]) |
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
def k_d(self): |
|
|
|
|
return interp(self.speed, self._k_d[0], self._k_d[1]) |
|
|
|
|
|
|
|
|
|
def reset(self): |
|
|
|
|
self.p = 0.0 |
|
|
|
|
self.i = 0.0 |
|
|
|
|
self.d = 0.0 |
|
|
|
|
self.f = 0.0 |
|
|
|
|
self.control = 0 |
|
|
|
|
|
|
|
|
|
def update(self, setpoint, measurement, speed=0.0, override=False, feedforward=0., deadzone=0., freeze_integrator=False): |
|
|
|
|
def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0., freeze_integrator=False): |
|
|
|
|
self.speed = speed |
|
|
|
|
|
|
|
|
|
error = float(apply_deadzone(setpoint - measurement, deadzone)) |
|
|
|
|
self.p = error * self.k_p |
|
|
|
|
self.p = float(error) * self.k_p |
|
|
|
|
self.f = feedforward * self.k_f |
|
|
|
|
self.d = error_rate * self.k_d |
|
|
|
|
|
|
|
|
|
if override: |
|
|
|
|
self.i -= self.i_unwind_rate * float(np.sign(self.i)) |
|
|
|
|
else: |
|
|
|
|
i = self.i + error * self.k_i * self.i_rate |
|
|
|
|
control = self.p + self.f + i |
|
|
|
|
control = self.p + i + self.d + self.f |
|
|
|
|
|
|
|
|
|
# Update when changing i will move the control away from the limits |
|
|
|
|
# or when i will move towards the sign of the error |
|
|
|
@ -64,7 +64,7 @@ class PIController(): |
|
|
|
|
not freeze_integrator: |
|
|
|
|
self.i = i |
|
|
|
|
|
|
|
|
|
control = self.p + self.f + self.i |
|
|
|
|
control = self.p + self.i + self.d + self.f |
|
|
|
|
|
|
|
|
|
self.control = clip(control, self.neg_limit, self.pos_limit) |
|
|
|
|
return self.control |
|
|
|
|