diff --git a/README.md b/README.md
index c0eb937d..873d42c4 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@
[**Overview paper**](https://joss.theoj.org/papers/10.21105/joss.02498)
| [**Reinforcement learning paper**](https://arxiv.org/abs/1910.09434)
+| [**GEM control paper**](https://ieeexplore.ieee.org/document/10239044)
| [**Quickstart**](#getting-started)
| [**Install guide**](#installation)
| [**Reference docs**](https://upb-lea.github.io/gym-electric-motor/)
@@ -18,7 +19,8 @@
## Overview
The gym-electric-motor (GEM) package is a Python toolbox for the simulation and control of various electric motors.
-It is built upon [Faram Gymnasium Environments](https://gym.openai.com/), and, therefore, can be used for both, classical control simulation and [reinforcement learning](https://github.com/upb-lea/reinforcement_learning_course_materials) experiments. It allows you to construct a typical drive train with the usual building blocks, i.e., supply voltages, converters, electric motors and load models, and obtain not only a closed-loop simulation of this physical structure, but also a rich interface for plugging in any decision making algorithm, from linear feedback control to [Deep Deterministic Policy Gradient](https://spinningup.openai.com/en/latest/algorithms/ddpg.html) agents.
+It is built upon [Faram Gymnasium Environments](https://gymnasium.farama.org/), and, therefore, can be used for both, classical control simulation and [reinforcement learning](https://github.com/upb-lea/reinforcement_learning_course_materials) experiments. It allows you to construct a typical drive train with the usual building blocks, i.e., supply voltages, converters, electric motors and load models, and obtain not only a closed-loop simulation of this physical structure, but also a rich interface for plugging in any decision making algorithm, from linear feedback control to [Deep Deterministic Policy Gradient](https://spinningup.openai.com/en/latest/algorithms/ddpg.html) agents.
+Since v2.0.1, gym-electric-motor control is integrated in gym-electric-motor and provide automated classical control structures for the environment of the Toolbox.
## Getting Started
An easy way to get started with GEM is by playing around with the following interactive notebooks in Google Colaboratory. Most important features of GEM as well as application demonstrations are showcased, and give a kickstart for engineers in industry and academia.
@@ -116,6 +118,19 @@ A white paper for the utilization of this framework within reinforcement learnin
doi={10.1109/TNNLS.2020.3029573}}
```
+A white paper for the classical control approaches of gym-electric-motor control is available at [IEEE-Xplore](https://ieeexplore.ieee.org/document/10239044). Please use the following BibTeX entry for citing it:
+```
+@INPROCEEDINGS{10239044,
+ author={Book, Felix and Traue, Arne and Schenke, Maximilian and Haucke-Korber, Barnabas and Wallscheid, Oliver},
+ booktitle={2023 IEEE International Electric Machines & Drives Conference (IEMDC)},
+ title={Gym-Electric-Motor (GEM) Control: An Automated Open-Source Controller Design Suite for Drives},
+ year={2023},
+ volume={},
+ number={},
+ pages={1-7},
+ doi={10.1109/IEMDC55163.2023.10239044}}
+```
+
### Running Unit Tests with Pytest
To run the unit tests ''pytest'' is required.
All tests can be found in the ''tests'' folder.
diff --git a/examples/classic_controllers/classic_controllers.py b/examples/classic_controllers/classic_controllers.py
index 2f39c4fb..9ade5e6b 100644
--- a/examples/classic_controllers/classic_controllers.py
+++ b/examples/classic_controllers/classic_controllers.py
@@ -136,7 +136,7 @@ def reference_states(environment, **controller_kwargs):
def find_controller_type(environment, stages, **controller_kwargs):
_stages = stages
- if isinstance(environment.physical_system.unwrapped, DcMotorSystem):
+ if isinstance(environment.unwrapped.physical_system, DcMotorSystem):
if type(stages) is list:
if len(stages) > 1:
if type(stages[0]) is list:
@@ -413,24 +413,24 @@ def automated_gain(environment, stages, controller_type, _controllers, **control
_controllers[stages_a[i][0]["controller_type"]][1] == ContinuousController
): # had to add [0] to make dict in list acessable
if i == 0:
- p_gain = mp["l"] / (environment.physical_system.tau * a) / u_a_lim * i_a_lim
- i_gain = p_gain / (environment.physical_system.tau * a**2)
+ p_gain = mp["l"] / (environment.unwrapped.physical_system.tau * a) / u_a_lim * i_a_lim
+ i_gain = p_gain / (environment.unwrapped.physical_system.tau * a**2)
if _controllers[stages_a[i][0]["controller_type"]][2] == PIDController:
- d_gain = p_gain * environment.physical_system.tau
+ d_gain = p_gain * environment.unwrapped.physical_system.tau
stages_a[i][0]["d_gain"] = stages_a[i][0].get("d_gain", d_gain)
elif i == 1:
- t_n = environment.physical_system.tau * a**2
+ t_n = environment.unwrapped.physical_system.tau * a**2
p_gain = (
- environment.physical_system.mechanical_load.j_total
+ environment.unwrapped.physical_system.mechanical_load.j_total
/ (a * t_n)
/ i_a_lim
* omega_lim
)
i_gain = p_gain / (a * t_n)
if _controllers[stages_a[i][0]["controller_type"]][2] == PIDController:
- d_gain = p_gain * environment.physical_system.tau
+ d_gain = p_gain * environment.unwrapped.physical_system.tau
stages_a[i][0]["d_gain"] = stages_a[i][0].get("d_gain", d_gain)
stages_a[i][0]["p_gain"] = stages_a[i][0].get("p_gain", p_gain) # ?
diff --git a/examples/classic_controllers/classic_controllers_dc_motor_example.py b/examples/classic_controllers/classic_controllers_dc_motor_example.py
index 0a5a72ea..85a0ddfc 100644
--- a/examples/classic_controllers/classic_controllers_dc_motor_example.py
+++ b/examples/classic_controllers/classic_controllers_dc_motor_example.py
@@ -2,6 +2,7 @@
from externally_referenced_state_plot import ExternallyReferencedStatePlot
import gym_electric_motor as gem
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
from gym_electric_motor.visualization import MotorDashboard
from gym_electric_motor.visualization.render_modes import RenderMode
@@ -20,28 +21,27 @@
'Finite' Discrete Action Space
"""
+ """
motor_type = "PermExDc"
control_type = "TC"
action_type = "Cont"
+ """
- motor = action_type + "-" + control_type + "-" + motor_type + "-v0"
- if motor_type in ["PermExDc", "SeriesDc"]:
- states = ["omega", "torque", "i", "u"]
- elif motor_type == "ShuntDc":
- states = ["omega", "torque", "i_a", "i_e", "u"]
- elif motor_type == "ExtExDc":
- states = ["omega", "torque", "i_a", "i_e", "u_a", "u_e"]
- else:
- raise KeyError(motor_type + " is not available")
+ motor = Motor(
+ MotorType.PermanentlyExcitedDcMotor,
+ ControlType.TorqueControl,
+ ActionType.Continuous,
+ )
+
# definition of the plotted variables
- external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states]
+ external_ref_plots = [ExternallyReferencedStatePlot(state) for state in motor.states()]
motor_dashboard = MotorDashboard(additional_plots=external_ref_plots, render_mode=RenderMode.Figure)
# initialize the gym-electric-motor environment
env = gem.make(
- motor,
+ motor.env_id(),
visualization=motor_dashboard,
)
"""
diff --git a/examples/classic_controllers/classic_controllers_ind_motor_example.py b/examples/classic_controllers/classic_controllers_ind_motor_example.py
index 25f36d42..074a1f21 100644
--- a/examples/classic_controllers/classic_controllers_ind_motor_example.py
+++ b/examples/classic_controllers/classic_controllers_ind_motor_example.py
@@ -1,7 +1,9 @@
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
from classic_controllers import Controller
from externally_referenced_state_plot import ExternallyReferencedStatePlot
from external_plot import ExternalPlot
import gym_electric_motor as gem
+
from gym_electric_motor.visualization import MotorDashboard
from gym_electric_motor.physical_system_wrappers import FluxObserver
import numpy as np
@@ -16,27 +18,33 @@
action_type: 'AbcCont' Continuous Action Space
"""
-
+ """
motor_type = "SCIM"
control_type = "TC"
action_type = "Cont"
+ """
- env_id = action_type + "-" + control_type + "-" + motor_type + "-v0"
+ motor = Motor(
+ MotorType.SquirrelCageInductionMotor,
+ ControlType.TorqueControl,
+ ActionType.Continuous,
+ )
# definition of the plotted variables
states = ["omega", "torque", "i_sd", "i_sq", "u_sd", "u_sq"]
- external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states]
+ external_ref_plots = [ExternallyReferencedStatePlot(state) for state in motor.states()]
external_plot = [
- ExternalPlot(referenced=control_type != "CC"),
+ ExternalPlot(referenced= ControlType.TorqueControl != "CC"),
ExternalPlot(min=-np.pi, max=np.pi),
]
external_ref_plots += external_plot
+ motor_dashboard = MotorDashboard(state_plots=("omega", "psi_abs", "psi_angle"))
# initialize the gym-electric-motor environment
env = gem.make(
- env_id,
+ motor.env_id(),
physical_system_wrappers=(FluxObserver(),),
- visualization=MotorDashboard(state_plots=("omega", "psi_abs", "psi_angle")),
+ visualization=MotorDashboard(),
)
"""
@@ -60,4 +68,5 @@
if terminated:
env.reset()
controller.reset()
+ motor_dashboard.show_and_hold()
env.close()
diff --git a/examples/classic_controllers/classic_controllers_synch_motor_example.py b/examples/classic_controllers/classic_controllers_synch_motor_example.py
index ff8ad73f..4a367e0a 100644
--- a/examples/classic_controllers/classic_controllers_synch_motor_example.py
+++ b/examples/classic_controllers/classic_controllers_synch_motor_example.py
@@ -1,3 +1,4 @@
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
from classic_controllers import Controller
from externally_referenced_state_plot import ExternallyReferencedStatePlot
import gym_electric_motor as gem
@@ -16,22 +17,28 @@
action_type: 'Cont' Continuous Action Space in ABC-Coordinates
'Finite' Discrete Action Space
"""
-
+
+ """
motor_type = "PMSM"
control_type = "TC"
action_type = "Cont"
+ """
- env_id = action_type + "-" + control_type + "-" + motor_type + "-v0"
+ motor = Motor(
+ MotorType.PermanentMagnetSynchronousMotor,
+ ControlType.TorqueControl,
+ ActionType.Continuous
+ )
# definition of the plotted variables
external_ref_plots = [
- ExternallyReferencedStatePlot(state)
- for state in ["omega", "torque", "i_sd", "i_sq", "u_sd", "u_sq"]
+ ExternallyReferencedStatePlot(state) for state in ["omega", "torque", "i_sd", "i_sq", "u_sd", "u_sq"]
]
+ motor_dashboard = MotorDashboard(additional_plots=external_ref_plots)
# initialize the gym-electric-motor environment
env = gem.make(
- env_id, visualization=MotorDashboard(additional_plots=external_ref_plots)
+ motor.env_id(), visualization=motor_dashboard
)
"""
@@ -64,5 +71,6 @@
if terminated:
env.reset()
controller.reset()
-
+
+ motor_dashboard.show_and_hold()
env.close()
diff --git a/examples/classic_controllers/custom_classic_controllers_dc_motor_example.py b/examples/classic_controllers/custom_classic_controllers_dc_motor_example.py
index d22a6688..af6b414f 100644
--- a/examples/classic_controllers/custom_classic_controllers_dc_motor_example.py
+++ b/examples/classic_controllers/custom_classic_controllers_dc_motor_example.py
@@ -1,7 +1,9 @@
from classic_controllers import Controller
from externally_referenced_state_plot import ExternallyReferencedStatePlot
import gym_electric_motor as gem
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
from gym_electric_motor.visualization import MotorDashboard
+from gym_electric_motor.visualization.render_modes import RenderMode
if __name__ == "__main__":
"""
@@ -19,29 +21,27 @@
"""
# following manual controller design addresses an ExtExDc. Other motor types require different controller stages
+ """
motor_type = "ExtExDc"
control_type = "CC"
action_type = "Cont"
+ """
- motor = action_type + "-" + control_type + "-" + motor_type + "-v0"
+ motor = Motor(MotorType.ExternallyExcitedDcMotor,
+ ControlType.CurrentControl,
+ ActionType.Continuous,)
- if motor_type in ["PermExDc", "SeriesDc"]:
- states = ["omega", "torque", "i", "u"]
- elif motor_type == "ShuntDc":
- states = ["omega", "torque", "i_a", "i_e", "u"]
- elif motor_type == "ExtExDc":
- states = ["omega", "torque", "i_a", "i_e", "u_a", "u_e"]
- else:
- raise KeyError(motor_type + " is not available")
# definition of the plotted variables
- external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states]
+ external_ref_plots = [ExternallyReferencedStatePlot(state) for state in motor.states()]
+
+ motor_dashboard = MotorDashboard(additional_plots=external_ref_plots, render_mode=RenderMode.Figure)
# initialize the gym-electric-motor environment
env = gem.make(
- motor,
- visualization=MotorDashboard(additional_plots=external_ref_plots),
- render_mode="figure",
+ motor.env_id(),
+ visualization=motor_dashboard
+
)
"""
@@ -92,4 +92,5 @@
env.reset()
controller.reset()
+ motor_dashboard.show_and_hold()
env.close()
diff --git a/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py b/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py
index 50a4c3a3..135403f5 100644
--- a/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py
+++ b/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py
@@ -1,3 +1,4 @@
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
from classic_controllers import Controller
from externally_referenced_state_plot import ExternallyReferencedStatePlot
from external_plot import ExternalPlot
@@ -15,25 +16,34 @@
action_type: 'Cont' Continuous Action Space
"""
-
+
+ """
motor_type = "SCIM"
control_type = "SC"
action_type = "Cont"
+ """
- env_id = action_type + "-" + control_type + "-" + motor_type + "-v0"
+ motor = Motor(
+ MotorType.SquirrelCageInductionMotor,
+ ControlType.SpeedControl,
+ ActionType.Continuous,
+ )
# definition of the plotted variables
states = ["omega", "torque", "i_sd", "i_sq", "u_sd", "u_sq"]
external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states]
external_plot = [
- ExternalPlot(referenced=control_type != "CC"),
+ ExternalPlot(referenced=ControlType.SpeedControl != "CC"),
ExternalPlot(min=-np.pi, max=np.pi),
]
external_ref_plots += external_plot
+ motor_dashboard = MotorDashboard(additional_plots=external_ref_plots)
+
# initialize the gym-electric-motor environment
env = gem.make(
- env_id, visualization=MotorDashboard(additional_plots=external_ref_plots)
+ motor.env_id(),
+ visualization=motor_dashboard
)
"""
@@ -67,4 +77,6 @@
if terminated:
env.reset()
controller.reset()
+
+ motor_dashboard.show_and_hold()
env.close()
diff --git a/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py b/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py
index 293d0655..f1332d5b 100644
--- a/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py
+++ b/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py
@@ -1,3 +1,4 @@
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
from classic_controllers import Controller
from externally_referenced_state_plot import ExternallyReferencedStatePlot
import gym_electric_motor as gem
@@ -16,15 +17,18 @@
action_type: 'Cont' Continuous Action Space in ABC-Coordinates
'Finite' Discrete Action Space
"""
-
+ """
motor_type = "PMSM"
control_type = "SC"
action_type = "Cont"
+ """
- env_id = action_type + "-" + control_type + "-" + motor_type + "-v0"
+ motor = Motor(MotorType.PermanentMagnetSynchronousMotor,
+ ControlType.SpeedControl,
+ ActionType.Continuous)
# definition of the motor parameters
- psi_p = 0 if motor_type == "SynRM" else 45e-3
+ psi_p = 0 if "SynRM" in motor.env_id() else 45e-3
limit_values = dict(omega=12e3 * np.pi / 30, torque=100, i=280, u=320)
nominal_values = dict(
omega=10e3 * np.pi / 30, torque=95.0, i=240, epsilon=np.pi, u=300
@@ -39,9 +43,10 @@
for state in ["omega", "torque", "i_sd", "i_sq", "u_sd", "u_sq"]
]
+ motor_dashboard = MotorDashboard(additional_plots=external_ref_plots)
# initialize the gym-electric-motor environment
env = gem.make(
- env_id,
+ motor.env_id(),
visualization=MotorDashboard(additional_plots=external_ref_plots),
motor=dict(
limit_values=limit_values,
@@ -140,5 +145,5 @@
if terminated:
env.reset()
controller.reset()
-
+
env.close()
diff --git a/examples/environment_features/external_speed_profile.py b/examples/environment_features/external_speed_profile.py
index a5f0dc72..9e7f43aa 100644
--- a/examples/environment_features/external_speed_profile.py
+++ b/examples/environment_features/external_speed_profile.py
@@ -2,6 +2,8 @@
import gym_electric_motor as gem
from gym_electric_motor import reference_generators as rg
from gym_electric_motor.visualization import MotorDashboard
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
+from gym_electric_motor.physical_systems.solvers import ScipySolveIvpSolver
import time
from scipy import signal
@@ -67,11 +69,19 @@
# inital value is given by bias of the profile
sampling_time = 1e-4
+#define the motor env object
+motor = Motor(
+ motor_type=MotorType.SeriesDc,
+ control_type=ControlType.CurrentControl,
+ action_type=ActionType.Continuous,
+)
+
+
if __name__ == "__main__":
# Create the environment
env = gem.make(
- "Cont-CC-SeriesDc-v0",
- ode_solver="scipy.solve_ivp",
+ motor.env_id(),
+ ode_solver=ScipySolveIvpSolver(),
tau=sampling_time,
reference_generator=const_switch_gen,
visualization=MotorDashboard(state_plots=["omega", "i"], reward_plot=True),
diff --git a/examples/environment_features/scim_ideal_grid_simulation.py b/examples/environment_features/scim_ideal_grid_simulation.py
index a0688ab9..e3d20c29 100644
--- a/examples/environment_features/scim_ideal_grid_simulation.py
+++ b/examples/environment_features/scim_ideal_grid_simulation.py
@@ -6,6 +6,8 @@
import numpy as np
import gym_electric_motor as gem
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
+from gym_electric_motor.physical_systems.solvers import ScipyOdeSolver
import matplotlib.pyplot as plt
@@ -31,17 +33,18 @@ def grid_voltage(t):
return grid_voltage
+motor = Motor(motor_type=MotorType.SquirrelCageInductionMotor, control_type=ControlType.CurrentControl, action_type=ActionType.Continuous)
# Create the environment
env = gem.make(
# Choose the squirrel cage induction motor (SCIM) with continuous-control-set
- "Cont-CC-SCIM-v0",
+ motor.env_id(),
#
load=gem.physical_systems.PolynomialStaticLoad(
dict(a=0.0, b=0.0, c=0.0, j_load=1e-6)
),
# Define the numerical solver for the simulation
- ode_solver="scipy.ode",
+ ode_solver=ScipyOdeSolver(),
# Define which state variables are to be monitored concerning limit violations
# "()" means, that limit violation will not necessitate an env.reset()
constraints=(),
@@ -53,7 +56,7 @@ def grid_voltage(t):
limits = env.physical_system.limits
# reset the environment such that the simulation can be started
-(state, reference) = env.reset()
+(state, reference),_ = env.reset()
# We define these arrays in order to save our simulation results in them
# Initial state and initial time are directly inserted
diff --git a/examples/environment_features/userdefined_initialization.py b/examples/environment_features/userdefined_initialization.py
index 07ddf9ee..f427a7fe 100644
--- a/examples/environment_features/userdefined_initialization.py
+++ b/examples/environment_features/userdefined_initialization.py
@@ -1,6 +1,8 @@
import gym_electric_motor as gem
from gym_electric_motor import reference_generators as rg
from gym_electric_motor.visualization import MotorDashboard
+from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType
+from gym_electric_motor.physical_systems.solvers import ScipySolveIvpSolver
import time
"""
@@ -39,15 +41,17 @@
# initializer for a specific speed
load_init = {"states": {"omega": 20}}
+motor = Motor(motor_type=MotorType.SeriesDc, control_type=ControlType.CurrentControl, action_type=ActionType.Continuous)
+
if __name__ == "__main__":
env = gem.make(
- "Cont-CC-SeriesDc-v0",
+ motor.env_id(),
visualization=MotorDashboard(state_plots=["omega", "i"]),
motor=dict(
motor_parameter=dict(j_rotor=0.001), motor_initializer=gaussian_init
),
load=dict(j_load=0.001, load_initializer=uniform_init),
- ode_solver="scipy.solve_ivp",
+ ode_solver=ScipySolveIvpSolver(),
reference_generator=rg.SwitchedReferenceGenerator(
sub_generators=[
rg.SinusoidalReferenceGenerator(reference_state="omega"),
@@ -63,7 +67,7 @@
cum_rew = 0
for j in range(10):
- state, reference = env.reset()
+ (state, reference), _ = env.reset()
# Print the initial states:
denorm_state = state * env.limits
diff --git a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb
index a7222e84..36dcd2e4 100644
--- a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb
+++ b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb
@@ -32,7 +32,7 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -42,7 +42,7 @@
},
{
"cell_type": "code",
- "execution_count": 2,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -52,10 +52,12 @@
"from gekko import GEKKO\n",
"\n",
"import gym_electric_motor as gem\n",
+ "from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType\n",
"from gym_electric_motor.physical_systems import ConstantSpeedLoad\n",
"from gym_electric_motor.reference_generators import MultipleReferenceGenerator, SwitchedReferenceGenerator, \\\n",
" TriangularReferenceGenerator, SinusoidalReferenceGenerator, StepReferenceGenerator\n",
- "from gym_electric_motor.visualization.motor_dashboard import MotorDashboard"
+ "from gym_electric_motor.visualization.motor_dashboard import MotorDashboard\n",
+ "from gym_electric_motor.physical_systems.solvers import ScipySolveIvpSolver"
]
},
{
@@ -67,7 +69,7 @@
},
{
"cell_type": "code",
- "execution_count": 3,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -95,7 +97,7 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -253,7 +255,7 @@
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -271,7 +273,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": null,
"metadata": {},
"outputs": [],
"source": [
@@ -308,46 +310,21 @@
},
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": null,
"metadata": {},
- "outputs": [
- {
- "data": {
- "application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.get_websocket_type = function() {\n if (typeof(WebSocket) !== 'undefined') {\n return WebSocket;\n } else if (typeof(MozWebSocket) !== 'undefined') {\n return MozWebSocket;\n } else {\n alert('Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.');\n };\n}\n\nmpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = (this.ws.binaryType != undefined);\n\n if (!this.supports_binary) {\n var warnings = document.getElementById(\"mpl-warnings\");\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent = (\n \"This browser does not support binary websocket messages. \" +\n \"Performance may be slow.\");\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = $('
');\n this._root_extra_style(this.root)\n this.root.attr('style', 'display: inline-block');\n\n $(parent_element).append(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n fig.send_message(\"send_image_mode\", {});\n if (mpl.ratio != 1) {\n fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n }\n fig.send_message(\"refresh\", {});\n }\n\n this.imageObj.onload = function() {\n if (fig.image_mode == 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function() {\n fig.ws.close();\n }\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n}\n\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '');\n var titletext = $(\n '');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_div = $('');\n\n canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n\n function canvas_keyboard_event(event) {\n return fig.key_event(event, event['data']);\n }\n\n canvas_div.keydown('key_press', canvas_keyboard_event);\n canvas_div.keyup('key_release', canvas_keyboard_event);\n this.canvas_div = canvas_div\n this._canvas_extra_style(canvas_div)\n this.root.append(canvas_div);\n\n var canvas = $('');\n canvas.addClass('mpl-canvas');\n canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n\n this.canvas = canvas[0];\n this.context = canvas[0].getContext(\"2d\");\n\n var backingStore = this.context.backingStorePixelRatio ||\n\tthis.context.webkitBackingStorePixelRatio ||\n\tthis.context.mozBackingStorePixelRatio ||\n\tthis.context.msBackingStorePixelRatio ||\n\tthis.context.oBackingStorePixelRatio ||\n\tthis.context.backingStorePixelRatio || 1;\n\n mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband = $('');\n rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n\n var pass_mouse_events = true;\n\n canvas_div.resizable({\n start: function(event, ui) {\n pass_mouse_events = false;\n },\n resize: function(event, ui) {\n fig.request_resize(ui.size.width, ui.size.height);\n },\n stop: function(event, ui) {\n pass_mouse_events = true;\n fig.request_resize(ui.size.width, ui.size.height);\n },\n });\n\n function mouse_event_fn(event) {\n if (pass_mouse_events)\n return fig.mouse_event(event, event['data']);\n }\n\n rubberband.mousedown('button_press', mouse_event_fn);\n rubberband.mouseup('button_release', mouse_event_fn);\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband.mousemove('motion_notify', mouse_event_fn);\n\n rubberband.mouseenter('figure_enter', mouse_event_fn);\n rubberband.mouseleave('figure_leave', mouse_event_fn);\n\n canvas_div.on(\"wheel\", function (event) {\n event = event.originalEvent;\n event['data'] = 'scroll'\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n mouse_event_fn(event);\n });\n\n canvas_div.append(canvas);\n canvas_div.append(rubberband);\n\n this.rubberband = rubberband;\n this.rubberband_canvas = rubberband[0];\n this.rubberband_context = rubberband[0].getContext(\"2d\");\n this.rubberband_context.strokeStyle = \"#000000\";\n\n this._resize_canvas = function(width, height) {\n // Keep the size of the canvas, canvas container, and rubber band\n // canvas in synch.\n canvas_div.css('width', width)\n canvas_div.css('height', height)\n\n canvas.attr('width', width * mpl.ratio);\n canvas.attr('height', height * mpl.ratio);\n canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n\n rubberband.attr('width', width);\n rubberband.attr('height', height);\n }\n\n // Set the figure to an initial 600x600px, this will subsequently be updated\n // upon first draw.\n this._resize_canvas(600, 600);\n\n // Disable right mouse context menu.\n $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n return false;\n });\n\n function set_focus () {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n}\n\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('');\n nav_element.attr('style', 'width: 100%');\n this.root.append(nav_element);\n\n // Define a callback function for later on.\n function toolbar_event(event) {\n return fig.toolbar_button_onclick(event['data']);\n }\n function toolbar_mouse_event(event) {\n return fig.toolbar_button_onmouseover(event['data']);\n }\n\n for(var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n // put a spacer in here.\n continue;\n }\n var button = $('');\n button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n 'ui-button-icon-only');\n button.attr('role', 'button');\n button.attr('aria-disabled', 'false');\n button.click(method_name, toolbar_event);\n button.mouseover(tooltip, toolbar_mouse_event);\n\n var icon_img = $('');\n icon_img.addClass('ui-button-icon-primary ui-icon');\n icon_img.addClass(image);\n icon_img.addClass('ui-corner-all');\n\n var tooltip_span = $('');\n tooltip_span.addClass('ui-button-text');\n tooltip_span.html(tooltip);\n\n button.append(icon_img);\n button.append(tooltip_span);\n\n nav_element.append(button);\n }\n\n var fmt_picker_span = $('');\n\n var fmt_picker = $('');\n fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n fmt_picker_span.append(fmt_picker);\n nav_element.append(fmt_picker_span);\n this.format_dropdown = fmt_picker[0];\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = $(\n '', {selected: fmt === mpl.default_extension}).html(fmt);\n fmt_picker.append(option);\n }\n\n // Add hover states to the ui-buttons\n $( \".ui-button\" ).hover(\n function() { $(this).addClass(\"ui-state-hover\");},\n function() { $(this).removeClass(\"ui-state-hover\");}\n );\n\n var status_bar = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n}\n\nmpl.figure.prototype.send_message = function(type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n}\n\nmpl.figure.prototype.send_draw_message = function() {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n }\n}\n\n\nmpl.figure.prototype.handle_save = function(fig, msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n}\n\n\nmpl.figure.prototype.handle_resize = function(fig, msg) {\n var size = msg['size'];\n if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n fig._resize_canvas(size[0], size[1]);\n fig.send_message(\"refresh\", {});\n };\n}\n\nmpl.figure.prototype.handle_rubberband = function(fig, msg) {\n var x0 = msg['x0'] / mpl.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n var x1 = msg['x1'] / mpl.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0, 0, fig.canvas.width / mpl.ratio, fig.canvas.height / mpl.ratio);\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n}\n\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.figure.prototype.handle_cursor = function(fig, msg) {\n var cursor = msg['cursor'];\n switch(cursor)\n {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n}\n\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.figure.prototype.updated_canvas_event = function() {\n // Called whenever the canvas gets updated.\n this.send_message(\"ack\", {});\n}\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function(fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n evt.data.type = \"image/png\";\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src);\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n evt.data);\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig[\"handle_\" + msg_type];\n } catch (e) {\n console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n }\n }\n };\n}\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function(e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e)\n e = window.event;\n if (e.target)\n targ = e.target;\n else if (e.srcElement)\n targ = e.srcElement;\n if (targ.nodeType == 3) // defeat Safari bug\n targ = targ.parentNode;\n\n // jQuery normalizes the pageX and pageY\n // pageX,Y are the mouse positions relative to the document\n // offset() returns the position of the element relative to the document\n var x = e.pageX - $(targ).offset().left;\n var y = e.pageY - $(targ).offset().top;\n\n return {\"x\": x, \"y\": y};\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys (original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object')\n obj[key] = original[key]\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function(event, name) {\n var canvas_pos = mpl.findpos(event)\n\n if (name === 'button_press')\n {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * mpl.ratio;\n var y = canvas_pos.y * mpl.ratio;\n\n this.send_message(name, {x: x, y: y, button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event)});\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n}\n\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.figure.prototype.key_event = function(event, name) {\n\n // Prevent repeat events\n if (name == 'key_press')\n {\n if (event.which === this._key)\n return;\n else\n this._key = event.which;\n }\n if (name == 'key_release')\n this._key = null;\n\n var value = '';\n if (event.ctrlKey && event.which != 17)\n value += \"ctrl+\";\n if (event.altKey && event.which != 18)\n value += \"alt+\";\n if (event.shiftKey && event.which != 16)\n value += \"shift+\";\n\n value += 'k';\n value += event.which.toString();\n\n this._key_event_extra(event, name);\n\n this.send_message(name, {key: value,\n guiEvent: simpleKeys(event)});\n return false;\n}\n\nmpl.figure.prototype.toolbar_button_onclick = function(name) {\n if (name == 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message(\"toolbar_button\", {name: name});\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.close = function() {\n comm.close()\n };\n ws.send = function(m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function(msg) {\n //console.log('receiving', msg['content']['data'], msg);\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(msg['content']['data'])\n });\n return ws;\n}\n\nmpl.mpl_figure_comm = function(comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = $(\"#\" + id);\n var ws_proxy = comm_websocket_adapter(comm)\n\n function ondownload(figure, format) {\n window.open(figure.imageObj.src);\n }\n\n var fig = new mpl.figure(id, ws_proxy,\n ondownload,\n element.get(0));\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element.get(0);\n fig.cell_info = mpl.find_output_cell(\"\");\n if (!fig.cell_info) {\n console.error(\"Failed to find cell for figure\", id, fig);\n return;\n }\n\n var output_index = fig.cell_info[2]\n var cell = fig.cell_info[0];\n\n};\n\nmpl.figure.prototype.handle_close = function(fig, msg) {\n var width = fig.canvas.width/mpl.ratio\n fig.root.unbind('remove')\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable()\n $(fig.parent_element).html('');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.figure.prototype.push_to_output = function(remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width/mpl.ratio\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] = '';\n}\n\nmpl.figure.prototype.updated_canvas_event = function() {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message(\"ack\", {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () { fig.push_to_output() }, 1000);\n}\n\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('');\n nav_element.attr('style', 'width: 100%');\n this.root.append(nav_element);\n\n // Define a callback function for later on.\n function toolbar_event(event) {\n return fig.toolbar_button_onclick(event['data']);\n }\n function toolbar_mouse_event(event) {\n return fig.toolbar_button_onmouseover(event['data']);\n }\n\n for(var toolbar_ind in mpl.toolbar_items){\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) { continue; };\n\n var button = $('');\n button.click(method_name, toolbar_event);\n button.mouseover(tooltip, toolbar_mouse_event);\n nav_element.append(button);\n }\n\n // Add the status bar.\n var status_bar = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('');\n var button = $('');\n button.click(function (evt) { fig.handle_close(fig, {}); } );\n button.mouseover('Stop Interaction', toolbar_mouse_event);\n buttongrp.append(button);\n var titlebar = this.root.find($('.ui-dialog-titlebar'));\n titlebar.prepend(buttongrp);\n}\n\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.figure.prototype._canvas_extra_style = function(el){\n // this is important to make the div 'focusable\n el.attr('tabindex', 0)\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n }\n else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n\n}\n\nmpl.figure.prototype._key_event_extra = function(event, name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager)\n manager = IPython.keyboard_manager;\n\n // Check for shift+enter\n if (event.shiftKey && event.which == 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n}\n\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.find_output_cell = function(html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i=0; i= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] == html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n}\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "data": {
- "text/html": [
- ""
- ],
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- },
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Reward = -1189.2667069465801\n"
- ]
- }
- ],
+ "outputs": [],
"source": [
"%matplotlib notebook\n",
+ "#%matplotlib widget\n",
+ "# use %matplotlib widget for execution in visual studio code\n",
"visu = MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10) # visualization\n",
- "env = gem.make('Cont-CC-PMSM-v0',\n",
+ "motor = Motor(MotorType.PermanentMagnetSynchronousMotor,\n",
+ " ControlType.CurrentControl,\n",
+ " ActionType.Continuous)\n",
+ "env = gem.make(motor.env_id(),\n",
" visualization=visu,\n",
" load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30), # Fixed preset speed\n",
- " ode_solver='scipy.solve_ivp', # ODE Solver of the simulation\n",
+ " ode_solver=ScipySolveIvpSolver(), # ODE Solver of the simulation\n",
" reference_generator=reference_generator, # initialize the reference generators\n",
" \n",
" # defenitions for the reward function\n",
@@ -375,16 +352,16 @@
"outputs": [],
"source": [
"controller = Controller.make('mpc', env, ph=3) # initializing the MPC Controller\n",
- "state, reference = env.reset()\n",
+ "(state, reference), _ = env.reset()\n",
"cum_rew = 0\n",
"\n",
"for i in range(10000): # simulate the PMSM for 10000 steps (1 second)\n",
" env.render() # function for visualization\n",
" action = controller.control(state, reference) # calculation of the manipulated variables\n",
- " (state, reference), reward, done, _ = env.step(action) # simulate one step of the PMSM\n",
+ " (state, reference), reward, terminated, truncated, _ = env.step(action) # simulate one step of the PMSM\n",
" cum_rew += reward # adding up the Reward \n",
- " if done:\n",
- " state, reference = env.reset()\n",
+ " if terminated:\n",
+ " (state, reference), _ = env.reset()\n",
" controller.reset()\n",
"print('Reward =', cum_rew)\n",
"env.close()"
@@ -393,7 +370,7 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3",
+ "display_name": "GEM2v0",
"language": "python",
"name": "python3"
},
@@ -407,7 +384,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.5"
+ "version": "3.9.19"
}
},
"nbformat": 4,
diff --git a/examples/observers/state_observer_example.py b/examples/observers/state_observer_example.py
index 443837c3..4ebe9f4f 100644
--- a/examples/observers/state_observer_example.py
+++ b/examples/observers/state_observer_example.py
@@ -13,6 +13,7 @@
from classic_controllers import Controller # noqa: E402
from externally_referenced_state_plot import ExternallyReferencedStatePlot # noqa: E402
+
if __name__ == "__main__":
"""
motor type: 'PermExDc' Permanently Excited DC Motor
@@ -27,7 +28,7 @@
action_type: 'Cont' Continuous Action Space
'Finite' Discrete Action Space
"""
-
+
motor = Motor(
MotorType.PermanentlyExcitedDcMotor,
ControlType.SpeedControl,
@@ -66,7 +67,6 @@
"""
controller = Controller.make(env, external_ref_plots=external_ref_plots)
- # controller = GemController.make(env, env_id=motor.env_id())
(state, reference), _ = env.reset(seed=1337)
print("state_names: ", motor.states())
diff --git a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py
deleted file mode 100644
index bdf36a30..00000000
--- a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py
+++ /dev/null
@@ -1,279 +0,0 @@
-from tensorflow.keras.models import Sequential, Model
-from tensorflow.keras.layers import Dense, Flatten, Input, Concatenate
-from tensorflow.keras import initializers, regularizers
-from tensorflow.keras.optimizers import Adam
-from rl.agents import DDPGAgent
-from rl.memory import SequentialMemory
-from rl.random import OrnsteinUhlenbeckProcess
-from gymnasium.wrappers import FlattenObservation
-import numpy as np
-import sys
-import os
-
-sys.path.append(os.path.abspath(os.path.join("..")))
-import gym_electric_motor as gem
-from gym_electric_motor.reference_generators import (
- MultipleReferenceGenerator,
- ConstReferenceGenerator,
- WienerProcessReferenceGenerator,
-)
-from gym_electric_motor.visualization import MotorDashboard
-from gym_electric_motor.visualization.motor_dashboard_plots import MeanEpisodeRewardPlot
-from gym_electric_motor.physical_systems.mechanical_loads import ConstantSpeedLoad
-from gymnasium.core import Wrapper
-from gymnasium.spaces import Box, Tuple
-from gym_electric_motor.constraints import SquaredConstraint
-from gym_electric_motor.physical_system_wrappers import (
- DqToAbcActionProcessor,
- DeadTimeProcessor,
-)
-
-"""
-This example shows how we can use GEM to train a reinforcement learning agent to control the current within
-a permanent magnet synchronous motor three-phase drive.
-It is assumed that we have direct access to signals within the flux-oriented dq coordinate system.
-Hence, we assume to directly control the current in the dq frame.
-The state and action space is continuous.
-We use a deep-deterministic-policy-gradient (DDPG) agent
-to determine which action must be taken on a continuous-control-set
-"""
-
-
-class AppendLastActionWrapper(Wrapper):
- """
- The following environment considers the dead time in the real-world motor control systems.
- The real-world system changes its state, while the agent simultaneously calculates the next action based on a
- previously measured observation.
- Therefore, for the agents it seems as if the applied action affects the environment with one step delay
- (with a dead time of one time step).
-
- As a measure of feature engineering we append the last selected action to the observation of each time step,
- because this action will be the one that is active while the agent has to make the next decision.
- """
-
- def __init__(self, environment):
- super().__init__(environment)
- # append the action space dimensions to the observation space dimensions
- self.observation_space = Tuple(
- (
- Box(
- np.concatenate(
- (
- environment.observation_space[0].low,
- environment.action_space.low,
- )
- ),
- np.concatenate(
- (
- environment.observation_space[0].high,
- environment.action_space.high,
- )
- ),
- ),
- environment.observation_space[1],
- )
- )
-
- def step(self, action):
- (state, ref), rew, term, info = self.env.step(action)
-
- # extend the output state by the selected action
- state = np.concatenate((state, action))
-
- return (state, ref), rew, term, info
-
- def reset(self, **kwargs):
- state, ref = self.env.reset()
-
- # extend the output state by zeros after reset
- # no action can be appended yet, but the dimension must fit
- state = np.concatenate((state, np.zeros(self.env.action_space.shape)))
-
- return state, ref
-
-
-if __name__ == "__main__":
- # Define reference generators for both currents of the flux oriented dq frame
- # d current reference is chosen to be constantly at zero to simplify this showcase scenario
- d_generator = ConstReferenceGenerator("i_sd", 0)
- # q current changes dynamically
- q_generator = WienerProcessReferenceGenerator(reference_state="i_sq")
-
- # The MultipleReferenceGenerator allows to apply these references simultaneously
- rg = MultipleReferenceGenerator([d_generator, q_generator])
-
- # Set the electric parameters of the motor
- motor_parameter = dict(
- r_s=15e-3, l_d=0.37e-3, l_q=1.2e-3, psi_p=65.6e-3, p=3, j_rotor=0.06
- )
-
- # Change the motor operational limits (important when limit violations can terminate and reset the environment)
- limit_values = dict(i=160 * 1.41, omega=12000 * np.pi / 30, u=450)
-
- # Change the motor nominal values
- nominal_values = {key: 0.7 * limit for key, limit in limit_values.items()}
- physical_system_wrappers = (
- DeadTimeProcessor(),
- DqToAbcActionProcessor.make("PMSM"),
- )
-
- # Create the environment
- env = gem.make(
- # Choose the permanent magnet synchronous motor with continuous-control-set
- "Cont-CC-PMSM-v0",
- # Pass a class with extra parameters
- physical_system_wrappers=physical_system_wrappers,
- visualization=MotorDashboard(
- state_plots=["i_sq", "i_sd"],
- action_plots="all",
- reward_plot=True,
- additional_plots=[MeanEpisodeRewardPlot()],
- ),
- # Set the mechanical load to have constant speed
- load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30),
- # Define which numerical solver is to be used for the simulation
- ode_solver="scipy.ode",
- # Pass the previously defined reference generator
- reference_generator=rg,
- reward_function=dict(
- # Set weighting of different addends of the reward function
- reward_weights={"i_sq": 1000, "i_sd": 1000},
- # Exponent of the reward function
- # Here we use a square root function
- reward_power=0.5,
- ),
- # Define which state variables are to be monitored concerning limit violations
- # Here, only overcurrent will lead to termination
- constraints=(SquaredConstraint(("i_sq", "i_sd")),),
- # Consider converter dead time within the simulation
- # This means that a given action will show effect only with one step delay
- # This is realistic behavior of drive applications
- # Set the DC-link supply voltage
- supply=dict(u_nominal=400),
- motor=dict(
- # Pass the previously defined motor parameters
- motor_parameter=motor_parameter,
- # Pass the updated motor limits and nominal values
- limit_values=limit_values,
- nominal_values=nominal_values,
- ),
- # Define which states will be shown in the state observation (what we can "measure")
- state_filter=["i_sd", "i_sq", "epsilon"],
- )
-
- # Now we apply the wrapper defined at the beginning of this script
- env = AppendLastActionWrapper(env)
-
- # We flatten the observation (append the reference vector to the state vector such that
- # the environment will output just a single vector with both information)
- # This is necessary for compatibility with kerasRL2
- env = FlattenObservation(env)
-
- # Read the dimension of the action space
- # this allows us to define a proper learning agent for this task
- nb_actions = env.action_space.shape[0]
-
- # CAUTION: Do not use layers that behave differently in training and
- # testing
- # (e.g. dropout, batch-normalization, etc..)
- # Reason is a bug in TF2 where not the learning_phase_tensor is extractable
- # in order to put as an input to keras models
- # https://stackoverflow.com/questions/58987264/how-to-get-learning-phase-in-tensorflow-2-eager
- # https://stackoverflow.com/questions/58279628/what-is-the-difference-between-tf-keras-and-tf-python-keras?noredirect=1&lq=1
- # https://github.com/tensorflow/tensorflow/issues/34508
-
- # Define how many past observations we want the control agent to process each step
- # for this case, we assume to pass only the single most recent observation
- window_length = 1
-
- # Define an artificial neural network to be used within the agent as actor
- # (using keras sequential)
- actor = Sequential()
- # The network's input fits the observation space of the env
- actor.add(Flatten(input_shape=(window_length,) + env.observation_space.shape))
- actor.add(Dense(16, activation="relu"))
- actor.add(Dense(17, activation="relu"))
- # The network output fits the action space of the env
- actor.add(
- Dense(
- nb_actions,
- kernel_initializer=initializers.RandomNormal(stddev=1e-5),
- activation="tanh",
- kernel_regularizer=regularizers.l2(1e-2),
- )
- )
- print(actor.summary())
-
- # Define another artificial neural network to be used within the agent as critic
- # note that this network has two inputs
- action_input = Input(shape=(nb_actions,), name="action_input")
- observation_input = Input(
- shape=(window_length,) + env.observation_space.shape, name="observation_input"
- )
- # (using keras functional API)
- flattened_observation = Flatten()(observation_input)
- x = Concatenate()([action_input, flattened_observation])
- x = Dense(32, activation="relu")(x)
- x = Dense(32, activation="relu")(x)
- x = Dense(32, activation="relu")(x)
- x = Dense(1, activation="linear")(x)
- critic = Model(inputs=(action_input, observation_input), outputs=x)
- print(critic.summary())
-
- # Define a memory buffer for the agent, allows to learn from past experiences
- memory = SequentialMemory(limit=5000, window_length=window_length)
-
- # Create a random process for exploration during training
- # this is essential for the DDPG algorithm
- random_process = OrnsteinUhlenbeckProcess(
- theta=0.5,
- mu=0.0,
- sigma=0.1,
- dt=env.physical_system.tau,
- sigma_min=0.05,
- n_steps_annealing=85000,
- size=2,
- )
-
- # Create the agent for DDPG learning
- agent = DDPGAgent(
- # Pass the previously defined characteristics
- nb_actions=nb_actions,
- actor=actor,
- critic=critic,
- critic_action_input=action_input,
- memory=memory,
- random_process=random_process,
- # Define the overall training parameters
- nb_steps_warmup_actor=2048,
- nb_steps_warmup_critic=1024,
- target_model_update=1000,
- gamma=0.9,
- batch_size=128,
- memory_interval=2,
- )
-
- # Compile the function approximators within the agent (making them ready for training)
- # Note that the DDPG agent uses two function approximators, hence we define two optimizers here
- agent.compile([Adam(lr=3e-5), Adam(lr=3e-3)])
-
- # Start training for 75 k simulation steps
- agent.fit(
- env,
- nb_steps=75000,
- nb_max_start_steps=0,
- nb_max_episode_steps=10000,
- visualize=True,
- action_repetition=1,
- verbose=2,
- log_interval=10000,
- callbacks=[],
- )
- # Test the agent
- hist = agent.test(
- env,
- nb_episodes=5,
- action_repetition=1,
- visualize=True,
- nb_max_episode_steps=10000,
- )
diff --git a/examples/reinforcement_learning_controllers/ddpg_series_omega_control.py b/examples/reinforcement_learning_controllers/ddpg_series_omega_control.py
deleted file mode 100644
index 0b9373f0..00000000
--- a/examples/reinforcement_learning_controllers/ddpg_series_omega_control.py
+++ /dev/null
@@ -1,133 +0,0 @@
-"""Run this file from within the 'examples' folder:
->> cd examples
->> python ddpg_series_omega_control.py
-"""
-from tensorflow.keras.models import Sequential, Model
-from tensorflow.keras.layers import Dense, Flatten, Input, Concatenate
-import tensorflow as tf
-from tensorflow.keras.optimizers import Adam
-from rl.agents import DDPGAgent
-from rl.memory import SequentialMemory
-from rl.random import OrnsteinUhlenbeckProcess
-from gymnasium.wrappers import FlattenObservation
-import sys
-import os
-
-sys.path.append(os.path.abspath(os.path.join("..")))
-import gym_electric_motor as gem
-from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator
-from gym_electric_motor.visualization import MotorDashboard
-
-"""
-This example shows how we can use GEM to train a reinforcement learning agent to control the motor speed
-of a DC series motor. The state and action space is continuous.
-We use a deep-deterministic-policy-gradient (DDPG) agent to
-determine which action must be taken on a continuous-control-set
-"""
-
-if __name__ == "__main__":
- # Define the drive environment
- env = gem.make(
- # Define the speed control series DC motor environment with continuous-control-set
- "Cont-SC-SeriesDc-v0",
- )
-
- # For data processing we want to flatten the env output,
- # which means that the env will only output one array that contains states and references consecutively
- state, ref = env.reset()
- env = FlattenObservation(env)
- obs = env.reset()
-
- # Read the dimension of the action space
- # this allows us to define a proper learning agent for this task
- nb_actions = env.action_space.shape[0]
-
- # CAUTION: Do not use layers that behave differently in training and testing
- # (e.g. dropout, batch-normalization, etc..)
- # Reason is a bug in TF2 where not the learning_phase_tensor is extractable
- # in order to put as an input to keras models
- # https://stackoverflow.com/questions/58987264/how-to-get-learning-phase-in-tensorflow-2-eager
- # https://stackoverflow.com/questions/58279628/what-is-the-difference-between-tf-keras-and-tf-python-keras?noredirect=1&lq=1
- # https://github.com/tensorflow/tensorflow/issues/34508
-
- # Define how many past observations we want the control agent to process each step
- # for this case, we assume to pass only the single most recent observation
- window_length = 1
-
- # Define an artificial neural network to be used within the agent as actor
- # (using keras sequential)
- actor = Sequential()
- # The network's input fits the observation space of the env
- actor.add(Flatten(input_shape=(window_length,) + env.observation_space.shape))
- actor.add(Dense(16, activation="relu"))
- actor.add(Dense(16, activation="relu"))
- # The network output fits the action space of the env
- actor.add(Dense(nb_actions, activation="sigmoid"))
- print(actor.summary())
-
- # Define another artificial neural network to be used within the agent as critic
- # note that this network has two inputs
- action_input = Input(shape=(nb_actions,), name="action_input")
- observation_input = Input(
- shape=(window_length,) + env.observation_space.shape, name="observation_input"
- )
- # (using keras functional API)
- flattened_observation = Flatten()(observation_input)
- x = Concatenate()([action_input, flattened_observation])
- x = Dense(32, activation="relu")(x)
- x = Dense(32, activation="relu")(x)
- x = Dense(32, activation="relu")(x)
- x = Dense(1, activation="linear")(x)
- critic = Model(inputs=(action_input, observation_input), outputs=x)
- print(critic.summary())
-
- # Define a memory buffer for the agent, allows to learn from past experiences
- memory = SequentialMemory(limit=10000, window_length=window_length)
-
- # Create a random process for exploration during training
- # this is essential for the DDPG algorithm
- random_process = OrnsteinUhlenbeckProcess(theta=0.5, mu=0.0, sigma=0.2)
-
- # Create the agent for DDPG learning
- agent = DDPGAgent(
- # Pass the previously defined characteristics
- nb_actions=nb_actions,
- actor=actor,
- critic=critic,
- critic_action_input=action_input,
- memory=memory,
- random_process=random_process,
- # Define the overall training parameters
- nb_steps_warmup_actor=2048,
- nb_steps_warmup_critic=1024,
- target_model_update=1000,
- gamma=0.95,
- batch_size=128,
- memory_interval=1,
- )
-
- # Compile the function approximators within the agent (making them ready for training)
- # Note that the DDPG agent uses two function approximators, hence we define two optimizers here
- agent.compile((Adam(lr=1e-6), Adam(lr=1e-4)), metrics=["mae"])
-
- # Start training for 1.5 million simulation steps
- agent.fit(
- env,
- nb_steps=1500000,
- visualize=True,
- action_repetition=1,
- verbose=1,
- nb_max_start_steps=0,
- nb_max_episode_steps=10000,
- log_interval=10000,
- callbacks=[],
- )
-
- # Test the agent
- hist = agent.test(
- env,
- nb_episodes=10,
- action_repetition=1,
- nb_max_episode_steps=5000,
- visualize=True,
- )
diff --git a/examples/reinforcement_learning_controllers/dqn_series_current_control.py b/examples/reinforcement_learning_controllers/dqn_series_current_control.py
deleted file mode 100644
index e48ce011..00000000
--- a/examples/reinforcement_learning_controllers/dqn_series_current_control.py
+++ /dev/null
@@ -1,111 +0,0 @@
-"""Run this file from within the 'examples' folder:
->> cd examples
->> python dqn_series_current_control.py
-"""
-from tensorflow.keras.models import Sequential
-from tensorflow.keras.layers import Dense, Flatten
-from tensorflow.keras.optimizers import Adam
-from rl.agents.dqn import DQNAgent
-from rl.policy import LinearAnnealedPolicy, EpsGreedyQPolicy
-from rl.memory import SequentialMemory
-from gymnasium.wrappers import FlattenObservation
-import sys
-import os
-
-sys.path.append(os.path.abspath(os.path.join("..")))
-import gym_electric_motor as gem
-from gym_electric_motor.reward_functions import WeightedSumOfErrors
-from gym_electric_motor.visualization import MotorDashboard
-from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator
-
-"""
-This example shows how we can use GEM to train a reinforcement learning agent to control the motor current
-of a DC series motor. In this scenario, the state space is continuous while the action space is discrete.
-We use a deep Q learning agent to determine which action must be taken on a finite-control-set
-"""
-
-if __name__ == "__main__":
- # Define the drive environment
- # Default DcSeries Motor Parameters are changed to have more dynamic system and to see faster learning result
- env = gem.make(
- # Define the series DC motor with finite-control-set
- "Finite-CC-SeriesDc-v0",
- # Defines the utilized power converter, which determines the action space
- # 'Disc-1QC' is our notation for a discontinuous one-quadrant converter,
- # which is a one-phase buck converter with available actions 'switch on' and 'switch off'
- converter="Finite-1QC",
- # Define which states will be shown in the state observation (what we can "measure")
- state_filter=["omega", "i"],
- )
-
- # Now, the environment will output states and references separately
- state, ref = env.reset()
-
- # For data processing we sometimes want to flatten the env output,
- # which means that the env will only output one array that contains states and references consecutively
- env = FlattenObservation(env)
- obs = env.reset()
-
- # Read the number of possible actions for the given env
- # this allows us to define a proper learning agent for this task
- nb_actions = env.action_space.n
-
- window_length = 1
-
- # Define an artificial neural network to be used within the agent
- model = Sequential()
- # The network's input fits the observation space of the env
- model.add(Flatten(input_shape=(window_length,) + env.observation_space.shape))
- model.add(Dense(16, activation="relu"))
- model.add(Dense(16, activation="relu"))
- model.add(Dense(4, activation="relu"))
- # The network output fits the action space of the env
- model.add(Dense(nb_actions, activation="linear"))
-
- # Define a memory buffer for the agent, allows to learn from past experiences
- memory = SequentialMemory(limit=15000, window_length=window_length)
-
- # Define the policy which the agent will use for training and testing
- # in this case, we use an epsilon greedy policy with decreasing epsilon for training
- policy = LinearAnnealedPolicy(
- EpsGreedyQPolicy(eps=0.2),
- attr="eps",
- value_max=0.2, # initial value of epsilon (during training)
- value_min=0.01, # final value of epsilon (during training)
- value_test=0, # epsilon during testing, epsilon=0 => deterministic behavior
- nb_steps=20000, # annealing interval
- # (duration for the transition from value_max to value_min)
- )
-
- # Create the agent for deep Q learning
- dqn = DQNAgent(
- # Pass the previously defined characteristics
- model=model,
- policy=policy,
- nb_actions=nb_actions,
- memory=memory,
- # Define the overall training parameters
- gamma=0.9,
- batch_size=128,
- train_interval=1,
- memory_interval=1,
- )
-
- # Compile the model within the agent (making it ready for training)
- # using ADAM optimizer
- dqn.compile(Adam(lr=1e-4), metrics=["mse"])
-
- # Start training the agent
- dqn.fit(
- env,
- nb_steps=200000, # number of training steps
- action_repetition=1,
- verbose=2,
- visualize=True, # use the environment's visualization (the dashboard)
- nb_max_episode_steps=50000, # maximum length of one episode
- # (episodes end prematurely when drive limits are violated)
- log_interval=10000,
- )
-
- # Test the agent (without exploration noise, as we set value_test=0 within our policy)
- dqn.test(env, nb_episodes=3, nb_max_episode_steps=50000, visualize=True)
diff --git a/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb b/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb
deleted file mode 100644
index cfb86a82..00000000
--- a/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb
+++ /dev/null
@@ -1,659 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Training an RL agent with Keras-RL2 Using a GEM Environment\n",
- "\n",
- "This notebook serves as an educational introduction to the usage of Keras-RL2 using a GEM environment. Goal of this notebook is to give an understanding of what Keras-RL2 is and how to use it to train and evaluate a Reinforcement Learning agent which is able to solve a current control problem within the GEM toolbox."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## 1. Installation"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Before you can start you need to make sure that you have installed both, gym-electric-motor and Keras-RL2. You can install both easily using pip:\n",
- "\n",
- "- ```pip install gym-electric-motor```\n",
- "- ```pip install keras-rl2```\n",
- "\n",
- "Alternatively, you can install them and their latest developer version directly from GitHub (recommended) :\n",
- "\n",
- "- https://github.com/upb-lea/gym-electric-motor\n",
- "- https://github.com/wau/keras-rl2\n",
- "\n",
- "For this notebook, the following cell will do the job:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install -q git+https://github.com/upb-lea/gym-electric-motor.git git+https://github.com/wau/keras-rl2.git"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## 2. Setting up a GEM Environment\n",
- "\n",
- "The basic idea behind reinforcement learning is to create a so-called agent, that should learn by itself to solve a specified task in a given environment. \n",
- "This environment gives the agent feedback on its actions and reinforces the targeted behavior.\n",
- "In this notebook, the task is to train a controller for the current control of a *permanent magnet synchronous motor* (*PMSM*).\n",
- " \n",
- "In the following, the used GEM-environment is briefly presented, but this notebook does not focus directly on the detailed usage of GEM. If you are new to the used environment and interested in finding out what it does and how to use it, you should take a look at the [GEM cookbook](https://colab.research.google.com/github/upb-lea/gym-electric-motor/blob/master/examples/example_notebooks/GEM_cookbook.ipynb).\n",
- "\n",
- "The basic idea of the control setup from the GEM-environment is displayed in the following figure. \n",
- "\n",
- "![](../../docs/plots/SCML_Overview.png)\n",
- "\n",
- "The agent controls the converter who converts the supply currents to the currents flowing into the motor - for the *PMSM*: $i_{sq}$ and $i_{sd}$\n",
- "\n",
- "In the continuous case, the agent's action equals a duty cycle which will be modulated into a corresponding voltage. \n",
- "\n",
- "In the discrete case, the agent's actions denote switching states of the converter at the given instant. Here, only a discrete amount of options are available. In this notebook, for the PMSM the *discrete B6 bridge converter* with six switches is utilized per default. This converter provides a total of eight possible actions.\n",
- "\n",
- "![](../../docs/plots/B6.svg)\n",
- "\n",
- "The motor schematic is the following:\n",
- "\n",
- "\n",
- "![](../../docs/plots/ESBdq.svg)\n",
- "\n",
- "And the electrical ODEs for that motor are:\n",
- "\n",
- "