# Olei LiDAR Sensor - Implementation Notes ## Tổng Quan Project này cung cấp một high-performance server để nhận và phân giải dữ liệu từ Olei LiDAR Sensor (LR-1F / LR-1BS) qua giao thức UDP/IP. ## Kiến Trúc ### 1. Data Structures #### `LidarHeader` (40 bytes) - Struct với `StructLayout(LayoutKind.Sequential, Pack = 1)` để đảm bảo layout memory chính xác - Chứa thông tin frame ID, protocol version, distance scale, rotation rate, error status, v.v. - Các property helper để dễ dàng truy cập thông tin (RotationRate, IsCounterClockwise, HasError, etc.) #### `LidarDataBlock` (8 bytes) - Struct cho mỗi measurement point - Chứa angle (0-35999 = 0°-359.99°), distance, signal strength - Methods để chuyển đổi sang degrees/radians và Cartesian coordinates (X, Y) #### `LidarDataPacket` (1240 bytes total) - Class chứa header + 150 data blocks - Methods để lọc và iterate qua valid data points - Hỗ trợ conversion sang Cartesian coordinates ### 2. Parser #### `LidarPacketParser` - **Zero-allocation parsing** sử dụng `Span` và `ReadOnlySpan` - Sử dụng `MemoryMarshal.Read()` cho fast struct deserialization - Validation methods để kiểm tra packet nhanh mà không cần parse toàn bộ - Static class - không cần instantiation ### 3. Server #### `OleiLidarServer` - **Dedicated Thread** với `ThreadPriority.AboveNormal` cho receiving - **ArrayPool** để reuse buffers, giảm GC pressure - **ConcurrentBag** để pool LidarDataPacket objects cho reuse - **Event-based callbacks** cho data và error handling - Thread-safe operations với `Interlocked` cho counters - Proper `IDisposable` implementation cho resource cleanup ## Tối Ưu Hiệu Suất ### 1. Memory Optimization #### Buffer Pooling ```csharp private readonly ArrayPool _bufferPool = ArrayPool.Shared; byte[] buffer = _bufferPool.Rent(size); // ... use buffer ... _bufferPool.Return(buffer); ``` #### Packet Pooling (Automatic) ```csharp private readonly ConcurrentBag _packetPool; // Get from pool or create new if (!_packetPool.TryTake(out var packet)) packet = new LidarDataPacket(); // ... use packet ... // Automatically returned to pool after event handlers complete // User doesn't need to call ReturnPacketToPool! ``` ### 2. Zero-Allocation Parsing #### Using Span ```csharp public static bool TryParse(ReadOnlySpan data, LidarDataPacket packet) { // No allocation - direct memory access header = MemoryMarshal.Read(data); } ``` ### 3. Thread Optimization #### Dedicated Receive Thread - Thread riêng biệt với priority cao chỉ cho receiving - Tránh context switching và latency - Non-blocking với small sleep để prevent busy-waiting ```csharp _receiveThread = new Thread(ReceiveLoop) { Name = "OleiLidar-Receive", IsBackground = true, Priority = ThreadPriority.AboveNormal }; ``` ### 4. Struct vs Class - **Struct** cho `LidarHeader` và `LidarDataBlock`: Stack allocation, no GC - **Class** cho `LidarDataPacket`: Chứa array lớn, pooling cho reuse ## Usage Pattern ### Basic Usage ```csharp using var server = new OleiLidarServer(2368); server.DataReceived += (sender, e) => { var packet = e.Packet; // Process data foreach (var block in packet.GetValidDataBlocks()) { double angle = block.GetAngleDegrees(); double distance = block.GetDistance(packet.Header.DistanceScale); // ... process ... } // Packet is automatically returned to pool after handler completes // No manual pooling needed! }; server.Start(); ``` ## Performance Characteristics ### Expected Performance - **Throughput**: > 1000 packets/second - **Latency**: < 1ms per packet processing - **Memory**: Minimal allocation sau warm-up (thanks to pooling) - **CPU**: Low overhead do zero-allocation parsing ### Profiling Tips 1. Monitor `TotalPacketsReceived` vs `TotalPacketsParsed` để detect packet loss 2. Track `TotalParseErrors` để detect data corruption 3. Monitor memory usage - should be flat after initial warm-up 4. GC collections should be minimal ## Thread Safety - Server có thể start/stop từ bất kỳ thread nào (protected by lock) - Event callbacks được raised từ receive thread - Statistics counters sử dụng `Interlocked` cho atomic operations - Packet pool sử dụng `ConcurrentBag` (thread-safe) ## Error Handling ### Hardware Errors Server tự động detect và report lỗi từ LiDAR: - Motor fault (BIT0) - Abnormal voltage (BIT1) - Temperature fault (BIT2) Access qua: ```csharp if (packet.Header.HasError) { bool motorFault = packet.Header.HasMotorFault; bool voltageFault = packet.Header.HasAbnormalVoltage; bool tempFault = packet.Header.HasTemperatureFault; } ``` ### Network Errors Tất cả network errors được catch và raised qua `ErrorOccurred` event. ## Best Practices 1. **Don't store packet references** - Packets are automatically pooled and reused: ```csharp // ❌ WRONG - packet will be reused! LidarDataPacket? stored = null; server.DataReceived += (s, e) => { stored = e.Packet; }; // ✅ CORRECT - copy data if needed server.DataReceived += (s, e) => { var points = e.Packet.GetValidPoints().ToList(); }; ``` 2. **Process quickly** - Handler runs on receive thread, avoid long operations 3. **Subscribe to ErrorOccurred event** để monitor issues 4. **Use statistics** để track health (`GetStatistics()`) 5. **Dispose properly** để cleanup resources ## Configuration ### UDP Buffer Size Default: 1MB receive buffer ```csharp _udpClient.Client.ReceiveBufferSize = 1024 * 1024; ``` ### Packet Pool Size Initial: 10 packets, Max: 50 packets Có thể adjust trong constructor nếu cần. ## Testing ### Unit Tests Test các component riêng lẻ: - `LidarPacketParser.TryParse()` với test data - Header và DataBlock property accessors - Validation logic ### Integration Tests Test với real sensor hoặc UDP packet simulator: ```csharp // Send test packet using var client = new UdpClient(); byte[] testData = CreateTestPacket(); client.Send(testData, testData.Length, "localhost", 2368); ``` ## Future Enhancements 1. **Async/Await Support**: Add async version of server 2. **Recording**: Add packet recording to file 3. **Replay**: Add packet replay from file 4. **Filtering**: Add built-in distance/angle filtering 5. **Visualization**: Add real-time visualization support 6. **Multi-Sensor**: Support multiple sensors simultaneously ## Dependencies - **.NET 10.0** - **System.Buffers** (ArrayPool) - **System.Collections.Concurrent** (ConcurrentBag) - **System.Net.Sockets** (UdpClient) - **System.Runtime.InteropServices** (MemoryMarshal) ## License Project này là một phần của RobotNet10.