function simulador_segundo_orden() % ========================================================================= % SIMULADOR INTERACTIVO DE UN SISTEMA DE SEGUNDO ORDEN % ------------------------------------------------------------------------- % Versión 2.3 (Ajuste Fino de Layout): % - Corregido el solapamiento de la etiqueta "Factor de Amortiguamiento". % - Se aumentó la altura del control de texto para permitir el ajuste % de línea sin cortes. % - Se refinó el espaciado vertical entre todos los controles. % % Autor: Gemini % ========================================================================= % --- 1. DEFINICIÓN DEL TAMAÑO DE LA VENTANA --- fig_width = 1000; fig_height = 650; 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 de Sistema de Segundo Orden', ... 'Units', 'pixels', 'Position', [fig_pos_x, fig_pos_y, fig_width, fig_height], ... 'NumberTitle', 'off', 'MenuBar', 'none', 'ToolBar', 'none'); % --- 2. Crear el Panel de Controles --- panel_margin = 20; control_panel_width = 300; control_panel_height = fig_height - 2*panel_margin; controlPanel = uipanel(fig, 'Title', 'Parámetros del Sistema', ... 'FontWeight', 'bold', 'Units', 'pixels', ... 'Position', [panel_margin panel_margin control_panel_width control_panel_height]); % --- 3. Crear los Componentes Interactivos --- y_pos = control_panel_height - 50; default_bg_color = get(fig, 'Color'); uicontrol(controlPanel, 'Style', 'text', 'String', 'Ganancia (K)', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 140 22], 'BackgroundColor', default_bg_color); k_label = uicontrol(controlPanel, 'Style', 'text', 'String', '1.0', 'FontSize', 14, 'FontWeight', 'bold', 'HorizontalAlignment', 'right', 'Position', [180 y_pos-5 90 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 30; k_slider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.1, 'Max', 10, 'Value', 1.0, 'Position', [20 y_pos 260 20]); y_pos = y_pos - 60; uicontrol(controlPanel, 'Style', 'text', 'String', 'Tiempo de Estabilización (ts)', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 160 22], 'BackgroundColor', default_bg_color); ts_label = uicontrol(controlPanel, 'Style', 'text', 'String', '4.0 s', 'FontSize', 14, 'FontWeight', 'bold', 'HorizontalAlignment', 'right', 'Position', [180 y_pos-5 90 22], 'BackgroundColor', default_bg_color); y_pos = y_pos - 30; ts_slider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.5, 'Max', 20, 'Value', 4.0, 'Position', [20 y_pos 260 20]); y_pos = y_pos - 70; % Más espacio % ***** CORRECCIÓN DE LAYOUT AQUÍ ***** uicontrol(controlPanel, 'Style', 'text', 'String', 'Factor de Amortiguamiento (ζ)', 'FontWeight', 'bold', 'HorizontalAlignment', 'left', 'Position', [20 y_pos 160 35], 'BackgroundColor', default_bg_color); % Aumentar altura a 35 zeta_label = uicontrol(controlPanel, 'Style', 'text', 'String', '0.5', 'FontSize', 14, 'FontWeight', 'bold', 'HorizontalAlignment', 'right', 'Position', [180 y_pos+8 90 22], 'BackgroundColor', default_bg_color); % Alinear verticalmente y_pos = y_pos - 30; zeta_slider = uicontrol(controlPanel, 'Style', 'slider', 'Min', 0.01, 'Max', 2.0, 'Value', 0.5, 'Position', [20 y_pos 260 20]); % --- 4. Crear Panel de Fórmulas --- formulaPanel = uipanel(controlPanel, 'Title', 'Fórmulas y Valores Calculados', ... 'FontWeight', 'bold', 'Units', 'pixels', ... 'Position', [15 20 270 240]); ax_formula = axes('Parent', formulaPanel, 'Units', 'pixels', 'Position', [10 175 250 50]); axis(ax_formula, 'off'); tf_formula_str = '$$ G(s) = K \frac{\omega_n^2}{s^2 + 2\zeta\omega_n s + \omega_n^2} $$'; text(ax_formula, 0.5, 0.5, tf_formula_str, 'Interpreter', 'latex', 'FontSize', 12, 'HorizontalAlignment', 'center'); wn_formula_label = uicontrol(formulaPanel, 'Style', 'text', 'String', 'ωn ≈ 4 / (ζ * ts) = 2.00 rad/s', 'FontSize', 10, 'HorizontalAlignment', 'left', 'Position', [15 120 240 25], 'BackgroundColor', default_bg_color); mp_formula_label = uicontrol(formulaPanel, 'Style', 'text', 'String', 'Mp (%) = 16.30 %', 'FontSize', 10, 'HorizontalAlignment', 'left', 'Position', [15 80 240 35], 'BackgroundColor', default_bg_color); tp_formula_label = uicontrol(formulaPanel, 'Style', 'text', 'String', 'Tp = 1.81 s', 'FontSize', 10, 'HorizontalAlignment', 'left', 'Position', [15 40 240 35], 'BackgroundColor', default_bg_color); % --- 5. Crear el Panel de Gráficas --- plot_panel_left = control_panel_width + 2*panel_margin; plot_panel_width = fig_width - plot_panel_left - panel_margin; plotPanel = uipanel(fig, 'Title', 'Respuesta al Escalón Unitario', 'FontWeight', 'bold', 'Units', 'pixels', 'Position', [plot_panel_left panel_margin plot_panel_width control_panel_height]); ax = axes('Parent', plotPanel, 'Units', 'normalized', 'Position', [0.1 0.1 0.85 0.85]); % --- 6. Definir la Función de Callback --- function updatePlot(~, ~) K = get(k_slider, 'Value'); ts = get(ts_slider, 'Value'); zeta = get(zeta_slider, 'Value'); set(k_label, 'String', sprintf('%.2f', K)); set(ts_label, 'String', sprintf('%.2f s', ts)); set(zeta_label, 'String', sprintf('%.2f', zeta)); wn = 4 / (zeta * ts); if zeta < 1 Mp = 100 * exp(-pi*zeta / sqrt(1-zeta^2)); Tp = pi / (wn * sqrt(1-zeta^2)); set(mp_formula_label, 'String', sprintf('Mp (%%) = 100 * exp(-πζ/sqrt(1-ζ²)) = %.2f %%', Mp)); set(tp_formula_label, 'String', sprintf('Tp = π / (ωn*sqrt(1-ζ²)) = %.2f s', Tp)); else Mp = 0; Tp = NaN; set(mp_formula_label, 'String', 'Mp (%) = 0 (No hay sobreimpulso)'); set(tp_formula_label, 'String', 'Tp = N/A (No hay sobreimpulso)'); end set(wn_formula_label, 'String', sprintf('ωn ≈ 4 / (ζ * ts) = %.2f rad/s', wn)); s = tf('s'); sys = K * (wn^2 / (s^2 + 2*zeta*wn*s + wn^2)); T_final = max(1.5 * ts, 10 / (zeta * wn)); % Eje de tiempo más robusto info = stepinfo(sys * (1/K)); [y, t] = step(sys, 0:T_final/1000:T_final); cla(ax); plot(ax, t, y, 'b', 'LineWidth', 2, 'DisplayName', 'Respuesta del Sistema'); hold(ax, 'on'); yline(ax, K, 'r--', 'LineWidth', 1.5, 'DisplayName', 'Valor Final (K)'); if Mp > 0 && ~isnan(info.PeakTime) && info.PeakTime > 0 yline(ax, info.Peak*K, 'k:', 'LineWidth', 1.2, 'DisplayName', sprintf('Sobreimpulso (%.1f%%)', info.Overshoot)); xline(ax, info.PeakTime, 'k:', 'LineWidth', 1.2, 'DisplayName', sprintf('Tiempo de Pico (%.2fs)', info.PeakTime)); end hold(ax, 'off'); grid(ax, 'on'); title(ax, sprintf('Sistema de Segundo Orden: K=%.1f, ζ=%.2f, ts=%.1f s', K, zeta, ts)); xlabel(ax, 'Tiempo (s)'); ylabel(ax, 'Amplitud de Salida'); legend(ax, 'Location', 'SouthEast', 'AutoUpdate', 'off'); xlim(ax, [0 T_final]); ylim(ax, [0 max([K * (1 + Mp/100) * 1.1, K*1.2, 0.1])]); end % --- 7. Asignar la función de callback a los sliders --- callback_fcn = @updatePlot; addlistener(k_slider, 'ContinuousValueChange', callback_fcn); addlistener(ts_slider, 'ContinuousValueChange', callback_fcn); addlistener(zeta_slider, 'ContinuousValueChange', callback_fcn); % --- 8. Llamada Inicial para Dibujar la Gráfica --- updatePlot(); end