Luenberger observer: estimate velocity from position

Build a Luenberger observer for a cart with a position sensor. Calculate observer gains, follow estimation error, and see why a small position residual can hide a velocity error.

By 11 min read

A position sensor tells you where a cart is. A controller may also need its velocity. A Luenberger observer combines a motion model, the known input, and position measurements to estimate both states over time.

Estimate a state that the sensor does not report

The observer runs a model alongside the system. It predicts the output, compares that prediction with the measurement, and uses the difference to correct its state estimate. You choose correction gains that control how initial estimation errors evolve.

Åström and Murray's Output Feedback chapter gives this construction for linear systems. Here, an original cart example makes every state, gain, and error inspectable. The measurements are exact synthetic values, and the observer has the correct model.

Declare the cart and its measurement

Use a horizontal cart with mass m = 2 kg and viscous damping b = 1 N·s/m. The input u is the applied force in newtons. The true cart starts at p(0) = 0 m and v(0) = 0 m/s.

ṗ = v, m v̇ = u − bv
x = [p, v]ᵀ, y = p
A = [[0, 1], [0, −0.5]], B = [0, 0.5]ᵀ
C = [1, 0], D = 0

These are numeric matrices in the stated SI coordinates. Position and velocity have different units, as the state-space lesson explains. The lab omits springs, unknown loads, noise, delays, and actuator limits.

The observer receives u and y, plus an initial guess. It receives no direct velocity measurement. True velocity is available only to the teaching display so you can audit the estimate.

Correct the predicted output with an innovation

Write the estimated state as x̂ = [p̂, v̂]ᵀ. Its predicted output is ŷ = Cx̂ = p̂. The innovation, y − ŷ, measures the disagreement in position.

x̂̇ = Ax̂ + Bu + L(y − Cx̂)
L = [L₁, L₂]ᵀ
p̂̇ = v̂ + L₁(y − p̂)
v̂̇ = (u − bv̂)/m + L₂(y − p̂)

The position correction L₁(y − p̂) has units m/s, so L₁ has units 1/s. The velocity correction has units m/s², so L₂ has units 1/s². Both corrections use the same position residual, with different effects on the estimate.

The observer is a dynamical model. Its estimated position derivative includes the correction, so p̂̇ need not equal v̂ while an innovation remains. The MathWorks observer design reference gives the general form, including subtraction of Du when the measurement has direct feedthrough.

Derive the Luenberger observer error dynamics

Define the estimation error with one sign convention: e = x − x̂, or true state minus estimate. With exact measurements, y − Cx̂ = Ce. Subtract the observer equation from the true system equation:

ė = Ax + Bu − Ax̂ − Bu − LCe
ė = (A − LC)e
A − LC = [[−L₁, 1], [−L₂, −0.5]]

The common known input cancels. Changing u changes the true motion and the estimate, while the same initial error follows the same error trajectory. That cancellation requires the observer to know the actual applied input and use the matching model.

If every eigenvalue of A − LC has a negative real part, e approaches zero in this continuous-time model. This describes estimation error. The cart itself can keep moving while the estimate catches up.

Choose gains from two desired poles

The observer's characteristic polynomial follows from the determinant:

det(sI − (A − LC)) = s² + (L₁ + 0.5)s + 0.5L₁ + L₂
Desired poles: −ρ and −2ρ
(s + ρ)(s + 2ρ) = s² + 3ρs + 2ρ²
L₁ = 3ρ − 0.5, L₂ = 2ρ² − 0.5L₁

The numeric 0.5 represents b/m = 0.5 s⁻¹. The rate ρ uses 1/s; each coefficient retains the units from the observer equations. For ρ = 1 s⁻¹, the gains are L₁ = 2.5 s⁻¹ and L₂ = 0.75 s⁻².

Controller pole placement assigns eigenvalues of A − BK. Observer pole placement assigns eigenvalues of A − LC. The two matrices describe different dynamics, and the gains have different dimensions and roles.

Check the estimate after one second

Start with p̂(0) = −1 m and v̂(0) = 0 m/s. The true state starts at zero, giving eₚ(0) = 1 m and eᵥ(0) = 0 m/s. Use the default gains and hold u = 1 N.

Let τ = t/(1 s). The exact error is:

eₚ(t) = [−0.5 exp(−τ) + 1.5 exp(−2τ)] m
eᵥ(t) = 0.75[exp(−2τ) − exp(−τ)] m/s

At t = 1 s, the true position is 0.213061 m and true velocity is 0.393469 m/s. Subtracting eₚ = 0.019063 m and eᵥ = −0.174408 m/s gives p̂ = 0.193998 m and v̂ = 0.567877 m/s.

The observer estimates a velocity above the true value at that instant. Its initial velocity estimate was correct, yet correcting the position offset created a temporary velocity error. Negative poles guarantee eventual decay; they do not require every error component to decrease at every instant.

Separate true motion from estimation error

Position measurement, two-state estimate

Correct a model with a Luenberger observer

The true cart starts at rest at the origin. The observer knows the same applied force and receives exact position. Choose its initial guess and error poles, then inspect how the estimate changes.

1.000000 1/s
-1.000000 m
0.000000 m/s
1.000000 N
1.000000 s

A − LC = [[-2.500000, 1], [-0.750000, -0.5]]. State order: [position, velocity].

True position (m)
0.213061
Estimated position (m)
0.193998
True velocity (m/s)
0.393469
Estimated velocity (m/s)
0.567877
Position error: true - estimate (m)
0.019063
Velocity error: true - estimate (m/s)
-0.174408
Innovation (m)
0.019063
Position gain L₁ (1/s)
2.500000
Velocity gain L₂ (1/s²)
0.750000
Position correction (m/s)
0.047658
Velocity correction (m/s²)
0.014297
Slow pole (1/s)
-1.000000
Fast pole (1/s)
-2.000000

Measured position and its estimate

True and estimated cart position over six secondsHorizontal time is in seconds; vertical position is in m. Blue solid is the true state. Amber dashed is the observer estimate. The two markers show 1.000000 seconds. The observer receives exact position and the known force, with no direct velocity measurement.-1.51.64.60.03.06.0p (m)Time (s)
Blue solid and filled dot: true position. Amber dashed and open ring: estimate. Curves can overlap. Lines join exact values every 0.025 s; vertical bounds follow both curves. Inspection changes the markers, leaving the full curves fixed.

True velocity and its estimate

True and estimated cart velocity over six secondsHorizontal time is in seconds; vertical velocity is in m/s. Blue solid is the true state. Amber dashed is the observer estimate. The two markers show 1.000000 seconds. The observer receives exact position and the known force, with no direct velocity measurement.-0.20.51.20.03.06.0v (m/s)Time (s)
Blue solid and filled dot: true velocity. Amber dashed and open ring: estimate. Curves can overlap. Lines join exact values every 0.025 s; vertical bounds follow both curves. Inspection changes the markers, leaving the full curves fixed.

Both error poles are negative. The estimation error approaches zero in this exact, noiseless model; individual error components can change sign or initially grow.

Mass is 2 kg and damping is 1 N·s/m. The known force moves both models, so it cancels from the error equation. The observer does not control the cart. This example includes no noise, unknown load, bias, delay, saturation, or sampling effects. Presets reset inspection to 1 s.

Start with Correct a position offset, then change the known force from 1 N to −1 N. Position and velocity change direction, but the error readouts stay unchanged at the same inspection time. The observer and cart still share the applied force.

Next, choose Faster error poles. At one second, its velocity error is −0.216830 m/s, compared with −0.174408 m/s in the default case. A faster asymptotic rate does not promise a smaller error at every selected time.

Choose Start with the correct state to make both curves coincide. The lab evaluates the exact exponential solution, with 0.025 s samples used only to draw the lines. Inspecting time moves the markers without changing the complete six-second trajectories.

Check observability and detectability

For position sensing, the observability matrix is [C; CA] = [[1, 0], [0, 1]]. Its rank is two, so output history can reveal both cart states. This is the structural condition that lets us assign both observer poles.

Full observability is stronger than the condition needed for some stable observer. Detectability allows hidden modes provided all of them already decay asymptotically. In continuous time, those unobservable modes must have negative real parts; the observer cannot move them through a measurement that contains no information about them.

Oppenheim and Verghese's MIT notes, Chapter 6 explain this distinction and the observer-gain tradeoff. Their main observer derivation uses discrete time; stable poles then lie inside the unit circle. Our lab uses the continuous-time condition above.

If this cart measured only velocity, a constant position offset would remain hidden. Its unobservable position mode has eigenvalue zero, so it is not asymptotically stable. A velocity-only observer cannot make every initial position error vanish; the observability lesson shows the indistinguishable states.

Account for noise and model mismatch

The lab's innovation is a measured-position residual. It does not expose the whole error vector. At t = ln(3) s in the default run, the position error is exactly zero while the velocity error is −1/6 m/s.

With additive measurement noise n, the sensor reports y = Cx + n. Keeping e = x − x̂ gives:

ė = (A − LC)e − Ln

L changes both the error dynamics and how measurement noise enters. Larger gains can increase noise sensitivity and transient correction; pole speed alone gives no accuracy ranking. The lab includes no noise process and makes no claim about a particular sensor's performance.

Other departures need explicit models:

  • Unknown force: if the observer uses u_known while the cart receives u_actual, the error gains an input B(u_actual − u_known).
  • Position bias: a constant sensor offset can look like a real position shift. A stable observer does not automatically discover that bias.
  • Sampling and delay: a digital observer needs discrete equations and a stated measurement schedule. Continuous poles alone do not validate its implementation.
  • Parameter errors: an incorrect mass or damping prevents the exact cancellation used above.

A Kalman filter chooses correction using specified uncertainty models. Connecting this observer to a controller also changes the problem: the controller would use x̂ to choose u. This lab holds force independently and leaves that feedback connection for a separate design.

Reproduce the estimates in Python

This standard-library example uses two exponential error modes and the exact force-driven cart response. Time uses seconds, force newtons, position meters, and velocity meters per second. It generates the displayed audit values without a numerical integration step.

from math import exp, log


def observe(rate, ph0, vh0, force, t):
    l1 = 3*rate - 0.5
    l2 = 2*rate**2 - 0.5*l1
    ep0, ev0 = -ph0, -vh0
    a = ((2*rate-l1)*ep0 + ev0)/rate
    b = ep0-a
    ep = a*exp(-rate*t) + b*exp(-2*rate*t)
    ev = (l1-rate)*a*exp(-rate*t) + (l1-2*rate)*b*exp(-2*rate*t)
    v = force*(1-exp(-t/2))
    p = force*(t-2*(1-exp(-t/2)))
    return p, v, p-ep, v-ev, ep, ev, l1, l2


for name, rate, ph0, vh0 in [("Default", 1, -1, 0),
                            ("Faster", 3, -1, 0),
                            ("Velocity offset", 1, 0, 1)]:
    p, v, ph, vh, ep, ev, l1, l2 = observe(rate, ph0, vh0, 1, 1)
    print(f"{name}: L=({l1:.6f}, {l2:.6f})")
    print(f"  estimate=({ph:.6f} m, {vh:.6f} m/s), "
          f"error=({ep:.6f} m, {ev:.6f} m/s)")
row = observe(1, -1, 0, 1, log(3))
print(f"At ln(3) s: position error={abs(row[4]):.6f} m, "
      f"velocity error={row[5]:.6f} m/s")
row = observe(1, -1, 0, -1, 1)
print(f"Reverse force: error=({row[4]:.6f} m, {row[5]:.6f} m/s)")

Expected output:

Default: L=(2.500000, 0.750000)
  estimate=(0.193998 m, 0.567877 m/s), error=(0.019063 m, -0.174408 m/s)
Faster: L=(8.500000, 13.750000)
  estimate=(0.250006 m, 0.610299 m/s), error=(-0.036945 m, -0.216830 m/s)
Velocity offset: L=(2.500000, 0.750000)
  estimate=(0.445605 m, 0.877621 m/s), error=(-0.232544 m, -0.484152 m/s)
At ln(3) s: position error=0.000000 m, velocity error=-0.166667 m/s
Reverse force: error=(0.019063 m, -0.174408 m/s)

Try two observer exercises

Exercise 1. Choose observer poles −2 and −4 s⁻¹ for this cart. Calculate L₁ and L₂, including units. If the current position innovation is 0.1 m, what does each correction term contribute?

Check the gains and correction units

With ρ = 2 s⁻¹, L₁ = 6 − 0.5 = 5.5 s⁻¹. Then L₂ = 8 − 0.5 × 5.5 = 5.25 s⁻². The trace of A − LC is −6 s⁻¹ and its determinant is 8 s⁻², matching the sum and product of the requested poles.

The corrections are L₁ × 0.1 m = 0.55 m/s in p̂̇ and L₂ × 0.1 m = 0.525 m/s² in v̂̇. They correct the estimator's derivatives; they are not forces applied to the cart.

Exercise 2. Use the default initial guess and gains. At t = ln(3) s, exp(−τ) = 1/3 and exp(−2τ) = 1/9. Calculate both error components. Would the observer have a correct full-state estimate at that instant?

Check the zero-residual instant

Position error is −0.5/3 + 1.5/9 = 0 m. Velocity error is 0.75(1/9 − 1/3) = −1/6 m/s. Estimated velocity exceeds true velocity by 1/6 m/s.

The position innovation and both instantaneous correction terms are zero. The velocity error persists, so the full-state estimate is still wrong. The error equation gives ėₚ = eᵥ at this instant; the position residual immediately starts changing again.

Sources and further study

Next, compare this observer gain with the controller gain in pole placement. Then use linear quadratic regulation to make the cost of state error and control effort explicit before deciding how the controller will obtain its state.