HIGH_SPEED with VL53L0X?

Hello Pololu,

thank you for sharing your library for a VL53L0X proximity sensor!!

With that VL53L0X.h and your example code Singel.ino I get distance values each 33 ms. This is the default and it works, as expected. Great!

But for my experimenting I need the HIGH_SPEED mode of the sensor, which aims to reduce the measurement timing to 20 ms.

I expected, that uncommenting of //#define HIGH_SPEED would result in distance values each 20 ms. But in the serial console of my Arduino IDE I see something else. Often two following values have the same timestamp (and not 20 ms apart). And the other timestamps more fit into 33 ms periods, than 20 ms.

Has anyone been able to switch to HIGH_SPEED mode (20 ms) with a VL53L0X?
If so, how?

Thanks for reading

You can find more information about the timing requirements for taking measurements with the sensor in the “Ranging profile phases” section of the sensor’s datasheet (which begins on page 13). The setMeasurementTimingBudget() function in our library sets the time the sensor is in the “Range measurement” portion of measurement, but there is still some other processing that must occur before and after the sensor is actively measuring.

By the way, you can set the timing budget as low as 8ms if you need a faster sampling rate. Also, the continuous measurement mode should involve a little less overhead since the measurement does not need to be set up every time.

-Nathan

Hello nathanb,

thanks for answering!
I also tried the continuous measurement mode in the example. It has no HIGH_SPEED switch, so I inserted setMeasurementTimingBudget() manually. Further more I reduced the sketch to the minimum (no TIMEOUT ckecking, just one println). The code is attached.

/* This example shows how to use continuous mode to take
range measurements with the VL53L0X. It is based on
vl53l0x_ContinuousRanging_Example.c from the VL53L0X API.

The range readings are in units of mm. */

#include <Wire.h>
#include <VL53L0X.h>

VL53L0X sensor;

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

  sensor.init();
  // sensor.setTimeout(500);

  // Start continuous back-to-back mode (take readings as
  // fast as possible).  To use continuous timed mode
  // instead, provide a desired inter-measurement period in
  // ms (e.g. sensor.startContinuous(100)).
  sensor.setMeasurementTimingBudget(20000);
  sensor.startContinuous();
}

void loop()
{
  // Serial.print(sensor.readRangeContinuousMillimeters());
  // if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }

  // Serial.println();
  Serial.println(sensor.readRangeContinuousMillimeters());
}

I get values from Arduino IDE like the following (no 20 ms intervals):

07:24:53.948 -> 52
07:24:53.981 -> 56
07:24:53.981 -> 49
07:24:54.015 -> 49
07:24:54.015 -> 57
07:24:54.048 -> 54
07:24:54.048 -> 53
07:24:54.081 -> 55
07:24:54.114 -> 48
07:24:54.114 -> 51
07:24:54.148 -> 50
07:24:54.148 -> 54
07:24:54.181 -> 54
07:24:54.181 -> 55
07:24:54.214 -> 49
07:24:54.214 -> 50
07:24:54.247 -> 50
07:24:54.281 -> 55
07:24:54.281 -> 49
07:24:54.314 -> 52
07:24:54.314 -> 54
07:24:54.347 -> 48
07:24:54.347 -> 52
07:24:54.380 -> 49
07:24:54.380 -> 51
07:24:54.414 -> 48
07:24:54.447 -> 51
07:24:54.447 -> 48
07:24:54.480 -> 53
07:24:54.480 -> 52
07:24:54.513 -> 56
07:24:54.513 -> 48

It seems to me, that setMeasurementTimingBudget() is not enough to minimize the time for measurement. Has it ever worked at someone?

Reading the datasheet link (thanks!) I’m not sure, if 8 ms is theoretically possible. Maybe the datasheet means:

Initialization (~1ms*) plus
Range set up (~8ms*) plus
Range measurement (~8ms) plus
Digital processing (~0.8ms*)

In sum it is near 20 ms, but maybe I’m wrong with this interpretation. I would be happy, if even 20 ms would be possible. Down to 8 ms would be a dream.

Hi, Toffifee.

In your output, there are many readings with identical timestamps but different values, which suggest that the timestamps don’t accurately reflect the time between readings. I suspect this is because of some serial buffering going on between the Arduino and the computer where the Serial Monitor is running.

I modified your loop function slightly to have the Arduino itself track and print the time between measurements:

void loop()
{
  static uint16_t last;
  uint16_t now = millis();
  Serial.print((uint16_t)(now - last));
  last = now;
  Serial.print(" ms - ");
  Serial.println(sensor.readRangeContinuousMillimeters());
}

This is a sample of the output that I get:

18 ms - 66
18 ms - 59
19 ms - 61
18 ms - 64
19 ms - 60
18 ms - 59
19 ms - 61
18 ms - 59
18 ms - 62
19 ms - 60
18 ms - 59
19 ms - 61
18 ms - 64
19 ms - 59
18 ms - 64
18 ms - 63
19 ms - 64
18 ms - 61
19 ms - 62
18 ms - 66
19 ms - 64
18 ms - 59
19 ms - 61
18 ms - 57
18 ms - 59
19 ms - 59
18 ms - 60
19 ms - 61
18 ms - 63
19 ms - 65

So it looks like the measurements are actually taking less than 20 ms each.

As for the 8 ms minimum that Nathan mentioned, it looks like the datasheet and possibly the official API have been revised since I wrote our VL53L0X library (the older datasheet mentions a 20 ms minimum). Our library doesn’t allow a timing budget below 20 ms, and trying to set a lower value results in no change, so I’ll have to see if we need to update it to allow it to work with shorter timing budgets.

Kevin

1 Like

Excellent kevin!

I can confirm your results on my system, example:

19 ms - 79
18 ms - 80
19 ms - 83
18 ms - 76
19 ms - 85
18 ms - 83
19 ms - 81
18 ms - 83
19 ms - 83
19 ms - 84
18 ms - 82
19 ms - 81
18 ms - 82
19 ms - 82
18 ms - 86
19 ms - 80
18 ms - 82
19 ms - 81
18 ms - 84
19 ms - 81
19 ms - 81
18 ms - 80
19 ms - 80
18 ms - 79
19 ms - 80
18 ms - 81

I’m using this microcontroller
“Teensy 3.2/ 3.1, Serial, 96 MHz (overclock), Faster, US English on /dev/ttyACM0” under Debian 9 Linux. The abolute (wrong) timestamps were added by Arduino 1.8.8 IDE in its Serial Monitor window, if I understand it correctly.

The intention is to use the proximity sensor for velocity measurements in the nearfield, so shorter sampling intervals are desirable. I read this costs precision so I have to do some trial and error. Would be interesting, if ultra speed samplings with 8 ms are really possible with this amazing little laser sensors.

Many thanks!!

I ran across this topic while looking for solutions to my own problem with too-slow measurement responses for a wall-following robot project, and thought my testing results using a 6-element array of VL53L0X sensors might be useful for others.

I combined the code from the above posts with my setup code for multiple sensors, and recorded the distance measurements from all six sensors. The interesting this is the total loop time required to produce measurements for all six sensors was still only about 18-19 mSec. IOW (assuming I’m not blowing smoke here), the additional five sensors don’t impose any significant additional time penalty!

Here’s my code:

/*
    Name:       TeensyHexVL53L0XHighSpeedDemo.ino
    Created:	10/1/2020 3:49:01 PM
    Author:     FRANKNEWXPS15\Frank

    This demo program combines the code from my Teensy_VL53L0X_I2C_Slave_V2
    project with the VL53L0X 'high speed' code from the Pololu support
    forum post at https://forum.pololu.com/t/high-speed-with-vl53l0x/16585/3

*/


/*
    Name:       Teensy_VL53L0X_I2C_Slave_V2.ino
    Created:	8/4/2020 7:42:22 PM
    Author:     FRANKNEWXPS15\Frank

    This project merges KurtE's multi-wire capable version of Pololu's VL53L0X library
    with my previous Teensy_VL53L0X_I2C_Slave project, which used the Adafruit VL53L0X
    library.  This (hopefully) will be the version uploaded to the Teensy on the 2nd
    deck of Wall-E2 and which controls the left & right 3-VL53L0X sensor arrays

*/

#include <Wire.h> //using Wire.h here instead of i2c_t3.h for multiple I2C bus access
#include <VL53L0X.h>
#include "I2C_Anything.h" //local version modified to use Wire.h vs SBWire

volatile uint8_t request_type;


enum VL53L0X_REQUEST
{
  VL53L0X_CENTERS_ONLY,
  VL53L0X_RIGHT,
  VL53L0X_LEFT,
  VL53L0X_BOTH //added 08/05/20
};


//right side array
VL53L0X lidar_RF(&Wire1);
VL53L0X lidar_RC(&Wire1);
VL53L0X lidar_RR(&Wire1);

//left side array
VL53L0X lidar_LF(&Wire2);
VL53L0X lidar_LC(&Wire2);
VL53L0X lidar_LR(&Wire2);

//XSHUT required for I2C address init 
//right side array
const int RF_XSHUT_PIN = 23;
const int RC_XSHUT_PIN = 22;
const int RR_XSHUT_PIN = 21;

//left side array
const int LF_XSHUT_PIN = 5;
const int LC_XSHUT_PIN = 6;
const int LR_XSHUT_PIN = 7;

const int MAX_LIDAR_RANGE_MM = 1000; //make it obvious when nearest object is out of range

//right side array
uint16_t RF_Dist_mm;
uint16_t RC_Dist_mm;
uint16_t RR_Dist_mm;

//left side array
uint16_t LF_Dist_mm;
uint16_t LC_Dist_mm;
uint16_t LR_Dist_mm;


float RightSteeringVal;
float LeftSteeringVal;

const int SLAVE_ADDRESS = 0x20; //just a guess for now
const int DEFAULT_VL53L0X_ADDR = 0x29;

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

  Wire.begin(SLAVE_ADDRESS); //set Teensy Wire0 port up as slave
  Wire.onRequest(requestEvent);  //ISR for I2C requests from master (Mega 2560)
  Wire.onReceive(receiveEvent); //ISR for I2C data from master (Mega 2560)

  Wire1.begin();
  Wire2.begin();

  // wait until serial port opens ... For 5 seconds max
  while (!Serial && millis() < 5000);

  pinMode(RF_XSHUT_PIN, OUTPUT);
  pinMode(RC_XSHUT_PIN, OUTPUT);
  pinMode(RR_XSHUT_PIN, OUTPUT);

  pinMode(LF_XSHUT_PIN, OUTPUT);
  pinMode(LC_XSHUT_PIN, OUTPUT);
  pinMode(LR_XSHUT_PIN, OUTPUT);

  //Put all sensors in reset mode by pulling XSHUT low
  digitalWrite(RF_XSHUT_PIN, LOW);
  digitalWrite(RC_XSHUT_PIN, LOW);
  digitalWrite(RR_XSHUT_PIN, LOW);

  digitalWrite(LF_XSHUT_PIN, LOW);
  digitalWrite(LC_XSHUT_PIN, LOW);
  digitalWrite(LR_XSHUT_PIN, LOW);

  //now bring lidar_RF only out of reset and set it's address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(RF_XSHUT_PIN, INPUT);
  delay(10);

  if (!lidar_RF.init())
  {
    Serial.println("Failed to detect and initialize lidar_RF!");
    while (1) {}
  }

  //from Pololu forum post
  lidar_RF.setMeasurementTimingBudget(20000);
  lidar_RF.startContinuous();


  lidar_RF.setAddress(DEFAULT_VL53L0X_ADDR + 1);
  Serial.printf("lidar_RF address is 0x%x\n", lidar_RF.getAddress());

  //now bring lidar_RC only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(RC_XSHUT_PIN, INPUT);

  delay(10);
  if (!lidar_RC.init())
  {
    Serial.println("Failed to detect and initialize lidar_RC!");
    while (1) {}
  }

  //from Pololu forum post
  lidar_RC.setMeasurementTimingBudget(20000);
  lidar_RC.startContinuous();

  lidar_RC.setAddress(DEFAULT_VL53L0X_ADDR + 2);
  Serial.printf("lidar_RC address is 0x%x\n", lidar_RC.getAddress());

  //now bring lidar_RR only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(RR_XSHUT_PIN, INPUT);
  delay(10);

  if (!lidar_RR.init())
  {
    Serial.println("Failed to detect and initialize lidar_RR!");
    while (1) {}
  }

  //from Pololu forum post
  lidar_RR.setMeasurementTimingBudget(20000);
  lidar_RR.startContinuous();

  lidar_RR.setAddress(DEFAULT_VL53L0X_ADDR + 3);
  Serial.printf("lidar_RR address is 0x%x\n", lidar_RR.getAddress());

  //now bring lidar_LF only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(LF_XSHUT_PIN, INPUT);
  delay(10);

  if (!lidar_LF.init())
  {
    Serial.println("Failed to detect and initialize lidar_LF!");
    while (1) {}
  }

  //from Pololu forum post
  lidar_LF.setMeasurementTimingBudget(20000);
  lidar_LF.startContinuous();

  lidar_LF.setAddress(DEFAULT_VL53L0X_ADDR + 4);
  Serial.printf("lidar_LF address is 0x%x\n", lidar_LF.getAddress());

  //now bring lidar_LC only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(LC_XSHUT_PIN, INPUT);
  delay(10);

  if (!lidar_LC.init())
  {
    Serial.println("Failed to detect and initialize lidar_LC!");
    while (1) {}
  }

  //from Pololu forum post
  lidar_LC.setMeasurementTimingBudget(20000);
  lidar_LC.startContinuous();

  lidar_LC.setAddress(DEFAULT_VL53L0X_ADDR + 5);
  Serial.printf("lidar_LC address is 0x%x\n", lidar_LC.getAddress());

  //now bring lidar_LR only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(LR_XSHUT_PIN, INPUT);

  if (!lidar_LR.init())
  {
    Serial.println("Failed to detect and initialize lidar_LR!");
    while (1) {}
  }

  //from Pololu forum post
  lidar_LR.setMeasurementTimingBudget(20000);
  lidar_LR.startContinuous();

  lidar_LR.setAddress(DEFAULT_VL53L0X_ADDR + 6);
  Serial.printf("lidar_LR address is 0x%x\n", lidar_LR.getAddress());

  Serial.printf("Msec\tLFront\tLCtr\tLRear\tRFront\tRCtr\tRRear\tLSteer\tRSteer\n");
}

void loop()
{
  //from Pololu forum post
  RF_Dist_mm = lidar_RF.readRangeContinuousMillimeters();
  RC_Dist_mm = lidar_RC.readRangeContinuousMillimeters();
  RR_Dist_mm = lidar_RR.readRangeContinuousMillimeters();

  RightSteeringVal = (RF_Dist_mm - RR_Dist_mm) / 100.f; //rev 06/21/20 see PPalace post

  //from Pololu forum post
  LF_Dist_mm = lidar_LF.readRangeContinuousMillimeters();
  LC_Dist_mm = lidar_LC.readRangeContinuousMillimeters();
  LR_Dist_mm = lidar_LR.readRangeContinuousMillimeters();

  LeftSteeringVal = (LF_Dist_mm - LR_Dist_mm) / 100.f; //rev 06/21/20 see PPalace post

  Serial.printf("%lu\t%d\t%d\t%d\t%d\t%d\t%d\t%2.2f\t%2.2f\n",
    millis(),
    LF_Dist_mm, LC_Dist_mm, LR_Dist_mm,
    RF_Dist_mm, RC_Dist_mm, RR_Dist_mm,
    LeftSteeringVal, RightSteeringVal);
}

and here's the results from the run


Opening port
Port open
lidar_RF address is 0x2a
lidar_RC address is 0x2b
lidar_RR address is 0x2c
lidar_LF address is 0x2d
lidar_LC address is 0x2e
lidar_LR address is 0x2f
Msec	LFront	LCtr	LRear	RFront	RCtr	RRear	LSteer	RSteer
1238	530	488	424	195	271	297	1.06	-1.02
1256	532	516	411	190	261	294	1.21	-1.04
1274	558	525	429	193	249	296	1.29	-1.03
1293	545	526	418	194	254	293	1.27	-0.99
1311	532	516	411	196	270	284	1.21	-0.88
1329	528	516	411	193	274	295	1.17	-1.02
1347	547	515	412	190	259	303	1.35	-1.13
1365	542	503	404	191	261	295	1.38	-1.04
1383	556	536	425	199	250	295	1.31	-0.96
1401	551	536	415	195	247	296	1.36	-1.01
1420	525	516	411	190	265	300	1.14	-1.10
1438	540	530	415	205	257	299	1.25	-0.94
1456	532	508	420	201	280	298	1.12	-0.97
1474	525	521	411	202	246	295	1.14	-0.93
1492	544	529	429	186	247	294	1.15	-1.08
1511	533	528	423	197	260	288	1.10	-0.91
1529	544	522	417	193	251	287	1.27	-0.94
1547	549	530	420	200	271	296	1.29	-0.96
1565	545	496	424	203	261	287	1.21	-0.84
1583	544	504	410	198	269	301	1.34	-1.03
1601	553	537	409	194	262	299	1.44	-1.05
1619	551	522	409	191	264	290	1.42	-0.99
1638	548	537	430	195	256	292	1.18	-0.97
1656	538	505	420	186	252	293	1.18	-1.07
1674	538	516	423	194	265	284	1.15	-0.90
1692	535	529	429	193	258	296	1.06	-1.03
1710	544	514	428	197	260	288	1.16	-0.91
1728	539	528	415	201	270	277	1.24	-0.76
1746	541	519	420	198	248	294	1.21	-0.96
1765	535	522	426	190	265	294	1.09	-1.04
1783	536	521	411	193	254	293	1.25	-1.00
1801	540	522	423	194	265	308	1.17	-1.14
1819	553	540	422	196	263	297	1.31	-1.01

Apparently, I can get results from all six sensors in the same time as for just one sensor. Anyone care to comment - is this too good to be true?

Frank

2 Likes

Hi, I read the datasheet but I still dont know how to change my timing for less than 20ms. I’m trying to reach 10ms (I need something between 60~100Hz)
I tried to read the lib, there’s a MinTimingBudget but change that is obviously not enough. What else should I look for?

Hi, Barth.

Even though the datasheet states the minimum timing budget is 8ms, I looked into this again with the latest ST API and was unable to set it much lower than about 17.1ms (about 58Hz). I would guess there might be some measurement steps that could be disabled to get a higher speed at the cost of some reliability or accuracy, but I haven’t found any documentation or examples from ST that show how to do this.

Note that early versions of the API explicitly enforced a minimum of 20ms, so that is what the Pololu library does too. That check was removed in the current version of the API, which seems to let us push it a bit lower to ~17.1ms. I’ll update our library to match that. If you know of any info about how to use a an even lower timing budget with the ST API, let me know and I can try to make the library support that too.

Kevin

Hi Kevin, ty for your reply

I dont care much about the accuracy as I’m using it only as security system. I tried a lot, and only reached something around 18ms (i’m calculating other things), so I think its the same as you. Its not being a problem, but its not as good as I was expecting, so I just gave up for a while until I finish other things in my project or someone help me with this. When I get back to this if I find something I’ll let you know

Hey Kevin I have a question.
How to switch sensor between Range profile’s (Default mode, High accuracy, Long range, High speed)?
Why does the number in setMeasurementTimingBudget(20000) set the sensor to High Speed ?

Hi, tatarka.

There are no dedicated functions in the ST API or in our library to select range profiles, but they can be used by configuring specific settings as shown in the “Single” example sketch in our library. The settings come from the example programs that come with the ST API, and section 5.3.2 “Range profile examples” in the VL53L0X datasheet briefly covers them (although it only describes the timing budget setting, not any of the others). The default timing budget is 33 ms, and setMeasurementTimingBudget(20000) reduces it to 20 ms for the high speed profile.

Kevin