Tutorial 8: Damped Oscillations — The Real Pendulum

How friction adds a new factor to the generic structure and settles the system to equilibrium

Author

Kenneth Moore


From Sustained to Damped Oscillation

Tutorial 7 showed that two stocks with cross-coupled flows produce sustained oscillation — cycles that repeat indefinitely at constant amplitude. The frictionless pendulum was our example: velocity driving the change in position, and position driving the change in velocity through a restoring force.

But the frictionless pendulum is simplistic In the real world, a swinging pendulum loses energy with every cycle — to air resistance, to bearing friction at the pivot, to internal flexing of the rod. The oscillations do not sustain forever; they decay. The pendulum settles, gradually coming to rest at its equilibrium position.

This is damped oscillation: the same cross-coupled two-stock structure as Tutorial 7, but with an additional force that steadily removes energy from the system.


The Generic Structure, Revisited

The generic structure for sustained oscillation has a crucial feature we set aside in Tutorial 7: the diagram explicitly reserves space for factors affecting each flow.

Figure 1: The generic two-stock oscillating structure. Each flow is driven by the opposite stock (cross-coupling). The ‘Factors affecting Flow’ boxes are not decorative — they represent real-world forces that modify each flow beyond the cross-coupling alone.

In Tutorial 7, the pendulum’s Net Flow 1 (Changing Position) had no external factors — it was driven entirely by the other stock (Velocity). The “Factors affecting Flow 1” box was structurally empty. Net Flow 2 (Changing Velocity) did have factors: Gap, gravity, and pendulum length.

In the real world, Flow 2 has one more factor that Tutorial 7 ignored: friction. Air resistance and bearing friction act against the direction of motion — they oppose velocity, reducing the rate at which velocity changes with each swing. Friction is the missing entry in the “Factors affecting Flow 2” box.


Friction as a Structural Addition

Kinetic friction in a pendulum acts as viscous damping: the drag force is proportional to the current velocity and always opposes the direction of motion. In stock-and-flow terms, this adds a second term to the Changing Velocity flow:

  • Restoring acceleration — driven by the gap from equilibrium, gravity, and length (as in Tutorial 7).
  • Friction drag — proportional to current velocity, acting against it.

The new parameter is the friction coefficient, \(b\). A small value of \(b\) produces slow, gradual decay; a larger value causes faster settling.


Updated Stock-and-Flow Diagram

The structure is identical to Tutorial 7 with two additions to the Changing Velocity flow: the friction coefficient parameter, and a new information link from the Velocity stock back to its own flow (because the drag force depends on how fast the pendulum is currently moving).

Figure 2: Stock-and-flow structure of the damped pendulum. Two additions to Changing Velocity distinguish this from Tutorial 7: the friction coefficient parameter and a self-referencing information link from Velocity (the drag force is proportional to current speed). Everything else is unchanged.

Model Equations

The only change from Tutorial 7 is the addition of the friction drag term to the Changing Velocity equation:

\[\frac{d(\text{Position})}{dt} = \text{Velocity}\]

\[\frac{d(\text{Velocity})}{dt} = \frac{g}{L} \times \text{Gap} \ - \ b \times \text{Velocity}\]

\[\text{Gap} = \text{Desired Position} - \text{Position}\]

The friction term \(b \times \text{Velocity}\) carries the correct sign automatically: when the pendulum moves right (Velocity \(> 0\)), the term is positive and is subtracted — reducing the rightward acceleration. When it moves left (Velocity \(< 0\)), the term is negative and subtracting it increases the changing velocity — slowing the leftward motion. Friction always opposes the direction of movement, regardless of which way the bob swings.


R Implementation

library(deSolve)
library(tidyverse)

The model follows the standard deSolve function signature. Variable names use the project’s prefix convention (s_ stocks, p_ parameters, c_ converters, f_ flows).

model_damped <- function(time, stocks, params) {
  with(as.list(c(stocks, params)), {
    c_gap               <- p_desired_position - s_position
    f_changing_position <- s_velocity
    f_changing_velocity <- (p_gravity / p_length_of_pendulum) * c_gap -
                             p_friction_coeff * s_velocity
    ds_position_dt      <- f_changing_position
    ds_velocity_dt      <- f_changing_velocity
    return(list(
      c(ds_position_dt, ds_velocity_dt),
      gap = c_gap
    ))
  })
}

stocks <- c(s_position = 0.15, s_velocity = 0)
params <- c(
  p_desired_position   = 0,
  p_gravity            = 9.8,
  p_length_of_pendulum = 1,
  p_friction_coeff     = 0.3
)

The pendulum is released from 0.15 metres with zero initial velocity, same as Tutorial 7. The friction coefficient is set to 0.3 — small enough to produce slow, gradual decay rather than rapid settling.

Setting p_friction_coeff = 0 recovers the undamped Tutorial 7 model exactly; the same function covers both cases.

sim_time <- seq(0, 25, 0.01)

sim_damped <- ode(
  times  = sim_time,
  y      = stocks,
  parms  = params,
  func   = model_damped,
  method = "rk4"
) |>
  as_tibble() |>
  mutate(across(everything(), as.numeric))

Position Over Time

sim_damped |>
  ggplot(aes(x = time, y = s_position)) +
  geom_hline(yintercept = 0, linetype = "dashed", colour = "grey70", linewidth = 0.5) +
  geom_line(colour = "#4e8cd4", linewidth = 0.8) +
  labs(x = "Time (seconds)", y = "Position (metres)") +
  theme_minimal(base_size = 12)
Figure 3: Position of the damped pendulum over 25 seconds. The oscillation frequency is unchanged from Tutorial 7, but the amplitude decays steadily as friction removes energy from the system. The pendulum asymptotically approaches its desired position of zero.

Undamped vs Damped: Direct Comparison

params_undamped <- replace(params, "p_friction_coeff", 0)

sim_undamped <- ode(
  times  = sim_time,
  y      = stocks,
  parms  = params_undamped,
  func   = model_damped,
  method = "rk4"
) |>
  as_tibble() |>
  mutate(across(everything(), as.numeric), label = "Undamped (b = 0)")

sim_damped_labelled <- sim_damped |>
  mutate(label = "Damped (b = 0.3)")

bind_rows(sim_undamped, sim_damped_labelled) |>
  ggplot(aes(x = time, y = s_position, colour = label)) +
  geom_hline(yintercept = 0, linetype = "dashed", colour = "grey70", linewidth = 0.5) +
  geom_line(linewidth = 0.8) +
  scale_colour_manual(values = c("Undamped (b = 0)" = "grey60",
                                  "Damped (b = 0.3)"  = "#4e8cd4")) +
  labs(x = "Time (seconds)", y = "Position (metres)", colour = NULL) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")
Figure 4: Undamped (Tutorial 7) and damped oscillation on the same axes. The undamped pendulum cycles forever at constant amplitude. Friction gradually narrows each swing, drawing the trajectory toward the equilibrium position.

Phase Portrait: Ellipse to Spiral

The phase portrait — velocity plotted against position — reveals the difference in structure most clearly. The undamped pendulum traces a closed ellipse, returning to exactly the same state each cycle. The damped pendulum traces an inward spiral, each loop slightly smaller than the last, converging on the origin.

bind_rows(
  sim_undamped      |> select(s_position, s_velocity) |> mutate(label = "Undamped (b = 0)"),
  sim_damped_labelled |> select(s_position, s_velocity) |> mutate(label = "Damped (b = 0.3)")
) |>
  ggplot(aes(x = s_position, y = s_velocity, colour = label)) +
  geom_path(linewidth = 0.7) +
  scale_colour_manual(values = c("Undamped (b = 0)" = "grey70",
                                  "Damped (b = 0.3)"  = "#4e8cd4")) +
  labs(x = "Position (metres)", y = "Velocity (m/s)", colour = NULL) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")
Figure 5: Phase portraits of the undamped (grey) and damped (blue) pendulum. The undamped trajectory is a closed ellipse — constant amplitude forever. The damped trajectory spirals inward to the origin, where Position = 0 and Velocity = 0: the bob at rest at its desired position.

Sensitivity: How Much Friction Changes the Behaviour

friction_levels <- c(0.1, 0.3, 0.6)

sim_sensitivity <- map_dfr(friction_levels, function(b) {
  p <- replace(params, "p_friction_coeff", b)
  ode(times = sim_time, y = stocks, parms = p,
      func = model_damped, method = "rk4") |>
    as_tibble() |>
    mutate(across(everything(), as.numeric),
           label = paste0("b = ", b))
})

sim_sensitivity |>
  ggplot(aes(x = time, y = s_position, colour = label)) +
  geom_hline(yintercept = 0, linetype = "dashed", colour = "grey70", linewidth = 0.5) +
  geom_line(linewidth = 0.8) +
  scale_colour_manual(values = c("b = 0.1" = "#a8c8f0",
                                  "b = 0.3" = "#4e8cd4",
                                  "b = 0.6" = "#1a4a7a")) +
  labs(x = "Time (seconds)", y = "Position (metres)", colour = "Friction coefficient") +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")
Figure 6: Three levels of friction coefficient. Higher values of b remove energy faster, compressing the decay into a shorter window. All three runs share the same underlying cross-coupled structure; only the rate of energy loss differs.

Summary

  • The generic two-stock oscillating structure always has space for factors affecting each flow. In the frictionless pendulum, that space for Changing Velocity was partially filled (Gap, gravity, length). In the real pendulum, friction fills it further.
  • Friction enters the Changing Velocity equation as a drag term proportional to current velocity: \(-b \times \text{Velocity}\). It is automatically self-correcting in direction.
  • The result is damped oscillation: the same cross-coupled structure, same period, but with amplitude that decays exponentially toward the equilibrium.
  • The phase portrait transforms from a closed ellipse (sustained oscillation) to an inward spiral (damped oscillation) — one of the most visually direct ways to distinguish the two behaviours.
  • Increasing the friction coefficient accelerates the settling, but the qualitative behaviour — oscillatory decay to equilibrium — remains the same until damping becomes very large.