Giter Club home page Giter Club logo

sweep-sdk's Introduction

Sweep SDK

Continuous Integration Continuous Integration

SDK for Scanse Sweep LiDAR.

Real-time viewer for a device speed of 5 Hz:

viewer

Density-based clustering on the point cloud:

dbscan

License

Copyright © 2016 Daniel J. Hofmann

Distributed under the MIT License (MIT).

sweep-sdk's People

Contributors

daniel-j-h avatar hyunh90 avatar kent-williams avatar mbalszun avatar mikegitb avatar octylfractal avatar peterjohnson avatar pkubaj avatar sevenbitbyte avatar simon04 avatar vrong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sweep-sdk's Issues

Sweep locking up using C SDK

While using the C code SDK, My code uses the following libsweep.lib functions in this specific order:
sweep_device_construct(port, 115200, &error)
sweep_device_set_motor_speed(sweep, hz, &error)
sweep_device_set_sample_rate(sweep, sampleRate, &error)
sweep_device_get_motor_ready(sweep, &error)
int32_t speed = sweep_device_get_motor_speed(sweep, &error);
int32_t rate = sweep_device_get_sample_rate(sweep, &error);
sweep_device_start_scanning(sweep, &error);
check(error);
scan = sweep_device_get_scan(sweep, &error);
This is a similar setup to what is seen in example.c.
There are two issues that I run into.
The first, when I use the settings of hz = 5 and sampleRate =1000, the scanse gets locked up within sweep_device_get_scan(). Following the callstack, I noticed that within scanse.cc -> class ScanQueue is where the lock up occurs while "the_queue" is empty. Is there a way to reset the scanse after a certain amount of time of it being locked up? It seem that the scanse will wait indefinitely, and at some point I would like the scanse to reset.
The second issue occurs in the same place, however, when I use the setting hz = 5 and sampleRate = 500, the scans works as expected until a rotational acceleration is applied to the sweep. The motor monetarily stops, and seems to reset. However, the code gets locked up in the same place as described above. There are also cases where the sweep is running normally, and without any external disturbance, the code gets locked up in the same place. I would like for it to continue scanning similar to what the visualizer does. When I perform the same rotational acceleration to the sweep while it is running in the visualizer, the motor resets and the visualizer continues to receive data after a momentary pause. Any advice what to do in order to achieve that using the SDK?

I am using the version 1.3 of the sweep firmware, and the lastest version of libsweep.lib as of 5/8.
I am running Windows 7 x64

Switch libsweep from Makefile to CMake

We're currently using a Makefile + config.mk for the build system. Now with the implementation switching to C++11 and @dcyoung polishing up the Windows branch we should re-visit this decision.

The examples are using CMake, too, and we provide a FindSweep.cmake CMake module already.

Here is how users can then include libsweep into their CMake based build system.
Related issue: install FindSweep.cmake on the user's system.

Should probably land before merging the Windows support branch to eliminate the need for Visual Studio specific project files, if we decide on going this route.

Extra requirements for a CMake based build system:

  • Require CMake 2.8.12, so we can build without having to upgrade CMake going back to Trusty
  • Add SONAME (or equivalent) to shared library in a portable way
  • Provide install target for libsweep.so+sweep/sweep.h+sweep/sweep.hpp

Error handling in worker thread

I finally have some time and thought about tackeling the question of what should happen when an error occures in the worker thread (currently the thread will simply stop). But before I create a PR I wanted to discuss my ideas first:

  • First of all I think we need to distinguish between faults from which the thread should try to recover (e.g. crc errors) and error from which recovery is infeasible (device got disconnected).

  • In case of recoverable errors I'd just try to find the next valid packet and give up after a few iterations.

  • In case of non-recoverable errors, the loop would exit and the error would be propagated through the queue to sweep_device_get_scan.

The question is what the user or the library is supposed to to to bring the system back into a defined state (we could for example automatically call sweep_device_stop_scanning or require this from the user)

Project structure

This might be specific to my particular needs but I'd find it more intuitive and useful if things like the examples and docs would be put into a separate folder under the root directory, keeping the library directories clean:

-sweep-sdk
     |-libsweep
     |    |-include
     |    \-src
     |    
     |-sweepjs
     |-sweeppy
     |-docs
     |    \-serial_protocol_spec.md
     |
     |-examples
     |   |-c
     |   |-cpp
     |   |-js
     |   \-python
     |
     \-CMakeLists.txt  <-- master CMakeFile

Also, I mentioned this in a PR but I'd also prefer it, if the examples could be compiled without having to install the liibsweep library first.

E.g. on windows it is not clear cut, where such a library should be installed and in partucular if one just wants to have a quick look, I think it is preferable not to require any changes to the system. Pretty much the same might hold true for many embedded systems for which you need to use a crosscompiler.

Sweeppy setup.py missing PATCH version

Currently the sweeppy setup.py file only defines MAJOR.MINOR version scheme (eg: version='1.1'). Can this be changed to include full semantic version MAJOR.MINOR.PATCH? This would allow us to synchronize the sweepjs, sweeppy and libsweep versions.

sweeppy can't find C-libraries

libsweep version + affected bindings

libsweep 1.1.1

operating system

Arch Linux

Description:

The make file for the c code installs the libraries into '/usr/local/lib'. The python wrapper 'sweppy' uses those libraries, but looks only in '/usr/lib'.

I fixed it by running
sudo ln -s /usr/local/lib/libsweep.so /usr/lib/libsweep.so
sudo ln -s /usr/local/lib/libsweep.so.1 /usr/lib/libsweep.so.1

Would be nice if this could work out of the box. Or did I just understand something wrong?

Install pkg-config configuration / CMake module

At the moment we provide a basic installation target in libsweep's Makefile. It would be great to improve this by also installing a pkg-config / our FindSweep.cmake file.

Until then users have to manually -lsweep which works fine, too.

More graphical and fancy demos

At the moment I only started to play with the point-cloud 3d map idea, but we don't have much examples that are more beautiful than "just" printing some numbers on the screen.

I understand having library + bindings is a great first step, but users want fancy examples and use-cases.

Which languages to support?

At the moment there is libsweep the low-level ABI and API stable C library. There is a convenient C++11 header on top of that, Python2/3 and NodeJS FFI bindings.

Which of those do we actually want to support? The beauty of having a ABI and API compatible library is that FFI bindings are very lean and elegant to write. The downside is, someone still has to build and maintain not only the abstractions needed for the bindings, but also e.g. the build system, how to package and ship bindings etc.

If we decided on languages to support, we need a plan for packing library + bindings up to make the user's life as easy as possible.

High-resolution timestamp for each sample

At the moment we accumulate a full 360 degree scan and return this scan to the user.

Many use-cases need high-resolution synchronization which is currently not possible on a per-sample basis to do for the user (could be estimated using motor speed and timestamp on a scan, though).

For usability reasons it would be great to have a timestamp per angle, distance sample.

Tasks:

  • time.h needs a timestamp interface
  • time_unix.c implementation
  • sweep.h needs a timestamp interface for samples (similar to get_distance, get_angle, ..)
  • sweep.c and dummy.c implementations
  • add to sweep.hpp
  • add to sweeppy
  • add to sweepjs

Make libsweep allocator aware

We should provide means for the user to configure the allocator. The user should be able to plug in e.g. a efficient stack-allocator if she wants to. This needs to go hand in hand with the C++ abstractions.

Angle unit conversion

from sweep datasheet:

Angle measurement Sweep uses an optical encoder to measure the angle of the rotating sensor head. The angle that is recorded for a range data point is the angle the sensor is at when the measurement is completed.The beginning of the scan, and zero degrees is located where the status LED projects out of the base of the sensor

could we get provided with a formula to convert from the encoder "counter-ticks" to degrees?

or at least the encoder model, so that we can see the manufacturer information (ticks per degree or so).

kind regards!

lingering device with sweepjs bindings

Something appears to be persisting after using the sweepjs bindings. Attempting to use an alternate method of interfacing with the device (ie: the sweep-visualizer) after running the sweepjs example requires that the device be reset. This is not the case with the sweeppy python bindings. My best guess is that the python bindings properly cleanup and and destroy the device on exit, while perhaps the sweepjs bindings do not?

Delay in serial buffering of DX response on RaspberryPi, corrupts subsequent responses

I've discovered an issue but not entirely sure what is causing it.

  • OS: linux
  • Platform: so far, I can only reproduce this behavior on the raspberryPi
  • Library: libsweep (and by extension sweepy + sweepjs as well)
  • Repeatable: around 25% of the time

Code to reproduce:

sweep::sweep device{argv[1]};
device.set_sample_rate(500); // errors here, description below

The expected behavior should be as follows:

  • During device creation, sweep_device_stop_scanning() is called internally... which performs the following:
    • sends DX\n command to stop any potential active data stream
    • waits 35ms to allow any incoming data blocks, and the DX response (DX00P\n) to come in
    • flushes buffer (including the first DX response)
    • sends another DX\n command
    • reads the DX response (DX00P\n) that should be responding to the 2nd command
  • At this point the serial buffer should be empty!
  • Set sample rate
    • sweep_device_set_sample_rate() sends LR01\n command to set the sample rate and then reads LR response (LR01\n00P\n)

The issue is a checksum error when attempting to read the LR response. Instead of LR01\n00P\n, the response that is read from the serial buffer is DX00P\nLR0. It appears that a DX - Stop Scanning response (DX00P\n) is being left in the serial buffer, and is therefore being interpreted as the beginning of the LR response.

Using a protocol analyzer I have confirmed that the timing of the commands and responses, as well as their byte content, is as expected:
Here is a zoomed out timeline:

  • device is constructed (sends 2 DX commands 35 ms apart, both receive valid responses)
  • Set sample rate (with valid response)
  • program errors and the device is destructed (sends 2 DX commands 35 ms apart, both receive valid responses)

sniffer

From this I can tell that the communication over the wire is fine. No extra "DX" response (DX00P\n) is coming in, and no response is delayed. The bytes from the "LR" response (LR01\n00P\n) are valid as well.

It seems then that somehow the buffer isn't being flushed properly. @daniel-j-h Any idea what could be causing this?

Edit: it appears the flush is NOT the issue. See below.

"Error: unable to receive stop scanning command response" in example.cc

i'm running libsweep/examples/example.cc to get some calibration data and often find that after a quick stop/start i get Error: unable to receive stop scanning command response or Error: unable to receive start scanning command response 3 or 4 times before getting connectivity again. Unplugging the device and replugging it back in always seems to clear the problem (i.e. I don't get this error the first time I run the example).

Is this expected? Is there additionally debugging I can provide?

[Feature Request] 1D Ranging/Data Streaming

This is probably more a firmware issue, so please tell me if I should post this somewhere else.

I was wondering, why it isn't possible to scan when motor speed is zero? Together with a command to turn the head forward it would be a nice feature, if the lidar could also be used as a range finder.

Also, I don't know if this is related, but I think it would be preferable if the head wouldn't automatically start turning after a powercycle.

Polar to Carthesian Convenience Functions

At the moment we return angle, distance samples. Many use-cases need to convert from Polar to Cartesian coordinates anyway (see our examples).

It would be great to provide convenience helpers already in the C++, Python and Javascript bindings.

libsweep should not expose this as it's strictly not needed. We don't want to clutter the low-level API.

Viewer example misrepresents distance of sensor readings > 5m away

In the viewer example, any sensor reading with a distance > 5m are drawn with a distance of 5m.
see here

This can misrepresent the data that is being reported. For example, using the viewer from a sensor placed in our relatively small office space shows large arcs of inaccurate readings where the distance is being clipped.

Zooming in to a small 5x5 meter area is fine. And not drawing values beyond a certain distance threshold seems like an understandable design choice. But we should avoid augmenting the distance value as it looks like the sensor is not reporting accurate distances. A new user might think their sensor is malfunctioning.

Build and Distribute Python Wheel

Splitting off from @kenzierocks' initial Python packaging pull request #51 (please read for context).

At the moment we require users to compile, link, and install libsweep.so before they can install the Python bindings. It would be great if we no longer require these steps. Instead users should only have to

pip install sweep

and get everything pre-built, packaged and set up for their platform.

This requires us to build Python Wheels for Linux, macOS and Windows (if we want to support pre-built binaries for all of them). Which can be done but is a bit cumbersome.

I can think of a solution where we (at least for Linux) spawn up a Docker container and let Travis build and publish binaries for master automatically.

Please read the initial Python packaging pull request summarizes what needs to be done: #51.

Note: this is on the wish-list but certainly not prioritized right now. Feel free to jump in and help out here.

Refactor Error Translation

The serial sub-system provides it's own error types to be fully encapsulated and to not go near circular dependencies. The translation to sweep's error types happens manually all over the place in sweep.c (e.g.

if (serialerror) {
*error = sweep_error_construct("unable to write command");
sweep_serial_error_destruct(serialerror);
return;
}
where the serial error message is even discarded).

Refactor this translation into a helper function.

Add full semantic version scheme

Currently, we only use major and minor version scheme. Any reason we shouldn't add support for full semantic version scheme... ie: Major.Minor.Patch ?

Creating sweep object hangs

When we connect the sweep to our usb port it opens on /dev/ttyUSB0. The visualizer program works flawless with the sweep. So we took the example.cc file and succesfully compiled the project. But when we run the example with "./Lidar /dev/ttyUSB0" it prints "Constructing sweep device..." and then just hangs. It doesn't throw any error, but it's not proceeding. Looking at your code I would expect it to output its motor speed, but it doesn't do that. So it doesn't get past following line:
https://github.com/scanse/sweep-sdk/blob/master/libsweep/examples/example.cc#L16

If we disconnect the sweep it throws the "Error: opening serial port failed", so we know "/dev/ttyUSB0" is the right port.

Thanks in advance.

Scan Response Checksum Depends on Endianess

For the scan respone packet's checksum the spec. says:

adding the six bytes of data

isn't this endianness-dependant for the two-byte uint16_t types? That is, the current way of sliding over the chunk of memory is little-endian specific. Should this at least be documented in the spec?

Relevant location

getScan() hangs after resetting stationary device, regardless of new motor speed

We just discovered a bug in the current firmware (v1.3) that occurs when a device is reset while stationary (motor speed setting 0Hz)

Example:

  1. Device is stationary
  2. Device is reset using RR command
  3. do anything here... including adjust motor speed to non-zero setting
  4. Start scanning
  5. getScan() hangs

Any other device interactions work as expected, yielding normal responses. Additionally, the data stream actually initiates just fine, and the individual scan packets are received without error.

getScan() hangs because accumulate_scans() never encounters a sync reading. Instead, all the incoming data packets contain an azimuth value of 0°. ie: each 7 byte scan packet looks like [0,0,0,x,x,x,x] where x is a non-zero value.

Because the azimuth values do not increase, a sync reading is never encountered and a new scan is never produced. But neither is an error. The reason an error is never produced is because all the incoming scan packets pass their checksum. Its possible libsweep would break from this after receiving more than SWEEP_MAX_SAMPLES data packet responses. But my applications were timing out before that anyhow.

This is a firmware bug. We are working on a fix which will be released ASAP.
However, we may still want to implement a check in accumulate_scans() for successive packets that have the exact same azimuth, and throw an error.

Avoid struct padding specific to certain compilers?

When calling sweep_serial_device_read and potentially elsewhere, sizeof() is used to specify a number of bytes.

Ex:

sweep_serial_device_read(serial, scan, sizeof(sweep_protocol_response_scan_packet_s), &serialerror);

Something like sizeof(sweep_protocol_response_scan_packet_s) ends up being compiler dependent, for example its returning 7 bytes on linux and 8 bytes on windows (via gcc on MinGW).

I'm confused why this is happening, as it looks like the __attribute__((packed)) is being applied to all those struct definitions via SWEEP_PACKED. However, the input (len) to sweep_serial_device_read really shouldn't depend on the size of sweep_protocol_response_scan_packet_s. It should be based on the communication protocol spec and we should probably define this explicitly.

Perhaps we should create a mapping of receipt types to expected byte length and pull len from there. This could be easily achieved by adding a len field to each struct that outlines a command or response.

Edit: I thought it might be possible that MinGW compiler (gcc v5.3) was not passing this condition...

#if __GNUC__ >= 4

but I've verified that it is passing the condition and the definitions are being set. I'm very confused then why the structs are producing different sizeof()

Support for macOS

Currently I only tested libsweep, SweepPy and SweepJs on my Linux Ubuntu machine.

For platform specifics we can dispatch in config.mk. I assume we have to change some specifics such as the installation paths, maybe some cflags/ldflags. The _unix implementations are POSIX-conforming and should Just Work (tm).

Travis CI Integration

Similar to the .travis.yml here we want CI integration.

Compiling libsweep is be a good start, properly testing all sub-projects would be even better.

Tasks:

  • enable Travis for this repository
  • add a .travis.yml config
  • compile libsweep.so
  • build dummy libsweep.so (make dummy) and test projects against

Support for Windows

I can't help you much there. If we want this, we have to adapt the build system (config.mk, Makefile for libsweep, maybe gyp for SweepJs). Then provide implementations along the _unix ones conforming to our interfaces.

Implement sample rate

449565f introduced commands to get but more importantly set the sample rate.

For #7 the sample rate was left out since we could only read it. Now that we can modify it we should implement it for users to be able to set it.

Check Firmware Compatibility

From #70 (comment), quoting @dcyoung:

Correct, the SDK will no longer work with firmware v 1.0 which did not include the "MZ" command, among other things.

We should provide a way for the user to check if the sweep-sdk is compatible with the hardware device.

Or at least note it down in the readme and tag releases (i.e. git tag `vx.y.z)) and then provide changelog'ish summaries.

Communicating Failure in Updated Protocol

Scanse will start shipping devices shortly. The latest firmware that will come installed on the devices is using an updated communication protocol. The updated protocol is outlined in the docs on the motor-ready branch.

The relevant additions for this issue are...
Data Start - DS will NOT trigger data acquisition unless the motor is spinning ( > 0Hz) and the speed has stabilized.

  • If the motor is spinning and the speed has stabilized, then the device will respond to a DS command with a normal header ('DS00P\n'... ie: status bytes == '00') and data acquisition will commence.
  • If the motor is stationary or the speed has not stabilized, then the device will respond to a DS command with a modified header and data acquisition will NOT commence. This modified header will use the status bytes to indicate the type of failure. For example 'DS12S\n' (status bytes == '12') indicates that the motor speed has not yet stabilized, while 'DS13T\n' (status bytes == '13') indicates that the motor is stationary/0Hz. In either case the data acquisition will not commence.

Adjust Motor Speed - MS will not trigger an adjustment if the motor speed has not yet stabilized. Similar to above, the response message will use status bytes to indicate the failure.

  • Status bytes '00' indicate normal processing (ie: not issue)
  • Status bytes '11' indicate the original command was sent with an invalid parameter
  • Status bytes '12' indicate that the motor speed has not yet stabilized

We need a way to communicate these failures to the user without exiting or destroying the sweep. At the very least, the high level functions in sweep.cc need to be able to handle these conditions. I'm wondering about the following options. @daniel-j-h I am very open to suggestions, but need to get this going ASAP.

  • Add return values to many of the void functions (ie: sweep_device_start_scanning() could return false or an int capable of representing success + multiple failure conditions).
  • Error on these failures, but store the most recent error somewhere. Do not destroy anything on these kinds of errors. Include well defined error codes in the header such that the sweep.cc functions (and even the user) can check the most recent error code in the event of a failure.

Ie:

if(start scanning produces error){
  check the error type
  if (error type indicates the motor is stationary) {
    adjust the motor speed
    wait for motor speed to stabilize
    try to start scanning again
  }
}

Internal functions could opt to handle some of this logic. For example: sweep_device_set_motor_speed() might encounter an error because the motor speed is still adjusting from a previous command. The function could witness the error, wait for the motor speed to stabilize and then try again. All of this could happen without requiring any user code. However, some conditions like invalid parameters or a stationary motor will require user handling.

Parse DX receipt in "sweep_device_stop_scanning", instead of sending DX command twice.

Sending a DX command (host->sensor) to a streaming sensor stops data acquisition. However, this doesn't happen instantaneously. Once the DX command is sent out, the host will still receive a few Data Block receipts before seeing the DX receipt.

Currently the sweep_device_stop_scanning uses the following technique:

  1. Send DX command (host -> sensor)
  2. Wait a while (5000 us), for any remaining Data Block receipts and the DX receipt to arrive (sensor -> host).
  3. Flush the serial port.
  4. Send another DX command (host -> sensor)
  5. Read the DX receipt (sensor -> host) that returns as a response to step 4

This was really a temporary workaround and should be replaced by an actual check for the DX receipt among the incoming Data Block receipts. The initial reaction might be to assume we cannot reliably check for a DX receipt among incoming Data Block receipts because the bytes of a Data Block are variable. The fear is that they could theoretically take the appearance of a DX receipt. However, we can still reliably discern the two because:

  • While it is true that Data Block bytes 1-6 can take any form, byte 0 (sync/error byte) is limited to very specific error codes and is therefore controllable. We can guarantee that byte 0 will never look like the first byte of a DX receipt.
  • The sensor guarantees the transmission of complete Data Block receipts before sending a DX receipt. That is to say that a DX receipt will never come mid-Data Block.
  • The sensor guarantees that no Data Block receipts are transmitted after the DX receipt is transmitted.

Obviously we can't protect against data corruption with 100% reliability, but we can certainly protect against valid Data Block receipts being misinterpreted as a DX receipt.

In sweep_device_stop_scanning, we should do something along the lines of:

  1. Send a DX command (host -> sensor)
  2. Look at the incoming bytes, and discern if they belong to a Data Block receipt or a DX receipt.
  3. In the case of a Data Block receipt, handle it... (as we would during scanning, or simply trash it). In the case of a DX receipt, we proceed as if we picked up from step 5 above.

Motor Speed commands

@daniel-j-h I noticed that you still have MA and MX in the protocol. Instead of having a separate command for start, stop, and adjusting speed, we decided to just use MS and have 00 be motor stop and anything positive start the motor.

I'll work on getting the protocol spec in this repo soon.

Improve get_scan throughput

At the moment get_scan waits for the sync bit then records responses until the next sync bit. This makes sure we gather and report only full 360 degree scans:

sweep-sdk/libsweep/sweep.c

Lines 177 to 231 in 853f113

sweep_scan_s sweep_device_get_scan(sweep_device_s device, sweep_error_s* error) {
SWEEP_ASSERT(device);
SWEEP_ASSERT(error);
sweep_protocol_error_s protocolerror = NULL;
sweep_protocol_response_scan_packet_s responses[SWEEP_MAX_SAMPLES];
int32_t first = 0;
int32_t last = SWEEP_MAX_SAMPLES;
for (int32_t received = 0; received < SWEEP_MAX_SAMPLES; ++received) {
sweep_protocol_read_response_scan(device->serial, &responses[received], &protocolerror);
if (protocolerror) {
*error = sweep_error_construct("unable to receive sweep scan response");
sweep_protocol_error_destruct(protocolerror);
return NULL;
}
// Only gather a full scan. We could improve this logic to improve on throughput
// with complicating the code much more (think spsc queue of all responses).
// On the other hand, we could also discard the sync bit and check repeating angles.
if (responses[received].sync_error == 1) {
if (first != 0 && last == SWEEP_MAX_SAMPLES) {
last = received;
break;
}
if (first == 0 && last == SWEEP_MAX_SAMPLES) {
first = received;
}
}
}
sweep_scan_s out = malloc(sizeof(sweep_scan));
if (out == NULL) {
*error = sweep_error_construct("oom during sweep scan creation");
return NULL;
}
SWEEP_ASSERT(last - first >= 0);
SWEEP_ASSERT(last - first < SWEEP_MAX_SAMPLES);
out->count = last - first;
for (int32_t it = 0; it < last - first; ++it) {
out->angle[it] = sweep_protocol_u16_to_f32(responses[first + it].angle) * 1000.f;
out->distance[it] = responses[first + it].distance;
out->signal_strength[it] = (responses[first + it].signal_strength / 256.f) * 100.f;
}
return out;
}

But it's also wasteful since we're throwing away the data before the first sync bit, limiting our throughput.

To keep the low-level library simple I don't want to go the route of a worker thread, accumulating behind the scenes. Instead, we should be able to check for the angle we start recording at and stop recording as soon as we reach it again.

We don't guarantee for the first sample to start at angle 0 degree after all. If the user needs this, she can accumulate herself.

Check for !device.is_scanning before sending commands

It looks like the sample rate commit broke the c++ example by placing the calls to get_motor_speed and get_sample_rate AFTER start_scanning...

device.start_scanning();

This probably works with the dummy library because it sends back information regardless of whether or not its scanning. However, the actual sweep device cannot receive or respond to commands other than DX\n while it is scanning. The calls to get_motor_speed and get_sample_rate need to be placed before start_scanning.

Additionally, in those functions we should probably check if the device is scanning and simply error or report it somehow before trying to message a device... which will only cause more errors. We can do this easily, as we already set a is_scanning bool in the sweep_device struct

RuntimeError: unable to start scanning. could not verify motor speed.

sweep firmware version

Version 1.3

libsweep version + affected bindings

*latest at this moment (20160510- just cloned and installed from git repository)
*working with sweeppy

operating system

Linux raspberrypi 4.4.50-v7+ #970

Platform/Hardware Setup

Raspberry pi 3

Description:

Traceback (most recent call last):
  File "lidar360.py", line 196, in <module>
    main()
  File "lidar360.py", line 154, in main
    sweep.start_scanning()
  File "build/bdist.linux-armv7l/egg/sweeppy/__init__.py", line 136, in start_scanning
RuntimeError: unable to start scanning. could not verify motor speed.

Sensor protocol description nitpicking

The protocol description file e.g. says

Azimuth: Angle that ranging was recorded at (in degrees). Azimuth is a float value, transmitted as a 16 bit int. This needs to be converted from 16bit int to float. Use instructions in the Appendix. Note: the lower order byte is received first, higher order byte is received second.

I'm not sure if this is just confusing or even wrong, but in any case wouldn't it be simpler to say something like:

Azimuth: Angle that ranging was recorded at (in degrees). Azimuth is transmitted as a 16 bit fixed point value with a scaling factor of 16 (4bit after radix) in little endian format (first byte is low order).

Also the conversion from wire format to storage format seems to be unnecessarily complicated. Wouldn't a simple

static_cast<int>(angle/16.0 * 1000.0) 

do the trick?
Or have I overlooked something?

Improve Landing-Page

There should be a nice looking landing page with notes on what this project is about, use-cases and short explanations for the bindings.

sweep-ctl: interacting with the device from the command line

We really should have some sort of sweep-ctl binary to get set and reset the device state.

With an interface similar to

$ sweep-ctl /dev/ttyUSB0 get motor_speed
3
$ sweep-ctl /dev/ttyUSB0 set motor_speed 5
5
$ sweep-ctl /dev/ttyUSB0 set motor_speed
3

Otherwise users have to modify the examples and build their own binaries for these kind of interactions.

Should get installed in combination with a man page (generated by pandoc).

Implement error code bit

a0d5458 introduced an error code bit. Currently we only check the sync bit here to check when a new full scan starts. If the error bit is set we should throw away the scan.

Build and Distribute Binaries for npm

Similar to the ticket about Python Wheels we should build and distribute pre-built binaries.

This would allow users to do

npm install sweep

and get everything pre-built, packaged and set up for their platform. For now users have to compile, link, and install libsweep.so themselves before being able to install and use the Node bindings.

The common way to do this is to hook into the Node binding build system via node-re-gyp and store binaries on S3 or Github releases. Here's documentation and a skeleton repository:

Note: this is on the wish-list but certainly not prioritized right now. Feel free to jump in and help out here.

Worker Thread Discussion

We've discussed the need to introduce a worker thread for IO. However, the implementation must consider cross-platform compatibility. The debate is whether to...

  1. Shift the libsweep library's implementation to C++, while still maintaining the C interface. The C++ implementation would allow for all threading to handled by C++11 standard thread library, and avoid the need for thread abstraction. This would both consolidate the code. @daniel-j-h You mentioned that this requires us to link in libstdc++/libc++ which is a pain for abi stability and shipping portable binaries. Could you elaborate on those two points a bit? Isn't shipping platform specific binaries pretty standard?

  2. Abstract the threading into a shared interface that is platform agnostic, and then provide platform specific implementations.

For option 2 (abstraction), we could do this in a similar fashion to serial.h + (serial_unix.c or serial_win.c). In this case, we'd have something like sweep_threading.h + (sweep_threading_unix.c or sweep_threading_win.c). This interface would define an abstracted thread, mutex, conditional var, and single producer single consumer queue. Then the methods from the serial and sweep files would make use of the threads and queues via this abstracted interface. The unix implementation could use pthreads. I'm still open to suggestions for the win implementation but it could use WINAPI. Using WINAPI would require the inclusion of <windows.h>. Any complications with the build process or bindings there? @daniel-j-h

What do we think about the pros/cons of both?

Given that the current style of sweepSDK is to avoid global state...

No global state: _s context objects (_s since POSIX reserves _t) hold state and have to be passed explicitly.

how should we store and reference the working queue? I spent a while today trying to implement a queue and a worker thread that conformed to the limitations of no global state and the Pimpl idiom, but I didn't get very far.... I'm still getting the hang of the Pimpl idom :/ @daniel-j-h if you've got a plan for how to accomplish this I'm happy to collaborate however I can.

Examples Error on alternating runs without device reset

Running the examples ./example-c, ./example-c++ or ./example-viewer works the first time, but unless the sensor is reset between each run, the example will error on subsequent attempts in an alternating pattern. Ie: the 2nd attempt will fail, but the 3rd will succeed, 4th will fail, etc

In the case of ./example-c the stated error is:

Error: unable to receive motor speed command response

In the case of ./example-c++ the stated error is:

Error: unable to receive stop scanning command response

And in the case of ./example-viewer the stated error is:

Error: unable to receive start scanning command response

Has this always been the behavior or is it a new issue? I don't believe any changes were made to the protocol for DS or DX receipts. I haven't started tracking down the origin of the issue yet, but I'm assuming the device is not being shutdown properly. Whether its a logical error in the example itself, or an error with libsweep is unclear.

Edit: Looking at the example code, I don't see where we send a DX stop command to the sensor. However, after the example runs the device does stop transmitting serial data (judging from the TX led on the USB Serial adapter which stops blinking). It was my understanding that the device would continue to transmit data regardless of the state of the host port.... so somehow the device is getting that stop command. Is it hidden somewhere I might have overlooked?

If the example does trigger the host to send a DX stop data command before or during the process of shutting down, are the still incoming Data Block receipts handled properly? Recall that after sending a DX command, the host should still expect to receive Data Block receipts until the DX receipt is received, which indicates no further Data Block receipts are coming. Is it possible that the host sends the DX and then immediately shuts down? This might be leaving incoming bytes in the serial buffer that are misinterpreted at the beginning of the example's next execution... producing the observed alternating errors.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.