# Anomaly detection with time-series

\[We believe the setup is useful for anyone testing anomaly detection and root-cause analysis, and we've written this research guide so you can do the same]

## The problem

\[A live system (truck / engine / bioreactor, etc). we have a stream (time-series) of data from different variables: actuators, control inputs and sensor outputs. There is some causal relationship between these variables: actuators and control inputs drive the sensor measurements, which are also affected by external, unmeasured influences). The goal of an anomaly detection algorithm is to monitor the stream of data, and detect when an anomaly has happened; for extra credit, what was the cause of this anomaly (root cause analysis).]

\[Testing these algorithms outside of a computer simulation is difficult in practice for two reasons: (1) we don't know when anomalies happened, and (2) we don't know the underlying causal structure. This means we can't really check the answers that our algorithm gave us. As with many other fields, the gap between simulation and real, deployment scenarios is huge, making it difficult to properly test and refine our algorithms.]

As always, this is the gap that the chambers fill. They provide a real but controlled environment where (1) we can introduce anomalies at will, (2) we know the causal structure, and (3) there are still external, unmeasured influences that make the problem complicated enough.

Of course, an algorithm that works on the chambers does not necessarily work outside. But as one of the researchers at SCANIA told us "if it doesn't work on the Chamber it won't work on the real thing".

## A testbed for anomaly detection

{% columns %}
{% column %}

<figure><img src="/files/wpFogXGe3sph6gMgU3Bo" alt=""><figcaption></figcaption></figure>
{% endcolumn %}

{% column %}
Because it produces time-series data from a dynamical system, the Wind Tunnel Mk2 was the obvious choice for this problem.

The chamber exposes data from 44 variables, from sensor measurements to control inputs. We can pick subsets of these to make up our data stream and use the rest to introduce anomalies.
{% endcolumn %}
{% endcolumns %}

#### Building a task

\[To build a task, we pick variables: sensor measurements and control inputs that will drive them; then we pick other inputs and sensor parameters to act as 3rd variables, which modulate the relationships between our task variables, allowing us to introduce anomalies with a high degree of control.

<details>

<summary>scratch</summary>

To collect data: we set our control inputs of the chamber to follow a stochastic process of your choice (e.g., a random walk, real trajectories from some human user, etc.) and take measurements of all variables through time. This is our **baseline**, i.e., when the machine is operating normally.

\[Chamber variables dropdown]

It exposes \[X variables], including sensor measurements and control inputs / parameters. We will split the latter in two groups: some will be part of our data stream, and some will be used to introduce anomalies (see the relationship map).

\[Then, we can introduce anomalies]

\[graph]

</details>

### An example

Let's start with a simple example \[produce data for an anomaly detection algorithm: goal -> decide at which point in time the anomaly has occurred] using only a few variables:

* Control inputs: fan loads `load_in` and `load_out`
* Sensor measurements: fan speeds (`rpm_in/out`), fan currents (`current_in/out_raw`*),* air pressure (`pressure_upwind/downwind/intake/ambient`) and global chamber current (`supply_current`).

As anomalies

* Leak in the system: hatch; at random or related to the rest of the system -> at high inner pressure (downwind), probability is higher.
* Sensor drift: reference voltages -> fluctuations at random with low probability.

Below is the experimental data with the code to collect it from the Remote Lab.

{% tabs %}
{% tab title="Figure" %}

{% endtab %}

{% tab title="Code" %}

{% endtab %}
{% endtabs %}

### Anomaly index

\[To help you design different tasks, we find this map useful].

\[Reformulation of the map of physical effects (causal ground-truth) of the Wind Tunnel Mk2].

\[We reformulate the graph as] relationships <sup>(gray lines / arrows)</sup> in the [Wind Tunnel Mk2](/the-chambers/wind-tunnel-mk2.md) and the 3<sup>rd</sup> variables that modulate them <sup>(green arrows)</sup>. See the relationship index below for complete details and accompanying experiments and plots.

\[TODO: update graph: remove pressure\_ambient, add edges from hatch/rpm\_\* to load\_\*->rpm\_\* relationships]

<figure><picture><source srcset="/files/hlaqr7KLoblHcCvDXm9k" media="(prefers-color-scheme: dark)"><img src="/files/l8y30OJ4IdWEHQQml8MF" alt=""></picture><figcaption></figcaption></figure>

For each of the relationships in the graph (gray lines / arrows), you can find detailed description of each relationship and modulation, together with a visualization and the code to reproduce it.

{% hint style="info" %}
See the Map of physical effects for additional descriptions and visualizations of each edge.
{% endhint %}

**Note:** Each relationship (gray and green lines/arrows) can be seen independently in the index of physical effects for this chamber.

<details>

<summary><code>load_in/out</code> <span class="math">\longrightarrow</span>  <code>rpm_in/out</code></summary>

The fan loads `load_in` and `load_out` control the speed of the fans in an open-loop fashion. In steady state, when keeping all other variables (e.g., hatch position other fan constant) the relationship between load and speed is linear (Fig. 1).

<figure><picture><source srcset="/files/AFqGnKuD8IrOiofEFnOz" media="(prefers-color-scheme: dark)"><img src="/files/19uRazVxWJPiYotYhXBj" alt=""></picture><figcaption></figcaption></figure>

When the load is set to zero, the fan is completely powered off and no longer produces a tachometer signal, i.e., the corresponding speed measurement (rpm\_in/out) is the last measured speed (Fig. 2).

\[Figure 2: A: powered off fan, see experiment in old paper]

**Modulation by `res_rpm_in/out`**

The relationship can be modulated through the variables `res_rpm_in/out`, which control the resolution of the speed sensor of each fan. At lower resolution, the quantization error is higher, and is larger for higher fan speeds (Fig. 3).

\[Figure 3: impulse response + variations of resolution]

The results for `rpm_out` and `res_rpm_out` are virtually the same and not shown.

**Modulation by the other fan & hatch**

In the Wind Tunnel, the fans work in tandem, i.e., one pushes air in and another out of the chamber. Thus, if considering a single fan, the relationship between its load and speed is modulated by the speed of the other fan (Fig. 4).

\[Figure 4: A: time series and B: steady state, for intake fan load and exhaust fan levels]

The strength of this modulation is itself affected by the hatch position: the coupling between fan speeds is reduced when the hatch is open, creating an additional flow of air to/from the outside (Fig 5.)

\[Figure 5: impulses experiment, showing how coupling changes between fan speeds given the hatch position]

**Note:** the relationship between `hatch` and the actual hatch position (as measured by `hatch_angle` ) is itself modulated by the motor parameters (`mot_enabled/max/steps`), which control the torque of the sensor and its resolution (see the [entry](#hatch-hatch_angle) for `hatch` $$\longrightarrow$$ `hatch_angle` below)

</details>

<details>

<summary><code>load_in/out</code> <span class="math">\to</span> <code>current_in/out_raw</code></summary>

Because the load controls the fan speed, it also affects the electric current drawn by the fan. As for the speed, the change in current does not occur instantaneously (Fig 1a). In steady-state, and keeping all other variables constant, the relationship is cubic (Fig. 1, see also Appendix IV.1.1 of the original paper).

\[Figure 6: A: impulse and B: steady state of `load_in` and `current_rpm_in_raw`]

The results for `load_out` and `current_rpm_out_raw` are virtually the same and not shown.

**Modulation via sensor parameters**

The chamber provides calibrated (current\_in/out) and uncalibrated (current\_in\_raw, current\_out\_raw) measurements of the fan speeds. These measurements are affected by the parameters of the underlying analog sensors:

* offset\_current\_in/out: the reference voltage of the sensor. Changing it creates an additive shift in the measurements (Fig 1a)
* sps\_current\_in/out: controls the oversampling rate of the sensor, controlling the noise-to-signal ratio (i.e., variance, precision) of the underlying measurements (Fig 1b)
* res\_current\_in/out: controls the measurement range (resolution) of the sensor. Higher values correspond to smaller measurement ranges, increasing the resolution but risking saturation the sensor if the actual value falls outside this range (Fig 1c).

\[Figure 7: constant load, 2 rows (raw / calibrated), 4 columns: A: changes in offset, B: changes in sps, C: changes in res (with saturation)

Caption: Visualization of how the sensor parameters offset/sps/res\_current\_in affect the raw measurements (top: current\_in\_raw) and the calibrated measurements of the fan current (bottom: current\_in). The calibrated measurements compensate for changes in the reference voltage (offset\_\*) and resolution (sps\_\*), as long as saturation does not occur. The effects for \*current\_out\* are the same and not shown.

</details>

<details>

<summary><code>rpm_out</code> <span class="math">-</span> <code>rpm_in</code></summary>

In the Wind Tunnel, the fans work in tandem, i.e., one pushes air in and another out of the chamber. Thus, their speeds are coupled, i.e., changes in the speed of one fan will affect the other's, and viceversa.

**Modulation by** `res_rpm_in/out`

\[Copy from above]

**Modulation by** `hatch`

\[Copy / adapt from above]

* impulse response for different hatch positions
* steady state for different hatch positions
* constant load and hatch opening

</details>

<details>

<summary><code>rpm_in/out</code> <span class="math">-</span> <code>pressure_upwind/downwind/intake</code> </summary>

The fans pump air into and out of the chamber, affecting the air pressure inside the tunnel. Thus, their speed (as measured by `rpm_in/rpm_out`) affect the pressure measurements inside the tunnel (`pressure_upwind/downwind`) and at its intake (`pressure_intake`).

\[Figure 8: A: Impulse response (take one impulse from the dataset); B: steady state fan speeds vs. pressure heatmap]

> **Note:** All pressure measurements are affected by the [ambient atmospheric pressure](https://barometricpressure.app/zurich) at the location of our lab. To control for this effect, the variable `pressure_ambient` provides a direct measurement of ambient atmospheric pressure that is unaffected by the other chamber variables.

**Modulation via** `hatch`

The effect of the fan speeds on the air pressure is modulated by the hatch position, which controls an additional flow of air to/from the outside

* when the hatch is open, more air can escape, reducing the maximum possible change in the measurements `pressure_upwind/downwind`  (Fig 2).
* \[CHECK] when the hatch is open, the [impedance](https://blog.orientalmotor.com/fan-basics-air-flow-static-pressure-impedance) of the system is reduced, increasing the airspeed over the intake barometer and decreasing the measurement `pressure_intake` .

\[Figure 9: A: data from impulse experiment for different hatch positions, B: maybe steady-state heatmap?]

> **Note:** the relationship between `hatch` and the actual hatch position (as measured by `hatch_angle` ) is itself modulated by the motor parameters (`mot_enabled/max/steps`), which control the torque of the sensor and its resolution (see the [entry](#hatch-hatch_angle) for `hatch` $$\longrightarrow$$ `hatch_angle` below)

**Modulation via**  `osr_pressure_upwind/downwind/intake`

The variables `osr_pressure_upwind/downwind/intake/ambient` set the oversampling rate of the barometers, affecting the noise-to-signal ratio (i.e., variance, precision) of the resulting measurements.

\[Figure 10: time series from original paper figure, repeat the [osr\_barometers](https://github.com/juangamella/causal-chamber/blob/main/datasets/wt_test_v1/generators/osr_barometers.py) experiment]

</details>

<details>

<summary><code>hatch</code> <span class="math">\to</span> <code>hatch_angle</code></summary>

The variable `hatch_angle` produces a measurement of the hatch position using a rotary encoder. Under normal functioning of the motor that controls the hatch, the relationship between `hatch` and `hatch_angle` is the identity, up to the quantization error of the sensor (Fig 1.a).

\[Figure 11, hatch vs hatch angle: A: normal operation, B: different step sizes, C: effect of current (high, standard, low)]

**Modulation via the motor parameters** `mot_enable/steps/max`

The behaviour of the motor that opens the hatch can be controlled via three parameters:

* `mot_enabled` : whether the motor is enabled. When not (`mot_enabled=0`), changes in `hatch` have no effect on the actual hatch position, as measured by `hatch_angle`.
* `mot_steps`: controls the resolution of the motor, i.e., the number of steps per revolution.
* `mot_max` : controls the amount of electric current that flows through the motor. At very high values, the motor may exhibit oscillation after large movements (Fig 2c). For small values of `mot_steps`  and `mot_max` , the motor can miss steps, creating a mismatch between

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.causalchamber.ai/case-studies/anomaly-detection/anomaly-detection-with-time-series.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
