#ifndef HANDEYECALIBWIDGET_H #define HANDEYECALIBWIDGET_H #include #include #include #include #include #include #include #include /** * @brief 手眼标定相机信息 */ struct HandEyeCalibCameraInfo { int cameraIndex; // 相机索引(1-based) QString displayName; // 显示名称 }; /** * @brief 单个相机的标定数据缓存 * * eulerOrder 既用作机器人法兰姿态的输入分解约定, * 也用作工具姿态输出时的欧拉角合成约定; * rotX/Y/Z 以度为单位,按 Rx*Ry*Rz 顺序对 Eye 坐标系下的工具轴做补偿旋转。 */ struct HandEyeCalibData { int cameraIndex = 0; double matrix[16] = { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 }; bool isCalibrated = false; int eulerOrder = 11; // 默认外旋 ZYX double rotX = 0.0; double rotY = 0.0; double rotZ = 0.0; double approachOffset = 0.0; // 接近点偏移(mm),沿目标姿态反方向远离目标点 double offsetX = 0.0; // 目标点补偿偏移 X(mm),Robot 坐标系 double offsetY = 0.0; // 目标点补偿偏移 Y(mm) double offsetZ = 0.0; // 目标点补偿偏移 Z(mm) }; /** * @brief 手眼标定共享控件 * * 纯 QWidget,代码构建 UI(无 .ui 文件),可嵌入任意 tab/layout。 * 配置无关:不依赖任何 App 的 IVrConfig,只操作原始数据。 * 自带 INI 解析:用 QSettings 读取 [CalibMatrixInfo_0] section。 * * UI 布局:选择相机 ComboBox → 标定状态 Label → 4x4 矩阵 GridLayout → [可选外参区] → 加载/保存按钮 * 外参区(setExtrinsicControlsVisible(true) 时出现):欧拉角顺序 + 绕 X/Y/Z 旋转角度。 */ class HandEyeCalibWidget : public QWidget { Q_OBJECT public: explicit HandEyeCalibWidget(QWidget *parent = nullptr); ~HandEyeCalibWidget(); /** * @brief 设置相机列表 */ void setCameraList(const QVector& cameras); /** * @brief 设置指定相机的标定数据 */ void setCalibData(int cameraIndex, const double matrix[16], bool isCalibrated); /** * @brief 设置指定相机的欧拉角顺序与补偿旋转 */ void setExtrinsicData(int cameraIndex, int eulerOrder, double rotX, double rotY, double rotZ); /** * @brief 设置指定相机的欧拉角顺序、补偿旋转以及接近点偏移 */ void setExtrinsicData(int cameraIndex, int eulerOrder, double rotX, double rotY, double rotZ, double approachOffset); /** * @brief 设置指定相机的完整外参(含接近点偏移 + 目标点补偿偏移) */ void setExtrinsicData(int cameraIndex, int eulerOrder, double rotX, double rotY, double rotZ, double approachOffset, double offsetX, double offsetY, double offsetZ); /** * @brief 获取指定相机的标定数据 * @return 是否存在该相机的数据 */ bool getCalibData(int cameraIndex, double outMatrix[16], bool& outIsCalibrated) const; /** * @brief 获取指定相机的外参(欧拉角顺序 + 轴旋转角度) * @return 是否存在该相机的数据 */ bool getExtrinsicData(int cameraIndex, int& outEulerOrder, double& outRotX, double& outRotY, double& outRotZ) const; /** * @brief 获取指定相机的外参(含接近点偏移) * @return 是否存在该相机的数据 */ bool getExtrinsicData(int cameraIndex, int& outEulerOrder, double& outRotX, double& outRotY, double& outRotZ, double& outApproachOffset) const; /** * @brief 获取指定相机的完整外参(含接近点偏移 + 目标点补偿偏移) */ bool getExtrinsicData(int cameraIndex, int& outEulerOrder, double& outRotX, double& outRotY, double& outRotZ, double& outApproachOffset, double& outOffsetX, double& outOffsetY, double& outOffsetZ) const; /** * @brief 获取当前选中的相机索引 * @return 相机索引,未选择返回 -1 */ int currentCameraIndex() const; /** * @brief 设置默认文件打开路径 */ void setDefaultFilePath(const QString& path); /** * @brief 设置矩阵是否可编辑 */ void setMatrixEditable(bool editable); /** * @brief 显示/隐藏欧拉角顺序与绕 XYZ 旋转补偿的控件 */ void setExtrinsicControlsVisible(bool visible); /** * @brief 显示/隐藏目标点补偿偏移控件(默认隐藏) */ void setTargetOffsetVisible(bool visible); void setApproachOffsetVisible(bool visible); signals: /** * @brief 标定矩阵已从文件加载 * @param cameraIndex 相机索引 * @param matrix 16 个 double 的矩阵 */ void calibMatrixLoaded(int cameraIndex, const double* matrix); /** * @brief 请求保存标定数据 * @param cameraIndex 相机索引 * @param matrix 16 个 double 的矩阵 */ void saveCalibRequested(int cameraIndex, const double* matrix); private slots: void onCameraSelectionChanged(int index); void onLoadCalibMatrixClicked(); void onSaveCalibMatrixClicked(); private: void setupUI(); void setupExtrinsicGroup(); void initEulerOrderComboBox(); void displayMatrix(const double* matrix); void clearMatrix(); void displayIdentityMatrix(); void updateCalibStatus(bool isCalibrated); void displayExtrinsic(const HandEyeCalibData& data); void displayDefaultExtrinsic(); // 从矩阵编辑框读取当前矩阵值 bool readMatrixFromUI(double outMatrix[16]) const; // 从外参编辑框读取当前值并写回缓存 void commitExtrinsicToCache(int cameraIndex); // 查找缓存中指定相机的数据 HandEyeCalibData* findCalibData(int cameraIndex); const HandEyeCalibData* findCalibData(int cameraIndex) const; HandEyeCalibData& ensureCalibData(int cameraIndex); private: QComboBox* m_comboCamera; QLabel* m_labelStatus; QLineEdit* m_matrixEdits[4][4]; // 4x4 矩阵编辑框 QPushButton* m_btnLoad; QPushButton* m_btnSave; QGroupBox* m_groupExtrinsic; QComboBox* m_comboEulerOrder; QLineEdit* m_editRotX; QLineEdit* m_editRotY; QLineEdit* m_editRotZ; QLabel* m_labelApproachOffset; QLineEdit* m_editApproachOffset; QLabel* m_labelTargetOffset; QLineEdit* m_editOffsetX; QLineEdit* m_editOffsetY; QLineEdit* m_editOffsetZ; QVector m_calibDataCache; // 按相机索引缓存 QString m_defaultFilePath; bool m_matrixEditable; }; #endif // HANDEYECALIBWIDGET_H