Zadanie
Poniżej znajdziecie kod prostego symulatora - umożliwia on Wam obliczenie odpowiedzi układu regulacji na wymuszenie skokowe, i obliczenie podstawowych wskaźników jakości regulacji. Wykorzystajcie ten symulator do optymalizacji nastaw regulatora wybraną techniką.
import matplotlib.pyplot as plt
import numpy as np
class PIDSecondOrderSystem:
"""
Symulator obiektu inercyjnego drugiego rzędu z regulatorem PID.
"""
def __init__(self, K_p, T_1, T_2, T_n, T_d, K_r, T_i, T_d_reg, dt=0.01):
"""
Inicjalizacja symulatora.
Args:
K_p (float): Wzmocnienie obiektu.
T_1 (float): Stała czasowa pierwszego członu inercyjnego.
T_2 (float): Stała czasowa drugiego członu inercyjnego.
T_n (float): Stała czasowa opóźnienia transportowego (przybliżona dyskretyzacja).
T_d (float): Stała czasowa inercji pomiaru (przybliżona dyskretyzacja).
K_r (float): Wzmocnienie regulatora PID.
T_i (float): Czas całkowania regulatora PID.
T_d_reg (float): Czas różniczkowania regulatora PID.
dt (float): Krok czasowy symulacji.
"""
self.K_p = K_p
self.T_1 = T_1
self.T_2 = T_2
self.T_n = T_n
self.T_d = T_d
self.K_r = K_r
self.T_i = T_i if T_i != 0 else 1e-9 # Zapobieganie dzieleniu przez zero
self.T_d_reg = T_d_reg
self.dt = dt
# Stan obiektu
self.x1 = 0.0
self.x2 = 0.0
self.u_prev = 0.0 # Poprzednia wartość sterowania
# Stan regulatora PID
self.integral_error = 0.0
self.previous_error = 0.0
self.previous_measurement = 0.0
# Stan opóźnienia transportowego i inercji pomiaru
self.transport_delay_buffer = []
self.measurement_inertia_buffer = []
def simulate_step(self, setpoint):
"""
Wykonuje jeden krok symulacji.
Args:
setpoint (float): Wartość zadana.
Returns:
float: Aktualna wartość wyjściowa obiektu.
"""
# Obliczenie uchybu regulacji
measurement = self.x2 # Przyjmujemy x2 jako pomiar
error = setpoint - measurement
# Obliczenie składowych regulatora PID
proportional = self.K_r * error
self.integral_error += error * self.dt
integral = self.K_r / self.T_i * self.integral_error
derivative = self.K_r * self.T_d_reg * (error - self.previous_error) / self.dt
# Ograniczenie składowej całkującej (anti-windup - prosty)
if abs(self.u_prev) > 10: # Przykład prostego ograniczenia
if np.sign(self.u_prev) == np.sign(integral):
integral = self.u_prev - proportional - derivative
# Obliczenie sygnału sterującego
u = proportional + integral + derivative
# Ograniczenie sygnału sterującego (np. do zakresu +/- 10)
u = np.clip(u, -10, 10)
self.u_prev = u
self.previous_error = error
# Symulacja obiektu inercyjnego drugiego rzędu (metoda Eulera w przód)
dx1dt = (self.K_p * u - self.x1) / self.T_1
dx2dt = (self.x1 - self.x2) / self.T_2
self.x1 += dx1dt * self.dt
self.x2 += dx2dt * self.dt
# Symulacja opóźnienia transportowego (bufor)
self.transport_delay_buffer.append(self.x2)
if len(self.transport_delay_buffer) > int(self.T_n / self.dt):
output_with_delay = self.transport_delay_buffer.pop(0)
else:
output_with_delay = self.transport_delay_buffer[0] if self.transport_delay_buffer else 0
# Symulacja inercji pomiaru (przybliżenie 1. rzędu)
if not self.measurement_inertia_buffer:
self.measurement_inertia_buffer.append(output_with_delay)
else:
dy_dt = (output_with_delay - self.measurement_inertia_buffer[-1]) / self.T_d
self.measurement_inertia_buffer.append(self.measurement_inertia_buffer[-1] + dy_dt * self.dt)
return self.measurement_inertia_buffer[-1]
def simulate_step_response(self, setpoint, simulation_time):
"""
Symuluje odpowiedź na skokową zmianę wartości zadanej.
Args:
setpoint (float): Wartość zadana.
simulation_time (float): Czas trwania symulacji.
Returns:
tuple: Krotka zawierająca listy czasu, wartości zadanej i wartości wyjściowej.
"""
time_points = np.arange(0, simulation_time, self.dt)
setpoint_values = [setpoint] * len(time_points)
output_values = []
# Reset stanu symulatora przed nową symulacją
self.__init__(self.K_p, self.T_1, self.T_2, self.T_n, self.T_d,
self.K_r, self.T_i, self.T_d_reg, self.dt)
for t in time_points:
output = self.simulate_step(setpoint)
output_values.append(output)
return time_points, setpoint_values, output_values
def calculate_quality_indicators(self, time_points, setpoint_values, output_values):
"""
Oblicza podstawowe wskaźniki jakości regulacji.
Args:
time_points (list): Lista punktów czasowych.
setpoint_values (list): Lista wartości zadanych.
output_values (list): Lista wartości wyjściowych.
Returns:
dict: Słownik zawierający wskaźniki jakości regulacji.
"""
errors = [setpoint_values[i] - output_values[i] for i in range(len(time_points))]
# Czas regulacji (przyjmujemy tolerancję 2% wartości zadanej)
settling_time = None
tolerance = 0.02 * setpoint_values[-1] # 2% wartości zadanej na końcu symulacji
stable_region_start = None
# Szukamy momentu, od którego sygnał pozostaje w tolerancji
for i in range(len(time_points) - 1, -1, -1):
if abs(errors[i]) > tolerance:
stable_region_start = i + 1 # Czas regulacji jest po ostatnim przekroczeniu tolerancji
break
if stable_region_start is not None and stable_region_start < len(time_points):
settling_time = time_points[stable_region_start]
# Odchyłka statyczna (różnica między wartością zadaną a wyjściową w stanie ustalonym)
# Przyjmujemy ostatnią wartość jako stan ustalony
static_error = setpoint_values[-1] - output_values[-1]
# Całka z kwadratu odchyłki (ISE - Integral Squared Error)
ise = sum([e**2 * self.dt for e in errors])
# Całka z wartości bezwzględnej odchyłki (IAE - Integral Absolute Error)
iae = sum([abs(e) * self.dt for e in errors])
return {
"Czas regulacji (2% tolerancji)": f"{settling_time:.2f} s" if settling_time is not None else "Nie osiągnięto stanu ustalonego",
"Odchyłka statyczna": f"{static_error:.4f}",
"ISE (Integral Squared Error)": f"{ise:.4f}",
"IAE (Integral Absolute Error)": f"{iae:.4f}"
}
# Przykładowe użycie klasy
# Parametry obiektu inercyjnego drugiego rzędu
Kp_obj = 2.0 # Wzmocnienie obiektu
T1_obj = 5.0 # Stała czasowa 1
T2_obj = 2.0 # Stała czasowa 2
Tn_delay = 0.5 # Opóźnienie transportowe
Td_inertia = 0.1 # Inercja pomiaru
# Parametry regulatora PID (przykładowe wartości)
Kr_reg = 1.0 # Wzmocnienie regulatora
Ti_reg = 15 # Czas całkowania
Td_reg_val = 0.5 # Czas różniczkowania
# Krok i czas symulacji
simulation_step = 0.01
simulation_duration = 30.0 # Czas trwania symulacji w sekundach
setpoint_value = 5.0 # Wartość zadana (skok)
# Utworzenie instancji symulatora
simulator = PIDSecondOrderSystem(
K_p=Kp_obj,
T_1=T1_obj,
T_2=T2_obj,
T_n=Tn_delay,
T_d=Td_inertia,
K_r=Kr_reg,
T_i=Ti_reg,
T_d_reg=Td_reg_val,
dt=simulation_step
)
# Wykonanie symulacji odpowiedzi na skok
time_points, setpoint_values, output_values = simulator.simulate_step_response(
setpoint=setpoint_value,
simulation_time=simulation_duration
)
# Obliczenie wskaźników jakości regulacji
quality_indicators = simulator.calculate_quality_indicators(
time_points,
setpoint_values,
output_values
)
# Wyświetlenie wskaźników jakości regulacji
print("Wskaźniki jakości regulacji:")
for key, value in quality_indicators.items():
print(f"- {key}: {value}")
# Wyświetlenie wykresu odpowiedzi układu
plt.figure(figsize=(12, 6))
plt.plot(time_points, setpoint_values, label='Wartość zadana', linestyle='--')
plt.plot(time_points, output_values, label='Wartość wyjściowa')
plt.title('Odpowiedź układu regulacji na skok wartości zadanej')
plt.xlabel('Czas [s]')
plt.ylabel('Wartość')
plt.grid(True)
plt.legend()
plt.show()
Ostatnia modyfikacja: poniedziałek, 2 czerwca 2025, 16:02