La ley de Hooke

1 Introducción

La ley de Hooke es una ley empírica que dice que la fuerza F_e necesaria para comprimir o estirar un resorte cuya longitud natural es L_0 hasta una longitud L es proporcional a la deformación L - L_0:

F_e = k \, (L - L_0) \tag{1}

donde k es la constante elástica del resorte. Esta ley se puede considerar como una aproximación de primer orden de la fuerza de un resorte alrededor de su longitud natural:

F_e(L) = F_e(L_0) + \left.\frac{dF_e}{dL}\right\rvert_{L_0} (L - L_0) + \ldots

donde \left.\frac{dF_e}{dL}\right\rvert_{L_0} = k.

1.1 Caso estático

Una forma de medir la constante elástica k de un resorte es colgando una masa m de este (Figura 1). Cuando el sistema se encuentre en equilibrio, la fuerza de gravedad F_g se compensará con la fuerza elástica F_e:

\begin{align*} F_g &= F_e \\ m g &= k (x_{eq} - x_0) \end{align*} \tag{2}

En este caso, x_{eq} - x_0 es el estiramiento respecto de la longitud natural, y g es la aceleración de la gravedad. Midiendo este estiramiento y la fuerza, es decir, la masa y g, se puede determinar la constante k.

Figura 1: Esquema del estiramiendo de un resorte al colgar una masa de él. A la izquierda, se muestra el resorte colgado sin la masa, el cual tiene una longitud x_0. A la derecha, se muestra una masa (esfera verde) colgada del resorte que se estira hasta una longitud x_1. F_e y F_g corresponden a las fuerzas elástica y de gravedad que actúan sobre la masa.

1.2 Caso dinámico

Otra forma de medir la constante elástica es a través de su movimiento al realizar una perturbación de su equilibrio (Figura 2). Si se aparta la masa una distancia d de la posición de equilibrio x_{eq}, su posición x en función del tiempo t será:

x(t) = d \, \cos(\omega t) + x_{eq} \tag{3}

donde

\omega^2 = \frac{k}{m} \tag{4}

La velocidad v(t), la aceleración a(t) y la fuerza F(t) del sistema también son sinusoidales:

F(t) = m \, a(t) = m \frac{d^2x}{dt^2}(t) = - m \omega^2 d \, \cos(\omega t) \tag{5}

Midiendo el periodo \tau = \frac{2 \pi}{\omega} y la masa, se puede determinar k.

Figura 2: Animación del movimiento de una masa colgada de un resorte alrededor de su posición de equilibrio x_0. d es el desplazamiento inicial desde x_0.

2 Medición de la masa

Para colgar del resorte, se dispone de pesas de distinta masa la cual se puede determinar con la balanza que se encuentra en el laboratorio. Para obtener masas más grandes, hay que combinar varias de estas masas. Analice si conviene:

  1. pesar todas las masas por separado y después sumar las que se utilicen en cada medición;
  2. para cada medición, pesar todas las masas juntas.

3 Caso estático

Realicen mediciones de la posición de equilibrio del resorte x_{eq} variando la masa m que cuelga de él. A partir de esos datos, encuentren la constante elástica del resorte utilizando el modelo dado por la ecuación Ecuación 2.

3.1 Opción 1: x vs. m

No es posible determinar simultáneamente k y g a partir del ajuste, independientemente de si se usa la relación

x(m) = \frac{g}{k} \cdot m

o la forma inversa

m(x) = \frac{k}{g} \cdot x.

Como el ajuste no cambia si se multiplican k y g por el mismo factor, solo puede determinarse la relación A = k/g. Luego, se puede obtener k usando una medición independiente de g.

  1. ¿Se cumple la condición \sigma_y \gg |\frac{df}{dx}| \sigma_x para considerar únicamente el error en y? (Bevington y Robinson 2003, 102)

  2. ¿Hace falta incluir una ordenada al origen en el ajuste? En ese caso, ¿a qué puede deberse? ¿Se obtiene el mismo valor si se mide independientemente?

  3. Al igual que con el promedio, al usar más mediciones se reduce el error en los parámetros (A, en este caso) del ajuste. Entonces, ¿se puede reducir el error de k a 0? Si no, ¿hasta cuanto se podría?

  4. Compare el error asignado a la variable del eje y con la desviación estándar de los residuos. Realicen un histograma de los residuos. ¿Es gaussiano?

Extra Bajo ciertas condiciones (Widrow et al. 1996), una medición redondeada puede modelarse como la suma de la medición sin redondear más un error con distribución uniforme de ancho a = 10^{-d}, donde d es la cantidad de decimales. La desviación estándar de una distribución uniforme de ancho a es a / \sqrt{12}.

3.2 Opción 2: x vs. F

Extra
Importante

En este método, las fuerzas no son independientes y aparecen correlaciones. Si no se tienen en cuenta, se subestima el error en k. Si se consideran correctamente, se obtiene el mismo resultado que en la opción 1.

Una alternativa es ajustar la fuerza de la gravedad F_g = m g como función de la posición x (o al revés):

F_g = k x.

En este caso, se obtendría k directamente del ajuste.

Como en el punto anterior, se usa una medición independiente de g para convertir las masas en fuerzas:

\begin{align*} \begin{cases} &m_1 &\mapsto &F_1 = m_1 \cdot g \\ & &\vdots \\ &m_N &\mapsto &F_N = m_N \cdot g \end{cases} \end{align*}

Como estas fuerzas reutilizan la misma medición de g, están correlacionadas. Si la medición de g resulta menor (o mayor) que el valor real, todas las fuerzas serán menores (o mayores) que los valores reales. Es decir, sus errores no son independientes.

Cuando existen correlaciones, no basta con conocer el error de cada medición, sino que también hace falta conocer los “errores conjuntos” entre mediciones (ver errores dependientes). Se puede usar el paquete uncertainties para calcular la matriz de covarianza que describe esas correlaciones:

import labo1
import uncertainties
import uncertainties.unumpy

# Datos x y m
x       = ...
m       = ...
sigma_m = ...

# Agrego incertezas y calculo F
g = uncertainties.ufloat(9.80, 0.01)
m = uncertainties.unumpy.uarray(m, sigma_m)
F = m * g

# Ajusto
def func(x, k):
    return k * x


labo1.curve_fit(
    func,
    x=x,
    y=uncertainties.unumpy.nominal_values(F),

    # Elegir `y_err`:

    # (✅ correcto) utilizando la matriz de covarianza
    # y_err=uncertainties.covariance_matrix(F),

    # (❌ incorrecto) utilizando las incertezas
    # es decir, la raíz de la diagonal de la matriz de covarianza
    # y_err=uncertainties.unumpy.std_devs(F),
)
  1. Compare el resultado del caso correcto, con la matriz de covarianza, con el obtenido con el método anterior.

  2. Analicen cómo cambian los resultados al variar el error en g.

4 Caso dinámico

Usando un sensor de fuerza, midan la fuerza F en función del tiempo t al perturbar el sistema a partir de su equilibrio. Luego, midan \tau = \frac{2\pi}{\omega} para distintas masas m y determinen k a partir del modelo de las ecuaciones 3 y 4.

4.1 Sensor de fuerza

El sensor de fuerza mide un voltaje proporcional a la fuerza: V = a \cdot F + V_0 \tag{6} donde a tiene unidades de fuerza sobre voltaje. La señal se digitaliza con un conversor analógico-digital (SensorDAQ), que toma muestras de la señal analógica cada cierto período T (Figura 3). La frecuencia de muestreo f = 1/T puede configurarse hasta 48 kHz, esto es, 48 mil muestras por segundo. Puede considerarse despreciable el error en el tiempo que se asigna a cada muestra (ver manual).

Para tomar las mediciones en la computadora, hay que utilizar el programa MotionDAQ. El archivo que exporta se puede cargar con:

import pandas as pd

pd.read_csv(
    "archivo.txt",
    delimiter="\t",  # las columas están separadas por "tab"
    skiprows=3,      # ignorar las 3 primeras filas, que tienen metadata
    decimal=","      # opcional: los números usa coma decimal, en lugar de punto
)

Figura 3: Ejemplo de medición del voltaje en función del tiempo. Se simuló la digitalización de la misma señal para distintas frecuencias f y distintos niveles de ruido en el voltaje medido \sigma.

4.2 Determinar la frecuencia o el periodo

4.2.1 Opción 1: ajuste por cuadrados mínimos

Usando el modelo de la fuerza en función del tiempo (Ecuación 5) y teniendo en cuenta que el sensor mide un voltaje proporcional a la fuerza (Ecuación 6), determinen la frecuencia \omega o el período \tau ajustando la expresión

V(t) = A \, \cos(\omega t + \phi) + B \tag{7}

donde \{A, \omega, \phi, B\} son los parámetros a determinar.

  1. Teniendo en cuenta las ecuaciones Ecuación 5 y Ecuación 6, ¿qué significado físico tienen los distintos parámetros?

Al ser un ajuste no lineal en los parámetros, la solución que se obtiene minimizando cuadrados mínimos no es única. Por eso, conviene ayudar al algoritmo de minimización dando parámetros iniciales para iniciar la búsqueda. En este caso, el único parámetro realmente sensible es \omega.

Código: generación de datos de ejemplo
import numpy as np
import matplotlib.pyplot as plt
import labo1


def func(t, A, w, phi, B):
    return A * np.cos(w * t + phi) + B


t = np.linspace(0, 3, 100, endpoint=False)
v_real = func(t, A=1, w=10, phi=0, B=0)

sigma_v = 0.2
v = np.random.default_rng(0).normal(v_real, sigma_v)
Código: ajuste por cuadrados mínimos
# Ajuste cambiando los parámetros iniciales.
# Por defecto, usa 1.0 para todos.
r_1 = labo1.curve_fit(func, x=t, y=v, y_err=sigma_v)
r_2 = labo1.curve_fit(func, x=t, y=v, y_err=sigma_v, initial_params={"w": 9})
Código: generación del gráfico
def label(inicial, ajustado):
    x, sigma = ajustado
    return rf"$w = {inicial} \;\mapsto\; {x:+.2f} \pm {sigma:.2f}$"


plt.figure(figsize=(4, 2))
plt.errorbar(r_1.x, r_1.y, r_1.y_err, fmt=".")
plt.plot(t, r_1.eval(t), linewidth=2, label=label(inicial=1, ajustado=r_1["w"]))
(plt.plot(t, r_2.eval(t), linewidth=2, label=label(inicial=9, ajustado=r_2["w"])))
plt.xlabel("Tiempo (s)")
plt.ylabel("Voltaje (V)")
plt.legend(title=r"Parámetro (inicial $\mapsto$ final)", bbox_to_anchor=(1, 1))

Para estimar el error en los parámetros, se necesita conocer el error de la variable del eje y, es decir, el error en el voltaje.

  1. ¿Cómo puede estimarse el error en el voltaje?

4.2.2 Opción 1 (bis): oscilador amortiguado

Este modelo suele ser más sencillo de ajustar, ya que es menos sensible a la condición inicial para \omega. Por eso, resulta útil incluso si el amortiguamiento no es apreciable.

Ver extra

La fuerza de rozamiento con el aire puede modelarse como una fuerza viscosa, proporcional a la velocidad: F = b \cdot v. En ese caso, la posición en función del tiempo es x(t) = d \, e^{-\gamma t} \, \cos(\omega t + \phi) + x_{eq} donde

  • \omega^2 = \omega_0^2 - \gamma^2,
  • \gamma = \frac{b}{2 \sqrt{m k}},
  • \omega_0^2 = \frac{k}{m}.
Cuando el rozamiento es nulo, \gamma = 0, se recupera el caso de la Ecuación 3.

4.2.3 Opción 2 (extra): tiempo entre máximos

Ver extra

Una forma alternativa es calcular el periodo como la diferencia de tiempo entre dos máximos máximos de la señal (Figura 4, naranja). También se pueden elegir dos mínimos (verde), o cada vez que vuelva a pasar por la misma altura en la misma dirección (rojo).

Código
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal


def func(t):
    return np.cos(4 * t - 0.5)


t = np.linspace(0, 3, 21, endpoint=False)
v = func(t)

def plot_bar(ix, *, dy, color):
    x = t[ix]
    y = np.mean(v[ix])
    plt.scatter(t[ix], v[ix], color=color)
    plt.errorbar(
        x=np.mean(x),
        xerr=np.diff(x) / 2,
        y=y + dy,
        capsize=5,
        color=color,
    )

plt.figure(figsize=(6, 2))
plt.xlabel("Tiempo (s)")
plt.ylabel("Voltage (V)")
plt.scatter(t, v)

ix, _ = scipy.signal.find_peaks(v)
plot_bar(ix, dy=0.3, color="C1")

ix, _ = scipy.signal.find_peaks(-v)
plot_bar(ix, dy=-0.3, color="C2")

ix, = np.where(np.diff(np.sign(v), prepend=np.nan) < 0)
plot_bar(ix, dy=0.4, color="C3")
Figura 4: Medición de un periodo en una señal sinusoidal.

Es más simple asignar un error a este método cuando la señal tiene poco ruido en el eje y, como en las primeras dos señales de la Figura 3. En el caso de la última señal, el ruido en el voltaje añade un error al elegir el tiempo correspondiente al máximo “real”.

  1. Para las dos primeras señales de la Figura 3, ¿qué error asignaría para la medición de 1 período con este método? ¿Es el mismo en ambos casos? Recuerde que el error en el tiempo de cada muestra (punto) es despreciable.

4.3 Período en función de la masa

Una vez determinado el período \tau para una masa m, se puede repetir el procedimiento para varias masas y ajustar la relación entre ambas magnitudes:

m = k \cdot \left( \frac{\tau}{2\pi} \right)^2 \tag{8}

que se obtiene al reescribir la ecuación Ecuación 4 en términos del período \tau = \frac{2\pi}{\omega}.

  1. ¿Este modelo ajusta bien los datos o hace falta agregar un término constante? En ese caso, ¿qué representa? Cushing (1984).

  2. Si fuera necesario agregar un término constante a la ecuación Ecuación 8, ¿qué habría sucedido si hubieran determinado k a partir de una sola medición (m, \tau)?

Código para analizar múltiples archivos

Para cada masa, se tendrá un archivo con la medición de la fuerza en función del tiempo. Por ejemplo, para una masa de (100 \pm 1)\,\text{g}, se guardó un archivo llamado 100.txt. Esta información puede organizarse en una tabla (Tabla 1), donde la columna “archivo” contiene el nombre del archivo.

Tabla 1: Metadata de los experimentos.
archivo masa masa_err
0 100.txt 100 1
1 200.txt 200 1
2 300.txt 300 1

Si el análisis de cada archivo es independiente de los demás, puede escribirse una función que reciba el nombre del archivo y devuelva el resultado del análisis para ese archivo en particular. Luego, esa función puede aplicarse a cada fila de la columna archivo:

import pandas as pd

def cargar(archivo: str):
    """Carga las mediciones del SensorDAQ."""
    df = pd.read_csv(archivo)
    return df

def ajustar(df):
    """Ajusta las mediciones del voltaje en función del tiempo."""
    # Completar
    result = labo1.curve_fit(..., x=df["..."])
    return result

def extraer_periodo(result):
    """Extrae el período y su error del resultado del ajuste."""
    T, sigma_T = result["T"]
    return {
        "periodo": T,
        "sigma_periodo": sigma_T,
    }

def analisis(archivo: str):
    df = cargar(archivo)
    result = ajustar(df)
    return extraer_periodo(result)


df = pd.read_csv(...)     # Carga la tabla
df_periodo = (
    df["archivo"]         # Elije la columna archivo
    .map(analisis)        # Ejecuta la función en cada fila
    .apply(pd.Series)     # Expande la columna a tabla
)
df = df.join(df_periodo)  # Une la tabla original con la nueva

df

Si necesitan acceder a las demás columnas para hacer el ajuste, por ejemplo, para estimar una frecuencia inicial a partir de la masa, pueden usar:

def func(fila):
    archivo = fila["archivo"]
    masa = fila["masa"]

    w_inicial = k * masa
    result = labo1.curve_fit(..., initial_params={"w": w_inicial})
    return result


df.apply(func, axis="columns")

5 Bibliografía

Arrieta, Arrieta, A., y J. M. Tejeiros. 2009. «Masa Efectiva para un Sistema de Muelle Real». Revista Colombiana de Física 41 (2). https://web.archive.org/web/20131111082247/http://www.revcolfis.org/publicaciones/vol41_2/4102517.pdf.
Bevington, P., y D. K. Robinson. 2003. Data Reduction and Error Analysis for the Physical Sciences. McGraw-Hill Education.
Cushing, James T. 1984. «The spring-mass system revisited». American Journal of Physics 52 (10): 925-33. https://doi.org/10.1119/1.13796.
Widrow, B., I. Kollar, y Ming-Chang Liu. 1996. «Statistical theory of quantization». IEEE Transactions on Instrumentation and Measurement 45 (2): 353-61. https://doi.org/10.1109/19.492748.