Last week I got me LidarLite range finder. It seemed like a cool toy to play around with and with 100Hz max acquisition rate, I thought of a few projects it would work for.
Hardware Setup:
LidarLite is controlled over I2C protocol, so to run this thing we just need to connect PWR, GND, DATA and CLOCK. Since I’ve had it handy, I decided to hookup the trusty BeagleBone Black and write a Python application.
BeagleBone Black to LidarLite LidarLite pin BB Header_Pin signal 1 P9_5 VDD_5V 2 N/A PWR_EN 3 N/A MODE/PWMout 4 P9_19 SCL 5 P9_20 SDA 6 P9_1 GND
Software & drivers:
For basic operation (no PWM output) the MODE and PWR_EN pins are left unconnected. If you don’t already have in installed “python-smbus” is required for the code to run. Install by
sudo apt-get install python-smbus
Once you have python-smbus, download the full source code. All of the communication LidarLite is based on reading and writing to 8 bit registers. So far I wrote a simple application that connects to the LidarLite, reads and prints all the registers 0x00-0x15 and 0x40-0x69 at start and then sits in an infinite loop reading the distance values.
import os import sys lib_path = os.path.abspath(os.path.join('..', 'Lib')) sys.path.append(lib_path)</pre> <pre>from simpleI2C import Simple_I2C as I2C def main(): myI2C = I2C(0x62, 1) # initialize I2C address myI2C.writeU8(0x02, 32) # set number of acquisitions. Default 128</pre> <pre>print "Internal Registers" for i in range(0x00, 0x16): RegValue = myI2C.readU8(i) print "Addr = ", format(i, '02x'), "; Val(hex) = ", format(RegValue, '02x'), "(dec) = ", format(RegValue, '03d'), "(bin) = ", format(RegValue, '08b')</pre> <pre>print "External Registers" for i in range(0x40, 0x69): RegValue = myI2C.readU8(i) print "Addr = ", format(i, '02x'), "; Val(hex) = ", format(RegValue, '02x'), "(dec) = ", format(RegValue, '03d'), "(bin) = ", format(RegValue, '08b') while(1): myI2C.writeU8(0x00, 0x04) # Take acquisition & correlation processing with DC correction print "dist(cm)=", ((myI2C.readU8(0x0F, attempts=20)<<8) + myI2C.readU8(0x10, attempts=20)) print "sig. strength = ", myI2C.readU8(0x0e, attempts=20) #sleep(.5) if __name__ == '__main__': main()
You’ll notice that I’m using simpleI2C module, that is a wrapper I wrote around the python-smbus. It is located in the “Lib” directory.
Results:
I was able to get data out of the unit. Sitting on my desk, looking straight up at the ceiling it was reading 169-170cm, and putting my hand above it did cause the values to change. So I am getting real data out of it, haven’t done anything to measure how accurate the results are.
Issues & problems:
The I2C behavior of the unit is very peculiar. Once the “acquire data” command is sent (0x04 to address 0x00), the unit doesn’t seem to respond till data is ready. This may be by design but the result is timeout and errors of the SMBUS I2C driver. This was the reason for me to write a wrapper and add a “retry” and “timeout” functionality to reading the I2C data. At present time I’m only getting ~1 reading per second. I tried lowering the number of acquisitions, which is set to 128 by default. This doesn’t seem to impact the processing time by much. Setting the number of acquisitions below ~16 would results in distance readings of zero. There are plenty of other registers to mess with, but I’m really hoping to achieve a more sensible acquisition rate of 30-60Hz so I can put this on a pan&tilt and create a LIDAR scan. With 1Hz rate, this may be a daunting task.