Files
BQP/srcs/RobotNet10/RobotApp/Communication/Olei.LidarSensor/IMPLEMENTATION_NOTES.md
2026-07-13 09:25:40 +07:00

7.0 KiB

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<byte>ReadOnlySpan<byte>
  • Sử dụng MemoryMarshal.Read<T>() 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

private readonly ArrayPool<byte> _bufferPool = ArrayPool<byte>.Shared;
byte[] buffer = _bufferPool.Rent(size);
// ... use buffer ...
_bufferPool.Return(buffer);

Packet Pooling (Automatic)

private readonly ConcurrentBag<LidarDataPacket> _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

public static bool TryParse(ReadOnlySpan<byte> data, LidarDataPacket packet)
{
    // No allocation - direct memory access
    header = MemoryMarshal.Read<LidarHeader>(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
_receiveThread = new Thread(ReceiveLoop)
{
    Name = "OleiLidar-Receive",
    IsBackground = true,
    Priority = ThreadPriority.AboveNormal
};

4. Struct vs Class

  • Struct cho LidarHeaderLidarDataBlock: Stack allocation, no GC
  • Class cho LidarDataPacket: Chứa array lớn, pooling cho reuse

Usage Pattern

Basic Usage

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:

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:

    // ❌ 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

_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:

// 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.