From d154621d99a3a7099f043aacce1ba99d4ef83c7f Mon Sep 17 00:00:00 2001 From: detlefarend Date: Fri, 4 Oct 2024 15:28:10 +0200 Subject: [PATCH] BF: Basics of closed-loop control #1046 --- ...$MLPro-BF-Control_class_diagram.drawio.bkp | 1 + .../MLPro-BF-Control_class_diagram.drawio | 2 +- src/mlpro/bf/control/__init__.py | 3 +- src/mlpro/bf/control/basics.py | 89 +++++++++++++++---- .../bf/control/control_scenarios/__init__.py | 1 + .../bf/control/control_scenarios/basic.py | 78 ++++++++++++++++ src/mlpro/bf/ml/basics.py | 1 - src/mlpro/bf/streams/basics.py | 7 +- src/mlpro/oa/control/basics.py | 9 +- ...howto_bf_control_001_basic_control_loop.py | 35 ++++---- 10 files changed, 178 insertions(+), 48 deletions(-) create mode 100644 doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Control_class_diagram.drawio.bkp create mode 100644 src/mlpro/bf/control/control_scenarios/__init__.py create mode 100644 src/mlpro/bf/control/control_scenarios/basic.py diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Control_class_diagram.drawio.bkp b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Control_class_diagram.drawio.bkp new file mode 100644 index 000000000..895f90961 --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Control_class_diagram.drawio.bkp @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio index 700bfd3e5..a9cab54a3 100644 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/src/mlpro/bf/control/__init__.py b/src/mlpro/bf/control/__init__.py index eace87d6b..682957d50 100644 --- a/src/mlpro/bf/control/__init__.py +++ b/src/mlpro/bf/control/__init__.py @@ -1 +1,2 @@ -from mlpro.bf.control.basics import * \ No newline at end of file +from mlpro.bf.control.basics import * +from mlpro.bf.control.control_scenarios import ControlScenarioBasic \ No newline at end of file diff --git a/src/mlpro/bf/control/basics.py b/src/mlpro/bf/control/basics.py index c109d441b..d635e98cb 100644 --- a/src/mlpro/bf/control/basics.py +++ b/src/mlpro/bf/control/basics.py @@ -25,7 +25,7 @@ from mlpro.bf.events import Event, EventManager from mlpro.bf.math import Element, Function from mlpro.bf.streams import InstDict, Instance, StreamTask, StreamWorkflow, StreamShared, StreamScenario -from mlpro.bf.systems import ActionElement, Action, System +from mlpro.bf.systems import ActionElement, Action, State, System from mlpro.bf.various import Log @@ -295,6 +295,27 @@ def __init__( self, self._system : System = p_system +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict ): + + # 0 Intro + action : Action = None + state : State = None + + # 1 Get action from instance dict + for (inst_type, inst) in p_inst.values(): + if isinstance(p_inst,Action): + action = p_inst + elif isinstance(p_inst,State): + state + + + # 2 Hand over action to wrapped system + + # 3 Add system state t instance dict + raise NotImplementedError + + @@ -428,6 +449,11 @@ def __init__( self, p_visualize=p_visualize, p_logging=p_logging, **p_kwargs ) + + +## ------------------------------------------------------------------------------------------------- + def get_control_panel(self) -> ControlPanel: + return self.get_so() @@ -435,26 +461,54 @@ def __init__( self, ## ------------------------------------------------------------------------------------------------- ## ------------------------------------------------------------------------------------------------- -class ControlScenario ( StreamScenario ): +class ControlScenario (StreamScenario): """ ... """ + C_TYPE = 'Control Scenario' + + ## ------------------------------------------------------------------------------------------------- + def setup(self): + self._control_cycle = self._setup( p_mode=self.get_mode(), + p_visualize=self.get_visualization(), + p_logging=self.get_log_level() ) + + + ## ------------------------------------------------------------------------------------------------- + def _setup(self, p_mode, p_visualize: bool, p_logging) -> ControlCycle: + """ + Custom method to set up a control cycle. Create a new object of type ControlCycle and add + all control tasks of your scenario. + + Parameters + ---------- + p_mode + Operation mode. See Mode.C_VALID_MODES for valid values. Default = Mode.C_MODE_SIM. + p_visualize : bool + Boolean switch for visualisation. + p_logging + Log level (see constants of class Log). Default: Log.C_LOG_ALL. + + Returns + ------- + ControlCycle + Object of type ControlCycle. + """ + + raise NotImplementedError + + ## ------------------------------------------------------------------------------------------------- - def __init__( self, - p_mode, - p_cycle_limit=0, - p_visualize:bool=False, - p_logging=Log.C_LOG_ALL ): - - self._control_cycle : ControlCycle = None - - super.__init__( p_mode, - p_cycle_limit=p_cycle_limit, - p_auto_setup=True, - p_visualize=p_visualize, - p_logging=p_logging ) - + def _set_mode(self, p_mode): + self._control_cycle.set_mode + + +## ------------------------------------------------------------------------------------------------- + def _reset(self, p_seed): + self._iterator = iter(self._stream) + self._iterator.set_random_seed(p_seed=p_seed) + ## ------------------------------------------------------------------------------------------------- def get_control_panel(self) -> ControlPanel: @@ -464,4 +518,5 @@ def get_control_panel(self) -> ControlPanel: panel : ControlPanel Object that enables the external control of a closed-loop control process. """ - return self._control_cycle.get_so() + + return self._control_cycle.get_control_panel() \ No newline at end of file diff --git a/src/mlpro/bf/control/control_scenarios/__init__.py b/src/mlpro/bf/control/control_scenarios/__init__.py new file mode 100644 index 000000000..9da4eab2c --- /dev/null +++ b/src/mlpro/bf/control/control_scenarios/__init__.py @@ -0,0 +1 @@ +from mlpro.bf.control.control_scenarios.basic import ControlScenarioBasic \ No newline at end of file diff --git a/src/mlpro/bf/control/control_scenarios/basic.py b/src/mlpro/bf/control/control_scenarios/basic.py new file mode 100644 index 000000000..ee821c13c --- /dev/null +++ b/src/mlpro/bf/control/control_scenarios/basic.py @@ -0,0 +1,78 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.control_scenarios +## -- Module : basic.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-04 0.1.0 DA Initial implementation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.1.0 (2024-10-04) + +This module provides a simplified container class for a basic synchronous control scenario containing + +- a controller +- a control system +- an optional action incrementer + +""" + + +from mlpro.bf.control.basics import ControlCycle +from mlpro.bf.various import Log +from mlpro.bf.control import Controller, ControlSystem, ControlScenario + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlScenarioBasic (ControlScenario): + """ + Simplified container class for a basic synchronous control scenario containing + + - a controller + - a control system + - an optional action incrementer + + Parameters + ---------- + p_controller : Controller + Controller to be used in the control loop + p_control_system : ControlSystem + Control system to be used in the control loop + p_incremental_action : bool = False + If True, an optional action incrementer is added to control loop + """ + + C_TYPE = 'Control Cycle Basic' + C_NAME = '' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_controller : Controller, + p_control_system : ControlSystem, + p_mode, + p_incremental_actions : bool = False, + p_cycle_limit=0, + p_visualize:bool=False, + p_logging=Log.C_LOG_ALL ): + + self._controller = p_controller + self._control_system = p_control_system + self._incremental_actions = p_incremental_actions + + super().__init__( p_mode = p_mode, + p_cycle_limit = p_cycle_limit, + p_visualize = p_visualize, + p_logging = p_logging ) + + +## ------------------------------------------------------------------------------------------------- + def _setup(self, p_mode, p_visualize: bool, p_logging) -> ControlCycle: + + raise NotImplementedError + + + diff --git a/src/mlpro/bf/ml/basics.py b/src/mlpro/bf/ml/basics.py index a1080caa4..96e5fa14e 100644 --- a/src/mlpro/bf/ml/basics.py +++ b/src/mlpro/bf/ml/basics.py @@ -492,7 +492,6 @@ def _add_objective(self, **p_kwargs): raise NotImplementedError - ## ------------------------------------------------------------------------------------------------- def get_accuracy(self) -> float: """ diff --git a/src/mlpro/bf/streams/basics.py b/src/mlpro/bf/streams/basics.py index 443d8b561..429761f25 100644 --- a/src/mlpro/bf/streams/basics.py +++ b/src/mlpro/bf/streams/basics.py @@ -69,10 +69,11 @@ ## -- 2024-07-19 2.0.3 DA Class StreamTask: excluded non-numeric feature data from default ## -- visualization 2D,3D,ND ## -- 2024-09-11 2.1.0 DA Class Instance: new parent KWArgs +## -- 2024-10-01 2.1.1 DA Method StreamScenario.__init__(): simplification ## ------------------------------------------------------------------------------------------------- """ -Ver. 2.1.0 (2024-09-11) +Ver. 2.1.1 (2024-10-01) This module provides classes for standardized data stream processing. @@ -1663,10 +1664,6 @@ def __init__( self, p_visualize:bool=False, p_logging=Log.C_LOG_ALL ): - self._stream : Stream = None - self._iterator : Stream = None - self._workflow : StreamWorkflow = None - ScenarioBase.__init__( self, p_mode, p_cycle_limit=p_cycle_limit, diff --git a/src/mlpro/oa/control/basics.py b/src/mlpro/oa/control/basics.py index 892328e6a..1cd37f013 100644 --- a/src/mlpro/oa/control/basics.py +++ b/src/mlpro/oa/control/basics.py @@ -9,10 +9,11 @@ ## -- 2024-09-16 0.1.0 DA Initial implementation of class OAController ## -- 2024-09-19 0.2.0 DA Completion of classes and their parents ## -- 2024-09-27 0.3.0 DA New method OAController hdl_setpoint_changed +## -- 2024-10-04 0.3.1 DA Bugfix in OAController.__init__() ## ------------------------------------------------------------------------------------------------- """ -Ver. 0.3.0 (2024-09-27) +Ver. 0.3.1 (2024-10-04) This module provides basic classes around the topic online-adaptive closed-loop control. @@ -65,14 +66,16 @@ def __init__( self, p_logging = Log.C_LOG_ALL, **p_kwargs ): - Controller.__init__( p_name = p_name, + Controller.__init__( self, + p_name = p_name, p_range_max = p_range_max, p_duplicate_data = p_duplicate_data, p_visualize = p_visualize, p_logging = False, **p_kwargs ) - Model.__init__( p_ada = p_ada, + Model.__init__( self, + p_ada = p_ada, p_visualize = p_visualize, p_logging = p_logging ) diff --git a/test/howtos/bf/control/howto_bf_control_001_basic_control_loop.py b/test/howtos/bf/control/howto_bf_control_001_basic_control_loop.py index 926ba9eae..130951b0d 100644 --- a/test/howtos/bf/control/howto_bf_control_001_basic_control_loop.py +++ b/test/howtos/bf/control/howto_bf_control_001_basic_control_loop.py @@ -25,7 +25,7 @@ from time import sleep -from mlpro.bf.control import Controller, ControlSystem, ControlCycle, ControlScenario +from mlpro.bf.control import Controller, ControlSystem, ControlScenarioBasic from mlpro.bf.systems.pool import DoublePendulumSystemS4 @@ -40,23 +40,6 @@ class MyController (Controller): -## ------------------------------------------------------------------------------------------------- -## ------------------------------------------------------------------------------------------------- -class MyControlScenario (ControlScenario): - -## ------------------------------------------------------------------------------------------------- - def _setup(self, p_mode, p_visualize: bool, p_logging): - - # 1 Prepare control system (e.g. by wrapping) - ctrl_sys = ControlSystem( p_system = DoublePendulumSystemS4() ) - - # 2 Prepare control cycle - - # ... - - - - # 1 Preparation of demo/unit test mode if __name__ == '__main__': @@ -74,12 +57,24 @@ def _setup(self, p_mode, p_visualize: bool, p_logging): # 2 Init control scenario -myscenario = MyControlScenario() + +# 2.1 Controller +mycontroller = MyController() + +# 2.2 Control system +mycontrolsystem = ControlSystem( p_system = DoublePendulumSystemS4() ) + +# 2.3 Basic control scenario +myscenario = ControlScenarioBasic( p_controller = mycontroller, + p_control_system = mycontrolsystem, + p_visualize = visualize, + p_logging = logging ) + panel = myscenario.get_control_panel() -# 3 Start the control process asynchronously +# 3 Start the control process in background myscenario.run() panel.start()