From 6226cd8c344f37553b42908bf1d2b136a9ae4e42 Mon Sep 17 00:00:00 2001 From: DBras Date: Fri, 14 Jun 2024 14:13:14 +0200 Subject: [PATCH] step 1.2 --- battery_local_control.py | 24 +++++++++++++++++++----- mobileload_local_control.py | 27 +++++++++++++++++++-------- simlab_controller_d5_batt.py | 4 ++-- simlab_controller_d5_load.py | 9 +++------ 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/battery_local_control.py b/battery_local_control.py index 23be00a..7e7f215 100644 --- a/battery_local_control.py +++ b/battery_local_control.py @@ -1,4 +1,4 @@ -from util import pos, clamp, soc_scaler +from util import pos, clamp, soc_scaler, cast_to_cm import parameters from time import time, sleep import zmq @@ -7,8 +7,8 @@ import syslab logging.basicConfig(level=logging.INFO) # Controller Parameters -Kp = ... # P factor for our controller. -Ki = ... # I factor for our controller. +Kp = 0.4 # P factor for our controller. +Ki = 0.8 # I factor for our controller. ### Variables # Target that we are trying to reach at the grid connection. @@ -38,10 +38,16 @@ splitting_in_socket.subscribe(parameters.TOPIC_BATTERY_SPLITTING) ### Unit connections # TODO step 1.2: Set up connection to control the battery and reconstruct the pcc (remember that vswitchboard is still not working) +gaia = syslab.WindTurbine("vgaia1") +dumpload = syslab.Dumpload("vload1") +mobload1 = syslab.Dumpload("vmobload1") +pv319 = syslab.Photovoltaics("vpv319") +batt = syslab.Battery('vbatt1') ### Import your controller class # TODO step 1.2: Import the controller class from "simlab_controller_d5_batt.py" or copy/paste it here and pick reasonable controller parameters # Note: The controller is identical to Day 5 with the exception of incorporating the splitting factor +from simlab_controller_d5_batt import PIDController pid = PIDController(Kp=Kp, Ki=Ki, Kd=0.0, u_min=parameters.MIN_BATTERY_P, @@ -70,9 +76,15 @@ try: pass # Poll the grid connection to get the current grid exchange. - pcc_p = 10.0 # TODO step 1.2: Reconstruct the pcc from unit measurements + pcc_p = cast_to_cm(-( + gaia.getActivePower().value + + batt.getActivePower().value + + pv319.getACActivePower().value + - dumpload.getActivePower().value + - mobload1.getActivePower().value + )) # Check our own state of charge - battery_soc = 0.68 # TODO step 1.2: Read the true battery SOC + battery_soc = batt.getSOC() # TODO step 1.2: Read the true battery SOC # Calculate new requests using PID controller battery_setpoint = pid.update(pcc_p.value, x_batt=x_battery) @@ -82,6 +94,7 @@ try: # Send the new setpoint to the battery # TODO step 1.2: Send the new setpoint to the battery + batt.setActivePower(battery_setpoint) logging.info(f"Sent setpoint: {battery_setpoint}") # Publish our current state of charge for the supervisory controller to see @@ -92,5 +105,6 @@ try: finally: # Clean up by closing our sockets. # TODO step 1.2: Set the setpoint of the battery to zero after use + batt.setActivePower(0.) splitting_in_socket.close() soc_out_socket.close() diff --git a/mobileload_local_control.py b/mobileload_local_control.py index b06801b..92ac138 100644 --- a/mobileload_local_control.py +++ b/mobileload_local_control.py @@ -1,4 +1,4 @@ -from util import pos, clamp +from util import pos, clamp, cast_to_cm import parameters from time import time, sleep import zmq @@ -9,8 +9,8 @@ import logging logging.basicConfig(level=logging.INFO) # # Parameters -Kp = ... # P factor for our controller. -Ki = ... # I factor for our controller. +Kp = 0.4 # P factor for our controller. +Ki = 0.8 # I factor for our controller. # # Variables # Target that we are trying to reach at the grid connection. @@ -37,10 +37,16 @@ splitting_in_socket.subscribe(parameters.TOPIC_LOAD_SPLITTING) ### Unit connections # TODO step 1.2: Set up connection to control the battery and reconstruct the pcc (remember that vswitchboard is still not working) +gaia = syslab.WindTurbine("vgaia1") +dumpload = syslab.Dumpload("vload1") +mobload1 = syslab.Dumpload("vmobload1") +pv319 = syslab.Photovoltaics("vpv319") +batt = syslab.Battery('vbatt1') ### Import your controller class # TODO step 1.2: Import the controller class from "simlab_controller_d5_load.py" or copy/paste it here and pick reasonable controller parameters # Note: The controller is identical to Day 5 with the exception of incorporating the splitting factor +from simlab_controller_d5_load import PIDController pid = PIDController(Kp=Kp, Ki=Ki, Kd=0.0, u_min=parameters.MIN_LOAD_P, @@ -48,10 +54,6 @@ pid = PIDController(Kp=Kp, Ki=Ki, Kd=0.0, Iterm=0.0) -# # Unit connections -# TODO step 2.2: Set up connection to control the battery/mobile load and reconstruct the pcc (remember that vswitchboard is still not working) - - # Ensure the mobile load is on before we start (The sleeps wait for the load to respond.) while not mobload1.isLoadOn().value: print("Starting load") @@ -78,7 +80,13 @@ try: pass # Poll the grid connection to get the current grid exchange. - pcc_p = 10.0 # TODO step 1.2: Reconstruct the pcc from unit measurements + pcc_p = cast_to_cm(-( + gaia.getActivePower().value + + batt.getActivePower().value + + pv319.getACActivePower().value + - dumpload.getActivePower().value + - mobload1.getActivePower().value + )) # Calculate new requests using PID controller mobileload_setpoint = pid.update(pcc_p.value, x_load=x_load) @@ -88,6 +96,7 @@ try: # Send the new setpoint to the load # TODO step 1.2: Send the new setpoint to the load + mobload1.setPowerSetPoint(mobileload_setpoint) logging.info(f"Sent setpoint: {mobileload_setpoint}") # Loop once more in a second @@ -95,4 +104,6 @@ try: finally: # Clean up by closing our socket. # TODO step 1.2: Set the setpoint of the mobile load to zero and shut it down after use + mobload1.setPowerSetPoint(0.) + mobload1.stopLoad() splitting_in_socket.close() diff --git a/simlab_controller_d5_batt.py b/simlab_controller_d5_batt.py index f62726f..63f66a4 100644 --- a/simlab_controller_d5_batt.py +++ b/simlab_controller_d5_batt.py @@ -93,11 +93,11 @@ class PIDController: def _calculate_error(self, y_hat_2): # TODO step 1.2: calculate and return the error between reference and measurement - return 0.0 + return self.r - y_hat_2 def _calc_Pterm(self, error): # TODO step 1.2: calculate the proportional term based on the error and self.Kp - return 0.0 + return error * self.Kp def _calc_Iterm(self, error, delta_time): # TODO step 1.2: calculate the integral term based on error, last error and deltaT, and self.Ki diff --git a/simlab_controller_d5_load.py b/simlab_controller_d5_load.py index 860d529..9c41f4c 100644 --- a/simlab_controller_d5_load.py +++ b/simlab_controller_d5_load.py @@ -91,21 +91,18 @@ class PIDController: # optional code to filter the input / actuation signal return u_p - def _calculate_error(self, r, y_hat_2): - # calculate the error between reference and measurement - return r - y_hat_2 - def _calculate_error(self, y_hat_2): # TODO step 1.2: calculate and return the error between reference and measurement - return 0.0 + return self.r - y_hat_2 def _calc_Pterm(self, error): # TODO step 1.2: calculate the proportional term based on the error and self.Kp - return 0.0 + return error * self.Kp def _calc_Iterm(self, error, delta_time): # TODO step 1.2: calculate the integral term based on error, last error and deltaT, and self.Ki return 0.0 + def _calc_Dterm(self, error, delta_time): # calculate the proportional term based on error, last error and deltaT return self.Kd * 0.0