{"id":2101,"date":"2023-12-19T21:26:07","date_gmt":"2023-12-19T21:26:07","guid":{"rendered":"https:\/\/garikoitz.info\/blog\/?p=2101"},"modified":"2024-08-02T11:14:34","modified_gmt":"2024-08-02T11:14:34","slug":"el-algoritmo-pid-en-diferentes-lenguajes-de-programacion","status":"publish","type":"post","link":"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/","title":{"rendered":"El algoritmo PID en diferentes lenguajes de programaci\u00f3n"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u00a1Hola a todos los entusiastas de la programaci\u00f3n y la automatizaci\u00f3n! Hoy tengo algo realmente emocionante que compartir con vosotros. Ya sab\u00e9is que tengo una especie de obsesi\u00f3n (\u00a1pero de las buenas!) con el <a href=\"https:\/\/es.wikipedia.org\/wiki\/Controlador_PID\" target=\"_blank\" rel=\"noreferrer noopener\">algoritmo PID<\/a>. Es esa maravilla de la ingenier\u00eda que mantiene todo en equilibrio, desde nuestros termostatos hasta los drones. Bueno, pues cada vez que encuentro un momento libre, me zambullo de cabeza en el mundo de PID, experimentando con peque\u00f1as maquetas y desarrollos para ver qu\u00e9 puedo crear.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pero esta vez, he decidido llevar mi pasi\u00f3n un paso m\u00e1s all\u00e1. \u00bfHab\u00e9is o\u00eddo hablar del ranking <a href=\"https:\/\/pypl.github.io\/PYPL.html\" target=\"_blank\" rel=\"noreferrer noopener\">PYPL<\/a> (<a href=\"https:\/\/pypl.github.io\/PYPL.html\" target=\"_blank\" rel=\"noreferrer noopener\">PopularitY of Programming Language Index<\/a>)? Es como la lista de \u00e9xitos para los lenguajes de programaci\u00f3n, mostr\u00e1ndonos cu\u00e1les son los m\u00e1s utilizados en este loco mundo de la tecnolog\u00eda. As\u00ed que, \u00bfqu\u00e9 mejor manera de celebrar mi amor por el PID y la programaci\u00f3n que combin\u00e1ndolos?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">He preparado algo especial: una implementaci\u00f3n b\u00e1sica del algoritmo PID en los diez primeros lenguajes de programaci\u00f3n del ranking PYPL. \u00a1S\u00ed, hab\u00e9is le\u00eddo bien! Desde Python hasta JavaScript, pasando por Swift y muchos m\u00e1s. Voy a desglosar c\u00f3mo se puede implementar este algoritmo en cada uno de estos lenguajes populares, mostrando sus peculiaridades y sus puntos fuertes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As\u00ed que, si eres un fan\u00e1tico del control autom\u00e1tico o simplemente un curioso de la programaci\u00f3n, te invito a seguirme en esta aventura. Vamos a explorar juntos c\u00f3mo el algoritmo PID puede cobrar vida de formas nuevas y emocionantes. \u00a1Prep\u00e1rate para una mezcla de c\u00f3digo, creatividad y, por supuesto, un poco de diversi\u00f3n t\u00e9cnica!<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">\u00cdndice<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Alternar tabla de contenidos\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 eztoc-toggle-hide-by-default' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#Python\" >Python<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#Java\" >Java<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#Javascript\" >Javascript<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#C\" >C++<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#C-2\" >C#<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#PHP\" >PHP<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#R\" >R<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#Typescript\" >Typescript<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#Swift\" >Swift<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/#Objetive-C\" >Objetive-C<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Python\"><\/span>Python<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">PIDController &lt;- setRefClass(\n  \"PIDController\",\n  fields = list(\n    kp = \"numeric\",\n    ki = \"numeric\",\n    kd = \"numeric\",\n    sample_time = \"numeric\",\n    integral_limit = \"numeric\",\n    last_error = \"numeric\",\n    integral = \"numeric\",\n    last_update_time = \"numeric\"\n  ),\n  methods = list(\n    initialize = function(kp, ki, kd, sample_time, integral_limit) {\n      kp &lt;&lt;- kp\n      ki &lt;&lt;- ki\n      kd &lt;&lt;- kd\n      sample_time &lt;&lt;- sample_time\n      integral_limit &lt;&lt;- integral_limit\n      last_error &lt;&lt;- 0\n      integral &lt;&lt;- 0\n      last_update_time &lt;&lt;- Sys.time()\n    },\n    update = function(setpoint, current_value) {\n      now &lt;- Sys.time()\n      delta_time &lt;- difftime(now, last_update_time, units = \"secs\")\n\n      if (as.numeric(delta_time) >= sample_time) {\n        last_update_time &lt;&lt;- now\n        error &lt;- setpoint - current_value\n        # T\u00e9rmino proporcional\n        P &lt;- kp * error\n        # T\u00e9rmino integral\n        integral &lt;&lt;- integral + error * as.numeric(delta_time)\n        integral &lt;&lt;- min(max(integral, -integral_limit), integral_limit)\n        I &lt;- ki * integral\n        # T\u00e9rmino derivativo\n        derivative &lt;- (error - last_error) \/ as.numeric(delta_time)\n        D &lt;- kd * derivative\n        # Actualizar \u00faltimo error\n        last_error &lt;&lt;- error\n        return(P + I + D)\n      } else {\n        return(0)  # No hay cambio en la salida si no ha pasado el tiempo de muestreo\n      }\n    }\n  )\n)\n\n# Ejemplo de uso del controlador PID\npid &lt;- PIDController$new(kp = 0.1, ki = 0.01, kd = 0.1, sample_time = 1, integral_limit = 10)\nsetpoint &lt;- 100\ncurrent_value &lt;- 90\n# Funci\u00f3n de bucle para simular el control\nloop &lt;- function() {\n  control &lt;- pid$update(setpoint, current_value)\n  print(paste(\"Control output:\", control))\n  Sys.sleep(1)  # Espera un segundo antes de la pr\u00f3xima actualizaci\u00f3n\n}\n# Ejecutar el bucle (detenerlo manualmente)\nwhile(TRUE) {\n  loop()\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Java\"><\/span>Java<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public class PIDController {\n\n    private double kp; \/\/ Coeficiente proporcional\n    private double ki; \/\/ Coeficiente integral\n    private double kd; \/\/ Coeficiente derivativo\n    private long sampleTime; \/\/ Tiempo de muestreo en milisegundos\n    private double integralLimit; \/\/ L\u00edmite para prevenir el windup\n    private double lastError;\n    private double integral;\n    private long lastUpdateTime;\n\n    public PIDController(double kp, double ki, double kd, long sampleTime, double integralLimit) {\n        this.kp = kp;\n        this.ki = ki;\n        this.kd = kd;\n        this.sampleTime = sampleTime;\n        this.integralLimit = integralLimit;\n        this.lastError = 0;\n        this.integral = 0;\n        this.lastUpdateTime = System.currentTimeMillis();\n    }\n\n    public double update(double setpoint, double currentValue) {\n        long now = System.currentTimeMillis();\n        long timeChange = now - lastUpdateTime;\n        if (timeChange >= sampleTime) {\n            lastUpdateTime = now;\n            double error = setpoint - currentValue;\n            \/\/ T\u00e9rmino proporcional\n            double P = kp * error;\n            \/\/ T\u00e9rmino integral\n            integral += error * (timeChange \/ 1000.0); \/\/ Convertir a segundos\n            integral = Math.max(Math.min(integral, integralLimit), -integralLimit);\n            double I = ki * integral;\n            \/\/ T\u00e9rmino derivativo\n            double derivative = (error - lastError) \/ (timeChange \/ 1000.0);\n            double D = kd * derivative;\n            \/\/ Actualizar \u00faltimo error\n            lastError = error;\n            return P + I + D;\n        } else {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n    \/\/ M\u00e9todos getters y setters para kp, ki, kd, etc., si son necesarios\n}\n\npublic class Main {\n    public static void main(String[] args) {\n        PIDController pid = new PIDController(0.1, 0.01, 0.1, 1000, 10.0); \/\/ Ejemplo de par\u00e1metros\n        double setpoint = 100;\n        double currentValue = 90;\n        while (true) {\n            double control = pid.update(setpoint, currentValue);\n            System.out.println(\"Control output: \" + control);\n            \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n            try {\n                Thread.sleep(100); \/\/ Espera un poco antes de la pr\u00f3xima actualizaci\u00f3n\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Javascript\"><\/span>Javascript<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PIDController {\n    constructor(kp, ki, kd, sampleTime, integralLimit) {\n        this.kp = kp; \/\/ Coeficiente proporcional\n        this.ki = ki; \/\/ Coeficiente integral\n        this.kd = kd; \/\/ Coeficiente derivativo\n        this.sampleTime = sampleTime; \/\/ Tiempo de muestreo en segundos\n        this.integralLimit = integralLimit; \/\/ L\u00edmite para prevenir el windup\n        this.currentValue = 0;\n        this.lastError = 0;\n        this.integral = 0;\n        this.lastUpdateTime = Date.now();\n    }\n\n    update(setpoint, currentValue) {\n        const now = Date.now();\n        const deltaTime = (now - this.lastUpdateTime) \/ 1000; \/\/ Convertir a segundos\n        this.lastUpdateTime = now;\n        \/\/ Asegurarse de que estamos actualizando en el tiempo de muestreo establecido\n        if (deltaTime >= this.sampleTime) {\n            const error = setpoint - currentValue;\n            \/\/ T\u00e9rmino proporcional\n            const P = this.kp * error;\n            \/\/ T\u00e9rmino integral\n            this.integral += error * deltaTime;\n            this.integral = Math.max(Math.min(this.integral, this.integralLimit), -this.integralLimit); \/\/ Limitar el t\u00e9rmino integral\n            const I = this.ki * this.integral;\n            \/\/ T\u00e9rmino derivativo\n            const derivative = (error - this.lastError) \/ deltaTime;\n            const D = this.kd * derivative;\n            \/\/ Actualizar \u00faltimo error\n            this.lastError = error;\n            return P + I + D;\n        } else {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n}\n\n\/\/ Ejemplo de uso del controlador PID\nconst pid = new PIDController(0.1, 0.01, 0.1, 1, 10); \/\/ Ejemplo de par\u00e1metros\nconst setpoint = 100;\nlet currentValue = 90;\n\n\/\/ Suponer que esta funci\u00f3n se llama repetidamente, por ejemplo, en un bucle o con setInterval\nfunction loop() {\n    const control = pid.update(setpoint, currentValue);\n    console.log(\"Control output:\", control);\n    \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n}\n\/\/ Suponiendo un bucle de control que se actualiza cada segundo\nsetInterval(loop, 1000);<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"C\"><\/span>C++<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;iostream>\n#include &lt;chrono>\n#include &lt;thread>\n\nclass PIDController {\nprivate:\n    double kp; \/\/ Coeficiente proporcional\n    double ki; \/\/ Coeficiente integral\n    double kd; \/\/ Coeficiente derivativo\n    double sampleTime; \/\/ Tiempo de muestreo en segundos\n    double integralLimit; \/\/ L\u00edmite para prevenir el windup\n    double lastError;\n    double integral;\n    std::chrono::steady_clock::time_point lastUpdateTime;\n\npublic:\n    PIDController(double kp, double ki, double kd, double sampleTime, double integralLimit)\n        : kp(kp), ki(ki), kd(kd), sampleTime(sampleTime), integralLimit(integralLimit),\n          lastError(0), integral(0) {\n        lastUpdateTime = std::chrono::steady_clock::now();\n    }\n    double update(double setpoint, double currentValue) {\n        auto now = std::chrono::steady_clock::now();\n        std::chrono::duration&lt;double> elapsed = now - lastUpdateTime;\n        if (elapsed.count() >= sampleTime) {\n            lastUpdateTime = now;\n            double error = setpoint - currentValue;\n            \/\/ T\u00e9rmino proporcional\n            double P = kp * error;\n            \/\/ T\u00e9rmino integral\n            integral += error * elapsed.count();\n            integral = std::max(std::min(integral, integralLimit), -integralLimit);\n            double I = ki * integral;\n            \/\/ T\u00e9rmino derivativo\n            double derivative = (error - lastError) \/ elapsed.count();\n            double D = kd * derivative;\n            \/\/ Actualizar \u00faltimo error\n            lastError = error;\n            return P + I + D;\n        } else {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n};\n\nint main() {\n    PIDController pid(0.1, 0.01, 0.1, 1.0, 10.0); \/\/ Ejemplo de par\u00e1metros\n    double setpoint = 100;\n    double currentValue = 90;\n\n    while (true) {\n        double control = pid.update(setpoint, currentValue);\n        std::cout &lt;&lt; \"Control output: \" &lt;&lt; control &lt;&lt; std::endl;\n        \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n        std::this_thread::sleep_for(std::chrono::seconds(1)); \/\/ Espera un segundo antes de la pr\u00f3xima actualizaci\u00f3n\n    }\n\n    return 0;\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"C-2\"><\/span>C#<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System;\nusing System.Threading;\n\npublic class PIDController\n{\n    private double kp; \/\/ Coeficiente proporcional\n    private double ki; \/\/ Coeficiente integral\n    private double kd; \/\/ Coeficiente derivativo\n    private int sampleTime; \/\/ Tiempo de muestreo en milisegundos\n    private double integralLimit; \/\/ L\u00edmite para prevenir el windup\n    private double lastError;\n    private double integral;\n    private DateTime lastUpdateTime;\n\n    public PIDController(double kp, double ki, double kd, int sampleTime, double integralLimit)\n    {\n        this.kp = kp;\n        this.ki = ki;\n        this.kd = kd;\n        this.sampleTime = sampleTime;\n        this.integralLimit = integralLimit;\n        this.lastError = 0;\n        this.integral = 0;\n        this.lastUpdateTime = DateTime.Now;\n    }\n\n    public double Update(double setpoint, double currentValue)\n    {\n        var now = DateTime.Now;\n        var timeChange = (now - lastUpdateTime).TotalMilliseconds;\n        if (timeChange >= sampleTime)\n        {\n            lastUpdateTime = now;\n            double error = setpoint - currentValue;\n            \/\/ T\u00e9rmino proporcional\n            double P = kp * error;\n            \/\/ T\u00e9rmino integral\n            integral += error * (timeChange \/ 1000.0); \/\/ Convertir a segundos\n            integral = Math.Max(Math.Min(integral, integralLimit), -integralLimit);\n            double I = ki * integral;\n            \/\/ T\u00e9rmino derivativo\n            double derivative = (error - lastError) \/ (timeChange \/ 1000.0);\n            double D = kd * derivative;\n            \/\/ Actualizar \u00faltimo error\n            lastError = error;\n            return P + I + D;\n        }\n        else\n        {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n}\n\nclass Program\n{\n    static void Main()\n    {\n        PIDController pid = new PIDController(0.1, 0.01, 0.1, 1000, 10.0); \/\/ Ejemplo de par\u00e1metros\n        double setpoint = 100;\n        double currentValue = 90;\n        while (true)\n        {\n            double control = pid.Update(setpoint, currentValue);\n            Console.WriteLine(\"Control output: \" + control);\n            \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n            Thread.Sleep(100); \/\/ Espera un poco antes de la pr\u00f3xima actualizaci\u00f3n\n        }\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"PHP\"><\/span>PHP<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"php\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?php\n\nclass PIDController {\n    private $kp; \/\/ Coeficiente proporcional\n    private $ki; \/\/ Coeficiente integral\n    private $kd; \/\/ Coeficiente derivativo\n    private $sampleTime; \/\/ Tiempo de muestreo en segundos\n    private $integralLimit; \/\/ L\u00edmite para prevenir el windup\n    private $lastError;\n    private $integral;\n    private $lastUpdateTime;\n\n    public function __construct($kp, $ki, $kd, $sampleTime, $integralLimit) {\n        $this->kp = $kp;\n        $this->ki = $ki;\n        $this->kd = $kd;\n        $this->sampleTime = $sampleTime;\n        $this->integralLimit = $integralLimit;\n        $this->lastError = 0;\n        $this->integral = 0;\n        $this->lastUpdateTime = microtime(true);\n    }\n\n    public function update($setpoint, $currentValue) {\n        $now = microtime(true);\n        $timeChange = $now - $this->lastUpdateTime;\n\n        if ($timeChange >= $this->sampleTime) {\n            $this->lastUpdateTime = $now;\n            $error = $setpoint - $currentValue;\n            \/\/ T\u00e9rmino proporcional\n            $P = $this->kp * $error;\n            \/\/ T\u00e9rmino integral\n            $this->integral += $error * $timeChange;\n            $this->integral = max(min($this->integral, $this->integralLimit), -$this->integralLimit);\n            $I = $this->ki * $this->integral;\n            \/\/ T\u00e9rmino derivativo\n            $derivative = ($error - $this->lastError) \/ $timeChange;\n            $D = $this->kd * $derivative;\n            \/\/ Actualizar \u00faltimo error\n            $this->lastError = $error;\n            return $P + $I + $D;\n        } else {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n}\n\n\/\/ Ejemplo de uso del controlador PID\n$pid = new PIDController(0.1, 0.01, 0.1, 1, 10);\n$setpoint = 100;\n$currentValue = 90;\n\/\/ En un entorno de script PHP, el siguiente bucle no ser\u00eda pr\u00e1ctico.\n\/\/ En un entorno de servidor web, la l\u00f3gica de actualizaci\u00f3n deber\u00eda ser gestionada de manera diferente.\nwhile (true) {\n    $control = $pid->update($setpoint, $currentValue);\n    echo \"Control output: \" . $control . \"\\n\";\n    usleep(100000); \/\/ Espera 0.1 segundos (100000 microsegundos)\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"R\"><\/span>R<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"r\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;iostream>\n#include &lt;chrono>\n#include &lt;thread>\n\nclass PIDController {\nprivate:\n    double kp; \/\/ Coeficiente proporcional\n    double ki; \/\/ Coeficiente integral\n    double kd; \/\/ Coeficiente derivativo\n    double sampleTime; \/\/ Tiempo de muestreo en segundos\n    double integralLimit; \/\/ L\u00edmite para prevenir el windup\n    double lastError;\n    double integral;\n    std::chrono::steady_clock::time_point lastUpdateTime;\n\npublic:\n    PIDController(double kp, double ki, double kd, double sampleTime, double integralLimit)\n        : kp(kp), ki(ki), kd(kd), sampleTime(sampleTime), integralLimit(integralLimit),\n          lastError(0), integral(0) {\n        lastUpdateTime = std::chrono::steady_clock::now();\n    }\n\n    double update(double setpoint, double currentValue) {\n        auto now = std::chrono::steady_clock::now();\n        std::chrono::duration&lt;double> elapsed = now - lastUpdateTime;\n        if (elapsed.count() >= sampleTime) {\n            lastUpdateTime = now;\n            double error = setpoint - currentValue;\n            \/\/ T\u00e9rmino proporcional\n            double P = kp * error;\n            \/\/ T\u00e9rmino integral\n            integral += error * elapsed.count();\n            integral = std::max(std::min(integral, integralLimit), -integralLimit);\n            double I = ki * integral;\n            \/\/ T\u00e9rmino derivativo\n            double derivative = (error - lastError) \/ elapsed.count();\n            double D = kd * derivative;\n            \/\/ Actualizar \u00faltimo error\n            lastError = error;\n            return P + I + D;\n        } else {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n};\n\nint main() {\n    PIDController pid(0.1, 0.01, 0.1, 1.0, 10.0); \/\/ Ejemplo de par\u00e1metros\n    double setpoint = 100;\n    double currentValue = 90;\n\n    while (true) {\n        double control = pid.update(setpoint, currentValue);\n        std::cout &lt;&lt; \"Control output: \" &lt;&lt; control &lt;&lt; std::endl;\n        \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n        std::this_thread::sleep_for(std::chrono::seconds(1)); \/\/ Espera un segundo antes de la pr\u00f3xima actualizaci\u00f3n\n    }\n    return 0;\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Typescript\"><\/span>Typescript<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"typescript\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class PIDController {\n    private kp: number; \/\/ Coeficiente proporcional\n    private ki: number; \/\/ Coeficiente integral\n    private kd: number; \/\/ Coeficiente derivativo\n    private sampleTime: number; \/\/ Tiempo de muestreo en milisegundos\n    private integralLimit: number; \/\/ L\u00edmite para prevenir el windup\n    private lastError: number;\n    private integral: number;\n    private lastUpdateTime: number;\n\n    constructor(kp: number, ki: number, kd: number, sampleTime: number, integralLimit: number) {\n        this.kp = kp;\n        this.ki = ki;\n        this.kd = kd;\n        this.sampleTime = sampleTime;\n        this.integralLimit = integralLimit;\n        this.lastError = 0;\n        this.integral = 0;\n        this.lastUpdateTime = Date.now();\n    }\n\n    public update(setpoint: number, currentValue: number): number {\n        const now = Date.now();\n        const deltaTime = now - this.lastUpdateTime;\n        if (deltaTime >= this.sampleTime) {\n            this.lastUpdateTime = now;\n            const error = setpoint - currentValue;\n            \/\/ T\u00e9rmino proporcional\n            const P = this.kp * error;\n            \/\/ T\u00e9rmino integral\n            this.integral += error * (deltaTime \/ 1000); \/\/ Convertir a segundos\n            this.integral = Math.max(Math.min(this.integral, this.integralLimit), -this.integralLimit);\n            const I = this.ki * this.integral;\n            \/\/ T\u00e9rmino derivativo\n            const derivative = (error - this.lastError) \/ (deltaTime \/ 1000);\n            const D = this.kd * derivative;\n            \/\/ Actualizar \u00faltimo error\n            this.lastError = error;\n            return P + I + D;\n        } else {\n            return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n}\n\n\/\/ Ejemplo de uso\nconst pid = new PIDController(0.1, 0.01, 0.1, 1000, 10.0);\nconst setpoint = 100;\nlet currentValue = 90;\n\n\/\/ Simulaci\u00f3n de un bucle de control\nsetInterval(() => {\n    const control = pid.update(setpoint, currentValue);\n    console.log(\"Control output:\", control);\n    \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n}, 100);<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Swift\"><\/span>Swift<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"swift\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import Foundation\n\nclass PIDController {\n    private var kp: Double \/\/ Coeficiente proporcional\n    private var ki: Double \/\/ Coeficiente integral\n    private var kd: Double \/\/ Coeficiente derivativo\n    private var sampleTime: TimeInterval \/\/ Tiempo de muestreo en segundos\n    private var integralLimit: Double \/\/ L\u00edmite para prevenir el windup\n    private var lastError: Double\n    private var integral: Double\n    private var lastUpdateTime: Date\n\n    init(kp: Double, ki: Double, kd: Double, sampleTime: TimeInterval, integralLimit: Double) {\n        self.kp = kp\n        self.ki = ki\n        self.kd = kd\n        self.sampleTime = sampleTime\n        self.integralLimit = integralLimit\n        self.lastError = 0\n        self.integral = 0\n        self.lastUpdateTime = Date()\n    }\n\n    func update(setpoint: Double, currentValue: Double) -> Double {\n        let now = Date()\n        let timeChange = now.timeIntervalSince(lastUpdateTime)\n        if timeChange >= sampleTime {\n            lastUpdateTime = now\n            let error = setpoint - currentValue\n            \/\/ T\u00e9rmino proporcional\n            let P = kp * error\n            \/\/ T\u00e9rmino integral\n            integral += error * timeChange\n            integral = max(min(integral, integralLimit), -integralLimit)\n            let I = ki * integral\n            \/\/ T\u00e9rmino derivativo\n            let derivative = (error - lastError) \/ timeChange\n            let D = kd * derivative\n            \/\/ Actualizar \u00faltimo error\n            lastError = error\n            return P + I + D\n        } else {\n            return 0 \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n        }\n    }\n}\n\n\/\/ Ejemplo de uso\nlet pid = PIDController(kp: 0.1, ki: 0.01, kd: 0.1, sampleTime: 1.0, integralLimit: 10.0)\nlet setpoint = 100.0\nvar currentValue = 90.0\n\n\/\/ Bucle de control (simulaci\u00f3n)\nTimer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in\n    let control = pid.update(setpoint: setpoint, currentValue: currentValue)\n    print(\"Control output: \\(control)\")\n    \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Objetive-C\"><\/span>Objetive-C<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#import &lt;Foundation\/Foundation.h>\n\n@interface PIDController : NSObject {\n    double kp; \/\/ Coeficiente proporcional\n    double ki; \/\/ Coeficiente integral\n    double kd; \/\/ Coeficiente derivativo\n    NSTimeInterval sampleTime; \/\/ Tiempo de muestreo en segundos\n    double integralLimit; \/\/ L\u00edmite para prevenir el windup\n    double lastError;\n    double integral;\n    NSDate *lastUpdateTime;\n}\n\n- (id)initWithKp:(double)kp ki:(double)ki kd:(double)kd sampleTime:(NSTimeInterval)sampleTime integralLimit:(double)integralLimit;\n- (double)updateWithSetpoint:(double)setpoint currentValue:(double)currentValue;\n@end\n@implementation PIDController\n\n- (id)initWithKp:(double)kpValue ki:(double)kiValue kd:(double)kdValue sampleTime:(NSTimeInterval)sampleTimeValue integralLimit:(double)integralLimitValue {\n    self = [super init];\n    if (self) {\n        kp = kpValue;\n        ki = kiValue;\n        kd = kdValue;\n        sampleTime = sampleTimeValue;\n        integralLimit = integralLimitValue;\n        lastError = 0;\n        integral = 0;\n        lastUpdateTime = [NSDate date];\n    }\n    return self;\n}\n\n- (double)updateWithSetpoint:(double)setpoint currentValue:(double)currentValue {\n    NSDate *now = [NSDate date];\n    NSTimeInterval timeChange = [now timeIntervalSinceDate:lastUpdateTime];\n    if (timeChange >= sampleTime) {\n        lastUpdateTime = now;\n        double error = setpoint - currentValue;\n        \/\/ T\u00e9rmino proporcional\n        double P = kp * error;\n        \/\/ T\u00e9rmino integral\n        integral += error * timeChange;\n        integral = MAX(MIN(integral, integralLimit), -integralLimit);\n        double I = ki * integral;\n        \/\/ T\u00e9rmino derivativo\n        double derivative = (error - lastError) \/ timeChange;\n        double D = kd * derivative;\n        \/\/ Actualizar \u00faltimo error\n        lastError = error;\n        return P + I + D;\n    } else {\n        return 0; \/\/ No hay cambio en la salida si no ha pasado el tiempo de muestreo\n    }\n}\n\n@end\n\nint main(int argc, const char * argv[]) {\n    @autoreleasepool {\n        PIDController *pid = [[PIDController alloc] initWithKp:0.1 ki:0.01 kd:0.1 sampleTime:1.0 integralLimit:10.0];\n        double setpoint = 100.0;\n        double currentValue = 90.0;\n        \/\/ Bucle de control (simulaci\u00f3n)\n        while (true) {\n            double control = [pid updateWithSetpoint:setpoint currentValue:currentValue];\n            NSLog(@\"Control output: %f\", control);\n            [NSThread sleepForTimeInterval:0.1]; \/\/ Espera un poco antes de la pr\u00f3xima actualizaci\u00f3n\n            \/\/ Aqu\u00ed ir\u00eda la l\u00f3gica para actualizar el 'currentValue' basado en la salida del controlador\n        }\n    }\n    return 0;\n}<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-purple-color\">La imagen de portada de la entrada ha sido generada con ChatGPT.<\/mark><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&iexcl;Hola a todos los entusiastas de la programaci&oacute;n y la automatizaci&oacute;n! Hoy tengo algo realmente emocionante que compartir con vosotros. Ya sab&eacute;is que tengo una especie de obsesi&oacute;n (&iexcl;pero de las buenas!) con el algoritmo PID. Es esa maravilla de la ingenier&iacute;a que mantiene todo en equilibrio, desde nuestros termostatos hasta los drones. Bueno, pues cada vez que encuentro un momento libre, me zambullo de cabeza en el mundo de PID, experimentando con peque&ntilde;as maquetas y desarrollos para ver qu&eacute;&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/garikoitz.info\/blog\/2023\/12\/el-algoritmo-pid-en-diferentes-lenguajes-de-programacion\/\"> Leer m\u00e1s<span class=\"screen-reader-text\">  Leer m\u00e1s<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":2103,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","wpupg_custom_link":[],"wpupg_custom_link_behaviour":[],"wpupg_custom_link_nofollow":[],"wpupg_custom_image":[],"wpupg_custom_image_id":[],"footnotes":""},"categories":[15,74,210,68],"tags":[209,203,202,13,201,208,204,17,200,205,207,206],"class_list":["post-2101","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-arduino","category-curiosidades","category-programacion","category-raspberry-pi","tag-algoritmo","tag-c-2","tag-c","tag-java","tag-javascript","tag-objetive-c","tag-php","tag-pid","tag-python","tag-r","tag-swift","tag-typescript"],"_links":{"self":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2101","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/comments?post=2101"}],"version-history":[{"count":7,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2101\/revisions"}],"predecessor-version":[{"id":2298,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2101\/revisions\/2298"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/media\/2103"}],"wp:attachment":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/media?parent=2101"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/categories?post=2101"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/tags?post=2101"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}