by luoshi006
参考:
http://ethaneade.com/lie.pdf
http://www.qiujiawei.com/understanding-quaternions/#4
本文主要介绍表示三维空间变换(transformation)的李群。李群是一个拓扑群,也是一个光滑流形。主要用于机器人及计算机视觉领域。
Sophus 在 Eigen 的基础上,实现了 李群 李代数 的计算。三维旋转的计算以四元数为基础(Eigen :: Quaterniond)。
//根据高博文章,此处采用相同的代码版本;
git clone https://github.com/strasdat/Sophus.git
cd Sophus
git checkout a621ff
SO3 类中只声明一个变量:
protected:
Quaterniond unit_quaternion_;
计算过程中所需的 旋转矩阵 反对称矩阵 等,都在调用时通过 四元数 计算。
Note
unit_quaternion_ 为 单位四元数, (w,x,y,z) ( w , x , y , z ) 。
用于表示旋转的四元数都需要归一化,将四元数的自由变量限制在 三维。
四元数的一般形式:
也可以如下表示:
纯四元数:实部为 0 的四元数,
共轭四元数:虚部取反的四元数,
共轭四元数的乘积:
四元数范数
四元数归一化
四元数除以其范数,得到归一化形式。
单位四元数:范数为 1 的四元数,【欧拉参数】
其中, ||v||=1 | | v | | = 1 。
对于单位四元数,有:
SO3();
SO3(const SO3 & other);
SO3(const Matrix3d & _R);
SO3(const Quaterniond & unit_quaternion);
SO3(double rot_x,
double rot_y,
double rot_z);
构造函数的实现比较简单,直接调用 Eigen::Quaterniond
的构造函数完成 单位四元数 的初始化。
void SO3::operator=(const SO3 & other)
SO3 SO3::operator*(const SO3& other) const
void SO3::operator*=(const SO3& other)
四元数的乘法表示两次旋转依次执行。
Vector3d SO3::operator*(const Vector3d & xyz) const{
return unit_quaternion_._transformVector(xyz);
//Rotation of a vector by a quaternion.
//向量 xyz 以 四元数 进行旋转;
}
对于 R∈SO(3) R ∈ S O ( 3 ) , R−1=RT R − 1 = R T 。旋转矩阵的逆对应于四元数的共轭。
SO3 SO3
::inverse() const
{
return SO3(quaternion_.conjugate());
}
调用 eigen
库,求取四元数对应的旋转矩阵。
Matrix3d SO3
::matrix() const
{
return quaternion_.toRotationMatrix();
//Convert the quaternion to a 3x3 rotation matrix.
}
用于在 SO3 的正切平面上切换。
定义:
推导:
注:
若 P P 是一个 阶可逆矩阵,则
eP−1AP=P−1eAP e P − 1 A P = P − 1 e A P
Matrix3d SO3
::Adj() const
{
return quaternion_.toRotationMatrix();
}
李代数 so(3) s o ( 3 ) 的生成元,即 so(3) s o ( 3 ) 互不相干的独立变量。各个生成元之间相互正交。
其中,生成元:
Matrix3d SO3
::generator(int i)
{
//参数i 对应生成元索引号;
assert(i>=0 && i<3);
Vector3d e;
e.setZero();
e[i] = 1.f;
return hat(e);
}
其中,
Matrix3d SO3
::hat(const Vector3d & v)
{
Matrix3d Omega;
Omega << 0, -v(2), v(1)
, v(2), 0, -v(0)
, -v(1), v(0), 0;
return Omega;
}
对于单位四元数,有 q=[cosθ2,v⋅sinθ2] q = [ c o s θ 2 , v ⋅ s i n θ 2 ] 。其中, ||v||=1 | | v | | = 1 。
对数运算:
// Sophus first commit 版本代码;
Vector3d SO3
::log() const
{
return SO3::log(*this);
}
Vector3d SO3
::log(const SO3 & other)
{
double q_real = other.quaternion_.w();
if (q_real>1.-SMALL_EPS)
{
return Vector3d(0.,0.,0.);
}
double theta = 2.*acos(q_real);
double theta_by_sin_half_theta = theta/sqrt(1. - q_real*q_real);
return Vector3d(theta_by_sin_half_theta*other.quaternion_.x(),
theta_by_sin_half_theta*other.quaternion_.y(),
theta_by_sin_half_theta*other.quaternion_.z());
}
在后期版本中,对这部分代码优化:
Vector3d SO3
::logAndTheta(const SO3 & other, double * theta)
{
double n = other.unit_quaternion_.vec().norm();
double w = other.unit_quaternion_.w();
double squared_w = w*w;
double two_atan_nbyw_by_n;
// Atan-based log thanks to
//
// C. Hertzberg et al.:
// "Integrating Generic Sensor Fusion Algorithms with Sound State
// Representation through Encapsulation of Manifolds"
// Information Fusion, 2011
if (n < SMALL_EPS)
{
// If quaternion is normalized and n=1, then w should be 1;
// w=0 should never happen here!
assert(fabs(w)>SMALL_EPS);
two_atan_nbyw_by_n = 2./w - 2.*(n*n)/(w*squared_w);
}
else
{
if (fabs(w)<SMALL_EPS)
{
if (w>0)
{
two_atan_nbyw_by_n = M_PI/n;
}
else
{
two_atan_nbyw_by_n = -M_PI/n;
}
}
two_atan_nbyw_by_n = 2*atan(n/w)/n;
}
*theta = two_atan_nbyw_by_n*n;
return two_atan_nbyw_by_n * other.unit_quaternion_.vec();
}
使用三维角速度矢量 ω ω 构造四元数,得到SO3, q=[cosθ2,v⋅sinθ2] q = [ c o s θ 2 , v ⋅ s i n θ 2 ] 。
其中, θ=ωTω−−−−√ θ = ω T ω
SO3 SO3
::exp(const Vector3d & omega)
{
double theta = omega.norm();
if(theta<SMALL_EPS)
{
return SO3(Quaterniond::Identity());
}
double half_theta = 0.5*theta;
double sin_half_theta = sin(half_theta);
double sin_half_theta_by_theta = sin_half_theta/theta;
return SO3(Quaterniond(cos(half_theta),
sin_half_theta_by_theta*omega.x(),
sin_half_theta_by_theta*omega.y(),
sin_half_theta_by_theta*omega.z()));
}
在后期版本中,对这部分代码优化:
在小角度情况下加入优化细节【没看懂】
SO3 SO3
::expAndTheta(const Vector3d & omega, double * theta)
{
*theta = omega.norm();
double half_theta = 0.5*(*theta);
double imag_factor;
double real_factor = cos(half_theta);
if((*theta)<SMALL_EPS)
{
double theta_sq = (*theta)*(*theta);
double theta_po4 = theta_sq*theta_sq;
imag_factor = 0.5-0.0208333*theta_sq+0.000260417*theta_po4;
}
else
{
double sin_half_theta = sin(half_theta);
imag_factor = sin_half_theta/(*theta);
}
return SO3(Quaterniond(real_factor,
imag_factor*omega.x(),
imag_factor*omega.y(),
imag_factor*omega.z()));
}
vee 运算由反对称矩阵得到三维角速度矢量:
Vector3d SO3
::vee(const Matrix3d & Omega)
{
assert(fabs(Omega(2,1)-Omega(1,2))<SMALL_EPS);
assert(fabs(Omega(0,2)-Omega(2,0))<SMALL_EPS);
assert(fabs(Omega(1,0)-Omega(0,1))<SMALL_EPS);
return Vector3d(Omega(2,1), Omega(0,2), Omega(1,0));
}
李括号定义了两个向量场之间的差异,此处李括号封闭叉乘运算。
Vector3d SO3
::lieBracket(const Vector3d & omega1, const Vector3d & omega2)
{
return omega1.cross(omega2);
}
此处得到 2ω 2 ω ,在后期代码中已删除。
Vector3d SO3::
deltaR(const Matrix3d & R)
{
Vector3d v;
v(0) = R(2,1)-R(1,2);
v(1) = R(0,2)-R(2,0);
v(2) = R(1,0)-R(0,1);
return v;
}
转自:http://blog.sina.com.cn/s/blog_679741950100ivz0.html颜色通常用三个相对独立的属性来描述,三个独立变量综合作用,自然就构成一个空间坐标,这就是颜色空间。而颜色可以由不同的角度,用三个一组的不同属性加以描述,就产生了不同的颜色空间。但被描述的颜色对象本身是客观的,不同颜色空间只是从不同的角度去衡量同一个对象。
一个基于vue-cli3和express的前后端分离项目。实现了登录注册,无状态token验证。技术栈vue全家桶+ element-ui + express + mongoose +bcryptjs + cors + jsonwebtoken等https://github.com/akzaq/blog.git
1 需求在同一局域网内,手机(PC端)浏览器作为客户端,然后手机app里面通过socket写服务代码,然后浏览器访问手机服务端访问网页和图片和css文件和下载APK2 代码实现创建服务线程代码 var serverSocket: ServerSocket? = null var run = true inner class HttpServerThread : Runnable { var TAG = javaClass.na...
环境配置JDK1.8Win10问题描述今天使用Spring配置完之后, 运行测试提示 :Failed to initialize component [org.apache.catalina.webresources.JarResource解决办法找到本地仓库位置,一般在C:\Users\Admin\.m2\repository删除repository目录下面所有文件夹p...
【转载】BLEU,ROUGE,METEOR,CIDEr-浅述自然语言处理机器翻译常用评价度量1. BLUE-基于精确度的相似性度量方法2. ROUGE-基于召回率的相似性度量方法3. METEOR-基于召回率相似性度量方法4. CIDEr-基于-N-grams的度量方法https://blog.csdn.net/joshuaxx316/article/details/58696552...
SAP Business One 窗体应用程序ADDON开发
目前,WebServer几种主流的并发模型:多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求;但在高并发环境下,多线程的开销比较大;基于回调的异步IO,如Nginx服务器使用的epoll模型,这种模式通过事件驱动的方式使用异步IO,使服务器持续运转,但人的思维模式是串行的,大量回调函数会把流程分割,对于问题本身的反应不够自然;协程,不需要抢占式调度,可以有效...
console.log(xxx); 在控制台Console输xxx,不会打断页面的正常操作setTimeout(function(){func1();},0)func2();//在上面这段代码中func2先执行,func1后执行,0s延迟表示回调函数将插队到一个能立即执行的时段//如果不写0,浏览器自动配置时间,多为10-100ms,所以还是func2先执行,
// 添加要在此处预编译的标头#ifndef GZDEMO_H#define GZDEMO_H#pragma once#include <iostream>using namespace std; //函数定义void input(int[], int[]);void print(int[], int[]);//函数实现void input(int values[], int len){ if (len > 5) { cout << " 数组
标题:等差素数列2,3,5,7,11,13,....是素数序列。类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。上边的数列公差为30,长度为6。2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。这是数论领域一项惊人的成果!有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:长度为10的等差素数列,其公差最小值是多少?注意:需要提交的是...
1 基础为什么 Java 中只有值传递?int 范围?float 范围?hashCode 与 equals,什么关系?String StringBuffer 和 StringBuilder 的区别是什么?String 为什么是不可变的?Java 序列化中如果有些字段不想进行序列化 怎么办?构造器 Constructor 是否可被 override?java 异常体系?RuntimeException Exception Error 的区别,举常见的例子字符型常