controller - Optimizing PIDA Tuning to Reduce Peak Overshoot in DC-DC Buck Converter - Stack Overflow

admin2025-04-15  0

Issue with Peak Overshoot in DC-DC Buck-Boost Converter for EVs

I am currently working on a DC-DC Buck-Boost converter for electric vehicles (EVs). The controller I am using is a PIDA (Proportional-Integral-Derivative-Accelerated) controller, optimized with the Whale Optimization Algorithm (WOA). However, I am facing an issue with the peak overshoot in my system.

Regardless of the changes I make, I am unable to reduce the peak overshoot, which consistently reaches 20V, while my desired stable output is 12V. I have tried various configurations, including PI, PIDA, and PI with WOA, but the peak overshoot remains the same at 20V. The only difference between these configurations is the settling time; the overshoot is not affected. The simulation is being done in Proteus software.

Can anyone provide guidance on how to reduce the peak overshoot to achieve a more stable output?

import spidev
import time
import RPi.GPIO as GPIO
import random

PWM_PIN = 18  
GPIO.setmode(GPIO.BCM) 
GPIO.setup(PWM_PIN, GPIO.OUT)

FREQUENCY = 50000  
pwm = GPIO.PWM(PWM_PIN, FREQUENCY)
pwm.start(23.8)  

spi = spidev.SpiDev()
spi.open(0, 0)  

CALIB_OFFSET = 0.0

Kp = 2.5
Ki = 1.0
Kd = 0.1
Ka = 0.05

iae_list = []
ise_list = []
itae_list = []
itse_list = []
min_error_function_list = []  

duty_cycle_list = []
error_voltage_list = []

iteration_count = 0
min_error_value = float('inf')  
final_duty_cycle = 30  

SAMPLING_INTERVAL = 0.001  

POPULATION_SIZE = 5
MAX_ITER = 500
BOUNDS = [(0, 5), (0, 2), (0, 0.1), (0, 0.01)]  

class PIDA:
    def __init__(self, Kp, Ki, Kd, Ka):
        self.Kp = Kp
        self.Ki = Ki
        self.Kd = Kd
        self.Ka = Ka
        self.integral = 0
        self.previous_error = 0
        self.error_history = [0, 0]
        self.previous_time = time.time()

    def compute_control(self, error, dt):
        
        P = self.Kp * error

        self.integral += error * dt
        self.integral = max(min(self.integral, 20), -20) 
        I = self.Ki * self.integral

        derivative = (error - self.previous_error) / dt if dt > 0.0001 else 0
        alpha_derivative = 0.1  
        derivative_filtered = alpha_derivative * derivative + (1 - alpha_derivative) * self.error_history[-1]

        second_previous_error = self.error_history[-2] if len(self.error_history) > 1 else 0
        acceleration = (error - 2 * self.previous_error + second_previous_error) / (dt ** 2) if dt > 0.0001 else 0
        alpha_acceleration = 0.1  
        acceleration_filtered = alpha_acceleration * acceleration + (1 - alpha_acceleration) * self.error_history[-1]

        self.error_history.append(error)
        if len(self.error_history) > 2:
            self.error_history.pop(0)

        self.previous_error = error
        self.previous_time = time.time()

        return P + I + derivative_filtered + acceleration_filtered

class WOA:
    def __init__(self, population_size, max_iter, bounds):
        self.population_size = population_size
        self.max_iter = max_iter
        self.bounds = bounds

    def optimize(self, evaluate_error):
        population = [self._initialize_agent() for _ in range(self.population_size)]
        best_agent = min(population, key=lambda agent: evaluate_error(*agent))

        for iter in range(self.max_iter):
            a = 2 - iter * (2 / self.max_iter)

            for i, agent in enumerate(population):
                r = random.random()
                A = 2 * a * r - a
                C = 2 * r

                if r < 0.5:
                    if abs(A) < 1:
                        D = [abs(C * best_agent[j] - agent[j]) for j in range(4)]
                        new_agent = [best_agent[j] - A * D[j] for j in range(4)]
                    else:
                        random_agent = random.choice(population)
                        D = [abs(C * random_agent[j] - agent[j]) for j in range(4)]
                        new_agent = [random_agent[j] - A * D[j] for j in range(4)]
                else:
                    D_prime = [abs(best_agent[j] - agent[j]) for j in range(4)]
                    l = random.uniform(-1, 1)
                    b = 1
                    new_agent = [D_prime[j] * (2.718 ** (b * l)) * (2 ** 3.14 * l) + best_agent[j] for j in range(4)]

                new_agent = [max(self.bounds[j][0], min(self.bounds[j][1], new_agent[j])) for j in range(4)]
                population[i] = new_agent

            current_best = min(population, key=lambda agent: evaluate_error(*agent))
            if evaluate_error(*current_best) < evaluate_error(*best_agent):
                best_agent = current_best

            print(f"Iteration {iter + 1}/{self.max_iter}, Best Fitness: {evaluate_error(*best_agent):.4f}")

        return best_agent

    def _initialize_agent(self):
        return [random.uniform(bound[0], bound[1]) for bound in self.bounds]

def read_adc(channel):
    try:
        adc = spi.xfer2([1, (8 + channel) << 4, 0])
        data = ((adc[1] & 3) << 8) + adc[2]
        return data
    except Exception as e:
        print(f"Error reading ADC: {e}")
        return 0

def convert_to_voltage(adc_value, vref=3.3, scale_factor=5.7):
    return 12 - ((adc_value * vref * (3 / 2)) / 1023 * scale_factor)

def evaluate_error(Kp, Ki, Kd, Ka):
    pida = PIDA(Kp, Ki, Kd, Ka)
    start_time = time.time()
    end_time = start_time + 5  
    error_sum = 0

    while time.time() < end_time:
        adc_value = read_adc(0)
        error_voltage = convert_to_voltage(adc_value)
        error = error_voltage

        current_time = time.time()
        dt = current_time - pida.previous_time

        control = pidapute_control(error, dt)
        control = max(0, min(100, control))  
        pwm.ChangeDutyCycle(control)

        error_sum += abs(error)
        time.sleep(SAMPLING_INTERVAL)

    return error_sum

def main():
    global final_duty_cycle

    try:
        
        print("Calibrating... (Place multimeter on output)")
        pwm.ChangeDutyCycle(30)
        time.sleep(1)
        adc_value = read_adc(0)
        actual_voltage = float(input("Enter multimeter reading (V): "))
        CALIB_OFFSET = actual_voltage - convert_to_voltage(adc_value)
        print(f"Calibration offset: {CALIB_OFFSET:.2f}V")

        print("Optimizing PIDA parameters using WOA...")
        woa = WOA(POPULATION_SIZE, MAX_ITER, BOUNDS)
        optimal_params = woa.optimize(evaluate_error)
        Kp, Ki, Kd, Ka = optimal_params
        print(f"Optimized Parameters: Kp={Kp:.2f}, Ki={Ki:.2f}, Kd={Kd:.2f}, Ka={Ka:.2f}")

        pida = PIDA(Kp, Ki, Kd, Ka)
        while True:
            adc_value = read_adc(0)
            error_voltage = convert_to_voltage(adc_value)
            error = error_voltage

            current_time = time.time()
            dt = current_time - pida.previous_time

            control = pidapute_control(error, dt)
            control = max(0, min(100, control))  
            pwm.ChangeDutyCycle(control)

            print(f"Vout: {error_voltage:.2f}V | Duty: {control:.1f}% | Error: {error:.2f}V")
            time.sleep(SAMPLING_INTERVAL)

    except KeyboardInterrupt:
        print("\nStopped by user.")
    finally:
        pwm.stop()
        GPIO.cleanup()
        spi.close()
        print(f"Final Duty Cycle: {final_duty_cycle}")

if __name__ == "__main__":
    main()
转载请注明原文地址:http://www.anycun.com/QandA/1744716051a86628.html