36.1 Objetivo del proyecto
El laboratorio permitirá elegir entre un sistema masa-resorte y un péndulo simple no lineal, modificar parámetros, seleccionar un integrador y observar la evolución del estado y la energía.
El objetivo no es reunir fórmulas en una sola función, sino construir una aplicación donde cada parte tenga una responsabilidad clara y pueda comprobarse por separado.
36.2 Requisitos funcionales
- Elegir el modelo físico y configurar sus parámetros.
- Definir posición y velocidad iniciales.
- Usar Euler semimplícito o RK4 con paso fijo.
- Iniciar, pausar, reiniciar y avanzar manualmente.
- Dibujar el sistema y una historia temporal.
- Mostrar energía, escala natural, paso y cantidad de actualizaciones.
- Detectar entradas inválidas y estados no finitos.
36.3 Alcance físico
El masa-resorte utiliza:
ω₀ = √(k/m)
El péndulo utiliza:
ω₀ = √(g/L)
ζ es una razón de amortiguamiento configurable. No se incluyen choques, elasticidad no lineal del resorte, soporte móvil ni cuerda floja.
36.4 Arquitectura en cuatro partes
- Modelo: define aceleración, energía, escala y etiquetas.
- Integrador: transforma un estado actual en el siguiente.
- Motor: administra tiempo, paso fijo, historial y ejecución.
- Vista y control: dibuja y recibe acciones del usuario.
El motor no necesita saber si q representa metros o radianes; delega la física al modelo seleccionado.
36.5 Una interfaz común para el estado
{ tiempoS, coordenada, velocidad }En el resorte, coordenada es x y velocidad es v. En el péndulo son θ y θ̇. La interfaz común simplifica el integrador, pero no elimina las unidades: el modelo proporciona etiquetas y conversiones para presentar cada valor.
36.6 Contrato del modelo
function crearModeloResorte({ masaKg, constanteNpm, zeta }) {
const omega0 = Math.sqrt(constanteNpm / masaKg);
return {
tipo: "resorte",
omega0,
periodoReferenciaS: 2 * Math.PI / omega0,
aceleracion: estado => -(omega0 ** 2) * estado.coordenada
- 2 * zeta * omega0 * estado.velocidad,
energia: estado => 0.5 * masaKg * estado.velocidad ** 2
+ 0.5 * constanteNpm * estado.coordenada ** 2
};
}
const modelo = crearModeloResorte({ masaKg: 1, constanteNpm: 25, zeta: 0.05 });
console.log({ tipo: modelo.tipo, periodo: modelo.periodoReferenciaS,
aceleracion: modelo.aceleracion({ coordenada: 0.1, velocidad: 0 }) });El objeto expone lo que el motor necesita y mantiene dentro de la fábrica los detalles particulares.
36.7 Fábrica del péndulo
function crearModeloPendulo({ longitudM, gravedadMps2, masaKg, zeta }) {
const omega0 = Math.sqrt(gravedadMps2 / longitudM);
return {
tipo: "pendulo",
omega0,
periodoReferenciaS: 2 * Math.PI / omega0,
aceleracion: estado => -(omega0 ** 2) * Math.sin(estado.coordenada)
- 2 * zeta * omega0 * estado.velocidad,
energia: estado => 0.5 * masaKg * longitudM ** 2 * estado.velocidad ** 2
+ masaKg * gravedadMps2 * longitudM * (1 - Math.cos(estado.coordenada))
};
}
const modelo = crearModeloPendulo({
longitudM: 1, gravedadMps2: 9.81, masaKg: 0.5, zeta: 0
});
console.log({ periodoLineal: modelo.periodoReferenciaS,
energiaInicial: modelo.energia({ coordenada: Math.PI / 3, velocidad: 0 }) });36.8 Validar antes de construir
Las fábricas deben rechazar valores no finitos, masas y longitudes no positivas, rigidez no positiva y ζ negativa. Un objeto inválido no debería llegar al motor.
La interfaz gráfica puede limitar rangos, pero el modelo conserva su propia validación porque también podría recibir datos desde un archivo o una llamada directa.
36.9 Integrador semimplícito común
function pasoSemimplicito(modelo, estado, dtS) {
const velocidad = estado.velocidad + modelo.aceleracion(estado) * dtS;
return {
tiempoS: estado.tiempoS + dtS,
coordenada: estado.coordenada + velocidad * dtS,
velocidad
};
}
const modelo = {
aceleracion: estado => -25 * estado.coordenada
};
const inicial = { tiempoS: 0, coordenada: 0.1, velocidad: 0 };
console.log(pasoSemimplicito(modelo, inicial, 0.01));36.10 RK4 sin conocer el sistema
RK4 sólo necesita que el modelo evalúe la aceleración para estados intermedios. La derivada común es:
d(velocidad)/dt = modelo.aceleracion(estado)
Esto permite usar la misma implementación para el resorte lineal y el péndulo no lineal. Cada etapa debe incluir su tiempo, coordenada y velocidad intermedios.
36.11 Implementación autónoma de RK4
function pasoRK4(modelo, estado, dt) {
const derivada = e => ({ dq: e.velocidad, dv: modelo.aceleracion(e) });
const mover = (e, d, factor) => ({
tiempoS: e.tiempoS + factor * dt,
coordenada: e.coordenada + factor * dt * d.dq,
velocidad: e.velocidad + factor * dt * d.dv
});
const k1 = derivada(estado);
const k2 = derivada(mover(estado, k1, 0.5));
const k3 = derivada(mover(estado, k2, 0.5));
const k4 = derivada(mover(estado, k3, 1));
return {
tiempoS: estado.tiempoS + dt,
coordenada: estado.coordenada + dt * (k1.dq + 2*k2.dq + 2*k3.dq + k4.dq) / 6,
velocidad: estado.velocidad + dt * (k1.dv + 2*k2.dv + 2*k3.dv + k4.dv) / 6
};
}
const modelo = { aceleracion: e => -25 * e.coordenada };
console.log(pasoRK4(modelo, { tiempoS: 0, coordenada: 0.1, velocidad: 0 }, 0.01));36.12 Motor con paso físico fijo
El motor acumula tiempo real y consume pasos constantes:
acumulador += tiempoReal
mientras acumulador ≥ dt y pasosDelCuadro < límite:
estado = integrador(modelo, estado, dt)
historial.agregar(copia del estado)
acumulador -= dt
dibujar(estado)El límite de pasos por cuadro impide que una pestaña detenida intente recuperar miles de actualizaciones de una vez.
36.13 Reiniciar de forma determinista
Reiniciar debe reconstruir modelo, estado e historial desde los controles actuales:
coordenada = coordenadaInicial
velocidad = velocidadInicial
acumulador = 0
historial = [estado inicial]
Con los mismos datos, integrador y paso, una simulación determinista debe repetir la misma secuencia.
36.14 Cambiar de escenario
Al elegir otro modelo no conviene reinterpretar silenciosamente 0,1 m como 0,1 rad. Cada escenario conserva controles y unidades propios.
El cambio detiene la ejecución, selecciona valores iniciales adecuados y reinicia. Esta decisión evita estados híbridos que no pertenecen a ningún sistema físico.
36.15 Escalas para la visualización
El dibujo transforma metros o radianes en píxeles. Esa escala visual no debe entrar en las ecuaciones.
- El resorte usa una escala de píxeles por metro y limita sólo el dibujo.
- El péndulo calcula la masa mediante seno y coseno del ángulo físico.
- La gráfica etiqueta la coordenada según el modelo.
36.16 Historial con ventana móvil
Una simulación que funciona indefinidamente no debería guardar todos los pasos. Conservamos una ventana temporal y eliminamos muestras anteriores.
También podemos guardar cada varios pasos para reducir memoria, manteniendo el paso interno sin cambios.
36.17 Diagnóstico energético
El laboratorio muestra E/E₀. Con ζ = 0, debería permanecer cerca de 1; con ζ > 0, debe presentar una tendencia decreciente.
Esta métrica es diagnóstica, no una corrección. No debemos renormalizar la velocidad para obligar a conservar energía: eso escondería el comportamiento real del integrador.
36.18 Medir eventos y períodos
Podemos detectar cruces ascendentes de la coordenada e interpolar sus tiempos. La diferencia entre cruces equivalentes estima el período.
En el péndulo de gran amplitud, la comparación adecuada es con el período no lineal medido o calculado, no con 2π√(L/g) como si fuera exacto.
36.19 Exportar resultados
Una exportación sencilla puede usar columnas con unidades explícitas:
function historialACsv(historial, unidadCoordenada, unidadVelocidad) {
const cabecera = `tiempo_s,coordenada_${unidadCoordenada},velocidad_${unidadVelocidad}`;
const filas = historial.map(e =>
[e.tiempoS, e.coordenada, e.velocidad].map(Number).join(","));
return [cabecera, ...filas].join("\n");
}
const historial = [
{ tiempoS: 0, coordenada: 0.1, velocidad: 0 },
{ tiempoS: 0.01, coordenada: 0.09975, velocidad: -0.025 }
];
console.log(historialACsv(historial, "m", "m_s"));Para descargarlo en el navegador se crea un Blob y un enlace temporal. El texto generado puede probarse sin depender de la interfaz.
36.20 Accesibilidad y controles
- Cada entrada posee una etiqueta asociada.
- Los botones son elementos
buttonutilizables con teclado. - Los resultados importantes también aparecen como texto.
- El estado de reproducción se comunica mediante una región viva.
- Canvas incluye una descripción que se actualiza.
La animación no debe ser la única forma de comprender el resultado.
36.21 Manejo de errores
El motor se detiene si encuentra NaN, Infinity o una cantidad excesiva de pasos atrasados. El mensaje debe indicar qué ocurrió y conservar el último estado válido.
Los errores de configuración se muestran cerca de los controles. Los errores de programación no deberían transformarse silenciosamente en valores físicos por defecto.
36.22 Laboratorio interactivo final
Elegí un sistema, ajustá sus parámetros y compará los integradores. La gráfica conserva aproximadamente seis períodos de historia y la línea violeta muestra la energía relativa.
Laboratorio preparado. Presioná Iniciar o avanzá un paso.
36.23 Plan de verificación del proyecto
- Comprobar el reposo exacto en el equilibrio.
- Comparar el resorte ideal con su solución analítica.
- Verificar energía aproximadamente constante con ζ = 0.
- Verificar energía decreciente con ζ > 0.
- Comparar el péndulo de 5° con el período lineal.
- Confirmar que el péndulo de gran amplitud tarde más.
- Repetir con Δt/2 y comparar estados en iguales tiempos.
- Probar pausa, reinicio, cambio de modelo y entradas extremas.
36.24 Organización sugerida de archivos
laboratorio/
├── index.html
├── css/
│ └── laboratorio.css
├── js/
│ ├── modelos.js
│ ├── integradores.js
│ ├── motor.js
│ ├── vista.js
│ └── app.js
└── pruebas/
├── modelos.test.js
└── integradores.test.jsEn esta página el laboratorio está reunido para poder estudiarlo directamente. En un proyecto mantenible, separar módulos reduce dependencias y facilita las pruebas.
36.25 Extensiones y criterios de evaluación
Posibles extensiones:
- exportar el historial como CSV;
- detectar y mostrar períodos;
- agregar fuerza sinusoidal al resorte;
- guardar escenarios en JSON;
- superponer ejecuciones con distintos pasos;
- incorporar pruebas automáticas en cada cambio.
El proyecto se evalúa por corrección física, claridad del modelo, estabilidad numérica, manejo de unidades, accesibilidad, reproducibilidad y calidad de las comprobaciones; no sólo por su apariencia.
36.26 Cierre del curso
- Un oscilador se describe mediante estado, parámetros y una ley de evolución.
- Resortes y péndulos comparten ideas, pero no la misma ecuación.
- La linealización es útil cuando se conocen sus límites.
- Amortiguamiento, fuerza externa y resonancia modifican la transferencia de energía.
- Los objetos organizan datos; los integradores aproximan el tiempo continuo.
- La vista transforma unidades físicas en pantalla sin alterar el modelo.
- Una simulación confiable se compara con referencias y se refina.
- El resultado final debe poder explicarse, repetirse y ponerse a prueba.
El laboratorio integra estas ideas en una base extensible para simulaciones físicas más amplias.
Desafío final de comprobación
Para un resorte ideal con m = 1 kg, k = 25 N/m, x₀ = 0,12 m y v₀ = 0, calculá el período y la energía. Luego ejecutá el laboratorio con ambos integradores y dos pasos diferentes.
E₀ = ½·25·0,12² J = 0,180 J
const masaKg = 1;
const constanteNpm = 25;
const posicionInicialM = 0.12;
const velocidadInicialMps = 0;
const omega0RadS = Math.sqrt(constanteNpm / masaKg);
const periodoS = 2 * Math.PI / omega0RadS;
const energiaJ = 0.5 * masaKg * velocidadInicialMps ** 2
+ 0.5 * constanteNpm * posicionInicialM ** 2;
console.log({ omega0RadS, periodoS, energiaJ });