相较于Ceres而言,Eigen函数库相对较为简单,我们上一篇文章详细描述了Ceres的使用以及注意事项,由于Ceres能够使用ceres::AutoDiffCostFunction这一类的自动求导函数,相对而言更加轻松,所以Eigen更多的是做矩阵运算。这里我们给出上一篇文章最后的Ceres求解的Eigen版本。我们可以看到本质上差别不大,只是Eigen需要自己求解雅克比矩阵J,并在用GN构建增量方程后,使用ldlt求解线性方程HX=g。
#include
#include
#include
#include
#include
using namespace std;
using namespace Eigen;
int main(int argc, char **argv) {
double ar = 1.0, br = 2.0, cr = 1.0; // 真实参数值
double ae = 2.0, be = -1.0, ce = 5.0; // 估计参数值
int N = 100; // 数据点
double w_sigma = 1.0; // 噪声Sigma值
cv::RNG rng; // OpenCV随机数产生器
vector<double> x_data, y_data; // 数据
for (int i = 0; i < N; i++) {
double x = i / 100.0;
x_data.push_back(x);
y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma));
}
// 开始Gauss-Newton迭代
int iterations = 100; // 迭代次数
double cost = 0, lastCost = 0; // 本次迭代的cost和上一次迭代的cost
for (int iter = 0; iter < iterations; iter++) {
Matrix3d H = Matrix3d::Zero(); // Hessian = J^T J in Gauss-Newton
Vector3d b = Vector3d::Zero(); // bias
cost = 0;
//-----------------用GN构建增量方程,HX=g---------------------------------//
for (int i = 0; i < N; i++) {
double xi = x_data[i], yi = y_data[i]; // 第i个数据点
// start your code here
// double error = 0; // 填写计算error的表达式
double error = yi-exp(ae * xi * xi + be * xi + ce); // 第i个数据点的计算误差
Vector3d J; // 雅可比矩阵,3x1
J[0] = -xi*xi*exp(ae * xi * xi + be * xi + ce); // de/da,函数求倒数,-df/da
J[1] = -xi*exp(ae * xi * xi + be * xi + ce);; // de/db
J[2] = -exp(ae * xi * xi + be * xi + ce);; // de/dc
H += J * J.transpose(); // GN近似的H
b += -error * J;
// end your code here
cost += error * error;
}
// 求解线性方程 Hx=b,建议用ldlt
// start your code here
Vector3d dx;
//LDL^T Cholesky求解
// clock_t time_stt2 = clock();
dx = H.ldlt().solve(b);//Hx=b,,,H.ldlt().solve(b)
// cout<<"LDL^T分解,耗时:\n"<<(clock()-time_stt2)/(double)
// CLOCKS_PER_SEC<<"ms"<
cout<<"\n dx:"<<dx.transpose()<<endl;
// return 0;//一写就死
// end your code here
if (isnan(dx[0])) {
cout << "result is nan!" << endl;
break;
}
if (iter > 0 && cost > lastCost) {
// 误差增长了,说明近似的不够好
cout << "cost: " << cost << ", last cost: " << lastCost << endl;
break;
}
// 更新abc估计值
ae += dx[0];
be += dx[1];
ce += dx[2];
lastCost = cost;
cout << "total cost: " << cost << endl;
}
cout << "estimated abc = " << ae << ", " << be << ", " << ce << endl;
return 0;
}
值得注意的是Eigen函数库是不存在动态链接库的,我们在CMakeList.txt编译时候只需要引入include即可。
Core: #include<Eigen/Core>,包含Matrix和Array类,基础的线性代数运算和数组操作。
Geometry: #include<Eigen/Geometry>,包含旋转,平移,缩放,2维和3维的各种变换。
LU :#include<Eigen/LU>,包含求逆,行列式,LU分解。
Cholesky: #include<Eigen/Cholesky>,包含LLT和LDLT Cholesky分解。
SVD: #include<Eigen/SVD>,包含SVD分解。
QR: #include<Eigen/QR>,包含QR分解。
Eigenvalues :#include<Eigen/Eigenvalues>,包含特征值,特征向量分解。
Sparse: #include<Eigen/Sparse>,包含稀疏矩阵的存储和运算。
Dense :#include<Eigen/Dense>,包含了 Core/Geometry/LU/Cholesky/SVD/QR/Eigenvalues模块。
Eigen: #include<Eigen/Eigen>,包含Dense和Sparse。
#include
#include
矩阵 ( M a t r i x ) 类的函数介绍 \color{blue}{矩阵(Matrix)类的函数介绍} 矩阵(Matrix)类的函数介绍
在Eigen中,所有矩阵和向量均为Matrix模板类的对象,向量是矩阵的行(或列)为1是的特殊情况。
M a t r i x 类有 6 个模板参数,主要使用前三个,剩下的使用默认值。 \color{red}{Matrix类有6个模板参数,主要使用前三个,剩下的使用默认值。} Matrix类有6个模板参数,主要使用前三个,剩下的使用默认值。
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
# Scalar 元素类型
# RowsAtCompileTime 行
# ColsAtCompileTime 列
# 例 typedef Matrix<int, 3, 3> Matrix3i;
# Options 比特标志位
# MaxRowsAtCompileTime和MaxColsAtCompileTime表示在编译阶段矩阵的上限。
强制性的三参数模板的原型 (三个参数分别表示:标量的类型,编译时的行,编译时的列)
///tips:用typedef定义了很多模板
///例如:Matrix4f 表示 4×4 的floats 矩阵
typedef Matrix<float, 4, 4> Matrix4f;
# 列向量
typedef Matrix<double, 3, 1> Vector3d;
# 行向量
typedef Matrix<float, 1, 3> RowVector3f;
Eigen的矩阵不仅能够在编译是确定大小(fixed size),也可以在运行时确定大小,就是所说的动态矩阵(dynamic size)。
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
typedef Matrix<int, Dynamic, 1> VectorXi;
/* 也可使用‘行’固定‘列’动态的矩阵 */
Matrix<float, 3, Dynamic>
可以使用默认的构造函数,不执行动态分配内存,也没有初始化矩阵参数:
Matrix3f a; // a是3-by-3矩阵,包含未初始化的 float[9] 数组
Eigen::Matrix3d //旋转矩阵(3*3)
Eigen::AngleAxisd //旋转向量(3*1)
Eigen::Vector3d //欧拉角(3*1)
Eigen::Quaterniond //四元数(4*1)
Eigen::Isometry3d //欧式变换矩阵(4*4)
Eigen::Affine3d //放射变换矩阵(4*4)
Eigen::Projective3d //射影变换矩阵(4*4)
MatrixXf b; // b是动态矩阵,当前大小为 0-by-0, 没有为数组的系数分配内存
/* 矩阵的第一个参数表示“行”,数组只有一个参数。根据跟定的大小分配内存,但不初始化 */
MatrixXf a(10,15); // a 是10-by-15阵,分配了内存,没有初始化
VectorXf b(30); // b是动态矩阵,当前大小为 30, 分配了内存,没有初始化
/* 对于给定的矩阵,传递的参数无效 */
Matrix3f a(3,3);
/* 对于维数最大为4的向量,可以直接初始化 */
Vector2d a(5.0, 6.0);
Vector3d b(5.0, 6.0, 7.0);
Vector4d c(5.0, 6.0, 7.0, 8.0);
系数都是从0开始,矩阵默认按列存储
#include
#include
using namespace std; using namespace Eigen;
int main()
{
MatrixXd m(2, 2);
m(0, 0) = 3; m(1, 0) = 2.5; m(0, 1) = -1;
m(1, 1) = m(1, 0) + m(0, 1);
cout << "Here is the matrix m:" << endl;
cout << m << endl;
VectorXd v(2);
v(0) = 4;
v[1] = v[0] - 1; //operator[] 在 vectors 中重载,意义和()相同
cout << "Here is the vector v:" << endl;
cout << v << endl;
getchar();
}
Matrix3f m;
m << 1, 2, 3, 4, 5, 6, 7, 8, 9;
cout << m;
[]操作符可以用于向量元素的获取,但不能用于matrix。Eigen支持以下的读/写元素语法。
matrix(i,j);
vector(i)
vector[i]
vector.x() // 第一个系数
vector.y() // 第二个系数
vector.z() // 第三个系数
vector.w() // 第四个系数
Matrix<double, 3, 3> A; // 固定行列。Matrix3d一样。
Matrix<double, 3, Dynamic> B; // 固定行,动态列。
A.resize(4, 4); // Runtime error if assertions are on.
B.resize(4, 9); // Runtime error if assertions are on.
A.resize(3, 3); // Ok; size didn't change.
B.resize(3, 9); // Ok; only dynamic cols changed.
上述的元素访问方法都通过断言检查范围,代价比较大。
通过定义EIGEN_NO_DEBUG 或 NDEBUG,取消断言。
通过使用coeff()和coeffRef(),来取消检查。比如,MatrixBase::coeff(int,int) const, MatrixBase::coeffRef(int,int)等。
矩阵 ( M a t r i x ) 类的运算 \color{blue}{矩阵(Matrix)类的运算} 矩阵(Matrix)类的运算
Eigen不支持类型自动转化,因此矩阵元素类型必须相同。
MatrixXcf a = MatrixXcf::Random(3,3);
a.transpose(); # 转置
a.conjugate(); # 共轭
a.adjoint(); # 共轭转置(伴随矩阵)
// 对于实数矩阵,conjugate不执行任何操作,adjoint等价于transpose
a.transposeInPlace() #原地转置
Vector3d v(1,2,3);
Vector3d w(4,5,6);
v.dot(w); # 点积
v.cross(w); # 叉积
Matrix2d a;
a << 1, 2, 3, 4;
a.sum(); # 所有元素求和
a.prod(); # 所有元素乘积
a.mean(); # 所有元素求平均
a.minCoeff(); # 所有元素中最小元素
a.maxCoeff(); # 所有元素中最大元素
a.trace(); # 迹,对角元素的和
// minCoeff和maxCoeff还可以返回结果元素的位置信息
int i, j;
a.minCoeff(&i, &j);
Array是个类模板,前三个参数必须指定,后三个参数可选。和Matrix对比,Matrix的运算遵守矩阵运算规则,Array则提供更加灵活的运算,比如对应系数相乘,向量加数量等。
Array<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime>
# 常见类定义
typedef Array<float, Dynamic, 1> ArrayXf
typedef Array<float, 3, 1> Array3f
typedef Array<double, Dynamic, Dynamic> ArrayXXd
typedef Array<double, 3, 3> Array33d
ArrayXf a = ArrayXf::Random(5);
a.abs(); # 绝对值
a.sqrt(); # 平方根
a.min(a.abs().sqrt()); # 两个array相应元素的最小值
当执行array_array时,执行的是相应元素的乘积,所以两个array必须具有相同的尺寸。
Matrix对象——>Array对象:.array() 函数
Array对象——>Matrix对象:_*.matrix()__ 函数
块是matrix或array中的矩形子块。
// 方法1
.block(i, j, p, q) //起点(i, j),块大小(p, q),构建一个动态尺寸的block
.block<p, q>(i, j) // 构建一个固定尺寸的block
matrix.row(i): 矩阵第i行
matrix.col(j): 矩阵第j列
Matrix3f P; // 3x3 float matrix.
Vector3f x; // 3x1 float matrix.
x.head(n) // x(1:n)
x.head<n>() // x(1:n)
x.tail(n) // x(end - n + 1: end)
x.tail<n>() // x(end - n + 1: end)
x.segment(i, n) // x(i+1 : i+n)
x.segment<n>(i) // x(i+1 : i+n)
P.block(i, j, rows, cols) // P(i+1 : i+rows, j+1 : j+cols)
P.block<rows, cols>(i, j) // P(i+1 : i+rows, j+1 : j+cols)
P.row(i) // P(i+1, :)
P.col(j) // P(:, j+1)
P.leftCols<cols>() // P(:, 1:cols)
P.leftCols(cols) // P(:, 1:cols)
P.middleCols<cols>(j) // P(:, j+1:j+cols)
P.middleCols(j, cols) // P(:, j+1:j+cols)
P.rightCols<cols>() // P(:, end-cols+1:end)
P.rightCols(cols) // P(:, end-cols+1:end)
P.topRows<rows>() // P(1:rows, :)
P.topRows(rows) // P(1:rows, :)
P.middleRows<rows>(i) // P(i+1:i+rows, :)
P.middleRows(i, rows) // P(i+1:i+rows, :)
P.bottomRows<rows>() // P(end-rows+1:end, :)
P.bottomRows(rows) // P(end-rows+1:end, :)
P.topLeftCorner(rows, cols) // P(1:rows, 1:cols)
P.topRightCorner(rows, cols) // P(1:rows, end-cols+1:end)
P.bottomLeftCorner(rows, cols) // P(end-rows+1:end, 1:cols)
P.bottomRightCorner(rows, cols) // P(end-rows+1:end, end-cols+1:end)
P.topLeftCorner<rows,cols>() // P(1:rows, 1:cols)
P.topRightCorner<rows,cols>() // P(1:rows, end-cols+1:end)
P.bottomLeftCorner<rows,cols>() // P(end-rows+1:end, 1:cols)
P.bottomRightCorner<rows,cols>() // P(end-rows+1:end, end-cols+1:end)
特殊矩阵
零阵:类静态成员函数Zero()
常量矩阵:Constant(rows, cols, value)
随机矩阵:Random()
单位矩阵:Identity()
构建从low到high等间距的size长度的序列,适用于vector和一维数组:LinSpaced(size, low, high)
功能函数
setZero()
setIdentity()
Matrix<double, dynamic,="" dynamic=""> C; // Full dynamic. Same as MatrixXd.
MatrixXd::Identity(rows,cols) // eye(rows,cols)
C.setIdentity(rows,cols) // C = eye(rows,cols)
MatrixXd::Zero(rows,cols) // zeros(rows,cols)
C.setZero(rows,cols) // C = zeros(rows,cols)
MatrixXd::Ones(rows,cols) // ones(rows,cols)
C.setOnes(rows,cols) // C = ones(rows,cols)
MatrixXd::Random(rows,cols) // rand(rows,cols)_2-1 // MatrixXd::Random returns uniform random numbers in (-1, 1).
C.setRandom(rows,cols) // C = rand(rows,cols)_2-1
VectorXd::LinSpaced(size,low,high) // linspace(low,high,size)’
v.setLinSpaced(size,low,high) // v = linspace(low,high,size)’
VectorXi::LinSpaced(((hi-low)/step)+1, // low:step:hi
low,low+step*(size-1)) //
squaredNorm():L2范数,等价于计算vector自身点积
():p范数,p可以取Infinity,表无穷范数
Vector3f x; // 3x1 float matrix.
x.norm() // norm(x). 注意norm(R)在Eigen中不起作用。
x.squaredNorm() // dot(x, x) 注意,对于复数并不成立
x.dot(y) // dot(x, y)
x.cross(y) // cross(x, y) 需要 #include
布尔归约
all()=true: matrix或array中所有元素为true
any()=true: 到少有一个为true
count(): 返回true元素个数
// sample
ArrayXXf A(2, 2);
A << 1,2,3,4;
(A > 0).all();
(A > 0).any();
(A > 0).count();
迭代器,获取某元素位置
// Reductions.
int r, c;
// Eigen // Matlab
R.minCoeff() // min(R(:))
R.maxCoeff() // max(R(:))
s = R.minCoeff(&r, &c) // [s, i] = min(R(:)); [r, c] = ind2sub(size(R), i);
s = R.maxCoeff(&r, &c) // [s, i] = max(R(:)); [r, c] = ind2sub(size(R), i);
R.sum() // sum(R(:))
R.colwise().sum() // sum(R)
R.rowwise().sum() // sum(R, 2) or sum(R')'
R.prod() // prod(R(:))
R.colwise().prod() // prod(R)
R.rowwise().prod() // prod(R, 2) or prod(R')'
R.trace() // trace(R)
R.all() // all(R(:))
R.colwise().all() // all(R)
R.rowwise().all() // all(R, 2)
R.any() // any(R(:))
R.colwise().any() // any(R)
R.rowwise().any() // any(R, 2)
部分归约
// sample
Eigen::MatrixXf mat(2,3);
mat << 1,2,3,
4,5,6;
std::cout << mat.colwise().maxCoeff();
// output: 4, 5, 6
// mat.rowWise() the same as before
广播,针对vector,沿行或列重复构建一个matrix
// sample
Eigen::MatrixXf mat(2,3);
Eigen::VectorXf v(2);
mat << 1,2,3,4,5,6;
v << 0,1;
mat.colwise() += v;
// output: 1, 2, 3, 5, 6, 7
Map,用于利用数据的内在,并将其转为Eigen类型。
// Eigen can map existing memory into Eigen matrices.
float array[3];
Vector3f::Map(array).fill(10); // 数组上创建临时Map,并将vector大小设置为10
int data[4] = {1, 2, 3, 4};
Matrix2i mat2x2(data); // copies data into mat2x2
Matrix2i::Map(data) = 2*mat2x2; // overwrite elements of data with 2*mat2x2
MatrixXi::Map(data, 2, 2) += mat2x2; // adds mat2x2 to elements of data (alternative syntax if size is not know at compile time)
x = A.ldlt().solve(b)); // A sym. p.s.d. #include
x = A.llt() .solve(b)); // A sym. p.d. #include
x = A.lu() .solve(b)); // Stable and fast. #include
x = A.qr() .solve(b)); // No pivoting. #include
x = A.svd() .solve(b)); // Stable, slowest. #include
#include
#include
//g++ Linear_algebra_and_decompositions.cpp -o la -I/download/eigen
using namespace std;
using namespace Eigen;
//QR方法解线性方程组
int main()
{
Matrix3f A;
Vector3f b;
A << 1,2,3, 4,5,6, 7,8,10;
b << 3, 3, 4;
cout << "Here is the matrix A:\n" << A << endl;
cout << "Here is the vector b:\n" << b << endl;
Vector3f x = A.colPivHouseholderQr().solve(b);
cout << "The solution is:\n" << x << endl;
}
//矩阵求逆
int main()
{
Matrix2f A, b;
A << 2, -1, -1, 3;
b << 1, 2, 3, 1;
cout << "Here is the matrix A:\n" << A << endl;
cout << "Here is the right hand side b:\n" << b << endl;
Matrix2f x = A.ldlt().solve(b);
cout << "The solution is:\n" << x << endl;
}
//计算数值法求解和真实值的残差
int main()
{
MatrixXd A = MatrixXd::Random(100,100);
MatrixXd b = MatrixXd::Random(100,50);
MatrixXd x = A.fullPivLu().solve(b);
double relative_error = (A*x - b).norm() / b.norm(); // norm() is L2 norm
cout << "The relative error is:\n" << relative_error << endl;
}
//计算特征值和特征向量
int main()
{
Matrix2f A;
A << 1, 2, 2, 3;
cout << "Here is the matrix A:\n" << A << endl;
SelfAdjointEigenSolver<Matrix2f> eigensolver(A);
if (eigensolver.info() != Success) abort();
cout << "The eigenvalues of A are:\n" << eigensolver.eigenvalues() << endl;
cout << "Here's a matrix whose columns are eigenvectors of A \n"
<< "corresponding to these eigenvalues:\n"
<< eigensolver.eigenvectors() << endl;
}
//计算逆行列式
int main()
{
Matrix3f A;
A << 1, 2, 1,
2, 1, 0,
-1, 1, 2;
cout << "Here is the matrix A:\n" << A << endl;
cout << "The determinant of A is " << A.determinant() << endl;
cout << "The inverse of A is:\n" << A.inverse() << endl;
}
//最小二乘解
int main()
{
MatrixXf A = MatrixXf::Random(3, 2);
cout << "Here is the matrix A:\n" << A << endl;
VectorXf b = VectorXf::Random(3);
cout << "Here is the right hand side b:\n" << b << endl;
cout << "The least-squares solution is:\n"
<< A.jacobiSvd(ComputeThinU | ComputeThinV).solve(b) << endl;
}
//分离矩阵计算与构造(解耦合)
int main()
{
Matrix2f A, b;
LLT<Matrix2f> llt;
A << 2, -1, -1, 3;
b << 1, 2, 3, 1;
cout << "Here is the matrix A:\n" << A << endl;
cout << "Here is the right hand side b:\n" << b << endl;
cout << "Computing LLT decomposition..." << endl;
llt.compute(A);
cout << "The solution is:\n" << llt.solve(b) << endl;
A(1,1)++;
cout << "The matrix A is now:\n" << A << endl;
cout << "Computing LLT decomposition..." << endl;
llt.compute(A);
cout << "The solution is now:\n" << llt.solve(b) << endl;
}
// Rank-revealing分解
int main()
{
Matrix3f A;
A << 1, 2, 5,
2, 1, 4,
3, 0, 3;
cout << "Here is the matrix A:\n" << A << endl;
FullPivLU<Matrix3f> lu_decomp(A);
cout << "The rank of A is " << lu_decomp.rank() << endl;
cout << "Here is a matrix whose columns form a basis of the null-space of A:\n"
<< lu_decomp.kernel() << endl;
cout << "Here is a matrix whose columns form a basis of the column-space of A:\n"
<< lu_decomp.image(A) << endl; // yes, have to pass the original A
}
//LU分解,设定阈值求秩
int main()
{
Matrix2d A;
A << 2, 1,
2, 0.9999999999;
FullPivLU<Matrix2d> lu(A);
cout << "By default, the rank of A is found to be " << lu.rank() << endl;
lu.setThreshold(1e-5);
cout << "With threshold 1e-5, the rank of A is found to be " << lu.rank() << endl;
CMakeLists.txt
# cmake最低版本号要求
cmake_minimum_required(VERSION 3.14)
# 项目名称
project(eigen_demo)
# 设置Eigen3_DIR所在目录,对应eigen安装目录下的cmake目录,目录内包含有Eigen3Config.cmake等文件
set(Eigen3_DIR "./install/share/eigen3/cmake")
# 搜索查询Eigen3
find_package(Eigen3 REQUIRED NO_MODULE)
# 添加以main.cpp文件为基础的可执行目标文件
add_executable(eigen_base eigen_base.cpp)
# 为项目可行执行文件引入eigen依赖
target_link_libraries(eigen_base Eigen3::Eigen)
add_executable(eigen_geometry eigenGeometry.cpp)
# 为项目可行执行文件引入eigen依赖
target_link_libraries(eigen_geometry Eigen3::Eigen)
eigenGeometry.cpp
#include
#include
using namespace std;
#include
// Eigen 几何模块
#include
/****************************
* 本程序演示了 Eigen 几何模块的使用方法
****************************/
int main ( int argc, char** argv )
{
// ---------- 初始化 -----------//
//角轴 罗德公式转换 eulerAngles/geometry relationship
//旋转向量<======>旋转矩阵<======>四元数<======>欧拉角
// 旋转向量(轴角):沿Z轴旋转45°
Eigen::AngleAxisd rotation_vector ( M_PI/4, Eigen::Vector3d ( 0,0,1 ) );
cout<<"rotation_vector axis = \n" << rotation_vector.axis() <<"\n rotation_vector angle = "
<< rotation_vector.angle()<<endl;
cout << "旋转向量 to 旋转矩阵如下:\n" << rotation_vector.matrix() << endl; //旋转向量转为旋转矩阵
cout << ".........." << endl;
Eigen::Quaterniond quat2 = Eigen::Quaterniond(-1, 0, 1, 1);
quat2.normalize(); //四元数归一化
// 请注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
cout<<"四元数归一化:quaternion = \n"<<quat2.coeffs() <<endl;
//旋转矩阵:沿Z轴旋转45°
Eigen::Matrix3d rotation_matrix = Eigen::Matrix3d::Identity();
rotation_matrix << 0.707, -0.707, 0,
0.707, 0.707, 0,
0, 0, 1;
cout<<"rotation matrix =\n"<<rotation_matrix <<endl;
cout<<"-----------------由罗德公式转换-------------:\n"<<endl;
//由罗德公式转换:from rotation_vector to quaterniond
Eigen::Matrix<double, 3, 3> matrix_33;
Eigen::Matrix<double, 3, 3> matrix_axis_inverse_symmetry;
Eigen::Vector3d temp = rotation_vector.axis();
double n1 = temp(0);
double n2 = temp(1);
double n3 = temp(2);
matrix_axis_inverse_symmetry << 0, -n3, n2, n3, 0, -n1, -n2, n1, 0; //反对称阵
Eigen::Matrix3d rotation_matrix_1 = Eigen::Matrix3d::Identity(); //单位阵
matrix_33 = cos(rotation_vector.angle())*rotation_matrix_1 + (1-cos(rotation_vector.angle()))*
rotation_vector.axis()*rotation_vector.axis().transpose()+sin(rotation_vector.angle())*matrix_axis_inverse_symmetry;
cout << ".....罗德公式转换结果:....." << endl;
cout << matrix_33 << endl;
// 四元数:沿Z轴旋转45°
Eigen::Quaterniond quat = Eigen::Quaterniond(cos(rotation_vector.angle()/2),n1*sin(rotation_vector.angle()/2),
n2*sin(rotation_vector.angle()/2),n3*sin(rotation_vector.angle()/2)); //实部在第一个,直接计算
// 请注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
cout<<"四元数输出方法1:quaternion = \n"<<quat.coeffs() <<endl;
Eigen::Quaterniond quat1 = Eigen::Quaterniond(rotation_vector); //直接通过旋转向量得到
cout << "coeffs()输出四元数为:\n" << quat1.coeffs() << endl; //注意coeffs的顺序是(x,y,z,w)
cout<<"四元数输出方法3\n x = " << quat1.x() << "\n y = " << quat1.y() << "\n z = " << quat1.z() << "\n 实部w = " << quat1.w() << endl;
Eigen::Quaterniond quat3 = Eigen::Quaterniond(rotation_matrix); //直接通过旋转矩阵得到
cout << "通过旋转矩阵输出四元数为:\n" << quat3.coeffs() << endl; //注意coeffs的顺序是(x,y,z,w)
// 欧拉角: :沿Z轴旋转45°
Eigen::Vector3d euler_angles = Eigen::Vector3d(M_PI/4, 0, 0);// ZYX顺序,即roll pitch yaw顺序
cout<<"Euler: yaw pitch roll = "<<euler_angles.transpose()<<endl;
Eigen::Vector3d euler_angles1 = rotation_matrix.eulerAngles ( 2,1,0 ); // ZYX顺序,即roll pitch yaw顺序
cout<<"yaw pitch roll = "<<euler_angles1.transpose()<<endl;
return 0;
}
https://www.guyuehome.com/34670