# AGV 自定义路径功能使用指南

## 概述

本指南介绍如何使用新增的自定义路径功能，包括：
1. **从CSV文件加载路径**
2. **保存路径到CSV文件**
3. **样条插值生成平滑曲线**

## 功能特性

### 1. CSV 文件加载/保存

支持两种CSV格式：
- **完整格式**：`x, y, theta, kappa`
- **简化格式**：`x, y` （theta和kappa会自动计算）

### 2. 样条插值

使用 Catmull-Rom 样条插值，只需提供少量关键点，自动生成平滑的路径曲线。

## 安装步骤

### 第一步：修改头文件

在 `include/path_curve.h` 中添加以下方法声明（在 `setPathPoints` 方法之后）：

```cpp
// 在第77行之后添加：

/**
 * @brief 从CSV文件加载路径点
 * @param filename CSV文件路径
 * @param has_header 是否包含表头（默认true）
 * @return 是否加载成功
 *
 * CSV格式支持以下两种：
 * 1. 完整格式：x, y, theta, kappa
 * 2. 简化格式：x, y （theta和kappa将自动计算）
 */
bool loadFromCSV(const std::string& filename, bool has_header = true);

/**
 * @brief 将路径点保存到CSV文件
 * @param filename CSV文件路径
 * @return 是否保存成功
 */
bool saveToCSV(const std::string& filename) const;

/**
 * @brief 使用样条插值生成路径
 * @param key_points 关键路径点（只需指定x和y）
 * @param num_points 生成的路径点总数
 * @param tension 张力参数（0.0-1.0，控制曲线平滑度，默认0.5）
 *
 * 使用 Catmull-Rom 样条插值，生成经过所有关键点的平滑曲线
 */
void generateSpline(const std::vector<PathPoint>& key_points,
                   int num_points = 100,
                   double tension = 0.5);
```

同时在文件开头添加 string 头文件：
```cpp
#include <string>
```

### 第二步：添加实现文件到编译

在 `src/CMakeLists.txt` 或主 `CMakeLists.txt` 中添加：
```cmake
add_library(path_curve_lib
    src/path_curve.cpp
    src/path_curve_custom.cpp  # 新增
    # ... 其他文件
)
```

### 第三步：重新编译

```bash
cd build
cmake ..
cmake --build .
```

## 使用示例

### 示例 1：从CSV加载路径

```cpp
#include "path_curve.h"
#include "path_tracker.h"

int main() {
    // 创建路径对象
    PathCurve path;
    
    // 从CSV文件加载
    if (path.loadFromCSV("custom_path.csv", true)) {
        std::cout << "路径加载成功！" << std::endl;
        std::cout << "路径点数量: " << path.getPathPoints().size() << std::endl;
        std::cout << "路径长度: " << path.getPathLength() << " m" << std::endl;
        
        // 使用加载的路径进行跟踪
        AGVModel agv(2.0, 1.0, M_PI/4);
        PathTracker tracker(agv);
        tracker.setReferencePath(path);
        
        // 设置初始状态并生成控制序列
        const auto& points = path.getPathPoints();
        AGVModel::State initial(points[0].x, points[0].y, points[0].theta);
        tracker.setInitialState(initial);
        tracker.generateControlSequence("pure_pursuit", 0.1, 20.0);
        
        // 保存结果
        tracker.saveTrajectory("output_trajectory.csv");
    }
    
    return 0;
}
```

### 示例 2：使用样条插值

```cpp
#include "path_curve.h"

int main() {
    PathCurve path;
    
    // 定义关键点
    std::vector<PathPoint> key_points;
    key_points.push_back(PathPoint(0.0, 0.0));
    key_points.push_back(PathPoint(5.0, 2.0));
    key_points.push_back(PathPoint(10.0, 5.0));
    key_points.push_back(PathPoint(15.0, 3.0));
    key_points.push_back(PathPoint(20.0, 0.0));
    
    // 生成样条曲线（200个点）
    path.generateSpline(key_points, 200, 0.5);
    
    std::cout << "样条曲线生成完成" << std::endl;
    std::cout << "路径长度: " << path.getPathLength() << " m" << std::endl;
    
    // 保存到文件
    path.saveToCSV("spline_path.csv");
    
    return 0;
}
```

### 示例 3：手动创建并保存路径

```cpp
#include "path_curve.h"

int main() {
    PathCurve path;
    
    // 创建路径（例如：贝塞尔曲线）
    PathPoint p0(0, 0), p1(5, 10), p2(15, 10), p3(20, 0);
    path.generateCubicBezier(p0, p1, p2, p3, 150);
    
    // 保存到CSV
    if (path.saveToCSV("my_custom_path.csv")) {
        std::cout << "路径已保存" << std::endl;
    }
    
    return 0;
}
```

## CSV 文件格式

### 完整格式示例

```csv
# Custom Path Data
# x(m), y(m), theta(rad), kappa(1/m)
0.0, 0.0, 0.0, 0.0
1.0, 0.5, 0.1, 0.05
2.0, 1.2, 0.15, 0.03
```

### 简化格式示例

```csv
# Custom Path - Simple Format
# x(m), y(m)
0.0, 0.0
2.0, 1.0
4.0, 3.0
6.0, 4.0
```

## 参数说明

### loadFromCSV 参数

- `filename`: CSV文件路径（相对或绝对路径）
- `has_header`: 是否包含注释/表头行（默认true）

### generateSpline 参数

- `key_points`: 关键点数组（最少2个点）
- `num_points`: 生成的总点数（推荐100-500）
- `tension`: 张力系数（0.0 = 最平滑，1.0 = 最紧密，推荐0.5）

## 常见用例

### 用例 1：地图路径规划

从地图软件导出路径点（CSV格式），直接加载使用：

```cpp
PathCurve map_path;
map_path.loadFromCSV("map_waypoints.csv");
```

### 用例 2：平滑路径优化

将粗糙的路径点用样条插值平滑化：

```cpp
// 加载原始路径
PathCurve rough_path;
rough_path.loadFromCSV("raw_path.csv");

// 提取关键点（每隔10个点取一个）
std::vector<PathPoint> key_points;
const auto& points = rough_path.getPathPoints();
for (size_t i = 0; i < points.size(); i += 10) {
    key_points.push_back(points[i]);
}

// 生成平滑路径
PathCurve smooth_path;
smooth_path.generateSpline(key_points, 300, 0.3);
smooth_path.saveToCSV("smooth_path.csv");
```

### 用例 3：交互式路径定义

通过用户输入定义路径：

```cpp
std::vector<PathPoint> user_points;
int n;
std::cout << "输入路径点数量: ";
std::cin >> n;

for (int i = 0; i < n; i++) {
    double x, y;
    std::cout << "点" << (i+1) << " x: "; std::cin >> x;
    std::cout << "点" << (i+1) << " y: "; std::cin >> y;
    user_points.push_back(PathPoint(x, y));
}

PathCurve user_path;
user_path.generateSpline(user_points, 200);
user_path.saveToCSV("user_defined_path.csv");
```

## 与现有路径类型的对比

| 路径类型 | 定义方式 | 灵活性 | 适用场景 |
|---------|---------|--------|---------|
| **直线** | 起点+终点 | 低 | 简单直线移动 |
| **圆弧** | 圆心+半径+角度 | 低 | 固定半径转弯 |
| **贝塞尔曲线** | 4个控制点 | 中 | S型曲线 |
| **CSV加载** | 外部文件 | 高 | 复杂预定义路径 |
| **样条插值** | 关键点数组 | 极高 | 任意平滑曲线 |

## 调试技巧

### 1. 验证加载的路径

```cpp
const auto& points = path.getPathPoints();
for (size_t i = 0; i < points.size(); i += 10) {
    std::cout << "Point " << i << ": ("
              << points[i].x << ", " << points[i].y << ")" << std::endl;
}
```

### 2. 检查路径连续性

```cpp
double max_distance = 0.0;
for (size_t i = 1; i < points.size(); i++) {
    double dx = points[i].x - points[i-1].x;
    double dy = points[i].y - points[i-1].y;
    double dist = sqrt(dx*dx + dy*dy);
    max_distance = std::max(max_distance, dist);
}
std::cout << "最大点间距: " << max_distance << " m" << std::endl;
```

## 常见问题

**Q: CSV文件格式不正确怎么办？**
A: 确保每行格式为 `数字, 数字, ...`，使用逗号分隔，可以有注释行（以#开头）。

**Q: 样条插值生成的路径不平滑？**
A: 尝试增加点数（num_points），或减小tension参数。

**Q: 路径跟踪效果不理想？**
A: 检查路径曲率（kappa值），确保不超过AGV的最大转向能力。

## 完整示例程序

项目中提供了完整的示例：
- `examples/custom_path.csv` - 示例CSV路径文件
- `src/path_curve_custom.cpp` - 实现代码

## 总结

使用自定义路径功能的基本步骤：

1. **准备路径数据** - CSV文件或关键点数组
2. **创建PathCurve对象** - 使用loadFromCSV或generateSpline
3. **验证路径** - 检查点数和长度
4. **应用到跟踪器** - 使用tracker.setReferencePath()
5. **运行仿真** - 生成控制序列并保存结果

现在你可以摆脱预设曲线的限制，使用任意自定义路径进行AGV路径跟踪！
