Lidar 3D Scanner

This was my final project for my Intro to Photonics class. It’s a lidar 3D scanner, based off an Arduino, a TFT display, a servo, and laser distance sensor. The servo scans through the room recording spherical coordinates, I then convert those measurements to cylindrical, and “unfurl it”. This is by taking into account the distance along the cylinder formed at that radius. Every projection method has its flaws but this is the best I figured out. The dots are colored by distance. In the photo above you can see the silhouette of a person who was recorded by the system.

#include <Elegoo_TFTLCD.h> 
#include <Elegoo_GFX.h>    
#include <TouchScreen.h> //TFT

#include <Wire.h>
#include <VL53L1X.h> //LIDAR

#include <Servo.h> //Servo

#include <EasyColor.h>

//libraries ^^^

#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF
#define GRAY    0x8410
#define ORANGE  0xFD00

//base colors ^^^

#define TS_MINX 120
#define TS_MINY 80
#define TS_MAXX 920
#define TS_MAXY 905

#define XP 8
#define YP A3
#define XM A2
#define YM 9

Elegoo_TFTLCD tft(A3, A2, A1, A0, A4);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 364);
//touchscreen stuff ^^^

float countRate = 0.0; 
int rangeStatus = 0;
unsigned long StartT;
unsigned long currentT; //vars

VL53L1X sensor;
Servo servo1;
Servo servo2; //initalize

int servo_1_PIN = 44; // Horizontal Rotation
int servo_2_PIN = 45; // Vertical Rotation

int yawdiv = 2;
int pitchdiv = 2;

EasyColor::HSVRGB HSVConverter;





void setup() {
  Serial.begin(9600); //starts serial
  
  Wire.begin();
  Wire.setClock(400000); //starts wire

  servo1.attach(servo_1_PIN);
  servo2.attach(servo_2_PIN);

  sensor.setTimeout(500);
  if (!sensor.init()) {
    Serial.println("Failed to detect and initialize sensor!");
    while (1);
  }
  sensor.setDistanceMode(VL53L1X::Long);
  sensor.setMeasurementTimingBudget(50000);
  sensor.startContinuous(50); //stars LIDAR
  StartT = millis();  //count in milliseconds

  tft.reset();
  tft.begin(0x9341);
  tft.setRotation(3);
  
  UI();
  
} //end up setup





void loop() {

  //wait for button to be pressed to take an image

  for (int yaw = -45; yaw <= 45; yaw = yaw + yawdiv) {
    
    for (int pitch = 0; pitch <= 60; pitch = pitch + pitchdiv) {
      servo1.write(90 + yaw); //horizontal
      servo2.write(180 - pitch); //vertical
      delay(50); //wait for servo to respond
      
      float LIDARdst = getLIDAR(); //gets lidar distance

      if (LIDARdst == 0) { //skips 0 values
        continue;
      }
  
      //float metersx = LIDARdst * sin(degtorad(90 - pitch)) * cos(degtorad(yaw)); //depth
      //float metersy = LIDARdst * sin(degtorad(90 - pitch)) * sin(degtorad(yaw)); //side
      //float metersz = LIDARdst * cos(degtorad(90 - pitch)); //up
      //spherical to cartesian

      //float metersradius = LIDARdst * sin(degtorad(90 - pitch));
      //float theta = yaw;
      //float height = LIDARdst * cos(degtorad(90 - pitch));
      //spherical to cylinderical

      float metersx = LIDARdst * sin(degtorad(90 - pitch)); //depth
      float metersz = LIDARdst * cos(degtorad(90 - pitch)); //up
      float metersy = LIDARdst * sin(degtorad(90 - pitch)) * degtorad(yaw); //side
      //spherical to plane (map-projection)
      

//      Serial.print("  Yaw: ");
//      Serial.print(yaw);
//      Serial.print("  Pitch: ");
//      Serial.print(pitch);
//      Serial.print("  Distance: ");
//      Serial.print(LIDARdst, 3);
//      Serial.print("  metersx: ");
//      Serial.print(metersx, 3);
//      Serial.print("  metersy: ");
//      Serial.print(metersy, 3);
//      Serial.print("  metersz: ");
//      Serial.println(metersz, 3); //debug

      displayIMG(metersx, metersy, metersz); //plots on display
    }
    
  }

  exit(0);

} //end of loop



//Functions woooooo \/\/\/\/\/\/\/\/\/\/

int flip(int x) {
  int flipped = 320 - x;
  return flipped;
}

void UI() {
  tft.fillScreen(BLACK); //initalizes tft display
  tft.drawRect(40, 0, 240, 240, WHITE); //image box
  
  tft.drawRect(295, 41, 20, 160, WHITE); //color box
  
  for (int i = 41; i <= 200; i = i + 40) {
    tft.drawLine(295, i, 288, i, WHITE);
  }
  tft.drawLine(295, 200, 288, 200, WHITE); //big graduations

  for (int i = 41; i <= 200; i = i + 20) {
    tft.drawLine(295, i, 291, i, WHITE);
  }

  for (int i = 41; i <= 200; i = i + 10) {
    tft.drawLine(295, i, 293, i, WHITE);
  }

  tft.drawRect(10, 10, 3, 3, RED);
  tft.drawRect(20, 10, 3, 3, BLUE);
  tft.drawRect(10, 20, 3, 3, GREEN);

  tft.setCursor(300, 205);
  tft.setTextColor(WHITE);  
  tft.setTextSize(1);
  tft.println("0m");

  tft.setCursor(300, 30);
  tft.setTextColor(WHITE);  
  tft.setTextSize(1);
  tft.println("4m");

  for (int i = 199; i >= 42; i--) {
    int colordepth = map(i, 199, 42, 0, 300);
    
    hsv in_hsv;
      in_hsv.h = colordepth;
      in_hsv.s = 100;
      in_hsv.v = 100;

    rgb out_rgb;
      out_rgb.r;
      out_rgb.g;
      out_rgb.b;
  
    out_rgb = HSVConverter.HSVtoRGB(in_hsv, out_rgb);

    tft.drawLine(296, i, 313, i, tft.color565(out_rgb.r, out_rgb.g, out_rgb.b));
    
  }
  
}

float degtorad(float degree) {
  float radian = degree * (PI/180);
  return radian;
}


float getLIDAR() {
  currentT = millis();
  sensor.read();
  int distanceMM = sensor.ranging_data.range_mm;
  //Serial.println(distanceMM);
  countRate = sensor.ranging_data.peak_signal_count_rate_MCPS;
  rangeStatus = sensor.ranging_data.range_status;
  float distanceM = distanceMM * 0.001;
  //Serial.println(distanceM, 6);
  
  if (VL53L1X::rangeStatusToString(rangeStatus)=="wrap target fail"){
    return 0;
  }
  
  else {
    return distanceM;
  }
  
} //end of getLIDAR


void displayIMG(float xloc, float yloc, float zloc) { //adds a pixel 320x240pixels orgin: (160, 120)
  float yPix = 160 + (60 * yloc);
  float zPix = 10 + (60 * zloc);
  int yPix_mapped = yPix;
  int zPix_mapped = zPix;

  Serial.print("yPix: ");
  Serial.print(yPix_mapped);
  Serial.print("  zPix: ");
  Serial.print(zPix_mapped); //debug
  
  //function for color aquistion
  float colordepth = (xloc - 0) * (300 - 0) / (4 - 0) + 0; //map = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
  int colordepth_int = colordepth;

  Serial.print("  xloc: ");
  Serial.print(xloc, 6); //debug
  Serial.print("  xPix: ");
  Serial.println(colordepth_int);
  
  hsv in_hsv;
    in_hsv.h = colordepth_int;
    in_hsv.s = 100;
    in_hsv.v = 100;

  rgb out_rgb;
    out_rgb.r;
    out_rgb.g;
    out_rgb.b;
  
  out_rgb = HSVConverter.HSVtoRGB(in_hsv, out_rgb);

  if (yPix_mapped >= 41 && yPix_mapped <= 279 && zPix_mapped >= 1 && zPix_mapped <= 239) {
    tft.drawPixel(yPix_mapped, 240 - zPix_mapped, tft.color565(out_rgb.r, out_rgb.g, out_rgb.b));
  }
  
  
  
} //end of displayIMG
C++