Initial commit
This commit is contained in:
818
srcs/RobotNet10/Commons/RobotNet10.GlobalPathPlanner/README.md
Normal file
818
srcs/RobotNet10/Commons/RobotNet10.GlobalPathPlanner/README.md
Normal file
@@ -0,0 +1,818 @@
|
||||
# RobotNet10.GlobalPathPlanner
|
||||
|
||||
Thư viện path planning cho hệ thống robot, cung cấp các thuật toán tìm đường tối ưu trên graph với hỗ trợ nhiều loại robot khác nhau.
|
||||
|
||||
## 📋 Mục lục
|
||||
|
||||
- [Tổng quan](#tổng-quan)
|
||||
- [Cài đặt](#cài-đặt)
|
||||
- [Bắt đầu nhanh](#bắt-đầu-nhanh)
|
||||
- [Hướng dẫn sử dụng](#hướng-dẫn-sử-dụng)
|
||||
- [Các loại Path Planner](#các-loại-path-planner)
|
||||
- [Configuration Options](#configuration-options)
|
||||
- [API Reference](#api-reference)
|
||||
- [Ví dụ nâng cao](#ví-dụ-nâng-cao)
|
||||
- [Best Practices](#best-practices)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## 🎯 Tổng quan
|
||||
|
||||
`RobotNet10.GlobalPathPlanner` là một thư viện .NET cung cấp các thuật toán path planning cho robot, bao gồm:
|
||||
|
||||
- **A* Algorithm**: Thuật toán tìm đường tối ưu trên graph
|
||||
- **SSE A* Algorithm**: State-Space Enhanced A* cho forklift robots
|
||||
- **Hỗ trợ nhiều loại robot**: Differential drive, Forklift, Omni-directional drive
|
||||
- **Bezier Curve Support**: Hỗ trợ edges với curves bậc 1, 2, hoặc 3
|
||||
- **KD-Tree Optimization**: Tối ưu hóa tìm kiếm nearest neighbor
|
||||
- **Cancellation & Timeout**: Hỗ trợ hủy bỏ và timeout cho operations
|
||||
|
||||
## 📦 Cài đặt
|
||||
|
||||
### Thêm Project Reference
|
||||
|
||||
Thêm reference đến project trong file `.csproj`:
|
||||
|
||||
```xml
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="path/to/RobotNet10.GlobalPathPlanner.csproj" />
|
||||
</ItemGroup>
|
||||
```
|
||||
|
||||
### Using Statements
|
||||
|
||||
```csharp
|
||||
using RobotNet10.GlobalPathPlanner;
|
||||
using RobotNet10.GlobalPathPlanner.Model;
|
||||
```
|
||||
|
||||
## 🚀 Bắt đầu nhanh
|
||||
|
||||
### Ví dụ cơ bản
|
||||
|
||||
```csharp
|
||||
using RobotNet10.GlobalPathPlanner;
|
||||
using RobotNet10.GlobalPathPlanner.Model;
|
||||
|
||||
// 1. Tạo factory và planner
|
||||
var factory = new PathPlannerFactory();
|
||||
var planner = factory.CreateDifferentialPlanner();
|
||||
|
||||
// 2. Chuẩn bị dữ liệu map (nodes và edges)
|
||||
var nodes = new GlobalNode[]
|
||||
{
|
||||
new GlobalNode { Id = Guid.NewGuid(), X = 0, Y = 0, Name = "Start" },
|
||||
new GlobalNode { Id = Guid.NewGuid(), X = 10, Y = 10, Name = "Goal" }
|
||||
};
|
||||
|
||||
var edges = new GlobalEdge[]
|
||||
{
|
||||
new GlobalEdge
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
StartNodeId = nodes[0].Id,
|
||||
EndNodeId = nodes[1].Id,
|
||||
Degree = 1 // Linear edge
|
||||
}
|
||||
};
|
||||
|
||||
// 3. Set data cho planner
|
||||
planner.SetData(nodes, edges);
|
||||
|
||||
// 4. Tính toán đường đi
|
||||
var (pathNodes, pathEdges) = planner.PathPlanning(
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
theta: 0.0,
|
||||
goalId: nodes[1].Id
|
||||
);
|
||||
|
||||
// 5. Sử dụng kết quả
|
||||
foreach (var node in pathNodes)
|
||||
{
|
||||
Console.WriteLine($"Node: {node.Name} at ({node.X}, {node.Y})");
|
||||
}
|
||||
```
|
||||
|
||||
## 📖 Hướng dẫn sử dụng
|
||||
|
||||
### Workflow cơ bản
|
||||
|
||||
1. **Tạo Planner**: Sử dụng `PathPlannerFactory` để tạo planner phù hợp với loại robot
|
||||
2. **Set Data**: Gọi `SetData()` để load map data (nodes và edges)
|
||||
3. **Configure Options** (Optional): Gọi `SetOptions()` để cấu hình
|
||||
4. **Path Planning**: Gọi các method path planning để tính toán đường đi
|
||||
5. **Xử lý kết quả**: Sử dụng mảng nodes và edges trả về
|
||||
|
||||
### Tạo Path Planner
|
||||
|
||||
```csharp
|
||||
var factory = new PathPlannerFactory();
|
||||
|
||||
// Cho differential drive robot
|
||||
var differentialPlanner = factory.CreateDifferentialPlanner();
|
||||
|
||||
// Cho forklift robot (version 1)
|
||||
var forkliftPlanner = factory.CreateForkliftPlanner();
|
||||
|
||||
// Cho forklift robot (version 2 - enhanced)
|
||||
var forkliftPlannerV2 = factory.CreateForkliftPlannerV2();
|
||||
|
||||
// Cho omni-directional drive robot
|
||||
var omniPlanner = factory.CreateOmniDrivePlanner();
|
||||
```
|
||||
|
||||
### Load Map Data
|
||||
|
||||
```csharp
|
||||
// Chuẩn bị nodes (waypoints)
|
||||
var nodes = new GlobalNode[]
|
||||
{
|
||||
new GlobalNode
|
||||
{
|
||||
Id = Guid.Parse("..."),
|
||||
MapId = Guid.Parse("..."),
|
||||
X = 0.0,
|
||||
Y = 0.0,
|
||||
Name = "Node1",
|
||||
Orientation = Orientation.FORWARD
|
||||
},
|
||||
// ... thêm các nodes khác
|
||||
};
|
||||
|
||||
// Chuẩn bị edges (connections)
|
||||
var edges = new GlobalEdge[]
|
||||
{
|
||||
new GlobalEdge
|
||||
{
|
||||
Id = Guid.Parse("..."),
|
||||
MapId = Guid.Parse("..."),
|
||||
StartNodeId = nodes[0].Id,
|
||||
EndNodeId = nodes[1].Id,
|
||||
Degree = 2, // Bezier curve bậc 2
|
||||
ControlPoint1X = 5.0,
|
||||
ControlPoint1Y = 5.0,
|
||||
ControlPoint2X = 0.0,
|
||||
ControlPoint2Y = 0.0
|
||||
},
|
||||
// ... thêm các edges khác
|
||||
};
|
||||
|
||||
// Set data cho planner
|
||||
planner.SetData(nodes, edges);
|
||||
```
|
||||
|
||||
### Path Planning Methods
|
||||
|
||||
#### 1. Path Planning cơ bản (từ tọa độ)
|
||||
|
||||
```csharp
|
||||
var (nodes, edges) = planner.PathPlanning(
|
||||
x: 5.0, // Starting X coordinate
|
||||
y: 5.0, // Starting Y coordinate
|
||||
theta: 45.0, // Starting orientation (degrees)
|
||||
goalId: goalNodeId // Goal node ID
|
||||
);
|
||||
```
|
||||
|
||||
#### 2. Path Planning từ Node ID
|
||||
|
||||
```csharp
|
||||
var (nodes, edges) = planner.PathPlanning(
|
||||
startNodeId: startNodeId, // Starting node ID
|
||||
theta: 45.0, // Current orientation
|
||||
goalId: goalNodeId // Goal node ID
|
||||
);
|
||||
```
|
||||
|
||||
#### 3. Path Planning với Starting Direction
|
||||
|
||||
```csharp
|
||||
var (nodes, edges) = planner.PathPlanningWithStartDirection(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 45.0,
|
||||
goalId: goalNodeId,
|
||||
startDiretion: Orientation.FORWARD // FORWARD, BACKWARD, or NONE
|
||||
);
|
||||
```
|
||||
|
||||
#### 4. Path Planning với Final Direction
|
||||
|
||||
```csharp
|
||||
var (nodes, edges) = planner.PathPlanningWithFinalDirection(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 45.0,
|
||||
goalId: goalNodeId,
|
||||
goalDirection: Orientation.BACKWARD // FORWARD, BACKWARD, or NONE
|
||||
);
|
||||
```
|
||||
|
||||
#### 5. Path Planning với Final Angle
|
||||
|
||||
```csharp
|
||||
var (nodes, edges) = planner.PathPlanningWithAngle(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 45.0,
|
||||
goalId: goalNodeId,
|
||||
goalAngle: 90.0 // Desired final angle in degrees
|
||||
);
|
||||
```
|
||||
|
||||
### Sử dụng Cancellation Token
|
||||
|
||||
```csharp
|
||||
using var cts = new CancellationTokenSource();
|
||||
|
||||
// Set timeout
|
||||
cts.CancelAfter(TimeSpan.FromSeconds(5));
|
||||
|
||||
try
|
||||
{
|
||||
var (nodes, edges) = planner.PathPlanning(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 45.0,
|
||||
goalId: goalNodeId,
|
||||
cancellationToken: cts.Token
|
||||
);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Console.WriteLine("Path planning was cancelled");
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Console.WriteLine("Path planning timed out");
|
||||
}
|
||||
```
|
||||
|
||||
## 🤖 Các loại Path Planner
|
||||
|
||||
### 1. DifferentialPlanner
|
||||
|
||||
**Sử dụng cho:**
|
||||
- Differential drive robots (2 bánh độc lập)
|
||||
- Omni-directional drive robots
|
||||
|
||||
**Đặc điểm:**
|
||||
- Sử dụng A* algorithm cơ bản
|
||||
- Tính toán orientation (FORWARD/BACKWARD) tự động
|
||||
- Default options phù hợp cho hầu hết các trường hợp
|
||||
|
||||
**Ví dụ:**
|
||||
```csharp
|
||||
var planner = factory.CreateDifferentialPlanner();
|
||||
```
|
||||
|
||||
### 2. ForkliftPathPlanner
|
||||
|
||||
**Sử dụng cho:**
|
||||
- Forklift robots (phiên bản 1)
|
||||
|
||||
**Đặc điểm:**
|
||||
- Xử lý các ràng buộc đặc thù của forklift
|
||||
- Tính toán turning radius phù hợp
|
||||
|
||||
**Ví dụ:**
|
||||
```csharp
|
||||
var planner = factory.CreateForkliftPlanner();
|
||||
```
|
||||
|
||||
### 3. ForkLiftPathPlannerV2
|
||||
|
||||
**Sử dụng cho:**
|
||||
- Forklift robots (phiên bản 2 - enhanced)
|
||||
|
||||
**Đặc điểm:**
|
||||
- Sử dụng SSE A* algorithm (State-Space Enhanced A*)
|
||||
- Hiệu năng và chất lượng đường đi tốt hơn
|
||||
- Khuyến nghị sử dụng cho forklift
|
||||
|
||||
**Ví dụ:**
|
||||
```csharp
|
||||
var planner = factory.CreateForkliftPlannerV2();
|
||||
```
|
||||
|
||||
### 4. OmniDrivePlanner
|
||||
|
||||
**Sử dụng cho:**
|
||||
- Omni-directional drive robots
|
||||
|
||||
**Lưu ý:** Hiện tại sử dụng cùng planner với DifferentialPlanner
|
||||
|
||||
**Ví dụ:**
|
||||
```csharp
|
||||
var planner = factory.CreateOmniDrivePlanner();
|
||||
```
|
||||
|
||||
## ⚙️ Configuration Options
|
||||
|
||||
### PathPlannerOptions
|
||||
|
||||
```csharp
|
||||
var options = new PathPlannerOptions
|
||||
{
|
||||
// Khoảng cách tối đa đến edge để được coi là "gần" edge
|
||||
LimitDistanceToEdge = 1.0,
|
||||
|
||||
// Khoảng cách tối đa đến node để được coi là "tại" node
|
||||
LimitDistanceToNode = 0.3,
|
||||
|
||||
// Độ phân giải khi split path (khoảng cách giữa các điểm)
|
||||
ResolutionSplit = 0.1,
|
||||
|
||||
// Timeout cho path planning operation
|
||||
TimeOut = TimeSpan.FromSeconds(10),
|
||||
|
||||
// Góc (degrees) để quyết định đổi hướng (FORWARD/BACKWARD)
|
||||
ChangeOrientationAngle = 89.0
|
||||
};
|
||||
|
||||
planner.SetOptions(options);
|
||||
```
|
||||
|
||||
### Giải thích các tham số
|
||||
|
||||
| Tham số | Mô tả | Giá trị mặc định | Đơn vị |
|
||||
|---------|-------|------------------|--------|
|
||||
| `LimitDistanceToEdge` | Khoảng cách tối đa để được coi là gần edge | 1.0 | meters |
|
||||
| `LimitDistanceToNode` | Khoảng cách tối đa để được coi là tại node | 0.3 | meters |
|
||||
| `ResolutionSplit` | Độ phân giải khi chia nhỏ path | 0.1 | meters |
|
||||
| `TimeOut` | Timeout cho operation | null (no timeout) | TimeSpan |
|
||||
| `ChangeOrientationAngle` | Góc để quyết định đổi hướng | 89.0 | degrees |
|
||||
|
||||
## 📚 API Reference
|
||||
|
||||
### IPathPlanner Interface
|
||||
|
||||
#### SetData
|
||||
|
||||
```csharp
|
||||
void SetData(GlobalNode[] nodes, GlobalEdge[] edges)
|
||||
```
|
||||
|
||||
Thiết lập dữ liệu graph (nodes và edges) cho planner. **Phải gọi trước khi thực hiện path planning.**
|
||||
|
||||
#### SetOptions
|
||||
|
||||
```csharp
|
||||
void SetOptions(PathPlannerOptions options)
|
||||
```
|
||||
|
||||
Cấu hình options cho planner. **Optional**, nếu không gọi sẽ dùng default options.
|
||||
|
||||
#### PathPlanning (từ tọa độ)
|
||||
|
||||
```csharp
|
||||
(GlobalNode[] Nodes, GlobalEdge[] Edges) PathPlanning(
|
||||
double x,
|
||||
double y,
|
||||
double theta,
|
||||
Guid goalId,
|
||||
CancellationToken? cancellationToken = null
|
||||
)
|
||||
```
|
||||
|
||||
Tính toán đường đi từ tọa độ (x, y) đến goal node.
|
||||
|
||||
#### PathPlanning (từ Node ID)
|
||||
|
||||
```csharp
|
||||
(GlobalNode[] Nodes, GlobalEdge[] Edges) PathPlanning(
|
||||
Guid startNodeId,
|
||||
double theta,
|
||||
Guid goalId,
|
||||
CancellationToken? cancellationToken = null
|
||||
)
|
||||
```
|
||||
|
||||
Tính toán đường đi từ start node đến goal node. **Hiệu quả hơn** khi robot đã ở tại một node đã biết.
|
||||
|
||||
#### PathPlanningWithStartDirection
|
||||
|
||||
```csharp
|
||||
(GlobalNode[] Nodes, GlobalEdge[] Edges) PathPlanningWithStartDirection(
|
||||
double x,
|
||||
double y,
|
||||
double theta,
|
||||
Guid goalId,
|
||||
Orientation startDiretion = Orientation.NONE,
|
||||
CancellationToken? cancellationToken = null
|
||||
)
|
||||
```
|
||||
|
||||
Tính toán đường đi với ràng buộc hướng bắt đầu.
|
||||
|
||||
#### PathPlanningWithFinalDirection
|
||||
|
||||
```csharp
|
||||
(GlobalNode[] Nodes, GlobalEdge[] Edges) PathPlanningWithFinalDirection(
|
||||
double x,
|
||||
double y,
|
||||
double theta,
|
||||
Guid goalId,
|
||||
Orientation goalDirection = Orientation.NONE,
|
||||
CancellationToken? cancellationToken = null
|
||||
)
|
||||
```
|
||||
|
||||
Tính toán đường đi với ràng buộc hướng kết thúc.
|
||||
|
||||
#### PathPlanningWithAngle
|
||||
|
||||
```csharp
|
||||
(GlobalNode[] Nodes, GlobalEdge[] Edges) PathPlanningWithAngle(
|
||||
double x,
|
||||
double y,
|
||||
double theta,
|
||||
Guid goalId,
|
||||
double goalAngle,
|
||||
CancellationToken? cancellationToken = null
|
||||
)
|
||||
```
|
||||
|
||||
Tính toán đường đi với ràng buộc góc kết thúc.
|
||||
|
||||
### Data Models
|
||||
|
||||
#### GlobalNode
|
||||
|
||||
```csharp
|
||||
public class GlobalNode
|
||||
{
|
||||
public Guid Id { get; set; } // Unique identifier
|
||||
public Guid MapId { get; set; } // Map identifier
|
||||
public string? Name { get; set; } // Node name
|
||||
public double X { get; set; } // X coordinate
|
||||
public double Y { get; set; } // Y coordinate
|
||||
public Orientation Orientation { get; set; } // FORWARD, BACKWARD, or NONE
|
||||
|
||||
public double DistanceTo(GlobalNode other); // Calculate distance to another node
|
||||
}
|
||||
```
|
||||
|
||||
#### GlobalEdge
|
||||
|
||||
```csharp
|
||||
public record GlobalEdge
|
||||
{
|
||||
public Guid Id { get; set; } // Unique identifier
|
||||
public Guid MapId { get; set; } // Map identifier
|
||||
public Guid StartNodeId { get; set; } // Start node ID
|
||||
public Guid EndNodeId { get; set; } // End node ID
|
||||
public int Degree { get; set; } // Curve degree (1, 2, or 3)
|
||||
public double ControlPoint1X { get; set; } // Control point 1 X (for Bezier)
|
||||
public double ControlPoint1Y { get; set; } // Control point 1 Y (for Bezier)
|
||||
public double ControlPoint2X { get; set; } // Control point 2 X (for Bezier)
|
||||
public double ControlPoint2Y { get; set; } // Control point 2 Y (for Bezier)
|
||||
}
|
||||
```
|
||||
|
||||
#### Orientation Enum
|
||||
|
||||
```csharp
|
||||
public enum Orientation
|
||||
{
|
||||
FORWARD, // Di chuyển tiến
|
||||
BACKWARD, // Di chuyển lùi
|
||||
NONE // Không ràng buộc
|
||||
}
|
||||
```
|
||||
|
||||
## 💡 Ví dụ nâng cao
|
||||
|
||||
### Ví dụ 1: Path Planning với Error Handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var factory = new PathPlannerFactory();
|
||||
var planner = factory.CreateDifferentialPlanner();
|
||||
|
||||
planner.SetData(nodes, edges);
|
||||
|
||||
var options = new PathPlannerOptions
|
||||
{
|
||||
LimitDistanceToEdge = 1.0,
|
||||
LimitDistanceToNode = 0.3,
|
||||
ResolutionSplit = 0.1,
|
||||
TimeOut = TimeSpan.FromSeconds(5),
|
||||
ChangeOrientationAngle = 89.0
|
||||
};
|
||||
planner.SetOptions(options);
|
||||
|
||||
var (pathNodes, pathEdges) = planner.PathPlanning(
|
||||
x: currentX,
|
||||
y: currentY,
|
||||
theta: currentTheta,
|
||||
goalId: goalNodeId
|
||||
);
|
||||
|
||||
Console.WriteLine($"Path found with {pathNodes.Length} nodes");
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("does not exist"))
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
// Handle case when goal node doesn't exist or no path found
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Console.WriteLine("Path planning timed out");
|
||||
// Handle timeout
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Console.WriteLine("Path planning was cancelled");
|
||||
// Handle cancellation
|
||||
}
|
||||
```
|
||||
|
||||
### Ví dụ 2: Sử dụng với Async/Await
|
||||
|
||||
```csharp
|
||||
public async Task<(GlobalNode[] Nodes, GlobalEdge[] Edges)> PlanPathAsync(
|
||||
IPathPlanner planner,
|
||||
double x,
|
||||
double y,
|
||||
double theta,
|
||||
Guid goalId)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
return planner.PathPlanning(x, y, theta, goalId, cts.Token);
|
||||
}, cts.Token);
|
||||
}
|
||||
|
||||
// Usage
|
||||
var planner = factory.CreateDifferentialPlanner();
|
||||
planner.SetData(nodes, edges);
|
||||
|
||||
var (pathNodes, pathEdges) = await PlanPathAsync(
|
||||
planner,
|
||||
currentX,
|
||||
currentY,
|
||||
currentTheta,
|
||||
goalNodeId
|
||||
);
|
||||
```
|
||||
|
||||
### Ví dụ 3: Path Planning với Direction Constraints
|
||||
|
||||
```csharp
|
||||
// Robot cần bắt đầu di chuyển lùi
|
||||
var (nodes, edges) = planner.PathPlanningWithStartDirection(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 180.0,
|
||||
goalId: goalNodeId,
|
||||
startDiretion: Orientation.BACKWARD
|
||||
);
|
||||
|
||||
// Robot cần đến đích và quay mặt về phía trước
|
||||
var (nodes2, edges2) = planner.PathPlanningWithFinalDirection(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 0.0,
|
||||
goalId: goalNodeId,
|
||||
goalDirection: Orientation.FORWARD
|
||||
);
|
||||
|
||||
// Robot cần đến đích với góc cụ thể (90 độ)
|
||||
var (nodes3, edges3) = planner.PathPlanningWithAngle(
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
theta: 0.0,
|
||||
goalId: goalNodeId,
|
||||
goalAngle: 90.0
|
||||
);
|
||||
```
|
||||
|
||||
### Ví dụ 4: Xử lý kết quả Path
|
||||
|
||||
```csharp
|
||||
var (pathNodes, pathEdges) = planner.PathPlanning(
|
||||
startNodeId: startNodeId,
|
||||
theta: currentTheta,
|
||||
goalId: goalNodeId
|
||||
);
|
||||
|
||||
// Kiểm tra kết quả
|
||||
if (pathNodes.Length == 0)
|
||||
{
|
||||
Console.WriteLine("No path found");
|
||||
return;
|
||||
}
|
||||
|
||||
// In thông tin path
|
||||
Console.WriteLine($"Path contains {pathNodes.Length} nodes and {pathEdges.Length} edges");
|
||||
|
||||
for (int i = 0; i < pathNodes.Length; i++)
|
||||
{
|
||||
var node = pathNodes[i];
|
||||
Console.WriteLine($"Node {i}: {node.Name} at ({node.X:F2}, {node.Y:F2}) " +
|
||||
$"Orientation: {node.Orientation}");
|
||||
|
||||
if (i < pathEdges.Length)
|
||||
{
|
||||
var edge = pathEdges[i];
|
||||
Console.WriteLine($" Edge {i}: {edge.Id} (Degree: {edge.Degree})");
|
||||
}
|
||||
}
|
||||
|
||||
// Tính tổng khoảng cách
|
||||
double totalDistance = 0;
|
||||
for (int i = 0; i < pathNodes.Length - 1; i++)
|
||||
{
|
||||
totalDistance += pathNodes[i].DistanceTo(pathNodes[i + 1]);
|
||||
}
|
||||
Console.WriteLine($"Total path distance: {totalDistance:F2} meters");
|
||||
```
|
||||
|
||||
## ✅ Best Practices
|
||||
|
||||
### 1. Chọn đúng Planner cho Robot Type
|
||||
|
||||
```csharp
|
||||
// ✅ Đúng: Sử dụng ForkliftPlannerV2 cho forklift
|
||||
var forkliftPlanner = factory.CreateForkliftPlannerV2();
|
||||
|
||||
// ❌ Sai: Không dùng DifferentialPlanner cho forklift
|
||||
var wrongPlanner = factory.CreateDifferentialPlanner(); // Không phù hợp
|
||||
```
|
||||
|
||||
### 2. Luôn Set Data trước khi Planning
|
||||
|
||||
```csharp
|
||||
// ✅ Đúng
|
||||
planner.SetData(nodes, edges);
|
||||
var (nodes, edges) = planner.PathPlanning(...);
|
||||
|
||||
// ❌ Sai: Quên set data
|
||||
var (nodes, edges) = planner.PathPlanning(...); // Sẽ lỗi
|
||||
```
|
||||
|
||||
### 3. Sử dụng Node ID khi có thể
|
||||
|
||||
```csharp
|
||||
// ✅ Tốt hơn: Sử dụng Node ID khi robot đã ở tại node
|
||||
var (nodes, edges) = planner.PathPlanning(startNodeId, theta, goalId);
|
||||
|
||||
// ⚠️ Chấp nhận được: Sử dụng tọa độ khi robot không ở node
|
||||
var (nodes, edges) = planner.PathPlanning(x, y, theta, goalId);
|
||||
```
|
||||
|
||||
### 4. Cấu hình Timeout cho Operations dài
|
||||
|
||||
```csharp
|
||||
var options = new PathPlannerOptions
|
||||
{
|
||||
TimeOut = TimeSpan.FromSeconds(10) // Tránh hang
|
||||
};
|
||||
planner.SetOptions(options);
|
||||
```
|
||||
|
||||
### 5. Xử lý Exceptions đúng cách
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
var (nodes, edges) = planner.PathPlanning(...);
|
||||
}
|
||||
catch (Exception ex) when (ex.Message.Contains("does not exist"))
|
||||
{
|
||||
// Handle: Goal không tồn tại hoặc không tìm thấy đường đi
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
// Handle: Timeout
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Handle: Cancellation
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Reuse Planner Instance
|
||||
|
||||
```csharp
|
||||
// ✅ Tốt: Tạo một lần, dùng nhiều lần
|
||||
var planner = factory.CreateDifferentialPlanner();
|
||||
planner.SetData(nodes, edges);
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var (pathNodes, pathEdges) = planner.PathPlanning(...);
|
||||
// Process path
|
||||
}
|
||||
|
||||
// ❌ Không hiệu quả: Tạo mới mỗi lần
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var planner = factory.CreateDifferentialPlanner(); // Không cần thiết
|
||||
planner.SetData(nodes, edges);
|
||||
var (pathNodes, pathEdges) = planner.PathPlanning(...);
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Vấn đề: "Destination does not exist in the map"
|
||||
|
||||
**Nguyên nhân:** Goal node ID không có trong danh sách nodes đã set.
|
||||
|
||||
**Giải pháp:**
|
||||
```csharp
|
||||
// Kiểm tra goal node có tồn tại không
|
||||
var goalNode = nodes.FirstOrDefault(n => n.Id == goalId);
|
||||
if (goalNode == null)
|
||||
{
|
||||
throw new ArgumentException($"Goal node {goalId} not found in map");
|
||||
}
|
||||
```
|
||||
|
||||
### Vấn đề: "The path does not exist"
|
||||
|
||||
**Nguyên nhân:** Không có đường đi từ start đến goal (graph không liên thông).
|
||||
|
||||
**Giải pháp:**
|
||||
- Kiểm tra edges có kết nối start và goal không
|
||||
- Đảm bảo graph liên thông
|
||||
- Kiểm tra `LimitDistanceToEdge` và `LimitDistanceToNode` có quá nhỏ không
|
||||
|
||||
### Vấn đề: Timeout thường xuyên
|
||||
|
||||
**Nguyên nhân:** Graph quá lớn hoặc phức tạp.
|
||||
|
||||
**Giải pháp:**
|
||||
```csharp
|
||||
// Tăng timeout
|
||||
var options = new PathPlannerOptions
|
||||
{
|
||||
TimeOut = TimeSpan.FromSeconds(30) // Tăng từ 10s lên 30s
|
||||
};
|
||||
planner.SetOptions(options);
|
||||
```
|
||||
|
||||
### Vấn đề: Path không mượt
|
||||
|
||||
**Nguyên nhân:** `ResolutionSplit` quá lớn.
|
||||
|
||||
**Giải pháp:**
|
||||
```csharp
|
||||
// Giảm ResolutionSplit để có nhiều điểm hơn
|
||||
var options = new PathPlannerOptions
|
||||
{
|
||||
ResolutionSplit = 0.05 // Giảm từ 0.1 xuống 0.05
|
||||
};
|
||||
planner.SetOptions(options);
|
||||
```
|
||||
|
||||
### Vấn đề: Robot không đổi hướng đúng
|
||||
|
||||
**Nguyên nhân:** `ChangeOrientationAngle` không phù hợp.
|
||||
|
||||
**Giải pháp:**
|
||||
```csharp
|
||||
// Điều chỉnh góc đổi hướng
|
||||
var options = new PathPlannerOptions
|
||||
{
|
||||
ChangeOrientationAngle = 85.0 // Thử các giá trị khác nhau
|
||||
};
|
||||
planner.SetOptions(options);
|
||||
```
|
||||
|
||||
## 📝 Lưu ý
|
||||
|
||||
- **Thread Safety**: Planner instances không thread-safe. Mỗi thread nên có planner riêng.
|
||||
- **Memory**: Planner lưu toàn bộ nodes và edges trong memory. Với map lớn, cần xem xét memory usage.
|
||||
- **Performance**: Path planning với Node ID nhanh hơn so với tọa độ vì không cần tìm nearest node/edge.
|
||||
|
||||
## 🤝 Đóng góp
|
||||
|
||||
Nếu bạn phát hiện bug hoặc có đề xuất cải thiện, vui lòng tạo issue hoặc pull request.
|
||||
|
||||
## 📄 License
|
||||
|
||||
[Thêm thông tin license nếu có]
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0
|
||||
**Last Updated**: 2024
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user