Sophus 李群 --[SO3]_so3李群_luoshi006的博客-程序员秘密

技术标签: Sophus  lie-group  算法  so3  

by luoshi006

参考:
http://ethaneade.com/lie.pdf
http://www.qiujiawei.com/understanding-quaternions/#4

Sophus 李群

简介

本文主要介绍表示三维空间变换(transformation)的李群。李群是一个拓扑群,也是一个光滑流形。主要用于机器人及计算机视觉领域。
Sophus 在 Eigen 的基础上,实现了 李群 李代数 的计算。三维旋转的计算以四元数为基础(Eigen :: Quaterniond)。

//根据高博文章,此处采用相同的代码版本;
git clone https://github.com/strasdat/Sophus.git 
cd Sophus 
git checkout a621ff 

SO3

变量

SO3 类中只声明一个变量:

protected:
  Quaterniond unit_quaternion_;

计算过程中所需的 旋转矩阵 反对称矩阵 等,都在调用时通过 四元数 计算。

Note
unit_quaternion_ 为 单位四元数, (w,x,y,z) ( w , x , y , z )
用于表示旋转的四元数都需要归一化,将四元数的自由变量限制在 三维。

四元数

四元数的一般形式:

q=w+xi+yj+zk q = w + x i + y j + z k

也可以如下表示:

q=[w,v]=[w,xi+yj+zk] q = [ w , v ] = [ w , x i + y j + z k ]

纯四元数:实部为 0 的四元数,

q=[0,v]=[0,xi+yj+zk] q = [ 0 , v ] = [ 0 , x i + y j + z k ]

共轭四元数:虚部取反的四元数,

q=[w,v]q=[w,v] q = [ w , v ] q ∗ = [ w , − v ]

共轭四元数的乘积:

qq=[w,v][w,v]=[w2v(v),wv+wv+v×(v)]=[s2+v2,0] q ⋅ q ∗ = [ w , v ] ⋅ [ w , − v ] = [ w 2 − v ⋅ ( − v ) , − w v + w v + v × ( − v ) ] = [ s 2 + v 2 , 0 ]

四元数范数

||q||=w2+v2 | | q | | = w 2 + v 2

四元数归一化
四元数除以其范数,得到归一化形式。

q=qw2+v2 q ′ = q w 2 + v 2

单位四元数:范数为 1 的四元数,【欧拉参数

q=[cosθ2,vsinθ2] q = [ c o s θ 2 , v ⋅ s i n θ 2 ]

其中, ||v||=1 | | v | | = 1

对于单位四元数,有:

qq=[1,0]=[1,0,0,0]q1=q q ⋅ q ∗ = [ 1 , 0 ] = [ 1 , 0 , 0 , 0 ] q − 1 = q ∗


函数

构造函数

  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 以 四元数 进行旋转;
}

求逆

对于 RSO(3) R ∈ S O ( 3 ) R1=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 的伴随 Adjoint

用于在 SO3 的正切平面上切换。
定义:

ωso(3),RSO(3)Rexp(ω)=exp(AdjRω)R ω ∈ s o ( 3 ) , R ∈ S O ( 3 ) R ⋅ e x p ( ω ) = e x p ( A d j R ⋅ ω ) ⋅ R

推导:

exp(AdjRω)AdjRωAdjR=Rexp(ω)R1=R(i=13ωiGi)R1=Rω×R1=(Rω)×=R(1) (1) e x p ( A d j R ⋅ ω ) = R ⋅ e x p ( ω ) ⋅ R − 1 A d j R ⋅ ω = R ⋅ ( ∑ i = 1 3 ω i G i ) ⋅ R − 1 = R ⋅ ω × ⋅ R − 1 = ( R ω ) × 故 : A d j R = R

注:
P P 是一个 n × n 阶可逆矩阵,则

eP1AP=P1eAP e P − 1 A P = P − 1 e A P

Matrix3d SO3
::Adj() const
{
  return quaternion_.toRotationMatrix();
}

生成元 Generator

李代数 so(3) s o ( 3 ) 的生成元,即 so(3) s o ( 3 ) 互不相干的独立变量。各个生成元之间相互正交。

ω=[ω1,ω2,ω3]Tω×=ω1G1+ω2G2+ω3G3R3so(3)(2) (2) ω = [ ω 1 , ω 2 , ω 3 ] T ∈ R 3 ω × = ω 1 G 1 + ω 2 G 2 + ω 3 G 3 ∈ s o ( 3 )

其中,生成元:

G1=000001010,G2=001000100G3=010100000 G 1 = ( 0 0 0 0 0 − 1 0 1 0 ) , G 2 = ( 0 0 1 0 0 0 − 1 0 0 ) G 3 = ( 0 − 1 0 1 0 0 0 0 0 )

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,vsinθ2] q = [ c o s θ 2 , v ⋅ s i n θ 2 ] 。其中, ||v||=1 | | v | | = 1
对数运算:

[ωt]×=ln(R)=θ2sinθ(RRT) [ ω t ] × = l n ( R ) = θ 2 s i n θ ⋅ ( R − R T )

角速度矢量:
θv=θsinθ2vsinθ2 θ v = θ s i n θ 2 ⋅ v ⋅ s i n θ 2

// 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());
}

在后期版本中,对这部分代码优化:

https://arxiv.org/pdf/1107.1119.pdf

log¯¯¯¯¯¯[wv]=0    atan(||v||/w)||v||v    ±π/2||v||v    v=0v0,w0w=0 l o g ¯ [ w v ] = { 0         v = 0 a t a n ( | | v | | / w ) | | v | | v         v ≠ 0 , w ≠ 0 ± π / 2 | | v | | v         w = 0

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,vsinθ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 运算( ⋅ ∨

vee 运算由反对称矩阵得到三维角速度矢量:

:ω×Rn×nRn ⋅ ∨ : ω × ∈ R n × n → R n

ω×=0ω3ω2ω30ω1ω2ω10 ω × = ( 0 − ω 3 ω 2 ω 3 0 − ω 1 − ω 2 ω 1 0 )

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);
}

deltaR

此处得到 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;
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/luoshi006/article/details/78290177

智能推荐

RGB/HSV/YUV/Lab颜色空间模型总结_yuv lab_rzwangyu的博客-程序员秘密

转自: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

Android之浏览器作为客户端访问手机socket作为服务端下载图片和网页和APK_码莎拉蒂 .的博客-程序员秘密

1 需求在同一局域网内,手机(PC端)浏览器作为客户端,然后手机app里面通过socket写服务代码,然后浏览器访问手机服务端访问网页和图片和css文件和下载APK2 代码实现创建服务线程代码 var serverSocket: ServerSocket? = null var run = true inner class HttpServerThread : Runnable { var TAG = javaClass.na...

Failed to initialize component [org.apache.catalina.webresources.JarResource 解决_dadeity的博客-程序员秘密

环境配置JDK1.8Win10问题描述今天使用Spring配置完之后, 运行测试提示 :Failed to initialize component [org.apache.catalina.webresources.JarResource解决办法找到本地仓库位置,一般在C:\Users\Admin\.m2\repository删除repository目录下面所有文件夹p...

【转载】BLEU,ROUGE,METEOR,CIDEr-浅述自然语言处理机器翻译常用评价度量_盐汽水yj的博客-程序员秘密

【转载】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开发随手记_小阿阳啊的博客-程序员秘密

SAP Business One 窗体应用程序ADDON开发

GoLang之协程_weixin_33912445的博客-程序员秘密

目前,WebServer几种主流的并发模型:多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求;但在高并发环境下,多线程的开销比较大;基于回调的异步IO,如Nginx服务器使用的epoll模型,这种模式通过事件驱动的方式使用异步IO,使服务器持续运转,但人的思维模式是串行的,大量回调函数会把流程分割,对于问题本身的反应不够自然;协程,不需要抢占式调度,可以有效...

setTimeout与多线程_cherry4115的博客-程序员秘密

console.log(xxx);  在控制台Console输xxx,不会打断页面的正常操作setTimeout(function(){func1();},0)func2();//在上面这段代码中func2先执行,func1后执行,0s延迟表示回调函数将插队到一个能立即执行的时段//如果不写0,浏览器自动配置时间,多为10-100ms,所以还是func2先执行,

VS2017学习C++问题二(没有与这些操作数匹配的 “<<“ 运算符)_c++没有与这些操作数匹配的运算符_菩提树下祈愿的少年的博客-程序员秘密

// 添加要在此处预编译的标头#ifndef GZDEMO_H#define GZDEMO_H#pragma once#include &lt;iostream&gt;using namespace std; //函数定义void input(int[], int[]);void print(int[], int[]);//函数实现void input(int values[], int len){ if (len &gt; 5) { cout &lt;&lt; " 数组

第八届蓝桥杯省赛B组之等差素数列_蓝桥杯等差素数列快速解_李江林的博客-程序员秘密

标题:等差素数列2,3,5,7,11,13,....是素数序列。类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。上边的数列公差为30,长度为6。2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。这是数论领域一项惊人的成果!有这一理论为基础,请你借助手中的计算机,满怀信心地搜索:长度为10的等差素数列,其公差最小值是多少?注意:需要提交的是...

史上超级详细:java程序设计第三版课后答案_普通网友的博客-程序员秘密

1 基础为什么 Java 中只有值传递?int 范围?float 范围?hashCode 与 equals,什么关系?String StringBuffer 和 StringBuilder 的区别是什么?String 为什么是不可变的?Java 序列化中如果有些字段不想进行序列化 怎么办?构造器 Constructor 是否可被 override?java 异常体系?RuntimeException Exception Error 的区别,举常见的例子字符型常

推荐文章

热门文章

相关标签