31. Calcular velocidades posteriores y comprobar resultados

Una respuesta de colisión confiable no termina al obtener dos velocidades: debe demostrar que satisface el modelo, conservar las entradas y exponer residuos que permitan detectar errores.

31.1 Calcular y auditar son partes del mismo algoritmo

Una fórmula puede producir números aunque los datos no describan un choque, la normal sea inválida o exista un error de signos. Por eso conviene diseñar juntas la solución física y sus comprobaciones.

El resultado debe responder dos preguntas: “¿cuáles son las velocidades posteriores?” y “¿qué evidencia indica que son coherentes con las hipótesis?”.

31.2 Contrato de entrada y salida

EntradaDos cuerpos válidos, una normal orientada y un coeficiente de restitución.
SalidaVelocidades nuevas, impulso aplicado, estado del contacto y diagnóstico numérico.

Definir este contrato evita que la función dependa del dibujo, de controles HTML o de variables globales.

31.3 Conservar una fotografía del estado anterior

Las auditorías requieren comparar antes y después. Copiamos las velocidades antes de cualquier actualización:

const cuerpoA = { id: "A", velocidad: { x: 5, y: 1 } };
const cuerpoB = { id: "B", velocidad: { x: -2, y: 0 } };

const vAi = { ...cuerpoA.velocidad };
const vBi = { ...cuerpoB.velocidad };

console.log("Fotografía inicial de A:", vAi);
console.log("Fotografía inicial de B:", vBi);

cuerpoA.velocidad.x = 0;
console.log("Velocidad actual de A:", cuerpoA.velocidad);
console.log("La fotografía de A se conserva:", vAi);

Asignar const vAi = cuerpoA.velocidad solo crea otra referencia; si el vector cambia, se pierde el estado anterior.

31.4 Preparar la normal

La respuesta usa una normal unitaria n̂ orientada de A hacia B:

n̂ = n⃗/|n⃗|

Si |n⃗| es cero o demasiado pequeño, no existe una dirección fiable y el cálculo debe detenerse con un error descriptivo.

31.5 Comprobar la aproximación

La velocidad relativa normal inicial es:

gn,i = (v⃗Bi − v⃗Ai) · n̂

Con nuestra orientación, solo se resuelve un choque si gn,i < 0. Si gn,i ≥ 0, se devuelven las velocidades sin cambios y un estado "separando".

31.6 Calcular el impulso normal

Para masas traslacionales y superficies lisas:

j = −(1 + e)gn,i/(1/mA + 1/mB)

El impulso sobre A es −j n̂ y sobre B es +j n̂.

31.7 Obtener las velocidades posteriores

Cuerpo Av⃗Af = v⃗Ai − (j/mA)n̂
Cuerpo Bv⃗Bf = v⃗Bi + (j/mB)n̂

Calcular ambos vectores desde las mismas velocidades iniciales elimina dependencias accidentales del orden de escritura.

31.8 Devolver resultados sin mutar entradas

Una función pura devuelve cuerpos actualizados como objetos nuevos:

const cuerpoA = {
  id: "A", masa: 2, velocidad: { x: 5, y: 0 }
};
const cuerpoB = {
  id: "B", masa: 3, velocidad: { x: 0, y: 0 }
};
const vAf = { x: 0.95, y: -2.338 };
const vBf = { x: 2.7, y: 1.559 };

const cuerpoAf = {
  ...cuerpoA,
  velocidad: { ...vAf }
};
const cuerpoBf = {
  ...cuerpoB,
  velocidad: { ...vBf }
};

console.log("Resultado A:", cuerpoAf);
console.log("Resultado B:", cuerpoBf);
console.log("Entrada A sin cambios:", cuerpoA);
console.log("Entrada B sin cambios:", cuerpoB);
console.log("¿Son objetos nuevos?", cuerpoAf !== cuerpoA && cuerpoBf !== cuerpoB);

La simulación decide luego cuándo reemplazar el estado actual. Esto simplifica pruebas y permite comparar fácilmente ambos estados.

31.9 Residuo de cantidad de movimiento

Calculamos el momento total antes y después:

rP = |P⃗f − P⃗i|

En dos dimensiones:

rP = √[(Pfx − Pix)² + (Pfy − Piy)²]

El residuo conserva más información que comparar solo los módulos de P⃗.

31.10 Residuo de restitución

Después del choque calculamos gn,f con las velocidades obtenidas. El residuo es:

re = |gn,f + e gn,i|

Un valor próximo a cero confirma que la rapidez relativa normal final coincide con la exigida por e.

31.11 Comprobar la componente tangencial

Para superficies lisas, t̂ = (−ny, nx) y:

rt = máx{|vAt,f − vAt,i|, |vBt,f − vBt,i|}

Este control detecta impulsos tangenciales introducidos por un error de implementación.

31.12 Comprobar la energía cinética

Calculamos ΔK = Kf − Ki. Para un choque pasivo con 0 ≤ e ≤ 1 debe cumplirse ΔK ≤ 0 dentro de la tolerancia.

ΔK esperada = −½μ(1 − e²)gn,i²

La igualdad permite auditar no solo el signo, sino también el valor de la variación energética del modelo.

31.13 Escala y tolerancia

Un residuo absoluto de 10−8 puede ser excelente para momentos del orden de 1000 y grande para datos del orden de 10−10. Una comparación práctica combina tolerancias:

r ≤ tolerancia absoluta + tolerancia relativa · escala

El próximo tema profundizará en la elección de estas tolerancias y los casos límite.

31.14 Construir un informe de diagnóstico

// Valores obtenidos para el choque oblicuo de la sección 31.15.
const tolerancia = 1e-9;
const residuoMomento = 0;
const residuoRestitucion = 1.78e-15;
const residuoTangencial = 4.44e-16;
const ki = 25;
const kf = 20.95;

const momentoOk = residuoMomento <= tolerancia;
const restitucionOk = residuoRestitucion <= tolerancia;
const tangencialOk = residuoTangencial <= tolerancia;
const energiaOk = kf <= ki + tolerancia;

const diagnostico = {
  estado: "resuelto",
  momento: { residuo: residuoMomento, cumple: momentoOk },
  restitucion: { residuo: residuoRestitucion, cumple: restitucionOk },
  tangencial: { residuo: residuoTangencial, cumple: tangencialOk },
  energia: { inicial: ki, final: kf, cambio: kf - ki, cumple: energiaOk }
};

console.log(JSON.stringify(diagnostico, null, 2));

Guardar valores y decisiones permite investigar un fallo sin repetir el cálculo manualmente.

31.15 Ejemplo numérico

mA = 2 kg, mB = 3 kg, v⃗Ai = (5, 0) m/s, v⃗Bi = (0, 0), n̂ = (cos 30°, sen 30°) y e = 0,8.

gn,i ≈ −4,330 m/s    j ≈ 9,353 N·s
Velocidad de Av⃗Af ≈ (0,950; −2,338) m/s
Velocidad de Bv⃗Bf ≈ (2,700; 1,559) m/s

El momento final es (10, 0) kg·m/s, gn,f ≈ 3,464 m/s y ΔK = −4,05 J.

31.16 Resultados que no representan un choque nuevo

Si los cuerpos ya se separan, devolver velocidades iguales a las entradas no es un fallo. Es una respuesta válida con estado distinto:

function copiarCuerpo(cuerpo) {
  return { ...cuerpo, velocidad: { ...cuerpo.velocidad } };
}

function evaluarImpacto(cuerpoA, cuerpoB, normal, tolerancia = 1e-9) {
  const gnInicial =
    (cuerpoB.velocidad.x - cuerpoA.velocidad.x) * normal.x +
    (cuerpoB.velocidad.y - cuerpoA.velocidad.y) * normal.y;

  if (gnInicial >= -tolerancia) {
    return {
      estado: "sin-impacto",
      razon: "Los cuerpos no se aproximan",
      impulso: 0,
      cuerpoA: copiarCuerpo(cuerpoA),
      cuerpoB: copiarCuerpo(cuerpoB)
    };
  }

  return { estado: "requiere-resolucion", gnInicial };
}

const cuerpoA = { id: "A", velocidad: { x: -2, y: 0 } };
const cuerpoB = { id: "B", velocidad: { x: 2, y: 0 } };
const resultado = evaluarImpacto(cuerpoA, cuerpoB, { x: 1, y: 0 });

console.log(JSON.stringify(resultado, null, 2));

31.17 Flujo recomendado

Validar y copiar entradas
Resolver la respuesta
Calcular residuos
Devolver informe completo

La interfaz puede presentar este informe, guardarlo o convertir un incumplimiento en una aserción durante las pruebas.

31.18 Actividad interactiva: solucionador con auditoría

Modificá los datos del choque. El laboratorio calcula las velocidades finales y evalúa momento, restitución, componente tangencial y energía.

Solucionador y auditor físico

n̂ apunta de A hacia B; los ángulos se miden desde +x.

Velocidad final A(+0,95; −2,34) m/s
Velocidad final B(+2,70; +1,56) m/s
Impulso escalar9,35 N·s
Cambio de energía−4,05 J
Momento: cumple
Restitución: cumple
Tangencial: cumple
Energía: cumple

Choque resuelto: todas las comprobaciones satisfacen la tolerancia.

31.19 Implementación completa

La siguiente función reúne resolución y residuos principales:

function resolverYComprobar(a, b, normal, e, tolerancia = 1e-9) {
  if (a.masa <= 0 || b.masa <= 0 || e < 0) {
    throw new Error("Datos fuera de rango");
  }
  const longitud = Math.hypot(normal.x, normal.y);
  if (longitud <= tolerancia) throw new Error("Normal no válida");
  const n = { x: normal.x / longitud, y: normal.y / longitud };
  const dot = (u, v) => u.x * v.x + u.y * v.y;
  const gn = dot({ x: b.velocidad.x - a.velocidad.x,
                   y: b.velocidad.y - a.velocidad.y }, n);

  if (gn >= -tolerancia) {
    return { estado: "sin-impacto", impulso: 0 };
  }

  const j = -(1 + e) * gn / (1 / a.masa + 1 / b.masa);
  const vAf = { x: a.velocidad.x - j * n.x / a.masa,
                y: a.velocidad.y - j * n.y / a.masa };
  const vBf = { x: b.velocidad.x + j * n.x / b.masa,
                y: b.velocidad.y + j * n.y / b.masa };
  const gnFinal = dot({ x: vBf.x - vAf.x, y: vBf.y - vAf.y }, n);
  const momentoAntes = {
    x: a.masa * a.velocidad.x + b.masa * b.velocidad.x,
    y: a.masa * a.velocidad.y + b.masa * b.velocidad.y
  };
  const momentoDespues = {
    x: a.masa * vAf.x + b.masa * vBf.x,
    y: a.masa * vAf.y + b.masa * vBf.y
  };

  return {
    estado: "resuelto", impulso: j, vAf, vBf,
    residuos: {
      momento: Math.hypot(momentoDespues.x - momentoAntes.x,
                           momentoDespues.y - momentoAntes.y),
      restitucion: Math.abs(gnFinal + e * gn)
    }
  };
}

const cuerpoA = { masa: 2, velocidad: { x: 5, y: 0 } };
const cuerpoB = { masa: 3, velocidad: { x: 0, y: 0 } };
const normal = { x: Math.cos(Math.PI / 6), y: Math.sin(Math.PI / 6) };
const resultado = resolverYComprobar(cuerpoA, cuerpoB, normal, 0.8);

console.log(JSON.stringify(resultado, null, 2));

31.20 Pruebas automatizadas útiles

CasoResultado esperado
Masas iguales, e = 1, choque frontalIntercambio de componentes normales.
e = 0Velocidad relativa normal final nula.
Contacto oblicuo lisoComponentes tangenciales sin cambio.
Cuerpos separándoseImpulso cero y estado sin-impacto.
Normal escaladaMismo resultado después de normalizar.
Masa inválidaError explícito antes de calcular.

31.21 Errores frecuentes y ejercicios

Errores frecuentes

  • Modificar las entradas antes de guardar las velocidades iniciales.
  • Calcular v⃗Bf usando v⃗Af en lugar de v⃗Ai.
  • Comparar solo los módulos del momento.
  • Aplicar restitución a la rapidez total y no a la componente relativa normal.
  • Declarar que un resultado cumple sin informar tolerancia ni residuo.
  • Convertir “sin impacto” en una excepción aunque sea un estado físico normal.

Ejercicios propuestos

  1. Calculá j para mA = 1 kg, mB = 2 kg, gn,i = −6 m/s y e = 0,5.
  2. Con n̂ = (1, 0), v⃗Ai = (6, 2) y v⃗Bi = (0, −1), obtené las velocidades finales del ejercicio anterior.
  3. Comprobá por componentes el momento del ejercicio 2.
  4. Calculá gn,f y el residuo de restitución.
  5. Verificá que las componentes tangenciales no cambiaron.
  6. Diseñá una prueba para confirmar que las entradas no fueron mutadas.
  7. Usá el laboratorio con A moviéndose en sentido opuesto a n̂ y explicá el estado informado.
Ver soluciones y orientaciones
  1. j = −1,5(−6)/(1 + 1/2) = 6 N·s.
  2. v⃗Af = (0, 2) m/s y v⃗Bf = (3, −1) m/s.
  3. P⃗i = (6, 0) kg·m/s y P⃗f = 1(0, 2) + 2(3, −1) = (6, 0) kg·m/s.
  4. gn,f = 3 m/s y re = |3 + 0,5(−6)| = 0.
  5. Para A permanece +2 m/s; para B permanece −1 m/s.
  6. Guardá copias profundas, ejecutá la función y compará cada propiedad de entrada con la copia.
  7. Si gn,i ≥ 0, el laboratorio informa que no existe un impacto nuevo y devuelve las velocidades originales.

31.22 Ideas para recordar

  • Validar, calcular y auditar forman un único flujo.
  • Las velocidades finales deben calcularse desde el mismo estado inicial.
  • Una normal casi nula invalida el cálculo.
  • Los cuerpos que se separan no reciben un nuevo impulso.
  • Los residuos cuantifican cuánto se aparta el resultado de cada ecuación.
  • Momento, restitución, tangencial y energía comprueban aspectos diferentes.
  • Una función pura simplifica las pruebas y conserva los datos de entrada.
  • El informe de diagnóstico debe incluir valores, tolerancias y decisiones.

En el próximo tema profundizaremos en tolerancias numéricas, conservación y casos límite.