2025-09-10 00:31:27 +08:00
|
|
|
|
#include "dialogcameralevel.h"
|
|
|
|
|
|
#include "ui_dialogcameralevel.h"
|
|
|
|
|
|
#include "IVrUtils.h"
|
|
|
|
|
|
#include "PathManager.h"
|
|
|
|
|
|
#include <QThread>
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
#include <mutex>
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
|
|
#include "LaserDataLoader.h"
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
#include <QFileDialog>
|
|
|
|
|
|
#include <QStandardPaths>
|
2025-09-14 14:51:38 +08:00
|
|
|
|
#include "LapWeldPresenter.h"
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
DialogCameraLevel::DialogCameraLevel(QWidget *parent)
|
|
|
|
|
|
: QDialog(parent)
|
|
|
|
|
|
, ui(new Ui::DialogCameraLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏标题栏
|
|
|
|
|
|
setWindowFlags(Qt::FramelessWindowHint);
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化界面
|
|
|
|
|
|
initializeCameraCombo();
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化结果显示区域
|
|
|
|
|
|
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<std::pair<std::string, IVrEyeDevice*>>& cameraList,
|
2025-09-14 14:51:38 +08:00
|
|
|
|
LapWeldPresenter* presenter)
|
2025-09-10 00:31:27 +08:00
|
|
|
|
{
|
|
|
|
|
|
m_cameraList = cameraList;
|
|
|
|
|
|
m_presenter = presenter;
|
|
|
|
|
|
|
|
|
|
|
|
// 重新初始化相机选择框
|
|
|
|
|
|
initializeCameraCombo();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DialogCameraLevel::initializeCameraCombo()
|
|
|
|
|
|
{
|
|
|
|
|
|
ui->combo_camera->clear();
|
|
|
|
|
|
|
|
|
|
|
|
if (m_cameraList.empty()) {
|
|
|
|
|
|
ui->combo_camera->setEnabled(false);
|
|
|
|
|
|
ui->label_level_result->setText("无可用相机设备");
|
|
|
|
|
|
ui->label_level_result->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
for (const auto& camera : m_cameraList) {
|
|
|
|
|
|
ui->combo_camera->addItem(QString::fromStdString(camera.first));
|
|
|
|
|
|
}
|
|
|
|
|
|
ui->combo_camera->setEnabled(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查并显示当前选中相机的标定状态
|
|
|
|
|
|
checkAndDisplayCalibrationStatus(0); // 默认选中第一个相机
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DialogCameraLevel::on_btn_apply_clicked()
|
|
|
|
|
|
{
|
|
|
|
|
|
ui->label_level_result->setAlignment(Qt::AlignLeft);
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LEVEL_DEBUG_MODE
|
|
|
|
|
|
// 检查是否有可用的相机
|
|
|
|
|
|
if (m_cameraList.empty()) {
|
|
|
|
|
|
QMessageBox::warning(this, "错误", "无可用相机设备!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取选中的相机
|
|
|
|
|
|
int selectedIndex = ui->combo_camera->currentIndex();
|
|
|
|
|
|
if (selectedIndex < 0 || selectedIndex >= m_cameraList.size()) {
|
|
|
|
|
|
QMessageBox::warning(this, "错误", "请选择有效的相机!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// 清空之前的结果显示
|
|
|
|
|
|
ui->label_level_result->setText("调平计算中,请稍候...");
|
|
|
|
|
|
|
|
|
|
|
|
// 显示进度提示
|
|
|
|
|
|
ui->btn_apply->setEnabled(false);
|
|
|
|
|
|
QApplication::processEvents();
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 执行相机调平
|
|
|
|
|
|
if (performCameraLeveling()) {
|
|
|
|
|
|
// 调平成功,关闭对话框(这会触发析构函数中的回调恢复)
|
|
|
|
|
|
// accept();
|
|
|
|
|
|
} 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();
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 设置调平状态回调
|
|
|
|
|
|
setLevelingStatusCallback();
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef LEVEL_DEBUG_MODE
|
|
|
|
|
|
// Debug模式:使用文件对话框选择测试数据
|
|
|
|
|
|
LOG_INFO("=== DEBUG MODE ENABLED ===\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 选择debug数据文件
|
|
|
|
|
|
QString debugDataFile = selectDebugDataFile();
|
|
|
|
|
|
if (debugDataFile.isEmpty()) {
|
|
|
|
|
|
LOG_INFO("Debug data file selection cancelled\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用选择的debug数据进行模拟扫描
|
|
|
|
|
|
if (!loadDebugDataAndSimulateScan(debugDataFile)) {
|
|
|
|
|
|
LOG_ERROR("Failed to load debug data for camera leveling\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
// 正常模式:使用真实相机
|
|
|
|
|
|
if (selectedIndex < 0 || selectedIndex >= 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);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 清空之前的扫描数据
|
|
|
|
|
|
clearScanDataCache();
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 启动相机扫描地面数据
|
|
|
|
|
|
if (!startCameraScan(selectedIndex)) {
|
|
|
|
|
|
LOG_ERROR("Failed to start camera scan for leveling\n");
|
|
|
|
|
|
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; // 每100ms检查一次
|
|
|
|
|
|
|
|
|
|
|
|
while (!m_swingFinished && waitTime < maxWaitTime) {
|
|
|
|
|
|
QThread::msleep(checkInterval);
|
|
|
|
|
|
QApplication::processEvents(); // 处理UI事件
|
|
|
|
|
|
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");
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 检查是否收集到足够的数据
|
|
|
|
|
|
// 6. 调用调平算法计算
|
|
|
|
|
|
double planeCalib[9];
|
|
|
|
|
|
double planeHeight;
|
|
|
|
|
|
double invRMatrix[9];
|
|
|
|
|
|
|
|
|
|
|
|
if (!calculatePlaneCalibration(planeCalib, planeHeight, invRMatrix)) {
|
|
|
|
|
|
LOG_ERROR("Failed to calculate plane calibration\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Camera leveling calculation completed\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 更新界面显示
|
|
|
|
|
|
updateLevelingResults(planeCalib, planeHeight, invRMatrix);
|
|
|
|
|
|
|
|
|
|
|
|
// 8. 保存结果到配置
|
|
|
|
|
|
// 获取相机索引和名称传递给保存方法
|
|
|
|
|
|
int cameraIndex = selectedIndex + 1; // 转换为1-based索引
|
|
|
|
|
|
QString cameraName;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef LEVEL_DEBUG_MODE
|
|
|
|
|
|
// Debug模式下使用默认名称
|
|
|
|
|
|
cameraIndex = 1;
|
|
|
|
|
|
cameraName = QString("Camera_%1").arg(cameraIndex);
|
|
|
|
|
|
#else
|
|
|
|
|
|
// 正常模式下从列表获取名称
|
|
|
|
|
|
if (selectedIndex >= 0 && selectedIndex < m_cameraList.size()) {
|
|
|
|
|
|
cameraName = QString::fromStdString(m_cameraList[selectedIndex].first);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cameraName = QString("Camera_%1").arg(cameraIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
if (!saveLevelingResults(planeCalib, planeHeight, invRMatrix, cameraIndex, cameraName)) {
|
|
|
|
|
|
LOG_ERROR("Failed to save leveling results\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
clearScanDataCache();
|
|
|
|
|
|
LOG_INFO("Camera leveling completed successfully\n");
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Exception in performCameraLeveling: %s\n", e.what());
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DialogCameraLevel::updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9])
|
|
|
|
|
|
{
|
|
|
|
|
|
// 计算旋转角度的近似值用于显示
|
|
|
|
|
|
double rotX = atan2(planeCalib[5], planeCalib[8]) * 180.0 / M_PI;
|
|
|
|
|
|
double rotY = atan2(-planeCalib[2], sqrt(planeCalib[5]*planeCalib[5] + planeCalib[8]*planeCalib[8])) * 180.0 / M_PI;
|
|
|
|
|
|
|
|
|
|
|
|
// 构建显示文本,将矩阵直接显示到页面上
|
|
|
|
|
|
QString resultText;
|
|
|
|
|
|
#if 0
|
|
|
|
|
|
// 基本信息
|
|
|
|
|
|
resultText += QString("旋转角度 X: %1°\n").arg(QString::number(rotX, 'f', 2));
|
|
|
|
|
|
resultText += QString("旋转角度 Y: %1°\n").arg(QString::number(rotY, 'f', 2));
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
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 >= 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 >= 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<DialogCameraLevel*>(pUserData);
|
|
|
|
|
|
if (pThis && pLaserLinePoint) {
|
|
|
|
|
|
pThis->DetectionCallback(eDataType, pLaserLinePoint);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 静态状态回调函数
|
|
|
|
|
|
void DialogCameraLevel::StaticStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam)
|
|
|
|
|
|
{
|
|
|
|
|
|
DialogCameraLevel* pThis = reinterpret_cast<DialogCameraLevel*>(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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 转换数据格式:从SVzLaserLineData转换为SVzNL3DLaserLine并存储到缓存
|
|
|
|
|
|
SVzNL3DLaserLine laser3DLine;
|
|
|
|
|
|
|
|
|
|
|
|
// 复制基本信息
|
|
|
|
|
|
laser3DLine.nTimeStamp = pLaserLinePoint->llTimeStamp;
|
|
|
|
|
|
laser3DLine.nPositionCnt = pLaserLinePoint->nPointCount;
|
|
|
|
|
|
|
|
|
|
|
|
// 分配和复制点云数据
|
|
|
|
|
|
laser3DLine.p3DPosition = new SVzNL3DPosition[pLaserLinePoint->nPointCount];
|
|
|
|
|
|
// 复制点云数据
|
|
|
|
|
|
memcpy(laser3DLine.p3DPosition, pLaserLinePoint->p3DPoint, sizeof(SVzNL3DPosition) * pLaserLinePoint->nPointCount);
|
|
|
|
|
|
|
|
|
|
|
|
// 将转换后的数据保存到缓存中
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_scanDataMutex);
|
|
|
|
|
|
m_scanDataCache.push_back(laser3DLine);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调平算法计算
|
|
|
|
|
|
bool DialogCameraLevel::calculatePlaneCalibration(double planeCalib[9], double& planeHeight, double invRMatrix[9])
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> 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 {
|
|
|
|
|
|
// 调用实际的调平算法
|
2025-09-14 14:51:38 +08:00
|
|
|
|
SSG_planeCalibPara calibResult;// = sx_getBaseCalibPara(m_scanDataCache.data(), static_cast<int>(m_scanDataCache.size()));
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 将结构体中的数据复制到输出参数
|
|
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
|
|
planeCalib[i] = calibResult.planeCalib[i];
|
|
|
|
|
|
invRMatrix[i] = calibResult.invRMatrix[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
planeHeight = calibResult.planeHeight;
|
|
|
|
|
|
|
|
|
|
|
|
// 计算旋转角度用于日志显示
|
|
|
|
|
|
double rotAngleX = atan2(planeCalib[5], planeCalib[8]) * 180.0 / M_PI;
|
|
|
|
|
|
double rotAngleY = atan2(-planeCalib[2], sqrt(planeCalib[5]*planeCalib[5] + planeCalib[8]*planeCalib[8])) * 180.0 / M_PI;
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Plane calibration calculated: height=%.3f, rotX=%.2f°, rotY=%.2f°\n",
|
|
|
|
|
|
planeHeight, rotAngleX, rotAngleY);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef LEVEL_DEBUG_MODE
|
|
|
|
|
|
// 统计点云数据
|
|
|
|
|
|
int totalPoints = 0;
|
|
|
|
|
|
double avgHeight = 0.0;
|
|
|
|
|
|
for (const auto& scanLine : m_scanDataCache) {
|
|
|
|
|
|
for (int i = 0; i < scanLine.nPositionCnt; i++) {
|
|
|
|
|
|
if (scanLine.p3DPosition[i].pt3D.z > -1000 && scanLine.p3DPosition[i].pt3D.z < 1000) {
|
|
|
|
|
|
avgHeight += scanLine.p3DPosition[i].pt3D.z;
|
|
|
|
|
|
totalPoints++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("=== DEBUG MODE CALIBRATION RESULTS ===\n");
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Algorithm results:\n");
|
|
|
|
|
|
LOG_INFO(" Calculated plane height: %.3f mm\n", calibResult.planeHeight);
|
|
|
|
|
|
LOG_INFO("Calibration matrix:\n");
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
|
LOG_INFO(" [%.6f, %.6f, %.6f]\n", calibResult.planeCalib[i*3], calibResult.planeCalib[i*3+1], calibResult.planeCalib[i*3+2]);
|
|
|
|
|
|
}
|
|
|
|
|
|
LOG_INFO("Inverse rotation matrix:\n");
|
|
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
|
|
LOG_INFO(" [%.6f, %.6f, %.6f]\n", calibResult.invRMatrix[i*3], calibResult.invRMatrix[i*3+1], calibResult.invRMatrix[i*3+2]);
|
|
|
|
|
|
}
|
|
|
|
|
|
LOG_INFO("=======================================\n");
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Exception in sg_getBagBaseCalibPara: %s\n", e.what());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
} catch (...) {
|
|
|
|
|
|
LOG_ERROR("Unknown exception in sg_getBagBaseCalibPara\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空扫描数据缓存
|
|
|
|
|
|
void DialogCameraLevel::clearScanDataCache()
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_scanDataMutex);
|
|
|
|
|
|
|
|
|
|
|
|
LOG_DEBUG("Clearing scan data cache, current size: %zu\n", m_scanDataCache.size());
|
|
|
|
|
|
|
|
|
|
|
|
// 释放缓存的内存
|
|
|
|
|
|
for (auto& cachedLine : m_scanDataCache) {
|
|
|
|
|
|
if (cachedLine.p3DPosition) {
|
|
|
|
|
|
delete[] cachedLine.p3DPosition;
|
|
|
|
|
|
cachedLine.p3DPosition = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清空缓存容器
|
|
|
|
|
|
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_presenter) {
|
|
|
|
|
|
LOG_ERROR("Presenter is null, cannot save leveling results\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取配置对象
|
|
|
|
|
|
IVrConfig* config = m_presenter->GetConfig();
|
|
|
|
|
|
if (!config) {
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载当前配置
|
|
|
|
|
|
QString configPath = PathManager::GetConfigFilePath();
|
|
|
|
|
|
LOG_INFO("Config path: %s\n", configPath.toUtf8().constData());
|
|
|
|
|
|
ConfigResult configResult = config->LoadConfig(configPath.toStdString());
|
|
|
|
|
|
|
|
|
|
|
|
// 创建或更新指定相机的调平参数
|
|
|
|
|
|
VrCameraPlaneCalibParam cameraParam;
|
|
|
|
|
|
cameraParam.cameraIndex = cameraIndex;
|
|
|
|
|
|
cameraParam.cameraName = cameraName.toStdString();
|
|
|
|
|
|
cameraParam.planeHeight = planeHeight;
|
|
|
|
|
|
cameraParam.isCalibrated = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 复制校准矩阵
|
|
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
|
|
cameraParam.planeCalib[i] = planeCalib[i];
|
|
|
|
|
|
cameraParam.invRMatrix[i] = invRMatrix[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新配置中的相机校准参数
|
|
|
|
|
|
configResult.algorithmParams.planeCalibParam.SetCameraCalibParam(cameraParam);
|
|
|
|
|
|
|
|
|
|
|
|
// 保存配置
|
|
|
|
|
|
bool saveResult = config->SaveConfig(configPath.toStdString(), configResult);
|
|
|
|
|
|
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\n", planeHeight);
|
|
|
|
|
|
LOG_INFO("Calibration marked as completed\n");
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Exception in saveLevelingResults: %s\n", e.what());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 选择debug数据文件
|
|
|
|
|
|
QString DialogCameraLevel::selectDebugDataFile()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取应用程序目录作为起始目录
|
|
|
|
|
|
QString appDir = QCoreApplication::applicationDirPath();
|
|
|
|
|
|
QString testDataDir = appDir + "/TestData";
|
|
|
|
|
|
|
|
|
|
|
|
// 如果TestData目录不存在,使用应用程序目录
|
|
|
|
|
|
if (!QFileInfo::exists(testDataDir)) {
|
|
|
|
|
|
testDataDir = appDir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打开文件选择对话框
|
|
|
|
|
|
QString fileName = QFileDialog::getOpenFileName(
|
|
|
|
|
|
this,
|
|
|
|
|
|
"选择激光扫描数据文件",
|
|
|
|
|
|
testDataDir,
|
|
|
|
|
|
"文本文件 (*.txt);;所有文件 (*.*)"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (fileName.isEmpty()) {
|
|
|
|
|
|
LOG_INFO("No debug data file selected\n");
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查文件是否存在
|
|
|
|
|
|
if (!QFileInfo::exists(fileName)) {
|
|
|
|
|
|
LOG_ERROR("Selected debug data file does not exist: %s\n", fileName.toUtf8().constData());
|
|
|
|
|
|
QMessageBox::warning(this, "文件错误", "选择的文件不存在!");
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Selected debug data file: %s\n", fileName.toUtf8().constData());
|
|
|
|
|
|
|
|
|
|
|
|
return fileName;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载debug数据并模拟扫描过程
|
|
|
|
|
|
bool DialogCameraLevel::loadDebugDataAndSimulateScan(const QString& filePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("Loading debug data from: %s\n", filePath.toUtf8().constData());
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 清空之前的扫描数据
|
|
|
|
|
|
clearScanDataCache();
|
|
|
|
|
|
|
|
|
|
|
|
// 使用LaserDataLoader加载测试数据
|
|
|
|
|
|
LaserDataLoader dataLoader;
|
|
|
|
|
|
std::vector<std::pair<EVzResultDataType, SVzLaserLineData>> debugData;
|
|
|
|
|
|
int lineNum = 0;
|
|
|
|
|
|
float scanSpeed = 0.0f;
|
|
|
|
|
|
int maxTimeStamp = 0;
|
|
|
|
|
|
int clockPerSecond = 0;
|
|
|
|
|
|
|
|
|
|
|
|
int result = dataLoader.LoadLaserScanData(filePath.toStdString(), debugData,
|
|
|
|
|
|
lineNum, scanSpeed, maxTimeStamp, clockPerSecond);
|
|
|
|
|
|
|
|
|
|
|
|
if (result != 0) {
|
|
|
|
|
|
LOG_ERROR("Failed to load debug data: %s\n", dataLoader.GetLastError().c_str());
|
|
|
|
|
|
QMessageBox::critical(this, "Debug模式错误",
|
|
|
|
|
|
QString("加载测试数据失败:\n%1").arg(QString::fromStdString(dataLoader.GetLastError())));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (debugData.empty()) {
|
|
|
|
|
|
LOG_ERROR("Debug data is empty\n");
|
|
|
|
|
|
QMessageBox::warning(this, "Debug模式警告", "测试数据文件为空!");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Loaded %d lines of debug data, starting simulation\n", lineNum);
|
|
|
|
|
|
|
|
|
|
|
|
// 将加载的数据复制到缓存中(需要深拷贝)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_scanDataMutex);
|
|
|
|
|
|
m_scanDataCache.reserve(debugData.size());
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& linePair : debugData) {
|
|
|
|
|
|
EVzResultDataType dataType = linePair.first;
|
|
|
|
|
|
const SVzLaserLineData& lineData = linePair.second;
|
|
|
|
|
|
|
|
|
|
|
|
// 只处理Position类型的数据(相机调平只需要位置信息)
|
|
|
|
|
|
if (dataType == keResultDataType_Position && lineData.p3DPoint) {
|
|
|
|
|
|
SVzNL3DLaserLine copyLine;
|
|
|
|
|
|
copyLine.nTimeStamp = 0; // 从SVzLaserLineData中获取时间戳
|
|
|
|
|
|
copyLine.nPositionCnt = lineData.nPointCount;
|
|
|
|
|
|
|
|
|
|
|
|
if (lineData.nPointCount > 0) {
|
|
|
|
|
|
copyLine.p3DPosition = new SVzNL3DPosition[lineData.nPointCount];
|
|
|
|
|
|
memcpy(copyLine.p3DPosition, lineData.p3DPoint, sizeof(SVzNL3DPosition) * lineData.nPointCount);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
copyLine.p3DPosition = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
m_scanDataCache.push_back(copyLine);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 释放临时加载的数据
|
|
|
|
|
|
dataLoader.FreeLaserScanData(debugData);
|
|
|
|
|
|
|
|
|
|
|
|
// 启动模拟扫描过程
|
|
|
|
|
|
simulateScanProcess();
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
|
|
LOG_ERROR("Exception in loadDebugDataAndSimulateScan: %s\n", e.what());
|
|
|
|
|
|
QMessageBox::critical(this, "Debug模式异常", QString("加载debug数据时发生异常:\n%1").arg(e.what()));
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟扫描过程
|
|
|
|
|
|
void DialogCameraLevel::simulateScanProcess()
|
|
|
|
|
|
{
|
|
|
|
|
|
LOG_INFO("Starting scan simulation process\n");
|
|
|
|
|
|
|
|
|
|
|
|
// 创建定时器来模拟数据收集过程
|
|
|
|
|
|
QTimer* simulationTimer = new QTimer();
|
|
|
|
|
|
simulationTimer->setSingleShot(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟扫描需要的时间(2秒)
|
|
|
|
|
|
simulationTimer->setInterval(2000);
|
|
|
|
|
|
|
|
|
|
|
|
// 连接定时器信号
|
|
|
|
|
|
connect(simulationTimer, &QTimer::timeout, [this, simulationTimer]() {
|
|
|
|
|
|
LOG_INFO("Scan simulation completed\n");
|
|
|
|
|
|
m_swingFinished = true; // Debug模式下模拟摆动完成
|
|
|
|
|
|
|
|
|
|
|
|
// 显示调试信息
|
|
|
|
|
|
{
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(m_scanDataMutex);
|
|
|
|
|
|
LOG_INFO("Debug scan simulation completed with %zu lines\n", m_scanDataCache.size());
|
|
|
|
|
|
|
|
|
|
|
|
// 统计点云数据
|
|
|
|
|
|
int totalPoints = 0;
|
|
|
|
|
|
for (const auto& line : m_scanDataCache) {
|
|
|
|
|
|
totalPoints += line.nPositionCnt;
|
|
|
|
|
|
}
|
|
|
|
|
|
LOG_INFO("Total points in debug data: %d\n", totalPoints);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理定时器
|
|
|
|
|
|
simulationTimer->deleteLater();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 启动模拟扫描
|
|
|
|
|
|
simulationTimer->start();
|
|
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Simulation timer started, waiting for completion...\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 相机选择改变的槽函数
|
|
|
|
|
|
void DialogCameraLevel::on_combo_camera_currentIndexChanged(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (index >= 0 && index < m_cameraList.size()) {
|
|
|
|
|
|
LOG_INFO("Camera selection changed to index: %d (%s)\n", index,
|
|
|
|
|
|
QString::fromStdString(m_cameraList[index].first).toUtf8().constData());
|
|
|
|
|
|
checkAndDisplayCalibrationStatus(index);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
LOG_WARNING("Invalid camera index selected: %d\n", index);
|
|
|
|
|
|
ui->label_level_result->setText("无效的相机选择");
|
|
|
|
|
|
ui->label_level_result->setAlignment(Qt::AlignCenter);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载相机标定数据
|
|
|
|
|
|
bool DialogCameraLevel::loadCameraCalibrationData(int cameraIndex, const QString& cameraName,
|
|
|
|
|
|
double planeCalib[9], double& planeHeight, double invRMatrix[9])
|
|
|
|
|
|
{
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (!m_presenter) {
|
|
|
|
|
|
LOG_ERROR("Presenter is null, cannot load calibration data\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取配置对象
|
|
|
|
|
|
IVrConfig* config = m_presenter->GetConfig();
|
|
|
|
|
|
if (!config) {
|
|
|
|
|
|
LOG_ERROR("Config is null, cannot load calibration data\n");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载配置文件
|
|
|
|
|
|
QString configPath = PathManager::GetConfigFilePath();
|
|
|
|
|
|
ConfigResult configResult = config->LoadConfig(configPath.toStdString());
|
|
|
|
|
|
|
|
|
|
|
|
// 获取指定相机的标定参数
|
2025-11-01 17:39:39 +08:00
|
|
|
|
VrCameraPlaneCalibParam cameraParamValue;
|
|
|
|
|
|
if (!configResult.algorithmParams.planeCalibParam.GetCameraCalibParam(cameraIndex, cameraParamValue) || !cameraParamValue.isCalibrated) {
|
2025-09-10 00:31:27 +08:00
|
|
|
|
LOG_INFO("No calibration data found for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData());
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-11-01 17:39:39 +08:00
|
|
|
|
|
2025-09-10 00:31:27 +08:00
|
|
|
|
// 复制标定数据
|
|
|
|
|
|
for (int i = 0; i < 9; i++) {
|
2025-11-01 17:39:39 +08:00
|
|
|
|
planeCalib[i] = cameraParamValue.planeCalib[i];
|
|
|
|
|
|
invRMatrix[i] = cameraParamValue.invRMatrix[i];
|
2025-09-10 00:31:27 +08:00
|
|
|
|
}
|
2025-11-01 17:39:39 +08:00
|
|
|
|
planeHeight = cameraParamValue.planeHeight;
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Calibration data loaded successfully for camera %d (%s)\n", cameraIndex, cameraName.toUtf8().constData());
|
|
|
|
|
|
LOG_INFO("Plane height: %.3f\n", planeHeight);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
} 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 >= 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; // 转换为1-based索引
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试加载该相机的标定数据
|
|
|
|
|
|
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, showing instruction\n", cameraName.toUtf8().constData());
|
|
|
|
|
|
ui->label_level_result->setText(QString("请选择相机 \"%1\" 并点击调平按钮\n开始相机调平操作").arg(cameraName));
|
|
|
|
|
|
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");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复Presenter的状态回调
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-14 14:51:38 +08:00
|
|
|
|
// 恢复Presenter的状态回调 - 使用LapWeldPresenter的静态回调
|
|
|
|
|
|
m_presenter->SetCameraStatusCallback(&LapWeldPresenter::_StaticCameraNotify, m_presenter);
|
2025-09-10 00:31:27 +08:00
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Presenter status callback restored for all cameras\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|