Porting advanced People counter code to "fit" arduino (VL53L1X)

I have wanted to build a people counter sensor for a while now an after seeing People counter demo on youtube I ordered a couple of VL53L1X sensors.

I managed to find the code that ST Microelectronics uses in the demo but it is created for some other kind of board that ST sells. Here is a git repo which should get access to all of the sensors functions as I understand github repo. I am a novice so I gave up after just looking at the original code.

But how difficult would it be for someone that is a advanced arduino user to understand and convert the code to be able to use in the same way on an arduino. If necessary I could pay that person to have the code converted as I really want that function when trying to create my own "smart" home.

The code

See this for all your questions

Thanks for the answer, but it is a different sensor. Also the ST-code uses more advanced functions like zones if I understand correctly. I have not seen that in any arduino code.

but it is a different sensor.

No same sensor but this is already mounted on a board.

anyway lots of other links when you google.

The GitHub link you posted does have Arduino code anyway. Just see vl53l1x-st-api-arduino/vl53l1x-st-api/vl53l1x-st-api.ino at master · pololu/vl53l1x-st-api-arduino · GitHub

Also the ST-code uses more advanced functions like zones if I understand correctly

I don’t think you have. You get a single number from the sensor. The zones they talk of in the video are simply ranges of distance. The distance will be smaller as you are under the sensor, if the distance is less than the normal floor reading but greater than the directly under reading it is considered to be an outer zone. It is a bit of a trick as that demo would not work with children nor with people below a specific height.

If you take continuous readings from the sensor and plot them to the serial plotter then you should see a curve and it might be easer to spot where you have to set your height thresholds.

To count as a person in, you will get an outer zone followed by an inner zone and then followed by an outer zone. If you get this and immediately get an inner zone distance followed by an outer zone then this is counted as a partial entry.

Ok, but I still think that my theory about the zones is a little more complicated. I found some more code from sparkfun where you can specify different Areas (Regions of Interest):

void setROI(uint16_t x, uint16_t y); --- Set the height and width of the ROI in SPADs, lowest possible option is 4. ROI is always centered.
uint16_t getROIX(); --- Returns the width of the ROI in SPADs
uint16_t getROIY(); --- Returns the height of the ROI in SPADs

as the sensor is using a 16x16 arrray of detectors. But I don't understand how to specify a custom region when you only get a single x and y.

In the official documentation from ST they use:

VL53L1_UserRoi_t roiConfig;
roiConfig.TopLeftX = 9;
roiConfig.TopLeftY = 13;
roiConfig.BotRightX = 14;
roiConfig.BotRightY = 10;
status = VL53L1_SetUserROI(&VL53L1Dev, &roiConfig);

Which makes more sense

If necessary I could pay that person to have the code converted

Consider posting on the Gigs and Collaborations forum section.

My reading of the people counter code suggests that there are both ranging zones and regions of interest (in the field of view) zones. If the range is below a certain distance, a person is detected. Then, there is a state change algorithm that decides whether someone has entered or is in the left or right regions of interest, and is moving from one region to the other or exiting either. That is all generic C/C++, so the main task would be to convert any processor specific code to Arduino.

This is the heart of the algorithm and looks to be pretty well designed. It should compile and run on Arduino with no problems.

int ProcessPeopleCountingData(int16_t Distance, uint8_t zone) {

	static char PeopleCountString[4];
    static int PathTrack[] = {0,0,0,0};
    static int PathTrackFillingSize = 1; // init this to 1 as we start from state where nobody is any of the zones
    static int LeftPreviousStatus = NOBODY;
    static int RightPreviousStatus = NOBODY;
    static int PeopleCount = 0;

    int CurrentZoneStatus = NOBODY;
    int AllZonesCurrentStatus = 0;
    int AnEventHasOccured = 0;

	if (Distance < DIST_THRESHOLD_MAX) {
		// Someone is in !
		CurrentZoneStatus = SOMEONE;
	}

	// left zone
	if (zone == LEFT) {

		if (CurrentZoneStatus != LeftPreviousStatus) {
			// event in left zone has occured
			AnEventHasOccured = 1;

			if (CurrentZoneStatus == SOMEONE) {
				AllZonesCurrentStatus += 1;
			}
			// need to check right zone as well ...
			if (RightPreviousStatus == SOMEONE) {
				// event in left zone has occured
				AllZonesCurrentStatus += 2;
			}
			// remember for next time
			LeftPreviousStatus = CurrentZoneStatus;
		}
	}
	// right zone
	else {

		if (CurrentZoneStatus != RightPreviousStatus) {

			// event in left zone has occured
			AnEventHasOccured = 1;
			if (CurrentZoneStatus == SOMEONE) {
				AllZonesCurrentStatus += 2;
			}
			// need to left right zone as well ...
			if (LeftPreviousStatus == SOMEONE) {
				// event in left zone has occured
				AllZonesCurrentStatus += 1;
			}
			// remember for next time
			RightPreviousStatus = CurrentZoneStatus;
		}
	}

	// if an event has occured
	if (AnEventHasOccured) {
		if (PathTrackFillingSize < 4) {
			PathTrackFillingSize ++;
		}

		// if nobody anywhere lets check if an exit or entry has happened
		if ((LeftPreviousStatus == NOBODY) && (RightPreviousStatus == NOBODY)) {

			// check exit or entry only if PathTrackFillingSize is 4 (for example 0 1 3 2) and last event is 0 (nobobdy anywhere)
			if (PathTrackFillingSize == 4) {
				// check exit or entry. no need to check PathTrack[0] == 0 , it is always the case

				if ((PathTrack[1] == 1)  && (PathTrack[2] == 3) && (PathTrack[3] == 2)) {
					// This an entry
					PeopleCount ++;
                    sprintf(PeopleCountString, "%d", PeopleCount);
                    XNUCLEO53L1A1_SetDisplayString(PeopleCountString);

				} else if ((PathTrack[1] == 2)  && (PathTrack[2] == 3) && (PathTrack[3] == 1)) {
					// This an exit
					PeopleCount --;
                    sprintf(PeopleCountString, "%d", PeopleCount);
                    XNUCLEO53L1A1_SetDisplayString(PeopleCountString);
				}
			}

			PathTrackFillingSize = 1;
		}
		else {
			// update PathTrack
			// example of PathTrack update
			// 0
			// 0 1
			// 0 1 3
			// 0 1 3 1
			// 0 1 3 3
			// 0 1 3 2 ==> if next is 0 : check if exit
			PathTrack[PathTrackFillingSize-1] = AllZonesCurrentStatus;
		}
	}

	// output debug data to main host machine
	return(PeopleCount);

}

Hi, i was able to convert the algorithm from STM into an ESP8266 board.

It works, but sometime in home environment is not reliable, and tends to count multiple passes.

I don't know if it's mounting problem, hardware limitation or algorithm fail.

Anyone would help me to improve this project?

I have found the research document from st.

it explains that they alternate te field of view, and the meassurements.

I have no idea how to start coding this. I'm only searching for confirmation that it's possible.

Hi,

I just found this thread while looking for code tot get the poeple counting mechanism they use in the youtube video linked to by the topic author.

After some more googling i found this document from ST about using multiple ranges and just how it works from an API and device point of view. You might find it interesting: https://www.st.com/content/ccc/resource/technical/document/user_manual/group1/fc/c3/0b/8c/0c/da/4c/8d/DM00600212/files/DM00600212.pdf/jcr:content/translations/en.DM00600212.pdf

That's not the paper that explains the actual people counting. That one is linked by the comment above me from @JoerideMan.

Thus far, i haven't been able to find code to use in the ESP8266 for this but i assume it's not "that" difficult to get it working on there anymore as all code is just there (CodePile | Easily Share Piles of Code).

hi,

Got the people detection using VL53L1X working using the codes found from above, and install on the top of the door frame (aiming downward).

But not very accurate...sometimes not able to detect people in 1 direction... .at the end, the in and out do not match (if it works well, then the out = in).

Anyone got any idea what needs to be tweaked to get it to work?

ellegix:
Hi, i was able to convert the algorithm from STM into an ESP8266 board.

It works, but sometime in home environment is not reliable, and tends to count multiple passes.

I don't know if it's mounting problem, hardware limitation or algorithm fail.

Anyone would help me to improve this project?

Hi, could you share they code please? I'm trying to implement a people counter using this same sensor.

I too would like to see this implemented reliably, having just seen ST's suspiciously impressive youtube demo. Ideally as a ESPHome component.

A state of play, with some possible hints for improving accuracy...

st.com - VL53L1X - Data Sheet

  • "2.5.2 Timing budget (TB)" - specs 33ms minimum timing budget for all distance modes. I note their example code sets 20ms with 'long' sensing?
  • "2.8 Sensing array optical center" - stored calibration offsets may be worth adjusting for.

st.com - VL53L1X API user manual (Rev 2)

  • "2.2 Timing considerations" - "The minimum inter-measurement period must be longer than the timing budget + 4 ms." This is not in the example code, or in Rev 1.
  • "2.5.3 Distance mode" - 'short' mode ("Up to 1.3m" in dark) has "Better ambient immunity"

st.com - Counting people with the VL53L1X long-distance ranging Time-of-Flight sensor - User manual (bundle)

  • Section 7, "Setup reliability", details ways to improve their source code example, namely setting a minimum distance, and overlapping detection zones.

These are all part of ST's general bundle for the chip.

SparkFun intro/tutorial and https://github.com/sparkfun/SparkFun_VL53L1X_Arduino_Library/.../Example1_ReadDistance.ino
Adafruit have also written a library with a demo of multiple sensors.

github.com/tigoe/SensorExamples/.../VL53L1X_GetDirection.ino

  • A implementation of ST's ProcessPeopleCountingData() using SparkFun_VL53L1X library.
  • A an "unreliable" attempt at refactoring ST's example.
  • Also just the SPAD VL53L1X_SetROI.ino
  • Also just the calibration VL53L1X_OffsetCalibration.ino

Other duplicates(?) of the official ST example:

Non-ROI so non directional but nice complete people counter: github.com/Lyr3x/Roode (intro)

Threshold-based interrupts probably better than continuous distance reading for low power home automation.

There is also a ST VL53L3CX, which does some kind of processing of the histogram, returning an array of distances for "multi-target detection" (VL53LX_GetMultiRangingData(Dev, pMultiRangingData)). Different purpose?

Very useful links Cretep thank you. I have implemented the code from your link Here. I have found however, that the code is unreliable in detecting direction. Left -> Right movement is sometimes recorded as Right -> Left and vice versa. I was able to make it slightly more reliable by reducing SPAD array zone size, however it still fails ~25% of the time.

Edit: I managed to get much better results with these ROI(Region of Interest)/zone settings: Width=8, Height=8, opticalCentre[] = {167, 223}

Why not post the code?

I did? Its from the same link Cretep already posted above.