Operating the LIDAR

The Lidar Class

The lidar class should encapsulate all the essential operations required for lidar operation. In particular, the class should provide a method that provides a point representation that accurately depicts the environment.

import serial
import time

class Lidar:
    def __init__(self):
        #self.PORT = "/dev/ttyUSB0"
        self.PORT = "COM13"
        self.BAUDRATE = 460800
        self.serial =  serial.Serial(self.PORT, self.BAUDRATE, timeout=1)
        
    def send_cmd(self, cmd):
        self.serial.write(cmd)
        time.sleep(0.05)
        
    def start_scan(self):
        self.send_cmd(b'\xA5\x20')

    def stop_scan(self):
        self.send_cmd(b'\xA5\x25')

    def reset(self):
        self.send_cmd(b'\xA5\x40')

    def close(self):
        self.serial.close()
        
    # get_scan_packet  ---
    # get one tupel for : angle , distance, quality
    def get_scan_packet(self):
        while True:
            b0 = self.serial.read(1)
            if not b0:
                return None
            b0 = b0[0]

            # echtes RPLIDAR Sync-Kriterium
            if ((b0 & 0x01) ^ ((b0 >> 1) & 0x01)) == 1:
                rest = self.serial.read(4)
                if len(rest) != 4:
                    return None

                b1, b2, b3, b4 = rest

                quality = b0 >> 2

                angle_raw = ((b1 >> 1) | (b2 << 7))
                angle = angle_raw / 64.0

                dist_raw = b3 | (b4 << 8)
                distance = dist_raw / 4.0

                if 0 <= angle <= 360 and 10 <= distance <= 8000:
                    return angle, distance, quality

    # get_full_scan ------
    # scan until you have num_points; num_points should not be too small
    # to get a good scan
    def get_full_scan(self, num_points):
        frame = []
        while len(frame) < num_points:
            pkt = self.get_scan_packet()
            if not pkt:
                continue
            angle, distance, quality = pkt
            frame.append((angle, distance, quality))
        return frame

Visualization

This program serves as a test for the lidar class by displaying the collected data in a coordinate system.

import time
import math
import matplotlib.pyplot as plt
from Lidar import Lidar

# --- Plot one Frame (angle and distance) ---
def plot_frame(frame, sc, ax):
    xs = []
    ys = []

    for angle, distance, _ in frame: # quality will not be displayed
        r = distance / 1000.0  # convert from mm to m
        rad = math.radians(angle)
        x = r * math.cos(rad)
        y = r * math.sin(rad)
        xs.append(x)
        ys.append(y)

    sc.set_offsets(list(zip(xs, ys)))
    ax.set_title(f"360° Scan | Number of Points: {len(frame)}")

# --- Main ---
def main():
    ldr = Lidar()
    ldr.reset()
    time.sleep(2)
    ldr.start_scan()

    plt.ion()
    fig, ax = plt.subplots(figsize=(7,7))
    sc = ax.scatter([], [], s=6)
    ax.set_aspect('equal')
    ax.set_xlim(-4, 4)
    ax.set_ylim(-4, 4)
    ax.set_title("LiDAR 360° Scan")
    ax.grid(True)
    try:
        while True:
            # make a scan with 1000 points
            frame = ldr.get_full_scan(1000)
            plot_frame(frame, sc, ax)
            plt.pause(0.001)
            time.sleep(1)

    except KeyboardInterrupt:
        print("\nStop Scan...")
    finally:
        ldr.stop_scan()
        ldr.close()
        plt.ioff()
        plt.show()

if __name__ == "__main__":
    main()

The result of acan should look like this example: