sensor: model real temp sensor noise in TempSensorSim
Replaces the opaque variance/√12 formula with two explicit noise components: sigma (default 0.05 °C) — white Gaussian noise, calibrated against the 20260628T184903 Sud-0010 log (detrended hold-phase tick-to-tick std ≈ 0.053 °C). stirrer_sigma / stirrer_tau — optional AR(1) low-frequency component for stirrer-induced fluctuations (off by default); steady-state std equals stirrer_sigma, correlation time equals stirrer_tau ticks. plant_factory.py updated to use sigma=0.05 instead of the old variance=0.01 (which gave std ≈ 0.003 °C — far too quiet). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ class PlantFactory:
|
|||||||
if "sim" in plant_name:
|
if "sim" in plant_name:
|
||||||
heater = HeaterFactory.create("sim", heater_config)
|
heater = HeaterFactory.create("sim", heater_config)
|
||||||
pot = Pot(dt)
|
pot = Pot(dt)
|
||||||
sensor = TempSensorFactory.create("sim", temp_offset=-0.15, variance=0.01)
|
sensor = TempSensorFactory.create("sim", temp_offset=-0.15, sigma=0.05)
|
||||||
return heater, pot, sensor
|
return heater, pot, sensor
|
||||||
|
|
||||||
heater = HeaterFactory.create("Hendi", heater_config)
|
heater = HeaterFactory.create("Hendi", heater_config)
|
||||||
|
|||||||
@@ -3,17 +3,43 @@ import numpy as np
|
|||||||
|
|
||||||
|
|
||||||
class TempSensorSim(ATemperatureSensor):
|
class TempSensorSim(ATemperatureSensor):
|
||||||
|
"""Simulated temperature sensor with two independent noise components.
|
||||||
|
|
||||||
|
sigma -- white Gaussian noise std [°C]; calibrated against the
|
||||||
|
20260628T184903 Sud-0010 log: detrended hold-phase
|
||||||
|
tick-to-tick std ≈ 0.053 °C.
|
||||||
|
|
||||||
|
stirrer_sigma -- steady-state std [°C] of a slow AR(1) process that
|
||||||
|
models stirrer-induced low-frequency temperature
|
||||||
|
fluctuations at the sensor. Zero by default (off).
|
||||||
|
stirrer_tau -- correlation time [ticks] of the AR(1) process;
|
||||||
|
stirrer_sigma * sqrt(2/stirrer_tau) is the per-tick
|
||||||
|
innovation std so that the steady-state variance equals
|
||||||
|
stirrer_sigma².
|
||||||
|
"""
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return "FakeTemp"
|
return "FakeTemp"
|
||||||
|
|
||||||
def __init__(self, temp_offset=0.0, variance=0.0, **kwargs):
|
def __init__(self, sigma=0.05, stirrer_sigma=0.0, stirrer_tau=20.0,
|
||||||
|
temp_offset=0.0, **kwargs):
|
||||||
ATemperatureSensor.__init__(self)
|
ATemperatureSensor.__init__(self)
|
||||||
self.temp_set = 19.99
|
self.temp_set = 19.99
|
||||||
self.offset = temp_offset
|
self.offset = temp_offset
|
||||||
self.variance = variance
|
self.sigma = sigma
|
||||||
|
self.stirrer_sigma = stirrer_sigma
|
||||||
|
self.stirrer_tau = stirrer_tau
|
||||||
|
self._stirrer_state = 0.0
|
||||||
|
|
||||||
def set_fake_temp(self, temp):
|
def set_fake_temp(self, temp):
|
||||||
self.temp_set = temp
|
self.temp_set = temp
|
||||||
|
|
||||||
def temperature(self):
|
def temperature(self):
|
||||||
return self.temp_set + self.offset + self.variance * np.random.normal(0, 1) / np.sqrt(12.0)
|
white = self.sigma * np.random.normal() if self.sigma > 0 else 0.0
|
||||||
|
|
||||||
|
if self.stirrer_sigma > 0:
|
||||||
|
alpha = 1.0 / self.stirrer_tau
|
||||||
|
innovation = self.stirrer_sigma * np.sqrt(2.0 * alpha) * np.random.normal()
|
||||||
|
self._stirrer_state = (1.0 - alpha) * self._stirrer_state + innovation
|
||||||
|
|
||||||
|
return self.temp_set + self.offset + white + self._stirrer_state
|
||||||
|
|||||||
Reference in New Issue
Block a user