Current version:
Version | Date | Changes |
---|---|---|
0.9.4 | 09/19/2024 | Update Panic timestamp meaning |
0.9.3 | 07/09/2024 | Correct INFO V1 common header order |
0.9.2 | 05/29/2024 | Update to the PANIC packet offsets |
0.9.1 | 01/19/2024 | Update INFO and PANIC packet format |
0.9 | 07/19/2023 | Define frame aggregation behavior |
0.8.3 | 08/27/2022 | Define blooming and blocked bits |
0.8.2 | 03/02/2022 | Functional safety clarifications |
0.8.1 | 01/27/2022 | Add noise bit in point flag |
0.8 | 01/11/2022 | Clarify SDK reserved bits |
0.7.1 | 01/10/2022 | Draft document, add v2 point header |
0.7 | 08/21/2021 | Draft document, add v1 point data |
0.6 | 07/14/2021 | Draft document, not finalized |
This document outlines the data communication protocol for Vista-X and Nova series of lidars produced by Cepton.
There are three general type of communications supported by Cepton's lidar sensors:
Point data is accumulated as the lidar measurements happen. Once a full packet is filled the data will be sent out.
Point data is a stream of measurements following strict order:
0
.SecondReturn
flag will be set and relative timestamp is always 0
.In the payload of the UDP packet, after the 20 bytes header, each point is contiguously placed based on the point size. In V0 of point data, there are up to 143 points of 10 bytes each. In V1, there are up to 120 points of 12 bytes each.
Offset | Block | Size | Description |
---|---|---|---|
0 | Header | 20/24 | Point Data Packet Header |
20 | Point0 | Point #0 | |
Point1 | Point #1 | ||
... | ... | ... | ... |
PointN | Point #N |
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | Signature | 4 | STDV | 4 byte signature to identify packet type |
4 | Header Version | 1 | 1/2 | Header type |
5 | Header Size | 1 | Header size | |
6 | Flags | 2 | 0 | Flags |
8 | Timestamp | 8 | Reference timestamp | |
16 | Point Version | 1 | 0/1 | Point structure type |
17 | PointSize | 1 | Point structure size | |
18 | PointCount | 2 | 144/120* | Number of points in this packet |
20 | Sequence ID | 4 | Version 2+: Sequencing ID for packet stream |
(*When there aren't enough points to fill all 144 or 120 possible points in the UDP Packet, this number can be smaller, and the remaining space is filled with empty points (all values equal to 0). )
Corresponding C structure:
struct CeptonPointDataHeader {
uint32_t signature;
uint8_t header_version;
uint8_t header_size;
uint16_t flags;
uint64_t timestamp_usec;
uint8_t point_version;
uint8_t point_size;
uint16_t point_count;
uint32_t sequence_id;
};
Reference timestamp holds the reference time for the first point. This is the number of microseconds (us) since the sensor boot up time. To convert this timestamp to universal time, please reference INFO packet description.
Flags: There is no packet level flags defined. This byte has to be 0 at all time.
Offset | Field | Size | Unit | Range | Description |
---|---|---|---|---|---|
0 | X | 2 | 0.5cm | -163.840m to 163.835m | X coordinate |
2 | Y | 2 | 0.5cm | 0 to 327.68m | Y coordinate |
4 | Z | 2 | 0.5cm | -163.840m to 163.835m | Z coordinate |
6 | Reflectivity | 1 | % | 0 to 255 | Reflectivity |
7 | Timestamp | 1 | us | 0 to 255 | Time difference from the point before |
8 | Channel | 1 | 0 to 63 | Channel ID | |
9 | Flags | 1 | Flags | ||
10+ | Internal | When PointSize > 10, these are internal data |
Corresponding C structure:
struct CeptonPointData {
int16_t x;
uint16_t y;
int16_t z;
uint8_t reflectivity;
uint8_t relative_timestamp;
uint8_t channel_id;
uint8_t flags;
};
Coordinates: Looking from the sensor bore-sight,
Coordinates X and Z are signed short, while Y is unsigned. The reference point (0,0,0) is located at the geometric center of the sensor, which is at the mid-point of all 3 primary dimensions of the sensor enclosure.
Reflectivity: 0-100% reflectivity are measured by assuming Lambertian reflection model. Non-Lambertian materials, such as retro-reflective surfaces commonly found in road signs and warning cones, can produce higher than 100% reflectivity. For reflectivity less than 127%, intensity is equal to reflectivity. For reflectivity greater than or equal to 127%, we convert to intensity exponentially using quantize_table[reflectivity-127]
:
private static float[] quantize_table = {
127.0f, 130.7f, 134.5f, 138.4f, 142.4f, 146.6f, 150.9f, 155.3f,
159.8f, 164.4f, 169.2f, 174.1f, 179.2f, 184.4f, 189.8f, 195.3f,
201.0f, 206.9f, 212.9f, 219.1f, 225.4f, 232.0f, 238.8f, 245.7f,
252.9f, 260.2f, 267.8f, 275.6f, 283.6f, 291.9f, 300.4f, 309.1f,
318.1f, 327.4f, 336.9f, 346.7f, 356.8f, 367.2f, 377.9f, 388.9f,
400.2f, 411.9f, 423.9f, 436.2f, 448.9f, 462.0f, 475.4f, 489.2f,
503.5f, 518.1f, 533.2f, 548.8f, 564.7f, 581.2f, 598.1f, 615.5f,
633.4f, 651.9f, 670.8f, 690.4f, 710.5f, 731.1f, 752.4f, 774.3f,
796.9f, 820.1f, 843.9f, 868.5f, 893.8f, 919.8f, 946.6f, 974.1f,
1002.5f, 1031.7f, 1061.7f, 1092.6f, 1124.4f, 1157.2f, 1190.9f, 1225.5f,
1261.2f, 1297.9f, 1335.7f, 1374.6f, 1414.6f, 1455.8f, 1498.2f, 1541.8f,
1586.6f, 1632.8f, 1680.4f, 1729.3f, 1779.6f, 1831.4f, 1884.8f, 1939.6f,
1996.1f, 2054.2f, 2114.0f, 2175.5f, 2238.9f, 2304.0f, 2371.1f, 2440.1f,
2511.2f, 2584.3f, 2659.5f, 2736.9f, 2816.6f, 2898.6f, 2983.0f, 3069.8f,
3159.2f, 3251.1f, 3345.8f, 3443.2f, 3543.4f, 3646.6f, 3752.7f, 3862.0f,
3974.4f, 4090.1f, 4209.2f, 4331.7f, 4457.8f, 4587.6f, 4721.1f, 4858.6f,
5000.0f};
Timestamp: This value is the relative value from the time since last point in the packet. For the very first point, it is time since the packet header's reference time. Notice that 0
value is possible for dual firing mode or dual return's second return. The timestamp represents the time of firing, for the time accuracy afforded (> 1us, ~300m traveled), it is OK to use directly as target point time (time when target is hit by laser).
Channel ID: Reports the channel ID, which usually indicate a distinct laser->APD pathway. Each channel correspond to a unique set of laser/APD pair.
Flags: per-point flags are defined in the table below
Bit | Value | Name | Description |
---|---|---|---|
0 | 1 | Saturated | Set if the point is saturated |
1 | 2 | Reserved | Reserved for SDK use |
2 | 4 | FrameParity | Frame parity bit |
3 | 8 | Reserved* | Reserved for SDK use |
4 | 16 | SecondReturn | Set for second return |
5 | 32 | NoReturn | Set if this point has no return |
6 | 64 | Noise | Set if this point is a noise |
7 | 128 | Blocked | Set if this point is blocked |
(*The reserved bit#3 is defined in SDK as frame boundary. This is a software flag created by SDK and is not part of the communication protocol. Refer to Cepton SDK documentation for details)
Corresponding flags and enumerations
enum {
CEPTON_POINT_SATURATED = 1 << 0,
CEPTON_POINT_FRAME_PARITY = 1 << 2,
CEPTON_POINT_SECOND_RETURN = 1 << 4,
CEPTON_POINT_NO_RETURN = 1 << 5,
CEPTON_POINT_NOISE = 1 << 6,
CEPTON_POINT_BLOCKED = 1 << 7,
};
Saturated flag: Set if the signal received is too strong to be correctly measured. This usually happen when measuring retro-reflective targets. When this flag is set, the reflectivity is not trust worthy, and the distance accuracy is slightly reduced.
There are also some unspecified conditions that can be derived from the data stream itself:
FrameParity: Cleared for even frames and set for odd frames. This ensures the frame boundary as identified by the sensor is always communicated even when one of the data packet is lost.
Dual return case: If two consecutive points have same ChannelID
and second point has Timestamp
set to 0
. The second point is the second return of the same laser firing. Second return signifies that the fired laser was somehow split up (e.g. hitting a corner of a near wall, then hitting a wall farther down). In this case, the strongest is always the first and farthest return can be determined by comparing Y
value.
Blocked points: Some Cepton lidar products can detect blockage and report them. When blockage happens, there might still be a valid return. The distance of the return can be trusted but the reflectivity should be considered incorrect.
Point data packets can be turned off completely.
For sensors that support turning on or off the second return feature, point data structure will not change. The second return, when present, will just be a repeat of the same laser channel, with time offset 0
and SecondReturn
flag set.
Offset | Block | Size | Description |
---|---|---|---|
0 | Header | 76 / 96 | INFO packet header |
76 / 96 | Diagnosis Signals | Variable | Sensor specific diagnosis data |
INFO packet header is universal data for all Cepton sensors
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | Signature | 4 | INFZ | 4 byte signature to identify packet type |
4 | HeaderMagic | 4 | 4 bytes for future compatibility | |
8 | Model | 4 | Model number | |
12 | SerialNumber | 4 | Sensor serial number | |
16 | FirmwareVersion | 4 | Firmware version number | |
20 | ModelName | 28 | UTF-8 string for model name | |
48 | PartNumber | 4 | Part Number |
For V0 info block: HeaderMagic
has value of 0x004C
, the total header size is 76 bytes, with the second half look like this:
Offset | Field | Size | Value | Description |
---|---|---|---|---|
52 | PowerUpTime | 8 | Microseconds since power up | |
60 | TimeSyncOffset | 8 | Power up epoch time in microseconds | |
68 | TimeRateCorrection | 4 | Clock drift correction | |
72 | TimeSyncStatus | 1 | Time synchronization details | |
73 | Reserved | 3 | Reserved for future use |
Corresponding c structure:
struct CeptonInfoHeader_V0 {
uint32_t signature;
uint32_t header_magic;
uint32_t model;
uint32_t serial_number;
uint32_t firmware_version;
char model_name[28];
uint32_t part_number;
uint64_t power_up_time;
uint64_t time_sync_offset;
uint32_t time_rate_correction;
uint8_t time_sync_status;
uint8_t reserved[3];
};
For V1 info block: HeaderMagic
has value of 0x0860
, the total header size is 96 bytes, with the second half look like this:
Offset | Field | Size | Value | Description |
---|---|---|---|---|
52 | FirmwareVersionAPM | 4 | Internal APM firmware version | |
56 | FirmwareVersionIOX | 4 | Internal IOX firmware version | |
60 | FirmwareVersionIguana | 4 | Internal Iguana firmware version | |
64 | PowerUpTime | 8 | Microseconds since power up | |
72 | TimeSyncOffset | 8 | PTP master time offset in microseconds | |
80 | TimeRateCorrection | 4 | Clock drift correction | |
84 | TimeSyncStatus | 1 | Time synchronization status | |
85 | ReturnMode | 1 | Duel return mode enum | |
86 | ThermalDerateState | 1 | Thermal derating enum | |
87 | Reserved | 1 | ||
88 | Temperature | 2 | Sensor temperature | |
90 | ChannelCount | 2 | Channel count number | |
92 | FaultSummary | 4 | Fault cases summarization |
Corresponding c structure:
struct CeptonInfoHeader_V1 {
uint32_t signature;
uint32_t header_magic;
uint32_t model;
uint32_t serial_number;
uint32_t firmware_version;
char model_name[28];
uint32_t part_number;
uint32_t firmware_version_apm;
uint32_t firmware_version_iox;
uint32_t firmware_version_iguana;
uint64_t power_up_time;
uint64_t time_sync_offset;
uint32_t time_sync_correction;
uint8_t time_sync_status;
uint8_t return_mode;
uint8_t thermal_derate_state;
uint8_t reserved;
uint16_t temperature;
uint16_t channel_count;
uint32_t fault_summary;
};
SDK provides a few methods to aggregate points such that the point cloud creates a frame. Two types of aggregation are available:
The lidar has a built-in notion of when a frame starts and ends. This is signified by the parity on points (shown in the CEPTON_POINT_FRAME_PARITY
bit from the point flag). When the parity changes, the frame aggregator would then note to finish off the frame and start a new one.
Frame aggregation utilizes this mode as the default.
When point packets are dropped, this mode will robustly still detect a frame change. In the unlikely event that a whole frame of the opposite parity is dropped, the aggregator will produce two frames of the same parity with variable results on what the frame looks like visually, but then resume normal operation.
Users of the SDK can specify the period of time a frame should last before switching to a new frame by specifying it in microseconds (minimum 1000).
This should be passed when listening to frames via the CeptonListenFrames
function call.
At a given start timestamp, the internal frame aggregator would increment its time by adding on each point's relative timestamp value. When the starting timestamp reaches the fixed frame duration (or reach an internal buffer limit), it then takes the most recent point cloud packet's start time, adds on the aggregate time elapsed from the points taken in the packet, and restarts the time and frame aggregation from that point.
Note that based on this implementation, if any point packets get lost, the next incoming points will assume a timestamp earlier compared to its presumed ground truth timestamp until the current frame finishes. When the frame is complete, then the next frame will adjust accordingly to the current timestamp again.
There are several built-in mechanisms to ensure the data sanity of the point cloud stream coming out of the lidar:
In addition, when any faults happen inside the sensor, the sensor will send out a "panic message" to inform the host as soon as possible.
Panic packet format:
Offset | Field | Size | Description |
---|---|---|---|
0 | Header | 12 | PANIC packet header |
12 | Active Fault | 24 | Active fault detailed information |
Panic packet header format:
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | Magic | 4 | PANC | 8 bytes header magic signature |
4 | Serial Number | 4 | Sensor serial number | |
8 | Sequence Id | 2 | Continuous increasing sequence id | |
10 | Reserved | 2 | Reserve for future use |
Panic packet active fault format:
Offset | Field | Size | Value | Description |
---|---|---|---|---|
0 | Fault Identity | 4 | 4 bytes field for unique fault case identification | |
8 | Life Counter | 4 | Count of how many of this panic have been sent | |
12 | Lidar Timestamp | 8 | Microseconds since power on. Matches point data header | |
14 | Reserved | 8 | Reserve for future use |
packet structure:
struct panic_packet {
uint32_t signature;
uint32_t serial_number;
uint16_t sequence_id;
uint16_t reserved;
struct {
uint32_t fault_identity;
uint32_t life_counter;
uint64_t ptp_timestamp;
uint64_t reserved;
} active_fault;
};