C++
To simplify the integration in C++ projects, a C++ CMake Client Library brings header files and CMake support to automate the protocol generation.
Only choose C++ if your application is based on it or the high performance is required. Most of the time, the Python client library is also sufficiently performant and allows a more rapid prototyping & usage. |
Dependencies
-
Protobuf (minimum 3.6.1) with Protobuf Compiler
-
gRPC (minimum 1.16.1) with gRPC Compiler Plugin
In modern linux distributions (Ubuntu 20.04, Debian 10 and greater), the debian packages should be used:
$ sudo apt update
$ sudo apt install -y libgrpc++-dev protobuf-compiler-grpc libprotobuf-dev
For old distributions, the dependencies have to be compiled manually and installed into the system.
How to add it to an existing CMake project
One of the following approaches is recommended as they are tested.
FetchContent
This uses the CMake FetchContent module to populate the source code and target to your project.
include(FetchContent) FetchContent_Declare(blickfeld-qb2 GIT_REPOSITORY https://github.com/Blickfeld/blickfeld-qb2.git GIT_TAG main ) FetchContent_MakeAvailable(blickfeld-qb2)
cmake
The blickfeld-qb2
target is then available in the CMake project.
Install
In this approach, we clone the project, use CMake to configure it, build it and install it globally in the system.
$ git clone https://github.com/Blickfeld/blickfeld-qb2.git
$ mkdir blickfeld-qb2/build
$ cd blickfeld-qb2/build
$ cmake ..
$ make -j
$ sudo make install
The library is now installed in the system and can be found by your CMake project.
find_package(blickfeld-qb2 REQUIRED)
cmake
The blickfeld-qb2
target is then available in the CMake project.
How to use
With the target_link_libraries
statement, the blickfeld-qb2
needs to be linked to your target executable or library.
find_package(blickfeld-qb2 REQUIRED) # or FetchContent add_executable(example main.cpp) target_link_libraries(example PUBLIC blickfeld-qb2)
cmake
Connect to Qb2
In the source code, the library method connect_to_device
can then be used to connect to a Qb2. The resulting connection will be encrypted and authenticated with the supplied application key.
Required arguments are:
-
The hostname or IP-address of the Qb2
-
The serial number of the Qb2
-
An application key configured on the Qb2
#include <blickfeld/hardware/client.h>
int main() {
auto channel = blickfeld::hardware::connect_to_device(
"qb2-xxxxxxx",
"serial-number-xxx",
"application-key-xxxxxxx"
);
..
return 0;
}
cpp
A matching application key for the account which should be authenticated has to be created before. If unsure please read the documentation about application keys in order to understand how to do this first. If the application key is invalid, the first API call on the channel will result in a gRPC-error with code 14 / In the example replace |
The API services provided by Qb2 can then be used with the established channel. Note that the channel can also be reused.
Unauthenticated Connection
If user-management is not enabled on Qb2, an unauthenticated connection can be established. The resulting connection will still be encrypted.
Required arguments are:
-
The hostname or IP-address of the Qb2
#include <blickfeld/hardware/client.h>
int main() {
auto channel = blickfeld::hardware::connect_to_device("qb2-xxxxxxx");
..
return 0;
}
cpp
Establishing unauthenticated connections is deprecated and will be disabled for upcoming Qb2 firmware releases. |
Fetch Point Cloud
With the following code snippet, the established channel is used to fetch one point cloud frame.
#include <blickfeld/hardware/client.h>
#include <blickfeld/core_processing/services/point_cloud.grpc.pb.h>
int main() {
auto channel = blickfeld::hardware::connect_to_device(
"qb2-xxxxxxx",
"serial-number-xxx",
"application-key-xxxxxxx"
);
// Fetch one point cloud frame
auto service = blickfeld::core_processing::services::PointCloud::NewStub(channel);
grpc::ClientContext context;
auto response = service->Get(&context, blickfeld::core_processing::services::PointCloudGetRequest());
std::cout << "Received a frame with the ID " << response.frame().id() << std::endl;
return 0;
}
cpp
Process Point Cloud
The
object contains the response
which contains the binary point cloud data.
The API reference can be found here.frame
For performance reasons, the frame is transported in a binary format. To access the binary fields of the frame, the have to be casted to their corresponding types.
void process_frame(const blickfeld::core_processing::data::Frame& frame) {
// print frame parameters
printf("frame size: %d\n", frame.binary().length());
// access cartesian point data (flat array of floats)
auto xyz = (float*) frame.binary().cartesian().data();
// access photon count / intensity of the points
auto photon_counts = (uint16_t*) frame.binary().photon_count().data();
// access direction_id of the points
auto direction_ids = (uint32_t*) frame.binary().direction_id().data();
// read out x,y,z coordinates, photon count, direction_id of a point in the frame
int pointIndex = 10;
float x = xyz[pointIndex+0];
float y = xyz[pointIndex+1];
float z = xyz[pointIndex+2];
uint16_t photon_count = photon_counts[pointIndex];
uint32_t direction_id = direction_ids[pointIndex];
printf("point: %d, x: %.3f m, y: %.3f m, z: %.3f m, photon count: %u, direction_id: %u\n",
pointIndex, x, y, z, photon_count, direction_id);
}
cpp
Stream Point Clouds
#include <blickfeld/hardware/client.h>
#include <blickfeld/core_processing/services/point_cloud.grpc.pb.h>
int main() {
auto channel = blickfeld::hardware::connect_to_device(
"qb2-xxxxxxx",
"serial-number-xxx",
"application-key-xxxxxxx"
);
// Fetch one point cloud frame
auto service = blickfeld::core_processing::services::PointCloud::NewStub(channel);
grpc::ClientContext context;
blickfeld::core_processing::services::PointCloudStreamResponse response;
auto stream = service->Stream(&context, blickfeld::core_processing::services::PointCloudStreamRequest());
for (int i = 0; i < 10; i++) {
stream->Read(&response);
std::cout << "Received a frame with the ID " << response.frame().id() << std::endl;
process_frame(response.frame());
}
// Stop stream
context.TryCancel();
return 0;
}
cpp
$ ./cpp/examples/stream_point_clouds/stream_point_clouds-example
Received a frame with the ID 27
frame size: 144395
point: 10, x: 2.238 m, y: -0.269 m, z: -2.248 m, photon count: 2343, direction_id: 10
Received a frame with the ID 28
frame size: 144398
point: 10, x: 2.242 m, y: -0.269 m, z: -2.227 m, photon count: 2158, direction_id: 10
...
The source code of the examples can be found in the examples section of the Git Repository.
Please refer to the gRPC C++ documentation and further guides & examples in this documentation.