1. 程式人生 > >第三講 變換矩陣與齊次座標

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

從上一講的內容中可以知道一次完整的歐式變換(旋轉+平移)可以用式子:\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)

上面的程式中需要注意的就是:輸入矩陣資料的時候,使用的是輸出運算子而不是輸入運算子。(這裡的運算子都是過載之後的運算子)。

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