Skip to content
Plugins (190)

lidar

stable

2D lidar point cloud utilities for coordinate conversion, filtering, statistics, merging, downsampling, and generating simulated circular scans.

use plugin lidar
13 functions Game
Runs with the native Zolo host

Plugin snippets can be edited in the Playground, but native capabilities are executed locally with the Zolo CLI.

/ filter jk navigate Esc clear
Functions (13)
  1. polar_to_cartesian Convert polar coords to Cartesian {x, y}
  2. cartesian_to_polar Convert Cartesian coords to polar
  3. filter_by_distance Keep points within a distance range
  4. filter_by_angle Keep points within an angle range
  5. average_distance Compute mean distance of all points
  6. scan_stats Compute min, max, avg, centroid of a scan
  7. merge_scans Combine two point arrays into one
  8. downsample Keep every Nth point
  9. point_count Count points in a scan
  10. nearest_point Find the closest point to a position
  11. distance_between Euclidean distance between two Cartesian points
  12. sort_by_angle Sort points by angle ascending
  13. create_scan Generate a uniform circular scan

From import to result

  1. 01Import only what you use
  2. 02Copy the smallest example
  3. 03Run it with the Zolo CLI

Overview

lidar is a dependency-free toolkit for working with 2D lidar scans as plain point clouds. A scan is just an array of point tables, and every point is accepted in either polar form (angle_deg, distance) or Cartesian form (x, y) — the functions normalize between the two automatically, so you can mix sources without converting first. There are no opaque handles or sensor sessions: data flows in and out as ordinary tables you can store, slice, and pass around freely.

The mental model is a pipeline: build or capture a scan (create_scan), narrow it with the filter_* and downsample helpers, reshape it with merge_scans and sort_by_angle, then inspect it with scan_stats, average_distance, nearest_point, or the coordinate-conversion utilities. Distances are unitless — pick a unit (millimeters are typical) and stay consistent across every call.

Common patterns

Generate a scan, narrow it to a region of interest, and summarize it:

use plugin lidar::{create_scan, filter_by_angle, scan_stats}

let scan = create_scan(360, 1000.0)
let front = filter_by_angle(scan, -45.0, 45.0)
let stats = scan_stats(front)
print("front arc: {stats["count"]} points, avg {stats["avg_dist"]}")

Fuse two sensors, drop noise outside a usable distance band, and downsample:

use plugin lidar::{create_scan, merge_scans, filter_by_distance, downsample}

let left = create_scan(180, 800.0)
let right = create_scan(180, 1200.0)
let merged = merge_scans(left, right)
let clean = filter_by_distance(merged, 200.0, 2000.0)
let sparse = downsample(clean, 2)
print("usable points: {point_count(sparse)}")

Sort a scan by angle, then find the obstacle closest to the sensor origin:

use plugin lidar::{create_scan, sort_by_angle, nearest_point}

let scan = sort_by_angle(create_scan(360, 1000.0))
let closest = nearest_point(scan, 0.0, 0.0)
print("nearest obstacle at {closest["angle_deg"]} deg, {closest["distance"]} away")

Convert polar coords to Cartesian {x, y}

Converts polar coordinates to Cartesian. Returns a table with x and y fields. Angle is in degrees, distance is in the same unit used for all subsequent operations (typically mm).

use plugin lidar::{polar_to_cartesian}

let pt = polar_to_cartesian(45.0, 1000.0)
print("x={pt["x"]} y={pt["y"]}")

Project a single beam reading onto the X axis to test how far an obstacle sits directly ahead:

use plugin lidar::{polar_to_cartesian}

let ahead = polar_to_cartesian(0.0, 1500.0)
print("forward clearance: {ahead["x"]}")

Convert Cartesian coords to polar

Converts Cartesian coordinates to polar. Returns a table with angle_deg and distance fields.

use plugin lidar::{cartesian_to_polar}

let polar = cartesian_to_polar(707.0, 707.0)
print("angle={polar["angle_deg"]} dist={polar["distance"]}")

Keep points within a distance range

Returns only the points whose distance is in the inclusive range [min, max]. Input points can be in polar (angle_deg, distance) or Cartesian (x, y) format.

use plugin lidar::{create_scan, filter_by_distance}

let scan = create_scan(360, 1000.0)
let nearby = filter_by_distance(scan, 0.0, 500.0)
print("Nearby points: {nearby}")

Reject both too-close sensor noise and far-field clutter by bounding the band from both sides, then count what survives:

use plugin lidar::{create_scan, filter_by_distance, point_count}

let scan = create_scan(360, 1000.0)
let usable = filter_by_distance(scan, 200.0, 800.0)
print("in-band points: {point_count(usable)}")

Keep points within an angle range

Returns only the points whose angle is in the inclusive range [min_deg, max_deg].

use plugin lidar::{create_scan, filter_by_angle}

let scan = create_scan(360, 1000.0)
let front = filter_by_angle(scan, -45.0, 45.0)

Compute mean distance of all points

Computes the mean distance of all points in the scan. Returns 0.0 for an empty scan.

use plugin lidar::{create_scan, average_distance}

let scan = create_scan(360, 1000.0)
let avg = average_distance(scan)
print("Average distance: {avg}")

Compute min, max, avg, centroid of a scan

Computes statistics over a point cloud. Returns a table with min_dist, max_dist, avg_dist, count, centroid_x, and centroid_y.

use plugin lidar::{create_scan, scan_stats}

let scan = create_scan(360, 1000.0)
let stats = scan_stats(scan)
print("Min: {stats["min_dist"]}  Max: {stats["max_dist"]}  Count: {stats["count"]}")

Use the centroid to estimate where the surrounding geometry is biased relative to the sensor:

use plugin lidar::{create_scan, filter_by_angle, scan_stats}

let arc = filter_by_angle(create_scan(360, 1000.0), 0.0, 90.0)
let s = scan_stats(arc)
print("centroid at ({s["centroid_x"]}, {s["centroid_y"]})")

Combine two point arrays into one

Combines two point arrays into a single array. Points from scan1 come first, followed by scan2. Useful for fusing data from multiple sensors.

use plugin lidar::{create_scan, merge_scans}

let front = create_scan(180, 800.0)
let back = create_scan(180, 1200.0)
let full = merge_scans(front, back)

Keep every Nth point

Keeps every factor-th point, reducing the scan resolution. A factor of 2 halves the point count; factor of 1 returns all points.

use plugin lidar::{create_scan, downsample}

let scan = create_scan(360, 1000.0)
let sparse = downsample(scan, 4)
print("Reduced to {sparse} points")

Count points in a scan

Returns the number of points in a scan table.

use plugin lidar::{create_scan, point_count}

let scan = create_scan(360, 1000.0)
print("Points: {point_count(scan)}")

Find the closest point to a position

Finds and returns the point in points closest to the Cartesian target position (x, y). Returns nil if the scan is empty. The returned table has angle_deg, distance, x, and y.

use plugin lidar::{create_scan, nearest_point}

let scan = create_scan(360, 1000.0)
let closest = nearest_point(scan, 0.0, 0.0)
print("Nearest at angle {closest["angle_deg"]}")

Query relative to an arbitrary point — for example, the closest return to a target the robot is tracking:

use plugin lidar::{create_scan, nearest_point}

let scan = create_scan(360, 1000.0)
let near_target = nearest_point(scan, 700.0, 700.0)
print("closest to target: dist {near_target["distance"]}")

Euclidean distance between two Cartesian points

Computes the Euclidean distance between two Cartesian points.

use plugin lidar::{distance_between}

let d = distance_between(0.0, 0.0, 3.0, 4.0)
print("Distance: {d}")

Sort points by angle ascending

Returns a new table of points sorted by angle_deg in ascending order. Useful before rendering or comparing scans.

use plugin lidar::{create_scan, filter_by_angle, sort_by_angle}

let scan = create_scan(360, 1000.0)
let sorted = sort_by_angle(scan)

Generate a uniform circular scan

Generates a simulated uniform circular scan with num_points points, each at distance units, evenly spaced around 360 degrees. Each point has angle_deg, distance, x, and y fields.

use plugin lidar::{create_scan, scan_stats}

let scan = create_scan(360, 1000.0)
let stats = scan_stats(scan)
print("Centroid: ({stats["centroid_x"]}, {stats["centroid_y"]})")

Build a low-resolution scan as a quick test fixture and confirm its size:

use plugin lidar::{create_scan, point_count}

let coarse = create_scan(8, 500.0)
print("generated {point_count(coarse)} evenly-spaced points")

Global index

Find your way through Zolo

Try an idea

Start here

9 results

9 results

en