pid_tuning / src /helpers /optimization.py
AntonAndreenko's picture
Upload optimization.py
145f794 verified
Raw History Blame Contribute Delete
3.11 kB
import numpy as np
import pandas as pd
from scipy.optimize import minimize
from scipy.interpolate import interp1d
from scipy.optimize import differential_evolution
def objective_function(params, t, pv_hist, cv_hist):
# Unpack parameters
Kp, tau, theta = params
# Basic constraints to keep parameters realistic
if Kp <= 0 or tau <= 0 or theta < 0:
return 1e9 # Return a large number for invalid parameters
# Simulate the model response
pv_sim = simulate_fopdt(params, t, cv_hist)
# Calculate Sum of Squared Errors (SSE)
error = np.sum((pv_hist - pv_sim) ** 2)
return error
def simulate_fopdt(params, t, cv):
Kp, tau, theta = params
dt = t[1] - t[0]
pv_sim = np.zeros_like(t)
# Calculate the number of discrete steps for the dead time
delay_steps = int(round(theta / dt))
# Create a delayed version of the CV signal
cv_delayed = np.zeros_like(cv)
if delay_steps > 0 and delay_steps < len(cv):
cv_delayed[delay_steps:] = cv[:-delay_steps]
else:
cv_delayed = cv
for i in range(1, len(t)):
# First-order dynamics using the pre-calculated delayed CV
d_pv = (Kp * cv_delayed[i] - pv_sim[i - 1]) / tau
pv_sim[i] = pv_sim[i - 1] + d_pv * dt
return pv_sim
def identify_process_model(t, pv, cv):
bounds = [(0.1, 5.0), (1, 20.0), (0.5, 5.0)] # Adjusted bounds for robustness
# Run the optimization using differential_evolution.
result = differential_evolution(
objective_function,
bounds,
args=(t, pv, cv),
popsize=30,
maxiter=1000,
tol=0.01,
recombination=0.7,
strategy="best1bin",
)
if result.success:
identified_params = result.x
return identified_params
else:
print("Error: System identification failed.")
print(result.message)
return None
def calculate_tuning_params(Kp, tau, theta):
if Kp is None or tau is None or theta is None or theta == 0:
print("Cannot calculate tuning rules with invalid model parameters.")
return {}
# --- Ziegler-Nichols ---
zn_kc = (1.2 * tau) / (Kp * theta)
zn_ti = 2.0 * theta
zn_td = 0.5 * theta
zn_kp = zn_kc
zn_ki = zn_kc / zn_ti
zn_kd = zn_kc * zn_td
# --- Cohen-Coon ---
cc_kc = (tau / (Kp * theta)) * (4 / 3 + theta / (4 * tau))
cc_ti = theta * (32 + 6 * theta / tau) / (13 + 8 * theta / tau)
cc_td = theta * 4 / (11 + 2 * theta / tau)
cc_kp = cc_kc
cc_ki = cc_kc / cc_ti
cc_kd = cc_kc * cc_td
# --- IMC (Lambda) Tuning ---
lambda_val = tau
imc_kc = (tau + 0.5 * theta) / (Kp * (lambda_val + 0.5 * theta))
imc_ti = tau + 0.5 * theta
imc_td = (tau * theta) / (2 * tau + theta)
imc_kp = imc_kc
imc_ki = imc_kc / imc_ti
imc_kd = imc_kc * imc_td
tuning_results = {
"Ziegler-Nichols": {"Kp": zn_kp, "Ki": zn_ki, "Kd": zn_kd},
"Cohen-Coon": {"Kp": cc_kp, "Ki": cc_ki, "Kd": cc_kd},
"IMC (Lambda)": {"Kp": imc_kp, "Ki": imc_ki, "Kd": imc_kd},
}
return tuning_results