Retune the Kalman process noise, and fuse the two thermometers

Two changes to the same signal path, one large and one small.

The large one: all three filters were tuned to track one to three decades
faster than their signals move. In a still room the temperature filter
reported a median rate of 12.4 C/h while the air moved 0.4 C/h, and it
overshot a real -36 C/h event by 77%. Sweeping q against the RMSE of the
reported rate versus the true rate, using noise measured on the board
(temperature 0.088 C, pressure 0.022 hPa, humidity 0.40 %):

   temperature   6.45 -> 0.37 C/h RMSE     2e-6 -> 1e-9
   pressure      2.15 -> 0.24 hPa/h RMSE   1e-5 -> 1e-8
   humidity     27.94 -> 3.55 %/h RMSE     5e-5 -> 2e-8

Tracking does not suffer. Lag against a genuine 2 C/h ramp is 0.003 C at both
the old and new values, and the peak response to a five-minute event moves
closer to the truth rather than further from it, because the overshoot goes
away. What is given up is response to sub-minute transients, which for a
station forecasting fifteen minutes to a day ahead is noise to reject.

This matters most for pressure, whose tendency drives the precipitation
forecast, and which was the worst tuned of the three.

config.yaml shadowed kalman_q_temp, so editing the dataclass alone changed
nothing. All six values are now listed there with that hazard spelled out,
because a silent shadow cost real time here.

The small one: temp_raw was the plain average of two thermometers whose
white-noise sds differ by 7x (LPS25HB 0.007 C, HTS221 0.049 C), which throws
the quiet one away. Inverse-variance weighting cuts the raw noise 3.5x.

The trap is that the chips do not agree. They sit at different distances from
the SoC and stand about 1.3 C apart, so weighting by variance alone drags
temp_raw 0.48 C onto the LPS25HB, which after the 1.55x gain of the inverse
compensator is 0.75 C of silent bias on every reading, since k was fitted
against the mean of the two. The gradient is therefore tracked and removed
before weighting and only the deviations are fused: measured mean shift
0.0001 C, noise still 3.5x lower. The tracked gradient is retained because it
is a second observation of self-heating.

Also corrected: the earlier claim that the HTS221 was the quieter channel was
wrong, taken from twelve samples at a cadence slow enough that real drift
dominated. At 0.5 s over 120 samples the LPS25HB is quieter by 7x and takes
98% of the weight.
This commit is contained in:
2026-08-19 19:14:46 +01:00
parent e3176e29c9
commit 40f934901d
4 changed files with 205 additions and 6 deletions
+12 -1
View File
@@ -14,8 +14,19 @@ sensor:
persist_period_s: 30.0
rotation_deg: 90
cpu_heat_k: 0.55 # starting point only, calibrate from the dashboard
kalman_q_temp: 2.0e-6 # raise to track faster, lower to smooth harder
# Process noise. Raise to track faster, lower to smooth harder. These were
# retuned against noise measured on a real board by sweeping each q against
# the RMSE of the reported rate versus the true rate. The originals tracked
# two to three decades faster than any of these signals move: in a still room
# the temperature filter reported a median rate of 12.4 C/h while the air
# moved 0.4 C/h. All three are listed because this file shadows the defaults
# in ashvale/config.py, and a value present here silently wins.
kalman_q_temp: 1.0e-9
kalman_r_temp: 0.02
kalman_q_press: 1.0e-8
kalman_r_press: 0.05
kalman_q_hum: 2.0e-8
kalman_r_hum: 0.60
model:
grid_s: 300