LiDAR: custom serial driver for LDRobot LD06, LD19 or STL27L
Panorama: 6K 360° spherical map
3D Scene: assembly of 3D scenes from 2D planes based on angle and offsets
single scans, no registration, no post processing.
klick the images to open the pointclouds in Sketchfab.
Exterior Scan (colormapped Intensity)
Interior Scan (Vertex Colors)
Total: \~ $200 - $280 (in April of 2025) (not including power supply)
(links are only for example, not necessarily the recommended way to purchase)
Rev. 1 using 2x 18650 Batteries and Buck Converter
Rev. 2 using 10.000 mAh Powerbank and Boost Converter
angular resolution of LD06 (left) vs. STL27L (right)
LD06: - sampling frequency: 4500 Hz - baudrate 230400 - Sales page - mechanical Datasheet - Protocol Description
STL27L: - sampling frequency: 21600 Hz - baudrate 921600 - datasheet - wiki - ROS2 driver git
Scan duration: 12s initialisation 17s shooting 4x photos 1:24m scanning 0.167° x 0.18° 37s stitching, cleanup
Breadboard Rev. 2
enable gpio-shutdown
echo "dtoverlay=gpio-shutdown" >> /boot/firmware/config.txt
if necesessary:
sudo nano /etc/systemd/logind.conf
HandlePowerKey=poweroff
GY-521 (MPU 6060): Accelerometer, Gyroscope and thermometer
i2c adress: 0x68

Since GPIO3 is hardwired to the Power Button, we need to use i2c-GPIO to map custom i2c pins (tutorial). Unlike serial is not getting crossed, so we connect SDA-SDA and SCL-SCL.
SDA: GPIO22
SCL: GPIO27
disable ic2_arm and enable i2c-gpio in /boot/firmware/config.txt
dtparam=i2c_arm=off
dtoverlay=i2c-gpio,bus=3,i2c_gpio_delay_us=1,i2c_gpio_sda=22,i2c_gpio_scl=27
search for devices on i2c bus 3:
sudo i2cdetect -y 3
# CPU fan at lower temp
echo "dtoverlay=gpio-fan,gpiopin=4,temp=45000" >> /boot/firmware/config.txt
# Power LED Heartbeat:
echo "dtparam=pwr_led_trigger=timer" >> /boot/firmware/config.txt
make script executable:
chmod +x gpio_interrupt.py
create new service for autostart
sudo nano /etc/systemd/system/pilidar.service
content:
[Unit]
Description=PiLiDAR-Button
After=network.target
[Service]
Type=simple
User=pi
Environment=LG_WD=/tmp
ExecStart=/usr/bin/python3 /home/pi/PiLiDAR/gpio_interrupt.py
Restart=no
[Install]
WantedBy=multi-user.target
reload daemon, enable and start service:
sudo systemctl daemon-reload
sudo systemctl enable pilidar.service
sudo systemctl start pilidar.service
check service if necessary:
sudo systemctl status pilidar.service
temporary solution:
sudo chmod a+rw /dev/ttyS0
sudo visudo
pi ALL=(ALL:ALL) NOPASSWD: /usr/bin/chmod a+rw /dev/ttyS0
then execute the temporary solution from python:
import subprocess
command = "sudo chmod a+rw /dev/ttyS0"
process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
(TODO: check and remove old!)
- forget about visudo and the subprocess call above.
- Open a terminal and run the following command: sudo nano /etc/udev/rules.d/50-ttyS0.rules
- Write the following line in the file and save it: KERNEL=="ttyS0",GROUP="dialout",MODE="0660"
- Run the following command to check if your user is a member of the dialout group: groups
- If you see dialout in the output, you are already a member of the group. If not, run the following command to add your user to the group: sudo usermod -a -G dialout pi
- Run the following command to reload the udev rules: sudo udevadm control --reload-rules
- Unplug and replug the serial device, or reboot the system, to apply the changes.
enable GPIO_18 (PWM0) and GPIO_19 (PWM1)
echo "dtoverlay=pwm-2chan" >> /boot/firmware/config.txt
check if "pwm_bcm2835" now exists:
lsmod | grep pwm
Install RPi Hardware PWM library:
pip install rpi-hardware-pwm
install Hugin with enblend plugin
sudo apt-get install hugin-tools enblend
using uhubctl cli tool. install:
sudo apt-get install uhubctl
list all available hubs and devices
sudo uhubctl
powering Raspberry Pi's USB-3-Ports (Hub 2) off / on
sudo uhubctl -l 2 -a off
sudo uhubctl -l 2 -a on
start jupyter for network access:
jupyter notebook --ip 192.168.1.16 --no-browser PiLiDAR.ipynb
! Housing and additional parts (obj and 3mf) in the PiLiDAR-Hardware Repo !
M12 to C-Mount lens adapter (thingiverse.com)
NEMA17 planetary reduction gearbox (printables.com)
Housing CAD model Rev. 2
FDM printing the old front panel (Rev. 1) in PETG
baudrate 230400, data bits 8, no parity, 1 stopbit
sampling frequency 4500 Hz, scan frequency 5-13 Hz, distance 2cm - 12 meter, ambient light 30 kLux
total package size: 48 Byte, big endian. - starting character:Length 1 Byte, fixed value 0x54, means the beginning of data packet; - Data Length: Length 1 Byte, the first three digits reserved, the last five digits represent the number of measured points in a packet, currently fixed value 12; - speed:Length 2 Byte, in degrees per second; - Start angle: Length: 2 Byte; unit: 0.01 degree; - Data: Length 36 Byte; containing 12 data points with 3 Byte each: 2 Byte distance (unit: 1 mm), 1 Byte luminance. For white objects within 6m, the typical luminance is around 200. - End Angle: Length: 2 Byte; unit: 0.01 degree; - Timestamp: Length 2 Bytes in ms, recount if reaching to MAX 30000; - CRC check: Length 1 Byte
The Angle value of each data point is obtained by linear interpolation of the starting angle and the ending angle.
The calculation method of the angle is as following:
step = (end_angle – start_angle)/(len – 1)
angle = start_angle + step*i
len is the length of the packet, and the i value range is [0, len].
using ~~Web Visualizer~~ Plotly to display 3D pointclouds works great in Jupyter.
Plotly seems to render client-sided, unlike Open3D Web Visualizer which renders host-sided and streams jpg sequences, which strains the Pi's both CPU and WIFI.
cd /home/pi/PiLiDAR
git clone https://github.com/LaserBorg/usb_dump --depth 1
cd usb_dump && chmod +x install.sh && ./install.sh "$(pwd)"echo '{"source_directories": ["/home/pi/PiLiDAR/scans"], "target_root_directory": null}' > usbdump.jsonCheck the log file:
tail -f /tmp/usbdump.log
to uninstall the service, run
chmod +x uninstall.sh && ./uninstall.sh
if the mount point is still persistend after being removed, just delete them.
sudo rm -rf /media/pi/<your device name>
get CP210x_Universal_Windows_Driver.zip here:
https://www.waveshare.com/wiki/DTOF_LIDAR_STL27L#Software_Download
current bookworm version has deprecated sysfs GPIO interface removed.
use LGPIO as described here:
sudo apt remove python3-rpi.gpio
sudo apt update
sudo apt install python3-rpi-lgpio
# or in an env without system packages:
pip3 install rpi-lgpio
LGPIO creates temp-files (issue) like ".lgd-nfy0". gpio-interrupt.py executes 'export LG_WD=/tmp' to set it's CWD.
disable hardware acceleration for VS Code (source)
Preferences: Configure Runtime Arguments
Set "disable-hardware-acceleration": true
there is no wheel for arm64. build requires libxerces:
sudo apt install libxerces-c-dev
pip install pye57
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
# make sure country code is set:
country=DE
add entry to wpa_supplicant.conf
sudo wpa_passphrase "YOUR_SSID" "YOUR_PASSWORD" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
inspirations - LIDAR_LD06_python_loder and Lidar_LD06_for_Arduino by Inoue Minoru ("henjin0") - ShaunPrice's StereoPi-supporting fork of BrianBock's 360-camera script (Article on Medium)
another Lidar implementation in Python - [pyLIDAR](https://github.com/Paradoxdruid/p
$ claude mcp add PiLiDAR \
-- python -m otcore.mcp_server <graph>