Ignore bad scans, add documentation.

This commit is contained in:
Piv
2020-09-22 21:41:10 +09:30
parent f5fde9a23c
commit be85d7d39e
2 changed files with 94 additions and 12 deletions

View File

@@ -20,7 +20,6 @@ func main() throws {
extension SerialPort: LidarSerial{ extension SerialPort: LidarSerial{
public func setBaudrate(baudrate: Int) { public func setBaudrate(baudrate: Int) {
// TODO: handle different baudrates. Only need this for now.
switch baudrate{ switch baudrate{
default: default:
setSettings(receiveRate: .baud115200, transmitRate: .baud115200, minimumBytesToRead: 3, timeout: 1) setSettings(receiveRate: .baud115200, transmitRate: .baud115200, minimumBytesToRead: 3, timeout: 1)

View File

@@ -38,6 +38,19 @@ public enum RPLidarError: Error {
INCORRECT_DESCRIPTOR_FORMAT INCORRECT_DESCRIPTOR_FORMAT
} }
/**
Calculates the quality, angle and distance of a scan from the raw Data.
- returns:
LidarScan containing the quality, angle in degrees, and distance in mm.
- throws:
RPLidarError.SCAN_ERROR if the raw input is malformed or empty.
- parameters:
- raw: The bytes containing the scan data.
*/
func processScan(raw: Data) throws -> LidarScan { func processScan(raw: Data) throws -> LidarScan {
let newScan = raw[0] & 0b1 let newScan = raw[0] & 0b1
let inversedNewScan = (raw[0] >> 1) & 0b1 let inversedNewScan = (raw[0] >> 1) & 0b1
@@ -61,7 +74,20 @@ public struct LidarScan{
} }
/**
Function alias for measurement (single scan) closure. Use with iterMeasurements
- parameters:
- scan: Single lidar scan.
*/
public typealias MeasurementHandler = (_ scan: LidarScan) -> Bool public typealias MeasurementHandler = (_ scan: LidarScan) -> Bool
/**
Function alias for scan (multiple scans) closure. Use with iterScans
- parameters:
- scans: Array of lidar scans for this batch.
*/
public typealias ScanHandler = (_ scans: [LidarScan]) -> Bool public typealias ScanHandler = (_ scans: [LidarScan]) -> Bool
public class SwiftRPLidar { public class SwiftRPLidar {
@@ -70,6 +96,15 @@ public class SwiftRPLidar {
private var motorRunning = false private var motorRunning = false
private let measurementDelayus: UInt32 private let measurementDelayus: UInt32
/**
A class to interact with the RPLidar A1 and A2.
- parameters:
onPort: Serial Port the lidar is attached to
measurementDelayus: Delay that should be applied between each measurement. This is needed in case the serial port timeout is not working correctly.
- throws If the serial port cannot be connected, or the motor cannot be started.
*/
public init(onPort serialPort: LidarSerial, measurementDelayus: UInt32 = 500) throws { public init(onPort serialPort: LidarSerial, measurementDelayus: UInt32 = 500) throws {
self.serialPort = serialPort self.serialPort = serialPort
self.measurementDelayus = measurementDelayus self.measurementDelayus = measurementDelayus
@@ -87,12 +122,18 @@ public class SwiftRPLidar {
} }
} }
/**
Connect and configure the serial port.
*/
public func connect() throws { public func connect() throws {
disconnect() disconnect()
try serialPort.openPort() try serialPort.openPort()
serialPort.setBaudrate(baudrate: 115200) serialPort.setBaudrate(baudrate: 115200)
} }
/**
Close the serial port.
*/
public func disconnect(){ public func disconnect(){
// Need to close, SwiftSerial is blocking. // Need to close, SwiftSerial is blocking.
serialPort.closePort() serialPort.closePort()
@@ -112,12 +153,11 @@ public class SwiftRPLidar {
motorRunning = false motorRunning = false
} }
public func setPwm(_ pwm: Int) throws{ /**
assert(0 <= pwm && pwm <= Constants.MAX_MOTOR_PWM) Gets information about the connected lidar.
try self.sendPayloadCommand(Constants.SET_PWM_BYTE, payload: pwm.asData())
}
// Returns (model, firmware, hardware, serialNumber) - returns (model, firmware, hardware, serialNumber)
*/
public func getInfo() throws -> (UInt8, (UInt8, UInt8), UInt8, String){ public func getInfo() throws -> (UInt8, (UInt8, UInt8), UInt8, String){
try sendCommand(Data([Constants.GET_HEALTH])) try sendCommand(Data([Constants.GET_HEALTH]))
let (dataSize, isSingle, dataType) = try readDescriptor()! let (dataSize, isSingle, dataType) = try readDescriptor()!
@@ -130,6 +170,12 @@ public class SwiftRPLidar {
return (raw[0], (raw[2], raw[1]), raw[3], serialNumber) return (raw[0], (raw[2], raw[1]), raw[3], serialNumber)
} }
/**
Get the current health status of the lidar.
- returns (Health status, errorCode
- throws If the received health status is in the incorrect format.
*/
public func getHealth() throws -> (HEALTH_STATUSES, UInt8){ public func getHealth() throws -> (HEALTH_STATUSES, UInt8){
try sendCommand(Constants.GET_HEALTH.asData()) try sendCommand(Constants.GET_HEALTH.asData())
guard let (dataSize, isSingle, dataType) = try readDescriptor() else { guard let (dataSize, isSingle, dataType) = try readDescriptor() else {
@@ -144,18 +190,39 @@ public class SwiftRPLidar {
return (status, errorCode) return (status, errorCode)
} }
/**
Clear all data in the serial port buffer.
- throws If the buffer could not be emptied.
*/
public func clearInput() throws{ public func clearInput() throws{
_ = try serialPort.readData(ofLength: serialPort.inWaiting) _ = try serialPort.readData(ofLength: serialPort.inWaiting)
} }
/**
Send the stop command to the lidar.
- throws If the stop command cannot be sent.
*/
public func stop() throws{ public func stop() throws{
try sendCommand(Constants.STOP.asData()) try sendCommand(Constants.STOP.asData())
} }
/**
Send the reset command to the lidar. This puts the lidar into a state as though it just connected and started scannning.
- throws If the reset command annot be reset.
*/
public func reset() throws{ public func reset() throws{
try sendCommand(Constants.RESET.asData()) try sendCommand(Constants.RESET.asData())
} }
/**
Iterate over the lidar measurments, calling the given measurement handler each time scan is processed.
- parameters:
maxBufferMeasurements: Max number of measurements to include in the buffer before flushing it.
onMeasure: Measurement handler to execute on every read.
- throws If the input data is in the incorrect format, or the health retrieved errors.
*/
public func iterMeasurements(maxBufferMeasurements: Int = 500, _ onMeasure: MeasurementHandler) throws { public func iterMeasurements(maxBufferMeasurements: Int = 500, _ onMeasure: MeasurementHandler) throws {
try startMotor() try startMotor()
let (status, _) = try getHealth() let (status, _) = try getHealth()
@@ -183,10 +250,12 @@ public class SwiftRPLidar {
_ = try serialPort.readData(ofLength: dataInWaiting / Int(dataSize) * Int(dataSize)) _ = try serialPort.readData(ofLength: dataInWaiting / Int(dataSize) * Int(dataSize))
} }
} }
if raw.count > 0 {
read = try onMeasure(processScan(raw: raw)) read = try onMeasure(processScan(raw: raw))
// TODO: Figure out why this delay is needed. }
// If some delay is not present, then there is a high chance that the scan will be incorrect, and processScan will error. if measurementDelayus > 0 {
usleep(measurementDelayus) usleep(measurementDelayus)
}
} catch RPLidarError.INCORRECT_DESCRIPTOR_FORMAT { } catch RPLidarError.INCORRECT_DESCRIPTOR_FORMAT {
print("Incorrect Descriptor, skipping scan") print("Incorrect Descriptor, skipping scan")
} catch RPLidarError.SCAN_ERROR(let message) { } catch RPLidarError.SCAN_ERROR(let message) {
@@ -195,6 +264,15 @@ public class SwiftRPLidar {
} }
} }
/**
Iterate over batches of measurements.
- parameters:
maxBufferMeasurements: Max number of measurements to include in the buffer before flushing it.
minLength: Minimum number of measurements to include in a scan.
onScan: Handler function to execute on each scan.
- throws If the input data is in the incorrect format, or the health retrieved errors.
*/
public func iterScans(maxBufferMeasurements: Int = 500, minLength: Int = 5, _ onScan: ScanHandler) throws { public func iterScans(maxBufferMeasurements: Int = 500, minLength: Int = 5, _ onScan: ScanHandler) throws {
var scan: [LidarScan] = [] var scan: [LidarScan] = []
var read = true var read = true
@@ -203,7 +281,7 @@ public class SwiftRPLidar {
if scan.count > minLength { if scan.count > minLength {
read = onScan(scan) read = onScan(scan)
} }
scan = [] scan.removeAll()
} }
if measurement.quality > 0 && measurement.distance > 0 { if measurement.quality > 0 && measurement.distance > 0 {
scan.append(measurement) scan.append(measurement)
@@ -212,6 +290,11 @@ public class SwiftRPLidar {
} }
} }
private func setPwm(_ pwm: Int) throws{
assert(0 <= pwm && pwm <= Constants.MAX_MOTOR_PWM)
try self.sendPayloadCommand(Constants.SET_PWM_BYTE, payload: pwm.asData())
}
private func sendPayloadCommand(_ cmd: Int, payload: Data) throws{ private func sendPayloadCommand(_ cmd: Int, payload: Data) throws{
let size = UInt8(payload.count) let size = UInt8(payload.count)
var req = Constants.SYNC.asData() + cmd.asData() + size.asData() + payload var req = Constants.SYNC.asData() + cmd.asData() + size.asData() + payload