CPython on Raspberry Pi

The simplest way to use the RPLIDAR with a Raspberry Pi is to interface through the bundled USB adapter. Connect a USB cable on the Raspberry Pi and then plug the other end into the LIDAR USB adapter board. Ensure the adapter board is fitted correctly onto the sensor per the instructions in the manual.

PyGame

We'll be using the 2.8" PiTFT display. See this guide for instructions on installing and configuring the required support software. We'll also need pyGame to provide that data plotting capabilities. See the pyGame site for installation instructions.

The Code

To use the RPLIDAR, you need to start the motor spinning and tell it to start taking readings. It will then stream each reading it takes until you tell it to stop. Each reading includes the distance sensed, and the angle of the reading. To make things easier, the Skoltech library provides a convenient wrapper for this functionality, and that's what we'll use for this guide.

This example will consume data from the RPLIDAR and display it on a 2.8" PiTFT display. We start by setting up a few things.

Download: file
import os
from math import cos, sin, pi, floor
import pygame
from adafruit_rplidar import RPLidar

# Set up pygame and the display
os.putenv('SDL_FBDEV', '/dev/fb1')
pygame.init()
lcd = pygame.display.set_mode((320,240))
pygame.mouse.set_visible(False)
lcd.fill((0,0,0))
pygame.display.update()

# Setup the RPLidar
PORT_NAME = '/dev/ttyUSB0'
lidar = RPLidar(None, PORT_NAME)

# used to scale data to fit on the screen
max_distance = 0

Next we create a buffer in which to keep distance data. Each item in the list stores the distance measured at each degree for a complete rotation. We use this to maintain the most recent set of measurements even though each scan will be incomplete.

Download: file
scan_data = [0]*360

We use the iter_scans function of the RPLIDAR object. This starts the motor and the scanning for us so we can skip that in our code.  iter_scans accumulates measurements for a single rotation and returns a list of tuples, from which we are interested in the second and third items. These are the angle and the distance measured at that angle. Both are floating point values.

Once we have a scan, we step through each data point. The floor of the angle is taken and used as the index at which to store the measurement value. Because it is possible that multiple consecutive measurements in a scan could be close to the same angle (and thus having the same floor value) there is a small chance that some measurements are lost. But there is no real disadvantage to this, so it can be disregarded.

Download: file
scan_data = [0]*360

try:
    print(lidar.info)
    for scan in lidar.iter_scans():
        for (_, angle, distance) in scan:
            scan_data[min([359, floor(angle)])] = distance
        process_data(scan_data)

except KeyboardInterrupt:
    print('Stoping.')
lidar.stop()
lidar.disconnect()

Once we have updated scan_data, it is passed to the process_data function.

The process_data function can do anything from finding the closest object, to choosing the best direction to move in. The only requirement is that it be as fast as possible. If it takes too long to process a scan, data from the RPLIDAR will eventually be dropped. Since we're constantly reading fresh data, dropping an occasional scan shouldn't be an issue.

In this example we display the distance data on a PiTFT.

The first step of a pass is to clear the display. Then it looks at each angle: 0-359. If the distance recorded for that angle is zero, it's because a reading hasn't be acquired for that angle yet, and we can safely ignore it. If there is a distance recorded for the angle, max_distance is adjusted as necessary to keep the display scaled to fit on the screen. The angle is converted to radians (from degrees) and the vector for the reading is converted to a cartesian coordinate which is then used to plot the data point on the display.

After all measurements have been plotted, the display is updated.

Download: file
def process_data(data):
    global max_distance
    lcd.fill((0,0,0))
    for angle in range(360):
        distance = data[angle]
        if distance > 0:                  # ignore initially ungathered data points
            max_distance = max([min([5000, distance]), max_distance])
            radians = angle * pi / 180.0
            x = distance * cos(radians)
            y = distance * sin(radians)
            point = (160 + int(x / max_distance * 119), 120 + int(y / max_distance * 119))
            lcd.set_at(point, pygame.Color(255, 255, 255))
    pygame.display.update()

Full code

"""
Consume LIDAR measurement file and create an image for display.

Adafruit invests time and resources providing this open source code.
Please support Adafruit and open source hardware by purchasing
products from Adafruit!

Written by Dave Astels for Adafruit Industries
Copyright (c) 2019 Adafruit Industries
Licensed under the MIT license.

All text above must be included in any redistribution.
"""

import os
from math import cos, sin, pi, floor
import pygame
from adafruit_rplidar import RPLidar

# Set up pygame and the display
os.putenv('SDL_FBDEV', '/dev/fb1')
pygame.init()
lcd = pygame.display.set_mode((320,240))
pygame.mouse.set_visible(False)
lcd.fill((0,0,0))
pygame.display.update()

# Setup the RPLidar
PORT_NAME = '/dev/ttyUSB0'
lidar = RPLidar(None, PORT_NAME)

# used to scale data to fit on the screen
max_distance = 0

#pylint: disable=redefined-outer-name,global-statement
def process_data(data):
    global max_distance
    lcd.fill((0,0,0))
    for angle in range(360):
        distance = data[angle]
        if distance > 0:                  # ignore initially ungathered data points
            max_distance = max([min([5000, distance]), max_distance])
            radians = angle * pi / 180.0
            x = distance * cos(radians)
            y = distance * sin(radians)
            point = (160 + int(x / max_distance * 119), 120 + int(y / max_distance * 119))
            lcd.set_at(point, pygame.Color(255, 255, 255))
    pygame.display.update()


scan_data = [0]*360

try:
    print(lidar.info)
    for scan in lidar.iter_scans():
        for (_, angle, distance) in scan:
            scan_data[min([359, floor(angle)])] = distance
        process_data(scan_data)

except KeyboardInterrupt:
    print('Stoping.')
lidar.stop()
lidar.disconnect()
This guide was first published on Mar 19, 2019. It was last updated on Mar 19, 2019. This page (CPython on Raspberry Pi) was last updated on Sep 16, 2019.