230 lines
7.4 KiB
Markdown
230 lines
7.4 KiB
Markdown
# 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加载功能。
|