Thursday, June 20, 2024

Calibrating the vl5310x TOF laser distance sensor.

Here is a good starting point, however, it might need a polynomial regression.

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

VL53L0X sensor;
Preferences laserPrefs;

const int numMeasurements = 100;  // Number of measurements to average
const int numCalPoints = 7;  // Number of calibration points
uint16_t measuredDistances[numCalPoints];
uint16_t actualDistances[numCalPoints] = {10, 100, 300, 600, 900, 1000, 1050}; // Replace with your actual distances in mm

// Calibration parameters
float offset = 0.0;
float scale = 1.0;

void setup()
{
  Serial.begin(9600);
  delay(500);
  Wire.begin();

  // Initialize Preferences for laser calibration data (read-write)
  laserPrefs.begin("laser_calibration", false); // Initialize for writing
  if (!laserPrefs.isKey("offset")) {
    Serial.println("No laser calibration data found. Please calibrate and store offsets.");
    // Perform laser sensor calibration and store offsets if necessary
  } else {
    Serial.println("Laser calibration data loaded.");
    // Load laser calibration offsets if they exist
  }

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

  // 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.startContinuous();
  // Perform calibration if no stored data found
  for (int i = 0; i < numCalPoints; i++) {
    Serial.print("Place the sensor at ");
    Serial.print(actualDistances[i]);
    Serial.println(" mm");
    delay(5000); // Wait for user to place the sensor

    measuredDistances[i] = measureDistance();
    Serial.print("Measured distance: ");
    Serial.println(measuredDistances[i]);
    // Prompt for user confirmation of measured distance
    if (!confirmDistance(actualDistances[i], measuredDistances[i])) {
      Serial.println("Measurement rejected. Please adjust sensor position and retry.");
      i--; // Retry measurement for the same distance
    } else {
      Serial.println("Measurement accepted.");
    }
  }

  // Calculate calibration parameters
  offset = calculateOffset();
  scale = calculateScale();

  // Store calibration data
  laserPrefs.putFloat("offset", offset);
  laserPrefs.putFloat("scale", scale);
  Serial.println("Calibration data stored in laserPrefs");
 
  Serial.print("Calibration offset: ");
  Serial.println(offset);
  Serial.print("Calibration scale: ");
  Serial.println(scale);

  Serial.println("Entering main loop...");
}

void loop()
{
  int tofDist = sensor.readRangeContinuousMillimeters();
  if(tofDist < 2000 && tofDist != 8191){
    float calibratedDist = tofDist * scale + offset;
    Serial.print("Raw Distance: ");
    Serial.print(tofDist);
    Serial.print(" mm, Calibrated Distance: ");
    Serial.print(calibratedDist);
    Serial.println(" mm");
    delay(1000); // Adjust delay as necessary
  }
 
  //if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }

 
}

uint16_t measureDistance() {
  uint32_t sum = 0;  // Use uint32_t to avoid overflow if the sum gets large
  int validCount = 0;

  for (int i = 0; i < numMeasurements; i++) {
    uint16_t distance = sensor.readRangeContinuousMillimeters();
   
    if (distance < 4000) {  // Check if the distance is valid (assuming valid distances are < 4000 mm)
      sum += distance;
      validCount++;
      Serial.print("VM: ");
      Serial.print(validCount);
      Serial.print(" ");
      Serial.println(distance);
    } else {
      // Handle invalid measurements if necessary
      Serial.print("VM: ");
      Serial.print(validCount);
      Serial.print(" Invalid measurement: ");
      Serial.println(distance);
    }
   
    delay(50); // Small delay between measurements
  }

  Serial.print("SUM: ");
  Serial.println(sum);
  Serial.print("Valid measurements: ");
  Serial.println(validCount);

  if (validCount > 0) {
    return sum / validCount;  // Return the average of valid measurements
  } else {
    return 0;  // Return 0 or handle this case appropriately if no valid measurements were recorded
  }
}

float calculateOffset() {
  float sum = 0;
  for (int i = 0; i < numCalPoints; i++) {
    sum += (actualDistances[i] - measuredDistances[i]);
  }
  return sum / numCalPoints;
}

float calculateScale() {
  float sum = 0;
  for (int i = 0; i < numCalPoints; i++) {
    sum += (actualDistances[i] / measuredDistances[i]);
  }
  return sum / numCalPoints;
}
bool confirmDistance(uint16_t actual, uint16_t measured) {
  Serial.print("Measured distance is ");
  Serial.print(measured);
  Serial.print(" mm. Is this correct for ");
  Serial.print(actual);
  Serial.println(" mm? (y/n)");

  while (true) {
    if (Serial.available() > 0) {
      char response = Serial.read();
      if (response == 'y' || response == 'Y') {
        return true;
      } else if (response == 'n' || response == 'N') {
        return false;
      }
    }
    delay(100); // Wait for user input
  }
}

Sunday, June 2, 2024

Pinky's Lighthouse

 After getting communication set up with the Raspberry Pi controller in this post, we are off to send and interpret sensor data with ROS2.

Cartographer is a good starting point. In my ROS2 docker container:
sudo apt install ros-$ROS_DISTRO-cartographer

It installs, but it looks like CARTOGRAPHER IS NOT SUPPORTED ON IRON...    I don't know why this package is available...

Moving on to nav2.

From inside the container, I can't launch GUIs. My working container must be committed to create a new image and re-run with the following:

sudo docker run -it \

    --privileged \

    -p 8888:8888/udp \

    --name c5cont \

    --env DISPLAY=$DISPLAY \

    --env QT_X11_NO_MITSHM=1 \

    --volume /tmp/.X11-unix:/tmp/.X11-unix \

    --cpus="3" \

    c5 /bin/bash

Then create a run file that has the following:
#/bin/bash
sudo docker start <container name>
xhost +local:docker
sudo docker exec -it <container name> /bin/bash

Also look into creating a dockerfile:

I'll have to reformat the data sent from the lighthouse IMU and laser distance sensor to mimic a lidar array.