garmin / LIDARLite_Arduino_Library

High-performance optical distance sensing.
Apache License 2.0
181 stars 84 forks source link

Add the acquisition frequency depending on the mode. #1

Closed AlexisTM closed 7 years ago

AlexisTM commented 7 years ago

Dear,

Here are the results of my benchmark (for one laser), so people can see what configuration fits the user needs.

The test is done with :

Results :

Configuration -1 distanceFast(true) : 559.69Hz, errors : 36 on 1000 measurements
Configuration -1 distanceFast(false) : 1048.50Hz, errors : 40 on 1000 measurements
Configuration 0 distance(true) : 258.22Hz, errors : 0 on 1000 measurements
Configuration 0 distance(false) : 321.70Hz, errors : 0 on 1000 measurements
Configuration 1 distance(true) : 371.17Hz, errors : 0 on 1000 measurements
Configuration 1 distance(false) : 506.87Hz, errors : 0 on 1000 measurements
Configuration 2 distance(true) : 473.07Hz, errors : 0 on 1000 measurements
Configuration 2 distance(false) : 669.00Hz, errors : 0 on 1000 measurements
Configuration 3 distance(true) : 257.01Hz, errors : 0 on 1000 measurements
Configuration 3 distance(false) : 322.77Hz, errors : 0 on 1000 measurements
Configuration 4 distance(true) : 256.93Hz, errors : 0 on 1000 measurements
Configuration 4 distance(false) : 323.17Hz, errors : 0 on 1000 measurements
Configuration 5 distance(true) : 256.37Hz, errors : 0 on 1000 measurements
Configuration 5 distance(false) : 323.01Hz, errors : 0 on 1000 measurements

Code :

/*------------------------------------------------------------------------------

  LIDARLite v2 library (from Garmin) benchmark

  This script test the performance of the lasers depending on the 
configurations 
------------------------------------------------------------------------------*/

#include <Wire.h>
#include <LIDARLite.h>

LIDARLite myLidarLite;

long now, last;
int errors = 0;

#define N_MEASURE_PROFILE 1000.0

void setup()
{
  Serial.begin(115200); 
  myLidarLite.begin(0, true); // Set configuration to default and I2C to 400 kHz
}

void loop()
{
  /* distanceFast Config */ 
  myLidarLite.write(0x02, 0x0d); // Maximum acquisition count of 0x0d. (default is 0x80)
  myLidarLite.write(0x04, 0b00000100); // Use non-default reference acquisition count
  myLidarLite.write(0x12, 0x03); // Reference acquisition count of 3 (default is 5)

  last = micros();
  for(int i = 0; i < N_MEASURE_PROFILE; i++)
  {
    isError(distanceFast(true));
  }
  now = micros();
  Serial.print("Configuration ");
  Serial.print("-1");
  Serial.print(" distanceFast(true) : ");
  Serial.print(N_MEASURE_PROFILE*1000000.0/double(now-last));
  Serial.print("Hz, errors : ");
  Serial.print(errors);
  Serial.println(" on 1000 measurements");
  errors = 0;

  last = micros();
  for(int i = 0; i < N_MEASURE_PROFILE; i++)
  {
    isError(distanceFast(false));
  }
  now = micros();
  Serial.print("Configuration ");
  Serial.print("-1");
  Serial.print(" distanceFast(false) : ");
  Serial.print(N_MEASURE_PROFILE*1000000.0/double(now-last));
  Serial.print("Hz, errors : ");
  Serial.print(errors);
  Serial.println(" on 1000 measurements");
  errors = 0;

  for(int conf = 0; conf < 6; conf++){
    myLidarLite.configure(conf); 
    last = micros();
    for(int i = 0; i < N_MEASURE_PROFILE; i++)
    {
      isError(myLidarLite.distance(true));
    }
    now = micros();
    Serial.print("Configuration ");
    Serial.print(conf);
    Serial.print(" distance(true) : ");
    Serial.print(N_MEASURE_PROFILE*1000000.0/double(now-last));
    Serial.print("Hz, errors : ");
    Serial.print(errors);
    Serial.println(" on 1000 measurements");
    errors = 0;

    last = micros();
    for(int i = 0; i < N_MEASURE_PROFILE; i++)
    {
      isError(myLidarLite.distance(false));
    }
    now = micros();
    Serial.print("Configuration ");
    Serial.print(conf);
    Serial.print(" distance(false) : ");
    Serial.print(N_MEASURE_PROFILE*1000000.0/double(now-last));
    Serial.print("Hz, errors : ");
    Serial.print(errors);
    Serial.println(" on 1000 measurements");
    errors = 0;
  }
}

// Read distance. The approach is to poll the status register until the device goes
// idle after finishing a measurement, send a new measurement command, then read the
// previous distance data while it is performing the new command.
int distanceFast(bool biasCorrection)
{
  byte isBusy = 1;
  int distance;
  int loopCount;

  // Poll busy bit in status register until device is idle
  while(isBusy)
  {
    // Read status register
    Wire.beginTransmission(LIDARLITE_ADDR_DEFAULT);
    Wire.write(0x01);
    Wire.endTransmission();
    Wire.requestFrom(LIDARLITE_ADDR_DEFAULT, 1);
    isBusy = Wire.read();
    isBusy = bitRead(isBusy,0); // Take LSB of status register, busy bit

    loopCount++; // Increment loop counter
    // Stop status register polling if stuck in loop
    if(loopCount > 9999)
    {
      break;
    }
  }

  // Send measurement command
  Wire.beginTransmission(LIDARLITE_ADDR_DEFAULT);
  Wire.write(0X00); // Prepare write to register 0x00
  if(biasCorrection == true)
  {
    Wire.write(0X04); // Perform measurement with receiver bias correction
  }
  else
  {
    Wire.write(0X03); // Perform measurement without receiver bias correction
  }
  Wire.endTransmission();

  // Immediately read previous distance measurement data. This is valid until the next measurement finishes.
  // The I2C transaction finishes before new distance measurement data is acquired.
  // Prepare 2 byte read from registers 0x0f and 0x10
  Wire.beginTransmission(LIDARLITE_ADDR_DEFAULT);
  Wire.write(0x8f);
  Wire.endTransmission();

  // Perform the read and repack the 2 bytes into 16-bit word
  Wire.requestFrom(LIDARLITE_ADDR_DEFAULT, 2);
  distance = Wire.read();
  distance <<= 8;
  distance |= Wire.read();

  // Return the measured distance
  return distance;
}

void isError(int data){
  if(data > 250) {
    errors++;
  } else if(data < 150){
    errors++;
  }
}

Configure function at the time of the writing :

void LIDARLite::configure(int configuration, char lidarliteAddress)
{
  switch (configuration)
  {
    case 0: // Default mode, balanced performance
      write(0x02,0x80,lidarliteAddress); // Default
      write(0x04,0x08,lidarliteAddress); // Default
      write(0x1c,0x00,lidarliteAddress); // Default
    break;

    case 1: // Short range, high speed
      write(0x02,0x1d,lidarliteAddress);
      write(0x04,0x08,lidarliteAddress); // Default
      write(0x1c,0x00,lidarliteAddress); // Default
    break;

    case 2: // Default range, higher speed short range
      write(0x02,0x80,lidarliteAddress); // Default
      write(0x04,0x00,lidarliteAddress);
      write(0x1c,0x00,lidarliteAddress); // Default
    break;

    case 3: // Maximum range
      write(0x02,0xff,lidarliteAddress);
      write(0x04,0x08,lidarliteAddress); // Default
      write(0x1c,0x00,lidarliteAddress); // Default
    break;

    case 4: // High sensitivity detection, high erroneous measurements
      write(0x02,0x80,lidarliteAddress); // Default
      write(0x04,0x08,lidarliteAddress); // Default
      write(0x1c,0x80,lidarliteAddress);
    break;

    case 5: // Low sensitivity detection, low erroneous measurements
      write(0x02,0x80,lidarliteAddress); // Default
      write(0x04,0x08,lidarliteAddress); // Default
      write(0x1c,0xb0,lidarliteAddress);
    break;
  }
} /* LIDARLite::configure */
nseverson commented 7 years ago

Hi Alexis,

Full descriptions of device settings for v3 can be found in the new Operation Manual.

By the way, I saw your LIDAREnhanced library, nice job!

AlexisTM commented 7 years ago

I had to rewrite the library, because the base library was unusable for our application. Indeed, we needed high acquisition speed across multiple lasers and absolutely a non blocking architecture. If a laser have a "bug", which happens with v2, it had to go on with the next one.

With more lasers, our acquisition speed limitation is the I2C bus. To allow higher speed we would need to predict the next measure time to avoid using the bus while we know it will be busy. (Actual maximum speed: 2kHz, maximal estimated speed with synchronisation of busy time: 3kHz)

Anyway, does the receiver bias correction simply increase the acquisition time or does it add imprecision across measurements if we do it at every cycle ?

nseverson commented 7 years ago

The measurement duration increases-- the device tries to find the right dc level for the receiver, it performs several iterations until the best dc level is found, and it will timeout after a certain number of iterations.

So the time the routine adds will vary, but there is an upper limit to this added time.