4

Starting with an analog signal from any sensor, how do I automatically determine if there is a real signal change or a reset? Below is the sample code that better explains my goal:

value = sensorRead();

if (millis() > t + 1000) {
  value_old = value;
  t         = millis();
}
float d = value - value_old;
if (d < -3) {
  print("signal change")
  myfunction(); //ex. bink led
}

So if value(current) = 0.5 and value_old = 100 there has been a real change in signal, even if, because of noise, the value is not exactly 0.

It's very raw code, and I'd like to use a more professional approach that can detect both increments and decrements. Ideally, a suitable library, or alternatively a more efficient algorithm, would be ideal.

5
  • you could average the reading over a period of time
    – jsotola
    Jan 27, 2022 at 0:38
  • 1
    if (millis() > t + 1000) { will run into problems about 49 days after arduino reset ... read this norwegiancreations.com/2018/10/…
    – jsotola
    Jan 27, 2022 at 0:50
  • The problem is not the millis(). The values are already filtered by a moving average, I want to distinguish the increments.
    – boromyr
    Jan 27, 2022 at 1:41
  • What do you know about the process's insignificant noise? Do you care about drift? E.g. would +0.1 per cycle be noteworthy?
    – Dave X
    Jan 27, 2022 at 1:50

1 Answer 1

4

There is no simple and universal way of deciding what is to be considered a “significant” change. My recommendation would be to create an algorithm by playing with the data on your computer:

  • Upload a sketch to your Arduino that simply reads the sensor and transmits the readings to your computer via Serial.

  • Capture a long run of such readings in a file.

  • Graph the readings and look carefully at the graphs: what would you consider a significant change? What criteria could you use to decide that a change is significant? Devise an algorithm to take this decision.

  • Implement and test the algorithm on your computer, using the recorded data.

  • Port the code to the Arduino.

Below are two examples that maybe could give you some ideas. First comes the simplest I can think of: it says the change is significant if the current value is far enough from a previously recorded value. That recorded value is only updated after a significant change has been detected.

bool changed_significantly(int value) {
    const int change_threshold = 4;  // tune to taste
    static int old_value;
    bool changed = abs(value - old_value) >= change_threshold;
    if (changed)
        old_value = value;
    return changed;
}

It would be used like this:

void loop() {
    int value = sensorRead();
    if (changed_significantly(value))
        Serial.println("Signal changed.");
}

Note that, with this algorithm, a very slow drift would periodically trigger the detection of significant changes. If this is not what you want, you may prefer to only detect changes that happen fast enough. This can be detected by the use of a high-pass filter. The function below implements a simple low-pass filter (exponentially weighted moving average) and compares the current input to the previous output. The difference is equivalent to a high-pass filter. If that difference is large enough, a significant change is detected, then the filter is reset in order to prevent it from being repeatedly triggered by a drift:

bool changed_significantly(int value) {
    const float change_threshold = 4;   // tune to taste
    const float filter_constant = 0.1;  // this one also
    static float filtered_value;
    float delta = value - filtered_value;
    bool changed = abs(delta) >= change_threshold;
    if (changed)
        filtered_value = value;
    else
        filtered_value += filter_constant * delta;  // low-pass filter
    return changed;
}

Edit: some details about the filter.

This high-pass filter alone could be simplified as this:

float filter(float x)
{
    static float y;
    float delta = x - y;
    y += filter_constant * delta;
    return delta;
}

Note that, if this function returned y, we would have a conventional first-order low pass filter (exponentially weighted moving average). Since it returns instead the difference between the input and the previous low-pass-filtered value, it behaves as a high-pass filter. Its transfer function is

H = (z − 1) / (z + k − 1)

where k is the filter constant. This constant is related to the cut-off frequency as

fc ≈ k fs / (2π)

where fs is the sampling frequency. Note that, for frequencies significantly smaller than the cut-off, the filter behaves like a scaled derivative:

H x ≈ 1/(k fs) dx/dt

5
  • We don't deserve you Edgar! Jan 27, 2022 at 19:54
  • Why would small drift over time trigger the first code? Is it because it accumulates over a long period of time? Basically, is it a math thing, or a programming issue? Jan 27, 2022 at 20:09
  • 1
    @ZhelyazkoGrudov: It's the algorithm. The recorded value is only updated after a significant change has been detected. If there is a slow drift, it will accumulate change until that detection is triggered. Jan 27, 2022 at 20:22
  • Thank you so much, I tried your last function, and it works as desired, simple and effective!!!! I don't really understand the filter_constant function, how can I estimate the ideal value based on usage?
    – boromyr
    Jan 28, 2022 at 20:55
  • @boromyr: See expanded answer. Jan 28, 2022 at 22:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.