Go Down

Topic: [solved] VL53L0X distance meter does not beep faster/slower in linear fashion (Read 584 times) previous topic - next topic

Lagom

Jul 13, 2018, 10:30 am Last Edit: Aug 28, 2018, 04:49 pm by Lagom Reason: Added a photo
Hej,

using the code below, the buzzer does beep in slower and faster intervals as the distance increases or decreases - but it does so in audibly discrete levels, as if there were "beep speed levels" every few centimetres, while I had intended a continuous acceleration and deceleration of beeps according to the distance measured. Later, the distance shall also be mapped to an LED strip, and that should also happen without the apparent discretisation.

How can I remove the discretisation of beeps?

Many thanks in advance!

Code: [Select]
#include <Wire.h>
#include <VL53L0X.h> // Pololu library https://github.com/pololu/vl53l0x-arduino

const int buzzerPin = 8;

unsigned long previousMillis = 0; // Store the millisecond value after each time the buzzer was triggered

int maxBeepInterval = 1000; // Longest beep interval
int minBeepInterval = 50; // Shortest beep interval; cannot be shorter than sensing interval?!
int frequency = 440; // Buzzer frequency in Hz

long distance; // Distance from sensor
int triggerDistance = 1500; // Buzzer beeps if the measured distance in millimetres is less

VL53L0X sensor;

void setup() {
  Serial.begin(115200);
  Wire.begin();

  sensor.init();
  sensor.setTimeout(0); // Set sensor timeout in milliseconds (0 = no timeout)
  sensor.startContinuous(50); // Sensing interval
}

void loop() {
  long distance = sensor.readRangeContinuousMillimeters();

  if (distance < triggerDistance) { // Check if the distance is within range
    unsigned long currentMillis = millis(); // Store the current time

    // Beep interval will shorten as the measured distance decreases; from 1 beep per second (maxBeepInterval) to 1 beep every 20 milliseconds (minBeepInterval)
    // Map the distance value (which ranges from 30 millimetres to triggerDistance 1500 millimetres) from minBeepInterval to maxBeepInterval

    int val = map(distance, 30, triggerDistance, minBeepInterval, maxBeepInterval);

    if (currentMillis - previousMillis >= val) {
      tone(buzzerPin, frequency, val * 0.5);  //Trigger the buzzer (buzzerPin, frequency, duration); here each beep's duration is set to 50% of the time before it will be triggered again
      Serial.println(distance);
      previousMillis = currentMillis; // Save current time when the buzzer was triggered
    }
  }
}



pylon

Quote
How can I remove the discretisation of beeps?
How many discrete steps do you hear?

Did you check the sensor output? Does that have the same discrete steps?

Lagom

#2
Jul 13, 2018, 07:56 pm Last Edit: Jul 13, 2018, 08:06 pm by Lagom
Thanks, I adjusted the range for beeps between 30 and 1000 millimetres and, trying to move the target (matt light grey cardboard) as slow as possible, have the impression of 7 discrete "zones" that get larger as the distance increases.

The serial monitor does not show odd jumps, but also seems to display the distance in a ever faster fashion, as the distance decreases, although I've set the sensing interval to 100.

I have removed the serial monitor printing to see if it makes a difference, but it does not.

dougp

using the code below, the buzzer does beep in slower and faster intervals as the distance increases or decreases - but it does so in audibly discrete levels, as if there were "beep speed levels" every few centimetres, while I had intended a continuous acceleration and deceleration of beeps according to the distance measured.
Interesting.  I encountered the same phenomenon using the similar VL6180X in my project.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

Lagom

I encountered the same phenomenon using the similar VL6180X in my project.
Did you manage to rid your project of this "effect" or at least find what may be the reason? I am too much of a beginner to judge whether it's my sloppy coding or something else.

dougp

Did you manage to rid your project of this "effect" or at least find what may be the reason?
No, I just decided to live with it.  Although, I have a niggling feeling that tone() is somehow to blame.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

Lagom

No, I just decided to live with it. Although, I have a niggling feeling that tone() is somehow to blame.
With or without tone, the basic serial monitor output seems smooth to me.

Maybe I save that together with a time stamp and then try to move the target as evenly as possible to then see in Excel, if there are discrete plateaux or jumps.

pylon

Quote
Although, I have a niggling feeling that tone() is somehow to blame.
Does that mean the discrete levels are just in the length of the tone, not in the pause between the tones?

dougp

Does that mean the discrete levels are just in the length of the tone, not in the pause between the tones?
I just mean tone is a common feature between OP's issue and my own experience.  I did no testing to find the cause.  I could be completely off base.
So two neutrinos went into a bar.  Nothing happened.  They were just passing through.

Lagom

#9
Jul 17, 2018, 11:02 am Last Edit: Jul 17, 2018, 01:07 pm by Lagom Reason: Added screenshot/measurements/STM PDF link/latest code
A little bit of progress... even at a fixed distance to an object, the distance measured with the VL53L0X fluctuates rather much, (I would have expected the sensor to be better), introducing a weighted average filter takes some care of the fluctuations.

77 mm measured manually

55 mm shiny aluminium (extrusion)
67 mm coated/lacquered paper (Beginning C for Arduino book cover)
68 mm satin stainless steel (Mitutoyo slide rule)
71 mm anodised aluminium (iPad back)
77 mm near black matt rubber (iPad cover)
79 mm rough plank o' maple
86 mm human skin

These measurements are repeatable after resetting the Metro Mini or while interchanging the objects.

Is this to be expected? Are there some settings in the Pololu library or other tricks how one can get something far more predictable? In STMicroelectronics PDF from 2017 on page 4 it says that reflectance of object is not an issue and everything is just so wonderful : (

Back on topic, I meanwhile read that tone() uses its own timer and should not interfere with the rest of the code. Well, now back to the plateauing buzz issue then...



Code: [Select]
/******* VL53L0X distance meter (with OLED display and piezoelectronic buzzer) *******/

/******* LIBRARIES *******/

#include <Wire.h>
#include <VL53L0X.h> // Pololu library https://github.com/pololu/vl53l0x-arduino
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

/******* FUNCTION PROTOTYPES *******/

int ReadSensorVL53L0X();
float WeightedAverageFilter(float incomingValue, float lastValue);
void WriteToDisplaySSD1306 (int distance);
void EmitTimedBuzz (int distance);

/******* SENSOR *******/

VL53L0X sensor;

#define HIGH_ACCURACY
#define RANGEMIN 30
#define RANGEMAX 1200
#define VL53L0X_CORR 0 // Correction factor of 15 mm from table surface
#define WAF_WEIGHT 0.2 // Factor for the weighted average filter; higher = less smoothing

float lastDistance = 0;
float currentRange = RANGEMAX - RANGEMIN;

/******* DISPLAY *******/

#define OLED_RESET 4

Adafruit_SSD1306 display(OLED_RESET);

/******* BUZZER *******/

#define BUZZER_PIN 8 // Buzzer pin on Adafruit Metro Mini
#define BUZZER_FREQ 784 // Buzzer frequency in Hz (g''/G5) https://en.wikipedia.org/wiki/Piano_key_frequencies
#define BUZZMAX 1000 // Maximum duration of tone()

unsigned long buzzTimer;

/******* FUNCTIONS *******/

void setup() {
  Wire.begin();

  Serial.begin(9600);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // Default I2C address (for 0x3C connect SA0 pin to GND)
  display.setRotation(2); // Rotates display 180°
  display.clearDisplay();

  sensor.init();
  sensor.setTimeout(0); // Set sensor timeout in milliseconds (0 = no timeout)
  sensor.startContinuous(100); // Sensing interval four times per second
  sensor.setMeasurementTimingBudget(100000); // For HIGH_ACCURACY mode (default = 33)

  buzzTimer = millis(); // Start buzz timer
}

void loop() {
  int distance = ReadSensorVL53L0X();
  float currentDistance = WeightedAverageFilter(distance, lastDistance);
  lastDistance = currentDistance;
  if (currentDistance > RANGEMIN && currentDistance < RANGEMAX) {
    WriteToDisplaySSD1306 (currentDistance);
    EmitTimedBuzz (currentDistance);
  }
  else {
    Serial.println("Out of range!");
    display.display(); // Tell display to display nothing
    display.clearDisplay();
  }
}

void EmitTimedBuzz (int distance) {
  float distancePercentage = distance / currentRange * 100;
  float buzzLength = BUZZMAX / 100 * distancePercentage;

  if ((millis() - buzzTimer) > buzzLength) {
    buzzTimer += buzzLength; // Reset timer by moving it along to the next interval
    tone(BUZZER_PIN, BUZZER_FREQ, 100);
  }
}

int ReadSensorVL53L0X() {
  int reading = sensor.readRangeContinuousMillimeters();
  reading = reading + VL53L0X_CORR;
  return reading;
}

float WeightedAverageFilter(float incomingValue, float lastValue) { // See https://www.tigoe.com/pcomp/code/arduinowiring/37/
  float result = WAF_WEIGHT * incomingValue + (1.0 - WAF_WEIGHT) * lastValue;
  return result;
}

void WriteToDisplaySSD1306 (int distance) {
  display.setCursor(8, 20); // x,y co-ordinates
  display.setTextSize(3);
  display.setTextColor(WHITE); // Monochrome, BLACK would erase
  display.println(distance); // Reading from sensor
  display.setCursor(86, 20);
  display.setTextSize(3);
  display.println("mm");
  display.display(); // Tell display to display everything
  display.clearDisplay(); // Clears the display
}

pylon

According to the Pololu product description below I would have expected about that behavior.

Quote
The sensor can report distances of up to 2 m (6.6 ft) with 1 mm resolution, but its effective range and accuracy (noise) depend heavily on ambient conditions and target characteristics like reflectance and size, as well as the sensor configuration. (The sensor's accuracy is specified to range from ±3% at best to over ±10% in less optimal conditions.)

mikb55

If you have ever shined a flashlight through your hand you will know that skin is translucent and therefore some of the photons reflected back to the sensor will be from several mm below the surface of the skin. Perhaps it errs on the side of longest travel time rather than shortest.

Wood, paper and many plastics are translucent to some degree  with light bouncing around inside the material before re-emerging at the surface and returning to the sensor. Fluorescent pigments are added to paper to make it appear super bright white under office lighting. This may be confusing the sensor as fluorescence isn't instantaneous.   

The transmitted 'beam' is actually a 30 deg cone. The acceptance angle of the sensor is 25 deg, therefore when measuring a flat surface at a short distance the geometry means that photons at the periphery of the cone will be travelling further to reach the target than those directly on axis.

The off-axis reflectivity characteristics may also influence the way the distance is calculated within the device. For something highly polished the vast majority of the received photons will be directly on axis. For something like paper you would expect photons from all angles and therefore a range of distances.

Lagom

#12
Jul 17, 2018, 05:39 pm Last Edit: Jul 17, 2018, 05:53 pm by Lagom Reason: Added new measurements

Thanks m &p; the STM PDF on slide 4 made it sound very different though, which was behind the decision not to use an IR or ultrasonic sensor.

203 (77) mm measured manually, same ambient light conditions as before, room temperature also

176 174 (55) mm shiny aluminium (extrusion)
197 192 (67) mm coated/lacquered paper (Beginning C for Arduino book cover)
193 189 (68) mm satin stainless steel (Mitutoyo slide rule)
205 203 (71) mm anodised aluminium (iPad back)
214 211 (77) mm near black matt rubber (iPad cover)

215 213 (79) mm rough plank o' maple
220 219 (86) mm human skin

First row numbers are with the weighted averaging disabled, they fluctuate much and are averaged by eye; looks like that ole iPad is the winner ; )

But it really means this sensor is no good with such a huge delta. What breakout-board would you suggest as an alternative, even if it costs twice or thrice the price?

mikb55

Thanks m &p; the STM PDF on slide 4 made it sound very different though, which was behind the decision not to use an IR or ultrasonic sensor.
You known in those tv commercials when they make something sound really good, then right at the end someone speaks really fast and says a bunch of stuff that basically says that "your milage may vary, conditions apply", well it's like that.

That's why companies sell evaluation boards so that people can see for themselves how a product behaves under real world conditions before committing to using it in a product.

pylon

Quote
What breakout-board would you suggest as an alternative, even if it costs twice or thrice the price?
To be able to suggest anything we have to know your specs or at least what it would be used for (so we might create the specs ourselves). Do you want a metering device that's exact to the millimeter or a car parking lot beeper?

Go Up