{"id":2412,"date":"2025-04-02T21:11:14","date_gmt":"2025-04-02T21:11:14","guid":{"rendered":"https:\/\/garikoitz.info\/blog\/?p=2412"},"modified":"2025-04-23T11:57:45","modified_gmt":"2025-04-23T11:57:45","slug":"control-de-velocidad-con-un-motor-de-corriente-continua-simulado","status":"publish","type":"post","link":"https:\/\/garikoitz.info\/blog\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/","title":{"rendered":"Modelado y Control de Velocidad de un Servo Simulado (1\u00aaParte)"},"content":{"rendered":"\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\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Introduccion\" >Introducci\u00f3n<\/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\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#El_Simulador\" >El Simulador<\/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\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Funcionamiento\" >Funcionamiento<\/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\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Control_de_Velocidad\" >Control de Velocidad<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/garikoitz.info\/blog\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Identificacion\" >Identificaci\u00f3n<\/a><\/li><\/ul><\/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\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Codigo_Python\" >C\u00f3digo Python<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/garikoitz.info\/blog\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Graficar_velocidadpy\" >Graficar_velocidad.py<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/garikoitz.info\/blog\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Identificacion_velocidadpy\" >Identificaci\u00f3n_velocidad.py<\/a><\/li><\/ul><\/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\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/#Enlaces\" >Enlaces<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Introduccion\"><\/span>Introducci\u00f3n<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Se trata de un simulador desarrollado por la <a href=\"https:\/\/www.uned.es\/universidad\/inicio\/\" target=\"_blank\" rel=\"noreferrer noopener\">UNED<\/a> para sus <a href=\"https:\/\/unilabs.dia.uned.es\/mod\/data\/view.php?id=1966\" target=\"_blank\" rel=\"noreferrer noopener\">laboratorios virtuales<\/a> los cuales, nos permiten descargar y ejecutar localmente la aplicaci\u00f3n realizada en html+javascript para practicar. El simulador est\u00e1 muy logrado e implementa el funcionamiento de la maqueta con bastante detalle aunque he tenido que modificar parte del c\u00f3digo javascript ya que no funcionaba la parte de guardar los datos. Pod\u00e9is probarlo <a href=\"https:\/\/garikoitz.info\/blog\/descargas\/MotorDC_SIM\/Simulador\/\" target=\"_blank\" rel=\"noreferrer noopener\">aqu\u00ed<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/DCmotor_sim-png.webp\"><img decoding=\"async\" src=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/DCmotor_sim-png.webp\" alt=\"\" class=\"wp-image-2432\"\/><\/a><figcaption class=\"wp-element-caption\">Imagen 1 &#8211; Interfaz del simulador<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"El_Simulador\"><\/span>El Simulador<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">La interfaz es muy intuitiva. En la parte izquierda tenemos el disco graduado simulado. En la parte derecha tenemos las diferentes gr\u00e1ficas para ver la evoluci\u00f3n de los par\u00e1metros y en la parte inferior tenemos los par\u00e1metros del PID y el control de la simulaci\u00f3n. Cada vez que modificamos un par\u00e1metro la caja de texto se pone en amarillo y debemos pulsar la tecla \u00abenter\u00bb para que tenga efecto. Ya que siempre uso unos t\u00e9rminos espec\u00edficos en todas mis maquetas PID os voy a poner la correspondencia de par\u00e1metros aunque entiendo que son muy intuitivos.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ref = SP (Punto de consigna o SetPoint)<\/li>\n\n\n\n<li>U = OP (en t\u00e9rminos acad\u00e9micos la U siempre se refiere a la salida del controlador)<\/li>\n\n\n\n<li>Kp = Kc (creo m\u00e1s acertado llamar Kc a la ganancia del controlador)<\/li>\n\n\n\n<li>Posici\u00f3n = PV (en el control de posici\u00f3n)<\/li>\n\n\n\n<li>Velocidad = PV (en el control de velocidad)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Funcionamiento\"><\/span>Funcionamiento<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Disponemos de un servo de 5 voltios que en su eje lleva acoplado un disco graduado entre 0 y 360 grados. El voltaje aplicado sobre el motor hace que \u00e9ste gire en un sentido de forma continua pudi\u00e9ndose invertir la polaridad para que el motor gire en sentido contrario. A medida que aplicamos m\u00e1s voltaje, m\u00e1s r\u00e1pido gira el disco.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Control_de_Velocidad\"><\/span>Control de Velocidad<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Atendiendo al simulador, el rango de la velocidad (PV) va de -30 a 30 \u00ba\/s y el voltaje del motor (OP) va de -5 a 5 V.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Identificacion\"><\/span>Identificaci\u00f3n<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Con el control en manual y en reposo introducimos un voltaje y observamos la respuesta de la velocidad. Por suerte para nosotros la velocidad muestra una respuesta de primer orden, o dicho de otro modo, estamos antes una respuesta autorregulada.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/Ident_v1-png.webp\"><img decoding=\"async\" src=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/Ident_v1-1024x512.webp\" alt=\"\" class=\"wp-image-2434\"\/><\/a><figcaption class=\"wp-element-caption\">Imagen 2 &#8211; StepTest. Imagen creada con el script Graficar_velocidad.py<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Aprovechando que estamos ante una respuesta autorregulada vamos a realizar la identificaci\u00f3n del sistema mediante el m\u00e9todo de T1 y T2 para hallar T<sub>0<\/sub> y T<sub>p<\/sub>. Tanto para generar la gr\u00e1fica PV\/OP como para la identificaci\u00f3n he creado dos sencillos scripts de Python que os dejo en la <a href=\"https:\/\/garikoitz.info\/blog\/descargas\/MotorDC_SIM\/\" target=\"_blank\" rel=\"noreferrer noopener\">carpeta del proyecto<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><a href=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/Ident_v1_identificado-png.webp\"><img decoding=\"async\" src=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/Ident_v1_identificado-1024x614.webp\" alt=\"\" class=\"wp-image-2436\"\/><\/a><figcaption class=\"wp-element-caption\">Imagen 3 &#8211; Identificaci\u00f3n<\/figcaption><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">Una caracter\u00edstica interesante de esta identificaci\u00f3n es la ausencia de tiempo muerto. Si os fij\u00e1is en la imagen 3, pod\u00e9is apreciar que en el momento en que se realiza el salto en la OP comienza a reaccionar la PV. En cualquier proceso existe cierto retardo aunque en esta simulaci\u00f3n no se ha tenido en cuenta. En casos as\u00ed, lo correcto es tomar el tiempo de muestreo como tiempo muerto y es de ah\u00ed de donde salen los 50 ms de T<sub>0<\/sub>.  Las sinton\u00edas calculadas con estas constantes dan resultados con ganancias muy grandes lo que hace que tengamos que descartar los \u00abm\u00e9todos cl\u00e1sicos\u00bb como Ziegler Nichols o Cohen Coon.<\/p>\n\n\n\n<figure class=\"wp-block-table aligncenter\"><table><tbody><tr><td class=\"has-text-align-right\" data-align=\"right\"><strong>Kp<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\">0,95<\/td><td>%\/%<\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\"><strong>T<sub>0<\/sub><\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\">0,05<\/td><td>segundos<\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\"><strong>T<sub>p<\/sub><\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\">0,52<\/td><td>segundos<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Por tanto, el mejor ajuste lo podemos obtener con las sinton\u00edas Lambda e IMC, ya que mediante la constante de tiempo deseado en lazo cerrado (\u03bb o Tf) podemos retocar la respuesta seg\u00fan nuestras necesidades.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\"><strong>Lambda<\/strong><\/td><td class=\"has-text-align-center\" data-align=\"center\">Tf=10s<br>Conservadora<\/td><td class=\"has-text-align-center\" data-align=\"center\">Tf=5s<br>Normal<\/td><td class=\"has-text-align-center\" data-align=\"center\">Tf=2s<br>Agresiva<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Kc<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.05<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.10<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.25<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Ti<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.52s<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.52s<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.52s<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">Td<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.05s<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.05s<\/td><td class=\"has-text-align-center\" data-align=\"center\">0.05s<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">La prueba de sinton\u00edas confirma lo esperado. La sinton\u00eda conservadora es algo lenta, la normal tiene una respuesta suave y precisa, y la agresiva presenta sobrepasamiento. La prueba de sinton\u00edas ha consistido en 4 cambi\u00f3s del punto de consigna de 10\u00ba\/s.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/04\/DCMotorSIM_vel_20250402T194703-1-png.webp\"><img decoding=\"async\" src=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/04\/DCMotorSIM_vel_20250402T194703-1-png.webp\" alt=\"\" class=\"wp-image-2457\"\/><\/a><figcaption class=\"wp-element-caption\">Imagen 4 &#8211; Prueba de sinton\u00edas<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Codigo_Python\"><\/span>C\u00f3digo Python<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Graficar_velocidadpy\"><\/span>Graficar_velocidad.py<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Simplemente especificar el nombre del archivo y el script os devolver\u00e1 una imagen png como la imagen 2.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"7\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import re\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\n\n# Ruta al archivo .m\nruta_archivo = \"Ident_v1.m\"\n\n# Leer el contenido del archivo\nwith open(ruta_archivo, \"r\", encoding=\"utf-8\") as f:\n    contenido = f.read()\n\n# Buscar el bloque que contiene los datos\ncoincidencias = re.findall(r\"data\\s*=\\s*\\[([\\s\\S]+?)\\];\", contenido)\n\nif not coincidencias:\n    raise ValueError(\"No se encontr\u00f3 el bloque de datos en el archivo.\")\n\n# Procesar las filas de datos\nfilas = coincidencias[0].strip().split(\"\\n\")\ndatos = np.array([[float(valor) for valor in fila.strip().split()] for fila in filas])\n\n# Separar columnas\nt, th, w, u, ref = datos.T\n\n# Nombre del archivo de salida PNG (mismo nombre que el .m)\nnombre_base = os.path.splitext(os.path.basename(ruta_archivo))[0]\nnombre_png = f\"{nombre_base}.png\"\n\n# Crear la gr\u00e1fica (solo velocidad y control)\nplt.figure(figsize=(12, 6))\n\nplt.subplot(2, 1, 1)\nplt.plot(t, w, color='orange', label='PV')\nplt.ylabel('PV (\u00b0\/s)')\nplt.legend()\nplt.grid(True)\n\nplt.subplot(2, 1, 2)\nplt.plot(t, u, color='green', label='OP')\nplt.xlabel('Tiempo (s)')\nplt.ylabel('OP (V)')\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.savefig(nombre_png, dpi=300)\nplt.close()\n\nprint(f\"Gr\u00e1fica guardada como {nombre_png}\")<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Identificacion_velocidadpy\"><\/span>Identificaci\u00f3n_velocidad.py<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Especificar en el script el tiempo inicial y final del salto a analizar y los rangos. Al ejecutar el script os aparecer\u00e1 un cuadro de di\u00e1logo para elegir el archivo *.m de los datos y os devolver\u00e1 una imagen png como la imagen 4 y un txt con la identificaci\u00f3n.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"17-19,21-25\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import re\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os\nfrom tkinter import Tk, filedialog\n\n# --- OpenFileDialog para seleccionar el archivo .m ---\nTk().withdraw()\nruta_archivo = filedialog.askopenfilename(\n    title=\"Selecciona el archivo .m\",\n    filetypes=[(\"Archivos MATLAB\", \"*.m\")]\n)\n\nif not ruta_archivo:\n    raise FileNotFoundError(\"No se seleccion\u00f3 ning\u00fan archivo.\")\n\n# --- CONFIGURACI\u00d3N ---\nt_inicial = 37.5\nt_final = 42.5\n\n# Rango de operaci\u00f3n\nPV_min = -30\nPV_max = 30\nOP_min = -5\nOP_max = 5\n\n# Leer archivo\nwith open(ruta_archivo, \"r\", encoding=\"utf-8\") as f:\n    contenido = f.read()\n\ncoincidencias = re.findall(r\"data\\s*=\\s*\\[([\\s\\S]+?)\\];\", contenido)\nif not coincidencias:\n    raise ValueError(\"No se encontr\u00f3 el bloque de datos en el archivo.\")\n\nfilas = coincidencias[0].strip().split(\"\\n\")\ndatos = np.array([[float(valor) for valor in fila.strip().split()] for fila in filas])\nt, th, w, u, ref = datos.T\n\nTs = np.mean(np.diff(t))\n\nmask = (t >= t_inicial) &amp; (t &lt;= t_final)\nt_sel = t[mask]\nw_sel = w[mask]\nu_sel = u[mask]\n\ndelta_u = np.diff(u_sel)\nidx_salto = np.where(np.abs(delta_u) > 0.1)[0]\nif len(idx_salto) == 0:\n    raise ValueError(\"No se detect\u00f3 un escal\u00f3n en el rango seleccionado.\")\n\ni_salto = idx_salto[0] + 1\nt0 = t_sel[i_salto]\nu_ini, u_fin = u_sel[i_salto - 1], u_sel[i_salto]\nw_ini = w_sel[i_salto]\nw_resp = w_sel[i_salto:]\nt_resp = t_sel[i_salto:]\n\ndelta_u_val = u_fin - u_ini\ndelta_w_val = w_resp[-1] - w_ini\nKp = delta_w_val \/ delta_u_val\nKp_norm = (delta_w_val \/ (PV_max - PV_min)) \/ (delta_u_val \/ (OP_max - OP_min))\n\nPV28 = w_ini + 0.283 * delta_w_val\nPV63 = w_ini + 0.632 * delta_w_val\n\nidx_t1 = np.argmax(w_resp > PV28)\nidx_t2 = np.argmax(w_resp > PV63)\n\nT1_abs = t0 + idx_t1 * Ts\nT2_abs = t0 + idx_t2 * Ts\n\nTp = 1.5 * (T2_abs - T1_abs)\nT0_abs = T2_abs - Tp\nT0_rel = T0_abs - t0\nT0_mostrar = Ts if T0_rel &lt; 3 * Ts else T0_rel\n\n# Sinton\u00edas Lambda\nTi = Tp\nTf1, Tf2, Tf3 = 4 * Tp, 10 * Tp, 20 * Tp\n\n# PI\nKc_PI_1 = Tp \/ (Kp_norm * (Tf1 + T0_mostrar))\nKc_PI_2 = Tp \/ (Kp_norm * (Tf2 + T0_mostrar))\nKc_PI_3 = Tp \/ (Kp_norm * (Tf3 + T0_mostrar))\n\n# PID\nKc_PID_1 = (Tp \/ (Kp_norm * (Tf1 + T0_mostrar))) * ((Tf1 + T0_mostrar) \/ (Tf1 + 0.5 * T0_mostrar))\nTd1 = (T0_mostrar * Tf1) \/ (Tf1 + 0.5 * T0_mostrar)\n\nKc_PID_2 = (Tp \/ (Kp_norm * (Tf2 + T0_mostrar))) * ((Tf2 + T0_mostrar) \/ (Tf2 + 0.5 * T0_mostrar))\nTd2 = (T0_mostrar * Tf2) \/ (Tf2 + 0.5 * T0_mostrar)\n\nKc_PID_3 = (Tp \/ (Kp_norm * (Tf3 + T0_mostrar))) * ((Tf3 + T0_mostrar) \/ (Tf3 + 0.5 * T0_mostrar))\nTd3 = (T0_mostrar * Tf3) \/ (Tf3 + 0.5 * T0_mostrar)\n\n# GRAFICAR\nplt.figure(figsize=(10, 6))\nplt.subplot(2, 1, 1)\nplt.plot(t_sel, w_sel, label='PV', color='orange')\nplt.axvline(t0, color='gray', linestyle='--', label='Escal\u00f3n')\nplt.axvline(T1_abs, color='blue', linestyle=':', label='T1 (PV28.3%)')\nplt.axvline(T2_abs, color='red', linestyle=':', label='T2 (PV63.2%)')\nplt.axhline(PV28, color='blue', linestyle='--')\nplt.axhline(PV63, color='red', linestyle='--')\nplt.ylabel('PV (\u00b0\/s)')\nplt.legend()\nplt.grid(True)\n\nplt.subplot(2, 1, 2)\nplt.plot(t_sel, u_sel, label='OP', color='green')\nplt.xlabel('Tiempo (s)')\nplt.ylabel('OP (V)')\nplt.grid(True)\nplt.legend()\nplt.tight_layout()\n\n# Guardar archivos\nbase_name = os.path.splitext(os.path.basename(ruta_archivo))[0]\nnombre_base = f\"{base_name}_identificado\"\nplt.savefig(f\"{nombre_base}.png\", dpi=300)\nplt.close()\n\n# TXT\nwith open(f\"{nombre_base}.txt\", \"w\", encoding=\"utf-8\") as ftxt:\n    ftxt.write(\"------------------------------------------------------------\\n\")\n    ftxt.write(\"ACLARACI\u00d3N SOBRE UNIDADES Y NORMALIZACI\u00d3N\\n\")\n    ftxt.write(\"------------------------------------------------------------\\n\")\n    ftxt.write(\"- El modelo del sistema se identifica en unidades de ingenier\u00eda reales:\\n\")\n    ftxt.write(\"    * Velocidad (PV) en grados\/segundo [\u00b0\/s]\\n\")\n    ftxt.write(\"    * Se\u00f1al de control (OP) en voltios [V]\\n\")\n    ftxt.write(\"    * Tiempos en segundos [s]\\n\")\n    ftxt.write(\"- Para calcular las sinton\u00edas, se normaliza usando %\/%\\n\")\n    ftxt.write(\"------------------------------------------------------------\\n\\n\")\n\n    ftxt.write(\"--C\u00c1LCULOS--\\n\")\n    ftxt.write(f\"\u0394OP = {delta_u_val:.3f} V\\n\")\n    ftxt.write(f\"\u0394PV = {delta_w_val:.3f} \u00b0\/s\\n\")\n    ftxt.write(f\"PV28 = {PV28:.3f} \u00b0\/s\\n\")\n    ftxt.write(f\"PV63 = {PV63:.3f} \u00b0\/s\\n\")\n    ftxt.write(f\"T1 = {T1_abs:.4f} s\\n\")\n    ftxt.write(f\"T2 = {T2_abs:.4f} s\\n\")\n    ftxt.write(f\"T0 absoluto = {T0_abs:.4f} s\\n\\n\")\n\n    ftxt.write(\"--SISTEMA--\\n\")\n    ftxt.write(f\"Kp_EU  = {Kp:.4f}\\n\")\n    ftxt.write(f\"Kp_%\/% = {Kp_norm:.4f}\\n\")\n    ftxt.write(f\"T0 = {T0_mostrar:.4f} s (tiempo muerto desde el escal\u00f3n)\\n\")\n    ftxt.write(f\"Tp = {Tp:.4f} s\\n\\n\")\n\n    ftxt.write(\"--SINTON\u00cdA LAMBDA--\\n\")\n    ftxt.write(f\"Kc = {Kc_PI_1:.4f}, Ti = {Tp:.4f} Td = 0 (Tp*4)\\n\")\n    ftxt.write(f\"Kc = {Kc_PI_2:.4f}, Ti = {Tp:.4f} Td = 0 (Tp*10)\\n\")\n    ftxt.write(f\"Kc = {Kc_PI_3:.4f}, Ti = {Tp:.4f} Td = 0 (Tp*20)\\n\\n\")\n\n    ftxt.write(f\"Kc = {Kc_PID_1:.4f}, Ti = {Tp:.4f} Td = {Td1:.4f} (Tp*4)\\n\")\n    ftxt.write(f\"Kc = {Kc_PID_2:.4f}, Ti = {Tp:.4f} Td = {Td2:.4f} (Tp*10)\\n\")\n    ftxt.write(f\"Kc = {Kc_PID_3:.4f}, Ti = {Tp:.4f} Td = {Td3:.4f} (Tp*20)\\n\")\n\nprint(f\"Archivos generados: {nombre_base}.png y {nombre_base}.txt\")\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Enlaces\"><\/span>Enlaces<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Simulador [<a href=\"https:\/\/garikoitz.info\/blog\/descargas\/MotorDC_SIM\/Simulador\/\" target=\"_blank\" rel=\"noreferrer noopener\">garikoitz.info<\/a>]<\/li>\n\n\n\n<li>Carpeta del Proyecto [<a href=\"https:\/\/garikoitz.info\/blog\/descargas\/MotorDC_SIM\/\" target=\"_blank\" rel=\"noreferrer noopener\">garikoitz.info<\/a>]<\/li>\n\n\n\n<li>Respuesta autorregulada [<a href=\"https:\/\/garikoitz.info\/blog\/2022\/05\/introduccion-al-algoritmo-pid-y-su-implementacion-en-arduino\/#Respuesta_autorregulada\" target=\"_blank\" rel=\"noreferrer noopener\">garikoitz.info<\/a>]<\/li>\n\n\n\n<li>Identificaci\u00f3n de procesos en lazo abierto [<a href=\"https:\/\/garikoitz.info\/blog\/2022\/05\/introduccion-al-algoritmo-pid-y-su-implementacion-en-arduino\/#En_lazo_abierto\" target=\"_blank\" rel=\"noreferrer noopener\">garikoitz.info<\/a>]<\/li>\n\n\n\n<li>Laboratorio virtuales [<a href=\"https:\/\/unilabs.dia.uned.es\/mod\/data\/view.php?id=1966\" target=\"_blank\" rel=\"noreferrer noopener\">Unilabs<\/a>] [<a href=\"https:\/\/unilabs.dia.uned.es\/pluginfile.php\/10549\/mod_data\/content\/145\/ServoMotor.zip\">Simulador<\/a>]<\/li>\n\n\n\n<li>Modelado y Control de Posici\u00f3n de un servo simulado (2\u00aaparte) [<a href=\"https:\/\/garikoitz.info\/blog\/2025\/04\/modelado-y-control-de-posicion-de-un-servo-simulado\/\" target=\"_blank\" rel=\"noreferrer noopener\">garikoitz.info<\/a>]<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducci&oacute;n Se trata de un simulador desarrollado por la UNED para sus laboratorios virtuales los cuales, nos permiten descargar y ejecutar localmente la aplicaci&oacute;n realizada en html+javascript para practicar. El simulador est&aacute; muy logrado e implementa el funcionamiento de la maqueta con bastante detalle aunque he tenido que modificar parte del c&oacute;digo javascript ya que no funcionaba la parte de guardar los datos. Pod&eacute;is probarlo aqu&iacute;. El Simulador La interfaz es muy intuitiva. En la parte izquierda tenemos el disco&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/garikoitz.info\/blog\/2025\/04\/control-de-velocidad-con-un-motor-de-corriente-continua-simulado\/\"> Leer m\u00e1s<span class=\"screen-reader-text\">  Leer m\u00e1s<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":2419,"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":[213,210],"tags":[232,201,159,122,17,200,233,235,236],"class_list":["post-2412","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pid","category-programacion","tag-integrador","tag-javascript","tag-lambda","tag-pi","tag-pid","tag-python","tag-simulador","tag-uned","tag-unilabs"],"_links":{"self":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2412","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=2412"}],"version-history":[{"count":47,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2412\/revisions"}],"predecessor-version":[{"id":2493,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2412\/revisions\/2493"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/media\/2419"}],"wp:attachment":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/media?parent=2412"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/categories?post=2412"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/tags?post=2412"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}