quaternion

quaternion

什么是四元数

In mathematics, the quaternions are a number system that extends
the complex numbers.
They were first described by Irish mathematician William Rowan Hamilton in
184312 and applied to mechanics in three-dimensional space.
A feature of quaternions is that multiplication of two quaternions is
noncommutative.
Hamilton defined a quaternion as the quotient of two directed lines
in a three-dimensional space3
or equivalently as the quotient of two vectors.4

Quaternions are generally represented in the form:

where a, b, c, and d are real numbers,
and i, j, and k are the fundamental quaternion units.

相比欧拉角(euler angles) 四元数 (quaternion) 则是一种紧凑、易于迭代、
又不会出现奇异值的表示方法.
它在程序中广为使用, 例如 ros 和几个著名的 slam 公开数据集、
g2o 等程序都使用四元数记录机器人的姿态.
因此, 理解四元数的含义与用法, 对学习 slam 来说是必须的.

四元数仅是 3d 姿态的一种表达方式,
我们用一个单位四元数表达原本用旋转矩阵表示的三维旋转.
这样做一个直接的好处是省空间.
一个旋转阵有 9 个分量, 但只有三个自由度.
那么, 能不能用三个数来描述呢? 可以是可以的,
但不可避免会出现奇异的情况, 欧拉角就是一个例子.
而四元数, 比三维向量多了一个分量, 从而可以无奇异地表示各种姿态.

四元数是 hamilton 找到的一种扩展的复数.
一个四元数拥有一个实部和三个虚部
(故事上说他原先找了很久带两个虚部的, 结果怎么也找不到,
最后豁然开朗找到了三虚部的四元数):


其中 i, j, k 为四元数的三个虚部. 这三个虚部满足关系式:


where i, j, and k are basis elements of H,
determine all the possible products of i, j, and k.


All the other possible products can be determined by similar methods,
resulting in


由于它的这种特殊表示形式, 有时人们也用一个标量和一个向量来表达四元数:

这里, 标量 r 称为四元数的实部, 而向量 v 称为它的虚部.
如果一个四元数虚部为 0, 称之为实四元数.
反之, 若它的实部为0, 称之为虚四元数. 该定义和复数是相似的.

四元数可以表示三维空间中任意一个旋转. 与旋转矩阵中类似,
我们仍假设某个旋转是绕单位向量:


进行了角度为 θ 的旋转, 那么这个旋转的四元数形式为:

\(
\begin{equation} \label{eq:ntheta2quaternion}
\mathbf{q} = \left[
\cos \frac{\theta}{2},
n_x \sin \frac{\theta}{2},
n_y \sin \frac{\theta}{2},
n_z \sin \frac{\theta}{2}\right]^T
\end{equation}
\)

事实上 这还是一个模长为 1 的四元数 称为单位四元数.
反之 我们亦可通过任意一个长度为 1 的四元数 计算对应旋转轴与夹角:

\(
\begin{equation} \begin{cases}
\theta = 2\arccos {q_0}\\
{\left[{{n_x},{n_y},{n_z}} \right]^T} =
{{{\left[ {{q_1},{q_2},{q_3}} \right]}^T}}
/ {\sin \frac{\theta }{2}} \end{cases} \end{equation}
\)

对式 θ 加上 2π 我们得到一个相同的旋转 但此时对应的四元数变成了 -1.
因此 在四元数中 任意的旋转都可以由两个互为相反数的四元数表示.
同理 取 θ 为 0 则得到一个没有任何旋转的四元数:

\(
\begin{equation} \mathbf{q}_0 = \left[ { \pm 1,0,0,0} \right]^T \end{equation}
\)

see also and reference

cmake

cmake

see also https://yuiwong.org/gitlab/documentation/compiler

cmake-compileflags

  • CFLAGS
  • CXXFLAGS

#


howto

  • via add_definitions

add_definitions

Adds -D define flags to the compilation of source files.

add_definitions(-DFOO -DBAR ...)

Adds definitions to the compiler command line for targets in the current
directory and below
(whether added before or after this command is invoked).
This command can be used to add any flags,
but it is intended to add preprocessor definitions
(see the add_compile_options() command to add other flags).
Flags beginning in -D or /D that look like preprocessor definitions are
automatically added to the COMPILE_DEFINITIONS directory property
for the current directory.
Definitions with non-trivial values may be left in the set of flags
instead of being converted for reasons of backwards compatibility.
See documentation of the directory, target,
source file COMPILE_DEFINITIONS properties for details
on adding preprocessor definitions to specific scopes and configurations.

See the cmake-buildsystem(7) manual for more on defining
buildsystem properties

  • via set, see below example

  • via SET_TARGET_PROPERTIES

example

SET_TARGET_PROPERTIES(demo PROPERTIES COMPILE_FLAGS "\
  -std=c++11 \
  -Wall -Wextra")

cpp std

example

add_definitions(-std=c++11)

example 2

# check C++11 or C++0x support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  add_definitions(-DCOMPILEDWITHC11)
  message(STATUS "using flag -std=c++11.")
elseif(COMPILER_SUPPORTS_CXX0X)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
  add_definitions(-DCOMPILEDWITHC0X)
  message(STATUS "using flag -std=c++0x.")
else()
  message(FATAL_ERROR
    "the compiler ${CMAKE_CXX_COMPILER} has no C++11 support. "
   "please use a different C++ compiler.")
endif()

warning

example

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wundef")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunreachable-code")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Winline")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")

reference

external

usually slow ..

how to define

example

cmake .. -DCMAKE_INSTALL_PREFIX=/opt/aaa

prefix

CMAKE_INSTALL_PREFIX

install

  • TARGETS

example

install(TARGETS ${PROJECT_NAME}
  LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)

example 2

install(TARGETS
  linetest
  RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

master-sub-cmakes

  • master CMakeLists.txt “make into” sub-directories via add_subdirectory:
add_subdirectory(SOME-PATH)
  • use add_custom_target, example
add_custom_target(config_Pangolin
  mkdir -p ${CMAKE_BINARY_DIR}/libs/Pangolin
  && cd ${CMAKE_BINARY_DIR}/libs/Pangolin
  && ${CMAKE_COMMAND} ${PROJECT_SOURCE_DIR}/libs/Pangolin
  -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/install/Pangolin
  -DCMAKE_CXX_FLAGS="-I${librealsense_INCLUDE_DIRS}")

how to cpp (coding)

how to cpp (coding)

my cpp standard (style / specification / .. how to coding!)

#


list

simple thing should be simple

  • no redundant prefix, e.x. m / _ / i / ..
  • no redundant suffix, e.x. _ / ..
  • example not:
bool bShouldReboot;
int iNumber;
AAAA maaaa;
BBBB bbbb_;


/* then access example */
if (bShouldReboot) { /* where the bShouldReboot from */
}
bbbb_.foo(); /* where the bbbb_ from */

but should be:

bool shouldReboot;
int number;
AAAA aaaa;
BBBB bbbb;

/* then access example */
if (self.shouldReboot) { /* the shouldReboot is myself */
}

if (this->shouldReboot) {
}

this->bbbb.foo(); /* the bbbb from this object */

about ostream

  • better not use endl .. std::endl, but \n is ok !

indent

  • usually 1 is enough!!

example not

void somemaybeokfooMaybebadaaa(const AAAA& aaaa, const BBBB& bbbb,
                               const CCCC& cccc, const DDDD& dddd,
                               const EEEE& eeee);

but should be

void somemaybeokfooMaybebadaaa(
    const AAAA& aaaa,
    const BBBB& bbbb,
    const CCCC& cccc,
    const DDDD& dddd,
    const EEEE& eeee);

80

  • yes !! usually max width of each line is 80
  • 可以换行啊 !!!

no garbage

  • no garbage lines at middle of file
  • no garbage lines at end of file
  • no garbage space (space / table / ..) at end of line
  • no garbage space (space / table / ..) at begin of line(
    example, need 2 but 3 provided)

1 space

  • example not:
if(some){
}

sum=a+b;

but shoule be

if (some) {
}

sum = a + b;

block statement

  • always {}, even if 1 statement, example:
/* NOT */
some;
if (ok)
    fly();
else
    walk();
other;

/* BUT SHOULE BE: */
some;
if (ok) {
    fly();
} else {
    walk();
}
other;

/* and further more is good: */
some;
if (ok) {
    fly();
    saysomething();
} else {
    walk();
}
stopFirst();
other;

path

  • example for include: NOT ../../.hpp or ../.hpp
    or too/many/name/...hpp

but should be: some/…hpp and use -I...

lower and upper

  • type: Type
  • instance / member: instance / data / foo / staticFoo
  • generic function: Foo

prefix

  • better no! if could be simple !!
  • global: use k e.x. kGraphProcessor.
    (NOT use e.x. g)

plusplus

  • if not have to be some++(case for some++ when which can simplify 2 to 1)

SO USUALLY use ++some (not use some++ if not have to), because:
i see plus one, the result of the expression should be plused one !!
but this expression maybe little silly!

  • example, better:
for (size_t i = 0; i < sz; ++i) {
}

/* got whatsthis */
++whatsthis;/* should plus one or whatsthis += 1; */
someFoo(whatsthis);

when to declare

when to declare: when use, example

/* BAD: heyheyhey example */
Some heyheyhey;/* BAD */
doSome();
doOther();
thisFooNeedRefAndAlwaysSetIt(heyheyhey);

/* BAD: heyheyhey example 2 */
Some heyheyhey = 0;/* BAD */
...
thisFooNeedRefAndMaybeNotSetIt(heyheyhey);

/* BAD: heyheyhey example 3 */
Some heyheyhey;/* BAD */
heyheyhey = 0;/* BAD */
...
thisFooNeedRefAndMaybeNotSetIt(heyheyhey);

/* BAD: heyheyhey example 4 */
Some heyheyhey;/* BAD */
doSome();
...
heyheyhey = 0;/* BAD */
thisFooNeedRefAndMaybeNotSetIt(heyheyhey);

/* GOOD: heyheyhey example */
doSome();
doOther();
Some heyheyhey;/* GOOD ! */
thisFooNeedRefAndAlwaysSetIt(heyheyhey);

/* GOOD: heyheyhey example 2 */
doSome();
...
Some heyheyhey = 0;/* GOOD ! */
thisFooNeedRefAndMaybeNotSetIt(heyheyhey);

how to assign init

how to assign: do not assign repeatedly or maybe repeatedly, example

/* bad: */
size_t count = 10;
if (ok) {
    count  = 100;
}

/* good: */
size_t count;
if (ok) {
    count  = 100;
} else {
    count  = 10;
}

should dump when need

  • for data thread safe
  • for less call: if use not only once => dump it!, example
/*
 * consider now datas is latched .. here means
 * bad case:
 */
some();
for (size_t i = 0; i < datas.size(); ++i) {
    foo(datas[i]);
}
other();

/*
 * bad case 2:
 */
sayToSomebody(getNameById(1), "hi");
sayToSomebody(getNameById(1), "hey");

/*
 * consider now datas is latched .. here means
 * good case:
 */
some();
{
    const size_t sz = datas.size();/* dump the size */
    for (size_t i = 0; i < sz; ++i) {
        foo(datas[i]);
    }
}
other();

/*
 * good case 2:
 */
{
    const std::string who = getNameById(1);/* dump the name */
    sayToSomebody(who, "hi");
    sayToSomebody(who, "hey");
}

how to debug by output log

  • __FILE__
  • __LINE__
/* SO BAD example */
some();
std::cout << "aaaaa" << std::endl;
watch();
std::cout << "bbbbb222222!!!" << std::endl;/* what u b !! */

/* GOOD example */
some();
std::cout << "(" << __FILE__ << "+" << __LINE__ << ")" NAME " "
    << "before watch\n";/* good */
watch();
std::cout << "(" << __FILE__ << "+" << __LINE__ << ")" NAME " "
    << "after watch\n";/* good */

/* further more */
const int err = fly();
if (err) {
    std::cerr << "(" << __FILE__ << "+" << __LINE__ << ")" NAME " "
        << "ERROR: fly fail: " << err << "\n";/* so good */
    die();
}

no flag

  • just better NOT use the name flag, example:
/* bad case */
void setStartable(bool flag)
{
    this->startable = flag;
}

/* bad case 2 */
void setResult(bool flag)
{
    this->startable = flag;
}

/* good case: just to what it should be */
void setStartable(bool startable)
{
    this->startable = startable;
}

/* good case 2 */
void setResult(bool result)
{
    this->result = result;
}

further