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.

ocrdu
1,7953 gold badges12 silver badges24 bronze badges
asked Jan 26, 2022 at 23:28
5
  • best-microcontroller-projects.com/arduino-absolute-value.html Commented Jan 27, 2022 at 0:38
  • you could average the reading over a period of time Commented 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/… Commented 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. Commented 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? Commented 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

answered Jan 27, 2022 at 8:33
5
  • We don't deserve you Edgar! Commented 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? Commented 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. Commented 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? Commented Jan 28, 2022 at 20:55
  • @boromyr: See expanded answer. Commented Jan 28, 2022 at 22:53

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.