File size: 4,477 Bytes
bd19734 7ca0449 bd19734 d6dc1ed bd19734 d6dc1ed bd19734 | 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 | import numpy as np
import pandas as pd
from .base import BaseController
class PIDController(BaseController):
"""PID-регулятор для управления отступами на основе загрузки специалистов"""
def __init__(self, name="PID",
kp_load=0.1, ki_load=0.01, kd_load=0.05,
load_weight=1.0, target_load=0.8,
# Начальные значения параметров
init_threshold=0.5,
init_lr_low=0.3, init_lr_high=0.4,
init_second_low=0.35, init_second_high=0.45):
super().__init__(name)
# Коэффициенты PID для загрузки
self.kp_load = kp_load
self.ki_load = ki_load
self.kd_load = kd_load
self.load_weight = load_weight
self.target_load = target_load
# Состояния PID
self.prev_error_load = 0
self.integral_load = 0
# Начальные параметры
self.init_threshold = init_threshold
self.init_lr_low = init_lr_low
self.init_lr_high = init_lr_high
self.init_second_low = init_second_low
self.init_second_high = init_second_high
# Текущие параметры (отступы)
self.threshold = init_threshold
self.lr_low = init_lr_low
self.lr_high = init_lr_high
self.second_low = init_second_low
self.second_high = init_second_high
# Границы отступов
self.bounds = {
'lr_low': (0.05, self.threshold - 0.05),
'lr_high': (0.05, 1 - self.threshold - 0.05),
'second_low': (0.05, self.threshold - 0.05),
'second_high': (0.05, 1 - self.threshold - 0.05)
}
# Ограничение интеграла
self.integral_limit = 1.0
def update(self, current_load):
""" current_load: текущая загрузка специалистов (0-1)"""
# Ошибка по загрузке
error_load = self.target_load - current_load
# PID для загрузки
P_load = self.kp_load * error_load
self.integral_load += error_load
self.integral_load = np.clip(self.integral_load, -self.integral_limit, self.integral_limit)
I_load = self.ki_load * self.integral_load
D_load = self.kd_load * (error_load - self.prev_error_load)
self.prev_error_load = error_load
# Выход регулятора
output_load = P_load + I_load + D_load
output = self.load_weight * output_load
# Адаптируем отступы
self._update_parameters(output)
# Сохраняем историю
self.history.append({
'time': len(self.history),
'error_load': error_load,
'output': output,
'threshold': self.threshold,
'lr_low': self.lr_low,
'lr_high': self.lr_high,
'second_low': self.second_low,
'second_high': self.second_high,
'load': current_load,
})
return self.get_margins()
def _update_parameters(self, output):
"""Обновляет отступы на основе выхода регулятора"""
delta = output * 0.1
self.lr_low = np.clip(
self.lr_low + delta,
self.bounds['lr_low'][0],
self.bounds['lr_low'][1]
)
self.lr_high = np.clip(
self.lr_high + delta,
self.bounds['lr_high'][0],
self.bounds['lr_high'][1]
)
self.second_low = np.clip(
self.second_low + delta,
self.bounds['second_low'][0],
self.bounds['second_low'][1]
)
self.second_high = np.clip(
self.second_high + delta,
self.bounds['second_high'][0],
self.bounds['second_high'][1]
)
def get_margins(self, hour=None):
"""Возвращает текущие отступы"""
return {
'lr_low': self.lr_low,
'lr_high': self.lr_high,
'second_low': self.second_low,
'second_high': self.second_high
}
def get_history(self):
"""Возвращает историю для визуализации"""
return pd.DataFrame(self.history) |