From 3e4b48d34336efc78d47bb025bf59448e2bc5ec7 Mon Sep 17 00:00:00 2001
From: Quentin Guilloteau <Quentin.Guilloteau@inria.fr>
Date: Tue, 10 Jan 2023 15:32:31 +0100
Subject: [PATCH] more content in jy notebooks

---
 jupyter_notebooks/02_BangBang.ipynb       |  52 ++++++--
 jupyter_notebooks/03_PController.ipynb    | 155 ++++++++++++++++------
 jupyter_notebooks/04_PIController.ipynb   |  51 +++++--
 jupyter_notebooks/05_Identification.ipynb |  56 +++++---
 jupyter_notebooks/06_RealSystem.ipynb     |  46 +++++--
 5 files changed, 270 insertions(+), 90 deletions(-)

diff --git a/jupyter_notebooks/02_BangBang.ipynb b/jupyter_notebooks/02_BangBang.ipynb
index c6cdb3b..05189e1 100644
--- a/jupyter_notebooks/02_BangBang.ipynb
+++ b/jupyter_notebooks/02_BangBang.ipynb
@@ -19,7 +19,8 @@
     "from tuto_control_lib.plot import plot_u_y\n",
     "\n",
     "import matplotlib.pyplot as plt\n",
-    "import numpy as np"
+    "import numpy as np\n",
+    "from statistics import mean"
    ]
   },
   {
@@ -29,13 +30,14 @@
     "tags": []
    },
    "source": [
-    "One way to do this would be to have two bounds for the system output:\n",
+    "One way to do regulate the output of a system would be to have two bounds for the system sensor:\n",
     "\n",
     "- one upper bound\n",
     "- one lower bound\n",
     "\n",
     "When the system output is greater than the upper bound, we decrease the input.\n",
-    "And when the system output is lower than the lower bound, we increse the input."
+    "And when the system output is lower than the lower bound, we increase the input.\n",
+    "Else, we keep the previous input."
    ]
   },
   {
@@ -45,7 +47,14 @@
     "tags": []
    },
    "source": [
-    "Say that we want to regulate our system around the value 6.\n",
+    "Say that we want to regulate our system around the value 1.\n",
+    "\n",
+    "We now have to chose the values of the bounds.\n",
+    "\n",
+    "The issue is that there is no protocol to find the values of the bounds and the incremental part.\n",
+    "\n",
+    "So, we have to proceed by try-and-error.\n",
+    "\n",
     "\n",
     "We can say take as lower bound 5 and as upper bound 7."
    ]
@@ -62,17 +71,18 @@
     "system, u, y_values, u_values, max_iter = IntroSystem(), 0, [], [], 100\n",
     "\n",
     "reference_value = 1\n",
-    "upper_bound = 0.75\n",
-    "lower_bound = 1.25\n",
+    "upper_bound = 0.5\n",
+    "lower_bound = 1.5\n",
+    "increment = 0.5\n",
     "\n",
     "for i in range(max_iter):\n",
     "    y = system.sense()\n",
     "    y_values.append(y)\n",
     "    \n",
     "    if y < lower_bound:\n",
-    "        u += 0.25\n",
+    "        u += increment\n",
     "    elif y > upper_bound:\n",
-    "        u -= 0.25\n",
+    "        u -= increment\n",
     "    else:\n",
     "        pass\n",
     "    system.apply(u)\n",
@@ -88,7 +98,31 @@
     "tags": []
    },
    "source": [
-    "As we can see, the system converges, but not to the reference value..."
+    "As we can see, the system is somewhat under control, but oscillate a lot."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ecdc7121-d253-477d-92dc-4485abd97d29",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "mean_error = mean(map(lambda x: abs(reference_value - x), y_values))\n",
+    "max_overshoot = (max(y_values) - reference_value) /  reference_value\n",
+    "\n",
+    "print(f\"Mean Error: {mean_error}\")\n",
+    "print(f\"Max. Overshoot: {max_overshoot}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2b58c216-0ef8-4d17-8b35-514974b47570",
+   "metadata": {},
+   "source": [
+    "<div class=\"alert alert-info\">\n",
+    "Try changing the values of the bounds and the increment to see the behaviour of the system.\n",
+    "</div>"
    ]
   },
   {
diff --git a/jupyter_notebooks/03_PController.ipynb b/jupyter_notebooks/03_PController.ipynb
index 8c48125..485503f 100644
--- a/jupyter_notebooks/03_PController.ipynb
+++ b/jupyter_notebooks/03_PController.ipynb
@@ -25,12 +25,32 @@
   },
   {
    "cell_type": "markdown",
-   "id": "125b3cfd-1f55-4202-8f50-97d0cafb5b87",
+   "id": "028756f1-641f-4c33-9879-cbb53ee0f256",
    "metadata": {},
    "source": [
+    "We have seen that a Bang-Bang solution manages to roughly get the system in the desired state.\n",
+    "\n",
+    "However, the lack of protocol and guarentees of this solution limits its adoption on production systems.\n",
+    "\n",
+    "In this section, we introduce the most basic controller from Control Theory: the *Proportional Controller*.\n",
+    "\n",
     "The idea of the proportional controller is to have a response proportional to the control error.\n",
     "\n",
-    "The control error is the distance between the desired behaviour and the current state of the system."
+    "The control error is the distance between the desired behaviour and the current state of the system.\n",
+    "\n",
+    "The equation of a P Controller is the following:\n",
+    "\n",
+    "$$\n",
+    "u(k) = K_p \\times e(k) = K_p \\times \\left(y_{ref} - y(k)\\right)\n",
+    "$$\n",
+    "\n",
+    "where:\n",
+    "\n",
+    "- $K_p$ is the propotional gain of the controller\n",
+    "- $y_{ref}$ is the reference value for our system (i.e. the desired value of the system output)\n",
+    "- $y(k)$ is the system output at iteration $k$\n",
+    "- $e(k)$ is the control error at iteration $k$\n",
+    "- $u(k)$ is the input at iteration $k$"
    ]
   },
   {
@@ -40,13 +60,10 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "max_iter = 100\n",
-    "system = IntroSystem()\n",
+    "system, u_values, y_values, u, max_iter = IntroSystem(), [], [], 0, 100\n",
+    "\n",
     "reference_value = 1\n",
     "kp = 3.3\n",
-    "y_values = []\n",
-    "u_values = []\n",
-    "u = 0\n",
     "\n",
     "for i in range(max_iter):\n",
     "    y = system.sense()\n",
@@ -61,6 +78,15 @@
     "plot_u_y(u_values, y_values, reference_value)"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "63203ee3-be90-4e9a-92d4-57a08d042631",
+   "metadata": {},
+   "source": [
+    "As we can see, the system converges, but to a values different than the reference value.\n",
+    "The controller introduces oscillations before converging."
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -68,7 +94,17 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "y_values[-1]"
+    "print(f\"Steady state value: {y_values[-1]}\\nReference value: {reference_value}\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f664e07a-5ea6-4d0f-b826-beb0caeb6c90",
+   "metadata": {},
+   "source": [
+    "<div class=\"alert alert-info\">\n",
+    "Try changing the values of the proportional gain $K_p$ and the reference value.\n",
+    "</div>"
    ]
   },
   {
@@ -79,6 +115,39 @@
     "# Design of a Proportional Controller"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "49936477-c739-476b-a351-c59ec23f9d68",
+   "metadata": {},
+   "source": [
+    "To design a Proportional Controller with guarentees, we must have a model of our system.\n",
+    "\n",
+    "A model, in the sense of Control Theory, is a relation between the inputs and the outputs.\n",
+    "\n",
+    "The general form of a model is the following:\n",
+    "\n",
+    "$$\n",
+    "y(k + 1) = \\sum_{i = 0}^k a_i y(k - i) + \\sum_{i = 0}^k b_i u(k - i)\n",
+    "$$\n",
+    "\n",
+    "where:\n",
+    "\n",
+    "- $y(k + 1)$ is the next value of the output\n",
+    "- $y(k-i)$ and $u(k-i)$ are previous values of the output and the input\n",
+    "- $a_i$ and $b_i$ are the coefficients of the model ($\\forall i, (a_i, b_i) \\in (\\mathbb{R}, \\mathbb{R})$)\n",
+    "\n",
+    "Usually, and to simplify this introduction, we consider *first order models*.\n",
+    "\n",
+    "This means that the model only considers the last values of $y$ and $u$ to get the next value of $y$.\n",
+    "\n",
+    "$$\n",
+    "y(k + 1) = a y(k) + b u(k)\n",
+    "$$\n",
+    "\n",
+    "In this section, we will suppose that we have a first order model which we know the coefficients.\n",
+    "In a [future section](./05_Identification.ipynb), we will look at how to find these coefficients."
+   ]
+  },
   {
    "cell_type": "code",
    "execution_count": null,
@@ -86,7 +155,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "# Our system\n",
     "system = IntroSystem()\n",
+    "# The coefficients\n",
     "a = 0.8\n",
     "b = 0.5"
    ]
@@ -139,31 +210,25 @@
   },
   {
    "cell_type": "markdown",
-   "id": "ff825fc1-825d-4d0f-9172-cc3d25edb83b",
+   "id": "35f5b723-9eb7-4314-8fe1-7989b612e1fe",
    "metadata": {},
    "source": [
     "Proportional Controllers are inheritly imprecise.\n",
-    "But we can tune their precision based on the reference value.\n",
+    "But we can tune their precision ($e_{ss}$) based on the reference value ($r_{ss}$).\n",
     "\n",
     "$$\n",
     "\\begin{aligned}\n",
     "e_{ss} &= r_{ss} ( 1 - F_R(1)) \\\\\n",
-    "       &= r_{ss} \\left(1 - \\frac{b K_p}{1 - (a - b K_p)}\\right)\n",
+    "       &= r_{ss} \\left(1 - \\frac{b K_p}{1 - (a - b K_p)}\\right) < e_{ss}^*\n",
     "\\end{aligned}\n",
-    "$$"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "f86f5dcd-564c-4f73-8156-cc0a5b2c685c",
-   "metadata": {},
-   "source": [
-    "Say we want the steady state error to be less that $e_{target}$.\n",
+    "$$\n",
+    "\n",
+    "Say we want the steady state error to be less that $e_{ss}^*$.\n",
     "\n",
     "Then,\n",
     "\n",
     "$$\n",
-    "K_p > \\frac{\\left(1 - \\frac{e_t}{r_{ss}}\\right)\\left(1 - a\\right)}{b\\frac{e_t}{r_{ss}}}\n",
+    "K_p > \\frac{\\left(1 - \\frac{e_{ss}^*}{r_{ss}}\\right)\\left(1 - a\\right)}{b\\frac{e_{ss}^*}{r_{ss}}}\n",
     "$$\n",
     "\n",
     "In our case:"
@@ -177,9 +242,9 @@
    "outputs": [],
    "source": [
     "r_ss = 1\n",
-    "e_t = 0.15\n",
+    "e_star = 0.15\n",
     "\n",
-    "precision_lower_bound = (1 - e_t/r_ss) * (1 - a)/(b * (e_t/r_ss))\n",
+    "precision_lower_bound = (1 - e_star/r_ss) * (1 - a)/(b * (e_star/r_ss))\n",
     "\n",
     "print(f\"K_p > {precision_lower_bound}\")"
    ]
@@ -197,6 +262,8 @@
    "id": "320e2695-9a0b-4a8d-909b-4385dbebf6af",
    "metadata": {},
    "source": [
+    "The settling time, or the time to reach the steady state value is defined as follows:\n",
+    "\n",
     "$$\n",
     "k_s \\simeq \\frac{-4}{\\log | a - b K_p| }\n",
     "$$\n",
@@ -219,9 +286,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "k_s = 10\n",
-    "settling_time_lower_bound = (a - exp(-4/k_s)) / b\n",
-    "settling_time_upper_bound = (a + exp(-4/k_s)) / b\n",
+    "ks_star = 10\n",
+    "settling_time_lower_bound = (a - exp(-4/ks_star)) / b\n",
+    "settling_time_upper_bound = (a + exp(-4/ks_star)) / b\n",
     "\n",
     "print(f\"{settling_time_lower_bound} < K_p < {settling_time_upper_bound}\")"
    ]
@@ -239,6 +306,9 @@
    "id": "a04de440-6087-4f45-a302-02c0f3bfdd20",
    "metadata": {},
    "source": [
+    "The maximum overshoot is the maximum error above the reference value.\n",
+    "It is defined as:\n",
+    "\n",
     "$$\n",
     "M_p = | a - b K_p|\n",
     "$$\n",
@@ -249,6 +319,8 @@
     "\\frac{a - M_p^*}{b} < K_p < \\frac{a + M_p^*}{b}\n",
     "$$\n",
     "\n",
+    "But we really are only interested in the upper bound.\n",
+    "\n",
     "In our case:"
    ]
   },
@@ -259,8 +331,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "mp = 0.1\n",
-    "max_overshoot_upper_bound = (a + mp) / b\n",
+    "mp_star = 0.1\n",
+    "max_overshoot_upper_bound = (a + mp_star) / b\n",
     "print(f\"K_p < {max_overshoot_upper_bound}\")"
    ]
   },
@@ -294,9 +366,9 @@
    "source": [
     "As we can see, there is no value of $K_p$ that satisfies all the properties.\n",
     "\n",
-    "You can play with the different values defining the desired behavior to find a $K_p$ value that satisfies them.\n",
-    "\n",
-    "The key point is that implementing a Proportional controller requires some **trade-off**!\n",
+    "<div class=\"alert alert-danger\" role=\"alert\">\n",
+    "    The key point is that implementing a Proportional controller requires some <b>trade-off</b>!\n",
+    "</div>\n",
     "\n",
     "In the example above, the value $K_p = 2.5$ seems to statisfy most of the properties."
    ]
@@ -308,10 +380,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "max_iter = 20\n",
     "reference_value = 1\n",
     "kp = 2.5\n",
-    "y_values, u_values, u, system = [], [], 0, IntroSystem()\n",
+    "y_values, u_values, u, system, max_iter = [], [], 0, IntroSystem(), 20\n",
     "\n",
     "for i in range(max_iter):\n",
     "    y = system.sense()\n",
@@ -342,12 +413,12 @@
    "outputs": [],
    "source": [
     "e_ss = reference_value - y_values[-1]\n",
-    "max_overshoot = (max(y_values) - reference_value) /  reference_value\n",
+    "max_overshoot = (max(y_values) - y_values[-1]) / y_values[-1]\n",
     "settling_time = len([x for x in y_values if abs(x - y_values[-1]) > 0.05])\n",
     "\n",
-    "print(f\"Precision: {e_ss} -> desired: < {e_t}\")\n",
-    "print(f\"Settling Time: {settling_time} -> desired: < {k_s}\")\n",
-    "print(f\"Max. Overshoot: {max_overshoot} -> desired: < {mp}\")"
+    "print(f\"Precision: {e_ss} -> desired: < {e_star}\")\n",
+    "print(f\"Settling Time: {settling_time} -> desired: < {ks_star}\")\n",
+    "print(f\"Max. Overshoot: {max_overshoot} -> desired: < {mp_star}\")"
    ]
   },
   {
@@ -358,6 +429,16 @@
     "As expected, the closed loop system overshoots too much, but the other properties are respected."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "01c36250-4482-47d9-a1ab-6451f7470ca7",
+   "metadata": {},
+   "source": [
+    "<div class=\"alert alert-info\" role=\"alert\">\n",
+    "  Try to change the requirements on the closed-loop properties to find different values of $K_p$ and plot the system.\n",
+    "</div>"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "09a14ea2-b496-4d8a-b5d7-155a5b4d60c9",
diff --git a/jupyter_notebooks/04_PIController.ipynb b/jupyter_notebooks/04_PIController.ipynb
index 9ab0629..6c82620 100644
--- a/jupyter_notebooks/04_PIController.ipynb
+++ b/jupyter_notebooks/04_PIController.ipynb
@@ -30,8 +30,10 @@
    "metadata": {},
    "source": [
     "As we have seen before, a Proportional controller is inheritly imprecise.\n",
+    "\n",
     "One way to improve the precision of the closed loop system is to add an integral term to the controller.\n",
-    "The integral term aims at cancel the steady state error.\n",
+    "\n",
+    "The integral term aims at canceling the steady state error.\n",
     "\n",
     "The form of the controller (in discrete time) is the following:\n",
     "\n",
@@ -55,7 +57,7 @@
     "ki = 1.5\n",
     "y_values, u_values, u, system, integral = [], [], 0, IntroSystem(), 0\n",
     "\n",
-    "for i in range(max_iter):\n",
+    "for _ in range(max_iter):\n",
     "    y = system.sense()\n",
     "    y_values.append(y)\n",
     "    \n",
@@ -78,6 +80,16 @@
     "However, there are some oscillations and overshooting..."
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "cb414899-941e-4652-93c1-df6ab939c6f8",
+   "metadata": {},
+   "source": [
+    "<div class=\"alert alert-info\" role=\"alert\">\n",
+    "  Try to change the values of $K_p$ and $K_i$ to observe the change of behavior.\n",
+    "</div>"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "dc185bc1-7b31-41df-8dd3-b260aca6097b",
@@ -92,7 +104,14 @@
    "metadata": {},
    "source": [
     "As for the P Controller, we have to chose the desired closed loop behavior.\n",
-    "In the case of a PI Controller, we have the precision by the integral term.\n",
+    "\n",
+    "In the case of a PI Controller, we have the precision by the integral term, and the precision as for the P Controller.\n",
+    "\n",
+    "There are several methods to find gains for a PI Controller.\n",
+    "In the following we use the *pole placement method*.\n",
+    "The idea is to chose the poles of the closed-loop system to fit the desired behavior.\n",
+    "\n",
+    "Without too much details to avoid being too \"mathy\", we give the equations leading to the gains.\n",
     "\n",
     "Given the desired values for $k_s$ (settling time) and $M_p$ (max. overshoot), we get:\n",
     "\n",
@@ -118,11 +137,13 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "# The coefficients of our system\n",
     "a = 0.8\n",
     "b = 0.5\n",
     "\n",
-    "k_s = 10\n",
-    "mp = 0.05\n",
+    "# Our desired properties\n",
+    "ks = 10\n",
+    "mp = 0.01\n",
     "\n",
     "r = exp(-4/k_s)\n",
     "theta = pi * log(r) / log(mp)\n",
@@ -142,15 +163,15 @@
    "source": [
     "max_iter = 50\n",
     "reference_value = 1\n",
-    "y_values, u_values, u, system, integral = [], [], 0, IntroSystem(), 0\n",
+    "y_values, u_values, u, system, integral_error = [], [], 0, IntroSystem(), 0\n",
     "\n",
     "for i in range(max_iter):\n",
     "    y = system.sense()\n",
     "    y_values.append(y)\n",
     "    \n",
     "    error = reference_value - y\n",
-    "    integral += error\n",
-    "    u = kp * error + ki * integral\n",
+    "    integral_error += error\n",
+    "    u = kp * error + ki * integral_error\n",
     "    \n",
     "    system.apply(u)\n",
     "    u_values.append(u)\n",
@@ -166,14 +187,24 @@
    "outputs": [],
    "source": [
     "e_ss = reference_value - y_values[-1]\n",
-    "max_overshoot = (max(y_values) - reference_value) /  reference_value\n",
+    "max_overshoot = (max(y_values) - y_values[-1]) / y_values[-1]\n",
     "settling_time = len([x for x in y_values if abs(x - y_values[-1]) > 0.05])\n",
     "\n",
     "print(f\"Precision: {e_ss}\")\n",
-    "print(f\"Settling Time: {settling_time} -> desired: < {k_s}\")\n",
+    "print(f\"Settling Time: {settling_time} -> desired: < {ks}\")\n",
     "print(f\"Max. Overshoot: {max_overshoot} -> desired: < {mp}\")"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "id": "41159696-e75e-4ae5-8b69-44799bf482d9",
+   "metadata": {},
+   "source": [
+    "<div class=\"alert alert-info\" role=\"alert\">\n",
+    "  Try to change the requirements on the closed-loop properties to find different values of $K_p$ and $K_i$ and plot the system.\n",
+    "</div>"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "e8e31f05-7d99-4793-801e-51c292c7c8ec",
diff --git a/jupyter_notebooks/05_Identification.ipynb b/jupyter_notebooks/05_Identification.ipynb
index 803378d..92e3fde 100644
--- a/jupyter_notebooks/05_Identification.ipynb
+++ b/jupyter_notebooks/05_Identification.ipynb
@@ -30,15 +30,15 @@
    "metadata": {},
    "source": [
     "For moment, we supposed the model of the system known (i.e. the coeficients $a$ and $b$).\n",
-    "But in practice, we do not know.\n",
+    "But in practice, we do not know them.\n",
     "\n",
-    "In this Section, we will perform what is called the *identification* to get the model of the system.\n",
+    "In this Section, we will perform what is called the *Identification* to get the coefficient of the system model.\n",
     "\n",
     "The idea of the identification phase is simple: \"Get the relation between the input and output\".\n",
     "\n",
     "To do this, the most basic way is to perform a serie of step inputs and observe the output.\n",
     "\n",
-    "In this Section, we will use the `UnknownSystem`."
+    "In this Section, we will use the `UnknownSystem` and try to find its coefficients."
    ]
   },
   {
@@ -48,20 +48,18 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "max_iter = 200\n",
     "system = UnknownSystem()\n",
-    "y_values, u_values, u, system, integral = [], [], 0, UnknownSystem(), 0\n",
-    "#y_values, u_values, u, system, integral = [], [], 0, System(0.314, 0.628), 0\n",
+    "y_values_ident, u_values_ident, u, system, max_iter = [], [], 0, UnknownSystem(), 200\n",
     "for i in range(max_iter):\n",
     "    y = system.sense()\n",
-    "    y_values.append(y)\n",
+    "    y_values_ident.append(y)\n",
     "    \n",
     "    u = (i + 20) // 20\n",
     "    \n",
     "    system.apply(u)\n",
-    "    u_values.append(u)\n",
+    "    u_values_ident.append(u)\n",
     "\n",
-    "plot_u_y(u_values, y_values)"
+    "plot_u_y(u_values_ident, y_values_ident)"
    ]
   },
   {
@@ -69,9 +67,7 @@
    "id": "161a6257-f786-4437-b264-678feadd1863",
    "metadata": {},
    "source": [
-    "Let us first look at the steady state.\n",
-    "\n",
-    "We are looking for an expression of the following form:\n",
+    "We are looking for an expression for a first order system of the following form:\n",
     "\n",
     "$$\n",
     "y(k+1) = a y(k) + b u(k)\n",
@@ -97,12 +93,13 @@
    "source": [
     "previous_u = 1\n",
     "gain_ss = None\n",
-    "for (u, y) in zip(u_values, y_values):\n",
+    "for (u, y) in zip(u_values_ident, y_values_ident):\n",
     "    if u != previous_u:\n",
     "        print(f\"u: {previous_u} -> {gain_ss}\")\n",
     "        previous_u = u\n",
     "    else:\n",
-    "        gain_ss = y / u"
+    "        gain_ss = y / u\n",
+    "print(f\"u: {previous_u} -> {gain_ss}\")"
    ]
   },
   {
@@ -118,7 +115,7 @@
    "id": "3e9f8daf-b9c4-476b-b6f1-c5ea97b74574",
    "metadata": {},
    "source": [
-    "We can try to perform a Least Mean Square to get an estimation of the model:"
+    "We will perform a Least Mean Square to get an estimation of the model:"
    ]
   },
   {
@@ -128,8 +125,8 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "u_values_identification = u_values\n",
-    "y_values_identification = y_values\n",
+    "u_values_identification = u_values_ident\n",
+    "y_values_identification = y_values_ident\n",
     "\n",
     "s1 = sum(y * y for y in y_values_identification)\n",
     "s2 = sum(u * y for (u, y) in zip(u_values_identification, y_values_identification))\n",
@@ -186,7 +183,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "ks = 20\n",
+    "ks = 10\n",
     "mp = 0.05\n",
     "\n",
     "r = exp(-4/ks)\n",
@@ -205,17 +202,16 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "max_iter = 100\n",
     "reference_value = 1\n",
-    "y_values, u_values, u, system, integral = [], [], 0, UnknownSystem(), 0\n",
+    "y_values, u_values, u, system, integral_error, max_iter = [], [], 0, UnknownSystem(), 0, 50\n",
     "\n",
     "for i in range(max_iter):\n",
     "    y = system.sense()\n",
     "    y_values.append(y)\n",
     "    \n",
     "    error = reference_value - y\n",
-    "    integral += error\n",
-    "    u = kp * error + ki * integral\n",
+    "    integral_error += error\n",
+    "    u = kp * error + ki * integral_error\n",
     "    \n",
     "    system.apply(u)\n",
     "    u_values.append(u)\n",
@@ -223,6 +219,22 @@
     "plot_u_y(u_values, y_values, reference_value)"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2759322c-239d-4376-a70a-e9fc6346f0e8",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "e_ss = reference_value - y_values[-1]\n",
+    "max_overshoot = (max(y_values) - y_values[-1]) / y_values[-1]\n",
+    "settling_time = len([x for x in y_values if abs(x - y_values[-1]) > 0.05])\n",
+    "\n",
+    "print(f\"Precision: {e_ss}\")\n",
+    "print(f\"Settling Time: {settling_time} -> desired: < {ks}\")\n",
+    "print(f\"Max. Overshoot: {max_overshoot} -> desired: < {mp}\")"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "d916d270-f6c7-4f0c-9f19-869a9c1aac84",
diff --git a/jupyter_notebooks/06_RealSystem.ipynb b/jupyter_notebooks/06_RealSystem.ipynb
index 8deeddd..4400929 100644
--- a/jupyter_notebooks/06_RealSystem.ipynb
+++ b/jupyter_notebooks/06_RealSystem.ipynb
@@ -10,7 +10,7 @@
   },
   {
    "cell_type": "markdown",
-   "id": "ed9286f1-96d4-4713-8c63-d9c735c8ed63",
+   "id": "e38ada7d-aae2-4ff0-bd34-20d4c1bed366",
    "metadata": {},
    "source": [
     "We provide a semi-real system.\n",
@@ -20,19 +20,41 @@
     "We want to compute an estimation of $\\pi$.\n",
     "One way to do this is to use Monte-Carlo simulations.\n",
     "\n",
-    "The idea of Monte-Carlo simulations is to execute *a lot* of small **independant** simulations and compute the final result based on the results of the simulations."
+    "The idea of Monte-Carlo simulations is to execute **a lot of small and independant simulations** and compute the final result based on the results of the simulations.\n",
+    "\n",
+    "In our case, each simulation we draw a random number $x$ in $[-1, 1]$, and then compute (in a very inefficient way) $\\sqrt{1 - x^2}$.\n",
+    "\n",
+    "The final result is the sum of each simulation, which is an approximation of $\\int_{-1}^1 \\sqrt{1-x^2}dx = \\frac{\\pi}{2}$\n",
+    "\n",
+    "Our sensor is the `loadavg` of the machine. The `loadavg` is a metric representing the CPU utilization.\n",
+    "\n",
+    "Our actuator is the number of threads excuting simulations in parallel (between 0 and 8).\n",
+    "\n",
+    "> **Our control objective is to control the `loadavg` metric around a given value by adapting the number of threads executing simulations.**\n",
+    "\n",
+    "You can run the system with the following `docker` command:\n",
+    "\n",
+    "```sh\n",
+    "docker run --privileged -it -v $(pwd):/data registry.gitlab.inria.fr/control-for-computing/tutorial/system:v0.0 tuto-ctrl main.lua 1000000\n",
+    "```\n",
+    "\n",
+    "The `main.lua` file is the file containing the controller code, and the last parameter is the total number of iterations to do."
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "6d42e211-c4d6-4426-a811-2727a47d5db8",
+   "id": "8a1ea320-905c-4784-94ca-71c622046559",
    "metadata": {},
    "source": [
-    "You can run the system with the following `docker` command:\n",
-    "\n",
-    "```sh\n",
-    "docker run --privileged -it -v $(pwd):/data guilloteauq/tuto-ctrl-docker:july2022 tuto-ctrl main.lua 1000000\n",
-    "```"
+    "<div class=\"alert alert-info\" role=\"alert\">\n",
+    "   Your tasks:\n",
+    "   <ol>\n",
+    "      <li>Play with the system: Try different constant inputs and see the output</li>\n",
+    "      <li>Assuming the underlying model is a first order model: Perform the identification</li>\n",
+    "      <li>Design a PI Controller for this systems</li>\n",
+    "      <li>Introduce pertubations: At some point in your experiment, run `yes` in another terminal to act as a disturbance, and see how your controller reacts.\n",
+    "    </ol> \n",
+    "</div>"
    ]
   },
   {
@@ -40,12 +62,12 @@
    "id": "629824b3-eeb5-40c2-a55b-922def8fdfbd",
    "metadata": {},
    "source": [
-    "The `main.lua` file is the file containing the controller code.\n",
-    "\n",
-    "Why `lua`?\n",
+    "## Why `lua`?\n",
     "\n",
     "Because it is a simple, small language that integrate with `C` easily!\n",
-    "Instead of giving you the source code and asking you to write `C` code to implement the controller, we can just write the controller in `lua` and pass the `lua` file as an argument of the `C` binary to be loaded."
+    "Instead of giving you the source code and asking you to write `C` code to implement the controller, we can just write the controller in `lua` and pass the `lua` file as an argument of the `C` binary to be loaded.\n",
+    "\n",
+    "You can find a `lua` cheat sheet [here](https://devhints.io/lua)."
    ]
   },
   {
-- 
GitLab