两组3D点结算两坐标系的R和T

     每组最少三个点解两坐标系的R和T,采用论文《Least-Squares Fitting of Two 3-D Point Sets》的方法,参照nghiaho的博客:http://nghiaho.com/?page_id=671,及gihub的MATLAB代码,github地址为:https://github.com/nghiaho12/rigid_transform_3D,用C++及Eigen库实现了此功能(考虑了两组点尺度不同问题),记录一下。

#include <pangolin/pangolin.h>
#include <Eigen/Core>
#include <unistd.h>
#include<iostream>
#include<fstream>
#include<algorithm>
#include<chrono>
#include<opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <math.h>


using namespace std;
using namespace cv;
using namespace Eigen;

  

int main(int argc, char **argv) {

  //vector<Isometry3d, Eigen::aligned_allocator<Isometry3d>> poses;
   //Eigen::Quaterniond q;
    //MatrixXd t = MatrixXd::Random(3,1);
    Vector3d t = Vector3d::Random(),error = Vector3d::Random();
    Matrix3d R, R_estimate,V,U;


    R << -0.6208, 0.7763,-0.1091,
         -0.2820,-0.3509,-0.8929,
         -0.7315,-0.5236, 0.4368;
    //cout<<R.determinant()<<endl;
         
    int n = 10;
    //A: 10*3,  B: 3*10
    MatrixXd A = 100*MatrixXd::Random(n,3),A_move,B_move,A_norm,B_norm,lam2;
    MatrixXd B = 100*R*A.transpose()+(100*t).replicate(1,n)+error.replicate(1,n);
    
    Vector3d centroid_A = A.colwise().sum()/n;
    Vector3d centroid_B = B.rowwise().sum()/n;
    //cout<<"centroid_A = \n"<<centroid_A<<endl;
    //cout<<"centroid_B = \n"<<centroid_B<<endl;

    
    A_move = A.transpose() - centroid_A.replicate(1,n);
    B_move = B - centroid_B.replicate(1,n);
    A_norm = A_move.cwiseProduct(A_move).colwise().sum();
    B_norm = B_move.cwiseProduct(B_move).colwise().sum();
    //cout<<"A_norm.array():\n"<<A_norm.array()<<endl;
    lam2 = A_norm.cwiseQuotient(B_norm);
    double lam_2 = sqrt(lam2.mean());
    cout<<"lam_2:\n"<<1/lam_2<<endl;
    
    

    Matrix<double,3,3> H = (A.transpose()-centroid_A.replicate(1,n))*((lam_2*B-(lam_2*centroid_B).replicate(1,n)).transpose());
    //cout<<"H = "<<H<<endl;
    //SVD decomposition
    JacobiSVD<MatrixXd> svd(H, ComputeThinU | ComputeThinV);
    //cout<<"svd.matrixU():\n"<<svd.matrixU()<<endl;
    //cout<<"svd.matrixV():\n"<<svd.matrixV()<<endl;
    //cout<<"svd.singularValues():\n"<<svd.singularValues()<<endl;

    U = svd.matrixU();
    V = svd.matrixV();
    R_estimate = V*U.transpose();
    if(R_estimate.determinant()<0)
    {
        cout<<"Reflection detected\n"<<endl;
        V.col(2) = -1*V.col(2);
        R_estimate = V*U.transpose();
    }
    t = -R_estimate*centroid_A + lam_2 * centroid_B;
    
    Matrix<double,3,10> B2,err;
    double err_sum,rmse;
    
    B2 = R_estimate*A.transpose()+t.replicate(1,n);
    err = B2 -lam_2 *B;
    err =  err.cwiseProduct(err);
    err_sum = err.sum();
    rmse = sqrt(err_sum);
    cout<<"rmse:\n"<<rmse<<endl;
    return 0;
}

运行结果:

### 计算两个三维坐标系间的RT变换矩阵 已知两组两个三维坐标系下的对应坐标,可以通过计算旋转矩阵 \( R \) 平移向量 \( t \) 来确定两个坐标系之间的变换关系。这种变换通常称为刚体变换,其数学表达式为: \[ P^B = R \cdot P^A + t \] 其中 \( P^A \) \( P^B \) 分别表示坐标系 \( A \) \( B \) 中的坐标,\( R \) 为 \( 3 \times 3 \) 的旋转矩阵,\( t \) 为 \( 3 \times 1 \) 的平移向量。 #### 数据准备 假设已知至少三对对应的坐标: - 在坐标系 \( A \) 下的集为 \( \{P_1^A, P_2^A, P_3^A\} \),表示为矩阵 \( P_A = [P_1^A, P_2^A, P_3^A] \)。 - 在坐标系 \( B \) 下的集为 \( \{P_1^B, P_2^B, P_3^B\} \),表示为矩阵 \( P_B = [P_1^B, P_2^B, P_3^B] \)。 #### 计算步骤 1. **计算质心** 首先分别计算两组的质心: \[ \bar{P}_A = \frac{1}{n} \sum_{i=1}^{n} P_i^A, \quad \bar{P}_B = \frac{1}{n} \sum_{i=1}^{n} P_i^B \] 其中 \( n \) 是的数量。 2. **去质心化** 将所有相对于各自的质心进行去质心化处理: \[ Q_A = P_A - \bar{P}_A, \quad Q_B = P_B - \bar{P}_B \] 3. **构造协方差矩阵** 构造协方差矩阵 \( H \): \[ H = Q_A \cdot Q_B^T \] 4. **奇异值分解(SVD)** 对协方差矩阵 \( H \) 进行奇异值分解: \[ H = U \cdot \Sigma \cdot V^T \] 其中 \( U \) \( V \) 是正交矩阵,\( \Sigma \) 是对角矩阵。 5. **计算旋转矩阵** 根据 SVD 结果计算旋转矩阵 \( R \): \[ R = V \cdot \text{diag}(1, 1, \det(V \cdot U^T)) \cdot U^T \] 这里通过加入 \( \det(V \cdot U^T) \) 确保旋转矩阵 \( R \) 的行列式为 1,避免出现反射变换[^1]。 6. **计算平移向量** 平移向量 \( t \) 可通过以下公式计算: \[ t = \bar{P}_B - R \cdot \bar{P}_A \] #### Python 实现代码 以下是基于上述理论的 Python 实现: ```python import numpy as np def compute_rt_matrix(P_A, P_B): """ 计算两个三维坐标系之间的旋转平移矩阵 :param P_A: 坐标系 A 下的集 (3xN) :param P_B: 坐标系 B 下的集 (3xN) :return: 旋转矩阵 R (3x3), 平移向量 t (3x1) """ # 质心计算 centroid_A = np.mean(P_A, axis=1, keepdims=True) centroid_B = np.mean(P_B, axis=1, keepdims=True) # 去质心化 Q_A = P_A - centroid_A Q_B = P_B - centroid_B # 协方差矩阵 H = Q_A @ Q_B.T # 奇异值分解 U, _, Vt = np.linalg.svd(H) # 旋转矩阵 R = Vt.T @ np.diag([1, 1, np.linalg.det(Vt.T @ U.T)]) @ U.T # 平移向量 t = centroid_B - R @ centroid_A return R, t # 示例数据 P_A = np.array([[0, 1, 2], [0, 1, 2], [0, 0, 0]]) # 坐标系 A 下的集 (3x3) P_B = np.array([[1, 2, 3], [1, 2, 3], [0, 0, 0]]) # 坐标系 B 下的集 (3x3) R, t = compute_rt_matrix(P_A, P_B) print("旋转矩阵 R:\n", R) print("平移向量 t:\n", t) ``` #### 注意事项 - 如果输入对数量较少或存在噪声,可能导致计算结果不够准确。此时可以考虑使用优化方法,例如最小二乘法或鲁棒估计方法[^3]。 - 该方法假设两个坐标系之间的变换为刚体变换,即仅包含旋转平移。如果存在尺度变化,则需要扩展到仿射变换模型[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值