東京工業大学 ロボット技術研究会

東京工業大学の公認サークル「ロボット技術研究会」のブログです。 当サークルの日々の活動の様子を皆さんにお伝えしていきます。たくさんの人に気軽に読んでもらえると嬉しいです。
新歓特設ページ        ロボット技術研究会 HP        ロボット技術研究会 twitter公式アカウント

MATLAB

「ロボット技術研究会」通称「ロ技研」は、その名前の通りロボットの制作や研究はもとより、電子工作や機械工作、プログラミングなどの幅広い分野にわたるものつくり活動を行っています。

カテゴリ一覧: loading

MATLAB FBX LoaderとMATLAB 3D Model Rendererの開発

この記事はrogy Advent Calendar 2016 11日目
「MATLAB FBX LoaderとMATLAB 3D Model Rendererの開発」
の記事です.
担当はrej55です.
前半はMATLAB FBX Loaderについて,後半はMATLAB 3D Model Rendererについて書いています.

まえがき

近年,3DCGの技術が大きく発達してきているなと感じています.今年のAdvent Calenderでもisskくんの記事Lineaくんの記事などで綺麗なグラフィックスが載っています.彼らといろいろと話をしていて,自分も3DCGに興味を持ったのでどこかで勉強してみたいと思っていました.

私はMATLABを利用したプログラミングが大好きで,東工大では学生が無料でMATLABを使用できるという背景もあり,趣味でいろいろと開発をしていました.今回は,MATLABで3DCGの描画ができないか?ということで試してみることにしました.

MATLAB FBX Loaderとは?

FBXファイルとは,3Dデータを異なるアプリケーション間でやり取りできるように設計されているファイルのことで,Autodesk社によって開発されています.MATLAB FBX Loaderは,MATLABでこのFBXファイルを読み込むためのライブラリとなります.

MATLAB FBX LoaderではFBXファイル内に格納されている情報を出力します.具体的には,

  • 頂点の数
  • 頂点の3D座標
  • ポリゴンを構成する頂点の集合
  • 各頂点のUV座標
  • 各頂点の法線ベクトル
  • 各頂点のカラーデータ
を出力するようにしています.

FBX Loaderの開発

FBXファイルはバイナリ形式のファイルなので,基本的にフォーマットがわかっていないと読み込むことが不可能です.MATLABでもバイナリファイルの読み込みは可能なのですが,そもそもフォーマットがわかっていないので読み込みができません.

実は,Autodesk社のほうでFBXファイルの読み込みなどができるSDKを配布しています.このFBX SDKはC++とPythonのものしかなく,そのままではMATLABでは使えません.そこで今回はMEXというものを介してFBX SDKを利用することを考えました.

MEXについて

MEXとは,MATLAB上でCやC++で開発された関数を呼び出すための機能で,CおよびC++コードをMEXファイル生成用にコンパイルするとMATLABで関数を呼び出すことができるようになります.

MEXファイルの作成にあたっては,まず下記のようなmexFunctionを用意します. mexFunctionはMATLABがMEXファイルを呼び出したときに実行されるゲートウェイ関数です.基本的にこのmexFunction内で処理を記述します.

MATLAB FBX Loaderは,FBX SDKのAPIを呼び出し,FBXファイル内の必要なデータをひたすらMATLABに出力として渡すという処理を書いているものになります.FBX SDKの詳細についてはこちらこちらを参考にさせていただきました.

MATLAB 3D Model Rendererとは?

MATLAB 3D Model Rendererは,MATLAB FBX Loaderで読み込んだ3Dモデルのデータを元に3DCG画像を生成するシステムです.GPUのハードウェア機能を直接使っているわけではないので,いわゆるソフトウェアレンダラに分類されるかと思います.

MATLAB 3D Model Rendererのパイプラインは下記のとおりです.

  1. ワールド座標からビュー座標への変換
  2. ビュー座標からスクリーン座標への射影
  3. スクリーン座標データを元にラスタライズ
  4. テクスチャフェッチ
このパイプラインに沿って,どのように実装したのかを説明したいと思います.

ワールド座標からビュー座標への変換

この節では,ワールド座標上の3Dモデルをカメラから見たビュー座標系に変換する方法を説明します. ワールド座標とは,3Dモデルの3D空間上での物体の位置を表す絶対座標系です.ビュー座標系は,カメラの位置を原点として,視点方向ベクトルとカメラの上方向ベクトルとこの2つに直交するベクトルを基底とする座標系です.

まず,ビュー座標系の基底ベクトルを求めます.ビュー座標系の基底ベクトルはカメラの視線方向の単位ベクトル$c_t$,カメラの上方向単位ベクトル$c_u$,カメラの右方向の単位ベクトル$c_r$です.これらは下記のように求めます. $$ c_t = \frac{x - t}{\| x - t \|},\ c_u = \frac{c_t \times u}{\|c_t \times u\|},\ c_r = c_t \times c_r $$ ここで,$x$はカメラのワールド座標における位置,$t$は注視点,$u$はカメラの上方向を表すベクトルです. これらの基底ベクトルを利用して,下記のような同次座標変換行列を定義します. $$ T_{\mathrm{view}} = \begin{bmatrix} c_t^{\sf T} & -c_t^{\sf T} x\\ c_u^{\sf T} & -c_u^{\sf T} x\\ c_r^{\sf T} & -c_r^{\sf T} x\\ O & 1 \end{bmatrix} $$ この行列により座標変換を行うことで,ワールド座標からビュー座標への変換が完了します.具体的なソースコードは下記のようになります.

ビュー座標からスクリーン座標への射影

スクリーン座標はその名の通りスクリーン上における2次元の座標系です.ここでは,最も簡単なピンホールカメラモデルに基づいて,ビュー座標系で表される3次元空間上の頂点をスクリーン上の座標へ射影したいと思います.

カメラとスクリーンの距離を$f$とすると, $$ P_{\mathrm{screen}} = \begin{bmatrix} -f & & &0\\ & -f & &0\\ & & 1 &0 \end{bmatrix} $$ が射影変換行列となります.これを用いて,ビュー座標系における頂点の座標は下記のように射影されます. $$ \begin{bmatrix} U \\ V \\ S\end{bmatrix} = P_{\mathrm{screen}} \begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix} $$ 上記の処理は下記のように実装できます.

ラスタライズ

画像を生成するためには,座標データからピクセルのデータに変換する必要があります.この変換処理のことをラスタライズと呼びます.

ラスタライズを行うために,まず画像の各ピクセルとスクリーンの座標の対応を求めます.この処理は,meshgrid関数を利用すると簡単に求めることができます.MATLABでは画像データを行列形式で扱います.行列の各要素が各ピクセルの画素値に対応します.ここで, [X, Y] = meshgrid(linspace(-width, width, w_res), linspace(height, -height, h_res))とすると, XとYは画像の解像度と同じサイズの行列となり,Xには各ピクセルのスクリーン上における$x$座標,Yには$y$座標といったデータが入るため,このXとYを利用することで各ピクセルのスクリーン上における座標を得ることができます.なお,Y座標を反転させているのは,上記のピンホールカメラモデルでスクリーンへ投影すると上下反転した状態で投影されるからです.

ラスタライズを行うためには,各ポリゴンの内部を塗りつぶす処理が必要になります.そのためには,頂点データから多角形の内部判定を行う必要があります.多角形の内部判定を行う関数として,MATLABにはinpolygonという関数があり,これを利用すると各クエリ点が多角形の内部または境界上にあるかを判定することが可能です.

テクスチャフェッチ

ポリゴンをラスタライズする際に,どの色を割り当てるのかという問題が生じます.このとき,UV座標とテクスチャファイルが割り当てられていれば,頂点のUV座標をもとにしてポリゴンの中にテクスチャ画像に対応する色を付けることができます.この処理のことをテクスチャフェッチと呼びます.UV座標についてはこちらの解説を参考にさせていただきました.

UV座標は頂点に割り当てられているため,頂点に対応する各ピクセルのUV座標を求め,ポリゴン内部のピクセルのUV座標を求める必要があります.具体的には,頂点のスクリーン座標におけるUV座標から,各ピクセルの座標におけるUV座標を内挿する計算を行います.このような内挿は,scatteredInterpolantという関数を利用することで内挿のための関数のようなものを求めることができます.実際は,ポリゴンの外側のUV座標を計算するのは無駄なので,ポリゴンの内部のピクセルだけ計算することで計算の高速化を行います.また,隠れた面を描画しないようにするために,各頂点がスクリーンからどの程度離れているかを表すデプスの計算も行います.この計算も,各頂点のスクリーン座標に対するスクリーンからの距離をもとに各ピクセルにおけるデプス値を内挿するように行います.

上記のラスタライズおよびテクスチャフェッチを実装すると下記のようになります.

実際に試してみよう

今回は例としてユニティちゃんを表示してみたいと思います.

ユニティちゃんライセンス
ここでのユニティちゃんの画像につきましては,ユニティちゃんライセンス条項の元に提供されております.

実行した結果がこちらです. CzOAUiyUQAAXgvL
ジャギーを少し軽くするため,上記の処理に加えて今回は出来た画像に対しガウシアンフィルタで軽く平滑化をかけています.

続いて,背景画像と一緒に全身を表示してみます. CzOGIUnUAAIAlBM
背景画像を初期値に指定すれば,3Dモデルを上から塗り重ねるようにしてレンダリングすることができます.

まとめ

長くなってしまいましたが,MATLABでFBXを読み込む方法とMATLABで3Dモデルをレンダリングする方法について書きました.いかがでしたか?

レンダリング自体はかなり計算時間がかかるため,正直なところリアルタイムには向いてないと思います.ですので,現状のままではMATLABで3Dゲームを作る,みたいなものは非常に厳しいと思います.なお,レンダリング処理はGPUを利用することで高速化できますが,これでもリアルタイムには厳しいといったところです.

基本的な描画が可能になったので,今後は下記のような課題が考えられます.

  1. 光源の影響を計算するシェーディング処理
  2. カメラのボケなどを再現するエフェクト処理
上記の課題を達成したらMATLABでの3DモデルレンダリングフレームワークとしてGithubなどで公開したいと考えております.公開できる状況になったときにはまたこちらなどで報告させていただきます. ということで,今後はこれらをやっていきたいと思いますが,まずは修論をがんばって倒したいと思います.

私の記事は以上となります.明日はPLM_precure氏の「ジープがお好き?結構。ではますます好きになりますよ。」の記事となります!

MATLABゲーム制作の基礎の基礎

みなさんアローラ!13のマハトです.
これは rogy Advent Calendar 2016 の7日目の記事です.

普段はMATLABで作った変なものを紹介する記事を書いている僕ですが,今回はせっかくのAdvent Calendar企画だし少し技術的な記事にしようということで,「どうやったらゲームっぽい動きをMATLABで作れるか」について軽く書きたいと思います.

※この記事はわかりやすさを重視するために厳密には正確でない表記を用いている部分があります.またどうプログラムするかは個人の好みの部分ですので,プログラム例はあくまで参考としてお使いください.

・グラフィックスオブジェクト

 誰もが使ったことのある「plot」をはじめとした,figure上に描画できる図形のことをグラフィックスオブジェクトと言います.グラフィックスオブジェクトを作成する関数は色々ありますが,その全てが返り値としてそのオブジェクト自身を示す変数(グラフィックオブジェクト変数)を返します.
p = plot(t,x);		%pはplot(t,x)自体を指す.
よく使うグラフィックスオブジェクトを下に挙げておきます.それぞれの関数の使い方は各自調べてみてください.
f = figure(); %figureウィンドウ
ax = axes(); %座標軸
pat = patch(x,y,color); %多角形
rec = rectangle('Pos',pos); %曲率を設定可能な長方形
text = text(x,y,text); %文字
im = image(x,y,C); %画像
(ちなみにパズドラのゲーム画面はここに挙げたオブジェクトのみで作ってます)


・プロパティ
 MATLABのグラフィックスオブジェクトには「プロパティ」という設定値がありまして,このプロパティを設定することで図形を編集できます.例えばグラフの線の太さや色はそれぞれ「LineWidth」,「Color」プロパティに対応します.プロパティの値を設定するにはグラフィックスオブジェクト変数を使って
set(オブジェクト変数,'プロパティ名','プロパティ値');
または
オブジェクト変数.プロパティ名 = プロパティ値;
とします.逆に,プロパティの値を知りたいときには
value = get(オブジェクト変数,'プロパティ名');
または
value = オブジェクト変数.プロパティ名;
とします.例えば上のpの色を赤に変えたければ,
set(p,'Color','r');    or    p.Color = 'r';
です.逆にpの色を知りたければ,
p_color = get(p,'Color');    or    p_color = p.Color
です.各オブジェクトが持つプロパティは全てmathworks社のページに載っています.「(オブジェクト名) プロパティ」で調べるとすぐに出てきますので,気になるオブジェクトについていくつか調べてみるといいかもしれません.


・ゲームっぽいプログラムの例
 ここからゲーム制作に話が移ります(いきなり難易度が上がります).ここではこれまでの知識を使って「ドラッグすると図形が動く」というパズドラの一番基礎となるプログラムを例として,それについて解説を入れていきます.
patchとfigureとaxesのプロパティのページを見ながら読み進めるとわかりやすいかと思います.

function test
f = figure();
ax = axes();
axis([0 5 0 5]); %座標の範囲をx:0~5,y:0~5に
pat = patch([0,1,1,0],[0,0,1,1],'r'); %(0,0),(1,0),(1,1),(0,1)を頂点とする正方形
pat.ButtonDownFcn = @bdf; %①

    function bdf(object,eventdata) %patがクリックされた時に実行される関数,objectにはpatが入る
        cp = ax.CurrentPoint %②
        x = cp(1,1);
        y = cp(1,2);
        pat.XData = [x-0.5 x+0.5 x+0.5 x-0.5]; %patの中心のx座標をマウス位置に変更
        pat.YData = [y-0.5 y-0.5 y+0.5 y+0.5]; %patの中心のy座標をマウス位置に変更
        f.WindowButtonMotionFcn = @wbmf; %③
        f.WindowButtonUpFcn = @wbuf; %③
    end

    function wbmf(fig,eventdata) %figure上でマウスが動いた時に実行される関数,figにはfが入る
        cp = ax.CurrentPoint; %④
        x = cp(1,1);
        y = cp(1,2);
        pat.XData = [x-0.5 x+0.5 x+0.5 x-0.5];
        pat.YData = [y-0.5 y-0.5 y+0.5 y+0.5];
    end

    function wbuf(fig,eventdata) %マウスが離れたときに実行される関数,figにはfが入る
        fig.WindowButtonMotionFcn = ''; %⑤
        fig.WindowButtonUpFcn = ''; %⑤
    end
end
―メイン(test)関数―
①patch:ButtonDownFcn
 patchには「ButtonDownFcn」というプロパティがあり,このプロパティの値を「@関数名」と設定しておくことで,patchがマウスでクリックされたときに設定した関数が実行されます.このプログラムでは「bdf」という関数を設定しています. 

―bdf関数―
②axes:CurrentPoint
 axesには「CurrentPoint」というプロパティがあり,マウスが最後にクリックされた座標が行列形式で保存されており,(1,1)にはx座標,(1,2)にはy座標が保存されています.これを使うことでpatをクリックされた位置に移動させることができます.

③figure:WindowButtonMotionFcn,WindowButtonUpFcn
 figureには「WindowButtonMotion(Up)Fcn」というプロパティがあり,①同様関数名を設定しておくことで,figure上でマウスが動いた(離れた)ときに設定した関数が実行されます.このプログラムでは「wbmf(wbuf)」という関数を設定しています. ちなみにメイン関数ではなくbdf関数内で設定している理由は,「ドラッグ」を検知したいからです.マウスが押されてから離れるまでの間にのみ処理が実行されて欲しいわけです.

―wbmf関数― 
④axes:CurrentPoint
 先ほど解説した「CurrentPoint」ですが,figureの「WindowButtonMotionFcn」に何か関数が設定されている場合は「最後にクリックされた場所」ではなく「直前のマウスの座標」が保存されています.そのため,マウスが少しでも動けば「CurrentPoint」の値が更新されていきます.

―wbuf関数― 
⑤ figure:WindowButtonMotionFcn,WindowButtonUpFcn
 先ほど設定した2つの関数の設定をリセットします.これでマウスが離れた後はマウスが動いても何も処理が実行されません.


こんなかんじで,意外と簡単に実装できてしまいます.ちなみにこのpatchを複数のrectangleにして動きに応じて位置が入れ替わるようにしたのがMAT&ドLABンズです.


・まとめ
今回読んでもらった知識があれば結構いろんなものが作れます.色々わかりにくいところもあったと思いますが,講義や課題でシミュレーションしてグラフ作ることにしかMATLABを使っていない人もこれを機に遊んでみてもらえたらな,と思います. 

・今後作りたいもの
 
 修士1年になって忙しくなってきたのでこれまでみたいに色々作れるわけではないですが,とりあえず2つほど作ってみたいなというものがあります.
1.パズドラメーカー
 今のパズドラのコードを整理するとともに,チームやステージなどを自作できるツールを開発できたら面白そうだと思っています. 
2.ポ〇モン
  1つの時間軸があってそれに伴ってNPC達がランダムに色々動く,という処理を前から作ってみたかったので,どうせならポ〇モンでも作ろうかな,というようなかんじです.当初simulinkで作りたかったんですが,自分のPCだとsimulinkが重すぎてとても開発できないので,やるならfigureベースになるかなといった感じ.


以上です. 最後まで読んでいただきありがとうございました!

MATLABでグラフエディタ作ってみた

みなさんお久しぶりです,マハトです.

前回のMAT&ドLABンズの記事から早3か月,最近卒論が忙しいせいでなかなかMATLABで遊ぶことができなかったのですが,先日自分の卒論を書く上で使うツールをMATLABで開発し,それが意外といい出来になったので紹介したいと思います.

作ったものはタイトルの通り,グラフエディタです.グラフとはいっても「いわゆるx軸とy軸があって~」というグラフではなく(そもそもそれならplot関数でいいですよね),下の図のように通信構造などを表すためのグラフです.
 example
各頂点がエージェント,矢印が通信を表しています.例えばエージェント4はエージェント1,3から情報を得ることができます.
また,このグラフがどのような構造をしているかを数学的に表現するものとして「グラフラプラシアン」という行列があります.グラフラプラシアンの各要素をL_ijとすると,その定義は次のようになります.

・i =jのとき
L_ij=自身に向かう矢印の数
・jからiへの矢印が存在するとき
L_ij=-1
・それ以外
L_ij=0
  例えば上のグラフではグラフラプラシアンは次のようになります.
1 0 0 0 0 -1
-1 1 0 0 0 0
0 -1 1 0 0 0
-1 0 -1 2 0 0
0 0 0 -1 0
0 0 -1 0 -1 2

 1には6のみから矢印が来ているので(1,1)は1,(1,6)は-1というようなかんじです(見比べてみてください).

このグラフとグラフラプラシアンは基本的にセットで使うので,グラフを書きつつそのグラフラプラシアンを計算するツールが欲しかったんです.そこで,MATLABのGUI機能を使ってそのようなツールを作ってみました. 

start
起動するとこんなかんじで2つのウィンドウが開きます.左でグラフを書き,右で頂点の数を変えたりグラフラプラシアンを計算したりします.

1click edge
矢印を書きたければ,始めの頂点をクリック(頂点が赤くなる)し,終わりの頂点をクリックすることで簡単に書けます.ちなみに消したいときは矢印をクリックしてからキーボードの「d」を押すと消せます.number
右のテキストボックスで頂点の数を変えられます.このときすでに書かれている矢印は保存されるので,いちいち書き直す必要はありません.

gl2
右の「Calculation」ボタンを押すとコマンドウィンドウにグラフラプラシアンが表示されます.

 グラフを保存するときは,plotを保存する時のように「ファイル」→「名前を付けて保存」とすることで好きな形式で保存できます..figファイルで保存すれば,それをMATLABで開くことで再編集できます.


機能は大体こんなかんじです.自分の卒論用だったので,頂点の位置は円周上に均等に並べることしかできませんし,中の文字も変えられません.卒論が終われば暇になると思うので9月にはそこらへんも修正して公開したいなと思っています.
特にこれから卒業研究をする人の中にはマルチエージェントシステムを扱う人もいると思うので,そういった人が便利に使えるようなツールに仕上げていきたいと思います.


ということで,今回はここまでとなります.fmシンセサイザ,パズドラ,ペイントときて,ついに「MATLABで○○作ってみた」企画では初となる実用的なツールが完成しそうで,ちょっと感動です.  

MATLABでパズドラ作ってみた!(MAT&ドLABンズ)

みなさんお久しぶりです,マハトです.
今回は4月7日,11日の新歓展示で展示する予定のゲームを紹介したいと思います.

まず初めに,みなさんは「MATLAB」というソフトをご存じでしょうか? MATLABはMathWorks社が開発している「数値解析ソフトウェア」です.非常に高性能かつ簡潔に数値計算を行えるソフトで,大学の授業等にも使われています.東工大では学生は無料で使うことができるので,使ったことがある東工大生は多いんじゃないかと思います.
今回はその数値解析ソフトウェアであるMATLABを使って,あの有名スマホゲームである「パズル&ドラゴンズ」を作ってみました!


↓実際に僕がプレイしている動画です(多少バグが残っています)



個人的にはまあまあ再現できているんじゃないかと思います.動画の説明蘭にある程度解説を書いたのでよかったら見てみてください.ちなみに一番悔いが残るのは,防御等を考えていない単純な攻撃力しか表示できず,敵に実際に与えているダメージを表示する場所が無かったことです.この点はプレイする上で非常に不便(固い相手かどうか分からない等)なので,近々調整したいと思っています.



このパズドラですが,冒頭でも書いた通り,4月7日,11日の新歓展示で展示します! ロボット技術研究会の新歓展示はS223S22417:00~19:00にやっていますので,興味のある方はぜひ来てください!



~ここからMATLABを弄ったことがある人向けにちょっと詳しい解説~
使っている主な機能は,「figure」の「マウス制御」です.MATLABを弄ったことがある人なら必ず「plot」でグラフを書いたことがあると思いますが,そのときに出てくるウィンドウがfigureです.実はあのfigureには「マウスがクリックされたとき」や「マウスが動いたとき」に実行される関数を設定することができます.今回作ったパズドラもマウスがクリックされたときにドロップを持って,動いたときにドロップを動かして……というような処理を行っています.
こういったMATLABのGUI機能は非常に使ってて面白いのですが,やはり授業や研究ではほとんど使わないからか,知名度が低いです.mathworks社の「figureのプロパティ」のページに色々書いてありますので,興味を持った方は一度読んでみると幸せになれるかもしれません.

MATLABでGPUコンピューティング

はじめに

この記事はrogy Advent Calendar 2015 11日目
「MATLABでGPUコンピューティング」
の記事です。
担当は@cyan_rej55です。

どんな内容?

ほぼタイトルの通りなのですが、MATLABでGPUコンピューティングを行う入門記事的なものを書いてみようと思います。
GPU計算を利用するためにはParallel Computing Toolboxが必要となります。
また、Compute Capability 2.0以上を満たすCUDAに対応したnvidia社のGPUが必要です。
こちらで対応しているかどうかを確認できます。
なお、本記事では実行環境としてMATLAB R2015bを利用します。

GPUコンピューティングとは?

今回のテーマであるGPUコンピューティングとは、GPU(Graphics Processing Units)を利用した計算のことを指します。 GPUはその名の通りグラフィックス周りの演算に特化したプロセッサユニットです。グラフィックスの演算では単純な演算を大量に行うことが多く、GPUではそれに特化した形でハードウェアが構成されています。

 GPUはグラフィックスの処理の用途で用いられていたのですが、大量の演算を一度に行う並列演算の能力を他の分野に応用するGPGPU(General-Purpose Computing on GPU)が近年流行っています。 応用分野としては、画像処理などのグラフィックス処理はもちろんとして、物理シミュレーションや機械学習など多岐に渡ります。

 これらを扱うにはGPUでの処理を記述するプログラムを作る必要がありますが、多くの場合はCUDAを利用する必要があります。しかし、CUDAによる実装ではGPU上のメモリ管理などをすべて自分で行う必要があるなど、非常に手間がかかります。そこで今回はMATLABを利用して手軽にGPUコンピューティングに触れてみよう!というのが今回の趣旨となります。

準備:自分のGPUデバイスを調べよう

MATLAB上で自分の使用しているGPUデバイスの情報を調べるには、gpuDeviceを利用します。MATLABのコマンドウィンドウ上で

>> gpuDevice

と入力すると自分の環境を調べることができます。複数GPU環境を利用されている場合は、gpuDevice(index)という形でインデックスを引数で与えることで利用するGPUを選択して調べられます。 今回は話が複雑になるのでシングルGPU環境という前提で話を進めます。

基本編:gpuArrayを使おう

GPU上で計算を行う場合には、GPUのメモリ上にデータを転送する必要があります。そのための関数として gpuArrayがあります。また、逆にGPU上のメモリを拾ってくるときにはgatherという関数を利用します。

GPU上での計算の基本的な流れは、

  1. gpuArrayでGPUにデータを転送
  2. 演算を実行
  3. gatherでGPU上のデータをワークスペースに転送
となります。

それでは、実際に動かしてみましょう。今回は固有値計算を例にしてみたいと思います。

こちらのスクリプトを実行してみます。私の環境では以下のようになりました。

>> gputest
経過時間は 14.211752 秒です。
経過時間は 6.273133 秒です。

このように、高速化されていることがわかるかと思います。先ほどのコードのNの値を増やせば増やすほどその効果を実感できると思います。ただし、Nが少ない場合には逆にGPUのほうが遅くなることも多々あります。この点には注意してください。

このように、gpuArrayで確保したデータに対して通常の関数を適用するだけでGPUの恩恵を受けることができます。gpuArrayのデータに対応した関数一覧はこちらに掲載されています。

応用編:arrayfunを使って並列計算

ここまで、gpuArrayとビルトイン関数を利用して簡単にGPUを利用する方法を説明しました。 しかし、ビルトイン関数をそのまま使えないなど、自分で関数を作りたいという要求もあるかと思います。 そこで、arrayfunという関数を利用して並列計算を行ってみたいと思います。

[B1, B2, ..., Bm] = arrayfun(fun, A1, A2, ..., An)は、n個の引数からm個の値を返す関数funを、A1, A2, ... , Anの各要素に適用してB1, B2, ..., Bmに返す関数です。 反復法的に書くと、

for i = 1:k
[B1(i), B2(i), ..., Bm(i)] = fun(A1(i), A2(i), ..., An(i))
end

という処理をまとめてやってくれるものになります。今回は例として、行列の各要素に値が0.5を超えるものは1に、超えないものは0にするという処理をやってみたいと思います。
(注意:この処理の場合はarrayfunを使うよりもずっと簡単なやり方がありますが、今回はあくまでも例ということでarrayfunで実行します。)

上記2つのスクリプトファイルを用意した上で実行してみます。

>> gputest2
経過時間は 15.774822 秒です。
経過時間は 2.594181 秒です。

やはり高速化されていますね。これで個々の配列要素に対してまとめてGPUで演算を行うという処理ができるようになりました。

実践編:MATLABで2次元移流拡散方程式を解く

今回は、GPUの利用例のひとつとして2次元の移流拡散方程式を数値的に計算するプログラムを作ってみようと思います。なお、2次元移流拡散方程式を解く際にはステンシル計算というものを行う必要がありますが、上記のarrayfunでステンシル計算を行う場合はちょっとしたテクニックが必要です。それについては話がややこしくなるので詳細は私の記事を参考にしてください。

移流拡散方程式は、 $$ \frac{\partial \phi}{\partial t} + \nabla \cdot (c\phi) = D\nabla^2 \phi $$ で表されます。この方程式は、物理量$\phi$が速度$c$で移流し、拡散係数(ここでは簡単のため定数)$D$のもとで拡散する様子を表すものです。特に今回は速度場が定常状態であると考えて、$c$の$x$方向成分を定数$u$、$y$方向成分を定数$v$とすると、 $$ \frac{\partial \phi}{\partial t} + u\frac{\partial \phi}{\partial x} + v\frac{\partial \phi}{\partial y} = D\left( \frac{\partial^2 \phi}{\partial x^2} + \frac{\partial^2 \phi}{\partial y^2} \right) $$ のように書き表すことも可能です。

今回は、有限差分法を用いて2次元の移流拡散方程式を解きます。離散化手法としては、移流項$\nabla \cdot (c\phi)$を1次の風上差分法、拡散項$D\nabla^2 \phi$を中心差分法で離散化します。また、時間発展はオイラー法で行います。こちらの離散化の詳細については長くなるので省略します。 そして、境界条件は周期境界条件を仮定します。

ステンシル計算では、配列の各要素に対して、その近傍の要素の値を利用して配列のデータをアップデートします。先ほどのarrayfunを利用する場合、近傍の要素の値の参照をうまく行うために入れ子関数を利用します。入れ子関数は関数の中に関数を定義する手法で、親関数で定義された変数を利用することが可能になります。ということで、移流拡散方程式を解く関数の中に、ステンシル計算を行う関数と境界条件処理を行う関数を定義するという形で実装します。

では、こちらを実行してみましょう。 パラメータ等を指定したら、adv_dif_2d_runを実行すれば計算が行われ、結果がアニメーションで表示されます。 私の実行環境では以下の画像のような結果になりました。

matlab_gpu

このように、GPUを使うと大幅に高速化されていることがわかります。

まとめ

今回は、MATLABでGPUコンピューティングを行う方法をご紹介しました。いかがでしたでしょうか。

「GPUを使って高速化したい!でもCUDAを使うほど面倒なことはしたくない!」という方には非常にオススメです。基本的なビルトイン関数ではGPUに対応しているので、画像処理やフーリエ変換など基本的な処理なら基本編の内容だけで簡単にこなせます。少しマニアックなことをやりたい場合には応用編の内容が必要になります。

ただし、極限までチューニングをして高速化を極めたい場合にはCUDAを使わざるを得ないかと思います。CUDAを使う場合は結構労力をがいるので、どこまでやりたいかを考えて、そのときどきで適切なものを使うのがよろしいかと思います。

私の記事が皆様のお役に立てれば幸いです。
それでは、よいMATLABライフを!

ギャラリー
  • ABUロボコン結果報告
  • スマホから部屋の電気をつけてみた
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • MakerFaireTokyo2017に出展します
  • たのしいロボット帝国 製作物紹介
記事検索
最新コメント