Rauch-Tung-Striebel Smoother (gnc-autonomy/estimation-filtering/rts-smoother)
Use when the task is fixed-interval smoothing of a stored forward
Kalman-filter history: a constant-velocity forward pass produces the
predicted and filtered means and covariances at every measurement
epoch, and the Rauch-Tung-Striebel (RTS) backward recursion then
refines each filtered estimate with the measurements that came after
it, giving the optimal smoothed state that uses all measurements, past
and future. This leaf implements that pair in pure Python, stdlib
only, deterministic and offline. It is the offline batch complement to
gnc-autonomy/navigation/kalman-filter-design (the forward filter whose
stored outputs feed the smoother) and pairs with
gnc-autonomy/estimation-filtering/extended-kalman-filter and
gnc-autonomy/estimation-filtering/unscented-kalman-filter on the
nonlinear side. It never touches the live navigation state: smoothing
is post-processing only, distinct from the cheap online tracking of
alpha-beta-filter and the attitude fusion of complementary-filter, and
it does not smooth raw flight-test traces with moving averages
(flight-test-operations/planning/flight-test-data-reduction owns that).
Domain quick reference
- State x = [position, velocity] in SI units (m, m/s). Constant-
velocity transition F = [[1, dt], [0, 1]] over the step dt (s),
measurement model H = [[1, 0]], measurement noise variance r (m2).
- Discrete process noise: Q = q * [[dt^4/4, dt^3/2], [dt^3/2, dt^2]]
where q is the continuous acceleration noise intensity (m2/s3).
- Forward predict: x_pred = F x_filt, P_pred = F P_filt F^T + Q.
- Forward update: innovation y = z - H x_pred, innovation variance
S = H P_pred H^T + r, gain K = P_pred H^T / S (2x1), x_filt =
x_pred + K y, and the Joseph-form covariance P_filt = (I - K H)
P_pred (I - K H)^T + K r K^T.
- RTS backward recursion, k = n-2 down to 0, initialized at the last
step with the filtered mean and covariance: smoother gain K_k =
P_filt[k] F^T (P_pred[k+1])^-1 (2x2 inverse), then x_s[k] =
x_filt[k] + K_k (x_s[k+1] - x_pred[k+1]) and P_s[k] = P_filt[k] +
K_k (P_s[k+1] - P_pred[k+1]) K_k^T.
- Boundary identity: at the final step the smoothed state and
covariance equal the filtered values; every smoothed position
variance sits at or below the filtered one, the fixed-interval
smoothing benefit.
- Units are SI throughout; the algorithms above are standard linear
estimation methodology, summary-only.
Workflow
- Gather the stored measurement list (position samples), the model
parameters dt, q, r, and the prior state x0 with covariance P0.
dt must be positive, q non-negative, r positive, x0 length 2, P0
2x2, and at least two measurements present.
- Run forward_kalman(measurements, dt, q, r, x0, p0). It returns one
record per step with x_pred, P_pred, x_filt, P_filt, the
innovation and its variance; each record also carries the step dt
so the smoother can rebuild the transition matrix.
- Inspect the forward history, for example the last filtered state
and covariance, to confirm the filter behaved before smoothing.
- Run rts_smooth(fwd_results). It returns (smoothed_states,
smoothed_covs, gains): three lists of length n; gains[k] is the 2x2
smoother gain leaving step k and the final entry is a zero
placeholder.
- Quantify the benefit with smoother_reduction(fwd_results,
smoothed_covs), which returns max_reduction (largest relative drop
in position variance), all_reduced and boundary_matches.
- Report the smoothed position and velocity history with the reduced
covariance for the offline trajectory reconstruction or navigation
data post-processing task; keep the live filter untouched.
- Confirm the deterministic checks with the contract test
scripts/test_rts_smoother.py.
Worked example
dt = 1 s, q = 0.1 m2/s3, r = 25 m2, x0 = [0.0, 5.0] m, P0 =
diag(100, 10), with the 10 position samples [2.1, 6.8, 11.9, 16.4,
21.2, 26.5, 30.9, 36.2, 41.4, 45.8] m.
- Forward filtered at the last step k=9: position 45.8108 m, velocity
4.8570 m/s, position variance 8.8487, velocity variance 0.5941.
- Innovation variance runs from 135.025 (first step) down to 38.697
at the final step as the filter converges.
- Smoothed at k=0: position 2.2080 m, velocity 4.8275 m/s. The
forward filtered position at k=0 was 2.6369 m, so the backward pass
has pulled the start of the track onto the data line.
- Smoothed at k=4: position 21.5444 m, velocity 4.8439 m/s, with
smoothed variances 2.7726 and 0.3326, both clearly below the
filtered values 12.7131 and 1.8860 at k=4.
- Smoother gain at k=0: about [0.9983, 0.0034]^T; the smoothed state
at k=9 equals the filtered state at k=9 exactly.
- Covariance verdict: all_reduced True, boundary_matches True,
max_reduction about 0.78.
Verification
- Confirm forward_kalman on the worked example returns the k=9
anchors (45.8108, 4.8570, 8.8487, 0.5941) within 0.01 and that
rts_smooth returns the k=0 and k=4 anchors (2.2080, 4.8275,
21.5444, 4.8439, 2.7726, 0.3326) within 0.01.
- Confirm the boundary identity: the smoothed state and covariance at
the last step equal the filtered values within 1e-12.
- Confirm the covariance reduction verdict: every smoothed position
variance at or below the filtered value, boundary_matches True.
- Structural identity: smoothing a perfectly noiseless
constant-velocity ramp (measurements 5*k for k = 0..9, exact model
q = 0, initial state on the ramp at the epoch before the first
sample) returns smoothed velocities within 1e-9 of 5.0 m/s at every
step. With process noise q = 0.1 an off-ramp initial state is a
legitimate prior inconsistency, so the exact-model ramp is the
identity check.
- Confirm every non-physical input raises ValueError: dt 0 or
negative, q negative, r 0, fewer than two measurements, x0 not
length 2, P0 not 2x2, and a singular 2x2 inversion.
- Run the contract test offline: python3
scripts/test_rts_smoother.py (34 tests, deterministic).
Pitfalls
- Running the smoother online: RTS is a fixed-interval offline batch
post-processor over a stored forward Kalman history (smoothed state at the
final step equals the filtered state); it never touches the live
navigation state.
- Smoothing with fewer than two measurements: the backward recursion needs
at least two epochs and fewer raise ValueError; the smoother gain at the
final step is a zero placeholder, not a real gain.
- Quoting the smoothing benefit from a single step: every smoothed position
variance sits at or below the filtered one and smoother_reduction reports
all_reduced, boundary_matches and max_reduction (about 0.78 on the worked
example); the boundary identity at the last step is the structural check.
- Expecting the exact-model identity with process noise: the noiseless
constant-velocity ramp (q = 0, initial state on the ramp) returns
velocities within 1e-9 of 5.0 m/s; with q = 0.1 an off-ramp initial state
is a legitimate prior inconsistency, not a bug.
- Model parameters are validated: dt must be positive, q non-negative, r
positive, x0 length 2, P0 2x2, and a singular 2x2 inversion raises
ValueError.
Related leaves
- gnc-autonomy/navigation/kalman-filter-design: the forward discrete
Kalman filter whose stored outputs this smoother consumes.
- gnc-autonomy/estimation-filtering/extended-kalman-filter and
gnc-autonomy/estimation-filtering/unscented-kalman-filter:
nonlinear forward filtering that the linear RTS recursion does not
cover.
- gnc-autonomy/estimation-filtering/alpha-beta-filter and
gnc-autonomy/estimation-filtering/complementary-filter: cheap
online tracking and attitude fusion, the real-time alternatives to
batch smoothing.
- flight-test-operations/planning/flight-test-data-reduction:
moving-average smoothing of raw flight-test traces, a different
smoothing task with no state model.
Behavior contract (gate 3)
Run the deterministic contract test (stdlib unittest, offline):
python3 scripts/test_rts_smoother.py
The test covers the 2x2 matrix helpers (multiplication, add, sub,
scale, transpose, inverse with singular rejection), the forward
filter record structure, the worked-example anchors at k=9, k=4 and
the final-step innovation variance 38.697, the smoothed anchors at
k=0 and k=4, the smoothed-below-filtered covariance comparison at
k=4, the boundary identity, the smoother gain anchor at k=0, the
covariance reduction verdict, the noiseless-ramp identity at q = 0,
and ValueError rejection of every non-physical input. It runs in well
under a second.
Compliance
- ARP4754A frames the development-assurance context for navigation
estimation software; it is referenced, not reproduced, and the
estimation relations above are standard engineering methodology,
summary-only per standards-map.yaml.
- compliance: STANDARDS-REF, gated: false.
1---2name: rts-smoother3description: Use when you must run a fixed-interval Rauch-Tung-Striebel (RTS) smoother over a stored forward Kalman-filter output for a discrete constant-velocity model in SI units: propagate the position and velocity state with the constant-velocity transition matrix, run the linear forward pass that stores the predicted and filtered means and covariances at every step, then execute the backward recursion with the smoother gain to combine each filtered estimate with the future measurements. Produces the smoothed state history, the smoother gains, the smoothed covariance history and the covariance reduction verdict that gate offline trajectory reconstruction and post-processing of navigation data. Trigger: rts smoother, rauch-tung-striebel, fixed-interval smoothing, backward pass, smoothed state, offline trajectory reconstruction, post-processing navigation data, constant-velocity model.4license: Apache-2.05---67# Rauch-Tung-Striebel Smoother (gnc-autonomy/estimation-filtering/rts-smoother)89Use when the task is fixed-interval smoothing of a stored forward10Kalman-filter history: a constant-velocity forward pass produces the11predicted and filtered means and covariances at every measurement12epoch, and the Rauch-Tung-Striebel (RTS) backward recursion then13refines each filtered estimate with the measurements that came after14it, giving the optimal smoothed state that uses all measurements, past15and future. This leaf implements that pair in pure Python, stdlib16only, deterministic and offline. It is the offline batch complement to17gnc-autonomy/navigation/kalman-filter-design (the forward filter whose18stored outputs feed the smoother) and pairs with19gnc-autonomy/estimation-filtering/extended-kalman-filter and20gnc-autonomy/estimation-filtering/unscented-kalman-filter on the21nonlinear side. It never touches the live navigation state: smoothing22is post-processing only, distinct from the cheap online tracking of23alpha-beta-filter and the attitude fusion of complementary-filter, and24it does not smooth raw flight-test traces with moving averages25(flight-test-operations/planning/flight-test-data-reduction owns that).2627## Domain quick reference2829- State x = [position, velocity] in SI units (m, m/s). Constant-30 velocity transition F = [[1, dt], [0, 1]] over the step dt (s),31 measurement model H = [[1, 0]], measurement noise variance r (m2).32- Discrete process noise: Q = q * [[dt^4/4, dt^3/2], [dt^3/2, dt^2]]33 where q is the continuous acceleration noise intensity (m2/s3).34- Forward predict: x_pred = F x_filt, P_pred = F P_filt F^T + Q.35- Forward update: innovation y = z - H x_pred, innovation variance36 S = H P_pred H^T + r, gain K = P_pred H^T / S (2x1), x_filt =37 x_pred + K y, and the Joseph-form covariance P_filt = (I - K H)38 P_pred (I - K H)^T + K r K^T.39- RTS backward recursion, k = n-2 down to 0, initialized at the last40 step with the filtered mean and covariance: smoother gain K_k =41 P_filt[k] F^T (P_pred[k+1])^-1 (2x2 inverse), then x_s[k] =42 x_filt[k] + K_k (x_s[k+1] - x_pred[k+1]) and P_s[k] = P_filt[k] +43 K_k (P_s[k+1] - P_pred[k+1]) K_k^T.44- Boundary identity: at the final step the smoothed state and45 covariance equal the filtered values; every smoothed position46 variance sits at or below the filtered one, the fixed-interval47 smoothing benefit.48- Units are SI throughout; the algorithms above are standard linear49 estimation methodology, summary-only.5051## Workflow52531. Gather the stored measurement list (position samples), the model54 parameters dt, q, r, and the prior state x0 with covariance P0.55 dt must be positive, q non-negative, r positive, x0 length 2, P056 2x2, and at least two measurements present.572. Run forward_kalman(measurements, dt, q, r, x0, p0). It returns one58 record per step with x_pred, P_pred, x_filt, P_filt, the59 innovation and its variance; each record also carries the step dt60 so the smoother can rebuild the transition matrix.613. Inspect the forward history, for example the last filtered state62 and covariance, to confirm the filter behaved before smoothing.634. Run rts_smooth(fwd_results). It returns (smoothed_states,64 smoothed_covs, gains): three lists of length n; gains[k] is the 2x265 smoother gain leaving step k and the final entry is a zero66 placeholder.675. Quantify the benefit with smoother_reduction(fwd_results,68 smoothed_covs), which returns max_reduction (largest relative drop69 in position variance), all_reduced and boundary_matches.706. Report the smoothed position and velocity history with the reduced71 covariance for the offline trajectory reconstruction or navigation72 data post-processing task; keep the live filter untouched.737. Confirm the deterministic checks with the contract test74 scripts/test_rts_smoother.py.7576## Worked example7778dt = 1 s, q = 0.1 m2/s3, r = 25 m2, x0 = [0.0, 5.0] m, P0 =79diag(100, 10), with the 10 position samples [2.1, 6.8, 11.9, 16.4,8021.2, 26.5, 30.9, 36.2, 41.4, 45.8] m.8182- Forward filtered at the last step k=9: position 45.8108 m, velocity83 4.8570 m/s, position variance 8.8487, velocity variance 0.5941.84- Innovation variance runs from 135.025 (first step) down to 38.69785 at the final step as the filter converges.86- Smoothed at k=0: position 2.2080 m, velocity 4.8275 m/s. The87 forward filtered position at k=0 was 2.6369 m, so the backward pass88 has pulled the start of the track onto the data line.89- Smoothed at k=4: position 21.5444 m, velocity 4.8439 m/s, with90 smoothed variances 2.7726 and 0.3326, both clearly below the91 filtered values 12.7131 and 1.8860 at k=4.92- Smoother gain at k=0: about [0.9983, 0.0034]^T; the smoothed state93 at k=9 equals the filtered state at k=9 exactly.94- Covariance verdict: all_reduced True, boundary_matches True,95 max_reduction about 0.78.9697## Verification9899- Confirm forward_kalman on the worked example returns the k=9100 anchors (45.8108, 4.8570, 8.8487, 0.5941) within 0.01 and that101 rts_smooth returns the k=0 and k=4 anchors (2.2080, 4.8275,102 21.5444, 4.8439, 2.7726, 0.3326) within 0.01.103- Confirm the boundary identity: the smoothed state and covariance at104 the last step equal the filtered values within 1e-12.105- Confirm the covariance reduction verdict: every smoothed position106 variance at or below the filtered value, boundary_matches True.107- Structural identity: smoothing a perfectly noiseless108 constant-velocity ramp (measurements 5*k for k = 0..9, exact model109 q = 0, initial state on the ramp at the epoch before the first110 sample) returns smoothed velocities within 1e-9 of 5.0 m/s at every111 step. With process noise q = 0.1 an off-ramp initial state is a112 legitimate prior inconsistency, so the exact-model ramp is the113 identity check.114- Confirm every non-physical input raises ValueError: dt 0 or115 negative, q negative, r 0, fewer than two measurements, x0 not116 length 2, P0 not 2x2, and a singular 2x2 inversion.117- Run the contract test offline: python3118 scripts/test_rts_smoother.py (34 tests, deterministic).119120## Pitfalls121122- Running the smoother online: RTS is a fixed-interval offline batch123 post-processor over a stored forward Kalman history (smoothed state at the124 final step equals the filtered state); it never touches the live125 navigation state.126- Smoothing with fewer than two measurements: the backward recursion needs127 at least two epochs and fewer raise ValueError; the smoother gain at the128 final step is a zero placeholder, not a real gain.129- Quoting the smoothing benefit from a single step: every smoothed position130 variance sits at or below the filtered one and smoother_reduction reports131 all_reduced, boundary_matches and max_reduction (about 0.78 on the worked132 example); the boundary identity at the last step is the structural check.133- Expecting the exact-model identity with process noise: the noiseless134 constant-velocity ramp (q = 0, initial state on the ramp) returns135 velocities within 1e-9 of 5.0 m/s; with q = 0.1 an off-ramp initial state136 is a legitimate prior inconsistency, not a bug.137- Model parameters are validated: dt must be positive, q non-negative, r138 positive, x0 length 2, P0 2x2, and a singular 2x2 inversion raises139 ValueError.140141## Related leaves142143- gnc-autonomy/navigation/kalman-filter-design: the forward discrete144 Kalman filter whose stored outputs this smoother consumes.145- gnc-autonomy/estimation-filtering/extended-kalman-filter and146 gnc-autonomy/estimation-filtering/unscented-kalman-filter:147 nonlinear forward filtering that the linear RTS recursion does not148 cover.149- gnc-autonomy/estimation-filtering/alpha-beta-filter and150 gnc-autonomy/estimation-filtering/complementary-filter: cheap151 online tracking and attitude fusion, the real-time alternatives to152 batch smoothing.153- flight-test-operations/planning/flight-test-data-reduction:154 moving-average smoothing of raw flight-test traces, a different155 smoothing task with no state model.156157## Behavior contract (gate 3)158159Run the deterministic contract test (stdlib unittest, offline):160161 python3 scripts/test_rts_smoother.py162163The test covers the 2x2 matrix helpers (multiplication, add, sub,164scale, transpose, inverse with singular rejection), the forward165filter record structure, the worked-example anchors at k=9, k=4 and166the final-step innovation variance 38.697, the smoothed anchors at167k=0 and k=4, the smoothed-below-filtered covariance comparison at168k=4, the boundary identity, the smoother gain anchor at k=0, the169covariance reduction verdict, the noiseless-ramp identity at q = 0,170and ValueError rejection of every non-physical input. It runs in well171under a second.172173## Compliance174175- ARP4754A frames the development-assurance context for navigation176 estimation software; it is referenced, not reproduced, and the177 estimation relations above are standard engineering methodology,178 summary-only per standards-map.yaml.179- compliance: STANDARDS-REF, gated: false.