Initial commit
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
# 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>` và `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<byte>** để 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<byte> _bufferPool = ArrayPool<byte>.Shared;
|
||||
byte[] buffer = _bufferPool.Rent(size);
|
||||
// ... use buffer ...
|
||||
_bufferPool.Return(buffer);
|
||||
```
|
||||
|
||||
#### Packet Pooling (Automatic)
|
||||
```csharp
|
||||
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<byte>
|
||||
```csharp
|
||||
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
|
||||
|
||||
```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.
|
||||
Reference in New Issue
Block a user