第3講 變換矩陣與齊次座標

從上一講的內容中可以知道一次完整的歐式變換(旋轉+平移)可以用式子:\vec{a^{'}}=R\vec{a}+\vec{t}來表示。

假設我們現在對\vec{a}做了兩次歐式變換:R_{1},\vec{t_{1}}R_{2},\vec{t_{2}},得到的向量分別爲\vec{b}\vec{c},用上式來表示就是:

\vec{b}=R_{1}\vec{a}+\vec{t_{1}}\vec{c}=R_{2}\vec{b}+\vec{t_{2}}

於是從\vec{a}\vec{c}的變換就可以寫成\vec{c}=R_{2}(R_{1}\vec{a}+\vec{t_{1}})+\vec{t_{2}}

這不是一個線性關係,可以想象到,如果經過多次的歐式變換,那麼這個式子就會變的異常的複雜。

引入變換矩陣和齊次座標就是爲了解決這個問題。

變換矩陣和齊次座標

我們可以將上面從\vec{a}\vec{c}變換的式子改寫成用矩陣表示的形式:

在上面的式子中,我們在一個三維向量的末尾添加了數字1,使得左邊變成了四維向量,稱爲齊次座標。

將上面的矩陣打開就是下面的式子:\begin{bmatrix} \vec{a^{'}} \\ 1 \end{bmatrix} = \begin{bmatrix} R\vec{a}+\vec{t} \\ 1 \end{bmatrix}

對於這個四維向量,我們可以把旋轉和平移寫在一個矩陣裏面,通過引入矩陣T使得整個關係變成線性關係。矩陣T稱爲變換矩陣。

關於齊次座標,還有下面這些需要知道。

      通過添加最後一維,我們用四個實數來描述了一個三維向量,這顯然多了一個自由度,但是這樣允許我們把變換寫成線性的形式。在齊次座標中,某個點{\color{Red} x}的每個分量同時乘以一個非零常數{\color{Red} k}後,仍然表示同一個點。因此一個點的具體座標值不是唯一的。[1,1,1,1]^{T}[2,2,2,2]^{T}表示的是同一個點。

      但是,當最後一項不爲0時,我們總是可以把所有座標分量除以最後一項,強制最後一項爲1,從而得到一個點唯一的座標表示。(也就是將非齊次座標轉換爲齊次座標)。如:a^{'}=[x,y,z,w]^{T}=[x/w,y/w,z/w,1]^{T}

通過齊次座標和變換矩陣,兩次變換(多次變換也是如此)的累加就可以有很好的形式:

\vec{b}=T_{1}\vec{a}\vec{c}=T_{2}\vec{b}  =====>    \vec{c}=T_{2}T_{1}\vec{a}

實踐部分:Eigen庫使用

      Eigen是一個c++開源線性代數庫。它提供了快速的有關矩陣的線性代數運算,包括解方程等功能。關於Eigen的更多資料可以在這裏找到。

1、Eigen庫的安裝

如果在ubuntu上還沒有安裝Eigen庫的話,可以先使用apt-get命令安裝Eigen:

sudo apt-get install libeigen3-dev

我已經安裝過了,執行之後提示如下:

Eigen頭文件的默認位置在 /usr/include/eigen3/ 中。

與其他庫相比,Eigen的特殊之處在於:它是一個純用頭文件搭建起來的庫。這意味着你只能找到它的頭文件,而沒有.so或者.a那樣的二進制文件。在使用的時候,也只需要引入Eigen的頭文件即可,不需要鏈接庫文件(因爲根本就沒有庫文件)。

2、調用Eigen庫進行矩陣運算

首先,簡單的介紹介紹一下Matrix class:Eigen庫以矩陣爲基本數據單元,它是一個模板類,定義如下:     

Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>

三個參數的含義分別爲:數據類型、行數、列數。

例如,下面語句定義了一個4*4的float類型的矩陣:

Eigen::Matrix<float, 4, 4> matrix4f;

使用Eigen庫的時候,由於Eigen庫沒有庫文件,因此不需要使用target_link_directories語句,只需要使用一個鏈接頭文件目錄的語句include_directories,如下:

include_directories("/usr/include/eigen3")

源文件內容:

#include <iostream>
#include "Eigen/Dense"

using namespace std;

int main() {
    //定義一個2*3的int矩陣和一個3*2的int矩陣
    Eigen::Matrix<int, 2, 3> matrix3i_a;
    Eigen::Matrix<int, 3, 2> matrix3i_b;

    //輸入矩陣matrix3i_a數據
    cout<<"matrix3i_a的內容爲:"<<endl;
    matrix3i_a<<1, 2, 3, 4, 5, 6;
    cout<<matrix3i_a<<endl;

    //輸入矩陣matrix3i_b數據
    for(int i=0; i<3; i++){
        for(int j=0; j<2; j++){
            matrix3i_b(i, j)=2*i;
        }
    }
    cout<<"matrix3i_b的內容爲:"<<endl;
    cout<<matrix3i_b<<endl;

    //matrix3i_a和matrix3i_b進行四則運算
    cout<<"matrix3i_a*matrix3i_b的結果爲:"<<endl;
    cout<<matrix3i_a*matrix3i_b<<endl;

    cout<<"matrix3i_a的轉置爲:"<<endl;
    cout<<matrix3i_a.transpose()<<endl;
    cout<<"matrix3i_a的各個元素的和爲:"<<endl;
    cout<<matrix3i_a.sum()<<endl;

    return 0;
}

CMakeLists.txt的內容:

cmake_minimum_required(VERSION 3.12)
project(use_eigen)

set(CMAKE_CXX_STANDARD 14)

#添加頭文件
include_directories("/usr/include/eigen3")

add_executable(use_eigen main.cpp)

上面的程序中需要注意的就是:輸入矩陣數據的時候,使用的是輸出運算符而不是輸入運算符。(這裏的運算符都是重載之後的運算符)。

這一篇暫時就先學到這裏了,明天開始學習旋轉向量、歐拉角、四元數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章