Unityによる弾性変形のシミュレーション

東京電機大学 工学部 情報通信工学科

学籍番号 11EC093

氏名 冨川 健太

指導教員 坂本 直志 教授

1.はじめに

機械や構造物に求められることは、第一に壊れないことである。ここで言う壊れることというのは、ある荷重に対して、その物体が破断などの永久に残る変形をするということである。

物体の変形には弾性変形と塑性変形がある。このうち永久に残る変形が発生しないのが弾性変形の領域であり、壊れないためにはこの領域に変形量が収まってなければならない。壊れない部品を設計するために、荷重に対しての変形量を計算する。

物体の変形量を解析するには、コンピューターでの計算のしやすさからほとんどの場合で有限要素法の考え方が使われる。CAE( computer aided engineering)の発展により、有限要素法を使った多くの解析ソフトウェアが研究、開発されてきた。

本研究では、弾性変形シミュレーションの解法としてUnityによる物理シミュレーションを提案し、その妥当性について評価する。

1.1.材料力学

機械や構造物に外力が作用すると、各部材が外力によって変形する。その変形がどうなるか明らかにするために、材料力学と呼ばれる力学の一分野が存在する。

1.1.1.応力

図1のように両端から荷重を受ける真直棒があるとする。

zu1
図1 両端から荷重を受ける真直棒

軸に垂直な断面における内力と外力の釣り合いを考える。断面を微小面積 mojiの領域に分け、その領域に生じている内力を mojiとする。図における断面において、内力の合計は外力と釣り合っていなければならない。すなわち、任意の断面で式(1)

1 (1)

が成り立つ。

材料力学では力が作用する面積に対する強さを表すのに応力を用いる。応力とは、単位面積あたりに働く内力であり、式(2)で与えられる。

2(2)

式より物体に働く力から、単位面積あたりに働く内力である応力を求めることができる。応力の単位はSI単位系において mojiでパスカルと呼ばれている。応力には、垂直応力(normal stress)とせん断応力(shear stress)がある。ある点の垂直応力は式(3)

3(3)

で定義される。図2に示すように同じ長さの異なる材料の棒Aと棒Bに荷重を加えるとする。

zu2
図2 長さの異なる棒

棒Bのほうが破断までに大きな荷重を加えることができたとしても、単純に棒Aより棒Bの強度が大きいと結論することはできない。棒Bのほうが横方向の断面積が大きいからである。物体に働く荷重と変形の関係を考えるには、荷重を断面積で割った応力を考慮する必要がある。

図3のように、棒が伸びるような外力を引張荷重、縮むような外力を圧縮荷重という。引張荷重を受けた場合、横断面には引張応力が生じる。圧縮荷重を受けた場合、横断面には圧縮応力が生じる。

zu3
図3 引張荷重と圧縮荷重

1.1.2.ひずみ

物体の伸びから単位長さあたりの伸びであるひずみを求めることができる。図4に示すように、長さmojiの棒に引張荷重mojiを加えて引っ張ったとする。

zu4
図4 棒の伸び

その結果棒の長さがmojiになったとすると、mojiを伸びといい、λで表す。単位長さについての伸びをひずみといい、εで表す。すなわち式(4)となる。

4(4)

ひずみの単位は、長さの単位の伸びを長さで割っているので無次元である。

ひずみについても応力の場合と同様に、伸びに対するひずみを引張ひずみ、縮みに対するひずみを圧縮ひずみという。

1.1.3.せん断方向の応力とひずみ

前項では、圧縮と引張について考えた。ここでは、材料に互い違いのずらす力を加えた場合について考える。このような荷重の加え方をせん断荷重と呼ぶ。

図5に示すように部材ABに荷重方向が軸と垂直方向に向いている外力F、すなわちせん断荷重Fが作用しているとする。

zu5
図5 せん断荷重を受ける棒

2つの荷重の作用点間の断面を考える。部材ACには図5のように内力が生じており、その合力はFに等しいはずである。この内力のことをせん断力と呼ぶ。力の作用面に沿って生じる応力をせん断応力といい、τで表す。垂直応力の定義式と同様にせん断応力についても式(5)のように定義することができる。

5 (5)

物体内に微小な立方体を考える。図5のようにせん断応力が各面に作用すると、この立方体は図のように変形する。

zu6
図6 せん断ひずみ

垂直ひずみの定義と同様に、 mojiをもとの長さ、mojiを伸びと考えれば、ひずみは式(6)と表すことができる。

6(6)

このひずみをせん断ひずみと呼ぶ。また、図6に示すように、始めにπ/2であった角度が mojiになったとすると、せん断ひずみは式(7)の角度変化としても定義することができる。

7(7)

γが微小である場合、mojiが近似的に成り立つことから式(8)が成り立つ。

8(8)

これは式(6)のひずみの定義と一致する。よって、角度の変化が微小であれば、せん断ひずみは角度の変化で近似することができる。せん断ひずみについても無次元量である。

1.1.4.フックの法則

材料の特性は材料試験によって得られる。材料試験の中で最も基本的な引張特性を調べる引張試験について述べる。図7のような試験片を用意し、荷重Pを加える。

zu7
図7 引張試験片

荷重Pに伴う試験片の伸びλを記録する。荷重Pと伸びλからそれぞれ応力σとひずみεを求め、応力を縦軸、ひずみを横軸にとると図8のような応力-ひずみ線図が得られる。

zu8
図8 応力-ひずみ線図

図の点Aから点Bまでの応力とひずみが比例関係にある領域を弾性変形領域という。この比例定数をヤング率と呼び、定数をEとすると式(9)が成り立つ。この関係がフックの法則である。

9 (9)

せん断応力τとせん断ひずみγの間にも式と同様の比例関係があり、比例定数をGとすると式(10)が成り立つ。

10  (10)

物体にかかる力から変形量を直接求めることはできない。材料力学の問題を解決するには、力→応力→ひずみ→変形の順に迂回しなければならない。ここで、応力とひずみの関係は、前述で説明したフックの法則を用い、比例関係であると考える。力、応力、ひずみ、変形の間を自由に行き来できることが重要である。

1.2.有限要素法

物体が単純な形状の立体であれば、力→応力→ひずみ→変形の順で変換していくことにより、力から変形を求めることができる。しかし一般的に複雑な形状の立体では、単純な変換公式がなく、応力とひずみの関係は微分方程式による表現となる。複雑な微分方程式の解を求めるのは難しい。そこで、近似解を得るために開発された方法が有限要素法である。

有限要素法で複雑な形状の変形を解析するには、まず複雑な形状を単純な形状の小さな要素に分割する。この小さな要素の特性を数式で表し、それをすべて組み合わせて全体の特性を近似する。

もともと構造物の静的な弾性変形を求めるために、1950年代にアメリカで開発された。汎用性のある方法のため、現在では弾性変形の解析以外にも広く使われている。

1.3.コンピューターによる弾性変形シミュレーション

複雑な形状の立体では、力と変形の関係は非線形である。これを有限要素法では、線形的な変形をする小さな要素の組み合わせで近似する。よって、小さな要素の特性は連立方程式で表現できる。これを組み合わせた全体の特性を近似する式は、多元連立方程式となるためマトリックスで表現できる。このマトリックスの解は機械的に求めることができるため、コンピューターでの計算と相性がよい。しかし、精度を高めていくために立体の分割数を多くすると、マトリックスの次元数が膨大となる。有限要素法の課題のひとつとして、この膨大なマトリックスの計算に時間がかかることが挙げられる。当初は専門的な知識と高価なコンピューターが必要であったが、現在では家庭用のコンピューターでも十分実用的な解析が可能となってきた。

近年、3DCGの発展により、GPUやグラフィック専用のライブラリが発達してきた。有限要素法の膨大なマトリックスの計算に、GPUを用いる研究もされている。

1.4.Unityについて

Unityは、Unity Technologies社が開発したゲームエンジンである。ゲームエンジンとは、コンピュータゲームを効率的に作成するためのソフトウェアの総称である。ゲームエンジンでは、3Dの物体の挙動の計算や、画像や音楽の再生など、ゲームで共通して用いられる主要な機能がまとめられたライブラリが利用できる。ゲームの作成者は、従来これらの機能を自分で実装する必要があった。しかし、ゲームエンジンを利用すればゲームエンジン上から処理を呼び出すだけでこれらの機能を実装できる。Unity以外にも、Epic Games社が開発したUnreal Engineや、Klab社が開発したPlaygroundなどのゲームエンジンがある。Unityの最大の特徴は、ゲーム内のオブジェクトの配置や、そのオブジェクトに対する機能の追加を、エディタ上で視覚的に行うことができる点である。

Unityには物理エンジンのPhysicsが付属している。Physicsは、Rigidbodyコンポーネントからなっている。Rigidbodyコンポーネントをゲーム内のオブジェクトに適用すると、オブジェクトに重力や摩擦などの力をかけることができる。ゲームオブジェクトにはColliderコンポーネントが適用されており、オブジェクトに対する当たり判定が実装されている。従来、これらの機能を実装するには複雑なプログラムを書く必要があった。しかしUnityでは、エディタ上で2つのコンポーネントをオブジェクトに適用するだけで、物理法則に従った挙動をする当たり判定のついたオブジェクトを実現できる。したがって、例えばステージ上で主人公に重力が働くアクションゲームなどを、Unityを使用すると従来より簡単に制作することができる。

UnityはPCだけでなく、スマートフォンやゲーム機といった複数のプラットフォームに対応している。1つのゲームシーンからそれぞれのプラットフォームに向けた実行ファイルを書き出すことができる。

これらの理由から、当初Unityは個人開発者などの比較的小規模な開発者に普及した。しかし最近ではゲーム開発会社での採用実績も増えている。さらに、ゲーム以外の製作でも使用が広がってきた。

1.5.本研究の概要

本研究では、弾性変形シミュレーションの方法としてUnityを使ったシミュレーションを提案する。シミュレーションの題材は片持はりとする。このシミュレーション結果から、Unityを使ったシミュレーションが有用であるか評価する。

2章では本研究で行ったUnityでの物理実験について解説する。

3章では本研究でおこなった片持はりのシミュレーションについて説明する。

4章では本研究のシミュレーション結果について、理論値との比較から評価する。

5章では本研究のまとめを述べる。

1.6.測定環境

表1 測定環境

OS

Windows 10 64bit

Unity

Version 5.4.0f3 personal

CPU

Intel Core i7-5820K 3.30Ghz

RAM

16GB

プログラム言語はC#を使用した。

2.Unityによる物理実験

UnityではRigidbodyコンポーネントをゲームオブジェクトに適用することで、ゲームオブジェクトに対し物理演算を行うことができる。本章では、このRigidbodyコンポーネントによる物理演算についての実験を行う。Rigidbodyコンポーネントを適用したゲームオブジェクトを実際に動作させ、Rigidbodyコンポーネントの物理演算の特性について評価する。本実験の題材は自由落下、放物運動である。

2.1.自由落下の実験

2.1.1.実験内容

自由落下の実験では球体のゲームオブジェクトを作成する。球体を重力に従って落下させ、ゲームシーンの開始から球体の高さが0となるときまでの時刻をプログラムで計測する。球体には重力を働かせるため、Rigidbodyコンポーネントをアタッチする。球体の開始時のPositionを(X,Y,Z)=(0,10,0)としたときと、(X,Y,Z)=(0,100,0)としたときで実験を行った。

実験で使用したプログラムについて説明する。使用したプログラムは付録8.1.1である。StartメソッドとUpdateメソッドの2つのメソッドがある。Unityでは、Startメソッド内のプログラムはスクリプトの起動直後に一度だけ実行される。Startメソッドに続いて、Updateメソッド内のプログラムがフレームごとに繰り返し実行される。

Startメソッド内では、落下する球体のゲームオブジェクトをGameObject型の変数Sphereに代入している。

Updateメソッド内の処理について説明する。18行目で、GameObject型のSphereのy座標を取得し、float型の変数lengthに代入している。Time.deltaTimeには最後のフレームからの経過時間が格納されている。前回のUpdateメソッドを実行したときからの経過時間が計測できる。これを加算していくと、ゲームシーンの開始時から任意のフレームまでの時間を計測することができる。20行目から23行目で、球体のy座標が0以上のときfloat型のtimeにTime.deltaTimeを加算している。25行目でtimeを出力している。

2.1.2.実験結果

自由落下の経過時間の理論値は、式(1)で計算できる。理論値として重力加速度gを9.81としたときの値を計算した。

11 (11)

表2 自由落下

測定回数

高さ10[m]

高さ100[m]

1回目

1.398[s]

4.484[s]

2回目

1.387[s]

4.494[s]

3回目

1.395[s]

4.489[s]

4回目

1.395[s]

4.498[s]

5回目

1.385[s]

4.498[s]

理論値

1.42784[s]

4.5152[s]

zu9
図9 自由落下の実験

2.2.放物運動の実験

2.2.1.実験内容

放物運動の実験では自由落下の実験と同じ環境で球体を落下させるが、開始時点で高さが0である。球体に(X,Y,Z)=(30,30,0)の速度をかける。球体が打ち上げられたのち、高さが0になるまでの時間と開始時点からの到達距離を測定する。図10

zu10
図10 斜方投射の実験

実験で使用したプログラムについて説明する。落下時間は実験2.1で使用した付録8.1.1のプログラムで計測した。球体に放物運動をさせるため付録8.1.2のプログラムを使用した。13行目と22行目はプログラムからRigidbodyコンポーネントにアクセスするための関数である。Start関数では14行目で(X,Y,Z)=(30,30,0)のベクトルを生成している。15行目でこのベクトルを引数としてAddForceメソッドを呼び出している。

Update関数では24行目から27行目で、球体のy座標が0以下になったとき移動を停止している。停止した点のx座標と初期位置のx座標を比較することで到達距離を測定する。

2.2.2.実験結果

放物運動の到達距離と滞空時間の理論値は式(12)、式(13)で計算できる。

12(12) 13(13)

表3 放物運動

測定回数

到達距離[m]

滞空時間[s]

1回目

183

6.078

2回目

183

6.079

3回目

183

6.07

4回目

183

6.072

5回目

183

6.075

理論値

183.430

6.11284

zu11
図11 斜方投射の実験

2.3.考察

理論値よりやや下振れすることがわかった。

この実験から、Unity内のパラメータの単位は大きさが[m]、速度が[m/s]、時間が[s]であり、SI単位系に準拠していることがわかった。

実験結果の数値がややばらつくのは、プログラムの時間の計測の方法によるものと考えられる。

3.Unityによる弾性変形のシミュレーション

有限要素法で変形をシミュレーションするには、通常物体の全体の特性をマトリックスで表現する。本研究ではマトリックスを使わず、ゲームオブジェクトを組み合わせることにより全体の特性を近似する。

3.1.シミュレーション内容

片持はりとは、一端が固定され、もう一端が自由な状態にあるはりのことである。このはりの固定されていない一端に荷重をかけると、もう一端が固定されているのではりがたわむ。

本研究では図12のような片持はりと呼ばれる3次元のはりの変形をシミュレーションする。はりが伸びる水平方向をx軸、垂直方向をy軸、奥行き方向をz軸と設定する。一端を固定、もう一端にy軸方向の荷重をかける。このとき、はりのy軸方向の変形量を計測する。

zu12zu12
図12 片持はり
14 (14) 15(15)
P
はりにかかる荷重
l
はりの長さ
b
はりの断面の幅
h
はりの断面の高さ
E
ヤング率
I
断面二次モーメント

3.2.Unityによる片持はりのモデル

弾性体の片持はりをUnityのゲームシーン内で再現する方法を考察する。弾性体とは力に対して変形をする物体のことである。Unityでは、直接弾性体を表現できる適当なコンポーネントがない。本研究では、剛体を表現するRigidbodyコンポーネントと、それを連結するJointの組み合わせで弾性体を表現する。Rigidbodyは力に対して変形をしないが、Jointは一定量の変形をする。RigidbodyをJointで多数連結すると、この組み合わせ全体として弾性体の変形を近似することができる。

Jointには複数の種類があるが、本実験ではSpring JointとCharcter Jointを使用した。それぞれのJointに共通するのが、2つのRigidbodyコンポーネントを適用したゲームオブジェクト間の弾性を表現するという点である。

本研究では、3次元上のx,y,z方向に立方体を配置し、それぞれをJointで接続して片持はりを再現する。立方体同士のJointの接続について考察する。図12の片持はりは、y軸方向に変形する。そのため、それぞれの立方体の接合部において、図の13ようにy軸方向にはy軸方向の垂直応力が働き、x軸とz軸方向にはy軸方向のせん断応力が働くものと考えられる。

zu13zu13
図13 立方体の間に発生するひずみ

よって、図14ように立方体のy軸方向の面にはy軸方向の垂直ひずみ、x,z軸方向の面にはy軸方向のせん断ひずみが発生するものと考えられる。

zu14zu14
図14 立方体に発生するひずみ

よって、立方体のy軸同士は垂直ひずみに対応するJointで接続し、x,z軸同士はせん断ひずみに対応するJointで接続する。本研究では、垂直ひずみをSpring Joint、せん断ひずみをCharacter Jointの変形で代替する。よって、立方体のy軸の面同士はSpring Jointで接続し、x,z面同士はCharacter Jointで接続する。

zu15
図15 垂直ひずみを近似するJoint
zu16
図16 せん断ひずみを近似するJoint

立方体をx,y,z軸方向に等間隔に配置する。図15、図16のように立方体同士を接続し、図17のような構造とする。図17の構造のうち、Rigidbodyコンポーネントは変形しないが、Jointについてはそれぞれ垂直方向とせん断方向に変形をする。そのため、図17の構造は、全体としてy軸方向に変形する片持はりの変形を近似することができる。本研究では、この図17の片持はりの変形のシミュレーションを行う。

zu17zu17
図17 本研究で提案する片持はり

3.3.ジョイントのパラメータについて

弾性体は物体のひずみと応力が比例するフックの法則が成り立つ。この法則のひずみと応力の関係をつかさどるパラメータがヤング率である。前項で述べたように、本研究ではひずみをジョイントの変形で代替する。このジョイントは、弾性体と同じヤング率というパラメータを持っていない。ジョイントの硬さを変更できるパラメータを持っている。次項の実験でこのジョイントの硬さのパラメータについて実験し、ヤング率と比べた考察をする。

実験で設定したJointのパラメータについて解説する。2つのJointで共通のパラメータについて説明する。

3.4.Spring Jointの実験

3.4.1.実験内容

Spring Jointは、2つのゲームオブジェクトを押し引きする方向に弾性を発揮する。イメージとしてはゲームオブジェクト間にばねをつけた状態である。本実験では、このSpring JointのSpringパラメータの特性について実験する。

図のように、Rigidbodyコンポーネントを適用した立方体を2つ生成する。それぞれの立方体のサイズは(X,Y,Z)=(1,1,1)とする。立方体Aの座標を(X,Y,Z)=(0,0,0)、立方体Bの座標を(X,Y,Z)=(2,0,0)とする。この立方体の間にSpring Jointを適用する。立方体Aと立方体Bには、Spring Jointコンポーネントを適用するため、Rigidbodyコンポーネントを適用する。Y軸方向に立方体が落下してしまうため、Use Gravityのチェックを外しておく。立方体AにSpring Jointコンポーネントを追加し、Connected Bodyのパラメータに立方体Bを指定する。

zu18
図18 Spring Joint

立方体AはRigidbody Constraintsによって固定する。立方体Bに付録7.1.3のプログラムを使用し、x方向に1の荷重をかける。立方体Bは荷重に対してx方向に変形する。立方体Aが固定されているため、立方体BはSpring Jointによって引き戻され、ある点で最大の変形量をとる。このときの立方体Bのx座標を計測し、座標 (X,Y,Z)=(2,0,0) からの変化を変形量とする。Springのパラメータを変え、このときの変形量の特性を測定する。

3.4.2.実験結果

表4 Spring Jointの特性

spring

変形量

1

2.5

2

1.3

2.5

1

10

0.27

zu19
図19 Spring Jointの特性

表4の結果について、ここでばねの伸びと力が変形するフックの法則を考える。式(16)

16(16)
F
ばねにかかる荷重
x
ばねの変位

Spring Jointの特性を見ると、Springの値に対して変形量がおおむね反比例しており、この法則に従っている。ばねにかけた力が(X,Y,Z)=(1,0,0)であるのでF=1とすると、k=2.5のときx=1となることがわかる。すなわち、ばねの弾性を考えたとき、ばねのkに対してSpring JointのSpringの値は2.5倍する必要がある。

3.5.Character Jointの実験

3.5.1.実験内容

Character Jointは2つのゲームオブジェクトの間のある軸に対し、その軸に対する回転方向への変形に弾性を発揮する。Character Jointでは、この軸を2本設定することができる。2本の軸を直交させると、2方向の回転方向の変形に弾性を設けることができる。

実験3.5.のSpring Jointの実験と同様に、立方体Aを固定し立方体Bに荷重をかける。この実験ではz方向の軸まわりの回転について測定をする。立方体Aと立方体Bの間の(X,Y,Z)=(1,0,0)からz方向へ向かうベクトルを軸とする。立方体Bに付録7.1.4.のプログラムを使用しy方向に0.1の荷重をかける。立方体Bは荷重に対してy方向に変形する。y方向への変形は立方体の間に設定したz軸方向への軸まわりの回転である。よって、立方体BはCharacter Jointによって引き戻される、ある点で最大の変形量をとる。このときの立方体Bのy座標を計測し、座標(X,Y,Z)=(2,0,0)を変形量とする。Springのパラメータを変え、このときの変形量の特性を測定する。

zu20
図20 Character Joint

Character Joint特有のパラメータについて解説する。

・AxisとSwing Axis 弾性を発揮させたい回転方向の軸について考える。Character Jointはこの軸を2本用意できる。1本をAxisに、もう1本をSwing Axisに指定する。回転の軸は、AxisとSwing Axisに指定した座標と原点を通るベクトルである。

3.5.2.実験結果

表5 Character Jointの特性

spring

変形量

1

0.51

2

0.31

3

0.23

5

0.17

10

0.13

20

0.1

zu21
図21 Character Jointの特性

表5の結果を見る。ばねにかけた力が(X,Y,Z)=(0,0.1,0)であり、F=0.1とするとSpringの値が20のとき変形が0.1である。Character Jointが弾性を発揮するのはひねりであるから、ゲームオブジェクトの変形は回転である。ここで、ゲームオブジェクトの変形が微小であるとして、式(8)のようにこの変形をせん断ひずみと近似できるとする。せん断変形のしにくさを表す物性値は剛性率と呼ばれ式(17)と定義される。

17 (17)
G
剛性率
A
断面積
Δx
変位
zu22
図22 剛性率を表す図

ゲームオブジェクトは1辺の長さが1の立方体であり、A=l=1である。よって式(17)は式(18)と変形できる。

 18 (18)

F=Δx=1となるSpringの値が20であるから、剛性率の値に対してCharacter Jointの値は20倍する必要がある。

3.6.片持はりのシミュレーション

片持はりの長さ20[m]、断面の高さと幅5[m]、ヤング率200[Pa] 、剛性率を80[Pa]と設定する。このはりの荷重の大きさ、長さ、断面の高さ、幅、の特性をシミュレーションする。式(14)、式(15)より変形量の理論値を求める。これらの数値と設定した荷重を当てはめて計算すると、変形量dを計算できる。

3.6.1.片持はりの生成方法

立方体を生成する方法として、UnityのPrefabの機能を使う。Prefabとは、同じゲームオブジェクトを大量に用意するための仕組みである。あらかじめ生成したいゲームオブジェクトをPrefabとして登録しておくことで、Instantiateメソッドからゲームオブジェクトを複製することができる。Instantiateメソッドを呼び出すスクリプトは、ゲームシーン上の空のゲームオブジェクトに適用する。

この実験でPrefabとして登録したゲームオブジェクトは、Scale(X,Y,Z)=(1,1,1)のRigidbodyコンポーネントを適用した立方体である。この立方体を1ずつ等間隔に開けてX,Y,Z方向にCube型の配列としてインスタンス化する。X方向に20個、Y,Z方向に5個を配置した。配置した立方体に、X,Z方向にCharacter Joint、Y方向にSpring Jointを適用する。Jointのパラメータについて次項で解説する。

3.6.2.Spring Jointのパラメータ

ヤング率をSpring JointのSpringのパラメータに置き換える。3.3.2.実験結果より、ヤング率を2.5倍した値をSpring JointのSpringの値に設定する。よって、Springの値はヤング率200[Pa]を2.5倍した500となる。

3.6.3.Character Jointのパラメータ

剛性率をCharacter JointのSpringのパラメータに置き換える。3.4.2.実験結果より、剛性率を20倍した値をCharacter JointのSpringの値に設定する。よって、Springの値は剛性率80[Pa]を20倍した1600となる。

Character Jointは立方体のx面とz面に適用する。回転の軸を2本用意する。x面についてはy方向の回転軸とz方向の回転軸とする。z面についてはy方向の回転軸とx方向の回転軸とする。それぞれ、AxisとSwing Axisの座標を以下のように設定した。

表6 Character Jointの軸

X方向Character Joint

Z方向Character Joint

Axis (X,Y,Z)=

(0,1,0)

(1,0,0)

Swing Axis(X,Y,Z)=

(0,0,1)

(0,1,0)

3.6.4.プログラムの説明

Start関数の中では、立方体を初期化し、立方体同士をJointで接続することを行っている。9行目から11行目ではりの大きさを定義している。それぞれx方向、y方向、z方向にこの数だけ立方体を生成するということである。12行目で片持はりを定義している。3次元の配列として、すべての立方体を格納する。9行目から26行目で立方体をインスタンス化している。立方体同士の間隔を1空けるため、for文のインデックスに2を掛けた値を立方体の座標としている。31行目から53行目までで立方体同士をJointで接続している。順に、x方向、y方向、z方向のJointである。y方向のみ、Spring Jointで接続するため、Baneメソッドの呼び出しをしている。立方体の終端にはJointを設けないため、立方体の数より1小さい値までJointを接続するメソッドの呼び出しを行う。55行目から64行目までで、一端が固定され、他端が自由端になっている片持はりの構造を再現する。Rigidbodyコンポーネントのconstraints変数を変更することで、ゲームオブジェクトの移動と回転を制限することができる。インデックスがx=0のゲームオブジェクトのconstraintsをFreezeAllとして、位置を固定することで片持はりの固定端を再現する。68、69行目で表6の軸を設定するため、ベクトルを3つ定義している。axisとswingaxisについて、それぞれ1番めのベクトルはx方向のCharacter Jointの軸、2番めのベクトルはz方向のCharacter Jointの軸を設定する。71行目のConnectメソッドでCharacter Jointによる接続を行う。96行目と97行目で剛性率を定義している。113行目のBaneメソッドでSpring Jointによる接続を行う。120行目でヤング率を定義している。

Update関数の中では、片持はりに荷重をかける。自由端の側のゲームオブジェクトに荷重をかけるため、インデックスが(X,Y,Z)=(19,2,2)のゲームオブジェクトにAddForceメソッドを実行する。ForceModeはForceとする。このゲームオブジェクトの開始時点のY座標から最大の変形までの変化を変形量とする。

3.6.5.シミュレーション結果

表7 片持はりの荷重に対する変形特性

荷重 F

シミュレーション値

理論値

シミュレーション値と理論値の差

1

0.67

0.256

0.414

5

1.33

1.28

0.05

10

2.1

2.56

-0.46

15

2.8

3.84

-1.04

zu23
図23 片持はりの荷重特性

表8 片持はりの荷重-変形量特性

長さ

シミュレーション値

理論値

シミュレーション値と理論値の差

5

0.26

0.04

0.22

10

0.77

0.32

0.45

15

1.35

1.08

0.27

20

1.94

2.56

-0.62

25

2.71

5

-2.29

30

3.51

8.64

-5.13

zu24
図24 片持はりの長さ特性

表9 片持はりの長さ-変形量特性

断面の高さ

シミュレーション値

理論値

シミュレーション値と理論値の差

3

4.57

11.85

-7.28

4

2.76

5

-2.24

5

1.94

2.56

-0.62

6

1.61

1.48

0.13

7

1.41

0.93

0.48

8

1.21

0.6

0.61

zu25
図25 片持はりの断面の高さ特性

表10 片持はりの断面の高さ-変形量特性

断面の幅

シミュレーション値

理論値

シミュレーション値と理論値の差

3

2.98

4.26

-1.28

5

1.94

2.56

-0.62

10

1.15

1.28

-0.13

20

0.8

0.64

0.16

zu26
図26 片持はりの断面の幅特性

4.評価

結果をみると、変形量が大きい領域で誤差が大きくなっている。パラメータの変化に対しての変形量の増加が弱く、片持はりとして硬い特性となっている。図23では、荷重と変形量が比例する特性は再現できているが、傾きが一致していない。シミュレーション値の傾きを1.8倍すると理論値の傾きと一致することがわかった。同様に図24と図25の関係については1.8倍、図6については1.2倍すると理論値の傾きに近づくことがわかった。総じて、特性は再現できているが、大きな変形に弱いと言える。

原因として構造上の欠陥が挙げられる。本研究では、片持はりのせん断ひずみを立方体の回転で近似しているが、この角度が大きくなると近似が成り立たなくなる。また、変形量が大きくなると回転の変形では無理のある形の変形となってしまい、正確なシミュレーションが行えなくなる。この回転の変形量が微小であれば以上のことを無視できるが、変形量が増大するにつれこのような原因により誤差が大きくなるものと考えられる。立方体間のJointの変形量を微小にすれば誤差も小さくなる。よって、立方体の体積を小さくし、単位体積あたりの立方体の数を上げればシミュレーションの精度を上げることができるものと考えられる。また、よりせん断変形に近い形で変形をするJointの開発が今後の課題である。

5.まとめ

本研究では弾性変形シミュレーションの新たな方法として、Unityでのシミュレーションを提案した。特性をおおむね再現できたが、大変形に対して弱いという結果となった。しかし、Unityを使ったシミュレーションの可能性を示すことができた。

本研究ではゲームオブジェクトの垂直方向にSpring Joint、水平方向にCharacter Jointを適用したものを一つの要素とした。このため、全体としてこれを組み合わせたとき、ある一方向にしか変形しない汎用性の乏しい構造となってしまった。

今後の課題として、より自由度と特性の再現度の高いJointの構造の開発を進めていきたい。

6.参考文献

  1. 日本機械学会 (2007) 『JSMEテキストシリーズ 材料力学』丸善出版株式会社
  2. UnityDocumentation(Version:5.4)(online) https://docs.unity3d.com/ja/540/Manual/

7.付録

7.1.プログラム

7.1.1.落下時間を計測するプログラム


using System;
using UnityEngine;
using System.Collections;

public class rakkajikan : MonoBehaviour {
    GameObject Sphere;
    private float time;
    
    // Use this for initialization
    void Start () {
        this.Sphere= GameObject.Find("Sphere");

    }

    // Update is called once per frame
    void Update()
    {
        float length = this.Sphere.transform.position.y;
       
        if (length >=0)
        {
            time += Time.deltaTime;
        }
    
        Debug.Log(time);
    }
}

		

7.1.2.放物運動を計測するプログラム


using UnityEngine;
using System.Collections;

public class sokudo : MonoBehaviour
{
    private Rigidbody _rb;


    // Use this for initialization
    void Start()
    {

        this._rb = GetComponent<Rigidbody>();
        Vector3 sokudo = new Vector3(30, 30, 0);
        _rb.AddForce(sokudo,ForceMode.VelocityChange);

    }

    // Update is called once per frame
    void Update()
    {
        this._rb = GetComponent<Rigidbody>();
        float length = this._rb.transform.position.y;
        if (length<=0)
        {
            _rb.constraints = RigidbodyConstraints.FreezePosition;
        }
    }
}

		

7.1.3.x方向に1荷重を加えるプログラム


using UnityEngine;
using System.Collections;

public class Addforce : MonoBehaviour
{
    private Rigidbody _rb;


    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        this._rb = GetComponent<Rigidbody>();
        Vector3 sokudo = new Vector3(1, 0, 0);
       _rb.AddForce(sokudo, ForceMode.Force);

    }
}

		

7.1.4.y方向に0.1荷重を加えるプログラム


using UnityEngine;
using System.Collections;

public class AddyForce : MonoBehaviour
{
    private Rigidbody _rb;


    // Use this for initialization
    void Start()
    {

        this._rb = GetComponent<Rigidbody>();
        Vector3 sokudo = new Vector3(0, (float) 0.1, 0);
        //_rb.AddForce(sokudo, ForceMode.VelocityChange);

    }

    // Update is called once per frame
    void Update()
    {
        this._rb = GetComponent<Rigidbody>();
        Vector3 sokudo = new Vector3(0, (float) 0.1, 0);
        _rb.AddForce(sokudo, ForceMode.Force);

    }
}

		

7.1.5.片持はりのシミュレーションプログラム


using System;
using UnityEngine;
using System.Collections;

public class purun
    : MonoBehaviour
{
    public GameObject CubePrefab;
    static private int x = 20;
    static private int y = 5;
    static private int z = 5;
    GameObject[,,] Cube = new GameObject[x, y, z];
    Vector3 ForceSize = new Vector3(0, -10, 0);

    // Use this for initialization
    void Start()
    {

        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                for (int k = 0; k < z; k++)
                {

                    Cube[i, j, k] = Instantiate(CubePrefab, new Vector3(i * 2, j * 2, k * 2), Quaternion.identity) as GameObject;

                }
            }
        }
        for (int i = 0; i < x; i++)
        {
            for (int j = 0; j < y; j++)
            {
                for (int k = 0; k < z; k++)
                {
                    if (i < x - 1)
                    {
                        Connect(Cube[i, j, k], Cube[i + 1, j, k], 0);
                    }

                    if (j < y - 1)
                    {
                        //Connect(Cube[i, j, k], Cube[i, j + 1, k], 1);
                        Bane(Cube[i, j, k], Cube[i, j + 1, k], 1);
                    }
                    if (k < z - 1)
                    {
                        Connect(Cube[i, j, k], Cube[i, j, k + 1], 2);
                    }
                }
            }
        }

        for (int j = 0; j < y; j++)
        {
            for (int k = 0; k < z; k++)
            {

                Rigidbody aa = Cube[0, j, k].GetComponent<Rigidbody>();
                aa.constraints = RigidbodyConstraints.FreezeAll;
                // aa.mass = (float) 0.1;
            }
        }
    }

    //private Vector3[] anch = { new Vector3((float)0.5, 0, 0), new Vector3(0, (float)0.5, 0), new Vector3(0, 0, (float)0.5) };
    private Vector3[] axis = { new Vector3(0, 1, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0) };
    Vector3[] swingaxis = { new Vector3(0, 0, 1), new Vector3(0, 1, 0), new Vector3(0, 1, 0) };

    private void Connect(GameObject o1, GameObject o2, int nn)
    {
        CharacterJoint[] aa = o1.GetComponents<CharacterJoint>();
        o1.AddComponent<CharacterJoint>();
        CharacterJoint[] a = o1.GetComponents<CharacterJoint>();
        int n = aa.Length;
        a[n].connectedBody = o2.GetComponent<Rigidbody>();


        SoftJointLimitSpring characterSpring = a[n].twistLimitSpring;
        SoftJointLimitSpring swingSpring = a[n].swingLimitSpring;
        //SoftJointLimit lowtwistlimit = a[n].lowTwistLimit;
        //SoftJointLimit hightwistlimit = a[n].highTwistLimit;
        //SoftJointLimit swing1limit = a[n].swing1Limit;

        //lowtwistlimit.limit = 0;
        //lowtwistlimit.bounciness = 0;
        //lowtwistlimit.contactDistance = 0;
        //hightwistlimit.limit = 0;
        //hightwistlimit.bounciness = 0;
        //hightwistlimit.contactDistance = 0;
        //swing1limit.limit = 3;
        //swing1limit.bounciness = 0;
        //swing1limit.contactDistance = 0;

        characterSpring.spring = 160;
        swingSpring.spring = 160;
        characterSpring.damper = 10;
        swingSpring.damper = 10;

        a[n].twistLimitSpring = characterSpring;
        a[n].swingLimitSpring = swingSpring;
        a[n].autoConfigureConnectedAnchor = true;
        //a[n].anchor = anch[nn];
        a[n].axis = axis[nn];
        a[n].swingAxis = swingaxis[nn];
        //a[n].lowTwistLimit = lowtwistlimit;
        //a[n].highTwistLimit = hightwistlimit;
        //a[n].swing1Limit = swing1limit;
        //a[n].swing2Limit = swing1limit;
    }

    private void Bane(GameObject o1, GameObject o2, int nn)
    {
        SpringJoint[] aa = o1.GetComponents<SpringJoint>();
        o1.AddComponent<SpringJoint>();
        SpringJoint[] a = o1.GetComponents<SpringJoint>();
        int n = aa.Length;
        a[n].connectedBody = o2.GetComponent<Rigidbody>();
        a[n].spring = 500;
        a[n].damper = 200;
        a[n].autoConfigureConnectedAnchor = true;
        //a[n].anchor = anch[nn];
        //a[n].axis = ax;
    }

    // Update is called once per frame
    void Update()
    {
        Rigidbody aa = Cube[19, 2, 2].GetComponent<Rigidbody>();
        aa.AddForce(ForceSize, ForceMode.Force);

    }

}