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.
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.
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.
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.
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 saw | Name | The intuition |
|---|---|---|
| Bounces forever, never settles | Undamped | No damper at all (b = 0). Energy is conserved, so it rings eternally. |
| Bounces a few times, shrinking, then settles | Underdamped | A little damping. It still overshoots and oscillates, but each swing is smaller. |
| Returns fast, never overshoots | Critically damped | The Goldilocks point. The fastest possible return with zero overshoot. This is the robot-arm sweet spot. |
| Crawls back slowly, never overshoots | Overdamped | Too 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.
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.
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:
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.
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:
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.
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.
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:
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:
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").
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″.
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″.
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.
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"):
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.
Substitute the guess into m·x″ + b·x′ + k·x = 0:
Every single term has a factor of eλt. Factor it out:
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:
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.
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:
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:
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.
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.
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:
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.
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.
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.
| Regime | Condition | Roots λ | Behavior |
|---|---|---|---|
| Undamped | b = 0 | ±ωi (pure imaginary) | Rings forever |
| Underdamped | Δ < 0 | −λ ± ωi (complex) | Decaying oscillation |
| Critically damped | Δ = 0 | One repeated real root | Fastest, no overshoot |
| Overdamped | Δ > 0 | Two distinct real roots | Slow 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.
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.
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 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:
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.
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:
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″:
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:
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:
To see the structure cleanly, set m = 1 (you can always rescale time later to recover any mass). The system becomes:
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:
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.
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.
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.
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.
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:
The determinant of a 2×2 matrix [[p, q], [r, s]] is ps − qr. Here p = −λ, q = 1, r = −k, s = −b−λ. So:
Expand the first product: (−λ)(−b−λ) = λb + λ² = λ² + bλ. And the second term: −(1)(−k) = +k. So the determinant is λ² + bλ + k. Setting it to zero:
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.
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:
| Regime | Eigenvalue location | Real part → | Imag part → |
|---|---|---|---|
| Undamped | On the imaginary axis (±ωi) | Zero → no decay | Nonzero → rings forever |
| Underdamped | Left half, off the real axis | Negative → decays | Nonzero → oscillates |
| Critically damped | One repeated point on neg. real axis | Negative → decays | Zero → no oscillation |
| Overdamped | Two points on neg. real axis | Negative → decays | Zero → 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.
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.
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.
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.
“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
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.)
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.
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.
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″.
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.
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?
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.
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.
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 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
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.
Take m = 1, k = 4, b = 0. Compute the discriminant Δ = b² − 4mk.
Δ 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.
Same spring, m = 1, k = 4, now b = 2. Compute Δ = b² − 4mk.
Δ < 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.
Same spring, m = 1, k = 4, now b = 4. Compute Δ = b² − 4mk.
Δ = 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.
Same spring, m = 1, k = 4, now b = 6. Compute Δ = b² − 4mk.
Δ > 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.
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.
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.
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 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.
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.
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.
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)?
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.
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′.
(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.
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].
javascript function ydot(y1, y2, k, b) { return [y2, -k * y1 - b * y2]; }
Put the steps of the ODE→state-space conversion in the correct order.
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 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.
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.
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.
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 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.
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).
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.
Write discriminant(m, b, k) returning Δ = b² − 4mk. (A correct Δ is all the regime test ever needs.)
javascript function discriminant(m, b, k) { return b * b - 4 * m * k; }
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].
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.
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).
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
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.
Step 1: for no overshoot at maximum speed you need Δ = 0. Solve b² = 4mk for the critical b with m = 1, k = 9.
Equivalently bcrit = 2√(mk) = 2√9 = 6. Below 6 it overshoots; above 6 it crawls.
Step 2: with b = 6, the characteristic equation λ² + 6λ + 9 = 0 has a repeated root. Enter it.
Both eigenvalues sit at −3, real and negative: critically damped, decaying as e−3t with no oscillation.
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.
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.
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.
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.
| Where this goes next | Lesson |
|---|---|
| Tracking a state-space system through noise | Kalman Filter |
| Choosing eigenvalue locations to control behavior | MDPs & Control |
| The recursive Bayesian estimator this form feeds | Bayes 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