Skip to main content

Overview

The pendulum experiment demonstrates simple harmonic motion, energy conservation, and damping. Using video tracking with sophisticated pivot detection, you can measure period, frequency, amplitude decay, and even calculate gravitational acceleration.

Physics Theory

Simple Pendulum Motion

For small angles (θ < 15°), the period is: T=2πLgT = 2\pi\sqrt{\frac{L}{g}} Therefore: g=4π2LT2g = \frac{4\pi^2 L}{T^2} Where:
  • TT is the period (seconds)
  • LL is the pendulum length (meters)
  • gg is gravitational acceleration (m/s²)

Damped Harmonic Motion

With air resistance, the angular displacement follows: θ(t)=Aeγtcos(ωt+ϕ)+θ0\theta(t) = A e^{-\gamma t} \cos(\omega t + \phi) + \theta_0 Where:
  • AA is initial amplitude
  • γ\gamma is damping coefficient
  • ω\omega is angular frequency
  • ϕ\phi is phase offset
  • θ0\theta_0 is equilibrium offset

Energy Analysis

At maximum displacement: E=mgh=mgL(1cosθ)12mgLθ2E = mgh = mgL(1 - \cos\theta) \approx \frac{1}{2}mgL\theta^2 At lowest point (maximum velocity): E=12mv2E = \frac{1}{2}mv^2

Measurement Method

PhysisLab uses camera-based tracking with dual color detection:
  1. Bob (pendulum mass): Tracked for position and angle
  2. Pivot point: Tracked to compensate for camera movement
Source Files:
  • Standard tracking: ~/workspace/source/Pendulo/analisis.py
  • With pivot tracking: ~/workspace/source/Pendulo/analisisTrackingpivote.py

Hardware Requirements

  • Camera (webcam, USB camera, or phone)
  • Pendulum setup (string + colored mass)
  • Colored marker at pivot point
  • Ruler or measuring tape
  • Stable mounting for camera

Experimental Procedure

1

Setup Physical Pendulum

  1. Attach a colored bob (ball, weight) to a string
  2. Measure and record string length L (pivot to center of bob)
  3. Mark the pivot point with a different colored marker
  4. Mount camera with clear view of full swing range
2

Record Video

Record a video of the pendulum swinging:
  • Ensure both bob and pivot are always visible
  • Use consistent lighting
  • Record at least 10-20 oscillations
  • Keep camera as stable as possible
3

Run Analysis Script

cd ~/workspace/source/Pendulo
python analisisTrackingpivote.py
4

Configure Analysis

Follow the interactive prompts:Frame Selection:
  • Navigate: d (next), a (previous)
  • Mark start: i
  • Mark end: f
  • Confirm: ENTER
Color Calibration:
  • Select ROI around pendulum bob → automatic HSV detection
  • Select ROI around pivot marker → automatic HSV detection
5

Spatial Calibration

  • Enter pendulum length L in meters
  • Click on bob center to measure L in pixels
  • System calculates pixel-to-meter scale
6

Data Collection

  • Script tracks bob and pivot automatically
  • Real-time visualization shows:
    • Green circle: Bob position
    • Cyan circle: Pivot (OK)
    • Yellow circle: Pivot (fallback to last known)
  • ESC to stop early

Code Walkthrough

Color Calibration

Adaptive HSV range calculation (analisis.py:82-98):
# Bob color calibration
object_bob = frame_ref[yb:yb+hb, xb:xb+wb]
hsv_bob = cv2.cvtColor(object_bob, cv2.COLOR_BGR2HSV)

h_bob = np.mean(hsv_bob[:, :, 0])
s_bob = np.mean(hsv_bob[:, :, 1])
v_bob = np.mean(hsv_bob[:, :, 2])

# Adaptive margins
margen_h = 15
margen_s = max(40, s_bob * 0.4)
margen_v = max(40, v_bob * 0.4)

hsv_bob_lower = np.array([max(0, h_bob - margen_h),
                          max(0, s_bob - margen_s),
                          max(0, v_bob - margen_v)])
hsv_bob_upper = np.array([min(179, h_bob + margen_h),
                          min(255, s_bob + margen_s),
                          min(255, v_bob + margen_v)])

Pivot Detection and Tracking

Dynamic pivot tracking with fallback (analisisTrackingpivote.py:222-244):
# Detect pivot in current frame
mask_eje_f = cv2.inRange(hsv, hsv_eje_lower, hsv_eje_upper)
mask_eje_f = cv2.morphologyEx(mask_eje_f, cv2.MORPH_OPEN, kernel)
contornos_e, _ = cv2.findContours(mask_eje_f, cv2.RETR_EXTERNAL, 
                                    cv2.CHAIN_APPROX_SIMPLE)

pivot_color = (0, 255, 255)   # Cyan = detected
pivot_perdido = False

if contornos_e:
    ce = max(contornos_e, key=cv2.contourArea)
    Me = cv2.moments(ce)
    if Me["m00"] != 0:
        pivot_actual = [int(Me["m10"] / Me["m00"]),
                        int(Me["m01"] / Me["m00"])]
    else:
        pivot_color = (0, 215, 255)
        pivot_perdido = True
else:
    pivot_color = (0, 215, 255)   # Yellow = using last valid
    pivot_perdido = True

pivot_trayectoria.append(tuple(pivot_actual))

Angular Position Calculation

Coordinate transformation and angle measurement (analisis.py:228-237):
# Position relative to pivot (this frame)
dx_px = cx - pivot_actual[0]
dy_px = pivot_actual[1] - cy   # Y axis up

x_m = dx_px * escala
y_m = dy_px * escala

# Angle from vertical (positive = right)
theta = np.arctan2(dx_px, dy_px)

tiempo = (frame_num - frame_inicio) / fps
datos.append((tiempo, x_m, y_m, theta))

Sinusoidal Fitting

Damped harmonic oscillator fit (analisis.py:288-315):
def seno_amort(t, A, omega, phi, gamma, offset):
    return A * np.exp(-gamma * t) * np.cos(omega * t + phi) + offset

# Initial parameter estimation
A0 = (np.max(theta) - np.min(theta)) / 2
off0 = np.mean(theta)
omega0 = 2 * np.pi / (t[-1] / max(1, np.sum(np.diff(np.sign(theta - off0)) != 0) / 2))
p0 = [A0, omega0, 0, 0.01, off0]

popt, _ = curve_fit(seno_amort, t, theta, p0=p0, maxfev=10000)
A_fit, omega_fit, phi_fit, gamma_fit, off_fit = popt

T_fit = 2 * np.pi / omega_fit
g_exp = (2 * np.pi / T_fit)**2 * L_cuerda

print(f"Amplitud inicial  : {np.degrees(A_fit):.2f}°")
print(f"Período           : {T_fit:.4f} s")
print(f"Amortiguamiento γ : {gamma_fit:.4f} s⁻¹")
print(f"g experimental    : {g_exp:.4f} m/s²")

Data Analysis

Output Files

Data File: datos_pendulo.txt
t(s)  x(m)  y(m)  theta(rad)  vx(m/s)  vy(m/s)
0.000  0.0854  -0.6892  0.1234  0.0000  0.0000
0.033  0.0842  -0.6901  0.1213  -0.0365  -0.0273
...
Figures Generated:
  1. fig1_posicion_velocidad.png - Position and velocity vs time
  2. fig2_angulo.png - Angular displacement with sinusoidal fit
  3. fig3_trayectoria.png - Pendulum trajectory (x-y plane)

Example Results

--- Resultados del ajuste ---
Amplitud inicial  : 15.34°
Período           : 1.6847 s
Amortiguamiento γ : 0.0234 s⁻¹
g experimental    : 9.7821 m/s²

Velocity and Acceleration

Velocities calculated via numerical differentiation (analisis.py:277-283):
# Smoothing function
def smooth(arr, w=5):
    return np.convolve(arr, np.ones(w)/w, mode='same')

x_s = smooth(x_data)
y_s = smooth(y_data)

vx = np.gradient(x_s, t)
vy = np.gradient(y_s, t)

vx = smooth(vx)
vy = smooth(vy)

Visualization

Position and Velocity Plots

The analysis generates comprehensive plots:
fig1, axs1 = plt.subplots(2, 2, figsize=(14, 8))

# Position X
axs1[0, 0].plot(t, x_data, 'o', ms=3, alpha=0.4, label='Datos')
axs1[0, 0].plot(t, x_s, '-', lw=2, label='Suavizado')

# Position Y
axs1[0, 1].plot(t, y_data, 'o', ms=3, alpha=0.4)

# Velocity X
axs1[1, 0].plot(t, vx, '-', lw=2, color='#7FFF00')

# Velocity Y
axs1[1, 1].plot(t, vy, '-', lw=2, color='#FFD700')

Angular Displacement with Fit

fig2, ax_theta = plt.subplots(figsize=(10, 5))

ax_theta.plot(t, np.degrees(theta), 'o', ms=3, alpha=0.4, label='θ datos')

if ajuste_ok:
    t_fine = np.linspace(t[0], t[-1], 500)
    ax_theta.plot(t_fine, np.degrees(seno_amort(t_fine, *popt)), '-',
                  lw=2, label=f'Ajuste  T={T_fit:.3f} s  |  g={g_exp:.3f} m/s²')

Trajectory Visualization

fig3, ax_tray = plt.subplots(figsize=(7, 7))

sc = ax_tray.scatter(x_data, y_data, c=t, cmap='plasma', s=12, alpha=0.85)
ax_tray.plot(0, 0, 'y*', ms=16, label='Pivote (origen)')

# Theoretical arc
ang_range = np.linspace(np.min(theta), np.max(theta), 200)
ax_tray.plot(L_cuerda * np.sin(ang_range), -L_cuerda * np.cos(ang_range),
             '--', color='gray', label='Arco teórico')

Advanced Features

Camera Motion Compensation

The pivot tracking version compensates for camera shake:
# Calculate position relative to current pivot
dx_px = cx - pivot_actual[0]
dy_px = pivot_actual[1] - cy

# Convert to meters using scale
x_m = dx_px * escala
y_m = dy_px * escala
This ensures accurate measurements even if the camera moves slightly.

Energy Conservation Check

Calculate potential and kinetic energy:
import numpy as np

# From your data
theta_data = datos[:, 3]
vx = datos[:, 4]
vy = datos[:, 5]

m = 0.150  # mass in kg (measure your bob)
g = 9.8
L = 0.70   # your pendulum length

# Potential energy (relative to lowest point)
PE = m * g * L * (1 - np.cos(theta_data))

# Kinetic energy
v_total = np.sqrt(vx**2 + vy**2)
KE = 0.5 * m * v_total**2

# Total mechanical energy
E_total = PE + KE

print(f"Energy variation: {np.std(E_total)/np.mean(E_total)*100:.1f}%")

Tips for Best Results

  • Use thin, inextensible string (fishing line works well)
  • Ensure pivot is frictionless (ball bearing ideal)
  • Use dense bob (metal sphere) to minimize air resistance
  • Keep pendulum length between 0.5-1.5 meters
  • Start with small angles (< 15°) for theoretical validity
  • Mount camera securely (tripod essential)
  • Position perpendicular to swing plane
  • Ensure full swing visible in frame
  • Use contrasting background
  • Adequate lighting without glare
  • Record at 30+ FPS for smooth tracking
  • Bob and pivot should be different, bright colors
  • Avoid colors present in background
  • Solid, non-reflective surfaces work best
  • Test color detection before recording
  • Measure L from pivot to bob center of mass
  • Use multiple measurements and average
  • Account for bob radius if significant
  • Ensure string is taut when measuring

Troubleshooting

IssueSolution
Pivot detection lostUse brighter marker, improve lighting, check HSV range
Erratic trackingReduce bob motion blur, increase FPS, smooth data more
Poor sinusoidal fitUse more oscillations, start with smaller amplitude
g value far from 9.8Check L measurement, ensure small angles, verify FPS
Tracking jumpsBackground has similar colors, use more distinct markers

Extensions and Variations

Physical Pendulum

Modify theory for extended objects with moment of inertia

Coupled Pendulums

Study energy transfer between two connected pendulums

Damping Study

Vary damping by adding air resistance (card attached to bob)

Nonlinear Effects

Use large angles to observe departure from SHM

Next Steps

Spring-Mass System

Study another form of harmonic motion

Data Analysis Guide

Learn advanced fitting techniques

Build docs developers (and LLMs) love