Lidar sorta working but not as good as laser

I'm building a car counter with ability (I hope) of determining not only when a vehicle goes by on the highway but also what type of vehicle it is, using the transit time at the posted speed limit. E.g. a motorcycle is short so it takes less time to pass in front of my detector than a pickup truck. I have 5 vehicles defined.
I had started with an US sensor from Maxbotix, but found it had too much overhead because of noisy signals that needed averaging. I then used a simple laser with a laser-detector-relay that WORKED extremely well, except for the fact that it would require a reflector on far side of the highway, and alignment was really difficult.

So I decided to us a TFMini LIDAR with range of 36+ feet, which should get me to the far lane of traffic. The laser was a simple Digital Write to turn it on and wait for the sensor (laser relay) to trigger an interrupt. Worked flawlessly as long as I could get the alignment right- in my shop that is...I never tried it for real.

The LIDAR uses Software Serial (on a Nano) but the results I get are not discrete. The basic idea is that a vehicle blocks the LIDAR, which starts a timer which is shut off when the LIDAR is unblocked, creating a "transit time". From that time, the program determines what vehicle it was, and lights an LED. It also puts a count value in the proper spot on a LCD. (I'm also using the serial monitor for testing). I've been at it a week trying to figure out why it doesn't work. What happens is that I get MORE than the one reading. If I do a short block (for motorcycle), I'll get that LED plus one or two or even three of the others. The only one that works consistently is the bus, where I block the LIDAR for 3-4 seconds. I have photos and screen print to show what happens when I make one simple block of the LIDAR (and get 3-4 responses).

/* Name: TFMiniLidarCarCounterV15
By:
Start Date: 22Oct2021
Last Rev: 08Nov2021
MCU: Arduino Nano
Hardware: UNO, DFRobot Sen0366 Lidar
Description: Lidar sensor range to 30M (outdoor,for vehicle detection
This program was named MaxBotixVehicle_Detection_32FE.

Vers History:
1.0 Start modifications Removed all references to ultrasonic sensor
    LIDAR shows distance, counts based on conditional statement.
1.1 Mods to allow clean counts, using state machine and vehicle timer
1.2 This version worked with TF03 Lidar which I think I've since destroyed
1.3 Changed name to TFMiniLidarCarCounterV13
1.5 Using different LIDAR TF-Mini
    Added command to test all 5 LEDs
    Changed serial pins from 2,3 to 4,5 so could use interrupts on 2,3.

Note: from previous versions using laser and Maxbotix US sensor:
Arduino Forum user BlackFin removed my interrupt code and recoded much of this
sketch, using "typedef struct", millis polling, and created the LED State Machine
function.

Transit times based on average length of vehicle shown in table,
at 3 speed limits.  Very crude determination because speed is assumed,
not necessarily followed and vehicle lengths are averages.  Speed not
measured (another project).

                       11.1M/sec     13.9M/sec    16.7M/sec
                         40kph         50kph        60kph

Vehicle length Transit time Transit time Transit time
meters sec sec sec
motorcycle 2.5 0.23 0.18 0.013 / Harley 2.2 Meters
car 4.5 0.41 0.32 0.024 / Ford Mustang 4.8 M
SUV 5.2 0.47 0.37 0.028 / Ford Escape 4.4 M
Pickup 6.6 0.59 0.47 0.036 / F150 6.2 M
Bus, semi 14 1.26 1.01 0.076 / 72 pass bus 10 M
*/
#include <SoftwareSerial.h>
SoftwareSerial mySerial(4, 5); //Define software serial, 5 is TX, 4 is RX. Need 2,3 for interrupts
char buff[4] = {0x80, 0x06, 0x03, 0x77};//continuous measurement - default
unsigned char data[11] = {0};
char Waiting [20] = "Waiting for Vehicle";
char Detected [20] = " Vehicle Detected ";
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TFMini.h>
#define NUM_VEH_TYPES 5
#define KLED_OFF HIGH
#define KLED_ON LOW
#define Trigger 100 //trigger is 100 cm for testing
#define DEBUG_PRINTS 1 //set to 1 to print results to monitor
const byte LEDCycle = 8; //motorcycle LED
const byte LEDCar = 9; //car
const byte LEDSUV = 10; //SUV
const byte LEDPickup = 11; //pickup
const byte LEDBus = 12; //bus
//uint16_t distance;
uint16_t lastDist;
uint16_t duration;
int count = 0;
LiquidCrystal_I2C lcd(0x27, 20, 4);
TFMini tfmini;
uint16_t distance;
/Vehicle transit times array/
unsigned long grTimes[NUM_VEH_TYPES][2] =//these are the expected/estimated transit times
{ {180, 280},//motorcycle between 0 and 300 mS - chart above shows nominal 230 mS
{360, 460},//car 410mS added +/- 50mS to each parameter with center at nominal value
{420, 520},//SUV 470mS
{540, 640},//pickup 590 mS
{1200, 1310} //bus or transport truck 1260mS
};
/Typedef Structure
/
typedef struct structVehicleLEDs
{ byte pinNo;
bool bActive;
char *pszVehicleType;// * is a pointer to data. Represents 5 different vehicle types
int count;
int timeLEDStart;
int timeLEDHold;

} sVehicleLEDs; //this parameter is equal to 5 values above, like an alias was sVehicleLEDs
//this section-struct- determines what LEDs are lit
sVehicleLEDs VehicleLEDs[NUM_VEH_TYPES] =
{
{ .pinNo = LEDCycle,
.bActive = false,
.pszVehicleType = "Cyc ",
.count = 0,
.timeLEDStart = 0,
.timeLEDHold = 500ul //time to keep LED on so I can see it (1/2 second)
},
{ .pinNo = LEDCar,
.bActive = false,
.pszVehicleType = "Car ",
.count = 0,
.timeLEDStart = 0,
.timeLEDHold = 500ul
},
{ .pinNo = LEDSUV,
.bActive = false,
.pszVehicleType = "SUV ",
.count = 0,
.timeLEDStart = 0,
.timeLEDHold = 500ul
},
{ .pinNo = LEDPickup,
.bActive = false,
.pszVehicleType = "Pup ",
.count = 0,
.timeLEDStart = 0,
.timeLEDHold = 500ul
},
{ .pinNo = LEDBus,
.bActive = false,
.pszVehicleType = "Bus ",
.count = 0,
.timeLEDStart = 0,
.timeLEDHold = 500ul
}
};
//Setup Function**********/
void setup()
{ Serial.begin(115200);
while (!Serial);
mySerial.begin(115200);
Wire.begin(); // start I2C
lcd.begin(20, 4);
tfmini.begin(&mySerial);
for ( byte x = 0; x < 5; x++ )// x is index, NOT pin number
{ pinMode( VehicleLEDs[x].pinNo, OUTPUT );//Set all vehicle LED pins(in array) to Output
digitalWrite( VehicleLEDs[x].pinNo, KLED_ON ); //ON for startup self-test
delay(500);
digitalWrite( VehicleLEDs[x].pinNo, KLED_OFF );//OFF until required
}
//distance = tfmini.getDistance();
lastDist = distance;

lcd.backlight();
lcd.setCursor(0, 0);//Display constant names on LCD
lcd.print("Cyc");
lcd.setCursor (0, 1);
lcd.print("Car");
lcd.setCursor (0, 2);
lcd.print("SUV");
lcd.setCursor(0, 3);
lcd.print("Pup");
lcd.setCursor(9, 0);
lcd.print("Bus");
}
//Loop Function*****/
void loop() {
distance = 0;//need or it doesn't work!
while (!distance) {
distance = tfmini.getDistance();
delay(100);}
VehicleTimer(distance);
LED_StateMachine();
}
//****Vehicle Timer Function/
void VehicleTimer(uint16_t distance)
{ static unsigned long timeStart;
unsigned long timeVehicle;
uint16_t NewDist = distance;
if ( NewDist != lastDist ) //has distance changed, ie sensor blocked or unblocked?
{ lastDist = NewDist;//no change, go to END and back to LOOP to start again
if ( NewDist <= Trigger ) //is laser blocked?
{
timeStart = millis(); // yes, start timing
}
else//No, laser no longer blocked, so stop the timer and...
{ timeVehicle = millis() - timeStart;//get the time it was blocked
for ( int i = 0; i < NUM_VEH_TYPES; i++ ) //for each of the 5 vehicle types
{ if ( timeVehicle >= grTimes[i][0] && timeVehicle < grTimes[i][1] )//if time is between these two entries
{ digitalWrite( VehicleLEDs[i].pinNo, KLED_ON );//turn on respective LED
(VehicleLEDs[i].bActive = true); //activate vehicle LED flag
VehicleLEDs[i].count++;//increment individual count
delay(100);//testing
VehicleLEDs[i].timeLEDStart = millis();//start timing of delay to keep LED on

#ifdef DEBUG_PRINTS
printResult(VehicleLEDs[i].pszVehicleType, VehicleLEDs[i].count);
#endif
}//if
}//for
}//else
}//if
}//End VehicleTimer

//LED_StateMachine Function********
void LED_StateMachine(void)//sets LED hold time
{ static byte idx = 0;
if ( VehicleLEDs[idx].bActive )//if bActive is true, do this:
{ if ( (millis() - VehicleLEDs[idx].timeLEDStart) >= VehicleLEDs[idx].timeLEDHold )
{ digitalWrite( VehicleLEDs[idx].pinNo, KLED_OFF );//hold time of 500 mS is over, shut off LED
VehicleLEDs[idx].bActive = false;//Reset, turn off LED
}//if
}//if
idx++;//get next index value
if ( idx == NUM_VEH_TYPES ) //if all 5 cycled through,
idx = 0;//start over
}//End LED_StateMachine
//printResult Function*****
void printResult(char *szVehicle, int count )
{ char choice;
byte row;
byte col;
Serial.print(szVehicle);
Serial.print (" count: ");
Serial.println (count);
choice = szVehicle[2];
switch (choice)
{ case 'c': row = 0, col = 6; break;
case 'r': row = 1, col = 6; break;
case 'V': row = 2, col = 6; break;
case 'p': row = 3, col = 6; break;
case 's': row = 0, col = 14; break;//MUST be lower case s, not B
}
lcd.setCursor (col, row);
lcd.print(count);
}
`

QD, I think I've seen you around the forum before. You remember the thing about code tags?

And whiles adding code tags to the OP. What is the signal strength of the multi reads? Perhaps the signal strength can be used to differentiate return pulses.

Please edit your post to add code tags, so it looks like this:

mySerial.begin(115200);

Software serial is totally unreliable at 115200 Baud, if it works at all.

I thought I did... That's the </> icon that comes up as "preformatted text". That's what I used. There's nothing there that says "code tags."

Thanks, that was on my list of things to try next, reduce the baud rate, despite what Benewake says.

I did an auto-format from the IDE. Is this better?


/*  Name: TFMiniLidarCarCounterV16
   
 /*   Transit times based on average length of vehicle shown in table,
    at 3 speed limits.  Very crude determination because speed is assumed,
    not necessarily followed and vehicle lengths are averages.  Speed not
    measured (another project).

                           11.1M/sec     13.9M/sec    16.7M/sec
                             40kph         50kph        60kph
  Vehicle       length    Transit time  Transit time  Transit time
                meters        sec            sec          sec
  motorcycle     2.5         0.23           0.18         0.013  / Harley        2.2 Meters
  car            4.5         0.41           0.32         0.024  / Ford Mustang  4.8 M
  SUV            5.2         0.47           0.37         0.028  / Ford Escape   4.4 M
  Pickup         6.6         0.59           0.47         0.036  / F150          6.2 M
  Bus, semi      14          1.26           1.01         0.076  / 72 pass bus   10 M
*/
#include <SoftwareSerial.h>
SoftwareSerial mySerial(4, 5); //Define software serial, 5 is TX, 4 is RX.  Need 2,3 for interrupts
char buff[4] = {0x80, 0x06, 0x03, 0x77};//continuous measurement - default
unsigned char data[11] = {0};
char Waiting [20] = "Waiting for Vehicle";
char Detected [20] = " Vehicle Detected  ";
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <TFMini.h>
#define NUM_VEH_TYPES        5
#define KLED_OFF             HIGH
#define KLED_ON              LOW
#define Trigger              100 //trigger is 100 cm for testing
#define DEBUG_PRINTS         1 //set to 1 to print results to monitor
const byte LEDCycle =    8;  //motorcycle LED
const byte LEDCar =      9;  //car
const byte LEDSUV =     10;  //SUV
const byte LEDPickup =  11;  //pickup
const byte LEDBus =     12;  //bus
uint16_t lastDist;
uint16_t duration;
uint16_t distance;
int count = 0;
LiquidCrystal_I2C lcd(0x27, 20, 4);
TFMini tfmini;

/***********************Vehicle transit times array******************/
unsigned long grTimes[NUM_VEH_TYPES][2] =//these are the expected/estimated transit times
{ {200, 300},  //motorcycle  between 0 and 300 mS - chart above shows nominal 230 mS
  {310, 450},  //car 410mS  10mS gap between types
  {460, 520},  //SUV 470mS
  {530, 680},  //pickup 590 mS
  {700, 1500} //bus or transport truck 1260mS
};
/***********************Typedef Structure****************************/
typedef struct   structVehicleLEDs
{ byte            pinNo;
  bool            bActive;
  char            *pszVehicleType;// * is a pointer to data. Represents 5 different vehicle types
  int             count;
  int             timeLEDStart;
  int             timeLEDHold;

} sVehicleLEDs; //this parameter is equal to 5 values above, like an alias
//this section-struct- determines what LEDs are lit
sVehicleLEDs VehicleLEDs[NUM_VEH_TYPES] =
{
  { .pinNo = LEDCycle,
    .bActive = false,
    .pszVehicleType = "Cyc  ",
    .count = 0,
    .timeLEDStart = 0,
    .timeLEDHold = 500ul  //time to keep LED on so I can see it (1/2 second)
  },
  { .pinNo = LEDCar,
    .bActive = false,
    .pszVehicleType = "Car  ",
    .count = 0,
    .timeLEDStart = 0,
    .timeLEDHold = 500ul
  },
  { .pinNo = LEDSUV,
    .bActive = false,
    .pszVehicleType = "SUV  ",
    .count = 0,
    .timeLEDStart = 0,
    .timeLEDHold = 500ul
  },
  { .pinNo = LEDPickup,
    .bActive = false,
    .pszVehicleType = "Pup  ",
    .count = 0,
    .timeLEDStart = 0,
    .timeLEDHold = 500ul
  },
  { .pinNo = LEDBus,
    .bActive = false,
    .pszVehicleType = "Bus  ",
    .count = 0,
    .timeLEDStart = 0,
    .timeLEDHold = 500ul
  }
};
//*******************Setup Function*****************************/
void setup()
{ Serial.begin(115200);
  while (!Serial);
  mySerial.begin(115200);
  Wire.begin(); // start I2C
  lcd.begin(20, 4);
  tfmini.begin(&mySerial);
  for ( byte x = 0; x < 5; x++ )// x is index, NOT pin number
  { pinMode( VehicleLEDs[x].pinNo, OUTPUT );//Set all vehicle LED pins(in array) to Output
    digitalWrite( VehicleLEDs[x].pinNo, KLED_ON ); //ON for startup self-test
    delay(500);//half second "ON" time
    digitalWrite( VehicleLEDs[x].pinNo, KLED_OFF );//OFF until required
  }
  //lastDist = tfmini.getDistance();//no difference if I use this instead of next
  lastDist = distance;

  lcd.backlight();
  lcd.setCursor(0, 0);//Display constant names on LCD
  lcd.print("Cyc");
  lcd.setCursor (0, 1);
  lcd.print("Car");
  lcd.setCursor (0, 2);
  lcd.print("SUV");
  lcd.setCursor(0, 3);
  lcd.print("Pup");
  lcd.setCursor(9, 0);
  lcd.print("Bus");
}
//**********************Loop Function***************************/
void loop() {
  distance = 0;//need or it doesn't work!
  while (!distance) {
    distance = tfmini.getDistance();
    delay(100);
  }
  VehicleTimer(distance);
  LED_StateMachine();
}
//**********************Vehicle Timer Function******************/
void VehicleTimer(uint16_t distance)
{ static unsigned long timeStart;
  unsigned long timeVehicle;
  uint16_t NewDist = distance;
  if ( NewDist != lastDist ) //has distance changed, ie sensor blocked or unblocked?
  { lastDist = NewDist;
    if ( NewDist >= Trigger ) //is laser blocked?  Is distance >=100 cm? makes no sense
    { timeStart = millis(); // yes, start timing
    }
    else//No, laser no longer blocked, so stop the timer and...
    { timeVehicle = millis() - timeStart;//get the time it was blocked
      for ( int i = 0; i < NUM_VEH_TYPES; i++ )
      { if ( timeVehicle >= grTimes[i][0] && timeVehicle < grTimes[i][1] )//if time is between the two entries
        { digitalWrite( VehicleLEDs[i].pinNo, KLED_ON );//turn on respective LED
          (VehicleLEDs[i].bActive = true); //activate vehicle LED flag
          VehicleLEDs[i].count++;//increment individual count
          VehicleLEDs[i].timeLEDStart = millis();//start timing of delay to keep LED on

#ifdef DEBUG_PRINTS
          printResult(VehicleLEDs[i].pszVehicleType, VehicleLEDs[i].count, timeVehicle);
#endif
        }//if
      }//for
    }//else
  }//if
}//End VehicleTimer

//****************LED_StateMachine Function************************
void LED_StateMachine(void)//sets LED hold time
{ static byte idx = 0;
  if ( VehicleLEDs[idx].bActive )//if bActive is true, do this:
  { if ( (millis() - VehicleLEDs[idx].timeLEDStart) >= VehicleLEDs[idx].timeLEDHold )
    { digitalWrite( VehicleLEDs[idx].pinNo, KLED_OFF );//hold time of 500 mS is over, shut off LED
      VehicleLEDs[idx].bActive = false;//Reset, turn off LED
    }//if
  }//if
  idx++;//get next index value
  if ( idx == NUM_VEH_TYPES ) //if all 5 cycled through,
    idx = 0;//start over
}//End LED_StateMachine
//***************printResult Function********************
void printResult(char *szVehicle, int count, unsigned long timeVehicle )
{ char choice;
  byte row;
  byte col;
  Serial.print("Transit Time= ");
  Serial.print(timeVehicle);
  Serial.print("   ");
  Serial.print(szVehicle);
  Serial.print ("count: ");
  Serial.println (count);

  choice = szVehicle[2];
  switch (choice)
  { case 'c': row = 0, col = 6; break;
    case 'r': row = 1, col = 6; break;
    case 'V': row = 2, col = 6; break;
    case 'p': row = 3, col = 6; break;
    case 's': row = 0, col = 14; break;//MUST be lower case s, not B
  }
  lcd.setCursor (col, row);
  lcd.print(count);
}

Much better, thanks!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.