#include "dialogalgoarg.h" #include "ui_dialogalgoarg.h" #include #include #include #include #include #include #include #include #include #include #include #include "PathManager.h" #include "StyledMessageBox.h" #include "VrLog.h" DialogAlgoarg::DialogAlgoarg(ConfigManager* configManager, QWidget *parent) : QDialog(parent) , ui(new Ui::DialogAlgoarg) , m_pConfigManager(configManager) { try { ui->setupUi(this); // 获取配置文件路径 m_configFilePath = PathManager::GetInstance().GetConfigFilePath(); // 检查配置文件路径是否有效 if (m_configFilePath.isEmpty()) { StyledMessageBox::critical(this, "错误", "无法获取配置文件路径!"); return; } // 初始化旋转顺序下拉框 InitEulerOrderComboBox(); // 初始化标定相机选择下拉框 InitCalibCameraComboBox(); // 初始化姿态输出顺序下拉框 InitPoseOutputOrderComboBox(); // 初始化方向向量反向下拉框 InitDirVectorInvertComboBox(); // 初始化排序模式下拉框 InitSortModeComboBox(); // 从配置文件加载数据到界面 LoadConfigToUI(); } catch (const std::exception& e) { StyledMessageBox::critical(this, "初始化错误", QString("对话框初始化失败: %1").arg(e.what())); } catch (...) { StyledMessageBox::critical(this, "初始化错误", "对话框初始化失败!(未知错误)"); } } DialogAlgoarg::~DialogAlgoarg() { delete ui; } void DialogAlgoarg::LoadConfigToUI() { if (!m_pConfigManager) { StyledMessageBox::critical(this, "错误", "配置对象未初始化!"); return; } try { // 从ConfigManager获取配置数据 ConfigResult configData = m_pConfigManager->GetConfigResult(); // 检查配置文件路径是否有效 if (m_configFilePath.isEmpty()) { LOG_WARNING("Config file path is empty\n"); } // 加载算法参数到UI const VrAlgorithmParams& algoParams = configData.algorithmParams; // 加载各个参数组 LoadDetectionParamToUI(algoParams.detectionParam); LoadFilterParamToUI(algoParams.filterParam); LoadSortModeToUI(algoParams.sortMode); // 加载网络配置(PLC和机械臂服务端) LoadPlcRobotServerConfigToUI(configData.plcRobotServerConfig); // 初始化标定矩阵缓存(必须在LoadCalibMatrixToUI之前调用) m_calibMatrixCache = configData.handEyeCalibMatrixList; // 确保缓存至少有一个元素(防止配置文件中为空) if (m_calibMatrixCache.empty()) { LOG_WARNING("handEyeCalibMatrixList is empty, adding default matrix\n"); m_calibMatrixCache.push_back(VrHandEyeCalibMatrix()); } // 加载手眼标定矩阵 LoadCalibMatrixToUI(); } catch (const std::exception& e) { LOG_ERROR("Exception in LoadConfigToUI: %s\n", e.what()); StyledMessageBox::warning(this, "警告", QString("加载配置时发生异常: %1\n将使用默认参数显示").arg(e.what())); // 发生异常时使用默认参数 ConfigResult configData; const VrAlgorithmParams& algoParams = configData.algorithmParams; LoadDetectionParamToUI(algoParams.detectionParam); LoadFilterParamToUI(algoParams.filterParam); LoadSortModeToUI(algoParams.sortMode); } catch (...) { LOG_ERROR("Unknown exception in LoadConfigToUI\n"); StyledMessageBox::warning(this, "警告", "加载配置文件失败(未知错误),将使用默认参数显示"); // 发生未知异常时使用默认参数 ConfigResult configData; const VrAlgorithmParams& algoParams = configData.algorithmParams; LoadDetectionParamToUI(algoParams.detectionParam); LoadFilterParamToUI(algoParams.filterParam); LoadSortModeToUI(algoParams.sortMode); } } void DialogAlgoarg::LoadDetectionParamToUI(const VrHoleDetectionParam& param) { if (!ui) return; ui->lineEdit_neighborCount->setText(QString::number(param.neighborCount)); ui->lineEdit_angleThresholdPos->setText(QString::number(param.angleThresholdPos)); ui->lineEdit_angleThresholdNeg->setText(QString::number(param.angleThresholdNeg)); ui->lineEdit_minPitDepth->setText(QString::number(param.minPitDepth)); ui->lineEdit_minRadius->setText(QString::number(param.minRadius)); ui->lineEdit_maxRadius->setText(QString::number(param.maxRadius)); ui->lineEdit_expansionSize1->setText(QString::number(param.expansionSize1)); ui->lineEdit_expansionSize2->setText(QString::number(param.expansionSize2)); ui->lineEdit_minVTransitionPoints->setText(QString::number(param.minVTransitionPoints)); } void DialogAlgoarg::LoadFilterParamToUI(const VrHoleFilterParam& param) { if (!ui) return; ui->lineEdit_maxEccentricity->setText(QString::number(param.maxEccentricity)); ui->lineEdit_minAngularCoverage->setText(QString::number(param.minAngularCoverage)); ui->lineEdit_maxRadiusFitRatio->setText(QString::number(param.maxRadiusFitRatio)); ui->lineEdit_minQualityScore->setText(QString::number(param.minQualityScore)); ui->lineEdit_maxPlaneResidual->setText(QString::number(param.maxPlaneResidual)); ui->lineEdit_maxAngularGap->setText(QString::number(param.maxAngularGap)); ui->lineEdit_minInlierRatio->setText(QString::number(param.minInlierRatio)); } void DialogAlgoarg::LoadSortModeToUI(int sortMode) { if (!ui) return; int index = ui->comboBox_sortMode->findData(sortMode); if (index >= 0) { ui->comboBox_sortMode->setCurrentIndex(index); } } bool DialogAlgoarg::SaveConfigFromUI() { if (!m_pConfigManager) { return false; } try { // 获取当前配置 SystemConfig systemConfig = m_pConfigManager->GetConfig(); VrAlgorithmParams& algoParams = systemConfig.configResult.algorithmParams; // 保存检测参数 if (!SaveDetectionParamFromUI(algoParams.detectionParam)) { StyledMessageBox::warning(this, "错误", "检测参数输入有误,请检查!"); return false; } // 保存过滤参数 if (!SaveFilterParamFromUI(algoParams.filterParam)) { StyledMessageBox::warning(this, "错误", "过滤参数输入有误,请检查!"); return false; } // 保存排序模式 if (!SaveSortModeFromUI(algoParams.sortMode)) { StyledMessageBox::warning(this, "错误", "排序模式设置有误,请检查!"); return false; } // 保存网络配置(PLC和机械臂服务端) if (!SavePlcRobotServerConfigFromUI(systemConfig.configResult.plcRobotServerConfig)) { StyledMessageBox::warning(this, "错误", "网络配置输入有误,请检查!"); return false; } // 保存手眼标定矩阵到配置(多相机) if (!SaveCalibMatrixToConfig(systemConfig.configResult.handEyeCalibMatrixList)) { StyledMessageBox::warning(this, "错误", "手眼标定矩阵输入有误,请检查!"); return false; } // 更新并保存配置到文件 if (!m_pConfigManager->UpdateFullConfig(systemConfig)) { return false; } return m_pConfigManager->SaveConfigToFile(m_configFilePath.toStdString()); } catch (const std::exception& e) { return false; } } bool DialogAlgoarg::SaveDetectionParamFromUI(VrHoleDetectionParam& param) { bool ok = true; param.neighborCount = ui->lineEdit_neighborCount->text().toInt(&ok); if (!ok) return false; param.angleThresholdPos = ui->lineEdit_angleThresholdPos->text().toDouble(&ok); if (!ok) return false; param.angleThresholdNeg = ui->lineEdit_angleThresholdNeg->text().toDouble(&ok); if (!ok) return false; param.minPitDepth = ui->lineEdit_minPitDepth->text().toDouble(&ok); if (!ok) return false; param.minRadius = ui->lineEdit_minRadius->text().toDouble(&ok); if (!ok) return false; param.maxRadius = ui->lineEdit_maxRadius->text().toDouble(&ok); if (!ok) return false; param.expansionSize1 = ui->lineEdit_expansionSize1->text().toInt(&ok); if (!ok) return false; param.expansionSize2 = ui->lineEdit_expansionSize2->text().toInt(&ok); if (!ok) return false; param.minVTransitionPoints = ui->lineEdit_minVTransitionPoints->text().toInt(&ok); if (!ok) return false; return true; } bool DialogAlgoarg::SaveFilterParamFromUI(VrHoleFilterParam& param) { bool ok = true; param.maxEccentricity = ui->lineEdit_maxEccentricity->text().toDouble(&ok); if (!ok) return false; param.minAngularCoverage = ui->lineEdit_minAngularCoverage->text().toDouble(&ok); if (!ok) return false; param.maxRadiusFitRatio = ui->lineEdit_maxRadiusFitRatio->text().toDouble(&ok); if (!ok) return false; param.minQualityScore = ui->lineEdit_minQualityScore->text().toDouble(&ok); if (!ok) return false; param.maxPlaneResidual = ui->lineEdit_maxPlaneResidual->text().toDouble(&ok); if (!ok) return false; param.maxAngularGap = ui->lineEdit_maxAngularGap->text().toDouble(&ok); if (!ok) return false; param.minInlierRatio = ui->lineEdit_minInlierRatio->text().toDouble(&ok); if (!ok) return false; return true; } bool DialogAlgoarg::SaveSortModeFromUI(int& sortMode) { if (!ui) return false; sortMode = ui->comboBox_sortMode->currentData().toInt(); return true; } void DialogAlgoarg::on_btn_camer_ok_clicked() { if (SaveConfigFromUI()) { StyledMessageBox::information(this, "成功", "配置保存成功!"); accept(); } else { StyledMessageBox::warning(this, "失败", "配置保存失败,请检查文件权限或参数输入!"); } } void DialogAlgoarg::on_btn_camer_cancel_clicked() { reject(); } void DialogAlgoarg::on_btn_loadCalibMatrix_clicked() { QString filePath = QFileDialog::getOpenFileName( this, "选择手眼标定文件", QString(), "INI文件 (*.ini);;所有文件 (*.*)" ); if (filePath.isEmpty()) { return; } LoadCalibMatrixFromFile(filePath); StyledMessageBox::information(this, "提示", QString("已从文件导入标定矩阵到相机 %1").arg(m_currentCalibCameraIndex + 1)); } void DialogAlgoarg::onCalibCameraChanged(int index) { // 切换前,先保存当前相机的矩阵到缓存 SaveCurrentCalibMatrixToCache(); // 更新当前选中的相机索引 m_currentCalibCameraIndex = index; // 如果缓存中有对应相机的数据,加载到 UI LoadCalibMatrixForCamera(index); } void DialogAlgoarg::InitCalibCameraComboBox() { if (!m_pConfigManager) return; ConfigResult configData = m_pConfigManager->GetConfigResult(); // 初始化标定矩阵缓存 m_calibMatrixCache = configData.handEyeCalibMatrixList; // 根据相机列表填充下拉框 ui->comboBox_calibCamera->blockSignals(true); ui->comboBox_calibCamera->clear(); if (configData.cameraList.empty()) { // 没有相机配置时,至少添加一个默认项 ui->comboBox_calibCamera->addItem("相机 1", 0); // 确保缓存至少有一个元素 if (m_calibMatrixCache.empty()) { m_calibMatrixCache.push_back(VrHandEyeCalibMatrix()); } } else { for (size_t i = 0; i < configData.cameraList.size(); i++) { QString cameraName = QString::fromStdString(configData.cameraList[i].name); if (cameraName.isEmpty()) { cameraName = QString("相机 %1").arg(i + 1); } ui->comboBox_calibCamera->addItem(cameraName, static_cast(i)); } // 确保缓存数量与相机数量匹配 while (m_calibMatrixCache.size() < configData.cameraList.size()) { m_calibMatrixCache.push_back(VrHandEyeCalibMatrix()); } } ui->comboBox_calibCamera->blockSignals(false); // 连接信号 connect(ui->comboBox_calibCamera, QOverload::of(&QComboBox::currentIndexChanged), this, &DialogAlgoarg::onCalibCameraChanged); m_currentCalibCameraIndex = 0; } QLineEdit* DialogAlgoarg::GetCalibLineEdit(int row, int col) { // 通过名称查找对应的QLineEdit (UI中使用 lineEdit_calib_row_col 格式) QString name = QString("lineEdit_calib_%1_%2").arg(row).arg(col); QLineEdit* edit = findChild(name); if (!edit) { LOG_WARNING("LineEdit not found: %s\n", name.toStdString().c_str()); } return edit; } void DialogAlgoarg::LoadCalibMatrixToUI() { // 加载当前选中相机的标定矩阵 LoadCalibMatrixForCamera(m_currentCalibCameraIndex); } void DialogAlgoarg::LoadCalibMatrixForCamera(int cameraIndex) { LOG_INFO("LoadCalibMatrixForCamera: cameraIndex=%d, cacheSize=%zu\n", cameraIndex, m_calibMatrixCache.size()); // 检查索引是否有效 if (cameraIndex < 0 || cameraIndex >= static_cast(m_calibMatrixCache.size())) { LOG_WARNING("Invalid camera index, loading identity matrix\n"); LoadIdentityMatrixToUI(); return; } const VrHandEyeCalibMatrix& calibMatrix = m_calibMatrixCache[cameraIndex]; // 打印矩阵前4个元素用于调试 LOG_INFO("Matrix values: [0]=%.6f, [1]=%.6f, [2]=%.6f, [3]=%.6f\n", calibMatrix.matrix[0], calibMatrix.matrix[1], calibMatrix.matrix[2], calibMatrix.matrix[3]); // 直接加载矩阵数据到UI(不再检查是否为空) for (int i = 0; i < 16; i++) { QLineEdit* edit = GetCalibLineEdit(i / 4, i % 4); if (edit) { edit->setText(QString::number(calibMatrix.matrix[i], 'f', 6)); } else { LOG_WARNING("LineEdit not found for matrix[%d]\n", i); } } // 加载旋转顺序 int eulerOrder = calibMatrix.eulerOrder; LOG_INFO("Loading eulerOrder: %d\n", eulerOrder); int index = ui->comboBox_eulerOrder->findData(eulerOrder); if (index >= 0) { ui->comboBox_eulerOrder->setCurrentIndex(index); } else { LOG_WARNING("EulerOrder %d not found in comboBox, using default\n", eulerOrder); ui->comboBox_eulerOrder->setCurrentIndex(0); } LOG_INFO("LoadCalibMatrixForCamera completed\n"); } void DialogAlgoarg::LoadIdentityMatrixToUI() { LOG_INFO("LoadIdentityMatrixToUI called\n"); // 显示单位矩阵 for (int i = 0; i < 16; i++) { int row = i / 4; int col = i % 4; QLineEdit* edit = GetCalibLineEdit(row, col); if (edit) { // 对角线元素为1,其他为0 double value = (row == col) ? 1.0 : 0.0; edit->setText(QString::number(value, 'f', 6)); } else { LOG_WARNING("LineEdit not found for identity matrix[%d]\n", i); } } // 默认旋转顺序:外旋ZYX int index = ui->comboBox_eulerOrder->findData(11); if (index >= 0) { ui->comboBox_eulerOrder->setCurrentIndex(index); } else { LOG_WARNING("Default eulerOrder 11 not found, using index 0\n"); ui->comboBox_eulerOrder->setCurrentIndex(0); } LOG_INFO("LoadIdentityMatrixToUI completed\n"); } void DialogAlgoarg::LoadCalibMatrixFromFile(const QString& filePath) { QSettings settings(filePath, QSettings::IniFormat); // 根据当前选中的相机索引决定 INI section QString section = QString("CalibMatrixInfo_%1").arg(m_currentCalibCameraIndex); settings.beginGroup(section); // 如果该 section 不存在,尝试默认 section if (settings.childKeys().isEmpty()) { settings.endGroup(); settings.beginGroup("CalibMatrixInfo_0"); } for (int i = 0; i < 16; i++) { int row = i / 4; int col = i % 4; QString key = QString("dCalibMatrix_%1").arg(i); double val = settings.value(key, (row == col) ? 1.0 : 0.0).toDouble(); QLineEdit* edit = GetCalibLineEdit(row, col); if (edit) { edit->setText(QString::number(val, 'f', 6)); } } settings.endGroup(); } bool DialogAlgoarg::SaveCurrentCalibMatrixToCache() { if (m_currentCalibCameraIndex < 0 || m_currentCalibCameraIndex >= static_cast(m_calibMatrixCache.size())) { LOG_ERROR("Invalid camera index: %d, cache size: %zu\n", m_currentCalibCameraIndex, m_calibMatrixCache.size()); return false; } VrHandEyeCalibMatrix& calibMatrix = m_calibMatrixCache[m_currentCalibCameraIndex]; for (int i = 0; i < 16; i++) { int row = i / 4; int col = i % 4; QLineEdit* edit = GetCalibLineEdit(row, col); if (!edit) { LOG_ERROR("LineEdit not found for matrix[%d][%d] (index=%d)\n", row, col, i); StyledMessageBox::warning(this, "错误", QString("找不到标定矩阵输入框 [%1][%2],请检查UI设计!").arg(row).arg(col)); return false; } QString text = edit->text().trimmed(); if (text.isEmpty()) { LOG_ERROR("Empty input for matrix[%d][%d] (index=%d)\n", row, col, i); StyledMessageBox::warning(this, "错误", QString("标定矩阵 [%1][%2] 不能为空!").arg(row).arg(col)); return false; } bool ok = false; calibMatrix.matrix[i] = text.toDouble(&ok); if (!ok) { LOG_ERROR("Invalid number format for matrix[%d][%d]: '%s'\n", row, col, text.toStdString().c_str()); StyledMessageBox::warning(this, "错误", QString("标定矩阵 [%1][%2] 输入格式错误:'%3'\n请输入有效的数字!").arg(row).arg(col).arg(text)); return false; } } calibMatrix.eulerOrder = ui->comboBox_eulerOrder->currentData().toInt(); LOG_INFO("Successfully saved calib matrix for camera %d\n", m_currentCalibCameraIndex); return true; } bool DialogAlgoarg::SaveCalibMatrixToConfig(std::vector& calibMatrixList) { // 先保存当前编辑中的相机矩阵到缓存 if (!SaveCurrentCalibMatrixToCache()) { return false; } // 将缓存写入输出参数 calibMatrixList = m_calibMatrixCache; return true; } void DialogAlgoarg::InitEulerOrderComboBox() { // 添加外旋旋转顺序选项(工业机械臂常用) ui->comboBox_eulerOrder->addItem("RZ-RY-RX (外旋ZYX)", 11); // ABB、KUKA、发那科等常用 ui->comboBox_eulerOrder->addItem("RX-RY-RZ (外旋XYZ)", 10); ui->comboBox_eulerOrder->addItem("RZ-RX-RY (外旋ZXY)", 12); ui->comboBox_eulerOrder->addItem("RY-RX-RZ (外旋YXZ)", 13); ui->comboBox_eulerOrder->addItem("RY-RZ-RX (外旋YZX)", 14); ui->comboBox_eulerOrder->addItem("RX-RZ-RY (外旋XZY)", 15); // 默认选择外旋 ZYX ui->comboBox_eulerOrder->setCurrentIndex(0); } void DialogAlgoarg::InitPoseOutputOrderComboBox() { // 添加姿态输出顺序选项 ui->comboBox_poseOutputOrder->addItem("RPY (Roll, Pitch, Yaw)", POSE_ORDER_RPY); // 默认 ui->comboBox_poseOutputOrder->addItem("RYP (Roll, Yaw, Pitch)", POSE_ORDER_RYP); ui->comboBox_poseOutputOrder->addItem("PRY (Pitch, Roll, Yaw)", POSE_ORDER_PRY); ui->comboBox_poseOutputOrder->addItem("PYR (Pitch, Yaw, Roll)", POSE_ORDER_PYR); ui->comboBox_poseOutputOrder->addItem("YRP (Yaw, Roll, Pitch)", POSE_ORDER_YRP); ui->comboBox_poseOutputOrder->addItem("YPR (Yaw, Pitch, Roll)", POSE_ORDER_YPR); // 默认选择 RPY ui->comboBox_poseOutputOrder->setCurrentIndex(0); } void DialogAlgoarg::InitDirVectorInvertComboBox() { // 添加方向向量反向选项 ui->comboBox_dirVectorInvert->addItem("不反向", DIR_INVERT_NONE); ui->comboBox_dirVectorInvert->addItem("XY反向", DIR_INVERT_XY); ui->comboBox_dirVectorInvert->addItem("XZ反向", DIR_INVERT_XZ); ui->comboBox_dirVectorInvert->addItem("YZ反向(默认)", DIR_INVERT_YZ); // 默认选择 YZ反向(兼容原有行为) ui->comboBox_dirVectorInvert->setCurrentIndex(3); } void DialogAlgoarg::InitSortModeComboBox() { // 添加排序模式选项 ui->comboBox_sortMode->addItem("不排序", HOLE_SORT_NONE); ui->comboBox_sortMode->addItem("按半径排序(最大优先)", HOLE_SORT_BY_RADIUS); ui->comboBox_sortMode->addItem("按深度排序(最深优先)", HOLE_SORT_BY_DEPTH); ui->comboBox_sortMode->addItem("按质量分数排序(最高优先)", HOLE_SORT_BY_QUALITY); // 默认不排序 ui->comboBox_sortMode->setCurrentIndex(0); } void DialogAlgoarg::LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config) { if (!ui) return; // 加载TCP协议端口 ui->lineEdit_tcpServerPort->setText(QString::number(config.tcpServerPort)); // 加载姿态输出顺序 int poseOrderIndex = ui->comboBox_poseOutputOrder->findData(config.poseOutputOrder); if (poseOrderIndex >= 0) { ui->comboBox_poseOutputOrder->setCurrentIndex(poseOrderIndex); } // 加载方向向量反向配置 int dirInvertIndex = ui->comboBox_dirVectorInvert->findData(config.dirVectorInvert); if (dirInvertIndex >= 0) { ui->comboBox_dirVectorInvert->setCurrentIndex(dirInvertIndex); } } bool DialogAlgoarg::SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config) { if (!ui) return false; // 获取TCP协议端口 bool ok = false; config.tcpServerPort = ui->lineEdit_tcpServerPort->text().toInt(&ok); if (!ok || config.tcpServerPort <= 0 || config.tcpServerPort > 65535) { return false; } // 获取姿态输出顺序 config.poseOutputOrder = ui->comboBox_poseOutputOrder->currentData().toInt(); // 获取方向向量反向配置 config.dirVectorInvert = ui->comboBox_dirVectorInvert->currentData().toInt(); return true; }