37. Integrar la rotación, normalizar ángulos y verificar resultados

Una simulación transforma derivadas en pequeños cambios temporales. La elección del paso, el orden de actualización y las verificaciones físicas determinan si el movimiento calculado es confiable.

37.1 Hacer evolucionar el estado

En el tema anterior representamos un instante mediante θ, ω y otras variables. Para producir movimiento debemos actualizar ese estado a medida que avanza el tiempo. Como una computadora opera en pasos discretos, aproxima la evolución continua mediante muchos intervalos Δt.

Modelo diferencial

Relaciona aceleración, velocidad y posición angular en tiempo continuo.

Integración numérica

Convierte derivadas en incrementos aplicados durante cada paso Δt.

Verificación

Compara el resultado con soluciones conocidas, invariantes y tolerancias.

37.2 Las ecuaciones que se integran

La velocidad angular es la derivada del ángulo y la aceleración angular es la derivada de la velocidad angular:

ω = dθ/dt     α = dω/dt

Si el torque neto y el momento de inercia son conocidos:

α = τnet/I

Un integrador usa α para actualizar ω y usa ω para actualizar θ.

37.3 El paso de tiempo Δt

Δt debe expresarse en segundos si ω está en rad/s y α en rad/s². En una simulación de 60 actualizaciones por segundo, un paso idealizado sería:

Δt = 1/60 s ≈ 0,01667 s

Un paso menor suele mejorar la precisión, pero exige más cálculos. Un paso grande puede omitir cambios rápidos, atravesar límites o acumular errores visibles.

37.4 Euler explícito

El método de Euler explícito usa los valores al comienzo del paso:

θn+1 = θn + ωnΔt
ωn+1 = ωn + αnΔt
function pasoEulerExplicito(estado, alfa, dt) {
  const omegaAnterior = estado.omega;
  estado.angulo += omegaAnterior * dt;
  estado.omega += alfa * dt;
}

const estadoExplicito = { angulo: 1, omega: 2 };
pasoEulerExplicito(estadoExplicito, 3, 0.1);
console.log("Estado después del paso:", estadoExplicito);

Es simple y útil para aprender, pero puede introducir errores importantes en sistemas oscilatorios o con fuerzas que dependen del estado.

37.5 El orden de actualización importa

En Euler explícito, θ se actualiza con la velocidad anterior. Si primero se modifica ω y luego se usa ese nuevo valor para θ, ya no se está aplicando el mismo método.

Error habitual: dos fragmentos que contienen las mismas asignaciones pueden implementar integradores diferentes si cambia su orden. Conviene guardar temporalmente los valores anteriores o nombrar explícitamente el método.

37.6 Euler semimplícito

Euler semimplícito —también llamado Euler simpléctico en este contexto— actualiza primero ω y usa la nueva velocidad para θ:

ωn+1 = ωn + αnΔt
θn+1 = θn + ωn+1Δt
function pasoEulerSemimplicito(estado, alfa, dt) {
  estado.omega += alfa * dt;
  estado.angulo += estado.omega * dt;
}

const estadoSemimplicito = { angulo: 1, omega: 2 };
pasoEulerSemimplicito(estadoSemimplicito, 3, 0.1);
console.log("Estado después del paso:", estadoSemimplicito);

Tiene un costo similar y suele comportarse mejor en sistemas mecánicos, aunque sigue siendo un método de primer orden.

37.7 Solución exacta con α constante

Cuando α permanece constante, conocemos una solución analítica que sirve como referencia:

ω(t) = ω₀ + αt
θ(t) = θ₀ + ω₀t + ½αt²

Comparar la integración numérica contra estas ecuaciones permite medir el error sin depender de una inspección visual.

37.8 Error global para aceleración constante

Si T es un múltiplo exacto de Δt y α es constante, ambos métodos calculan ω(T) exactamente salvo redondeo, pero aproximan θ:

errorEuler = θnum − θexacto = −½αTΔt
errorsemimplícito = +½αTΔt

Al reducir Δt a la mitad, el error angular también se reduce aproximadamente a la mitad: son métodos de primer orden.

37.9 Alcanzar exactamente el tiempo final

Si T no es múltiplo de Δt, repetir siempre el paso completo hace que la simulación sobrepase el tiempo solicitado. El último paso debe limitarse:

const estado = { angulo: 0, omega: 2 };
const alfa = 1;
const dt = 0.3;
const tiempoFinal = 1;
let tiempo = 0;
let pasos = 0;

function integrar(estado, alfa, paso) {
  estado.omega += alfa * paso;
  estado.angulo += estado.omega * paso;
}

while (tiempo < tiempoFinal) {
  const paso = Math.min(dt, tiempoFinal - tiempo);
  integrar(estado, alfa, paso);
  tiempo += paso;
  pasos++;
}

console.log({ tiempo, pasos, estado });

Así la suma de los pasos coincide con T dentro de la precisión numérica disponible.

37.10 Tiempo simulado y número de cuadros

La física debe depender del tiempo transcurrido, no de cuántas veces se dibujó la pantalla. Actualizar angulo += omega una vez por cuadro omite Δt y hace que la velocidad cambie según el rendimiento del dispositivo.

incremento correcto: Δθ = ωΔt

Dos equipos que procesan diferentes cantidades de cuadros deben producir la misma evolución física para el mismo tiempo simulado.

37.11 Medir tiempo con requestAnimationFrame

En el navegador, requestAnimationFrame entrega una marca temporal en milisegundos:

let anterior;
let angulo = 0;
const omega = 2;

function cuadro(ahora) {
  if (anterior === undefined) anterior = ahora;
  const dt = (ahora - anterior) / 1000;
  anterior = ahora;

  angulo += omega * dt;
  return { ahora, dt, angulo };
}

// El navegador llamaría a cuadro mediante requestAnimationFrame.
// Estas marcas permiten ejecutar el ejemplo sin dejar un bucle activo.
const muestras = [1000, 1016.7, 1033.4].map(cuadro);
console.log(muestras);

La división por 1000 convierte milisegundos a segundos.

37.12 Por qué limitar un Δt variable

Si la pestaña se pausa o el equipo se bloquea momentáneamente, el siguiente Δt puede ser enorme. Aplicarlo de una vez puede volver inestable la simulación o atravesar colisiones.

const dtReal = 0.18;
const dtMaximo = 0.05;
const dtLimitado = Math.min(dtReal, dtMaximo);

console.log({ dtReal, dtMaximo, dtLimitado });

Limitar Δt sacrifica tiempo simulado durante pausas largas, pero evita saltos destructivos. Para mayor consistencia se usa un acumulador con paso fijo.

37.13 Acumulador de paso fijo

El renderizado puede ocurrir a ritmo variable mientras la física avanza con pasos constantes:

const PASO = 1 / 120;
let acumulado = 0;
let pasosFisicos = 0;

function avanzar(dtReal) {
  acumulado += Math.min(dtReal, 0.25);
  while (acumulado >= PASO) {
    pasosFisicos++;
    acumulado -= PASO;
  }
  return acumulado / PASO;
}

const interpolacion = avanzar(0.025);
console.log({ pasosFisicos, acumulado, interpolacion });

El paso fijo mejora la reproducibilidad: la misma secuencia de entradas genera los mismos pasos físicos, independientemente de los cuadros dibujados.

37.14 Por qué normalizar el ángulo

Después de muchas vueltas, θ puede crecer hasta valores enormes. Seno y coseno son periódicos, por lo que la orientación visual se repite cada 2π:

θ y θ + 2πn representan la misma orientación

Normalizar mantiene el valor en un intervalo manejable para dibujar, comparar orientaciones y mostrar información. El ángulo acumulado debe conservarse aparte si interesa contar vueltas.

37.15 Normalizar al intervalo [0, 2π)

El operador % de JavaScript conserva el signo del dividendo; por eso angulo % vuelta puede ser negativo. Una función segura es:

function normalizar0a2Pi(angulo) {
  const vuelta = 2 * Math.PI;
  return ((angulo % vuelta) + vuelta) % vuelta;
}

console.log({
  menosPiSobreDos: normalizar0a2Pi(-Math.PI / 2),
  nuevePiSobreDos: normalizar0a2Pi(9 * Math.PI / 2)
});
0 ≤ θnormalizado < 2π

37.16 Normalizar al intervalo [−π, π)

Para errores de orientación y giros mínimos suele ser más cómodo un intervalo centrado en cero:

function normalizar0a2Pi(angulo) {
  const vuelta = 2 * Math.PI;
  return ((angulo % vuelta) + vuelta) % vuelta;
}

function normalizarMenosPiAPi(angulo) {
  return normalizar0a2Pi(angulo + Math.PI) - Math.PI;
}

const diferencia = normalizarMenosPiAPi(
  1 * Math.PI / 180 - 359 * Math.PI / 180
);
console.log("Diferencia mínima:", diferencia * 180 / Math.PI, "grados");
−π ≤ θnormalizado < π

Por ejemplo, la diferencia entre 359° y 1° debe interpretarse como 2°, no como −358°.

37.17 Conservar las vueltas completas

Si una rueda recorre una distancia o un motor contabiliza revoluciones, no alcanza con guardar el ángulo normalizado. Conviene mantener un ángulo acumulado θacum:

vueltas algebraicas = θacum/(2π)

Para informar vueltas completas puede usarse truncamiento hacia cero:

const anguloAcumulado = 15 * Math.PI;
const vueltasCompletas = Math.trunc(
  anguloAcumulado / (2 * Math.PI)
);
console.log("Vueltas completas:", vueltasCompletas);

La orientación dibujada usa el ángulo normalizado; la distancia y el historial usan el acumulado.

37.18 Precisión de punto flotante

Números como 0,1 no tienen representación binaria exacta. Después de miles de sumas pueden aparecer pequeñas diferencias. No deben ocultarse redondeando el estado en cada paso, porque ese redondeo introduce su propio error sistemático.

comparación recomendada: |calculado − esperado| ≤ tolerancia

La tolerancia debe elegirse según la escala del problema, el método, Δt y la precisión requerida; no existe un único valor correcto para todas las simulaciones.

37.19 Verificar con soluciones analíticas

Los casos simples son pruebas de referencia:

  • con α = 0, ω debe permanecer constante y θ = θ₀ + ω₀t;
  • con α constante, deben cumplirse las ecuaciones cinemáticas exactas;
  • si ω₀ = α = 0, el ángulo no debe cambiar;
  • al reducir Δt, el error debe disminuir con el orden esperado.

Una simulación que “parece girar bien” puede fallar estas pruebas numéricas.

37.20 Verificar invariantes físicos

Cuando el torque neto es cero, el momento angular L = Iω debe permanecer constante. En un sistema conservativo, la energía mecánica también debe conservarse dentro de una tolerancia.

error relativo = |valor − valorinicial| / max(|valorinicial|, escala mínima)

Registrar estos errores a lo largo del tiempo revela derivas. Un integrador puede respetar bien una magnitud y degradar otra; por eso se verifican varias propiedades independientes.

37.21 Estudio de convergencia

Ejecutá el mismo caso con Δt, Δt/2 y Δt/4. Para Euler de primer orden, el error final debería reducirse aproximadamente por factores de dos:

error(Δt)/error(Δt/2) ≈ 2

Si el error no disminuye, puede existir un fallo de implementación, una discontinuidad, una tolerancia mal elegida o una comparación contra una referencia incorrecta.

Verificar no es mirar: una prueba útil produce un número —error absoluto, error relativo, residuo o razón de convergencia— y define de antemano qué valor es aceptable.

37.22 Actividad interactiva: integrar y medir el error

Elegí un método y modificá las condiciones. El laboratorio integra hasta T usando un último paso parcial cuando hace falta. La rueda violeta representa el resultado numérico y la marca verde, la orientación exacta.

Laboratorio de integración rotacional

La aceleración angular es constante para disponer de una solución analítica de referencia.

30°
+2,0 rad/s
+1,5 rad/s²
4,0 s
0,20 s
Comparación entre integración numérica y solución angular exacta Euler explícito calcula un ángulo final menor que la solución exacta con aceleración angular positiva. Orientación final Numérico: 61,5°Exacto: 95,9°Ángulo acumulado: +19,92 rad · 3,17 vueltas Error angular durante el tiempoterror+0,6900 rad−0,6900 rad Error = −0,6000 radCon Δt/2: −0,3000 rad · razón ≈ 2,00 20 pasos · ω final = +8,00 rad/sθ normalizado = 61,5°
resultado numéricosolución exactaerror numéricoreferencias
Cantidad de pasos20
Ángulo acumulado numérico+19,92 rad
Ángulo exacto+20,52 rad
Ángulo normalizado1,07 rad · 61,5°
Velocidad final+8,00 rad/s
Error angular−0,6000 rad

Euler explícito subestima el ángulo con α positiva. Al usar la mitad de Δt, el error se reduce aproximadamente a la mitad, como corresponde a un método de primer orden.

37.23 Implementación y pruebas

function simularRotacion({
  anguloInicial,
  omegaInicial,
  alfa,
  tiempoFinal,
  dt,
  metodo = "semimplicito"
}) {
  if (dt <= 0 || tiempoFinal < 0) {
    throw new Error("Tiempo o paso no válido");
  }

  let angulo = anguloInicial;
  let omega = omegaInicial;
  let tiempo = 0;
  let pasos = 0;

  while (tiempo < tiempoFinal - 1e-12) {
    const h = Math.min(dt, tiempoFinal - tiempo);
    if (metodo === "explicito") {
      angulo += omega * h;
      omega += alfa * h;
    } else {
      omega += alfa * h;
      angulo += omega * h;
    }
    tiempo += h;
    pasos++;
  }

  return {
    anguloAcumulado: angulo,
    anguloNormalizado: normalizar0a2Pi(angulo),
    omega,
    pasos
  };
}

function normalizar0a2Pi(angulo) {
  const vuelta = 2 * Math.PI;
  return ((angulo % vuelta) + vuelta) % vuelta;
}

// Prueba contra la solución exacta con α constante.
const caso = {
  anguloInicial: Math.PI / 6,
  omegaInicial: 2,
  alfa: 1.5,
  tiempoFinal: 4,
  dt: 0.01
};
const resultado = simularRotacion(caso);
const exacto = caso.anguloInicial
  + caso.omegaInicial * caso.tiempoFinal
  + 0.5 * caso.alfa * caso.tiempoFinal ** 2;
const error = resultado.anguloAcumulado - exacto;
const pruebaSuperada = Math.abs(error) < 0.04;
console.log({ resultado, exacto, error, pruebaSuperada });

La prueba define una tolerancia compatible con el método y el paso. Para estudiar convergencia, se repite con varios valores de dt y se comprueba que el error disminuya aproximadamente de forma lineal.

Errores frecuentes

  • sumar ω al ángulo sin multiplicar por Δt;
  • usar milisegundos como si fueran segundos;
  • cambiar sin advertir el orden de Euler explícito y semimplícito;
  • suponer que más cuadros por segundo significan automáticamente física correcta;
  • aplicar un Δt enorme después de reactivar una pestaña;
  • normalizar el único ángulo almacenado y perder la cantidad de vueltas;
  • usar angulo % (2*Math.PI) esperando siempre un resultado positivo;
  • redondear θ y ω después de cada paso;
  • evaluar una simulación solo por su apariencia;
  • usar una tolerancia sin relacionarla con el paso y la escala del problema.

Ejercicios propuestos

  1. Aplicá un paso de Euler explícito a θ₀ = 1 rad, ω₀ = 2 rad/s, α = 3 rad/s² y Δt = 0,1 s.
  2. Repetí el ejercicio con Euler semimplícito.
  3. Calculá la solución exacta del estado anterior en t = 0,1 s y compará los ángulos.
  4. Normalizá 9π/2 al intervalo [0, 2π).
  5. Normalizá −π/2 al intervalo [0, 2π) y al intervalo [−π, π).
  6. Una simulación gira a 5 rad/s durante 2 s con α = 0. ¿Cuál debe ser Δθ?
  7. ¿Cuántos pasos completos de 0,03 s caben en 1 s y cuánto debe durar el último paso?
  8. Explicá por qué conviene guardar un ángulo acumulado y otro normalizado.
  9. Si el error de Euler es 0,08 rad con Δt = 0,02 s, estimá el error esperado con Δt = 0,01 s.
  10. Proponé dos invariantes o soluciones de referencia para probar una simulación rotacional.
Ver soluciones y explicaciones
  1. θ₁ = 1 + 2·0,1 = 1,2 rad; ω₁ = 2 + 3·0,1 = 2,3 rad/s.
  2. ω₁ = 2,3 rad/s; θ₁ = 1 + 2,3·0,1 = 1,23 rad.
  3. θ = 1 + 2·0,1 + ½·3·0,1² = 1,215 rad. Euler explícito da −0,015 rad de error y semimplícito +0,015 rad.
  4. 9π/2 = 4π + π/2; el resultado normalizado es π/2.
  5. En [0, 2π): 3π/2. En [−π, π): −π/2.
  6. Δθ = ωΔt = 5·2 = 10 rad.
  7. Caben 33 pasos, que suman 0,99 s; el último debe durar 0,01 s.
  8. El acumulado conserva vueltas y desplazamiento total; el normalizado mantiene una orientación compacta para dibujar y comparar.
  9. Al ser un método de primer orden, al reducir Δt a la mitad se espera aproximadamente 0,04 rad.
  10. Por ejemplo: solución exacta con α constante; ω constante si τ = 0; conservación de L sin torque; conservación de energía en un sistema ideal.

37.24 Ideas para recordar

  • Integrar es convertir derivadas en cambios acumulados durante pasos Δt.
  • Euler explícito usa ω anterior; Euler semimplícito usa ω actualizada.
  • El orden de las asignaciones define el método.
  • Δt se expresa en segundos y no debe confundirse con la cantidad de cuadros.
  • Un último paso parcial evita sobrepasar el tiempo final.
  • Un acumulador de paso fijo mejora estabilidad y reproducibilidad.
  • Normalizar a [0, 2π) mantiene compacta la orientación.
  • El ángulo acumulado debe conservarse si interesan vueltas o desplazamiento total.
  • Los resultados se comparan con tolerancias, soluciones exactas e invariantes.
  • Un estudio de convergencia comprueba que el error disminuya al reducir Δt.

En el próximo tema integraremos todos estos conceptos en un laboratorio completo de movimiento circular, rotación y rodadura.