615 lines
12 KiB
Markdown
615 lines
12 KiB
Markdown
# 开发指南
|
||
|
||
本文档提供 AGV 路径跟踪与控制系统的开发指南,包括代码结构、开发流程和最佳实践。
|
||
|
||
## 目录
|
||
|
||
- [开发环境设置](#开发环境设置)
|
||
- [代码结构](#代码结构)
|
||
- [开发流程](#开发流程)
|
||
- [编码规范](#编码规范)
|
||
- [测试指南](#测试指南)
|
||
- [调试技巧](#调试技巧)
|
||
|
||
---
|
||
|
||
## 开发环境设置
|
||
|
||
### Windows 开发环境(推荐)
|
||
|
||
#### 1. 安装 MSYS2
|
||
|
||
从 [msys2.org](https://www.msys2.org/) 下载并安装 MSYS2。
|
||
|
||
#### 2. 安装开发工具
|
||
|
||
```bash
|
||
# 打开 MSYS2 MinGW 64-bit 终端
|
||
pacman -Syu # 更新包数据库
|
||
|
||
# 安装基础开发工具
|
||
pacman -S mingw-w64-x86_64-gcc
|
||
pacman -S mingw-w64-x86_64-cmake
|
||
pacman -S mingw-w64-x86_64-make
|
||
|
||
# 安装 Qt6
|
||
pacman -S mingw-w64-x86_64-qt6-base
|
||
pacman -S mingw-w64-x86_64-qt6-tools
|
||
```
|
||
|
||
#### 3. 配置 PATH
|
||
|
||
```bash
|
||
export PATH="/c/Qt/Tools/mingw1310_64/bin:$PATH"
|
||
export PATH="/c/Qt/6.10.1/mingw_64/bin:$PATH"
|
||
```
|
||
|
||
### Linux 开发环境
|
||
|
||
#### Ubuntu/Debian
|
||
|
||
```bash
|
||
sudo apt-get update
|
||
sudo apt-get install build-essential cmake
|
||
sudo apt-get install qt6-base-dev qt6-tools-dev
|
||
sudo apt-get install python3 python3-matplotlib
|
||
```
|
||
|
||
#### Fedora/RHEL
|
||
|
||
```bash
|
||
sudo dnf install gcc gcc-c++ cmake
|
||
sudo dnf install qt6-qtbase-devel
|
||
sudo dnf install python3 python3-matplotlib
|
||
```
|
||
|
||
### IDE 推荐
|
||
|
||
- **Visual Studio Code**: 轻量级,插件丰富
|
||
- C/C++ 插件
|
||
- CMake Tools 插件
|
||
- Qt tools 插件
|
||
- **Qt Creator**: Qt 开发首选
|
||
- **CLion**: 强大的 C++ IDE
|
||
|
||
---
|
||
|
||
## 代码结构
|
||
|
||
### 目录组织
|
||
|
||
```
|
||
agv-control-slam/
|
||
├── include/ # 公共头文件
|
||
│ ├── agv_model.h
|
||
│ ├── path_curve.h
|
||
│ ├── path_tracker.h
|
||
│ ├── control_generator.h
|
||
│ └── can/
|
||
│ ├── CANController.h
|
||
│ └── CurtisMotorController.h
|
||
├── src/ # 源文件实现
|
||
│ └── control/
|
||
│ ├── agv_model.cpp
|
||
│ ├── path_curve.cpp
|
||
│ ├── path_curve_custom.cpp
|
||
│ ├── path_tracker.cpp
|
||
│ ├── control_generator.cpp
|
||
│ └── can/
|
||
│ ├── CANController.cpp
|
||
│ └── CurtisMotorController.cpp
|
||
├── examples/ # 示例程序
|
||
│ ├── demo.cpp
|
||
│ ├── qt_gui_demo.cpp
|
||
│ ├── curtis_demo.cpp
|
||
│ └── ...
|
||
├── lib/ # 第三方库
|
||
│ ├── ControlCAN.dll
|
||
│ └── ControlCAN.lib
|
||
├── scripts/ # 工具脚本
|
||
├── docs/ # 文档
|
||
└── CMakeLists.txt # CMake 配置
|
||
```
|
||
|
||
### 模块划分
|
||
|
||
#### 1. 核心模块 (`agv_model`, `path_curve`)
|
||
|
||
负责基础的运动学模型和路径表示。
|
||
|
||
- **职责**:提供 AGV 运动学仿真和路径数据结构
|
||
- **依赖**:C++ 标准库
|
||
- **接口**:清晰的类接口,便于单元测试
|
||
|
||
#### 2. 控制模块 (`control_generator`, `path_tracker`)
|
||
|
||
实现路径跟踪算法。
|
||
|
||
- **职责**:Pure Pursuit、Stanley 等算法实现
|
||
- **依赖**:核心模块
|
||
- **接口**:算法参数可配置
|
||
|
||
#### 3. CAN 通信模块 (`can/`)
|
||
|
||
处理硬件通信。
|
||
|
||
- **职责**:CAN 设备初始化、数据收发
|
||
- **依赖**:ControlCAN 库
|
||
- **接口**:硬件抽象层
|
||
|
||
#### 4. 界面模块 (`examples/qt_gui_demo.cpp`)
|
||
|
||
提供可视化界面。
|
||
|
||
- **职责**:路径显示、参数调整、数据导出
|
||
- **依赖**:Qt6、核心模块、控制模块
|
||
- **接口**:用户友好的 GUI
|
||
|
||
---
|
||
|
||
## 开发流程
|
||
|
||
### 1. 功能开发流程
|
||
|
||
```
|
||
需求分析 → 设计 → 实现 → 测试 → 文档 → 代码审查
|
||
```
|
||
|
||
#### 需求分析
|
||
|
||
- 明确功能需求和性能要求
|
||
- 确定接口设计
|
||
- 评估技术可行性
|
||
|
||
#### 设计
|
||
|
||
- 设计类结构和接口
|
||
- 确定数据结构
|
||
- 规划测试用例
|
||
|
||
#### 实现
|
||
|
||
- 遵循编码规范
|
||
- 编写清晰的注释
|
||
- 逐步实现功能
|
||
|
||
#### 测试
|
||
|
||
- 单元测试
|
||
- 集成测试
|
||
- 性能测试
|
||
|
||
#### 文档
|
||
|
||
- 更新 API 文档
|
||
- 编写使用示例
|
||
- 更新 README
|
||
|
||
### 2. 添加新算法
|
||
|
||
假设要添加一个新的路径跟踪算法 "LQR":
|
||
|
||
#### Step 1: 在 `control_generator.h` 中声明
|
||
|
||
```cpp
|
||
class ControlGenerator {
|
||
public:
|
||
// 现有算法...
|
||
|
||
/**
|
||
* @brief LQR 算法
|
||
* @param state 当前状态
|
||
* @param path 参考路径
|
||
* @param Q 状态权重矩阵
|
||
* @param R 控制权重
|
||
* @return 转向角
|
||
*/
|
||
static double lqr(
|
||
const AGVModel::State& state,
|
||
const PathCurve& path,
|
||
const Eigen::Matrix2d& Q,
|
||
double R
|
||
);
|
||
};
|
||
```
|
||
|
||
#### Step 2: 在 `control_generator.cpp` 中实现
|
||
|
||
```cpp
|
||
double ControlGenerator::lqr(
|
||
const AGVModel::State& state,
|
||
const PathCurve& path,
|
||
const Eigen::Matrix2d& Q,
|
||
double R
|
||
) {
|
||
// 实现 LQR 算法
|
||
// ...
|
||
return steering_angle;
|
||
}
|
||
```
|
||
|
||
#### Step 3: 在 `path_tracker.cpp` 中集成
|
||
|
||
```cpp
|
||
bool PathTracker::generateControlSequence(..., const std::string& algorithm) {
|
||
// ...
|
||
if (algorithm == "lqr") {
|
||
delta = ControlGenerator::lqr(current_state, path_, Q, R);
|
||
}
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### Step 4: 编写测试
|
||
|
||
```cpp
|
||
// examples/test_lqr.cpp
|
||
#include "path_tracker.h"
|
||
|
||
int main() {
|
||
AGVModel model(2.0, 1.0);
|
||
PathTracker tracker(model);
|
||
PathCurve path = PathCurve::generateCircularPath(5.0, 100);
|
||
|
||
tracker.setReferencePath(path);
|
||
tracker.generateControlSequence("lqr", 0.1, 10.0, 1.0);
|
||
|
||
// 验证结果...
|
||
}
|
||
```
|
||
|
||
### 3. 添加新的硬件接口
|
||
|
||
假设要添加对新电机控制器的支持:
|
||
|
||
#### Step 1: 创建新的控制器类
|
||
|
||
```cpp
|
||
// include/can/NewMotorController.h
|
||
class NewMotorController {
|
||
public:
|
||
bool initialize(...);
|
||
void setSpeed(double speed);
|
||
void setSteering(double angle);
|
||
// ...
|
||
};
|
||
```
|
||
|
||
#### Step 2: 实现接口
|
||
|
||
```cpp
|
||
// src/control/can/NewMotorController.cpp
|
||
#include "can/NewMotorController.h"
|
||
|
||
bool NewMotorController::initialize(...) {
|
||
// 初始化硬件
|
||
}
|
||
|
||
void NewMotorController::setSpeed(double speed) {
|
||
// 发送速度命令
|
||
}
|
||
```
|
||
|
||
#### Step 3: 创建示例程序
|
||
|
||
```cpp
|
||
// examples/new_motor_demo.cpp
|
||
#include "can/NewMotorController.h"
|
||
|
||
int main() {
|
||
NewMotorController controller;
|
||
controller.initialize(...);
|
||
controller.setSpeed(1.0);
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 编码规范
|
||
|
||
### 命名约定
|
||
|
||
- **类名**:大驼峰(PascalCase)
|
||
```cpp
|
||
class PathTracker { };
|
||
```
|
||
|
||
- **函数名**:小驼峰(camelCase)
|
||
```cpp
|
||
void setReferencePath(...);
|
||
```
|
||
|
||
- **变量名**:下划线分隔(snake_case)
|
||
```cpp
|
||
double lookahead_distance;
|
||
```
|
||
|
||
- **常量**:全大写加下划线
|
||
```cpp
|
||
const double MAX_SPEED = 10.0;
|
||
```
|
||
|
||
- **成员变量**:后缀下划线
|
||
```cpp
|
||
class MyClass {
|
||
private:
|
||
int value_;
|
||
};
|
||
```
|
||
|
||
### 注释规范
|
||
|
||
#### 头文件注释
|
||
|
||
```cpp
|
||
/**
|
||
* @file path_tracker.h
|
||
* @brief 路径跟踪器类定义
|
||
* @author Your Name
|
||
* @date 2025-11-27
|
||
*/
|
||
```
|
||
|
||
#### 类注释
|
||
|
||
```cpp
|
||
/**
|
||
* @brief 路径跟踪器
|
||
*
|
||
* 整合 AGV 模型、路径和控制算法,提供完整的路径跟踪功能。
|
||
* 支持多种算法:Pure Pursuit、Stanley 等。
|
||
*/
|
||
class PathTracker {
|
||
// ...
|
||
};
|
||
```
|
||
|
||
#### 函数注释
|
||
|
||
```cpp
|
||
/**
|
||
* @brief 生成控制序列
|
||
* @param algorithm 算法类型("pure_pursuit" 或 "stanley")
|
||
* @param dt 时间步长(秒)
|
||
* @param horizon 时域(秒)
|
||
* @param desired_velocity 期望速度(m/s)
|
||
* @return 成功返回 true,失败返回 false
|
||
*/
|
||
bool generateControlSequence(
|
||
const std::string& algorithm,
|
||
double dt,
|
||
double horizon,
|
||
double desired_velocity
|
||
);
|
||
```
|
||
|
||
### 代码风格
|
||
|
||
#### 缩进和空格
|
||
|
||
- 使用 4 个空格缩进
|
||
- 运算符两侧加空格
|
||
- 逗号后加空格
|
||
|
||
```cpp
|
||
// 好的风格
|
||
int result = a + b * c;
|
||
function(arg1, arg2, arg3);
|
||
|
||
// 不好的风格
|
||
int result=a+b*c;
|
||
function(arg1,arg2,arg3);
|
||
```
|
||
|
||
#### 大括号
|
||
|
||
```cpp
|
||
// 推荐:K&R 风格
|
||
if (condition) {
|
||
// code
|
||
} else {
|
||
// code
|
||
}
|
||
|
||
// 或 Allman 风格(保持一致即可)
|
||
if (condition)
|
||
{
|
||
// code
|
||
}
|
||
else
|
||
{
|
||
// code
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 测试指南
|
||
|
||
### 单元测试
|
||
|
||
创建测试文件 `src/control/tests/test_path_tracker.cpp`:
|
||
|
||
```cpp
|
||
#include "path_tracker.h"
|
||
#include <cassert>
|
||
#include <iostream>
|
||
|
||
void test_circular_path_tracking() {
|
||
AGVModel model(2.0, 1.0);
|
||
PathTracker tracker(model);
|
||
|
||
PathCurve path = PathCurve::generateCircularPath(5.0, 100);
|
||
tracker.setReferencePath(path);
|
||
|
||
AGVModel::State initial(0, -5, M_PI/2, 0);
|
||
tracker.setInitialState(initial);
|
||
|
||
bool success = tracker.generateControlSequence("pure_pursuit", 0.1, 10.0, 1.0);
|
||
assert(success && "Failed to generate control sequence");
|
||
|
||
const auto& sequence = tracker.getControlSequence();
|
||
assert(!sequence.predicted_states.empty() && "Empty control sequence");
|
||
|
||
std::cout << "✓ Circular path tracking test passed" << std::endl;
|
||
}
|
||
|
||
int main() {
|
||
test_circular_path_tracking();
|
||
// 更多测试...
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
### 集成测试
|
||
|
||
测试完整的系统流程:
|
||
|
||
```cpp
|
||
void test_full_system() {
|
||
// 1. 创建模型
|
||
AGVModel model(2.0, 1.0);
|
||
PathTracker tracker(model);
|
||
|
||
// 2. 加载路径
|
||
PathCurve path = PathCurve::loadFromCSV("test_path.csv");
|
||
tracker.setReferencePath(path);
|
||
|
||
// 3. 生成控制
|
||
tracker.generateControlSequence("stanley", 0.1, 10.0, 1.0);
|
||
|
||
// 4. 导出结果
|
||
tracker.exportToCSV("test_output.csv");
|
||
|
||
// 5. 验证结果
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 调试技巧
|
||
|
||
### 1. 日志输出
|
||
|
||
在关键位置添加日志:
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
#include <iomanip>
|
||
|
||
void PathTracker::generateControlSequence(...) {
|
||
std::cout << "[DEBUG] Starting control generation" << std::endl;
|
||
std::cout << " Algorithm: " << algorithm << std::endl;
|
||
std::cout << " dt: " << dt << ", horizon: " << horizon << std::endl;
|
||
|
||
// ...
|
||
|
||
std::cout << "[DEBUG] Generated " << steps << " control steps" << std::endl;
|
||
}
|
||
```
|
||
|
||
### 2. 数据可视化
|
||
|
||
使用 Python 脚本可视化结果:
|
||
|
||
```python
|
||
import pandas as pd
|
||
import matplotlib.pyplot as plt
|
||
|
||
data = pd.read_csv('output.csv')
|
||
plt.figure(figsize=(10, 10))
|
||
plt.plot(data['ref_x'], data['ref_y'], 'b-', label='Reference')
|
||
plt.plot(data['x'], data['y'], 'r--', label='Trajectory')
|
||
plt.legend()
|
||
plt.axis('equal')
|
||
plt.savefig('debug_plot.png')
|
||
```
|
||
|
||
### 3. GDB 调试
|
||
|
||
```bash
|
||
# 编译时添加调试符号
|
||
cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||
make
|
||
|
||
# 使用 GDB
|
||
gdb ./agv_demo
|
||
(gdb) break path_tracker.cpp:50
|
||
(gdb) run
|
||
(gdb) print current_state
|
||
```
|
||
|
||
### 4. Valgrind 内存检查(Linux)
|
||
|
||
```bash
|
||
valgrind --leak-check=full ./agv_demo
|
||
```
|
||
|
||
---
|
||
|
||
## 性能优化
|
||
|
||
### 1. 编译优化
|
||
|
||
```cmake
|
||
# CMakeLists.txt
|
||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native")
|
||
```
|
||
|
||
### 2. 代码优化
|
||
|
||
- 避免不必要的拷贝,使用引用
|
||
- 预分配内存
|
||
- 使用合适的数据结构
|
||
|
||
```cpp
|
||
// 好的实践
|
||
void processPath(const PathCurve& path) { // 使用引用
|
||
std::vector<double> results;
|
||
results.reserve(path.size()); // 预分配
|
||
// ...
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### Q: 编译时找不到 Qt
|
||
|
||
**A**: 设置 CMAKE_PREFIX_PATH:
|
||
|
||
```bash
|
||
cmake -DCMAKE_PREFIX_PATH=/path/to/Qt/6.10.1/mingw_64 ..
|
||
```
|
||
|
||
### Q: CAN 通信失败
|
||
|
||
**A**: 检查:
|
||
1. ControlCAN.dll 是否在 PATH 中
|
||
2. 硬件是否正确连接
|
||
3. 波特率是否匹配
|
||
|
||
### Q: 路径跟踪精度不够
|
||
|
||
**A**: 尝试:
|
||
1. 减小时间步长 dt
|
||
2. 调整算法参数(lookahead_distance、k 等)
|
||
3. 增加路径点密度
|
||
|
||
---
|
||
|
||
## 贡献指南
|
||
|
||
1. Fork 项目
|
||
2. 创建功能分支 (`git checkout -b feature/new-feature`)
|
||
3. 提交更改 (`git commit -am 'Add new feature'`)
|
||
4. 推送到分支 (`git push origin feature/new-feature`)
|
||
5. 创建 Pull Request
|
||
|
||
---
|
||
|
||
**最后更新**: 2025-11-27
|