#include "dialogcameralevel.h" #include "ui_dialogcameralevel.h" #include "WheelMeasurePresenter.h" #include "PathManager.h" #include "VrLog.h" #include "wheelArchHeigthMeasure_Export.h" #include #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif DialogCameraLevel::DialogCameraLevel(QWidget *parent) : QDialog(parent) , ui(new Ui::DialogCameraLevel) , m_pConfig(nullptr) , m_pConfigResult(nullptr) , m_currentCameraIndex(-1) { ui->setupUi(this); // 初始化结果显示区域 ui->label_level_result->setText("请选择相机,然后点击调平按钮\n开始相机调平操作"); ui->label_level_result->setAlignment(Qt::AlignCenter); } DialogCameraLevel::~DialogCameraLevel() { // 清理扫描数据缓存 clearScanDataCache(); // 确保恢复Presenter的状态回调 restorePresenterStatusCallback(); delete ui; } void DialogCameraLevel::setCameraList(const std::vector>& cameraList, WheelMeasurePresenter* presenter) { m_cameraList = cameraList; m_presenter = presenter; LOG_INFO("setCameraList called with %zu cameras\n", cameraList.size()); // 详细记录每个相机的信息 for (size_t i = 0; i < cameraList.size(); ++i) { const auto& camera = cameraList[i]; LOG_INFO(" Camera %zu: name='%s', device=%s\n", i + 1, camera.first.c_str(), (camera.second != nullptr ? "connected" : "not connected")); } // 初始化/重新初始化相机选择框 initializeCameraCombo(); } void DialogCameraLevel::setConfig(IVrWheelMeasureConfig* config, WheelMeasureConfigResult* configResult) { m_pConfig = config; m_pConfigResult = configResult; // 如果相机已经选择,重新加载当前相机的标定状态 // 修复:打开页面时配置可能在相机列表之后设置,导致初始加载失败 if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast(m_cameraList.size())) { checkAndDisplayCalibrationStatus(m_currentCameraIndex); } } void DialogCameraLevel::initializeCameraCombo() { LOG_INFO("initializeCameraCombo called, camera list size: %zu\n", m_cameraList.size()); ui->combo_camera->clear(); if (m_cameraList.empty()) { ui->combo_camera->setEnabled(false); if (!m_presenter) { ui->label_level_result->setText("Presenter未初始化\n无法获取相机列表"); LOG_ERROR("Presenter is null in initializeCameraCombo\n"); } else { ui->label_level_result->setText("相机列表为空\n\n可能原因:\n1. 配置文件中未配置相机\n2. 系统正在初始化中\n3. 所有相机连接失败"); LOG_WARNING("Camera list is empty in initializeCameraCombo\n"); } ui->label_level_result->setAlignment(Qt::AlignCenter); } else { LOG_INFO("Adding %zu cameras to combo box\n", m_cameraList.size()); // 添加所有相机到下拉列表 for (size_t i = 0; i < m_cameraList.size(); ++i) { const auto& camera = m_cameraList[i]; QString cameraName = QString::fromStdString(camera.first); // 如果相机没有连接,在名称后添加标记 if (camera.second == nullptr) { cameraName += " [未连接]"; } ui->combo_camera->addItem(cameraName); LOG_INFO(" Added camera %zu: %s (device=%s)\n", i + 1, camera.first.c_str(), (camera.second != nullptr ? "OK" : "NULL")); } ui->combo_camera->setEnabled(true); // 获取默认相机索引 int defaultCameraIndex = 0; if (m_presenter) { int presenterDefaultIndex = m_presenter->GetDefaultCameraIndex(); LOG_INFO("Presenter default camera index (1-based): %d\n", presenterDefaultIndex); if (presenterDefaultIndex > 0 && presenterDefaultIndex <= static_cast(m_cameraList.size())) { defaultCameraIndex = presenterDefaultIndex - 1; } } // 设置默认选中的相机 if (defaultCameraIndex >= 0 && defaultCameraIndex < static_cast(m_cameraList.size())) { ui->combo_camera->setCurrentIndex(defaultCameraIndex); m_currentCameraIndex = defaultCameraIndex; } // 检查并显示当前选中相机的标定状态 if (m_currentCameraIndex >= 0) { checkAndDisplayCalibrationStatus(m_currentCameraIndex); } } } void DialogCameraLevel::on_btn_apply_clicked() { ui->label_level_result->setAlignment(Qt::AlignLeft); // 检查是否有可用的相机 if (m_cameraList.empty()) { QMessageBox::warning(this, "错误", "无可用相机设备!"); return; } // 获取选中的相机 int selectedIndex = ui->combo_camera->currentIndex(); if (selectedIndex < 0 || selectedIndex >= static_cast(m_cameraList.size())) { QMessageBox::warning(this, "错误", "请选择有效的相机!"); return; } // 清空之前的结果显示 ui->label_level_result->setText("调平计算中,请稍候..."); // 显示进度提示 ui->btn_apply->setEnabled(false); QApplication::processEvents(); try { // 执行相机调平 if (performCameraLeveling()) { // 调平成功 } else { ui->label_level_result->setText("调平失败!\n\n请检查:\n1. 相机连接是否正常\n2. 地面扫描数据是否充足\n3. 扫描区域是否有足够的地面"); } } catch (const std::exception& e) { LOG_ERROR("Camera leveling failed with exception: %s\n", e.what()); QMessageBox::critical(this, "错误", QString("调平过程发生异常:%1").arg(e.what())); } // 恢复按钮状态 ui->btn_apply->setEnabled(true); } void DialogCameraLevel::on_btn_cancel_clicked() { reject(); } bool DialogCameraLevel::performCameraLeveling() { try { // 获取选中的相机索引 int selectedIndex = ui->combo_camera->currentIndex(); // 先检查索引有效性,再设置回调 if (selectedIndex < 0 || selectedIndex >= static_cast(m_cameraList.size())) { LOG_ERROR("Invalid camera index: %d\n", selectedIndex); return false; } LOG_INFO("Performing camera leveling with camera %d (index %d)\n", selectedIndex + 1, selectedIndex); // 1. 设置调平状态回调(在索引检查之后) setLevelingStatusCallback(); // 2. 清空之前的扫描数据 clearScanDataCache(); // 3. 启动相机扫描地面数据 if (!startCameraScan(selectedIndex)) { LOG_ERROR("Failed to start camera scan for leveling\n"); restorePresenterStatusCallback(); // 恢复回调 return false; } // 4. 等待扫描完成 LOG_INFO("Collecting ground scan data, waiting for swing finish signal...\n"); int waitTime = 0; const int maxWaitTime = 10000; // 最大等待10秒 const int checkInterval = 100; while (!m_swingFinished && waitTime < maxWaitTime) { QThread::msleep(checkInterval); QApplication::processEvents(); waitTime += checkInterval; } // 5. 停止扫描 stopCameraScan(selectedIndex); if (m_swingFinished) { LOG_INFO("Camera swing finished signal received, scan completed\n"); } else if (waitTime >= maxWaitTime) { LOG_WARNING("Timeout waiting for camera swing finish signal\n"); } // 6. 调用调平算法计算 double planeCalib[9]; double planeHeight; double invRMatrix[9]; if (!calculatePlaneCalibration(planeCalib, planeHeight, invRMatrix)) { LOG_ERROR("Failed to calculate plane calibration\n"); restorePresenterStatusCallback(); // 恢复回调 return false; } LOG_INFO("Camera leveling calculation completed\n"); // 7. 更新界面显示 updateLevelingResults(planeCalib, planeHeight, invRMatrix); // 8. 保存结果到配置 int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引 QString cameraName; if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast(m_cameraList.size())) { cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first); } else { cameraName = QString("Camera_%1").arg(cameraIndex); } if (!saveLevelingResults(planeCalib, planeHeight, invRMatrix, cameraIndex, cameraName)) { LOG_ERROR("Failed to save leveling results\n"); restorePresenterStatusCallback(); // 恢复回调 return false; } clearScanDataCache(); // 9. 调平完成后恢复回调 restorePresenterStatusCallback(); LOG_INFO("Camera leveling completed successfully\n"); return true; } catch (const std::exception& e) { LOG_ERROR("Exception in performCameraLeveling: %s\n", e.what()); restorePresenterStatusCallback(); // 异常时也恢复回调 return false; } } void DialogCameraLevel::updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9]) { // 构建显示文本 QString resultText; resultText += QString("地面高度: %1 mm\n").arg(QString::number(planeHeight, 'f', 2)); // 调平矩阵 resultText += QString("调平矩阵:\n"); for (int i = 0; i < 3; i++) { resultText += QString("[%1, %2, %3]\n") .arg(QString::number(planeCalib[i*3], 'f', 4)) .arg(QString::number(planeCalib[i*3+1], 'f', 4)) .arg(QString::number(planeCalib[i*3+2], 'f', 4)); } resultText += QString("逆旋转矩阵:\n"); for (int i = 0; i < 3; i++) { resultText += QString("[%1, %2, %3]\n") .arg(QString::number(invRMatrix[i*3], 'f', 4)) .arg(QString::number(invRMatrix[i*3+1], 'f', 4)) .arg(QString::number(invRMatrix[i*3+2], 'f', 4)); } ui->label_level_result->setText(resultText); ui->label_level_result->setAlignment(Qt::AlignLeft | Qt::AlignTop); } bool DialogCameraLevel::startCameraScan(int cameraIndex) { if (cameraIndex < 0 || cameraIndex >= static_cast(m_cameraList.size())) { LOG_ERROR("Invalid camera index for scan: %d\n", cameraIndex); return false; } IVrEyeDevice* camera = m_cameraList[cameraIndex].second; if (!camera) { LOG_ERROR("Camera device is null at index: %d\n", cameraIndex); return false; } // 启动相机检测 int result = camera->StartDetect(&DialogCameraLevel::StaticDetectionCallback, keResultDataType_Position, this); if (result != 0) { LOG_ERROR("Failed to start camera detection: %d\n", result); return false; } LOG_INFO("Camera scan started successfully for camera index: %d\n", cameraIndex); return true; } bool DialogCameraLevel::stopCameraScan(int cameraIndex) { if (cameraIndex < 0 || cameraIndex >= static_cast(m_cameraList.size())) { LOG_ERROR("Invalid camera index for stop scan: %d\n", cameraIndex); return false; } IVrEyeDevice* camera = m_cameraList[cameraIndex].second; if (!camera) { LOG_ERROR("Camera device is null at index: %d\n", cameraIndex); return false; } int result = camera->StopDetect(); if (result != 0) { LOG_WARNING("Failed to stop camera detection, error: %d\n", result); return false; } LOG_INFO("Camera scan stopped successfully for camera index: %d\n", cameraIndex); return true; } void DialogCameraLevel::StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData) { DialogCameraLevel* pThis = reinterpret_cast(pUserData); if (pThis && pLaserLinePoint) { pThis->DetectionCallback(eDataType, pLaserLinePoint); } } void DialogCameraLevel::StaticStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam) { DialogCameraLevel* pThis = reinterpret_cast(pInfoParam); if (pThis) { pThis->StatusCallback(eStatus, pExtData, nDataLength, pInfoParam); } } void DialogCameraLevel::StatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam) { LOG_DEBUG("[Leveling Status Callback] received: status=%d\n", (int)eStatus); switch (eStatus) { case EVzDeviceWorkStatus::keDeviceWorkStatus_Device_Swing_Finish: { LOG_INFO("[Leveling Status Callback] Camera swing finished, scan completed\n"); m_swingFinished = true; break; } default: LOG_DEBUG("[Leveling Status Callback] Other status: %d\n", (int)eStatus); break; } } void DialogCameraLevel::DetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint) { if (!pLaserLinePoint) { LOG_WARNING("[Leveling Callback] pLaserLinePoint is null\n"); return; } if (pLaserLinePoint->nPointCount <= 0) { LOG_WARNING("[Leveling Callback] Point count is zero or negative: %d\n", pLaserLinePoint->nPointCount); return; } if (!pLaserLinePoint->p3DPoint) { LOG_WARNING("[Leveling Callback] p3DPoint is null\n"); return; } // 将数据添加到缓存 std::vector lineData; lineData.reserve(pLaserLinePoint->nPointCount); // p3DPoint 是 void*,需要转换为 SVzNL3DPosition* SVzNL3DPosition* p3DPoints = reinterpret_cast(pLaserLinePoint->p3DPoint); for (int i = 0; i < pLaserLinePoint->nPointCount; ++i) { lineData.push_back(p3DPoints[i]); } std::lock_guard lock(m_scanDataMutex); m_scanDataCache.push_back(std::move(lineData)); } bool DialogCameraLevel::calculatePlaneCalibration(double planeCalib[9], double& planeHeight, double invRMatrix[9]) { std::lock_guard lock(m_scanDataMutex); if (m_scanDataCache.empty()) { LOG_ERROR("No scan data available for plane calibration\n"); return false; } LOG_INFO("Calculating plane calibration from %zu scan lines\n", m_scanDataCache.size()); try { // 调用 wheelArchHeigthMeasure SDK 的调平算法 SSG_planeCalibPara calibResult = wd_horizonCamera_getGroundCalibPara(m_scanDataCache); // 复制调平矩阵 for (int i = 0; i < 9; i++) { planeCalib[i] = calibResult.planeCalib[i]; invRMatrix[i] = calibResult.invRMatrix[i]; } planeHeight = calibResult.planeHeight; LOG_INFO("Plane calibration calculated successfully\n"); LOG_INFO(" planeHeight: %.3f\n", planeHeight); LOG_INFO(" planeCalib: [%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f]\n", planeCalib[0], planeCalib[1], planeCalib[2], planeCalib[3], planeCalib[4], planeCalib[5], planeCalib[6], planeCalib[7], planeCalib[8]); LOG_INFO(" invRMatrix: [%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f]\n", invRMatrix[0], invRMatrix[1], invRMatrix[2], invRMatrix[3], invRMatrix[4], invRMatrix[5], invRMatrix[6], invRMatrix[7], invRMatrix[8]); return true; } catch (const std::exception& e) { LOG_ERROR("Exception in calculatePlaneCalibration: %s\n", e.what()); return false; } } void DialogCameraLevel::clearScanDataCache() { std::lock_guard lock(m_scanDataMutex); LOG_DEBUG("Clearing scan data cache, current size: %zu\n", m_scanDataCache.size()); m_scanDataCache.clear(); LOG_DEBUG("Scan data cache cleared successfully\n"); } bool DialogCameraLevel::saveLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9], int cameraIndex, const QString& cameraName) { try { if (!m_pConfig || !m_pConfigResult) { LOG_ERROR("Config is null, cannot save leveling results\n"); return false; } if (cameraIndex <= 0) { LOG_ERROR("Invalid camera index: %d\n", cameraIndex); return false; } if (cameraName.isEmpty()) { LOG_ERROR("Camera name is empty\n"); return false; } // 获取UI中的误差补偿值 double errorCompensation = ui->edit_error_compensation->text().toDouble(); // 创建或更新相机调平参数 WheelCameraPlaneCalibParam cameraParam; cameraParam.cameraIndex = cameraIndex; cameraParam.cameraName = cameraName.toStdString(); cameraParam.planeHeight = planeHeight; cameraParam.isCalibrated = true; cameraParam.errorCompensation = errorCompensation; // 复制校准矩阵 for (int i = 0; i < 9; i++) { cameraParam.planeCalib[i] = planeCalib[i]; cameraParam.invRMatrix[i] = invRMatrix[i]; } // 查找是否已有该相机的调平参数 bool found = false; for (auto& param : m_pConfigResult->planeCalibParams) { if (param.cameraIndex == cameraIndex) { param = cameraParam; found = true; break; } } if (!found) { m_pConfigResult->planeCalibParams.push_back(cameraParam); } // 保存配置 QString configPath = PathManager::GetInstance().GetConfigFilePath(); bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult); if (!saveResult) { LOG_ERROR("Failed to save config with leveling results\n"); return false; } LOG_INFO("Leveling results saved successfully for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData()); LOG_INFO("Plane height: %.3f, Error compensation: %.2f\n", planeHeight, errorCompensation); return true; } catch (const std::exception& e) { LOG_ERROR("Exception in saveLevelingResults: %s\n", e.what()); return false; } } bool DialogCameraLevel::loadCameraCalibrationData(int cameraIndex, const QString& cameraName, double planeCalib[9], double& planeHeight, double invRMatrix[9]) { try { if (!m_pConfigResult) { LOG_ERROR("Config result is null, cannot load calibration data\n"); return false; } // 查找对应相机的调平参数 for (const auto& param : m_pConfigResult->planeCalibParams) { if (param.cameraIndex == cameraIndex && param.isCalibrated) { for (int i = 0; i < 9; i++) { planeCalib[i] = param.planeCalib[i]; invRMatrix[i] = param.invRMatrix[i]; } planeHeight = param.planeHeight; // 加载该相机的误差补偿值到UI ui->edit_error_compensation->setText(QString::number(param.errorCompensation, 'f', 1)); LOG_INFO("Calibration data loaded successfully for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData()); return true; } } // 没有找到标定数据时,设置默认误差补偿值 ui->edit_error_compensation->setText(QString::number(-5.0, 'f', 1)); LOG_INFO("No calibration data found for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData()); return false; } catch (const std::exception& e) { LOG_ERROR("Exception in loadCameraCalibrationData: %s\n", e.what()); return false; } } void DialogCameraLevel::checkAndDisplayCalibrationStatus(int cameraIndex) { if (cameraIndex < 0 || cameraIndex >= static_cast(m_cameraList.size())) { LOG_WARNING("Invalid camera index for status check: %d\n", cameraIndex); ui->label_level_result->setText("无效的相机索引"); ui->label_level_result->setAlignment(Qt::AlignCenter); return; } QString cameraName = QString::fromStdString(m_cameraList[cameraIndex].first); int configCameraIndex = cameraIndex + 1; double planeCalib[9]; double planeHeight; double invRMatrix[9]; if (loadCameraCalibrationData(configCameraIndex, cameraName, planeCalib, planeHeight, invRMatrix)) { // 有标定数据,显示 LOG_INFO("Displaying existing calibration data for camera %s\n", cameraName.toUtf8().constData()); updateLevelingResults(planeCalib, planeHeight, invRMatrix); } else { // 没有标定数据 LOG_INFO("No calibration data found for camera %s\n", cameraName.toUtf8().constData()); ui->label_level_result->setText(QString("相机: %1\n\n请点击调平按钮开始调平操作").arg(cameraName)); ui->label_level_result->setAlignment(Qt::AlignCenter); } } void DialogCameraLevel::on_combo_camera_currentIndexChanged(int index) { m_currentCameraIndex = index; if (index >= 0 && index < static_cast(m_cameraList.size())) { LOG_INFO("Camera selection changed to index: %d (%s)\n", index, QString::fromStdString(m_cameraList[index].first).toUtf8().constData()); checkAndDisplayCalibrationStatus(m_currentCameraIndex); } else { LOG_WARNING("Invalid camera index selected: %d\n", index); ui->label_level_result->setText("无效的相机选择"); ui->label_level_result->setAlignment(Qt::AlignCenter); } } void DialogCameraLevel::setLevelingStatusCallback() { if (!m_presenter) { LOG_ERROR("Presenter is null, cannot set leveling status callback\n"); return; } m_presenter->SetCameraStatusCallback(&DialogCameraLevel::StaticStatusCallback, this); m_swingFinished = false; m_callbackRestored = false; LOG_INFO("Leveling status callback set for all cameras\n"); } void DialogCameraLevel::restorePresenterStatusCallback() { if (m_callbackRestored.exchange(true)) { LOG_DEBUG("Presenter status callback already restored, skipping\n"); return; } if (!m_presenter) { LOG_ERROR("Presenter is null, cannot restore status callback\n"); return; } m_presenter->SetCameraStatusCallback(&WheelMeasurePresenter::_StaticCameraNotify, m_presenter); LOG_INFO("Presenter status callback restored for all cameras\n"); } void DialogCameraLevel::on_btn_save_compensation_clicked() { // 保存误差补偿值到当前相机的调平参数 if (!m_pConfig || !m_pConfigResult) { LOG_ERROR("Config is null, cannot save error compensation\n"); return; } if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast(m_cameraList.size())) { LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex); return; } // 获取UI中的误差补偿值 double errorCompensation = ui->edit_error_compensation->text().toDouble(); int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引 QString cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first); LOG_INFO("Saving error compensation for camera %d (%s): %.2f\n", cameraIndex, cameraName.toUtf8().constData(), errorCompensation); // 查找或创建相机调平参数 bool found = false; for (auto& param : m_pConfigResult->planeCalibParams) { if (param.cameraIndex == cameraIndex) { param.errorCompensation = errorCompensation; found = true; break; } } // 如果没有找到现有记录,创建一个新的(仅包含误差补偿,其他值为默认) if (!found) { WheelCameraPlaneCalibParam newParam; newParam.cameraIndex = cameraIndex; newParam.cameraName = cameraName.toStdString(); newParam.errorCompensation = errorCompensation; newParam.isCalibrated = false; // 尚未标定 m_pConfigResult->planeCalibParams.push_back(newParam); } // 保存配置到文件 QString configPath = PathManager::GetInstance().GetConfigFilePath(); bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult); if (saveResult) { LOG_INFO("Error compensation saved successfully for camera %d: %.2f\n", cameraIndex, errorCompensation); } else { LOG_ERROR("Failed to save error compensation\n"); } }