ikarosの作業場

飛行機の設計もできる系のCshaper。「なおこの記事は個人的見解であり、所属する組織の意見とは一切関係がありません」と書かざるを得なくなった悲しみを知れ

人工知能で飛行機の翼を作ったお話【MIS.W51代アドベントカレンダー】

クリスマスはいかがお過ごしでしょうか。今日がイブに当たるわけですが、私はバイト→人力飛行機の作業というコンボで一日がつぶれることが確定しました涙

さて、プログラミング・情報科学を専攻しながら、今、私はWASAという早稲田大の鳥人間サークルで人力飛行機の全体設計を担当させていただいております。
今日はそんなところでディープラーニング(以後DLと表記)っぽいことして翼の設計をしたよっていうお話です。

翼型(Airfoil)とは

今年度の設計では様々な点で設計アルゴリズムを改良して、飛行機を飛ばすときに発生してしまう抗力(世間一般で言う空気抵抗)を減らすようにいろいろやってました
今回DLを応用したのは翼型の設計についてです。

翼型(Airfoil)とは飛行機の翼の断面の形状のことで、翼型によって飛行機の抗力などが大きく変化します
f:id:ikarostech:20171222154919p:plain
図 翼型(水色の部分)[1]
f:id:ikarostech:20171222154730p:plain
人力飛行機での代表的な翼型 DAE21[2]

従来の翼型設計方法

円の複素写像

f:id:ikarostech:20171222164324p:plain
写像による翼型の生成(左),翼型による一様流への写像(右)[3]
複素平面状での円を
{ \displaystyle
\zeta = z + \frac{a^2}{z} e^{-i2\alpha}
}

という複素関数によって変形し、またその逆関数を円と一様流に対して変形することで、翼の周りの空気の流れを求め揚力や抗力を求められるということを利用して、欲しい揚力や抗力から逆問題として翼型へ変形する{ \displaystyle \zeta }を求めるもの


翼型解析ソフトにも用いられている理論ではある
こいつ(xfoilhttp://web.mit.edu/drela/Public/web/xfoil/)とか
こいつXFLR5とか

ただし翼の表面で空気の流れが摩擦力で引っ張られてしまう影響は考慮されていなかったり、翼の後ろ(後縁)の厚みが急速に0に収束し始めてしまい、実際に制作にはそのままでは応用できないという問題点がある。

翼型設計においては終盤に翼型の調整で用いられることの多い理論

翼型混合(遺伝的アルゴリズム:GA)

既存翼型をいろんな配合で混ぜ合わせてその中から性能のいい翼型を求め、その翼型をもとにさらに配合を繰り返す…ということを延々とやっていく方法
f:id:ikarostech:20171222173420p:plain
図 XGAGにおける遺伝的アルゴリズムにおける翼型の選定[4]
当然のことながら最初に選定する翼型によって性能がかなり引っ張られる
そのためこちらや
UIUC Airfoil Data Site
こちらで
airfoiltools.com

翼型を選定し、先ほど紹介したxfoilやxflr5で解析をかけた結果からさらに選定して解析にかけるっていう手法がとられる

ベジェ曲線を用いた翼型の生成

ここからは僕が開発したお話

従来の翼型設計から離れて

「翼型としてあり得そうな形をすべて作って全部解析してそのデータの中から選べるようにすればよくね?」

という発想で翼型を作ることにしました

ここで白羽の矢を立ったのはベジェ曲線です
f:id:ikarostech:20171222181843p:plain:w450
図 3次ベジェ曲線[5]

ベジェ曲線はコンピューターグラフィックスの分野で非常によく用いられる曲線で特にAdobe Illustlatorでの標準の曲線描画に用いられていることで知られています。

さて、2次のベジェ曲線は3つの制御点, { \displaystyle {\bf P_0},{\bf P_1},{\bf P_2} }を用いて次のように定義されます

{ \displaystyle
{\bf P}(t)=(1-t^2){\bf P_0}+2t(1-t){\bf P_1}+t^2 {\bf P_2} t\in[0,1]
}
f:id:ikarostech:20171222195332p:plain:w450
図 2次ベジェ曲線[6]

このベジェ曲線を用いることでわずか3つの点で滑らかな曲線が定義できるわけなのです!

また2次ベジェ曲線には次にあげる特徴があります

定理
 {\displaystyle
制御点{\bf P_0},{\bf P_1},{\bf P_2}によって定義される2次ベジェ曲線を{\bf P}(t)とする\\ \\
 1.{\bf P}(t)は{\bf P_0}および{\bf P_2}を通過する。\\また{\bf P_0}と{\bf P_2}を結ぶ直線上に{\bf P_1}が存在しないとき、{\bf P}(t)は{\bf P_1}を通過しない\\
2.{\bf P_0}での{\bf P}(t)の接線と{\bf P_2}での{\bf P}(t)の接線の交点は{\bf P_1}と一致する
}


どちらも重要な定理で、この定理をうまく用いることで2次ベジェ曲線をつなげて1回微分可能である連続な曲線(正接な曲線/C1 連続性)を構成することができます
f:id:ikarostech:20171222195801p:plain:w450
図 曲線の区別[7] 接触(C0連続性) (左) 正接(C1連続性) (中央) 曲率(C2連続性)(右)

というわけで6つの2次ベジェ曲線を組み合わせて作った翼型の1つがこちら
f:id:ikarostech:20171222202843p:plain
図 2次ベジェ曲線にて製作した翼型

このような翼型を約200万製作しすべて解析にかけました。この数は作業で製作しうるすべての翼型をカバーできたと思われます

人工知能の応用

さてというわけで本題ですよ!
ここまででベジェ曲線で翼型を作ったわけですがあまりきれいじゃないです
この不自然さを解消するために人工知能を作りました

今回の人工知能のモデルには"cycleGAN"(1)を用いました。

cycleGANはJun-Yan Zhu氏らの研究チームが開発した手法で、
あるデータ群Aと、Aとは異なるデータ群Bが存在するとき、AをBに変換する、あるいはBをAに変換するモデルです

彼らは画像処理においてこの方法について実験を行い、絵画を写真風に加工したり、しまうまの写真を普通の馬の写真に加工が可能であったという実験結果を報告しました。

彼らのソースコードは以下のGitHubにて公開されています
github.com


このモデルには2つの学習機、Discriminator(判別機)とGenerator(生成機)が必要であり、それぞれの学習手順は以下の通りです
1.AとBどちら側の画像か判別するようにDiscriminatorを学習させる
2.A側のデータをB側へ、B側のデータをA側へDiscriminatorを"欺かせる"ようにGeneratorを学習させる
3.1.と2.を繰り返し精度を上げる

f:id:ikarostech:20171222210504p:plain
図 Discriminator
f:id:ikarostech:20171222210546p:plain
図 Generator

この二つの学習機が高めあうことでどんどん精度がよくなっていくわけですね!

さて、今回は以下のようにデータセットをまとめます

  • A : 既存翼型(DAE21とか)の(x,y)座標データ、2次元ベクトル*161個
  • B : ベジェ曲線由来の翼型の(x,y)座標データ、2次元ベクトル*161個

AもBも以下のようにデータが格納されています
f:id:ikarostech:20171222211557p:plain:w100
x座標はAもBもまったく同じなので今回はy座標だけとりだして学習させましょう

というわけでDiscriminatorとGeneratorを以下のようなサポートベクターマシン(SVM)にて設計します。また学習方法には標準的な勾配降下法を用います。

  • Discriminator : 1×161行列 二値判定(Aのとき0,Bのとき1を返す)
  • Generator : 161×161行列 Aの値を受け取ってBもどきに変換する、逆行列はBの値を受け取ってAもどきに変換する

なるべく元のデータに手を加えない方がいいので初期値は1の対角行列にしてから学習させましょうね

(CNNでもやったんですが、画像などと比べて座標データはいかんせんが単純なのでCNNとSVMとであまり差が出ませんでした…優位な差が出ない場合、計算時間がより短いSVMの方がよいと思われます)

言語はPython,フレームワークにはTensorFlow 1.3.2を用いました

#Discriminator
import tensorflow as tf
x = tf.placeholder(tf.float32, [None, 161])
W = tf.Variable(tf.zeros([161,1]))
y = tf.nn.sigmoid(tf.matmul(x,W))
y_ = tf.placeholder(tf.float32, [None,1])
logistic_func = -tf.reduce_sum(y_ * tf.log(y) + (1 - y_) * tf.log(1 - y))
train_step = tf.train.GradientDescentOptimizer(0.0001).minimize(logistic_func)
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(10000):
    sess.run(train_step, feed_dict={x: xf, y_: yf})
#Generator
W_ = tf.Variables(tf.diag(tf.ones([161])))
z = tf.nn.sigmoid (tf.matmul(tf.matmul(x,W_), W))
logistic_func = -tf.reduce_sum(z)
train_step = tf.train.GradientDescentOptimizer(1e-10).minimize(logistic_func)
init = tf.initalize_all_variables()
sess = tf.Session()
sess.run(init)
for i in range(1e+10):
    sess.run(train_step, feed_dict={x: xf, y_:yf})

というわけで、人工知能とかを用いて修正した翼型がこちら
f:id:ikarostech:20171222202510p:plain
人工知能で補正した翼型

最後にちょっと手で手直ししたりして実際の飛行機に使われるわけです


参考文献

(1)Jun-Yan Zhu, Taesung Park, Phillip Isola, Alexei A. Efros, Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks, arXiv preprint arxiv:1703.10593, 2017.

当サイトのソースコード及びその他の情報は個人・商用問わず自由に使っていただいてかかまいませんが、当サイトの情報が元で発生したいかなる結果・不利益については責任を負いかねますのでご了承ください