This project contains scripts written in Python which receive and parse scan segments from SICK LiDAR sensors, either in the Compact format telegram type 1 (recommended) or in the MSGPACK format. The transport protocols UDP and TCP are supported.
Table of contents
- Supported product families
- Introduction
- Prerequisites
- Project setup
- Quick start
- Executing Python scripts in a Poetry environment
- Using the command line interface scansegmentapi cli
- Using the ScanSegmentAPI from Python
- Examples
- Compact format telegram type 1 & MSGPACK API mapping table
- Hints on sensor and network configuration
- Extracting data packages from a TCP stream
- Dependencies
- multiScan100 (e.g. 1131164)
- picoScan100 (e.g. 1134610)
- LRS4000 (e.g. 1098855)
The scripts which do the actual parsing are located in scansegmentapi/msgpack.py and scansegmentapi/compact.py respectively. They are written and documented with the intention to support the understanding of the two data formats. The scripts are not optimized for performance and not intended to be used in productive code.
To quickly test that data from a SICK LiDAR sensor is received successfully on your client, the command line tool scansegmentapi_cli.py can be used. Hints for the configuration of the sensor and the network of the client PC can be found in the sensor and network configuration hints.
To use the parsers in a python script, this package can simply be imported as shown in the examples below.
Finally, sample binary files with MSGPACK data and Compact format telegram type 1 data are provided in tests/sample_files. They can be used to check whether the parsing results of your own parser match with the results of the scripts provided here. Some documentation on the sample binary files is found in tests/sample_files/README.md.
Install Poetry, the tool this project uses for dependency and virtual-environment management. Follow the official installation guide for your platform.
Note
Windows: Poetry not found after install The installer may not update your PATH automatically. Add the it manually and reopen the terminal.
Open a terminal and change into folder:
cd scansegmentapiNote
pyproject.toml is Poetry's project configuration file (lists dependencies, Python version, etc.). It lives in this same folder. Every poetry command must be run from here, otherwise Poetry cannot find the project.
Install the dependencies:
poetry installAfter running poetry install you can immediately verify your setup without a physical sensor by parsing the included sample files:
poetry run python scansegmentapi_cli.py read compact -i ./tests/sample_files/sample.compact
poetry run python scansegmentapi_cli.py read msgpack -i ./tests/sample_files/sample.msgpackIf both commands print scan-segment data without errors, your installation is working correctly.
In general all Python scripts must be executed via Poetry either in a single command:
poetry run python <script1>
poetry run python <script2>
...You can always view help with the built-in help option --help, -h:
poetry run python scansegmentapi_cli.py -hThis will display a list of the available subcommands. Further help on the subcommands and their options can be retrieved using:
poetry run python scansegmentapi_cli.py <subcommand> -hIf no device is at hand, one of the provided sample files can be used to test if the project was correctly set up using
the read subcommand.
poetry run python scansegmentapi_cli.py read msgpack -i ./tests/sample_files/sample.msgpack
poetry run python scansegmentapi_cli.py read compact -i ./tests/sample_files/sample.compactTo receive segments sent by a SICK Lidar sensor the receive subcommand has to be used as follows:
poetry run python scansegmentapi_cli.py receive compact # or 'msgpack' respectively.By default this command listens on localhost at port 2115 for incoming data via UDP.
If the device is located in a different subnetwork the ip address to use for listening can be changed using the --ip command-line option.
Note that depending on the transport protocol, the ip address is the ip address of the network adapter of the client PC, if UDP is used, or the ip address of the sensor, if TCP is used.
The port can be changed using -p or --port flag:
poetry run python scansegmentapi_cli.py receive compact --ip 192.168.0.100 --port 2115The transport protocol can be selected between UDP and TCP using the --protocol argument. The default is UDP. For the protocols supported by your device refer to the manual.
Warning
After running receive, the command silently waits for incoming data and produces no output until the first segment arrives. If nothing appears after a few seconds, the sensor is likely not yet sending data to this host. Refer to the Hints on sensor and network configuration section for common causes and fixes (wrong IP address, wrong port, UDP firewall category, wrong data format).
To receive data from a SICK Lidar sensor in a Python script, the ScanSegmentAPI can be imported and the parsers can be instantiated.
Example for MSGPACK via UDP where the address 192.168.0.100 is the ip address of the network adapter of the client PC (not of the sensor!) and 2115 is the used port.
import scansegmentapi.msgpack as MSGPACKApi
from scansegmentapi.udp_handler import UDPHandler
if __name__ == "__main__":
transportLayer = UDPHandler("192.168.0.100", 2115, 65535)
receiver = MSGPACKApi.Receiver(transportLayer)
(segments, frameNumbers, segmentCounters) = receiver.receive_segments(200)
receiver.close_connection()Example for Compact format telegram type 1 via UDP where the address 192.168.0.100 is the ip address of the network adapter of the client PC (not of the sensor!) and 2115 is the used port.
import scansegmentapi.compact as CompactApi
from scansegmentapi.udp_handler import UDPHandler
if __name__ == "__main__":
transportLayer = UDPHandler("192.168.0.100", 2115, 65535)
receiver = CompactApi.Receiver(transportLayer)
(segments, frameNumbers, segmentCounters) = receiver.receive_segments(200)
receiver.close_connection()Example for Compact format telegram type 1 via TCP where the address 192.168.0.1 is the ip address of the sensor (not of the client PC!) and 2115 is the used port of the sensor.
import scansegmentapi.compact as CompactApi
from scansegmentapi.tcp_handler import TCPHandler
from scansegmentapi.compact_stream_extractor import CompactStreamExtractor
if __name__ == "__main__":
streamExtractor = CompactStreamExtractor()
transportLayer = TCPHandler(streamExtractor, "192.168.0.100", 2115, 1024)
receiver = CompactApi.Receiver(transportLayer)
(segments, frameNumbers, segmentCounters) = receiver.receive_segments(200)
receiver.close_connection()Note
The above snippets use the package import path scansegmentapi.X (e.g. scansegmentapi.compact), which is correct when the package is installed via poetry install or when your script is placed outside the scansegmentapi source directory.
More examples of the usage of the ScanSegmentAPI can be found in the examples directory.
The following table shows all information contained in a MSGPACK or Compact format telegram type 1 UDP package as described in the chapters "MSGPACK format" and "Compact format telegram type 1 format" of the data format description PDF which can be downloaded from sick.com.
Legend:
- E stands for a specific echo number
- M stands for a specific module number
- SC stands for a specific scan number within a module, which is used in the Compact format telegram type 1 format
- SM stands for a specific scan number within a segment, which is used in the MSGPACK format
- B stands for a specific beam
- segment is a single received segment
| Compact format telegram type 1 | Data access to Compact format telegram type 1 packages using the ScanSegmentAPI | MSGPACK | Data access to MSGPACK packages using the ScanSegmentAPI |
|---|---|---|---|
| Header: StartOfFrame | - | Framing: StartOfFrame | - |
| Header: CommandId | segment["CommandId"] | - | - |
| - | - | Framing: MSGPACK buffer | - |
| Header: TelegramCounter | segment["TelegramCounter"] | ScanSegment: TelegramCounter | segment["TelegramCounter"] |
| Header: TimeStampTransmit | segment["TimestampTransmit"] | ScanSegment: TimeStampTransmit | segment["TimestampTransmit"] |
| Header: TelegramVersion | segment["Version"] | - | - |
| Header: SizeModule0 | - | - | - |
| MetaData Module M: SegmentCounter | segment["Modules"][M]["SegmentCounter"] | ScanSegment: SegmentCounter | segment["SegmentCounter"] |
| MetaData Module M: FrameNumber | segment["Modules"][M]["FrameNumber"] | ScanSegment: FrameNumber | segment["FrameNumber"] |
| MetaData Module M: SenderId | segment["Modules"][M]["SenderId"] | ScanSegment: SenderId | segment["SenderId"] |
| MetaData Module M: NumberOfLinesInModule | segment["Modules"][M]["NumberOfLinesInModule"] | - | - |
| MetaData Module M: NumberOfBeamsPerScan | segment["Modules"][M]["NumberOfBeamsPerScan"] | Scan SM: BeamCount | segment["SegmentData"][SM]["BeamCount"] |
| MetaData Module M: NumberOfEchosPerBeam | segment["Modules"][M]["NumberOfEchosPerBeam"] | Scan SM: EchoCount | segment["SegmentData"][SM]["EchoCount"] |
| MetaData Module M: TimeStampStart | segment["Modules"][M]["TimestampStart"][SC] | Scan SM: TimeStampStart | segment["SegmentData"][SM]["TimestampStart"] |
| MetaData Module M: TimeStampStop | segment["Modules"][M]["TimestampStop"][SC] | Scan SM: TimeStampStop | segment["SegmentData"][SM]["TimestampStop"] |
| MetaData Module M: Phi | segment["Modules"][M]["Phi"][SC] | Scan SM: ChannelPhi | segment["SegmentData"][SM]["Phi"] |
| MetaData Module M: ThetaStart | segment["Modules"][M]["ThetaStart"][SC] | Scan SM: ThetaStart | segment["SegmentData"][SM]["ThetaStart"] |
| MetaData Module M: ThetaStop | segment["Modules"][M]["ThetaStop"][SC] | Scan SM: ThetaStop | segment["SegmentData"][SM]["ThetaStop"] |
| DistanceScanlingFactor | segment["Modules"][M]["DistanceScalingFactor"] | - | - |
| MetaData Module M: NextModuleSize | - | - | - |
| MetaData Module M: Reserved 1 | - | - | - |
| MetaData Module M: DataContentEchos | segment["Modules"][M]["DataContentEchos"] | - | - |
| MetaData Module M: DataContentBeams | segment["Modules"][M]["DataContentBeams"] | - | - |
| MetaData Module M: Reserved2 | - | - | - |
| MeasurementData Module M Beam B Echo E: Distance | segment["Modules"][M]["SegmentData"][SC]["Distance"][E ][B] | Scan SM: DistValues E B | segment["SegmentData"][SM]["Distance"][E ][B] |
| MeasurementData Module M Beam B Echo E: RSSI | segment["Modules"][M]["SegmentData"][SC]["Rssi"][E ][B] | Scan SM: RssiValues E B | segment["SegmentData"][SM]["Rssi"][E ][B] |
| MeasurementData Module M Beam B: Properties | segment["Modules"][M]["SegmentData"][SC]["Properties"][B ] | Scan SM: PropertyValues E B | segment["SegmentData"][SM]["Propertiesv"][B] |
| MeasurementData Module M Beam B: Theta | segment["Modules"][M]["SegmentData"][SC]["ChannelTheta"][B ] | Scan SM: ChannelTheta E B | segment["SegmentData"][SM]["ChannelTheta"] |
| - | - | ScanSegment: Availability | segment["Availability"] |
| - | - | ScanSegment: LayerId | segment["LayerId"][SM] |
| - | - | Scan SM: ScanNumber | segment["SegmentData"][SM]["ScanNumber"] |
| - | - | Scan SM: ModuleId | segment["SegmentData"][SM]["ModuleID"] |
| - | - | Framing: MSGPACK buffer size | - |
| Framing: CRC | - | Framing: CRC | - |
If no data can be received from the device or errors are reported in the data stream, here are some hints how to solve common problems.
If no data is received at all when using UDP, make sure that the correct port and the correct IP address of the client PC are configured on the sensor.
If no data is received at all when using TCP, make sure that the correct port and the correct IP address of the sensor is configured in the python script.
If UDP is used as the transport protocol on Windows PCs the network category of the used network adapter may need to be configured to 'Private'. To do so, open the Windows Powershell with administrator privileges and check the network category and the interface index of the network adapter which is used for communication with the Lidar sensor.
Then set the network category to 'Private' for the corresponding interface using the following command:
Set-NetConnectionProfile -InterfaceIndex <index as retrieved> -NetworkCategory Private
If data is received but a CRC check error is shown as output of the Python script, a reason might be that the wrong output data format is configured on the sensor. Make sure that MSGPACK output is configured when using the MSGPACK Python API and Compact format telegram type 1 is configured when using the Compact format telegram type 1 Python API.
In the case of UDP the extraction of the data from the UDP packet is straight forward because each UDP packet contains at most one measurement data package. In the case of TCP the measurement data packages have to be extracted from the TCP stream. In other words the TCP stream has to be split up into separate measurement data packages. For the Compact format telegram type 1 and MSGPACK protocol the data packages start with a delimiter and end with a checksum. The extraction of data packages from the TCP stream is implemented using state machines.
The state machine for the Compact format telegram type 1 format:
stateDiagram-v2
WAITING_FOR_STX --> WAITING_FOR_HEADER : STX received
WAITING_FOR_HEADER --> WAITING_FOR_MODULE_DATA : header received
WAITING_FOR_MODULE_DATA --> WAITING_FOR_MODULE_DATA : module data received and next_module_size != 0
WAITING_FOR_MODULE_DATA --> WAITING_FOR_CRC : module data received and next_module_size == 0
WAITING_FOR_CRC --> WAITING_FOR_STX : CRC received
The state machine for the MSGPACK format:
stateDiagram-v2
WAITING_FOR_STX --> WAITING_FOR_SIZE : STX received
WAITING_FOR_SIZE --> WAITING_FOR_CRC : size received
WAITING_FOR_CRC --> WAITING_FOR_STX : payload and crc received
This module relies on the following dependencies which are downloaded during the build process.
| Name | Version | License |
|---|---|---|
| certifi | 2025.8.3 | Mozilla Public License 2.0 (MPL 2.0) |
| charset-normalizer | 3.4.2 | MIT License |
| colorama | 0.4.6 | BSD-3-Clause License |
| coverage | 7.10.2 | Apache-2.0 License |
| exceptiongroup | 1.3.0 | MIT License |
| idna | 3.10 | BSD-3-Clause License |
| iniconfig | 2.1.0 | MIT License |
| msgpack | 1.1.1 | Apache-2.0 License |
| numpy | 2.0.2 | BSD-3-Clause License |
| packaging | 25.0 | Apache-2.0 License; BSD-3-Clause |
| pluggy | 1.6.0 | MIT License |
| pytest | 7.4.4 | MIT License |
| pytest-cov | 4.1.0 | MIT License |
| requests | 2.32.4 | Apache-2.0 License |
| scansegmentdecoding | 2.0.3 | MIT License |
| typing_extensions | 4.14.1 | PSF-2.0 |
| urllib3 | 2.5.0 | MIT License |


