Files
flow_control/controllers (2).py
T
2026-08-12 17:40:45 +08:00

146 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# controllers.py
import os, sys, time
def _pid_log(msg: str):
"""PID 内部日志,直接写文件 + 刷盘"""
try:
log_dir = os.path.join(os.path.dirname(sys.executable), "logs")
os.makedirs(log_dir, exist_ok=True)
log_file = os.path.join(log_dir, "control_debug.log")
with open(log_file, "a", encoding="utf-8") as f:
f.write(f"[{time.strftime('%H:%M:%S.%f')[:-3]}] [PID] {msg}\n")
f.flush()
os.fsync(f.fileno())
except Exception:
pass
class IncrementalPID:
"""增量式PID控制器"""
def __init__(self, kp: float, ki: float, kd: float, dt: float,
out_min: float, out_max: float, xa_full: float = 1062.5):
# PID参数
self.kp = kp
self.ki = ki
self.kd = kd
self.dt = dt # 默认50HZ执行周期防止除零
self.motor_max = 300.0
self.du_max = self.motor_max * self.dt
self.dead_area = 240.0
self.xa_full = xa_full # 总限幅(最大行程)
# self.kp = 1.392
# self.ki = 30.2
# self.kd = 0.000485
# self.dt = 0.0059
# 限幅设置
self.out_min = out_min
self.out_max = out_max
# 输入输出
self.target_pressure = 0.0 # 参考值(设定压力大小)
self.current_pressure = 0.0 # 反馈值
self.error = 0.0 # 当前误差
# 计算系数
self.a0 = 0.0
self.a1 = 0.0
self.a2 = 0.0
self._calculate_coefficients()
# 控制器状态
self.prev_error = 0.0 # 前次误差 e(k-1)
self.prev_error2 = 0.0 # 前前次误差 e(k-2)
self.output = 0.0 # 控制器总输出
def _calculate_coefficients(self):
"""重新计算增量式PID系数"""
if self.dt <= 0:
return
self.a0 = self.kp + (self.ki * self.dt / 2.0) + (2.0 * self.kd / self.dt)
self.a1 = -self.kp + (self.ki * self.dt / 2.0) - (4.0 * self.kd / self.dt)
self.a2 = (2.0 * self.kd) / self.dt
def update_pressure_values(self, current_pressure, target_pressure):
"""更新当前压力和目标压力值"""
self.current_pressure = current_pressure
self.target_pressure = target_pressure
def update(self, du_max=None):
# 计算当前误差
self.error = -(self.target_pressure - self.current_pressure)
# if abs(self.error) < 1:
# return self.output # 误差过小,直接返回当前输出
# 计算控制增量
delta = (self.a0 * self.error + self.a1 * self.prev_error + self.a2 * self.prev_error2)
if du_max is not None:
self.du_max = du_max
else:
self.du_max = self.get_du_max(self.target_pressure)
# 纯 Python 限幅(替代 np.clip
if delta > self.du_max:
delta = self.du_max
elif delta < -self.du_max:
delta = -self.du_max
# 计算新输出
new_output = self.output + delta
# print(f"output:{self.output}, delta:{delta}, new_output:{new_output}")
# 应用输出限幅
new_output = max(self.out_min, min(self.out_max, new_output))
# 更新历史状态
self.prev_error2 = self.prev_error
self.prev_error = self.error
self.output = new_output
return new_output
def reset(self):
"""重置PID控制器状态(保留参数)"""
self.prev_error = 0.0
self.prev_error2 = 0.0
self.output = 0.0
def update_parameters(self, kp: float, ki: float, kd: float):
self.kp = kp
self.ki = ki
self.kd = kd
self._calculate_coefficients()
def set_du_max(self, value):
"""设置 du_max(供外部模块通过方法调用设置,避免跨 .pyd 属性写入 crash"""
self.du_max = value
def get_du_max(self, target_pressure):
"""根据目标压力计算 PID 最大增量限幅(纯 Python 线性插值)"""
x = float(target_pressure)
dt = float(self.dt)
if x <= 0.0:
val = 500.0 * dt
elif x >= 200.0:
val = 250.0 * dt
elif x <= 100.0:
# 0~100:从 500 线性下降到 300
val = (500.0 - 2.0 * x) * dt
else:
# 100~200:从 300 线性下降到 250
val = (350.0 - 0.5 * x) * dt
return val
def init_v(self, position_x):
v = (self.xa_full - position_x) / (self.xa_full - self.dead_area) * 100
return v