Real-time Data
While scanning, the device actively reports the real-time point cloud and pose and pushes a camera video stream over RTSP. This page explains how to receive and decode them.
Real-time point cloud
The real-time point cloud is reported on lixel/application/report/lio_pcl as a MqttCompressMsg. To reduce the transfer size, the point cloud data is compressed.
message LioPclReport {
message Point {
sint64 x = 1; // val = raw value * header.scaler
sint64 y = 2; // val = raw value * header.scaler
sint64 z = 3; // val = raw value * header.scaler
uint32 rgbi = 4; // R + G + B + Intensity
}
Header header = 1;
repeated Point points = 2; // point cloud data
}
message MqttCompressMsg {
enum CompressionType {
CompressionLz4 = 0;
CompressionZstd = 1;
}
Header header = 1;
CompressionType compression = 2; // compression type
uint32 compressed_size = 3; // size before compression (serialized LioPclReport size)
bytes compressed_data = 4; // compressed data (compressed serialized LioPclReport)
}
Decoding steps
After receiving a MqttCompressMsg, decompress and decode it in two steps:
- Deserialize
MqttCompressMsgto getcompression(Lz4 / Zstd) andcompressed_data. - Decompress
compressed_databy compression type.CompressionLz4is the LZ4 block format, not the LZ4 frame format; decompression needs the decompressed length, i.e.compressed_size(the size before compression). In Python:lz4.block.decompress(data, uncompressed_size=compressed_size). Usinglz4.frame.decompressfails withERROR_frameType_unknown. - Deserialize the result as
LioPclReportto obtain the point array.
Coordinate reconstruction: the device multiplies the real coordinate by header.scaler and truncates toward zero (not rounding), then sends it as sint64; the reconstructed error is less than 1/scaler. To reconstruct, divide the wire integer by header.scaler, i.e. x_real = int(x) / int(header.scaler), and likewise for y, z. The device was measured to send scaler as 1000, so the wire integer 2921 maps to 2.921. By the protobuf JSON mapping, a sint64 becomes a string in the dict, so convert it to an integer before arithmetic. The raw value in the proto comment above means the real coordinate, not the wire integer; that is, wire integer = real coordinate × header.scaler. The device-side code fixes scaler to 1000; check that scaler is not 0 before dividing.
rgbi is a 32-bit unsigned integer; from the high byte to the low byte it is R, G, B, Intensity, 8 bits each (based on the order in the device-side packing code and the proto comment, not verified point-by-point against actual colors).
Note: decompression needs extra dependencies. When using the project example scripts, install
pip install lz4 zstandardto decode the point cloud; without them the script prints a hint and skips it. The example scriptpython/example/usage/lio_pcl.pyuses LZ4 block decompression from 1.0.1 on; the 1.0.0 script decompressed as frame and failed to decode every point cloud frame received on a real device, without printing any error.
Real-time pose
The real-time pose is reported on lixel/application/report/lio_pose as a LioPoseReport:
message LioPoseReport {
Header header = 1;
PoseStamped pose = 2;
float distance = 3; // distance (meters)
float pose_accuracy = 4; // not used yet
}
The PoseStamped structure holds a timestamp and the pose, which consists of a position (Point) and an orientation (Quaternion):
message PoseStamped {
sint64 stamp = 1;
Pose pose = 2;
}
message Pose {
Point position = 1; // x / y / z
Quaternion orientation = 2; // x / y / z / w
}
RTSP video stream
The device has a built-in RTSP server. After scanning starts successfully, it pushes a camera video stream in real time, at most one stream at a time. The URL format:
rtsp://<lixel_device_ipaddr>:8554/live/chn_<camera_name>
Examples:
rtsp://192.168.55.1:8554/live/chn_leftrtsp://192.168.55.1:8554/live/chn_right
| Parameter | Description |
|---|---|
lixel_device_ipaddr | Device IP: fixed at 192.168.55.1 for wired; obtained over Bluetooth for wireless; fixed at 192.168.56.1 in Quick Connect mode |
camera_name | Camera name, obtained via the device info request (DeviceInfoResponse.device_info.camera_info.camera_list). The list may contain empty strings (e.g. ["left", "", "right"]); the device puts an empty string where the camera for that slot does not exist or has no preview enabled, so skip it when building the URL |