first commit
This commit is contained in:
126
README.md
Normal file
126
README.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# Olei Lidar Driver
|
||||||
|
|
||||||
|
Driver UDP độc lập (C++17, không phụ thuộc CMake/ROS) cho lidar Olei. Tự nhận
|
||||||
|
diện họ giao thức (Family A/B) theo từng gói, hỗ trợ chạy nhiều lidar song song.
|
||||||
|
|
||||||
|
## Kiến trúc
|
||||||
|
|
||||||
|
| File | Vai trò |
|
||||||
|
|------|---------|
|
||||||
|
| `olei_lidar.hpp` | API: `Point`, `Scan`, `ModelConfig`, class `Driver` |
|
||||||
|
| `olei_lidar.cpp` | Parse Family A (0xFAF0) & Family B (0xFEF0), CRC, gom scan |
|
||||||
|
| `example.cpp` | Demo 1 lidar, `recv_scan()` blocking |
|
||||||
|
| `test_dual.cpp` | Demo 2 lidar song song (2 thread) |
|
||||||
|
|
||||||
|
Ba họ giao thức:
|
||||||
|
- **Family A** (`0xFAF0`) — VB/VF/LR-1F. Header 20B + block 3B/điểm. Có CRC32.
|
||||||
|
- **Family B** (`0xFEF0`) — LR-1BS5/LR-1BS2. Header 40B (preamble `0x010F` +
|
||||||
|
frame id ở offset [2-3], chuỗi tên model ASCII ở offset [7-17)) + block
|
||||||
|
8B/điểm.
|
||||||
|
- **Family C / protocol V3** (`0xFEAC`) — GS1-5. Header 48B, block 2/4B/điểm
|
||||||
|
tùy byte `Types`. **Port từ driver C# `OleiGS15Driver.cs` (RobotNet10.RobotApp),
|
||||||
|
CHƯA verify bằng phần cứng GS1-5 thật** (không có thiết bị để sniff) — chỉ
|
||||||
|
test bằng packet giả lập tự dựng theo đúng cấu trúc header.
|
||||||
|
|
||||||
|
Góc điểm đo (`Point::angle_deg`) luôn ở hệ **có dấu** `[-180, 180]`, 0 = phía
|
||||||
|
trước, dương = bên trái.
|
||||||
|
|
||||||
|
## Kết nối lidar
|
||||||
|
|
||||||
|
Mạng tham chiếu trên host này (`/home/robotics`):
|
||||||
|
|
||||||
|
```
|
||||||
|
eth0: 192.168.100.100/24
|
||||||
|
├─ front (scan_1): DeviceIp 192.168.100.11, DevicePort 2368
|
||||||
|
└─ rear (scan_2): DeviceIp 192.168.100.12, DevicePort 2369
|
||||||
|
```
|
||||||
|
|
||||||
|
(Khớp với `RobotApp/RobotNet10.RobotApp/appsettings.json`, các mục
|
||||||
|
`Olei-front`/`Olei-rear`.)
|
||||||
|
|
||||||
|
Kiểm tra kết nối trước khi test:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ip -4 addr show eth0 # phải thấy inet 192.168.100.100/24
|
||||||
|
ping -c1 192.168.100.11 # front
|
||||||
|
ping -c1 192.168.100.12 # rear
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lưu ý quan trọng:** nếu `RobotNet10.RobotApp` đang chạy, nó bind sẵn port
|
||||||
|
2368/2369 (không bật `SO_REUSEPORT`) → driver standalone sẽ bind lỗi
|
||||||
|
(`Khong mo duoc socket... interface khong ton tai?`). Kiểm tra ai đang giữ port:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ss -lunp | grep -E '2368|2369'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Không có CMakeLists, chỉ dùng g++ thuần:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
g++ -std=c++17 -O2 -pthread -Wall -Wextra -o test_dual test_dual.cpp olei_lidar.cpp
|
||||||
|
g++ -std=c++17 -O2 -pthread -Wall -Wextra -o example example.cpp olei_lidar.cpp
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test thử
|
||||||
|
|
||||||
|
### 2 lidar song song
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./test_dual
|
||||||
|
```
|
||||||
|
|
||||||
|
In ra 5 scan mỗi bên, kèm số điểm, timestamp, error status, và **model đã tự
|
||||||
|
dò được** (`model=...`).
|
||||||
|
|
||||||
|
### 1 lidar
|
||||||
|
|
||||||
|
Sửa model/IP/port trong `example.cpp` rồi build lại, hoặc gọi trực tiếp:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
olei::Driver drv(olei::MODEL_AUTO, "192.168.100.100", 2369);
|
||||||
|
drv.open();
|
||||||
|
olei::Scan scan;
|
||||||
|
drv.recv_scan(scan, 2000);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sniff packet thô (debug khi nghi ngờ offset header)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 - <<'EOF'
|
||||||
|
import socket
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.bind(("192.168.100.100", 2369)) # đổi port theo lidar cần xem
|
||||||
|
data, addr = s.recvfrom(4096)
|
||||||
|
print("from", addr, "len", len(data))
|
||||||
|
print(data[:40].hex(' '))
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Luôn ưu tiên capture thật hơn là tin comment trong code — comment mô tả ý
|
||||||
|
định lúc viết, không phải offset đã verify trên thiết bị thật.
|
||||||
|
|
||||||
|
## Chọn `ModelConfig`
|
||||||
|
|
||||||
|
| Constant | FOV (deg, có dấu) | Khi dùng |
|
||||||
|
|---|---|---|
|
||||||
|
| `MODEL_VB` | -135…135 | 2D 270°, Family A |
|
||||||
|
| `MODEL_VF` | -180…180 | 2D 360°, Family A |
|
||||||
|
| `MODEL_LR1F` | -180…180 | 2D 360° 50m, Family A |
|
||||||
|
| `MODEL_LR1BS5` | -180…180 | 2D 360°, Family B |
|
||||||
|
| `MODEL_LR16F` | -135…135 | 3D 16-line |
|
||||||
|
| `MODEL_GS15` | -180…180 | 2D 360°, Family C/V3 — **chưa verify hardware thật** |
|
||||||
|
| `MODEL_AUTO` | -180…180 (mặc định, có thể tự thu hẹp) | Không biết trước model |
|
||||||
|
|
||||||
|
`MODEL_AUTO`: chỉ có tác dụng tự-dò với **Family B** (đọc chuỗi tên model
|
||||||
|
trong header). Driver luôn lưu lại **tên thật** đọc từ packet (vd
|
||||||
|
`"OLELR-1BS5"`, `"OLELR-1BS2"`) — gọi `drv.detected_model()` để xem (trả về
|
||||||
|
`"AUTO"` nếu chưa nhận gói Family B nào). FOV chỉ tự thu hẹp khi tên đó khớp
|
||||||
|
một entry trong `kModelTable` (`olei_lidar.cpp`); nếu không khớp, FOV giữ
|
||||||
|
nguyên mặc định 360° (`-180..180`, không mất điểm) — an toàn nhưng có thể
|
||||||
|
giữ lại điểm ngoài FOV thật của thiết bị nếu thiết bị đó không quét tròn.
|
||||||
|
|
||||||
|
Family A không mang chuỗi tên model trong packet, nên `MODEL_AUTO` trên thiết
|
||||||
|
bị Family A cũng giữ nguyên FOV rộng — phải chỉ định model cụ thể (VD
|
||||||
|
`MODEL_VB`) nếu muốn thu hẹp FOV cho thiết bị góc hẹp.
|
||||||
Reference in New Issue
Block a user