function simulador_gui_avanzado_v7_layout_final() % ========================================================================= % SIMULADOR INTERACTIVO AVANZADO DE MOTOR DC (GUI PROGRAMÁTICA) % ------------------------------------------------------------------------- % Versión 7.0 (Layout Definitivo): % - Se fijó un tamaño de ventana de 1300x700 para garantizar la % compatibilidad con pantallas de 1366x768. % - Se recalculó y ajustó manualmente la posición de TODOS los % componentes para que quepan perfectamente en la nueva altura, % asegurando que el slider de tiempo de simulación sea siempre visible. % - Esta versión prioriza la consistencia y funcionalidad sobre el % tamaño adaptativo. % % Autor: Gemini % ========================================================================= % --- 1. CÁLCULO DE PARÁMETROS DEL MOTOR --- P_n = 5000; V_n = 240; N_n = 1800; efficiency = 0.90; volt_drop_percent = 0.05; tau_e_ms = 10; omega_n_rads = N_n * (2*pi/60); T_n = P_n / omega_n_rads; P_in = P_n / efficiency; I_n = P_in / V_n; motor.Ra = (V_n * volt_drop_percent) / I_n; motor.Kb = (V_n - I_n * motor.Ra) / omega_n_rads; motor.Kt = motor.Kb; motor.La = (tau_e_ms / 1000) * motor.Ra; motor.J = 0.02; motor.f = 0.01; motor.RPM_max = 2000; motor.T_max = T_n; % --- 2. DEFINICIÓN DEL TAMAÑO DE LA VENTANA --- fig_width = 1300; fig_height = 700; % Altura fija para compatibilidad screen_size = get(groot, 'ScreenSize'); screen_width = screen_size(3); screen_height = screen_size(4); fig_pos_x = (screen_width - fig_width) / 2; fig_pos_y = (screen_height - fig_height) / 2; fig = figure('Name', 'Simulador Avanzado de Accionamiento DC', ... 'Units', 'pixels', 'Position', [fig_pos_x, fig_pos_y, fig_width, fig_height], ... 'NumberTitle', 'off', 'MenuBar', 'none', 'ToolBar', 'none'); % --- 3. Crear el Panel de Controles --- panel_margin = 20; control_panel_width = 350; control_panel_height = fig_height - 2*panel_margin; controlPanel = uipanel(fig, 'Title', 'Controles y Parámetros', ... 'FontWeight', 'bold', 'Units', 'pixels', ... 'Position', [panel_margin panel_margin control_panel_width control_panel_height]); % --- 4. Crear Panel de Información del Motor --- y_pos = control_panel_height - 145; info_panel_width = control_panel_width - 30; infoPanel = uipanel(controlPanel, 'Title', 'Características del Motor', ... 'FontWeight', 'bold', 'Units', 'pixels', ... 'Position', [15 y_pos info_panel_width 130]); infoText = { '--- Datos de Placa ---', ... sprintf('Potencia: %.1f kW, Tensión: %d Vdc', P_n/1000, V_n), ... sprintf('Velocidad: %d RPM, Par Nominal: %.2f Nm', N_n, T_n), ... '', ... '--- Parámetros Calculados ---', ... sprintf('Ra = %.3f Ω, La = %.1f mH', motor.Ra, motor.La*1000), ... sprintf('Kt = %.3f Nm/A, Kb = %.3f V·s/rad', motor.Kt, motor.Kb), ... sprintf('J = %.3f kg·m², f = %.3f Nms/rad', motor.J, motor.f)}; default_bg_color = get(fig, 'Color'); uicontrol(infoPanel, 'Style', 'text', 'String', infoText, ... 'HorizontalAlignment', 'left', 'Units', 'pixels', ... 'Position', [10 5 info_panel_width-20 110], ... 'BackgroundColor', default_bg_color); % --- 5. Crear los Componentes Interactivos (Layout Manual Recalculado) --- y_pos = control_panel_height - 160; % Reiniciar y_pos con margen superior % Referencia de Velocidad y_pos = y_pos - 40; uicontrol(controlPanel, 'Style', 'text', 'String', 'Referencia de Velocidad', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 280 22], 'BackgroundColor', default_bg_color); speedRefLabel = uicontrol(controlPanel, 'Style', 'text', 'String', sprintf('%.0f RPM', N_n), 'FontSize', 14, 'FontWeight', 'bold', 'HorizontalAlignment', 'center', 'Position', [150 y_pos-5 150 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; speedRefSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0, 'Max', motor.RPM_max, 'Value', N_n, 'Position', [20 y_pos 300 20]); y_pos = y_pos - 40; % Par de Carga uicontrol(controlPanel, 'Style', 'text', 'String', 'Par de Carga (TL)', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 280 22], 'BackgroundColor', default_bg_color); loadTorqueLabel = uicontrol(controlPanel, 'Style', 'text', 'String', '0.00 Nm', 'FontSize', 14, 'FontWeight', 'bold', 'HorizontalAlignment', 'center', 'Position', [150 y_pos-5 150 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; loadTorqueSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0, 'Max', motor.T_max, 'Value', 0, 'Position', [20 y_pos 300 20]); y_pos = y_pos - 40; % Sintonización Lazo de Corriente uicontrol(controlPanel, 'Style', 'text', 'String', 'Sintonización Lazo de Corriente', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 280 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; uicontrol(controlPanel, 'Style', 'text', 'String', 'Tiempo Estabilización (ts)', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 150 22], 'BackgroundColor', default_bg_color); tsCurrentLabel = uicontrol(controlPanel, 'Style', 'text', 'String', '0.050 s', 'HorizontalAlignment', 'left', 'Position', [180 y_pos 120 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; tsCurrentSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.005, 'Max', 0.5, 'Value', 0.05, 'Position', [20 y_pos 300 20]); y_pos = y_pos - 30; uicontrol(controlPanel, 'Style', 'text', 'String', 'Factor Amortiguamiento (ζ)', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 150 22], 'BackgroundColor', default_bg_color); zetaCurrentLabel = uicontrol(controlPanel, 'Style', 'text', 'String', '0.707', 'HorizontalAlignment', 'left', 'Position', [180 y_pos 120 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; zetaCurrentSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.1, 'Max', 2.0, 'Value', 0.707, 'Position', [20 y_pos 300 20]); y_pos = y_pos - 40; % Sintonización Lazo de Velocidad uicontrol(controlPanel, 'Style', 'text', 'String', 'Sintonización Lazo de Velocidad', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 280 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; uicontrol(controlPanel, 'Style', 'text', 'String', 'Tiempo Estabilización (ts)', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 150 22], 'BackgroundColor', default_bg_color); tsSpeedLabel = uicontrol(controlPanel, 'Style', 'text', 'String', '1.00 s', 'HorizontalAlignment', 'left', 'Position', [180 y_pos 120 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; tsSpeedSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.1, 'Max', 5, 'Value', 1.0, 'Position', [20 y_pos 300 20]); y_pos = y_pos - 30; uicontrol(controlPanel, 'Style', 'text', 'String', 'Factor Amortiguamiento (ζ)', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 150 22], 'BackgroundColor', default_bg_color); zetaSpeedLabel = uicontrol(controlPanel, 'Style', 'text', 'String', '0.707', 'HorizontalAlignment', 'left', 'Position', [180 y_pos 120 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; zetaSpeedSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.1, 'Max', 2.0, 'Value', 0.707, 'Position', [20 y_pos 300 20]); y_pos = y_pos - 40; % Tiempo de Simulación uicontrol(controlPanel, 'Style', 'text', 'String', 'Tiempo de Simulación', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 280 22], 'BackgroundColor', default_bg_color); timeLabel = uicontrol(controlPanel, 'Style', 'text', 'String', '4.0 s', 'FontSize', 14, 'FontWeight', 'bold', 'HorizontalAlignment', 'center', 'Position', [150 y_pos-5 150 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 25; timeSlider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 1, 'Max', 20, 'Value', 4, 'Position', [20 y_pos 300 20]); % --- 6. Crear el Panel de Gráficas (Derecha) --- plot_panel_left = control_panel_width + 2*panel_margin; plot_panel_width = fig_width - plot_panel_left - panel_margin; plotPanel = uipanel(fig, 'Title', 'Respuesta Dinámica del Sistema', 'FontWeight', 'bold', 'Units', 'pixels', 'Position', [plot_panel_left panel_margin plot_panel_width control_panel_height]); ax_current = axes('Parent', plotPanel, 'Units', 'normalized', 'Position', [0.08 0.55 0.9 0.4]); ax_speed = axes('Parent', plotPanel, 'Units', 'normalized', 'Position', [0.08 0.08 0.9 0.4]); % --- 7. Definir la Función de Callback --- function updatePlot(~, ~) speed_ref_rpm = get(speedRefSlider, 'Value'); T_L = get(loadTorqueSlider, 'Value'); ts_i = get(tsCurrentSlider, 'Value'); zeta_i = get(zetaCurrentSlider, 'Value'); ts_w = get(tsSpeedSlider, 'Value'); zeta_w = get(zetaSpeedSlider, 'Value'); T_final = get(timeSlider, 'Value'); speed_ref_rads = speed_ref_rpm * (2*pi/60); set(speedRefLabel, 'String', sprintf('%.0f RPM', speed_ref_rpm)); set(loadTorqueLabel, 'String', sprintf('%.2f Nm', T_L)); set(tsCurrentLabel, 'String', sprintf('%.3f s', ts_i)); set(zetaCurrentLabel, 'String', sprintf('%.3f', zeta_i)); set(tsSpeedLabel, 'String', sprintf('%.2f s', ts_w)); set(zetaSpeedLabel, 'String', sprintf('%.3f', zeta_w)); set(timeLabel, 'String', sprintf('%.1f s', T_final)); wn_i = 4 / (zeta_i * ts_i); Kp_i = 2 * zeta_i * wn_i * motor.La - motor.Ra; Ki_i = motor.La * wn_i^2; wn_w = 4 / (zeta_w * ts_w); Kp_w = (2 * zeta_w * wn_w * motor.J - motor.f) / motor.Kt; Ki_w = (motor.J * wn_w^2) / motor.Kt; s = tf('s'); C_i = Kp_i + Ki_i/s; C_w = Kp_w + Ki_w/s; D_s = (motor.J*s + motor.f)*(motor.La*s + motor.Ra) + motor.Kt*motor.Kb; G_iv = (motor.J*s + motor.f) / D_s; G_wi = motor.Kt / (motor.J*s + motor.f); G_mech = 1 / (motor.J*s + motor.f); CL_i = feedback(C_i * G_iv, 1); OuterLoop_Plant = CL_i * G_wi; System_tf = feedback(C_w * OuterLoop_Plant, 1); Disturbance_tf = feedback(G_mech, C_w * CL_i * motor.Kt); I_ref_from_w_ref = C_w * (1 - System_tf); I_a_from_w_ref = CL_i * I_ref_from_w_ref; I_ref_from_dist = C_w * Disturbance_tf; I_a_from_dist = CL_i * I_ref_from_dist; t = 0:0.001:T_final; [y_speed_ref, ~] = step(System_tf * speed_ref_rads, t); [y_speed_dist, ~] = step(Disturbance_tf * T_L, t); y_speed_total_rads = y_speed_ref - y_speed_dist; y_speed_total_rpm = y_speed_total_rads * (60/(2*pi)); [y_Iref_ref, ~] = step(I_ref_from_w_ref * speed_ref_rads, t); [y_Iref_dist, ~] = step(I_ref_from_dist * T_L, t); y_Iref_total = y_Iref_ref + y_Iref_dist; [y_Ia_ref, ~] = step(I_a_from_w_ref * speed_ref_rads, t); [y_Ia_dist, ~] = step(I_a_from_dist * T_L, t); y_Ia_total = y_Ia_ref + y_Ia_dist; plot(ax_current, t, y_Iref_total, 'r--', 'LineWidth', 1.5); hold(ax_current, 'on'); plot(ax_current, t, y_Ia_total, 'b', 'LineWidth', 2); hold(ax_current, 'off'); grid(ax_current, 'on'); title(ax_current, 'Dinámica del Lazo de Corriente'); set(ax_current, 'XTickLabel', []); ylabel(ax_current, 'Corriente (A)'); legend(ax_current, 'Referencia (I_{ref})', 'Real (I_a)', 'Location', 'SouthEast'); plot(ax_speed, t, y_speed_total_rpm, 'b', 'LineWidth', 2); hold(ax_speed, 'on'); yline(ax_speed, speed_ref_rpm, 'r--', 'LineWidth', 1.5); hold(ax_speed, 'off'); grid(ax_speed, 'on'); info = stepinfo(y_speed_total_rpm, t, speed_ref_rpm); plotTitle = { 'Dinámica del Lazo de Velocidad', ... sprintf('ts = %.3f s, Sobreimpulso = %.2f %%', info.SettlingTime, info.Overshoot)}; title(ax_speed, plotTitle); xlabel(ax_speed, 'Tiempo (s)'); ylabel(ax_speed, 'Velocidad (RPM)'); legend(ax_speed, 'Velocidad del Motor', 'Referencia', 'Location', 'SouthEast'); linkaxes([ax_current, ax_speed], 'x'); xlim(ax_speed, [0 T_final]); end % --- 8. Asignar la función de callback a los sliders --- callback_fcn = @updatePlot; all_sliders = {speedRefSlider, loadTorqueSlider, tsCurrentSlider, ... zetaCurrentSlider, tsSpeedSlider, zetaSpeedSlider, timeSlider}; for k = 1:numel(all_sliders) addlistener(all_sliders{k}, 'ContinuousValueChange', callback_fcn); end % --- 9. Llamada Inicial para Dibujar la Gráfica --- updatePlot(); end