Foundations for Robotics & Control

From ODEs to State-Space

A mass, a spring, and a damper. One little system holds the seed of everything in control theory: differential equations, oscillation, damping, and the matrix whose eigenvalues decide a robot's fate.

Prerequisites: Basic algebra + knowing that a derivative is a rate of change. That's it. No calculus prerequisites you don't get built here.
5
Chapters
4+
Simulations
0
Assumed Knowledge

Chapter 0: Why a Spring and a Damper?

Push down on the hood of a parked car and let go. It bounces back up, overshoots, sinks a little, comes back up a little less, and after a wobble or two it settles. Now imagine that same car with worn-out shock absorbers: it bounces, and bounces, and keeps bouncing like a boat on the sea. Same car, same spring — the only thing that changed was one part. What is that part actually doing?

Or think of a robot arm reaching for a coffee cup. You command the gripper to a target position. If the controller is "stiff and twitchy," the arm snaps to the target, overshoots, smacks the cup, and oscillates. If it's "sluggish," it oozes toward the cup and takes forever. Somewhere in between is the sweet spot: fast, but no overshoot. That sweet spot has a name, and a precise mathematical condition, and you will be able to compute it by hand by the end of this lesson.

Every one of these stories — the car, the arm, a tuning fork ringing, a building swaying in an earthquake, the needle on an old analog meter, a quadcopter recovering from a gust — is the same system. It is a mass (something with inertia that resists changing its velocity), connected to a spring (something that pulls it back toward a rest position), and a damper (something that bleeds away energy, like friction or a shock absorber). We call it the mass-spring-damper, and it is the "hydrogen atom" of engineering: the simplest thing that already contains every essential behavior.

The one idea of this chapter: The spring stores energy and wants to oscillate forever. The damper steals energy and wants everything to stop. The fight between those two — who wins, and by how much — is what determines whether your system rings, settles smoothly, or crawls. Everything else in this lesson is making that fight precise.

Let's build the picture before we build any math. Picture a block sitting on a frictionless table. A spring connects it to a wall on the left. A damper (a piston in a cylinder of oil — think of a door closer) connects it to the wall too. We pull the block to the right and release it. What happens next depends entirely on the strengths of those two components.

Play with the simulation below. Drag the two sliders and hit Release. The slider k is the spring stiffness — how hard it yanks back. The slider b is the damping — how much the oil resists motion. Notice you don't need a single equation to feel the four distinct personalities this system has.

The Spring-Damper, Before Any Math

Drag the block right or hit Release to pull it to a fixed start and let go. Crank b to zero: it bounces forever (no friction to stop it). Crank b high: it just crawls back without a single bounce.

Spring k6.0
Damping b1.2

Did you find the four personalities? Here they are, named, so you have words for what you just saw. Don't memorize them — you'll derive the boundary between each one in Chapter 2.

What you sawNameThe intuition
Bounces forever, never settlesUndampedNo damper at all (b = 0). Energy is conserved, so it rings eternally.
Bounces a few times, shrinking, then settlesUnderdampedA little damping. It still overshoots and oscillates, but each swing is smaller.
Returns fast, never overshootsCritically dampedThe Goldilocks point. The fastest possible return with zero overshoot. This is the robot-arm sweet spot.
Crawls back slowly, never overshootsOverdampedToo much damping. So sluggish the motion is smothered before it can oscillate.

Why does any of this matter for robotics or control? Because every feedback controller you ever design behaves like a mass-spring-damper. When you write a PD controller for a motor, the "position gain" plays the role of the spring k, and the "velocity gain" plays the role of the damper b. Tuning a controller is choosing k and b for an equivalent spring-damper. If you understand this one toy, you understand the dynamics that controllers fight against and the dynamics they create.

And there's a second, deeper reason, which is the secret destination of this whole lesson. The spring-damper is described by a single equation with a second derivative in it (acceleration). That's awkward for computers and for theory, which love first-order systems. In Chapter 3 we'll perform a magic trick — the state-space transformation — that turns the one awkward second-order equation into a tidy pair of first-order equations wearing a matrix. And in Chapter 4 we'll discover that the four personalities above are secretly encoded in the eigenvalues of that matrix. The car's bounce, the robot's overshoot, the eigenvalues of a 2×2 matrix — all the same thing.

Why this is the right starting point: The spring-damper is the bridge between three worlds that beginners usually meet separately and never connect — physics (forces), differential equations (the math of change), and linear algebra (matrices and eigenvalues). Master this one example and those three worlds collapse into one.
You set the damping slider b to exactly zero and release the block. What do you expect to see, and why?

Chapter 1: Three Forces, One Equation

In Chapter 0 you felt the system. Now we make it precise — not by quoting a formula, but by writing down the forces one at a time and adding them up. By the end of this chapter you will have built the equation of motion from scratch, and every symbol in it will mean something physical to you.

Let x be the position of the block, measured from its rest position — the spot where the spring is neither stretched nor squished. So x = 0 means "at rest position," x > 0 means "pulled to the right," x < 0 means "pushed to the left." This choice (measuring from rest) is what keeps the math clean; it's worth a sentence of thought.

We need two more quantities, and they are both just rates of change of x. The velocity is how fast position changes, written x′ (read "x-prime") or dx/dt. The acceleration is how fast velocity changes, written x″ (read "x-double-prime") or d²x/dt². If those primes feel scary, hold this anchor: x′ is the speedometer, x″ is the gas-and-brake pedal. That's all a derivative is here — a rate.

Newton's law is the whole game. Newton said: mass times acceleration equals the sum of all forces, m·x″ = ΣF. Our entire job in this chapter is to figure out what forces act on the block, write each one in terms of x, x′, and x″, and plug them into that single sentence.

Force 1: The spring (the restoring force)

Stretch a spring twice as far, it pulls back twice as hard. This proportionality is Hooke's Law, and the constant of proportionality is the stiffness k (units: newtons per meter — how many newtons of pull you get per meter of stretch). A stiff spring (large k) yanks hard; a floppy spring (small k) barely tugs.

The crucial detail is the direction. The spring always pulls back toward rest. Pull the block right (x > 0) and the spring pulls left (negative force). Push it left (x < 0) and the spring pulls right (positive force). The force always opposes the displacement — that opposing sign is what makes it a restoring force. So:

Fspring = −k·x

Read that minus sign as a sentence: "the further you go from home, the harder home pulls you back." That single restoring term is the entire reason the system can oscillate at all. Without it (k = 0) there's nothing to pull the block back, so it never returns — it just sits wherever the damper lets it stop.

Force 2: The damper (the friction-like force)

A damper is a piston pushed through thick oil — exactly like the closer at the top of a slow-shutting door. The faster you try to move it, the harder it resists. It doesn't care where the block is; it cares how fast the block is moving. So the damping force is proportional to velocity x′, with constant b (the damping coefficient, units newton-seconds per meter).

And again the direction matters: the damper always opposes the motion. Move right (x′ > 0) and it pushes left; move left (x′ < 0) and it pushes right. The force opposes velocity, so it carries a minus sign:

Fdamp = −b·x′

Here is the physical heartbeat of the whole lesson: the damper is the only term that removes energy. The spring stores energy and gives it all back. The mass carries energy but doesn't create or destroy it. Only the damper, by always pushing against motion, drains the system's energy into heat. That is why b decides whether the system rings forever or settles.

Common confusion: Students mix up the spring and the damper because both "resist." But they resist different things. The spring resists displacement (it depends on x — where you are). The damper resists velocity (it depends on x′ — how fast you're going). Hold the block far from rest but perfectly still: the spring pushes hard, the damper pushes nothing. Whip the block through the rest position at high speed: the spring pushes nothing (x = 0), the damper pushes hard.

Force 3: Inertia (the mass term)

The last "force" isn't really an applied force at all — it's the left side of Newton's law. The mass m resists changes in velocity. Whatever the net force is, it produces acceleration through m·x″. A heavy block (large m) is sluggish; it accelerates slowly. A light block snaps around. We'll move this term to the same side as the others in a moment.

Assembling the equation of motion

Newton: mass × acceleration = sum of forces. The only two forces on our block (it's a frictionless table; the only contacts are the spring and the damper) are the spring force and the damping force:

m·x″ = Fspring + Fdamp = (−k·x) + (−b·x′)

Now we do one tiny piece of algebra: move every term to the left side so the equation equals zero. Add k·x and b·x′ to both sides:

m·x″ + b·x′ + k·x = 0

That's it. That is the equation of motion, and you just built it from three physical sentences. Read it left to right as a story: m·x″ (inertia fighting the change) + b·x′ (damping fighting the speed) + k·x (spring fighting the displacement) = 0 (they're in balance at every instant). The "= 0" means no one is pushing the block from outside — it's just the three internal effects negotiating among themselves. This is called the free response or homogeneous equation ("homogeneous" is jargon for "right side is zero, no external driving force").

Why this form is everywhere: Replace x with charge in a circuit and you get the equation for an RLC circuit (inductor = mass, resistor = damper, capacitor = spring). Replace it with the angle of a pendulum, the sway of a bridge, the voltage in a filter, the position error of a PD-controlled motor — the same three-term equation appears. Learn it once here; recognize it everywhere.

A worked sanity check with real numbers

Let's make sure every term is concrete. Take m = 2 kg, b = 3 N·s/m, k = 8 N/m. Suppose at some instant the block is at x = 0.5 m (pulled right half a meter) and moving at x′ = −1.2 m/s (moving left at 1.2 m/s). What is its acceleration right now?

Compute the spring force: Fspring = −k·x = −8 × 0.5 = −4 N. The spring pulls left (negative), because the block is to the right of rest. Good — that matches intuition.

Compute the damping force: Fdamp = −b·x′ = −3 × (−1.2) = +3.6 N. The damper pushes right (positive), because the block is moving left and the damper opposes that motion. Also matches intuition.

Net force = −4 + 3.6 = −0.4 N. Now Newton: acceleration x″ = (net force) / m = −0.4 / 2 = −0.2 m/s². So at this instant the block is accelerating gently to the left. We computed it from raw forces — no solution formula needed. The equation of motion is just bookkeeping for forces, evaluated instant by instant. (Note this matches plugging into m·x″ + b·x′ + k·x = 0: 2×(−0.2) + 3×(−1.2) + 8×0.5 = −0.4 − 3.6 + 4 = 0. The equation balances. ✓)

The widget below lets you do this for any state. Drag the block to set x, drag the velocity arrow to set x′, and watch the three force arrows update live, along with the resulting acceleration. This is the equation of motion made visible: three arrows that must sum to m·x″.

The Three Forces, Live

Slide position and velocity to set the block's state. The orange arrow is the spring force (−kx), the blue arrow is the damping force (−bx′), and the teal arrow is the resulting net force = m·x″.

Position x0.50
Velocity x′-1.20
Stiffness k8.0
Damping b3.0
Check your understanding by breaking it: In the widget, set velocity to exactly 0 but keep the block displaced. The blue damping arrow vanishes — damping depends only on speed. Now set position to 0 but velocity high: the orange spring arrow vanishes — the spring depends only on position. The two forces watch completely different variables.
Why does the damping term −b·x′ carry a minus sign, just like the spring term?

Chapter 2: Guessing the Answer, and the Four Regimes

We have an equation: m·x″ + b·x′ + k·x = 0. But an equation tells us the rule the motion obeys at every instant; it doesn't yet tell us the trajectory — the actual x(t) curve of position over time. Solving for that curve is the heart of this chapter, and the method is gloriously sneaky: we're going to guess.

The educated guess: what function survives differentiation?

Look at the equation again. We need a function x(t) such that adding up a copy of itself (the kx term), a copy of its first derivative (the bx′ term), and a copy of its second derivative (the mx″ term) gives exactly zero. For that cancellation to be possible at every instant, the function and its derivatives must all have the same shape — otherwise they could never cancel for all t.

What function is its own derivative (up to a constant)? The exponential. If x(t) = eλt, then x′ = λ·eλt and x″ = λ²·eλt. Every derivative is just the original times another factor of λ. That self-similarity is precisely the property we need. So we make the guess (called an ansatz, German for "an attempt"):

x(t) = eλt,    x′(t) = λ·eλt,    x″(t) = λ²·eλt

Here λ (lambda) is an unknown number we're solving for. It controls everything: a negative λ makes eλt decay toward zero (good — the block settles); a positive λ makes it blow up (a system that explodes); and as we'll see, a complex λ secretly hides a sine wave (oscillation). One symbol, all the behaviors.

Plug in and watch the magic cancellation

Substitute the guess into m·x″ + b·x′ + k·x = 0:

m·(λ²·eλt) + b·(λ·eλt) + k·(eλt) = 0

Every single term has a factor of eλt. Factor it out:

eλt · (m·λ² + b·λ + k) = 0

Now the key observation: eλt is never zero — an exponential never touches the axis. So the only way the product can be zero is if the other factor is zero:

m·λ² + b·λ + k = 0

This is the characteristic equation, and it is a thing of beauty. We started with a differential equation (the hard, calculus-flavored kind) and, by the single act of guessing an exponential, we've reduced it to an ordinary quadratic equation — the kind you solved in high school. The calculus has evaporated. All the dynamics are now hidden in the roots of a quadratic.

The trick in one sentence: Guessing x = eλt turns "solve a differential equation" into "solve a quadratic." The pattern m·x″ → m·λ², b·x′ → b·λ, k·x → k is a literal find-and-replace: each derivative becomes a power of λ. This is the most reused trick in all of linear systems.

Solving the quadratic: the roots

A quadratic aλ² + bλ + c = 0 has the famous solution λ = (−b ± √(b² − 4ac)) / (2a). Here a = m, the linear coefficient is b, and c = k. Substituting:

λ = ( −b ± √(b² − 4·m·k) ) / (2m)

Two roots, λ1 and λ2 (the ± gives two). Everything — whether the car bounces or settles, whether the robot overshoots — is decided by one piece of this formula: the quantity under the square root, called the discriminant:

Δ = b² − 4·m·k

The sign of Δ is a fork in the road. It compares b² (damping, squared) against 4mk (spring stiffness scaled by mass). When damping wins, Δ > 0. When the spring wins, Δ < 0. When they exactly balance, Δ = 0. Those three cases — plus the special case b = 0 — are exactly the four personalities you felt in Chapter 0. Let's nail each one with real numbers.

Regime 1 — Undamped (b = 0)

Set m = 1, b = 0, k = 4. The characteristic equation is λ² + 4 = 0, so λ² = −4, giving λ = ±√(−4) = ±2i. The roots are purely imaginary (i = √−1). A purely imaginary root is the mathematical signature of pure oscillation that never decays. Using Euler's identity (eiωt = cosωt + i·sinωt), the solution is x(t) = A·cos(2t) + B·sin(2t) — a sine wave of constant amplitude, ringing forever at angular frequency ω = 2 rad/s. This is the car with no shock absorber. No real part means no decay.

Regime 2 — Underdamped (Δ < 0, b small)

Now add a little damping: m = 1, b = 2, k = 5. Discriminant Δ = b² − 4mk = 4 − 20 = −16. Negative! So √Δ = √(−16) = 4i, and the roots are λ = (−2 ± 4i) / 2 = −1 ± 2i. Complex, with a negative real part (−1) and a nonzero imaginary part (±2).

Decode those two pieces. The real part −1 is a decay rate: it produces an envelope e−1·t that shrinks the motion over time. The imaginary part 2 is an oscillation frequency: it produces sin and cos waves at ω = 2 rad/s. Together they give a decaying oscillation — bounces that shrink. The general underdamped solution is:

x(t) = e−λt·( A·sin(ωt) + B·cos(ωt) )

where here λ = 1 is the decay rate (the magnitude of the real part) and ω = 2 is the ringing frequency (the imaginary part). The constants A and B are fixed by where you start (initial position and velocity). This is the car with worn-but-not-dead shocks, the under-tuned robot arm that overshoots. Underdamped is the only regime that oscillates while still settling.

Common confusion about "complex" roots: Beginners panic at imaginary numbers and think the motion is somehow imaginary or unphysical. It is not. The complex roots always come in conjugate pairs (−1+2i and −1−2i), and when you add the two eλt solutions, the imaginary parts cancel and leave a perfectly real sine/cosine. The complex number is just bookkeeping: its real part is the decay rate, its imaginary part is the frequency. Complex root = decaying wiggle. That's the whole translation.

Regime 3 — Critically damped (Δ = 0)

Tune the damping until the discriminant hits exactly zero. With m = 1, k = 5, we need b² = 4mk = 20, so b = √20 ≈ 4.47. Then Δ = 0, and the ±√0 collapses both roots onto a single value: λ = −b/(2m) = −4.47/2 ≈ −2.24 (a repeated real root). No imaginary part, so no oscillation; the root is real and negative, so it decays. This is the critically damped case: the fastest possible return to rest without a single overshoot. It is the exact boundary — one drop less damping and it would overshoot; one drop more and it would crawl.

Critical damping is the holy grail of mechanical design: car suspensions, screen-door closers, scale needles, and well-tuned servo loops are all designed to sit right here. Fast, but clean.

Regime 4 — Overdamped (Δ > 0)

Pile on more damping: m = 1, b = 6, k = 5. Discriminant Δ = 36 − 20 = +16. Positive, so √16 = 4 is a real number, and the roots are λ = (−6 ± 4)/2 = {−1, −5} — two distinct real, negative roots. No imaginary part anywhere, so no oscillation. Two pure decaying exponentials: x(t) = A·e−t + B·e−5t. The motion is so thoroughly smothered by friction it can't even complete one swing. It just oozes home. Slower than critical — the price of over-insuring against overshoot.

RegimeConditionRoots λBehavior
Undampedb = 0±ωi (pure imaginary)Rings forever
UnderdampedΔ < 0−λ ± ωi (complex)Decaying oscillation
Critically dampedΔ = 0One repeated real rootFastest, no overshoot
OverdampedΔ > 0Two distinct real rootsSlow crawl, no overshoot

The widget below is the regime explorer. As you drag b, watch the discriminant Δ flip sign, watch the two roots move in the complex plane (the right panel), and watch the resulting x(t) trajectory (the left panel). The whole story of this chapter lives in how those two dots move as you turn one knob.

Roots, Regimes, and Trajectory

Left: the position trajectory x(t). Right: the two roots plotted in the complex plane (horizontal = real part / decay, vertical = imaginary part / frequency). Drag b and watch the dots dive toward the real axis as you cross from underdamped to overdamped.

Mass m1.0
Damping b2.0
Stiffness k5.0
The single decision rule: Compute Δ = b² − 4mk. Negative → oscillates (underdamped). Zero → critically damped. Positive → overdamped. And b = 0 is the undamped corner. One number, Δ, sorts every spring-damper into its personality. Memorize this, not the four solution forms.
A system has m = 1, k = 9, and you set b = 4. Compute Δ = b² − 4mk and classify it.

Chapter 3: State-Space — Turning One Hard Equation Into Two Easy Ones

We can now solve the spring-damper by hand. But there's a problem lurking that you'll hit the moment you try to put this on a computer, or chain many of these together, or build a controller around it: the equation has a second derivative in it. m·x″ + b·x′ + k·x = 0 is a second-order ODE, and almost every tool in numerical computing, control theory, and machine learning is built for first-order systems — equations where the highest derivative is just a single prime.

So we need a way to rewrite one second-order equation as a system of first-order equations. The trick that does this is the single most important reframing in all of control theory and robotics, and it's called the state-space form. Once you see it you'll use it forever.

The core idea, before the algebra: A second-order equation is hard because it talks about acceleration, which depends on velocity, which depends on position — three layered quantities. The fix is to stop tracking position alone and start tracking the full state: position AND velocity together, as a pair. If you always carry both, then "what happens next" only ever needs one derivative of each. The cost (a 2-vector instead of a scalar) buys you a first-order system.

Step 1: Name the state variables

The state of a system is the minimal set of numbers you need to predict its entire future. For the spring-damper, knowing only the position is not enough — a block at x = 0 could be flying right, flying left, or sitting still, and those have wildly different futures. You also need the velocity. So the state is the pair (position, velocity). We give each a clean name:

y1 = x   (position)       y2 = x′   (velocity)

This is the entire creative leap, and it's deceptively simple: we promoted velocity from a derivative to a first-class variable with its own name. Instead of one quantity x and its hidden derivatives, we now have two quantities y1 and y2 standing side by side. Everything that follows is just turning the crank.

Step 2: Differentiate each new variable

We want a first-order system, which means we need to express y1′ and y2′ — the rates of change of our two state variables — purely in terms of y1 and y2 (no second derivatives allowed). Let's compute them one at a time.

The first equation is almost free. What is y1′? Since y1 = x, its derivative is y1′ = x′. But we just named x′ as y2! So:

y1′ = x′ = y2

Read it as plain English: "the rate of change of position is the velocity." Obvious, almost insultingly so — and yet it is half of our system. The definition of velocity is itself one of our two equations. This is the gift the change of variables hands us for free.

The second equation needs the physics. What is y2′? Since y2 = x′, its derivative is y2′ = x″ — the acceleration. And acceleration is exactly what the equation of motion tells us. Go back to m·x″ + b·x′ + k·x = 0 and solve it for x″:

m·x″ = −k·x − b·x′   ⇒   x″ = −(k/m)·x − (b/m)·x′

We just isolated acceleration: it's a weighted combination of position and velocity. Now substitute our state names — x is y1, and x′ is y2:

y2′ = x″ = −(k/m)·y1 − (b/m)·y2

And there is no second derivative left in sight — we expressed x″ entirely in terms of y1 and y2. We've successfully traded one second-order equation for two first-order ones. Let's set them side by side:

y1′ = y2
y2′ = −(k/m)·y1 − (b/m)·y2

Step 3: Take m = 1 and write it as a matrix

To see the structure cleanly, set m = 1 (you can always rescale time later to recover any mass). The system becomes:

y1′ = 0·y1 + 1·y2
y2′ = −k·y1 − b·y2

Notice I padded the first equation with "0·y1 + 1·y2" to make the pattern explicit: each y′ is a linear combination of y1 and y2. Whenever you see "outputs are linear combinations of inputs," you are looking at a matrix-times-vector in disguise. Stack y1 and y2 into a state vector y, collect the four coefficients into a 2×2 matrix, and the whole system collapses to one line:

y′ = A·y,    where  y = [ y1 ; y2 ]  and  A = [ 0  1 ; −k  −b ]

Read off the matrix entries against the two scalar equations to confirm them. Row 1 is [0, 1], because y1′ = 0·y1 + 1·y2. Row 2 is [−k, −b], because y2′ = −k·y1 − b·y2. That matrix A is called the state matrix (or system matrix), and it is the single most important object in linear control theory. Every property of the system — stability, oscillation, speed of response — is encoded in those four numbers.

Why the top row is always [0, 1]: That row is not physics — it's pure bookkeeping. It encodes the definition "velocity is the derivative of position," which is true for any mechanical system. Only the bottom row carries the actual physics (the forces, via k and b). This pattern — an identity-like shift on top, the dynamics on the bottom — is the universal shape of state-space models built from Newton's law. You'll recognize it in every robot you ever model.

A fully worked change of variables with numbers

Let's grind one all the way through so nothing is abstract. Take the underdamped system from Chapter 2: m = 1, b = 2, k = 5. The state matrix is A = [[0, 1], [−5, −2]]. Now suppose the block is at state y = [y1, y2] = [0.4, −0.3] (40 cm right of rest, drifting left at 0.3 m/s). What is the instantaneous rate of change of the whole state, y′ = A·y?

Matrix-vector multiply, row by row. Row 1: y1′ = 0×0.4 + 1×(−0.3) = −0.3. (Of course — the rate of change of position equals the velocity, −0.3, exactly as our top row promised.) Row 2: y2′ = (−5)×0.4 + (−2)×(−0.3) = −2.0 + 0.6 = −1.4. So the velocity is changing at −1.4 m/s² — the block is accelerating leftward.

So y′ = [−0.3, −1.4]. In one matrix multiply we got both the velocity and the acceleration of the system at this instant. That single line, y′ = A·y, repeated over and over, is a numerical simulator for the spring-damper. The simulation you played with in Chapter 0 was secretly doing exactly this multiply, thousands of times per second.

From state-space to code (it's a five-line simulator)

Here is the entire spring-damper, simulated, in plain Python. Notice there is no special "ODE solver" magic — we just apply y′ = A·y and step forward a tiny bit in time (Euler's method). The matrix is the program.

python
import numpy as np

# Pick the system (m=1 baked in)
k, b = 5.0, 2.0
A = np.array([[0, 1],
              [-k, -b]])          # the state matrix

y  = np.array([1.0, 0.0])   # start: x=1, v=0 (pulled right, released)
dt = 0.01                       # tiny time step

for step in range(1000):
    ydot = A @ y                 # y' = A y   (the whole physics, one line)
    y    = y + ydot * dt         # Euler step: nudge state forward
    # y[0] is position now, y[1] is velocity now

Trace one iteration by hand to convince yourself it matches our arithmetic above. Start near y = [0.4, −0.3]. Then A @ y gives ydot = [−0.3, −1.4] — the exact numbers we computed. The new state is y + ydot·dt = [0.4, −0.3] + [−0.3, −1.4]×0.01 = [0.397, −0.314]. Position drifted slightly left, velocity got slightly more negative. Do that a thousand times and you've drawn the whole trajectory. That's it — that's how every physics engine and every robot simulator works under the hood.

Common confusion: "Didn't we lose information by dropping to first order?" No — we relabeled it. The second derivative didn't disappear; it became y2′ (the derivative of the velocity-variable). We have the same amount of information, just organized as a 2-vector evolving by a 2×2 matrix instead of a scalar with hidden higher derivatives. Nothing was thrown away; everything was made first-order and explicit.
In the state matrix A = [[0, 1], [−k, −b]], why is the top row always exactly [0, 1], regardless of the spring or damper values?

Chapter 4: Eigenvalues — The Regimes Were Hiding in the Matrix

Here is the payoff that ties the whole lesson into a knot. In Chapter 2 we found that the system's personality is decided by the roots λ of the characteristic equation mλ² + bλ + k = 0. In Chapter 3 we packed the same system into a state matrix A = [[0, 1], [−k, −b]]. The stunning fact is: those roots λ are exactly the eigenvalues of A. The decay rates and oscillation frequencies you computed by hand are the eigenvalues of a 2×2 matrix. The two stories are one story.

What an eigenvalue is, in one breath: A matrix A usually rotates and stretches vectors. But for special directions — the eigenvectors — A only stretches, never rotates: A·v = λ·v. The stretch factor λ is the eigenvalue. For our dynamics y′ = A·y, an eigenvector is a direction in (position, velocity) space along which the motion just scales by eλt — a "natural mode" of the system. The eigenvalues λ tell you how fast each mode decays and whether it wiggles.

Computing the eigenvalues by hand

Eigenvalues come from the condition det(A − λI) = 0, where I is the identity matrix. Let's just do it for our A and watch something beautiful fall out. First form A − λI by subtracting λ from the diagonal:

A − λI = [ 0−λ   1 ; −k   −b−λ ] = [ −λ   1 ; −k   −b−λ ]

The determinant of a 2×2 matrix [[p, q], [r, s]] is ps − qr. Here p = −λ, q = 1, r = −k, s = −b−λ. So:

det(A − λI) = (−λ)(−b−λ) − (1)(−k)

Expand the first product: (−λ)(−b−λ) = λb + λ² = λ² + bλ. And the second term: −(1)(−k) = +k. So the determinant is λ² + bλ + k. Setting it to zero:

λ² + bλ + k = 0

Look at that. With m = 1, this is identically the characteristic equation from Chapter 2 (mλ² + bλ + k = 0 with m = 1). The "find the eigenvalues of the state matrix" calculation and the "guess eλt and solve the quadratic" calculation produce the exact same equation. They were never two different things. The characteristic polynomial of the ODE is the characteristic polynomial of its state matrix.

The grand unification of this lesson: Roots of the ODE = eigenvalues of the state matrix. So everything you learned about regimes transfers directly to linear algebra. Eigenvalues with negative real part → the system decays → stable. Eigenvalues with positive real part → the system blows up → unstable. Eigenvalues with nonzero imaginary part → the system oscillates. This is the central theorem of linear control theory, and you just derived it for a spring.

Reading the four regimes off the eigenvalues

Now re-read Chapter 2's table through the eigenvalue lens. The eigenvalues live in the complex plane, and where they sit tells you everything:

RegimeEigenvalue locationReal part →Imag part →
UndampedOn the imaginary axis (±ωi)Zero → no decayNonzero → rings forever
UnderdampedLeft half, off the real axisNegative → decaysNonzero → oscillates
Critically dampedOne repeated point on neg. real axisNegative → decaysZero → no oscillation
OverdampedTwo points on neg. real axisNegative → decaysZero → no oscillation

The horizontal axis (real part) is the decay/growth axis: further left = faster settling, right of zero = explosion. The vertical axis (imaginary part) is the oscillation axis: further from the real axis = faster ringing. Damping pulls the eigenvalues down toward the real axis (killing oscillation) and the discriminant Δ = b² − 4mk is just the test for whether they've reached it yet.

A worked eigenvalue computation

Take the overdamped system m = 1, b = 6, k = 5, so A = [[0, 1], [−5, −6]]. The characteristic polynomial is λ² + 6λ + 5 = 0. Factor it: (λ + 1)(λ + 5) = 0, giving eigenvalues λ = −1 and λ = −5. Both real, both negative — so the system decays without oscillating (overdamped), exactly as Chapter 2 predicted. The −5 eigenvalue is the "fast mode" (decays as e−5t, gone in a flash); the −1 is the "slow mode" (e−t, the lingering tail). The overall settling speed is governed by the slowest eigenvalue, −1 — which is why overdamped systems are sluggish: one lazy eigenvalue drags out the whole response.

Now compare against critically damped (b = 2√5 ≈ 4.47): the polynomial is λ² + 4.47λ + 5 = 0 with the repeated root λ = −2.24. Both eigenvalues sit on top of each other at −2.24 — further left than the overdamped slow mode (−1)! That is the precise reason critical damping is faster than overdamping: it pushes the slowest eigenvalue as far left as possible before oscillation kicks in. Maximizing the worst-case decay rate — that's what "critically damped" means in eigenvalue language.

Eigenvalues in the Complex Plane — The Whole Story, One View

Drag b from 0 upward. The two eigenvalues start on the imaginary axis (undamped, ringing forever), sweep left and down as damping grows (underdamped, decaying wiggles), collide on the real axis at the critical point, then split apart along the real axis (overdamped). The shaded region is the stable left-half-plane.

Mass m1.0
Damping b1.0
Stiffness k5.0
The misconception this kills: Students think "eigenvalues" are an abstract linear-algebra exam topic with no physical meaning. Wrong. For any dynamical system y′ = A·y, the eigenvalues of A are the system's natural frequencies and decay rates — the literal answer to "how does this thing move?" When an aerospace engineer says "the spiral mode is unstable" or a roboticist says "place the poles at −3 ± 2i," they are talking about eigenvalues of a state matrix. This little spring is your first encounter with the most practical idea in all of control.

Where this goes next

You've now seen the full arc: physical forces → a second-order ODE → a quadratic and its four regimes → a first-order state-space system → eigenvalues of a matrix. That arc is the backbone of estimation and control. A few doors this opens:

In a Kalman filter, the system you're tracking is written in exactly this state-space form, y′ = A·y (often with added noise and control). The "state" is the same idea — position and velocity bundled together — and the matrix A propagates your belief forward in time. Everything you built here is the skeleton the Kalman filter hangs flesh on.

In control and reinforcement learning, designing a controller often means choosing where to place the eigenvalues of the closed-loop state matrix — literally dragging the dots in that complex-plane widget to a location that gives fast, non-oscillating behavior. That technique is called pole placement, and "poles" are just another word for these eigenvalues.

And the moment your system isn't linear (a real robot arm with gravity and joint limits), you'll linearize it around an operating point to get a local state matrix A, then read its eigenvalues to ask "is this configuration stable?" The spring-damper is the rehearsal for that move, performed exactly once and reused forever.

The cheat sheet (carry this with you):
• Equation of motion: m·x″ + b·x′ + k·x = 0 — inertia + damping + restoring = 0.
• Characteristic eq: mλ² + bλ + k = 0; roots λ = (−b ± √(b²−4mk))/(2m).
• Discriminant Δ = b² − 4mk: Δ<0 underdamped (wiggle), Δ=0 critical, Δ>0 overdamped, b=0 undamped.
• State-space (m=1): y1=x, y2=x′, y′ = A·y with A = [[0, 1], [−k, −b]].
• Eigenvalues of A = roots of the ODE. Negative real part = stable. Nonzero imaginary part = oscillates.
A robot's linearized state matrix has eigenvalues λ = +0.5 ± 3i. What does this predict about the robot's behavior near that operating point?

“What I cannot create, I do not understand.” You just created the spring-damper from three forces, solved it four ways, rebuilt it as a matrix, and found its soul in two eigenvalues. That is the whole method of physics, estimation, and control, rehearsed on the simplest thing that contains it all. — after R. Feynman

Code Lab — build the state matrix, watch it move

Chapter 3 turned the second-order spring equation into the first-order state-space system y′ = A·y with A = [[0, 1], [−k, −b]]. The integrator (RK4) is handed to you fully built. You assemble the matrix A — and the whole motion lives or dies on its bottom-right entry, the single number that turns an eternal ring into a settling spiral. Fill the TODO, press Run, and watch the phase portrait collapse from a closed circle into an inward spiral. (Python boots once on the first Run, ~5s.)

Exercises & Workbook

The lesson taught you the spring-damper four ways. This workbook makes you create it — every exercise asks you to derive, compute, implement, or design, never just recall. Work them with a blank sheet and a pencil; the answers check your arithmetic, and every derivation reveal walks the full path. Nothing is a placeholder.

Mastery
0 / 35 exercises (0%)
0
Day Streak
Best: 0

Part A — Deriving the characteristic equation

The whole calculus of the spring-damper collapses to one quadratic the moment you guess x = eλt. These exercises make you perform that collapse, then run it forward and backward.

A.1 — The second derivative of the ansatz Trace
You substitute the guess x(t) = eλt into the equation. What is x″(t), the second derivative?
A.2 — Derive the characteristic equation from scratch Derive

Substitute x = eλt into m·x″ + b·x′ + k·x = 0, factor out eλt, and you are left with a quadratic m·λ² + b·λ + k = 0. For the concrete system m = 2, b = 6, k = 4, what is the coefficient of λ² in that characteristic equation?

Hint: the coefficient of λ² is exactly the multiplier on x″.

coeff of λ²
Show derivation
x = eλt ⇒ x′ = λeλt,   x″ = λ²eλt
mλ²eλt + bλeλt + keλt = 0
eλt(mλ² + bλ + k) = 0  ⇒  mλ² + bλ + k = 0
m = 2:   2λ² + 6λ + 4 = 0   → coeff of λ² is 2

Each derivative becomes a power of λ: x″→λ², x′→λ, x→1. The multiplier rides along untouched, so m·x″ becomes m·λ². The coefficient is the mass, 2.

A.3 — Why eλt can be factored away Trace
After substituting, you reach eλt(mλ² + bλ + k) = 0. Why may you drop the eλt and keep only the quadratic?
A.4 — Read the ODE back from its characteristic equation Derive

Run the trick in reverse. A spring-damper has characteristic equation 3λ² + 12λ + 9 = 0. The find-and-replace λ²→x″, λ→x′, 1→x recovers the ODE 3x″ + 12x′ + 9x = 0. What is the damping coefficient b of this system?

b
Show derivation
3λ² + 12λ + 9 = 0
λ² → x″,   λ → x′,   const → x
3x″ + 12x′ + 9x = 0   → (m, b, k) = (3, 12, 9)

The coefficient on λ is always the damping b, because the b·x′ term turns into b·λ. The map between an ODE and its characteristic polynomial is a perfect dictionary — you can walk it either direction.

A.5 — Solve the quadratic for the two roots Derive

For m = 1, b = 7, k = 12 the characteristic equation is λ² + 7λ + 12 = 0. It factors with two real roots. Enter the more negative root (the faster-decaying one).

Hint: find two numbers that multiply to 12 and add to 7.

λ
Show derivation
λ² + 7λ + 12 = (λ + 3)(λ + 4) = 0
λ = −3   or   λ = −4

Both negative → the system decays without oscillating (overdamped). The more negative root, −4, is the fast mode (e−4t); −3 is the slow mode that governs the lingering tail.

A.6 — The buggy ansatz substitution Debug

A student substitutes x = eλt to build the characteristic equation. Exactly one line is wrong. Click it.

x      = e^(λt)
x'     = λ·e^(λt)
x''    = λ·e^(λt)          # second derivative
plug:  m·λ²e^(λt) + b·λe^(λt) + k·e^(λt) = 0
factor: e^(λt)·(m·λ² + b·λ + k) = 0
since e^(λt) ≠ 0:  m·λ² + b·λ + k = 0
The Part A formula. Substituting x = eλt turns m·x″ + b·x′ + k·x = 0 into m·λ² + b·λ + k = 0. Derivative → power of λ. If you can run that both directions from a blank page, you own the characteristic equation.

Part B — The discriminant decides everything

One number, Δ = b² − 4mk, sorts every spring-damper into its personality. These exercises drill the four-regime classification on the concrete family m = 1, k = 4 with growing damping.

B.1 — Δ for the undamped corner (b = 0) Derive

Take m = 1, k = 4, b = 0. Compute the discriminant Δ = b² − 4mk.

Δ
Show derivation
Δ = b² − 4mk = 0² − 4(1)(4) = 0 − 16 = −16

Δ is negative, but because b = 0 exactly the system is the special undamped case: roots are ±2i (purely imaginary), so it rings forever at ω = 2 rad/s with no decay. Zero damping = zero real part.

B.2 — Δ with a little damping (b = 2) Derive

Same spring, m = 1, k = 4, now b = 2. Compute Δ = b² − 4mk.

Δ
Show derivation
Δ = 2² − 4(1)(4) = 4 − 16 = −12

Δ < 0 → underdamped. Roots: λ = (−2 ± √−12)/2 = −1 ± √3·i ≈ −1 ± 1.73i. Negative real part (decays), nonzero imaginary part (wiggles). The only regime that oscillates while settling.

B.3 — Δ at the critical point (b = 4) Derive

Same spring, m = 1, k = 4, now b = 4. Compute Δ = b² − 4mk.

Δ
Show derivation
Δ = 4² − 4(1)(4) = 16 − 16 = 0

Δ = 0 → critically damped, the boundary. The ±√0 collapses both roots onto one: λ = −b/(2m) = −4/2 = −2 (repeated, real, negative). Fastest possible return with zero overshoot. This is b = 2√(mk) = 2√4 = 4.

B.4 — Δ with heavy damping (b = 6) Derive

Same spring, m = 1, k = 4, now b = 6. Compute Δ = b² − 4mk.

Δ
Show derivation
Δ = 6² − 4(1)(4) = 36 − 16 = +20

Δ > 0 → overdamped. Roots: λ = (−6 ± √20)/2 = −3 ± √5 ≈ {−0.76, −5.24}. Two distinct real negative roots, no imaginary part → no oscillation, just a slow crawl home.

B.5 — Name the regime from Δ Trace
Across the family above (m = 1, k = 4), you computed Δ = −16, −12, 0, +20 for b = 0, 2, 4, 6. Which row correctly names all four regimes in order?
B.6 — Solve for the critical damping Derive

A robot joint has m = 2, k = 8. You want the fastest settling with no overshoot — critical damping, where Δ = 0. Solve b² = 4mk for the critical b.

bcrit
Show derivation
Δ = 0 ⇒ b² = 4mk = 4(2)(8) = 64
bcrit = √64 = 8   (= 2√(mk) = 2√16 = 8)

One drop less and it overshoots; one drop more and it crawls. bcrit = 2√(mk) is the design target for car suspensions, door closers, and well-tuned servos.

B.7 — The buggy regime classifier Debug

This function should return the damping regime. One line has a sign/logic bug that misclassifies systems. Click the buggy line.

def regime(m, b, k):
    D = b*b - 4*m*k          # discriminant
    if b == 0:
        return "undamped"
    if D > 0:                # negative => oscillates
        return "underdamped"
    if D == 0:
        return "critically damped"
    return "overdamped"
The Part B rule. Δ = b² − 4mk. Negative → underdamped (wiggle). Zero → critical. Positive → overdamped. b = 0 → undamped corner. Critical b = 2√(mk). One number sorts every spring-damper.

Part C — Building the state-space form by hand

The second-order ODE becomes a first-order matrix system once you name y1 = x, y2 = x′. These exercises make you turn that crank and verify y′ = A·y entry by entry.

C.1 — The free top-row equation Trace
With y1 = x and y2 = x′, what is y1′, the derivative of the first state variable?
C.2 — Solve the ODE for acceleration Derive

For the bottom row we need x″. Starting from m·x″ + b·x′ + k·x = 0 with m = 1, b = 2, k = 5, the acceleration is x″ = −k·x − b·x′. Evaluate x″ at the state x = 1, x′ = 0.

x″
Show derivation
m·x″ = −k·x − b·x′ ⇒ x″ = −(k/m)x − (b/m)x′
m = 1:   x″ = −5(1) − 2(0) = −5 + 0 = −5

Released from rest at x = 1, the spring yanks back hard (−5 m/s²) and the damper does nothing yet because the velocity is still zero. This is exactly the bottom-row computation y2′ = −k·y1 − b·y2.

C.3 — The bottom-left entry of A Derive

Hand-verify the conversion y1=x, y2=x′ ⇒ y′ = [[0, 1], [−k, −b]]·y. For m = 1, b = 3, k = 7, what is the entry A21 (bottom-left, the multiplier on y1 in the y2′ equation)?

A21
Show derivation
y1′ = x′ = y2  → row 1 = [0, 1]
y2′ = x″ = −k·y1 − b·y2  → row 2 = [−k, −b]
A = [[0, 1], [−7, −3]]  ⇒  A21 = −k = −7

The bottom-left entry is −k: the spring's restoring pull. The bottom-right is −b: the damper. The top row [0, 1] is pure bookkeeping (velocity is the derivative of position) and never changes.

C.4 — Why the top row is always [0, 1] Trace
Across every spring, damper, and mass value, the top row of A stays exactly [0, 1]. Why?
C.5 — One matrix-vector multiply, by hand Derive

Underdamped system m = 1, b = 2, k = 5, so A = [[0, 1], [−5, −2]]. The state is y = [0.4, −0.3]. Compute the bottom entry of y′ = A·y, i.e. y2′.

y2
Show derivation
Row 2: y2′ = (−5)(0.4) + (−2)(−0.3)
= −2.0 + 0.6 = −1.4

(Row 1 gives y1′ = 0·0.4 + 1·(−0.3) = −0.3 = the velocity, as the top row promises.) One matrix multiply yields both velocity and acceleration. Repeat it thousands of times per second and you have a physics simulator.

C.6 — Implement the state derivative Build

Write ydot(y1, y2, k, b) returning the array [y1′, y2′] for the m = 1 system y′ = [[0, 1], [−k, −b]]·y. Return a JavaScript array [a, b].

Show solution
javascript
function ydot(y1, y2, k, b) {
  return [y2, -k * y1 - b * y2];
}
C.7 — Order the state-space recipe Design

Put the steps of the ODE→state-space conversion in the correct order.

?
?
?
?
solve ODE for x″ name y1=x, y2=x′ collect into A·y differentiate y1, y2
C.8 — The buggy state-space matrix Debug

This code builds the state matrix A for m = 1. One line has a sign error that flips the spring into a repeller. Click it.

import numpy as np
k, b = 5.0, 2.0
A = np.array([[0, 1],           # y1' = y2
              [k, -b]])         # y2' = -k*y1 - b*y2
y = np.array([1.0, 0.0])
ydot = A @ y                    # the whole physics
The Part C move. y1=x, y2=x′ turns m·x″ + b·x′ + k·x = 0 into y′ = [[0, 1], [−k/m, −b/m]]·y. Top row is free (definition of velocity); bottom row is the physics. The 2×2 matrix is the whole system.

Part D — Simulating and reading eigenvalues

The matrix is the program. These exercises run a Euler step by hand, then connect the eigenvalues of A back to the regimes — the grand unification of the lesson.

D.1 — One Euler step Derive

The Euler update is y ← y + y′·dt. With y = [0.4, −0.3], y′ = [−0.3, −1.4] (from C.5), and dt = 0.01, compute the new position y1.

new y1
Show derivation
new y1 = y1 + y1′·dt = 0.4 + (−0.3)(0.01)
= 0.4 − 0.003 = 0.397

Position drifted slightly left because the velocity is negative. Do this a thousand times and you have drawn the whole trajectory — that is every physics engine under the hood.

D.2 — Eigenvalues = roots of the characteristic equation Derive

For A = [[0, 1], [−k, −b]], det(A − λI) = λ² + bλ + k. Take the overdamped system k = 5, b = 6: λ² + 6λ + 5 = 0 factors as (λ+1)(λ+5). Enter the slow-mode eigenvalue (the one closer to zero, which governs settling speed).

λslow
Show derivation
det(A − λI) = (−λ)(−b−λ) − (1)(−k) = λ² + bλ + k
λ² + 6λ + 5 = (λ+1)(λ+5) = 0 ⇒ λ = −1, −5

Slow mode = −1 (decays as e−t, the lingering tail); fast mode = −5. The slowest eigenvalue sets the overall settling time — one lazy eigenvalue drags out the whole response.

D.3 — Stability from the real part Trace
A linearized robot has eigenvalues λ = +0.5 ± 3i. What does this predict?
D.4 — Decode a complex eigenvalue Derive

An underdamped system has eigenvalues λ = −1 ± 2i. The real part is the decay rate and the imaginary part is the ringing frequency ω. Enter the ringing frequency ω (rad/s).

ω rad/s
Show derivation
λ = −1 ± 2i → real part −1, imag part ±2
x(t) = e−1·t(A·cos 2t + B·sin 2t),   ω = 2

The envelope e−t shrinks the motion (decay rate 1); the cos/sin ring at ω = 2 rad/s. Complex eigenvalue = decaying wiggle, decoded into its two real parts.

D.5 — Implement the discriminant classifier Build

Write discriminant(m, b, k) returning Δ = b² − 4mk. (A correct Δ is all the regime test ever needs.)

Show solution
javascript
function discriminant(m, b, k) {
  return b * b - 4 * m * k;
}
D.6 — Implement one Euler step Build

Write eulerStep(y1, y2, k, b, dt) that takes one explicit Euler step of y′ = [[0,1],[−k,−b]]·y and returns the new [y1, y2].

Show solution
javascript
function eulerStep(y1, y2, k, b, dt) {
  var d1 = y2;
  var d2 = -k * y1 - b * y2;
  return [y1 + d1 * dt, y2 + d2 * dt];
}

Check the second case: d1 = −0.3, d2 = −5(0.4)−2(−0.3) = −1.4. New = [0.4−0.003, −0.3−0.014] = [0.397, −0.314]. Matches the by-hand step in D.1.

D.7 — Place the poles Design

A controller designer wants the eigenvalues (“poles”) placed for the fastest non-oscillating response. Order these four eigenvalue locations from most desirable (fast, stable, no oscillation) to least (unstable).

?
?
?
?
−1 ± 3i (decaying wiggle) −8, −8 (fast real) −0.3, −9 (slow tail) +2, −5 (unstable)
D.8 — The buggy eigenvalue check Debug

This code decides whether a system is stable from its eigenvalue real parts. One line states the stability rule backward. Click it.

def is_stable(eigs):
    # eigs is a list of complex eigenvalues
    for lam in eigs:
        if lam.real > 0:        # positive real part => stable
            return False
    return True                 # all real parts <= 0 => stable
The grand unification. Roots of the ODE = eigenvalues of A = poles of the system. Negative real part → stable (decays). Positive real part → unstable (blows up). Nonzero imaginary part → oscillates. The whole lesson, in one sentence about a 2×2 matrix.

Part E — Capstone: design a critically-damped joint

One end-to-end problem chaining every skill: physics → characteristic equation → discriminant → state matrix → eigenvalues. You are tuning a robot joint modeled as a spring-damper with m = 1, k = 9.

E.1 — Find the critical damping Derive

Step 1: for no overshoot at maximum speed you need Δ = 0. Solve b² = 4mk for the critical b with m = 1, k = 9.

bcrit
Show derivation
b² = 4mk = 4(1)(9) = 36 ⇒ bcrit = √36 = 6

Equivalently bcrit = 2√(mk) = 2√9 = 6. Below 6 it overshoots; above 6 it crawls.

E.2 — The repeated eigenvalue Derive

Step 2: with b = 6, the characteristic equation λ² + 6λ + 9 = 0 has a repeated root. Enter it.

λ
Show derivation
λ² + 6λ + 9 = (λ + 3)² = 0 ⇒ λ = −3 (repeated)
also λ = −b/(2m) = −6/2 = −3 ✓

Both eigenvalues sit at −3, real and negative: critically damped, decaying as e−3t with no oscillation.

E.3 — Verify the acceleration at release Derive

Step 3: with A = [[0, 1], [−9, −6]] the joint is released from y = [1, 0]. Compute y2′ (the acceleration at release) via row 2 of A·y.

y2
Show derivation
y2′ = (−9)(1) + (−6)(0) = −9 + 0 = −9

At release the velocity is zero, so the damper contributes nothing; the spring alone yanks back at −9 m/s². The joint snaps toward home, decelerates smoothly, and settles without overshoot — the critically damped sweet spot.

E.4 — Classify three candidate tunings Trace
Still m = 1, k = 9. A teammate proposes b = 4. Compute Δ = b² − 4mk and pick the correct statement.
E.5 — Build the full settling-time classifier Build

Capstone code. Write classify(m, b, k) that returns one of the strings "undamped", "underdamped", "critical", or "overdamped" using Δ = b² − 4mk (and the b = 0 corner). Treat |Δ| < 1e-9 as critical.

Show solution
javascript
function classify(m, b, k) {
  var D = b * b - 4 * m * k;
  if (b === 0) return "undamped";
  if (Math.abs(D) < 1e-9) return "critical";
  if (D < 0) return "underdamped";
  return "overdamped";
}

Order matters: test the critical (Δ≈0) case before the strict < / > cases, or floating-point noise misroutes the boundary. This one function is the entire regime decision used by every controller designer.

E.6 — Pick the winning tuning Trace
m = 1, k = 9. Three engineers ship b = 4, b = 6, b = 10. Which one gives the fastest return to target with no overshoot — and why?
You designed it. From mass and stiffness alone you found the critical damping (b = 2√mk = 6), proved it sits at the repeated eigenvalue −3, verified the release acceleration, and rejected the over- and under-damped tunings. That is the entire pole-placement workflow, rehearsed on a spring.

Related Lessons

Where this goes nextLesson
Tracking a state-space system through noiseKalman Filter
Choosing eigenvalue locations to control behaviorMDPs & Control
The recursive Bayesian estimator this form feedsBayes Filter

“What I cannot create, I do not understand.” You just created the characteristic equation, classified every regime by hand, built the state matrix entry by entry, and read the system's fate off its eigenvalues. — after R. Feynman