Skip to content

Instantly share code, notes, and snippets.

@donno
Created July 10, 2020 14:05
Show Gist options
  • Save donno/7e6678826773d5835f0c92741a308cf2 to your computer and use it in GitHub Desktop.
Save donno/7e6678826773d5835f0c92741a308cf2 to your computer and use it in GitHub Desktop.
Python module for working with LVX (File format by Livox)
"""Defines the LVX 1.1.0.0 file format by Livox Tech using the Construct
Python package.
There is sample data provided at https://www.livoxtech.com/downloads
At the time of writing:
- Livox Mid-100 samples use version 1.0.0.0.
- Livox Horizon samples use version 1.1.0.0.
Licence: The MIT License
Basically. the author of this work does not find it is fair to claim ownership
of this work. This is because if you limit yourself to using Construct and the
specification then it leads you down the path that you will end up with what is
here. The choices that remain are the naming of fields, the level of commenting
and whether to use enums or not.
"""
from construct import *
docs = """
LVX format as used by Livox Tech as their point cloud file format.
The file format is based on their LiDAR sensors.
See "Lvx Specifications" on https://www.livoxtech.com/downloads
"""
public_header_block = Struct(
"signature" / Const(b"livox_tech\x00\x00\x00\x00\x00\x00"),
"version" / Byte[4],
"magic_code" / Const(0xAC0EA767, Int32ul),
)
private_header_block = Struct(
"frame_duration" / Int32ul, # Milliseconds. for 1.1.0.0 this is always 50.
"device_count" / Int8ul,
)
device_info = Struct(
"lidar_sn_code" / PaddedString(16, "utf8"),
"hub_sn_code" / PaddedString(16, "utf8"), # If empty there is no hub connecting this LiDAR.
"device_index" / Byte,
"device_type" / Enum(Byte,
LIVOX_HUB = 0,
MID_40_100 = 1, # Mid-40 and MID-100
TEL_15 = 2,
HORIZON = 3,
),
"extrinsic_enable" / Byte,
"roll" / Float32l,
"pitch" / Float32l,
"yaw" / Float32l,
"x" / Float32l,
"y" / Float32l,
"z" / Float32l,
)
# Data type 0 has 100 points per package
data_type_0 = Struct(
"x" / Int32sl, # millimetres
"y" / Int32sl, # millimetres
"z" / Int32sl, # millimetres
"reflectivity" / Byte,
)
# Data type 1 has 100 point per package.
data_type_1 = Struct(
"depth" / Int32sl,
"theta" / Int16ul, # Zenith angle (0.01 degree) 0 to 18000
"phi" / Int16ul, # Azimuth angle (0.01 degree), 0 to 36000
"reflectivity" / Byte,
)
# Data type 2 has 96 point per package.
data_type_2 = Struct(
"x" / Int32sl, # millimetres
"y" / Int32sl, # millimetres
"z" / Int32sl, # millimetres
"reflectivity" / Byte,
"tag" / Byte, # See Livox SDK Procotcol for details about this.
)
# Data type 3 has 96 point per package.
data_type_3 = Struct(
"depth" / Int32sl,
"theta" / Int16ul, # Zenith angle (0.01 degree) 0 to 18000
"phi" / Int16ul, # Azimuth angle (0.01 degree), 0 to 36000
"reflectivity" / Byte,
"tag" / Byte, # See Livox SDK Procotcol for details about this.
)
# TODO: Data types 4 and 5 aren't covered. yet.
# Data type 6 has 1 point per package.
data_type_6 = Struct(
"gyro_x" / Float32l,
"gyro_y" / Float32l,
"gyro_z" / Float32l,
"acc_x" / Float32l,
"acc_y" / Float32l,
"acc_z" / Float32l,
)
frame_header = Struct(
"current_offset" / Int64ul,
"next_offset" / Int64ul,
"frame_index" / Int64ul,
)
assert frame_header.sizeof() == 24
frame = Struct(
"frame_header" / frame_header,
#"packages" / Array(), # Unsure where the number of packages is located.
)
lvx = docs * Struct(
"public_header_block" / public_header_block,
"private_header_block" / private_header_block, # This is valid for 1.1.0.0
"device_block" / Array(this.private_header_block.device_count, device_info),
#"frames" / frame, # Technically there are N frames but the number of frames is unknown.
)
if __name__ == '__main__':
import sys
if len(sys.argv) != 2:
print('usage: %s filename.lvx' % sys.argv[0])
exit(1)
parsed_file = lvx.parse_file(sys.argv[1])
print(parsed_file)
$ python lvx_reader.py HorizonSample.lvx
Container:
public_header_block = Container:
signature = b'livox_tech\x00\x00\x00\x00\x00\x00' (total 16)
version = ListContainer:
1
1
0
0
magic_code = 2886641511
private_header_block = Container:
frame_duration = 50
device_count = 3
device_block = ListContainer:
Container:
lidar_sn_code = u'1HDDGAU00100101' (total 15)
hub_sn_code = u'13UUG1F004001C0' (total 15)
device_index = 0
device_type = (enum) HORIZON 3
extrinsic_enable = 1
roll = 0.4399999976158142
pitch = -0.5699999928474426
yaw = -112.73999786376953
x = 0.0
y = -0.05000000074505806
z = 0.0
Container:
lidar_sn_code = u'1HDDGAU00100231' (total 15)
hub_sn_code = u'13UUG1F004001C0' (total 15)
device_index = 1
device_type = (enum) HORIZON 3
extrinsic_enable = 1
roll = 0.0
pitch = 0.0
yaw = 0.0
x = 0.0
y = 0.0
z = 0.0
Container:
lidar_sn_code = u'1HDDGAU00100271' (total 15)
hub_sn_code = u'13UUG1F004001C0' (total 15)
device_index = 2
device_type = (enum) HORIZON 3
extrinsic_enable = 1
roll = -0.949999988079071
pitch = -0.2800000011920929
yaw = -19.90999984741211
x = 0.04600000008940697
y = -0.699999988079071
z = 0.006000000052154064
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment