Files
RCS-3000/docs/fixes/CSV_LOAD_FIX.md
CaiXiang af65c2425d initial
2025-11-14 16:09:58 +08:00

230 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CSV加载闪退问题修复方案
## 问题分析
经过代码审查,发现"Load from CSV"功能闪退的可能原因:
1. **Windows路径编码问题**(最可能的原因)
-`examples/qt_gui_demo.cpp`第309行和326行使用了`QString::toStdString()`
- 在Windows MINGW环境下当文件路径包含中文字符或特殊字符时这种转换可能产生错误的编码
- 导致文件路径无法正确打开,或在某些情况下导致程序崩溃
2. **单点路径处理问题**
-`src/path_curve.cpp``setPathPoints`函数中当CSV文件只包含1个数据点时该点的theta和kappa不会被正确初始化
3. **潜在的异常处理不完整**
- CSV解析过程中的某些异常可能未被完全捕获
## 修复方案
### 修复1: 更正文件路径编码(重要)
**文件**: `examples/qt_gui_demo.cpp`
**第309行** 需要修改为:
```cpp
// 原代码 (第308-317行):
if (!filename.isEmpty()) {
if (custom_path_.loadFromCSV(filename.toStdString(), true)) {
custom_path_loaded_ = true;
QMessageBox::information(this, "Success",
QString("Loaded %1 points from CSV!").arg(
custom_path_.getPathPoints().size()));
} else {
QMessageBox::warning(this, "Error", "Failed to load CSV file!");
}
}
// 修改为:
if (!filename.isEmpty()) {
// 使用toLocal8Bit以正确处理Windows路径包括中文路径
std::string filepath = filename.toLocal8Bit().constData();
if (custom_path_.loadFromCSV(filepath, true)) {
custom_path_loaded_ = true;
QMessageBox::information(this, "Success",
QString("Loaded %1 points from CSV!").arg(
custom_path_.getPathPoints().size()));
} else {
QMessageBox::warning(this, "Error", "Failed to load CSV file!");
}
}
```
**第326行** 需要修改为:
```cpp
// 原代码 (第325-329行):
if (!filename.isEmpty() && custom_path_loaded_) {
if (custom_path_.saveToCSV(filename.toStdString())) {
QMessageBox::information(this, "Success", "Path saved!");
}
}
// 修改为:
if (!filename.isEmpty() && custom_path_loaded_) {
// 使用toLocal8Bit以正确处理Windows路径包括中文路径
std::string filepath = filename.toLocal8Bit().constData();
if (custom_path_.saveToCSV(filepath)) {
QMessageBox::information(this, "Success", "Path saved!");
}
}
```
### 修复2: 改进单点路径处理
**文件**: `src/path_curve.cpp`
`setPathPoints`函数第106-134行添加单点处理逻辑
```cpp
void PathCurve::setPathPoints(const std::vector<PathPoint>& points) {
path_points_ = points;
// 计算每个点的切线方向和曲率
for (size_t i = 0; i < path_points_.size(); ++i) {
if (i == 0 && path_points_.size() > 1) {
// 第一个点
double dx = path_points_[i + 1].x - path_points_[i].x;
double dy = path_points_[i + 1].y - path_points_[i].y;
path_points_[i].theta = std::atan2(dy, dx);
} else if (i == path_points_.size() - 1 && path_points_.size() > 1) {
// 最后一个点
double dx = path_points_[i].x - path_points_[i - 1].x;
double dy = path_points_[i].y - path_points_[i - 1].y;
path_points_[i].theta = std::atan2(dy, dx);
} else if (path_points_.size() > 2) {
// 中间点
double dx = path_points_[i + 1].x - path_points_[i - 1].x;
double dy = path_points_[i + 1].y - path_points_[i - 1].y;
path_points_[i].theta = std::atan2(dy, dx);
// 计算曲率(使用三点法)
if (i > 0 && i < path_points_.size() - 1) {
path_points_[i].kappa = computeCurvature(
path_points_[i - 1], path_points_[i], path_points_[i + 1]);
}
}
// 添加: 处理只有单个点的情况
else if (path_points_.size() == 1) {
// 单个点保持其原有的theta和kappa值通常为0
// 不需要额外计算
}
}
}
```
### 修复3: 添加更完善的异常处理
**文件**: `src/path_curve_custom.cpp`
`loadFromCSV`函数中添加更完善的错误处理:
```cpp
bool PathCurve::loadFromCSV(const std::string& filename, bool has_header) {
try {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Error: Cannot open file " << filename << std::endl;
return false;
}
std::vector<PathPoint> points;
std::string line;
int line_num = 0;
// 跳过表头
if (has_header && std::getline(file, line)) {
line_num++;
}
while (std::getline(file, line)) {
line_num++;
// 跳过空行和注释行
if (line.empty() || line[0] == '#') {
continue;
}
std::stringstream ss(line);
std::string token;
std::vector<double> values;
bool parse_error = false;
// 解析CSV行
while (std::getline(ss, token, ',')) {
try {
// 去除前后空格
size_t start = token.find_first_not_of(" \t\r\n");
size_t end = token.find_last_not_of(" \t\r\n");
if (start == std::string::npos) {
// 空token跳过整行
parse_error = true;
break;
}
std::string trimmed = token.substr(start, end - start + 1);
values.push_back(std::stod(trimmed));
} catch (const std::exception& e) {
std::cerr << "Error parsing line " << line_num << ": " << line
<< " (reason: " << e.what() << ")" << std::endl;
parse_error = true;
break;
}
}
// 如果解析出错或值数量不足,跳过整行
if (parse_error) {
continue;
}
// 根据列数创建路径点
if (values.size() >= 2) {
PathPoint p;
p.x = values[0];
p.y = values[1];
p.theta = (values.size() >= 3) ? values[2] : 0.0;
p.kappa = (values.size() >= 4) ? values[3] : 0.0;
points.push_back(p);
}
}
file.close();
if (points.empty()) {
std::cerr << "Error: No valid path points loaded from " << filename << std::endl;
return false;
}
// 设置路径点会自动计算theta和kappa
setPathPoints(points);
std::cout << "Successfully loaded " << points.size() << " points from " << filename << std::endl;
return true;
} catch (const std::exception& e) {
std::cerr << "Exception in loadFromCSV: " << e.what() << std::endl;
return false;
}
}
```
## 测试建议
修复后,建议测试以下场景:
1. 加载包含中文路径的CSV文件
2. 加载只有2列x, y的CSV文件
3. 加载完整4列x, y, theta, kappa的CSV文件
4. 加载只有1个数据点的CSV文件
5. 加载空的CSV文件只有header
## 编译和重新生成
修改完成后,需要重新编译项目:
```bash
cd build
cmake --build . --config Release
# 或
cmake --build . --config Debug
```
编译完成后,运行 `agv_qt_gui.exe` 并测试CSV加载功能。